Add documentation infrastructure and certificate pinning
- Add mdBook configuration (book.toml) for documentation - Create new docs style guide appendix for conversation examples - Add WhatsApp-style chat CSS for consistent doc formatting - Replace flow diagram references with screen mockup SVGs - Create comprehensive SVG interface mockups for all Suite apps: - Main suite layout and individual app screens - Analytics, Calendar, Chat, Compliance, Designer - Drive, Mail, Meet, Paper, Player, Research - Sources, Tasks interfaces - Implement certificate pinning module (cert_pinning.rs) with: - SPKI fingerprint validation using SHA-256 - Support for primary and backup pins - Pin rotation with expiration tracking - Report-only mode for testing - Validation caching for performance - Add ring crate dependency for cryptographic operations
1
Cargo.lock
generated
|
|
@ -1465,6 +1465,7 @@ dependencies = [
|
|||
"regex",
|
||||
"reqwest 0.12.24",
|
||||
"rhai",
|
||||
"ring 0.17.14",
|
||||
"rust_xlsxwriter",
|
||||
"rustls 0.21.12",
|
||||
"rustls-native-certs 0.6.3",
|
||||
|
|
|
|||
|
|
@ -151,6 +151,7 @@ rcgen = { version = "0.11", features = ["pem"] }
|
|||
x509-parser = "0.15"
|
||||
rustls-native-certs = "0.6"
|
||||
webpki-roots = "0.25"
|
||||
ring = "0.17"
|
||||
time = { version = "0.3", features = ["formatting", "parsing"] }
|
||||
jsonwebtoken = "9.3"
|
||||
tower-cookies = "0.10"
|
||||
|
|
|
|||
19
docs/book.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
[book]
|
||||
title = "General Bots Documentation"
|
||||
authors = ["General Bots Team"]
|
||||
language = "en"
|
||||
multilingual = false
|
||||
src = "src"
|
||||
|
||||
[build]
|
||||
build-dir = "book"
|
||||
|
||||
[output.html]
|
||||
default-theme = "light"
|
||||
preferred-dark-theme = "navy"
|
||||
smart-punctuation = true
|
||||
additional-css = ["src/custom.css", "src/whatsapp-chat.css"]
|
||||
additional-js = ["src/theme-sync.js"]
|
||||
|
||||
[output.html.favicon]
|
||||
png = "favicon.png"
|
||||
|
|
@ -317,5 +317,7 @@
|
|||
|
||||
- [Appendix C: Environment Variables](./appendix-env-vars/README.md)
|
||||
|
||||
- [Appendix D: Documentation Style](./appendix-docs-style/conversation-examples.md)
|
||||
|
||||
[Glossary](./glossary.md)
|
||||
[Contact](./contact/README.md)
|
||||
235
docs/src/appendix-docs-style/conversation-examples.md
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
# Conversation Examples Style Guide
|
||||
|
||||
> **Standard format for displaying bot-user conversations in documentation**
|
||||
|
||||
## Overview
|
||||
|
||||
All conversation examples in General Bots documentation use a WhatsApp-style chat format. This provides a consistent, familiar, and readable way to show bot interactions.
|
||||
|
||||
## CSS Include
|
||||
|
||||
The styling is defined in `/assets/wa-chat.css`. Include it in your mdBook or HTML output.
|
||||
|
||||
---
|
||||
|
||||
## Basic Structure
|
||||
|
||||
```html
|
||||
<div class="wa-chat">
|
||||
<div class="wa-message bot">
|
||||
<div class="wa-bubble">
|
||||
<p>Bot message here</p>
|
||||
<div class="wa-time">10:30</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-message user">
|
||||
<div class="wa-bubble">
|
||||
<p>User message here</p>
|
||||
<div class="wa-time">10:31</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Message Types
|
||||
|
||||
### Bot Message
|
||||
|
||||
```html
|
||||
<div class="wa-message bot">
|
||||
<div class="wa-bubble">
|
||||
<p>Hello! How can I help you today?</p>
|
||||
<div class="wa-time">10:30</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### User Message
|
||||
|
||||
```html
|
||||
<div class="wa-message user">
|
||||
<div class="wa-bubble">
|
||||
<p>What meetings do I have today?</p>
|
||||
<div class="wa-time">10:31</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Formatting Within Messages
|
||||
|
||||
### Multiple Paragraphs
|
||||
|
||||
```html
|
||||
<div class="wa-bubble">
|
||||
<p>You have 2 meetings scheduled:</p>
|
||||
<p>• 2:00 PM - Team Standup (30 min)</p>
|
||||
<p>• 4:00 PM - Project Review (1 hour)</p>
|
||||
<div class="wa-time">10:31</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Bold Text
|
||||
|
||||
```html
|
||||
<p><strong>Name:</strong> John Smith</p>
|
||||
<p><strong>Email:</strong> john@example.com</p>
|
||||
```
|
||||
|
||||
### Emoji Usage
|
||||
|
||||
Emojis are encouraged to make conversations more expressive:
|
||||
|
||||
| Purpose | Emoji Examples |
|
||||
|---------|----------------|
|
||||
| Success | ✅ ✓ 🎉 |
|
||||
| Warning | ⚠️ ⚡ |
|
||||
| Error | ❌ 🔴 |
|
||||
| Info | ℹ️ 📋 |
|
||||
| File | 📄 📁 📎 |
|
||||
| Calendar | 📅 🗓️ |
|
||||
| Email | 📧 ✉️ |
|
||||
| Person | 👤 👥 |
|
||||
| Time | 🕐 ⏱️ |
|
||||
|
||||
### File Attachments
|
||||
|
||||
```html
|
||||
<div class="wa-message user">
|
||||
<div class="wa-bubble">
|
||||
<p>Here's the report</p>
|
||||
<p>📎 quarterly-report.pdf</p>
|
||||
<div class="wa-time">10:32</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### Action Buttons (visual representation)
|
||||
|
||||
```html
|
||||
<p>[📧 Send] [✏️ Edit] [🗑 Discard]</p>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Complete Example
|
||||
|
||||
```html
|
||||
<div class="wa-chat">
|
||||
<div class="wa-message user">
|
||||
<div class="wa-bubble">
|
||||
<p>Schedule a meeting with Sarah tomorrow at 2pm</p>
|
||||
<div class="wa-time">10:30</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-message bot">
|
||||
<div class="wa-bubble">
|
||||
<p>✅ Meeting scheduled!</p>
|
||||
<p>👥 Meeting with Sarah</p>
|
||||
<p>📅 Tomorrow at 2:00 PM</p>
|
||||
<p>⏱️ Duration: 1 hour</p>
|
||||
<p>Invitation sent to Sarah.</p>
|
||||
<div class="wa-time">10:30</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Rendered Output:**
|
||||
|
||||
<div class="wa-chat">
|
||||
<div class="wa-message user">
|
||||
<div class="wa-bubble">
|
||||
<p>Schedule a meeting with Sarah tomorrow at 2pm</p>
|
||||
<div class="wa-time">10:30</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="wa-message bot">
|
||||
<div class="wa-bubble">
|
||||
<p>✅ Meeting scheduled!</p>
|
||||
<p>👥 Meeting with Sarah</p>
|
||||
<p>📅 Tomorrow at 2:00 PM</p>
|
||||
<p>⏱️ Duration: 1 hour</p>
|
||||
<p>Invitation sent to Sarah.</p>
|
||||
<div class="wa-time">10:30</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## Variants
|
||||
|
||||
### Full Width
|
||||
|
||||
Add `wa-full-width` class for wider conversations:
|
||||
|
||||
```html
|
||||
<div class="wa-chat wa-full-width">
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
### Compact
|
||||
|
||||
Add `wa-compact` class for tighter spacing:
|
||||
|
||||
```html
|
||||
<div class="wa-chat wa-compact">
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
### Hide Timestamps
|
||||
|
||||
Add `wa-no-time` class to hide timestamps:
|
||||
|
||||
```html
|
||||
<div class="wa-chat wa-no-time">
|
||||
...
|
||||
</div>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Keep messages concise** - Break long bot responses into multiple paragraphs
|
||||
2. **Use consistent timestamps** - Use realistic times (10:30, 10:31, etc.)
|
||||
3. **Start with user context** - Show what the user asked before the bot response
|
||||
4. **Include visual feedback** - Use emojis for status (✅, ❌, 📋)
|
||||
5. **Show realistic flows** - Include multi-turn conversations when appropriate
|
||||
6. **Use semantic formatting** - Bold for labels, lists for options
|
||||
|
||||
---
|
||||
|
||||
## Files Using This Format
|
||||
|
||||
This format is used throughout the documentation:
|
||||
|
||||
- `chapter-02/template-crm-contacts.md`
|
||||
- `chapter-04-gbui/apps/*.md`
|
||||
- `chapter-06-gbdialog/basic-vs-automation-tools.md`
|
||||
- And many more...
|
||||
|
||||
---
|
||||
|
||||
## See Also
|
||||
|
||||
- [UI Structure](../chapter-04-gbui/ui-structure.md)
|
||||
- [Chat App Documentation](../chapter-04-gbui/apps/chat.md)
|
||||
|
||||
<style>
|
||||
.wa-chat{background-color:#e5ddd5;border-radius:8px;padding:20px 15px;margin:20px 0;max-width:500px;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif;font-size:14px}
|
||||
.wa-message{margin-bottom:10px}
|
||||
.wa-message.user{text-align:right}
|
||||
.wa-message.user .wa-bubble{background-color:#dcf8c6;display:inline-block;text-align:left}
|
||||
.wa-message.bot .wa-bubble{background-color:#fff;display:inline-block}
|
||||
.wa-bubble{padding:8px 12px;border-radius:8px;box-shadow:0 1px .5px rgba(0,0,0,.13);max-width:85%}
|
||||
.wa-bubble p{margin:0 0 4px 0;line-height:1.4;color:#303030}
|
||||
.wa-bubble p:last-child{margin-bottom:0}
|
||||
.wa-time{font-size:11px;color:#8696a0;text-align:right;margin-top:4px}
|
||||
</style>
|
||||
195
docs/src/assets/chapter-04/analytics-interface.svg
Normal file
|
|
@ -0,0 +1,195 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.card { fill: #fff; stroke: #ddd; stroke-width: 1; }
|
||||
.metric-value { fill: #1a1a1a; font-family: Arial, sans-serif; font-weight: bold; }
|
||||
.metric-label { fill: #666; font-family: Arial, sans-serif; }
|
||||
.metric-change-up { fill: #7ED321; font-family: Arial, sans-serif; }
|
||||
.metric-change-down { fill: #E74C3C; font-family: Arial, sans-serif; }
|
||||
.chart-line { stroke: #4A90E2; stroke-width: 2; fill: none; }
|
||||
.chart-area { fill: #4A90E2; opacity: 0.1; }
|
||||
.chart-grid { stroke: #eee; stroke-width: 1; }
|
||||
.bar { fill: #4A90E2; }
|
||||
.bar-alt { fill: #BD10E0; }
|
||||
.dropdown { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.card { fill: #2a2a2a; stroke: #444; }
|
||||
.metric-value { fill: #ffffff; }
|
||||
.metric-label { fill: #aaa; }
|
||||
.chart-grid { stroke: #333; }
|
||||
.dropdown { fill: #2a2a2a; stroke: #444; }
|
||||
.button { fill: #00D4FF; }
|
||||
.chart-line { stroke: #00D4FF; }
|
||||
.bar { fill: #00D4FF; }
|
||||
.bar-alt { fill: #E040FB; }
|
||||
.chart-area { fill: #00D4FF; opacity: 0.1; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Analytics - Dashboard Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Header -->
|
||||
<rect x="30" y="50" width="840" height="50" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="92" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- Title and Controls -->
|
||||
<text x="55" y="82" font-size="16" font-weight="600" class="main-text">📊 Analytics Dashboard</text>
|
||||
|
||||
<!-- Time Range Dropdown -->
|
||||
<g transform="translate(550, 62)">
|
||||
<rect x="0" y="0" width="120" height="30" rx="4" class="dropdown"/>
|
||||
<text x="15" y="20" font-size="13" class="main-text">Last 24h ▼</text>
|
||||
</g>
|
||||
|
||||
<!-- Refresh Button -->
|
||||
<g transform="translate(680, 62)">
|
||||
<rect x="0" y="0" width="80" height="30" rx="4" class="button"/>
|
||||
<text x="40" y="20" text-anchor="middle" font-size="13" class="button-text">⟳ Refresh</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="30" y1="100" x2="870" y2="100" class="divider"/>
|
||||
|
||||
<!-- Metric Cards Row -->
|
||||
<g transform="translate(50, 115)">
|
||||
<!-- Card 1: Messages -->
|
||||
<rect x="0" y="0" width="185" height="90" rx="8" class="card"/>
|
||||
<text x="92" y="35" text-anchor="middle" font-size="28" class="metric-value">1,234</text>
|
||||
<text x="92" y="55" text-anchor="middle" font-size="13" class="metric-label">Messages</text>
|
||||
<text x="92" y="75" text-anchor="middle" font-size="12" class="metric-change-up">↑ +12%</text>
|
||||
|
||||
<!-- Card 2: Success Rate -->
|
||||
<rect x="200" y="0" width="185" height="90" rx="8" class="card"/>
|
||||
<text x="292" y="35" text-anchor="middle" font-size="28" class="metric-value">89%</text>
|
||||
<text x="292" y="55" text-anchor="middle" font-size="13" class="metric-label">Success Rate</text>
|
||||
<text x="292" y="75" text-anchor="middle" font-size="12" class="metric-change-up">↑ +3%</text>
|
||||
|
||||
<!-- Card 3: Avg Response Time -->
|
||||
<rect x="400" y="0" width="185" height="90" rx="8" class="card"/>
|
||||
<text x="492" y="35" text-anchor="middle" font-size="28" class="metric-value">2.3s</text>
|
||||
<text x="492" y="55" text-anchor="middle" font-size="13" class="metric-label">Avg Response</text>
|
||||
<text x="492" y="75" text-anchor="middle" font-size="12" class="metric-change-down">↓ -0.2s</text>
|
||||
|
||||
<!-- Card 4: Users Today -->
|
||||
<rect x="600" y="0" width="185" height="90" rx="8" class="card"/>
|
||||
<text x="692" y="35" text-anchor="middle" font-size="28" class="metric-value">45</text>
|
||||
<text x="692" y="55" text-anchor="middle" font-size="13" class="metric-label">Users Today</text>
|
||||
<text x="692" y="75" text-anchor="middle" font-size="12" class="metric-change-up">↑ +8</text>
|
||||
</g>
|
||||
|
||||
<!-- Charts Row -->
|
||||
<g transform="translate(50, 220)">
|
||||
<!-- Line Chart: Messages Over Time -->
|
||||
<rect x="0" y="0" width="480" height="200" rx="8" class="card"/>
|
||||
<text x="20" y="25" font-size="14" font-weight="500" class="main-text">Messages Over Time</text>
|
||||
|
||||
<!-- Chart Grid -->
|
||||
<g transform="translate(30, 45)">
|
||||
<line x1="0" y1="0" x2="430" y2="0" class="chart-grid"/>
|
||||
<line x1="0" y1="40" x2="430" y2="40" class="chart-grid"/>
|
||||
<line x1="0" y1="80" x2="430" y2="80" class="chart-grid"/>
|
||||
<line x1="0" y1="120" x2="430" y2="120" class="chart-grid"/>
|
||||
|
||||
<!-- Chart Area -->
|
||||
<path d="M0,100 Q50,80 100,60 T200,40 T300,50 T400,20 L400,120 L0,120 Z" class="chart-area"/>
|
||||
|
||||
<!-- Chart Line -->
|
||||
<path d="M0,100 Q50,80 100,60 T200,40 T300,50 T400,20" class="chart-line"/>
|
||||
|
||||
<!-- Data Points -->
|
||||
<circle cx="0" cy="100" r="4" class="bar"/>
|
||||
<circle cx="100" cy="60" r="4" class="bar"/>
|
||||
<circle cx="200" cy="40" r="4" class="bar"/>
|
||||
<circle cx="300" cy="50" r="4" class="bar"/>
|
||||
<circle cx="400" cy="20" r="4" class="bar"/>
|
||||
|
||||
<!-- X-axis Labels -->
|
||||
<text x="0" y="140" text-anchor="middle" font-size="10" class="secondary-text">Mon</text>
|
||||
<text x="100" y="140" text-anchor="middle" font-size="10" class="secondary-text">Tue</text>
|
||||
<text x="200" y="140" text-anchor="middle" font-size="10" class="secondary-text">Wed</text>
|
||||
<text x="300" y="140" text-anchor="middle" font-size="10" class="secondary-text">Thu</text>
|
||||
<text x="400" y="140" text-anchor="middle" font-size="10" class="secondary-text">Fri</text>
|
||||
</g>
|
||||
|
||||
<!-- Bar Chart: Top Questions -->
|
||||
<rect x="500" y="0" width="285" height="200" rx="8" class="card"/>
|
||||
<text x="520" y="25" font-size="14" font-weight="500" class="main-text">Top Questions</text>
|
||||
|
||||
<g transform="translate(520, 45)">
|
||||
<!-- Bar 1 -->
|
||||
<text x="0" y="12" font-size="11" class="secondary-text">1. How do I reset...</text>
|
||||
<rect x="0" y="18" width="200" height="16" rx="3" class="bar"/>
|
||||
|
||||
<!-- Bar 2 -->
|
||||
<text x="0" y="52" font-size="11" class="secondary-text">2. What is the status...</text>
|
||||
<rect x="0" y="58" width="160" height="16" rx="3" class="bar"/>
|
||||
|
||||
<!-- Bar 3 -->
|
||||
<text x="0" y="92" font-size="11" class="secondary-text">3. Where can I find...</text>
|
||||
<rect x="0" y="98" width="130" height="16" rx="3" class="bar"/>
|
||||
|
||||
<!-- Bar 4 -->
|
||||
<text x="0" y="132" font-size="11" class="secondary-text">4. Help with login</text>
|
||||
<rect x="0" y="138" width="100" height="16" rx="3" class="bar"/>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Quick Stats Row -->
|
||||
<g transform="translate(50, 435)">
|
||||
<rect x="0" y="0" width="785" height="45" rx="6" class="card"/>
|
||||
|
||||
<text x="20" y="28" font-size="12" class="secondary-text">📈 Peak Hour:</text>
|
||||
<text x="100" y="28" font-size="12" font-weight="500" class="main-text">2:00 PM</text>
|
||||
|
||||
<line x1="160" y1="10" x2="160" y2="35" class="divider"/>
|
||||
|
||||
<text x="180" y="28" font-size="12" class="secondary-text">🔝 Top Intent:</text>
|
||||
<text x="260" y="28" font-size="12" font-weight="500" class="main-text">Support Query</text>
|
||||
|
||||
<line x1="370" y1="10" x2="370" y2="35" class="divider"/>
|
||||
|
||||
<text x="390" y="28" font-size="12" class="secondary-text">⚡ Avg Session:</text>
|
||||
<text x="480" y="28" font-size="12" font-weight="500" class="main-text">4.2 min</text>
|
||||
|
||||
<line x1="540" y1="10" x2="540" y2="35" class="divider"/>
|
||||
|
||||
<text x="560" y="28" font-size="12" class="secondary-text">🎯 Resolution:</text>
|
||||
<text x="640" y="28" font-size="12" font-weight="500" class="main-text">94%</text>
|
||||
|
||||
<!-- Export Button -->
|
||||
<rect x="700" y="8" width="70" height="28" rx="4" class="button"/>
|
||||
<text x="735" y="27" text-anchor="middle" font-size="11" class="button-text">Export</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<text x="0" y="0" font-size="11" class="secondary-text">Metrics: Real-time KPIs</text>
|
||||
<text x="150" y="0" font-size="11" class="secondary-text">Charts: Trends & Distribution</text>
|
||||
<text x="350" y="0" font-size="11" class="secondary-text">Time Range: Last 24h, 7d, 30d, Custom</text>
|
||||
<text x="600" y="0" font-size="11" class="secondary-text">Shortcuts: R = Refresh | E = Export</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.9 KiB |
211
docs/src/assets/chapter-04/calendar-interface.svg
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.today { fill: #4A90E2; }
|
||||
.event-bg { fill: #4A90E2; opacity: 0.8; }
|
||||
.event-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.weekend { fill: #f0f0f0; }
|
||||
.cell-border { stroke: #e0e0e0; stroke-width: 1; fill: none; }
|
||||
.nav-btn { fill: #f0f0f0; stroke: #ccc; stroke-width: 1; }
|
||||
.view-active { fill: #4A90E2; }
|
||||
.view-inactive { fill: #e0e0e0; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.today { fill: #00D4FF; }
|
||||
.event-bg { fill: #00D4FF; opacity: 0.8; }
|
||||
.weekend { fill: #252525; }
|
||||
.cell-border { stroke: #444; }
|
||||
.nav-btn { fill: #333; stroke: #555; }
|
||||
.button { fill: #00D4FF; }
|
||||
.view-active { fill: #00D4FF; }
|
||||
.view-inactive { fill: #333; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Calendar - Scheduling Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Header -->
|
||||
<rect x="30" y="50" width="840" height="50" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="92" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- Navigation -->
|
||||
<g transform="translate(50, 62)">
|
||||
<!-- Prev/Next Buttons -->
|
||||
<rect x="0" y="0" width="30" height="30" rx="4" class="nav-btn"/>
|
||||
<text x="15" y="21" text-anchor="middle" font-size="16" class="icon-text">◄</text>
|
||||
|
||||
<rect x="40" y="0" width="30" height="30" rx="4" class="nav-btn"/>
|
||||
<text x="55" y="21" text-anchor="middle" font-size="16" class="icon-text">►</text>
|
||||
|
||||
<!-- Month/Year -->
|
||||
<text x="100" y="22" font-size="18" font-weight="600" class="main-text">March 2024</text>
|
||||
</g>
|
||||
|
||||
<!-- View Toggle -->
|
||||
<g transform="translate(650, 62)">
|
||||
<rect x="0" y="0" width="50" height="30" rx="4" class="view-inactive"/>
|
||||
<text x="25" y="20" text-anchor="middle" font-size="12" class="main-text">Day</text>
|
||||
|
||||
<rect x="55" y="0" width="55" height="30" rx="4" class="view-inactive"/>
|
||||
<text x="82" y="20" text-anchor="middle" font-size="12" class="main-text">Week</text>
|
||||
|
||||
<rect x="115" y="0" width="60" height="30" rx="4" class="view-active"/>
|
||||
<text x="145" y="20" text-anchor="middle" font-size="12" fill="#fff">Month</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="30" y1="100" x2="870" y2="100" class="divider"/>
|
||||
|
||||
<!-- Calendar Grid -->
|
||||
<g transform="translate(50, 110)">
|
||||
<!-- Day Headers -->
|
||||
<g>
|
||||
<text x="55" y="20" text-anchor="middle" font-size="13" font-weight="500" class="secondary-text">Mon</text>
|
||||
<text x="170" y="20" text-anchor="middle" font-size="13" font-weight="500" class="secondary-text">Tue</text>
|
||||
<text x="285" y="20" text-anchor="middle" font-size="13" font-weight="500" class="secondary-text">Wed</text>
|
||||
<text x="400" y="20" text-anchor="middle" font-size="13" font-weight="500" class="secondary-text">Thu</text>
|
||||
<text x="515" y="20" text-anchor="middle" font-size="13" font-weight="500" class="secondary-text">Fri</text>
|
||||
<text x="630" y="20" text-anchor="middle" font-size="13" font-weight="500" class="secondary-text">Sat</text>
|
||||
<text x="745" y="20" text-anchor="middle" font-size="13" font-weight="500" class="secondary-text">Sun</text>
|
||||
</g>
|
||||
|
||||
<!-- Grid Lines and Cells -->
|
||||
<!-- Row 1 -->
|
||||
<g transform="translate(0, 35)">
|
||||
<rect x="0" y="0" width="115" height="65" class="cell-border"/>
|
||||
<rect x="115" y="0" width="115" height="65" class="cell-border"/>
|
||||
<rect x="230" y="0" width="115" height="65" class="cell-border"/>
|
||||
<rect x="345" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="435" y="20" text-anchor="middle" font-size="14" class="main-text">1</text>
|
||||
<rect x="460" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="550" y="20" text-anchor="middle" font-size="14" class="main-text">2</text>
|
||||
<rect x="575" y="0" width="115" height="65" class="cell-border weekend"/>
|
||||
<text x="665" y="20" text-anchor="middle" font-size="14" class="secondary-text">3</text>
|
||||
<rect x="690" y="0" width="110" height="65" class="cell-border weekend"/>
|
||||
</g>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<g transform="translate(0, 100)">
|
||||
<rect x="0" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="15" y="20" font-size="14" class="main-text">4</text>
|
||||
|
||||
<rect x="115" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="130" y="20" font-size="14" class="main-text">5</text>
|
||||
<!-- Event -->
|
||||
<rect x="120" y="28" width="105" height="22" rx="3" class="event-bg"/>
|
||||
<text x="125" y="43" font-size="11" class="event-text">Team Meeting</text>
|
||||
|
||||
<rect x="230" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="245" y="20" font-size="14" class="main-text">6</text>
|
||||
|
||||
<rect x="345" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="360" y="20" font-size="14" class="main-text">7</text>
|
||||
|
||||
<rect x="460" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="475" y="20" font-size="14" class="main-text">8</text>
|
||||
|
||||
<rect x="575" y="0" width="115" height="65" class="cell-border weekend"/>
|
||||
<text x="590" y="20" font-size="14" class="secondary-text">9</text>
|
||||
|
||||
<rect x="690" y="0" width="110" height="65" class="cell-border weekend"/>
|
||||
<text x="705" y="20" font-size="14" class="secondary-text">10</text>
|
||||
</g>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<g transform="translate(0, 165)">
|
||||
<rect x="0" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="15" y="20" font-size="14" class="main-text">11</text>
|
||||
|
||||
<rect x="115" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="130" y="20" font-size="14" class="main-text">12</text>
|
||||
|
||||
<rect x="230" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="245" y="20" font-size="14" class="main-text">13</text>
|
||||
<!-- Event -->
|
||||
<rect x="235" y="28" width="105" height="22" rx="3" class="event-bg"/>
|
||||
<text x="240" y="43" font-size="11" class="event-text">Project Review</text>
|
||||
|
||||
<rect x="345" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="360" y="20" font-size="14" class="main-text">14</text>
|
||||
<!-- Event -->
|
||||
<rect x="350" y="28" width="105" height="22" rx="3" class="event-bg"/>
|
||||
<text x="355" y="43" font-size="11" class="event-text">1:1 Meeting</text>
|
||||
|
||||
<rect x="460" y="0" width="115" height="65" class="cell-border"/>
|
||||
<!-- Today -->
|
||||
<circle cx="485" cy="18" r="14" class="today"/>
|
||||
<text x="485" y="23" text-anchor="middle" font-size="14" fill="#fff">15</text>
|
||||
|
||||
<rect x="575" y="0" width="115" height="65" class="cell-border weekend"/>
|
||||
<text x="590" y="20" font-size="14" class="secondary-text">16</text>
|
||||
|
||||
<rect x="690" y="0" width="110" height="65" class="cell-border weekend"/>
|
||||
<text x="705" y="20" font-size="14" class="secondary-text">17</text>
|
||||
</g>
|
||||
|
||||
<!-- Row 4 -->
|
||||
<g transform="translate(0, 230)">
|
||||
<rect x="0" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="15" y="20" font-size="14" class="main-text">18</text>
|
||||
|
||||
<rect x="115" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="130" y="20" font-size="14" class="main-text">19</text>
|
||||
|
||||
<rect x="230" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="245" y="20" font-size="14" class="main-text">20</text>
|
||||
|
||||
<rect x="345" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="360" y="20" font-size="14" class="main-text">21</text>
|
||||
|
||||
<rect x="460" y="0" width="115" height="65" class="cell-border"/>
|
||||
<text x="475" y="20" font-size="14" class="main-text">22</text>
|
||||
|
||||
<rect x="575" y="0" width="115" height="65" class="cell-border weekend"/>
|
||||
<text x="590" y="20" font-size="14" class="secondary-text">23</text>
|
||||
|
||||
<rect x="690" y="0" width="110" height="65" class="cell-border weekend"/>
|
||||
<text x="705" y="20" font-size="14" class="secondary-text">24</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Add Event Button -->
|
||||
<g transform="translate(750, 470)">
|
||||
<circle cx="30" cy="30" r="28" class="button"/>
|
||||
<text x="30" y="38" text-anchor="middle" font-size="28" fill="#fff">+</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<circle cx="10" cy="-4" r="8" class="today"/>
|
||||
<text x="25" y="0" font-size="11" class="secondary-text">Today</text>
|
||||
|
||||
<rect x="80" y="-10" width="40" height="14" rx="3" class="event-bg"/>
|
||||
<text x="130" y="0" font-size="11" class="secondary-text">Event</text>
|
||||
|
||||
<rect x="200" y="-10" width="20" height="14" class="weekend"/>
|
||||
<text x="230" y="0" font-size="11" class="secondary-text">Weekend</text>
|
||||
|
||||
<text x="400" y="0" font-size="11" class="secondary-text">Shortcuts: N = New event | T = Today | ← → = Navigate | D/W/M = View</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
200
docs/src/assets/chapter-04/compliance-interface.svg
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.button-secondary { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.card { fill: #fff; stroke: #ddd; stroke-width: 1; }
|
||||
.severity-critical { fill: #E74C3C; }
|
||||
.severity-high { fill: #F5A623; }
|
||||
.severity-medium { fill: #F1C40F; }
|
||||
.severity-low { fill: #7ED321; }
|
||||
.severity-info { fill: #4A90E2; }
|
||||
.severity-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.row-bg { fill: #fff; }
|
||||
.row-hover { fill: #fef6f6; }
|
||||
.fix-btn { fill: #E74C3C; }
|
||||
.review-btn { fill: #F5A623; }
|
||||
.table-header { fill: #f5f5f5; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.card { fill: #2a2a2a; stroke: #444; }
|
||||
.row-bg { fill: #2a2a2a; }
|
||||
.row-hover { fill: #3a2a2a; }
|
||||
.table-header { fill: #333; }
|
||||
.button { fill: #00D4FF; }
|
||||
.button-secondary { fill: #333; stroke: #555; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Compliance - Security Scanner Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Header -->
|
||||
<rect x="30" y="50" width="840" height="50" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="92" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- Title and Controls -->
|
||||
<text x="55" y="82" font-size="16" font-weight="600" class="main-text">🛡 Compliance Scanner</text>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<g transform="translate(600, 60)">
|
||||
<rect x="0" y="0" width="80" height="32" rx="4" class="button"/>
|
||||
<text x="40" y="21" text-anchor="middle" font-size="13" class="button-text">Scan</text>
|
||||
|
||||
<rect x="90" y="0" width="80" height="32" rx="4" class="button-secondary"/>
|
||||
<text x="130" y="21" text-anchor="middle" font-size="13" class="main-text">Export</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="30" y1="100" x2="870" y2="100" class="divider"/>
|
||||
|
||||
<!-- Severity Summary Cards -->
|
||||
<g transform="translate(50, 115)">
|
||||
<!-- Critical -->
|
||||
<rect x="0" y="0" width="145" height="70" rx="8" class="card"/>
|
||||
<rect x="10" y="15" width="50" height="40" rx="6" class="severity-critical"/>
|
||||
<text x="35" y="42" text-anchor="middle" font-size="20" font-weight="bold" class="severity-text">2</text>
|
||||
<text x="75" y="35" font-size="12" font-weight="500" class="main-text">Critical</text>
|
||||
<text x="75" y="52" font-size="11" class="secondary-text">🔴 Immediate</text>
|
||||
|
||||
<!-- High -->
|
||||
<rect x="160" y="0" width="145" height="70" rx="8" class="card"/>
|
||||
<rect x="170" y="15" width="50" height="40" rx="6" class="severity-high"/>
|
||||
<text x="195" y="42" text-anchor="middle" font-size="20" font-weight="bold" class="severity-text">5</text>
|
||||
<text x="235" y="35" font-size="12" font-weight="500" class="main-text">High</text>
|
||||
<text x="235" y="52" font-size="11" class="secondary-text">🟠 Priority</text>
|
||||
|
||||
<!-- Medium -->
|
||||
<rect x="320" y="0" width="145" height="70" rx="8" class="card"/>
|
||||
<rect x="330" y="15" width="50" height="40" rx="6" class="severity-medium"/>
|
||||
<text x="355" y="42" text-anchor="middle" font-size="20" font-weight="bold" class="severity-text">3</text>
|
||||
<text x="395" y="35" font-size="12" font-weight="500" class="main-text">Medium</text>
|
||||
<text x="395" y="52" font-size="11" class="secondary-text">🟡 Review</text>
|
||||
|
||||
<!-- Low -->
|
||||
<rect x="480" y="0" width="145" height="70" rx="8" class="card"/>
|
||||
<rect x="490" y="15" width="50" height="40" rx="6" class="severity-low"/>
|
||||
<text x="515" y="42" text-anchor="middle" font-size="20" font-weight="bold" class="severity-text">1</text>
|
||||
<text x="555" y="35" font-size="12" font-weight="500" class="main-text">Low</text>
|
||||
<text x="555" y="52" font-size="11" class="secondary-text">🟢 Minor</text>
|
||||
|
||||
<!-- Info -->
|
||||
<rect x="640" y="0" width="145" height="70" rx="8" class="card"/>
|
||||
<rect x="650" y="15" width="50" height="40" rx="6" class="severity-info"/>
|
||||
<text x="675" y="42" text-anchor="middle" font-size="20" font-weight="bold" class="severity-text">0</text>
|
||||
<text x="715" y="35" font-size="12" font-weight="500" class="main-text">Info</text>
|
||||
<text x="715" y="52" font-size="11" class="secondary-text">ℹ Notes</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="50" y1="200" x2="850" y2="200" class="divider"/>
|
||||
|
||||
<!-- Issues Table -->
|
||||
<g transform="translate(50, 210)">
|
||||
<!-- Table Header -->
|
||||
<rect x="0" y="0" width="800" height="35" rx="4" class="table-header"/>
|
||||
<text x="20" y="23" font-size="12" font-weight="500" class="secondary-text">Severity</text>
|
||||
<text x="120" y="23" font-size="12" font-weight="500" class="secondary-text">Issue</text>
|
||||
<text x="450" y="23" font-size="12" font-weight="500" class="secondary-text">File</text>
|
||||
<text x="650" y="23" font-size="12" font-weight="500" class="secondary-text">Action</text>
|
||||
|
||||
<!-- Row 1 - Critical -->
|
||||
<rect x="0" y="40" width="800" height="45" rx="4" class="row-hover"/>
|
||||
<rect x="15" y="52" width="70" height="22" rx="4" class="severity-critical"/>
|
||||
<text x="50" y="68" text-anchor="middle" font-size="11" class="severity-text">Critical</text>
|
||||
<text x="120" y="68" font-size="13" class="main-text">Hardcoded password detected</text>
|
||||
<text x="450" y="68" font-size="12" font-family="monospace" class="secondary-text">start.bas:15</text>
|
||||
<rect x="650" y="52" width="55" height="24" rx="4" class="fix-btn"/>
|
||||
<text x="677" y="69" text-anchor="middle" font-size="11" class="severity-text">Fix</text>
|
||||
|
||||
<!-- Row 2 - Critical -->
|
||||
<rect x="0" y="90" width="800" height="45" rx="4" class="row-hover"/>
|
||||
<rect x="15" y="102" width="70" height="22" rx="4" class="severity-critical"/>
|
||||
<text x="50" y="118" text-anchor="middle" font-size="11" class="severity-text">Critical</text>
|
||||
<text x="120" y="118" font-size="13" class="main-text">API key exposed in code</text>
|
||||
<text x="450" y="118" font-size="12" font-family="monospace" class="secondary-text">api.bas:42</text>
|
||||
<rect x="650" y="102" width="55" height="24" rx="4" class="fix-btn"/>
|
||||
<text x="677" y="119" text-anchor="middle" font-size="11" class="severity-text">Fix</text>
|
||||
|
||||
<!-- Row 3 - High -->
|
||||
<rect x="0" y="140" width="800" height="45" rx="4" class="row-bg"/>
|
||||
<rect x="15" y="152" width="70" height="22" rx="4" class="severity-high"/>
|
||||
<text x="50" y="168" text-anchor="middle" font-size="11" class="severity-text">High</text>
|
||||
<text x="120" y="168" font-size="13" class="main-text">SQL injection risk in query</text>
|
||||
<text x="450" y="168" font-size="12" font-family="monospace" class="secondary-text">data.bas:28</text>
|
||||
<rect x="650" y="152" width="65" height="24" rx="4" class="review-btn"/>
|
||||
<text x="682" y="169" text-anchor="middle" font-size="11" class="severity-text">Review</text>
|
||||
|
||||
<!-- Row 4 - High -->
|
||||
<rect x="0" y="190" width="800" height="45" rx="4" class="row-bg"/>
|
||||
<rect x="15" y="202" width="70" height="22" rx="4" class="severity-high"/>
|
||||
<text x="50" y="218" text-anchor="middle" font-size="11" class="severity-text">High</text>
|
||||
<text x="120" y="218" font-size="13" class="main-text">Insecure HTTP endpoint</text>
|
||||
<text x="450" y="218" font-size="12" font-family="monospace" class="secondary-text">config.csv:8</text>
|
||||
<rect x="650" y="202" width="65" height="24" rx="4" class="review-btn"/>
|
||||
<text x="682" y="219" text-anchor="middle" font-size="11" class="severity-text">Review</text>
|
||||
|
||||
<!-- Row 5 - Medium -->
|
||||
<rect x="0" y="240" width="800" height="45" rx="4" class="row-bg"/>
|
||||
<rect x="15" y="252" width="70" height="22" rx="4" class="severity-medium"/>
|
||||
<text x="50" y="268" text-anchor="middle" font-size="11" class="severity-text">Medium</text>
|
||||
<text x="120" y="268" font-size="13" class="main-text">Missing input validation</text>
|
||||
<text x="450" y="268" font-size="12" font-family="monospace" class="secondary-text">form.bas:55</text>
|
||||
<rect x="650" y="252" width="65" height="24" rx="4" class="button-secondary"/>
|
||||
<text x="682" y="269" text-anchor="middle" font-size="11" class="main-text">Review</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer Stats -->
|
||||
<g transform="translate(50, 480)">
|
||||
<text x="0" y="0" font-size="12" class="secondary-text">Last scan: 2 minutes ago</text>
|
||||
<text x="200" y="0" font-size="12" class="secondary-text">Files scanned: 24</text>
|
||||
<text x="380" y="0" font-size="12" class="secondary-text">Total issues: 11</text>
|
||||
|
||||
<!-- Pagination -->
|
||||
<text x="600" y="0" font-size="12" class="secondary-text">Showing 1-5 of 11</text>
|
||||
<rect x="720" y="-15" width="25" height="22" rx="4" class="button-secondary"/>
|
||||
<text x="732" y="2" text-anchor="middle" font-size="12" class="main-text">←</text>
|
||||
<rect x="750" y="-15" width="25" height="22" rx="4" class="button-secondary"/>
|
||||
<text x="762" y="2" text-anchor="middle" font-size="12" class="main-text">→</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<rect x="0" y="-10" width="14" height="14" rx="3" class="severity-critical"/>
|
||||
<text x="20" y="0" font-size="11" class="secondary-text">Critical</text>
|
||||
|
||||
<rect x="70" y="-10" width="14" height="14" rx="3" class="severity-high"/>
|
||||
<text x="90" y="0" font-size="11" class="secondary-text">High</text>
|
||||
|
||||
<rect x="130" y="-10" width="14" height="14" rx="3" class="severity-medium"/>
|
||||
<text x="150" y="0" font-size="11" class="secondary-text">Medium</text>
|
||||
|
||||
<rect x="200" y="-10" width="14" height="14" rx="3" class="severity-low"/>
|
||||
<text x="220" y="0" font-size="11" class="secondary-text">Low</text>
|
||||
|
||||
<text x="300" y="0" font-size="11" class="secondary-text">Fix = Auto-remediate | Review = Manual inspection needed</text>
|
||||
|
||||
<text x="650" y="0" font-size="11" class="secondary-text">LGPD • GDPR • CCPA</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 10 KiB |
258
docs/src/assets/chapter-04/designer-interface.svg
Normal file
|
|
@ -0,0 +1,258 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.canvas-bg { fill: #fff; }
|
||||
.canvas-grid { stroke: #f0f0f0; stroke-width: 1; }
|
||||
.node-talk { fill: #4A90E2; stroke: #3a7bc8; stroke-width: 2; }
|
||||
.node-hear { fill: #7ED321; stroke: #6bc01a; stroke-width: 2; }
|
||||
.node-if { fill: #F5A623; stroke: #d9911f; stroke-width: 2; }
|
||||
.node-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.connector { stroke: #666; stroke-width: 2; fill: none; }
|
||||
.connector-arrow { fill: #666; }
|
||||
.toolbox-item { fill: #fff; stroke: #ddd; stroke-width: 1; }
|
||||
.toolbox-item:hover { fill: #f0f7ff; }
|
||||
.properties-panel { fill: #fafafa; }
|
||||
.section-header { fill: #e8e8e8; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.canvas-bg { fill: #252525; }
|
||||
.canvas-grid { stroke: #333; }
|
||||
.toolbox-item { fill: #2a2a2a; stroke: #444; }
|
||||
.properties-panel { fill: #222; }
|
||||
.section-header { fill: #333; }
|
||||
.connector { stroke: #888; }
|
||||
.connector-arrow { fill: #888; }
|
||||
.button { fill: #00D4FF; }
|
||||
.node-talk { fill: #00D4FF; stroke: #00a8cc; }
|
||||
.node-hear { fill: #00FF88; stroke: #00cc6d; }
|
||||
.node-if { fill: #FF9500; stroke: #cc7700; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<defs>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" class="connector-arrow"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Designer - Visual Builder Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Left Panel - Toolbox -->
|
||||
<rect x="30" y="50" width="120" height="470" rx="8" class="panel-bg"/>
|
||||
<line x1="150" y1="50" x2="150" y2="520" class="divider"/>
|
||||
|
||||
<!-- Toolbox Header -->
|
||||
<rect x="30" y="50" width="120" height="30" rx="8" class="section-header"/>
|
||||
<rect x="30" y="72" width="120" height="8" class="panel-bg"/>
|
||||
<text x="90" y="70" text-anchor="middle" font-size="12" font-weight="500" class="main-text">Toolbox</text>
|
||||
|
||||
<!-- Toolbox Items -->
|
||||
<g transform="translate(40, 90)">
|
||||
<!-- TALK -->
|
||||
<rect x="0" y="0" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="23" font-size="14" class="icon-text">💬</text>
|
||||
<text x="35" y="23" font-size="12" class="main-text">TALK</text>
|
||||
|
||||
<!-- HEAR -->
|
||||
<rect x="0" y="45" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="68" font-size="14" class="icon-text">👂</text>
|
||||
<text x="35" y="68" font-size="12" class="main-text">HEAR</text>
|
||||
|
||||
<!-- SET -->
|
||||
<rect x="0" y="90" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="113" font-size="14" class="icon-text">📝</text>
|
||||
<text x="35" y="113" font-size="12" class="main-text">SET</text>
|
||||
|
||||
<!-- Separator -->
|
||||
<line x1="0" y1="140" x2="100" y2="140" class="divider"/>
|
||||
|
||||
<!-- IF -->
|
||||
<rect x="0" y="155" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="178" font-size="14" class="icon-text">🔀</text>
|
||||
<text x="35" y="178" font-size="12" class="main-text">IF</text>
|
||||
|
||||
<!-- FOR -->
|
||||
<rect x="0" y="200" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="223" font-size="14" class="icon-text">🔄</text>
|
||||
<text x="35" y="223" font-size="12" class="main-text">FOR</text>
|
||||
|
||||
<!-- SWITCH -->
|
||||
<rect x="0" y="245" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="268" font-size="14" class="icon-text">🔃</text>
|
||||
<text x="35" y="268" font-size="12" class="main-text">SWITCH</text>
|
||||
|
||||
<!-- Separator -->
|
||||
<line x1="0" y1="295" x2="100" y2="295" class="divider"/>
|
||||
|
||||
<!-- CALL -->
|
||||
<rect x="0" y="310" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="333" font-size="14" class="icon-text">📞</text>
|
||||
<text x="35" y="333" font-size="12" class="main-text">CALL</text>
|
||||
|
||||
<!-- SEND -->
|
||||
<rect x="0" y="355" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="378" font-size="14" class="icon-text">📧</text>
|
||||
<text x="35" y="378" font-size="12" class="main-text">SEND</text>
|
||||
|
||||
<!-- SAVE -->
|
||||
<rect x="0" y="400" width="100" height="35" rx="4" class="toolbox-item"/>
|
||||
<text x="15" y="423" font-size="14" class="icon-text">💾</text>
|
||||
<text x="35" y="423" font-size="12" class="main-text">SAVE</text>
|
||||
</g>
|
||||
|
||||
<!-- Center Panel - Canvas -->
|
||||
<rect x="150" y="50" width="560" height="470" class="canvas-bg"/>
|
||||
|
||||
<!-- Canvas Grid (subtle) -->
|
||||
<g transform="translate(150, 50)">
|
||||
<line x1="0" y1="100" x2="560" y2="100" class="canvas-grid"/>
|
||||
<line x1="0" y1="200" x2="560" y2="200" class="canvas-grid"/>
|
||||
<line x1="0" y1="300" x2="560" y2="300" class="canvas-grid"/>
|
||||
<line x1="0" y1="400" x2="560" y2="400" class="canvas-grid"/>
|
||||
<line x1="100" y1="0" x2="100" y2="470" class="canvas-grid"/>
|
||||
<line x1="200" y1="0" x2="200" y2="470" class="canvas-grid"/>
|
||||
<line x1="300" y1="0" x2="300" y2="470" class="canvas-grid"/>
|
||||
<line x1="400" y1="0" x2="400" y2="470" class="canvas-grid"/>
|
||||
<line x1="500" y1="0" x2="500" y2="470" class="canvas-grid"/>
|
||||
</g>
|
||||
|
||||
<!-- Flow Diagram on Canvas -->
|
||||
<g transform="translate(200, 80)">
|
||||
<!-- Node 1: TALK "Hello!" -->
|
||||
<rect x="0" y="0" width="120" height="60" rx="8" class="node-talk"/>
|
||||
<text x="20" y="25" font-size="14" class="node-text">💬 TALK</text>
|
||||
<text x="20" y="45" font-size="12" class="node-text">"Hello!"</text>
|
||||
|
||||
<!-- Connector 1 to 2 -->
|
||||
<line x1="60" y1="60" x2="60" y2="100" class="connector" marker-end="url(#arrowhead)"/>
|
||||
|
||||
<!-- Node 2: HEAR name -->
|
||||
<rect x="0" y="110" width="120" height="60" rx="8" class="node-hear"/>
|
||||
<text x="20" y="135" font-size="14" class="node-text">👂 HEAR</text>
|
||||
<text x="20" y="155" font-size="12" class="node-text">name</text>
|
||||
|
||||
<!-- Connector 2 to 3 -->
|
||||
<line x1="120" y1="140" x2="180" y2="140" class="connector" marker-end="url(#arrowhead)"/>
|
||||
|
||||
<!-- Node 3: IF condition -->
|
||||
<rect x="190" y="110" width="140" height="60" rx="8" class="node-if"/>
|
||||
<text x="210" y="135" font-size="14" class="node-text">🔀 IF</text>
|
||||
<text x="210" y="155" font-size="12" class="node-text">name = "Jo"</text>
|
||||
|
||||
<!-- Branch Labels -->
|
||||
<text x="220" y="195" font-size="11" class="secondary-text">Yes</text>
|
||||
<text x="310" y="195" font-size="11" class="secondary-text">No</text>
|
||||
|
||||
<!-- Connector to Yes branch -->
|
||||
<path d="M220 170 L220 200 L220 230" class="connector" marker-end="url(#arrowhead)"/>
|
||||
|
||||
<!-- Connector to No branch -->
|
||||
<path d="M310 170 L310 200 L310 230" class="connector" marker-end="url(#arrowhead)"/>
|
||||
|
||||
<!-- Node 4a: TALK "Hi Jo!" -->
|
||||
<rect x="160" y="240" width="120" height="60" rx="8" class="node-talk"/>
|
||||
<text x="180" y="265" font-size="14" class="node-text">💬 TALK</text>
|
||||
<text x="180" y="285" font-size="12" class="node-text">"Hi Jo!"</text>
|
||||
|
||||
<!-- Node 4b: TALK "Hello!" -->
|
||||
<rect x="290" y="240" width="120" height="60" rx="8" class="node-talk"/>
|
||||
<text x="310" y="265" font-size="14" class="node-text">💬 TALK</text>
|
||||
<text x="310" y="285" font-size="12" class="node-text">"Hello!"</text>
|
||||
</g>
|
||||
|
||||
<!-- Right Panel - Properties -->
|
||||
<rect x="710" y="50" width="160" height="470" rx="8" class="properties-panel"/>
|
||||
<line x1="710" y1="50" x2="710" y2="520" class="divider"/>
|
||||
|
||||
<!-- Properties Header -->
|
||||
<rect x="710" y="50" width="160" height="30" rx="8" class="section-header"/>
|
||||
<rect x="710" y="72" width="160" height="8" class="properties-panel"/>
|
||||
<text x="790" y="70" text-anchor="middle" font-size="12" font-weight="500" class="main-text">Properties</text>
|
||||
|
||||
<!-- Properties Content -->
|
||||
<g transform="translate(720, 95)">
|
||||
<text x="0" y="0" font-size="12" class="secondary-text">Node:</text>
|
||||
<text x="0" y="20" font-size="14" font-weight="500" class="main-text">TALK</text>
|
||||
|
||||
<line x1="0" y1="35" x2="140" y2="35" class="divider"/>
|
||||
|
||||
<text x="0" y="55" font-size="12" class="secondary-text">Message:</text>
|
||||
<rect x="0" y="65" width="140" height="60" rx="4" class="input-field"/>
|
||||
<text x="10" y="85" font-size="12" class="main-text">"Hello!"</text>
|
||||
|
||||
<line x1="0" y1="140" x2="140" y2="140" class="divider"/>
|
||||
|
||||
<text x="0" y="160" font-size="12" class="secondary-text">Voice:</text>
|
||||
<rect x="0" y="170" width="140" height="30" rx="4" class="input-field"/>
|
||||
<text x="10" y="190" font-size="12" class="secondary-text">Default ▼</text>
|
||||
|
||||
<text x="0" y="220" font-size="12" class="secondary-text">Delay:</text>
|
||||
<rect x="0" y="230" width="140" height="30" rx="4" class="input-field"/>
|
||||
<text x="10" y="250" font-size="12" class="main-text">0ms</text>
|
||||
|
||||
<line x1="0" y1="275" x2="140" y2="275" class="divider"/>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<rect x="0" y="290" width="65" height="30" rx="4" class="button"/>
|
||||
<text x="32" y="310" text-anchor="middle" font-size="11" class="button-text">Apply</text>
|
||||
|
||||
<rect x="75" y="290" width="65" height="30" rx="4" class="input-field"/>
|
||||
<text x="107" y="310" text-anchor="middle" font-size="11" class="main-text">Delete</text>
|
||||
</g>
|
||||
|
||||
<!-- Canvas Controls (bottom) -->
|
||||
<g transform="translate(160, 490)">
|
||||
<rect x="0" y="0" width="30" height="25" rx="4" class="input-field"/>
|
||||
<text x="15" y="17" text-anchor="middle" font-size="14" class="icon-text">+</text>
|
||||
|
||||
<rect x="35" y="0" width="30" height="25" rx="4" class="input-field"/>
|
||||
<text x="50" y="17" text-anchor="middle" font-size="14" class="icon-text">−</text>
|
||||
|
||||
<rect x="70" y="0" width="30" height="25" rx="4" class="input-field"/>
|
||||
<text x="85" y="17" text-anchor="middle" font-size="12" class="icon-text">⌖</text>
|
||||
|
||||
<text x="120" y="17" font-size="11" class="secondary-text">100%</text>
|
||||
|
||||
<rect x="480" y="0" width="60" height="25" rx="4" class="button"/>
|
||||
<text x="510" y="17" text-anchor="middle" font-size="11" class="button-text">▶ Run</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<rect x="0" y="-10" width="20" height="14" rx="3" class="node-talk"/>
|
||||
<text x="28" y="0" font-size="11" class="secondary-text">Talk</text>
|
||||
|
||||
<rect x="60" y="-10" width="20" height="14" rx="3" class="node-hear"/>
|
||||
<text x="88" y="0" font-size="11" class="secondary-text">Hear</text>
|
||||
|
||||
<rect x="120" y="-10" width="20" height="14" rx="3" class="node-if"/>
|
||||
<text x="148" y="0" font-size="11" class="secondary-text">Condition</text>
|
||||
|
||||
<text x="250" y="0" font-size="11" class="secondary-text">Drag nodes from Toolbox → Canvas | Connect by dragging</text>
|
||||
|
||||
<text x="600" y="0" font-size="11" class="secondary-text">Shortcuts: Delete | Ctrl+D = Duplicate | Ctrl+Z = Undo</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
155
docs/src/assets/chapter-04/drive-interface.svg
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.row-hover { fill: #f0f7ff; }
|
||||
.folder-icon { fill: #F5A623; }
|
||||
.file-icon { fill: #4A90E2; }
|
||||
.image-icon { fill: #7ED321; }
|
||||
.sidebar-item { fill: none; }
|
||||
.sidebar-item:hover { fill: #e8e8e8; }
|
||||
.label-blue { fill: #4A90E2; }
|
||||
.label-green { fill: #7ED321; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.row-hover { fill: #2a3a4a; }
|
||||
.button { fill: #00D4FF; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Drive - File Management Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<rect x="30" y="50" width="840" height="45" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="87" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- New Button -->
|
||||
<rect x="50" y="60" width="80" height="28" rx="4" class="button"/>
|
||||
<text x="90" y="79" text-anchor="middle" font-size="13" class="button-text">+ New ▼</text>
|
||||
|
||||
<!-- Search Bar -->
|
||||
<rect x="300" y="60" width="250" height="28" rx="14" class="input-field"/>
|
||||
<text x="320" y="79" font-size="13" class="secondary-text">🔍 Search files...</text>
|
||||
|
||||
<!-- View Toggle -->
|
||||
<g transform="translate(750, 60)">
|
||||
<rect x="0" y="0" width="30" height="28" rx="4" class="input-field"/>
|
||||
<text x="15" y="19" text-anchor="middle" font-size="14" class="icon-text">⊞</text>
|
||||
<rect x="35" y="0" width="30" height="28" rx="4" class="input-field"/>
|
||||
<text x="50" y="19" text-anchor="middle" font-size="14" class="icon-text">≡</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="30" y1="95" x2="870" y2="95" class="divider"/>
|
||||
|
||||
<!-- Left Sidebar -->
|
||||
<rect x="30" y="95" width="140" height="425" class="panel-bg"/>
|
||||
<line x1="170" y1="95" x2="170" y2="520" class="divider"/>
|
||||
|
||||
<!-- Sidebar Items -->
|
||||
<g transform="translate(45, 110)">
|
||||
<rect x="-5" y="-5" width="120" height="28" rx="4" class="row-hover"/>
|
||||
<text x="0" y="14" font-size="14" class="main-text">📁 My Drive</text>
|
||||
|
||||
<text x="0" y="50" font-size="14" class="secondary-text">⭐ Starred</text>
|
||||
<text x="0" y="80" font-size="14" class="secondary-text">🕐 Recent</text>
|
||||
<text x="0" y="110" font-size="14" class="secondary-text">🗑 Trash</text>
|
||||
|
||||
<line x1="-5" y1="130" x2="115" y2="130" class="divider"/>
|
||||
|
||||
<text x="0" y="155" font-size="12" font-weight="500" class="secondary-text">Labels</text>
|
||||
<circle cx="10" cy="175" r="6" class="label-blue"/>
|
||||
<text x="25" y="180" font-size="13" class="main-text">Work</text>
|
||||
<circle cx="10" cy="200" r="6" class="label-green"/>
|
||||
<text x="25" y="205" font-size="13" class="main-text">Personal</text>
|
||||
</g>
|
||||
|
||||
<!-- Breadcrumb -->
|
||||
<text x="190" y="120" font-size="13" class="secondary-text">📁 My Drive › Projects › 2024</text>
|
||||
|
||||
<!-- Column Headers -->
|
||||
<g transform="translate(185, 140)">
|
||||
<rect x="0" y="0" width="680" height="25" class="header-bg"/>
|
||||
<text x="30" y="17" font-size="12" font-weight="500" class="secondary-text">☐</text>
|
||||
<text x="60" y="17" font-size="12" font-weight="500" class="secondary-text">Name</text>
|
||||
<text x="400" y="17" font-size="12" font-weight="500" class="secondary-text">Size</text>
|
||||
<text x="520" y="17" font-size="12" font-weight="500" class="secondary-text">Modified</text>
|
||||
</g>
|
||||
|
||||
<!-- File List -->
|
||||
<g transform="translate(185, 170)">
|
||||
<!-- Row 1 - Folder -->
|
||||
<rect x="0" y="0" width="680" height="35" class="row-hover" opacity="0.5"/>
|
||||
<text x="30" y="23" font-size="14" class="secondary-text">☐</text>
|
||||
<text x="55" y="23" font-size="16" class="folder-icon">📁</text>
|
||||
<text x="80" y="23" font-size="14" class="main-text">Reports</text>
|
||||
<text x="400" y="23" font-size="13" class="secondary-text">—</text>
|
||||
<text x="520" y="23" font-size="13" class="secondary-text">Today</text>
|
||||
|
||||
<!-- Row 2 - Folder -->
|
||||
<rect x="0" y="40" width="680" height="35" fill="none"/>
|
||||
<text x="30" y="63" font-size="14" class="secondary-text">☐</text>
|
||||
<text x="55" y="63" font-size="16" class="folder-icon">📁</text>
|
||||
<text x="80" y="63" font-size="14" class="main-text">Presentations</text>
|
||||
<text x="400" y="63" font-size="13" class="secondary-text">—</text>
|
||||
<text x="520" y="63" font-size="13" class="secondary-text">Yesterday</text>
|
||||
|
||||
<!-- Row 3 - Excel File -->
|
||||
<rect x="0" y="80" width="680" height="35" fill="none"/>
|
||||
<text x="30" y="103" font-size="14" class="secondary-text">☐</text>
|
||||
<text x="55" y="103" font-size="16" class="file-icon">📄</text>
|
||||
<text x="80" y="103" font-size="14" class="main-text">Budget.xlsx</text>
|
||||
<text x="400" y="103" font-size="13" class="secondary-text">245 KB</text>
|
||||
<text x="520" y="103" font-size="13" class="secondary-text">Mar 15</text>
|
||||
|
||||
<!-- Row 4 - Doc File -->
|
||||
<rect x="0" y="120" width="680" height="35" fill="none"/>
|
||||
<text x="30" y="143" font-size="14" class="secondary-text">☐</text>
|
||||
<text x="55" y="143" font-size="16" class="file-icon">📄</text>
|
||||
<text x="80" y="143" font-size="14" class="main-text">Notes.docx</text>
|
||||
<text x="400" y="143" font-size="13" class="secondary-text">12 KB</text>
|
||||
<text x="520" y="143" font-size="13" class="secondary-text">Mar 14</text>
|
||||
|
||||
<!-- Row 5 - Image File -->
|
||||
<rect x="0" y="160" width="680" height="35" fill="none"/>
|
||||
<text x="30" y="183" font-size="14" class="secondary-text">☐</text>
|
||||
<text x="55" y="183" font-size="16" class="image-icon">🖼</text>
|
||||
<text x="80" y="183" font-size="14" class="main-text">Logo.png</text>
|
||||
<text x="400" y="183" font-size="13" class="secondary-text">89 KB</text>
|
||||
<text x="520" y="183" font-size="13" class="secondary-text">Mar 10</text>
|
||||
</g>
|
||||
|
||||
<!-- Drag & Drop Zone -->
|
||||
<rect x="185" y="380" width="680" height="100" rx="8" fill="none" stroke="#4A90E2" stroke-width="2" stroke-dasharray="8,4"/>
|
||||
<text x="525" y="425" text-anchor="middle" font-size="16" class="secondary-text">📤 Drag files here to upload</text>
|
||||
<text x="525" y="450" text-anchor="middle" font-size="13" class="secondary-text">or click + New to create</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<text x="0" y="0" font-size="11" class="secondary-text">Sidebar: Quick access, Labels</text>
|
||||
<text x="200" y="0" font-size="11" class="secondary-text">Main: File browser with sort columns</text>
|
||||
<text x="500" y="0" font-size="11" class="secondary-text">Actions: Upload, Create, Search, View toggle</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.3 KiB |
147
docs/src/assets/chapter-04/mail-interface.svg
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.unread-dot { fill: #4A90E2; }
|
||||
.selected-row { fill: #e8f4fd; }
|
||||
.row-bg { fill: #fff; }
|
||||
.sidebar-active { fill: #e8f4fd; }
|
||||
.badge { fill: #4A90E2; }
|
||||
.action-btn { fill: #f0f0f0; stroke: #ccc; stroke-width: 1; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.unread-dot { fill: #00D4FF; }
|
||||
.selected-row { fill: #2a3a4a; }
|
||||
.row-bg { fill: #222; }
|
||||
.sidebar-active { fill: #2a3a4a; }
|
||||
.badge { fill: #00D4FF; }
|
||||
.button { fill: #00D4FF; }
|
||||
.action-btn { fill: #333; stroke: #555; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Mail - Email Client Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Left Sidebar -->
|
||||
<rect x="30" y="50" width="130" height="470" rx="8" class="panel-bg"/>
|
||||
<rect x="152" y="50" width="8" height="470" class="bg"/>
|
||||
|
||||
<!-- Compose Button -->
|
||||
<rect x="45" y="65" width="100" height="35" rx="6" class="button"/>
|
||||
<text x="95" y="88" text-anchor="middle" font-size="13" class="button-text">✏ Compose</text>
|
||||
|
||||
<!-- Sidebar Items -->
|
||||
<g transform="translate(45, 120)">
|
||||
<!-- Inbox - Active -->
|
||||
<rect x="-5" y="-5" width="110" height="30" rx="4" class="sidebar-active"/>
|
||||
<text x="0" y="14" font-size="14" class="main-text">📥 Inbox</text>
|
||||
<rect x="75" y="2" width="22" height="16" rx="8" class="badge"/>
|
||||
<text x="86" y="14" text-anchor="middle" font-size="11" fill="#fff">3</text>
|
||||
|
||||
<text x="0" y="50" font-size="14" class="secondary-text">📤 Sent</text>
|
||||
<text x="0" y="80" font-size="14" class="secondary-text">📝 Drafts</text>
|
||||
<text x="0" y="110" font-size="14" class="secondary-text">🗑 Trash</text>
|
||||
</g>
|
||||
|
||||
<!-- Middle Panel - Email List -->
|
||||
<rect x="160" y="50" width="220" height="470" class="panel-bg"/>
|
||||
<line x1="380" y1="50" x2="380" y2="520" class="divider"/>
|
||||
|
||||
<!-- List Header -->
|
||||
<rect x="160" y="50" width="220" height="35" class="header-bg"/>
|
||||
<text x="175" y="73" font-size="14" font-weight="500" class="main-text">Inbox</text>
|
||||
|
||||
<!-- Email List Items -->
|
||||
<g transform="translate(160, 90)">
|
||||
<!-- Email 1 - Unread, Selected -->
|
||||
<rect x="0" y="0" width="220" height="70" class="selected-row"/>
|
||||
<circle cx="20" cy="20" r="5" class="unread-dot"/>
|
||||
<text x="35" y="20" font-size="13" font-weight="600" class="main-text">Project Update</text>
|
||||
<text x="35" y="38" font-size="12" class="secondary-text">from John</text>
|
||||
<text x="175" y="20" font-size="11" class="secondary-text">10:30 AM</text>
|
||||
<line x1="0" y1="70" x2="220" y2="70" class="divider"/>
|
||||
|
||||
<!-- Email 2 - Read -->
|
||||
<rect x="0" y="70" width="220" height="70" class="row-bg"/>
|
||||
<circle cx="20" cy="90" r="5" fill="none" stroke="#ccc"/>
|
||||
<text x="35" y="90" font-size="13" class="main-text">Meeting Notes</text>
|
||||
<text x="35" y="108" font-size="12" class="secondary-text">from Sarah</text>
|
||||
<text x="175" y="90" font-size="11" class="secondary-text">Yesterday</text>
|
||||
<line x1="0" y1="140" x2="220" y2="140" class="divider"/>
|
||||
|
||||
<!-- Email 3 - Read -->
|
||||
<rect x="0" y="140" width="220" height="70" class="row-bg"/>
|
||||
<circle cx="20" cy="160" r="5" fill="none" stroke="#ccc"/>
|
||||
<text x="35" y="160" font-size="13" class="main-text">Invoice #1234</text>
|
||||
<text x="35" y="178" font-size="12" class="secondary-text">from Vendor</text>
|
||||
<text x="175" y="160" font-size="11" class="secondary-text">Mar 15</text>
|
||||
<line x1="0" y1="210" x2="220" y2="210" class="divider"/>
|
||||
</g>
|
||||
|
||||
<!-- Right Panel - Email Content -->
|
||||
<rect x="380" y="50" width="490" height="470" rx="8" class="bg"/>
|
||||
|
||||
<!-- Email Header -->
|
||||
<g transform="translate(400, 70)">
|
||||
<text x="0" y="0" font-size="12" class="secondary-text">From:</text>
|
||||
<text x="45" y="0" font-size="12" class="main-text">john@company.com</text>
|
||||
|
||||
<text x="0" y="25" font-size="12" class="secondary-text">Subject:</text>
|
||||
<text x="55" y="25" font-size="14" font-weight="600" class="main-text">Project Update</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="400" y1="115" x2="850" y2="115" class="divider"/>
|
||||
|
||||
<!-- Email Body -->
|
||||
<g transform="translate(400, 135)">
|
||||
<text x="0" y="0" font-size="14" class="main-text">Hi,</text>
|
||||
<text x="0" y="30" font-size="14" class="main-text">Here's the latest update on our project.</text>
|
||||
<text x="0" y="55" font-size="14" class="main-text">We've completed the first milestone and</text>
|
||||
<text x="0" y="80" font-size="14" class="main-text">are now moving to phase two.</text>
|
||||
<text x="0" y="130" font-size="14" class="main-text">Best,</text>
|
||||
<text x="0" y="155" font-size="14" class="main-text">John</text>
|
||||
</g>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<g transform="translate(400, 460)">
|
||||
<rect x="0" y="0" width="80" height="35" rx="6" class="button"/>
|
||||
<text x="40" y="23" text-anchor="middle" font-size="13" class="button-text">Reply</text>
|
||||
|
||||
<rect x="90" y="0" width="90" height="35" rx="6" class="action-btn"/>
|
||||
<text x="135" y="23" text-anchor="middle" font-size="13" class="main-text">Forward</text>
|
||||
|
||||
<rect x="190" y="0" width="80" height="35" rx="6" class="action-btn"/>
|
||||
<text x="230" y="23" text-anchor="middle" font-size="13" class="main-text">Delete</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<text x="0" y="0" font-size="11" class="secondary-text">Sidebar: Folders</text>
|
||||
<text x="150" y="0" font-size="11" class="secondary-text">List: Email threads</text>
|
||||
<text x="320" y="0" font-size="11" class="secondary-text">Content: Read and reply</text>
|
||||
<text x="550" y="0" font-size="11" class="secondary-text">Shortcuts: C = Compose | R = Reply | F = Forward</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
139
docs/src/assets/chapter-04/meet-interface.svg
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #252525; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #aaa; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #ddd; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #444; stroke-width: 1; }
|
||||
.video-frame { fill: #333; stroke: #444; stroke-width: 2; }
|
||||
.avatar-bg { fill: #4A90E2; }
|
||||
.control-btn { fill: #444; stroke: #555; stroke-width: 1; }
|
||||
.control-btn-active { fill: #4A90E2; }
|
||||
.control-btn-danger { fill: #E74C3C; }
|
||||
.control-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.timer { fill: #7ED321; font-family: monospace; }
|
||||
.participant-count { fill: #4A90E2; }
|
||||
.chat-icon { fill: #fff; }
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.bg { fill: #2a2a2a; }
|
||||
.frame { stroke: #444; }
|
||||
.panel-bg { fill: #333; }
|
||||
.header-bg { fill: #3a3a3a; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Meet - Video Calls Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Header Bar -->
|
||||
<rect x="30" y="50" width="840" height="45" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="87" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- Meeting Title -->
|
||||
<text x="55" y="78" font-size="16" font-weight="500" class="main-text">Meeting Room</text>
|
||||
|
||||
<!-- Timer -->
|
||||
<text x="450" y="78" text-anchor="middle" font-size="16" class="timer">00:15:32</text>
|
||||
|
||||
<!-- Header Right Controls -->
|
||||
<g transform="translate(720, 58)">
|
||||
<!-- Participants -->
|
||||
<rect x="0" y="0" width="45" height="30" rx="4" class="control-btn"/>
|
||||
<text x="15" y="20" font-size="14" class="control-text">👥</text>
|
||||
<text x="35" y="20" font-size="12" class="participant-count">3</text>
|
||||
|
||||
<!-- Chat -->
|
||||
<rect x="55" y="0" width="35" height="30" rx="4" class="control-btn"/>
|
||||
<text x="72" y="20" text-anchor="middle" font-size="14" class="chat-icon">💬</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="30" y1="95" x2="870" y2="95" class="divider"/>
|
||||
|
||||
<!-- Video Grid Area -->
|
||||
<g transform="translate(50, 110)">
|
||||
<!-- Main Participant 1 (You) -->
|
||||
<rect x="0" y="0" width="380" height="220" rx="8" class="video-frame"/>
|
||||
<circle cx="190" cy="90" r="45" class="avatar-bg"/>
|
||||
<text x="190" y="100" text-anchor="middle" font-size="32" class="main-text">👤</text>
|
||||
<text x="190" y="150" text-anchor="middle" font-size="16" class="main-text">You</text>
|
||||
<text x="190" y="175" text-anchor="middle" font-size="13" class="secondary-text">(Camera)</text>
|
||||
<!-- Name label -->
|
||||
<rect x="10" y="190" width="60" height="22" rx="4" fill="#000" opacity="0.6"/>
|
||||
<text x="40" y="206" text-anchor="middle" font-size="12" class="main-text">You</text>
|
||||
<!-- Mute indicator -->
|
||||
<circle cx="355" cy="195" r="12" fill="#E74C3C"/>
|
||||
<text x="355" y="200" text-anchor="middle" font-size="10" class="main-text">🎤</text>
|
||||
|
||||
<!-- Main Participant 2 (John) -->
|
||||
<rect x="400" y="0" width="380" height="220" rx="8" class="video-frame"/>
|
||||
<circle cx="590" cy="90" r="45" class="avatar-bg"/>
|
||||
<text x="590" y="100" text-anchor="middle" font-size="32" class="main-text">👤</text>
|
||||
<text x="590" y="150" text-anchor="middle" font-size="16" class="main-text">John</text>
|
||||
<text x="590" y="175" text-anchor="middle" font-size="13" class="secondary-text">(Camera)</text>
|
||||
<!-- Name label -->
|
||||
<rect x="410" y="190" width="60" height="22" rx="4" fill="#000" opacity="0.6"/>
|
||||
<text x="440" y="206" text-anchor="middle" font-size="12" class="main-text">John</text>
|
||||
|
||||
<!-- Participant 3 (Sarah) - Smaller -->
|
||||
<rect x="0" y="235" width="250" height="145" rx="8" class="video-frame"/>
|
||||
<circle cx="125" cy="290" r="35" class="avatar-bg"/>
|
||||
<text x="125" y="298" text-anchor="middle" font-size="26" class="main-text">👤</text>
|
||||
<text x="125" y="340" text-anchor="middle" font-size="14" class="main-text">Sarah</text>
|
||||
<!-- Name label -->
|
||||
<rect x="10" y="355" width="55" height="20" rx="4" fill="#000" opacity="0.6"/>
|
||||
<text x="37" y="369" text-anchor="middle" font-size="11" class="main-text">Sarah</text>
|
||||
</g>
|
||||
|
||||
<!-- Control Bar -->
|
||||
<rect x="30" y="470" width="840" height="50" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="470" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- Control Buttons -->
|
||||
<g transform="translate(200, 480)">
|
||||
<!-- Mute Button -->
|
||||
<rect x="0" y="0" width="55" height="38" rx="6" class="control-btn-danger"/>
|
||||
<text x="27" y="25" text-anchor="middle" font-size="18" class="control-text">🎤</text>
|
||||
|
||||
<!-- Video Button -->
|
||||
<rect x="70" y="0" width="55" height="38" rx="6" class="control-btn-active"/>
|
||||
<text x="97" y="25" text-anchor="middle" font-size="18" class="control-text">📹</text>
|
||||
|
||||
<!-- Share Screen Button -->
|
||||
<rect x="140" y="0" width="55" height="38" rx="6" class="control-btn"/>
|
||||
<text x="167" y="25" text-anchor="middle" font-size="18" class="control-text">🖥</text>
|
||||
|
||||
<!-- Record Button -->
|
||||
<rect x="210" y="0" width="55" height="38" rx="6" class="control-btn"/>
|
||||
<text x="237" y="25" text-anchor="middle" font-size="18" class="control-text">🔴</text>
|
||||
|
||||
<!-- Transcribe Button -->
|
||||
<rect x="280" y="0" width="55" height="38" rx="6" class="control-btn"/>
|
||||
<text x="307" y="25" text-anchor="middle" font-size="18" class="control-text">📝</text>
|
||||
|
||||
<!-- End Call Button -->
|
||||
<rect x="365" y="0" width="70" height="38" rx="6" class="control-btn-danger"/>
|
||||
<text x="400" y="25" text-anchor="middle" font-size="18" class="control-text">📞</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<rect x="0" y="-10" width="20" height="14" rx="3" class="control-btn-active"/>
|
||||
<text x="28" y="0" font-size="11" class="secondary-text">Active</text>
|
||||
|
||||
<rect x="80" y="-10" width="20" height="14" rx="3" class="control-btn-danger"/>
|
||||
<text x="108" y="0" font-size="11" class="secondary-text">Muted/End</text>
|
||||
|
||||
<rect x="180" y="-10" width="20" height="14" rx="3" class="control-btn"/>
|
||||
<text x="208" y="0" font-size="11" class="secondary-text">Available</text>
|
||||
|
||||
<text x="350" y="0" font-size="11" class="secondary-text">Shortcuts: M = Mute | V = Video | S = Share | Space = Push-to-talk</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 6.4 KiB |
179
docs/src/assets/chapter-04/paper-interface.svg
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.toolbar-bg { fill: #f0f0f0; }
|
||||
.toolbar-btn { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.toolbar-btn-active { fill: #e0e0e0; stroke: #999; stroke-width: 1; }
|
||||
.ai-btn { fill: #BD10E0; }
|
||||
.ai-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.sidebar-item { fill: none; }
|
||||
.sidebar-active { fill: #e8f4fd; }
|
||||
.cursor { stroke: #4A90E2; stroke-width: 2; }
|
||||
.doc-content { fill: #fff; stroke: #ddd; stroke-width: 1; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.toolbar-bg { fill: #333; }
|
||||
.toolbar-btn { fill: #2a2a2a; stroke: #444; }
|
||||
.toolbar-btn-active { fill: #444; stroke: #555; }
|
||||
.sidebar-active { fill: #2a3a4a; }
|
||||
.doc-content { fill: #2a2a2a; stroke: #444; }
|
||||
.button { fill: #00D4FF; }
|
||||
.ai-btn { fill: #E040FB; }
|
||||
.cursor { stroke: #00D4FF; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Paper - AI Writing Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Left Sidebar - Document List -->
|
||||
<rect x="30" y="50" width="140" height="470" rx="8" class="panel-bg"/>
|
||||
<line x1="170" y1="50" x2="170" y2="520" class="divider"/>
|
||||
|
||||
<!-- Sidebar Header -->
|
||||
<text x="50" y="78" font-size="14" font-weight="500" class="main-text">📄 Notes</text>
|
||||
<line x1="40" y1="90" x2="160" y2="90" class="divider"/>
|
||||
|
||||
<!-- Document List -->
|
||||
<g transform="translate(40, 100)">
|
||||
<!-- Active Document -->
|
||||
<rect x="-5" y="0" width="125" height="30" rx="4" class="sidebar-active"/>
|
||||
<text x="0" y="20" font-size="13" class="main-text">Meeting Notes</text>
|
||||
|
||||
<text x="0" y="50" font-size="13" class="secondary-text">Project Plan</text>
|
||||
<text x="0" y="80" font-size="13" class="secondary-text">Ideas</text>
|
||||
|
||||
<line x1="-5" y1="100" x2="115" y2="100" class="divider"/>
|
||||
|
||||
<text x="0" y="125" font-size="12" font-weight="500" class="secondary-text">Quick Start</text>
|
||||
<text x="0" y="150" font-size="12" class="secondary-text">📄 Blank</text>
|
||||
<text x="0" y="175" font-size="12" class="secondary-text">📋 Meeting</text>
|
||||
<text x="0" y="200" font-size="12" class="secondary-text">✓ To-Do</text>
|
||||
<text x="0" y="225" font-size="12" class="secondary-text">🔬 Research</text>
|
||||
</g>
|
||||
|
||||
<!-- Main Editor Area -->
|
||||
<rect x="170" y="50" width="700" height="470" class="bg"/>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<rect x="170" y="50" width="700" height="45" class="toolbar-bg"/>
|
||||
<line x1="170" y1="95" x2="870" y2="95" class="divider"/>
|
||||
|
||||
<!-- Formatting Buttons -->
|
||||
<g transform="translate(185, 58)">
|
||||
<!-- Bold -->
|
||||
<rect x="0" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="15" y="20" text-anchor="middle" font-size="14" font-weight="bold" class="icon-text">B</text>
|
||||
|
||||
<!-- Italic -->
|
||||
<rect x="35" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="50" y="20" text-anchor="middle" font-size="14" font-style="italic" class="icon-text">I</text>
|
||||
|
||||
<!-- Underline -->
|
||||
<rect x="70" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="85" y="20" text-anchor="middle" font-size="14" class="icon-text" text-decoration="underline">U</text>
|
||||
|
||||
<!-- Separator -->
|
||||
<line x1="110" y1="5" x2="110" y2="25" class="divider"/>
|
||||
|
||||
<!-- H1 -->
|
||||
<rect x="120" y="0" width="30" height="28" rx="4" class="toolbar-btn-active"/>
|
||||
<text x="135" y="20" text-anchor="middle" font-size="12" font-weight="bold" class="icon-text">H1</text>
|
||||
|
||||
<!-- H2 -->
|
||||
<rect x="155" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="170" y="20" text-anchor="middle" font-size="12" class="icon-text">H2</text>
|
||||
|
||||
<!-- Separator -->
|
||||
<line x1="195" y1="5" x2="195" y2="25" class="divider"/>
|
||||
|
||||
<!-- Bullet List -->
|
||||
<rect x="205" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="220" y="20" text-anchor="middle" font-size="14" class="icon-text">•</text>
|
||||
|
||||
<!-- Horizontal Rule -->
|
||||
<rect x="240" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="255" y="20" text-anchor="middle" font-size="14" class="icon-text">―</text>
|
||||
|
||||
<!-- Link -->
|
||||
<rect x="275" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="290" y="20" text-anchor="middle" font-size="14" class="icon-text">🔗</text>
|
||||
|
||||
<!-- Image -->
|
||||
<rect x="310" y="0" width="30" height="28" rx="4" class="toolbar-btn"/>
|
||||
<text x="325" y="20" text-anchor="middle" font-size="14" class="icon-text">📷</text>
|
||||
|
||||
<!-- AI Button -->
|
||||
<rect x="360" y="0" width="70" height="28" rx="4" class="ai-btn"/>
|
||||
<text x="395" y="19" text-anchor="middle" font-size="12" class="ai-text">AI ✨</text>
|
||||
</g>
|
||||
|
||||
<!-- Document Content Area -->
|
||||
<rect x="190" y="110" width="660" height="395" rx="4" class="doc-content"/>
|
||||
|
||||
<!-- Document Content -->
|
||||
<g transform="translate(210, 135)">
|
||||
<!-- Title -->
|
||||
<text x="0" y="0" font-size="24" font-weight="bold" class="main-text">Project Proposal</text>
|
||||
<line x1="0" y1="10" x2="200" y2="10" stroke="#4A90E2" stroke-width="3"/>
|
||||
|
||||
<!-- Section -->
|
||||
<text x="0" y="55" font-size="18" font-weight="600" class="main-text">Introduction</text>
|
||||
<line x1="0" y1="65" x2="100" y2="65" class="divider"/>
|
||||
|
||||
<!-- Body text -->
|
||||
<text x="0" y="95" font-size="14" class="main-text">This document outlines our proposal for</text>
|
||||
<text x="0" y="118" font-size="14" class="main-text">the upcoming project. We aim to deliver</text>
|
||||
<text x="0" y="141" font-size="14" class="main-text">a comprehensive solution that addresses</text>
|
||||
<text x="0" y="164" font-size="14" class="main-text">all stakeholder requirements.</text>
|
||||
|
||||
<!-- Cursor -->
|
||||
<line x1="248" y1="150" x2="248" y2="170" class="cursor"/>
|
||||
|
||||
<!-- More content preview -->
|
||||
<text x="0" y="210" font-size="18" font-weight="600" class="secondary-text">Key Objectives</text>
|
||||
<text x="0" y="240" font-size="14" class="secondary-text">• Improve efficiency by 30%</text>
|
||||
<text x="0" y="263" font-size="14" class="secondary-text">• Reduce costs through automation</text>
|
||||
<text x="0" y="286" font-size="14" class="secondary-text">• Enhance user experience</text>
|
||||
</g>
|
||||
|
||||
<!-- Word Count / Status Bar -->
|
||||
<g transform="translate(190, 490)">
|
||||
<text x="0" y="0" font-size="11" class="secondary-text">Words: 156</text>
|
||||
<text x="100" y="0" font-size="11" class="secondary-text">Characters: 892</text>
|
||||
<text x="250" y="0" font-size="11" class="secondary-text">Reading time: 1 min</text>
|
||||
<text x="480" y="0" font-size="11" class="secondary-text">✓ Auto-saved 2 min ago</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<rect x="0" y="-10" width="20" height="14" rx="3" class="ai-btn"/>
|
||||
<text x="28" y="0" font-size="11" class="secondary-text">AI Assist</text>
|
||||
|
||||
<text x="100" y="0" font-size="11" class="secondary-text">Shortcuts: Ctrl+B = Bold | Ctrl+I = Italic | Ctrl+S = Save | Ctrl+/ = AI</text>
|
||||
|
||||
<text x="550" y="0" font-size="11" class="secondary-text">Markdown supported</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.9 KiB |
168
docs/src/assets/chapter-04/research-interface.svg
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.search-box { fill: #fff; stroke: #4A90E2; stroke-width: 2; }
|
||||
.filter-active { fill: #4A90E2; }
|
||||
.filter-inactive { fill: #e0e0e0; }
|
||||
.result-card { fill: #fff; stroke: #ddd; stroke-width: 1; }
|
||||
.source-link { fill: #4A90E2; font-family: Arial, sans-serif; }
|
||||
.sidebar-item { fill: none; }
|
||||
.sidebar-active { fill: #e8f4fd; }
|
||||
.collection-icon { fill: #F5A623; }
|
||||
.ai-highlight { fill: #f0e6ff; stroke: #BD10E0; stroke-width: 1; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.search-box { fill: #2a2a2a; stroke: #00D4FF; }
|
||||
.filter-inactive { fill: #333; }
|
||||
.result-card { fill: #2a2a2a; stroke: #444; }
|
||||
.source-link { fill: #00D4FF; }
|
||||
.sidebar-active { fill: #2a3a4a; }
|
||||
.button { fill: #00D4FF; }
|
||||
.filter-active { fill: #00D4FF; }
|
||||
.ai-highlight { fill: #2a2040; stroke: #E040FB; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Research - AI Search Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Left Sidebar -->
|
||||
<rect x="30" y="50" width="140" height="470" rx="8" class="panel-bg"/>
|
||||
<line x1="170" y1="50" x2="170" y2="520" class="divider"/>
|
||||
|
||||
<!-- Sidebar Header -->
|
||||
<text x="50" y="78" font-size="14" font-weight="500" class="main-text">🔍 Research</text>
|
||||
<line x1="40" y1="90" x2="160" y2="90" class="divider"/>
|
||||
|
||||
<!-- Focus Filters -->
|
||||
<g transform="translate(40, 105)">
|
||||
<text x="0" y="0" font-size="12" font-weight="500" class="secondary-text">Focus:</text>
|
||||
|
||||
<!-- All - Active -->
|
||||
<rect x="-5" y="10" width="115" height="26" rx="4" class="sidebar-active"/>
|
||||
<text x="5" y="28" font-size="13" class="main-text">🌐 All</text>
|
||||
|
||||
<text x="5" y="58" font-size="13" class="secondary-text">📚 Academic</text>
|
||||
<text x="5" y="83" font-size="13" class="secondary-text">💻 Code</text>
|
||||
<text x="5" y="108" font-size="13" class="secondary-text">🏠 Internal</text>
|
||||
|
||||
<line x1="-5" y1="125" x2="115" y2="125" class="divider"/>
|
||||
|
||||
<!-- Collections -->
|
||||
<text x="0" y="145" font-size="12" font-weight="500" class="secondary-text">Collections:</text>
|
||||
<text x="5" y="170" font-size="13" class="collection-icon">📁</text>
|
||||
<text x="25" y="170" font-size="13" class="main-text">Project A</text>
|
||||
<text x="5" y="195" font-size="13" class="collection-icon">📁</text>
|
||||
<text x="25" y="195" font-size="13" class="main-text">References</text>
|
||||
|
||||
<line x1="-5" y1="215" x2="115" y2="215" class="divider"/>
|
||||
|
||||
<!-- Recent Searches -->
|
||||
<text x="0" y="235" font-size="12" font-weight="500" class="secondary-text">Recent:</text>
|
||||
<text x="5" y="258" font-size="12" class="secondary-text">• market size</text>
|
||||
<text x="5" y="278" font-size="12" class="secondary-text">• competitors</text>
|
||||
<text x="5" y="298" font-size="12" class="secondary-text">• best practices</text>
|
||||
</g>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<rect x="170" y="50" width="700" height="470" class="bg"/>
|
||||
|
||||
<!-- Search Box -->
|
||||
<g transform="translate(190, 70)">
|
||||
<rect x="0" y="0" width="640" height="50" rx="25" class="search-box"/>
|
||||
<text x="25" y="32" font-size="16" class="secondary-text">What are the best practices for...</text>
|
||||
<circle cx="610" cy="25" r="18" class="button"/>
|
||||
<text x="610" y="31" text-anchor="middle" font-size="16" fill="#fff">→</text>
|
||||
</g>
|
||||
|
||||
<!-- AI Answer Section -->
|
||||
<g transform="translate(190, 140)">
|
||||
<rect x="0" y="0" width="640" height="180" rx="8" class="ai-highlight"/>
|
||||
|
||||
<!-- Header -->
|
||||
<text x="20" y="30" font-size="16" font-weight="600" class="main-text">AI Answer:</text>
|
||||
<line x1="20" y1="42" x2="100" y2="42" stroke="#BD10E0" stroke-width="2"/>
|
||||
|
||||
<!-- Answer Content -->
|
||||
<text x="20" y="70" font-size="14" class="main-text">Based on multiple sources, here are the key best practices:</text>
|
||||
|
||||
<text x="20" y="100" font-size="14" class="main-text">1. Start with clear requirements and objectives</text>
|
||||
<text x="20" y="122" font-size="14" class="main-text">2. Use iterative development with frequent feedback</text>
|
||||
<text x="20" y="144" font-size="14" class="main-text">3. Test early and often with real users</text>
|
||||
<text x="20" y="166" font-size="14" class="main-text">4. Document decisions and maintain knowledge base</text>
|
||||
</g>
|
||||
|
||||
<!-- Sources Section -->
|
||||
<g transform="translate(190, 335)">
|
||||
<text x="0" y="0" font-size="14" font-weight="600" class="main-text">Sources:</text>
|
||||
|
||||
<!-- Source Cards -->
|
||||
<rect x="0" y="15" width="200" height="70" rx="6" class="result-card"/>
|
||||
<text x="10" y="35" font-size="11" class="secondary-text">[1]</text>
|
||||
<text x="30" y="35" font-size="12" class="source-link">industry-guide.com</text>
|
||||
<text x="10" y="55" font-size="11" class="secondary-text">Best practices for software</text>
|
||||
<text x="10" y="70" font-size="11" class="secondary-text">development in 2024...</text>
|
||||
|
||||
<rect x="215" y="15" width="200" height="70" rx="6" class="result-card"/>
|
||||
<text x="225" y="35" font-size="11" class="secondary-text">[2]</text>
|
||||
<text x="245" y="35" font-size="12" class="source-link">techblog.dev</text>
|
||||
<text x="225" y="55" font-size="11" class="secondary-text">Modern development</text>
|
||||
<text x="225" y="70" font-size="11" class="secondary-text">methodology overview...</text>
|
||||
|
||||
<rect x="430" y="15" width="200" height="70" rx="6" class="result-card"/>
|
||||
<text x="440" y="35" font-size="11" class="secondary-text">[3]</text>
|
||||
<text x="460" y="35" font-size="12" class="source-link">your-docs/guidelines.pdf</text>
|
||||
<text x="440" y="55" font-size="11" class="secondary-text">📄 Internal document</text>
|
||||
<text x="440" y="70" font-size="11" class="secondary-text">Company guidelines...</text>
|
||||
</g>
|
||||
|
||||
<!-- Action Buttons -->
|
||||
<g transform="translate(190, 430)">
|
||||
<rect x="0" y="0" width="100" height="32" rx="6" class="button"/>
|
||||
<text x="50" y="21" text-anchor="middle" font-size="12" class="button-text">📁 Save</text>
|
||||
|
||||
<rect x="110" y="0" width="100" height="32" rx="6" class="input-field"/>
|
||||
<text x="160" y="21" text-anchor="middle" font-size="12" class="main-text">📤 Share</text>
|
||||
|
||||
<rect x="220" y="0" width="120" height="32" rx="6" class="input-field"/>
|
||||
<text x="280" y="21" text-anchor="middle" font-size="12" class="main-text">🔄 Deep Dive</text>
|
||||
|
||||
<rect x="350" y="0" width="100" height="32" rx="6" class="input-field"/>
|
||||
<text x="400" y="21" text-anchor="middle" font-size="12" class="main-text">📋 Copy</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<rect x="0" y="-10" width="20" height="14" rx="3" class="ai-highlight"/>
|
||||
<text x="28" y="0" font-size="11" class="secondary-text">AI Summary</text>
|
||||
|
||||
<rect x="100" y="-10" width="20" height="14" rx="3" class="result-card"/>
|
||||
<text x="128" y="0" font-size="11" class="secondary-text">Sources</text>
|
||||
|
||||
<text x="220" y="0" font-size="11" class="secondary-text">Focus: Web • Academic • Code • Internal docs</text>
|
||||
|
||||
<text x="550" y="0" font-size="11" class="secondary-text">Enter = Search | Ctrl+S = Save</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8 KiB |
206
docs/src/assets/chapter-04/sources-interface.svg
Normal file
|
|
@ -0,0 +1,206 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.panel-bg { fill: #fafafa; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.tab-active { fill: #4A90E2; }
|
||||
.tab-inactive { fill: #e0e0e0; }
|
||||
.card { fill: #fff; stroke: #ddd; stroke-width: 1; }
|
||||
.card-featured { fill: #fff; stroke: #4A90E2; stroke-width: 2; }
|
||||
.sidebar-active { fill: #e8f4fd; }
|
||||
.category-icon { fill: #666; }
|
||||
.star-icon { fill: #F5A623; }
|
||||
.tag { fill: #e8f4fd; stroke: #4A90E2; stroke-width: 1; }
|
||||
.tag-text { fill: #4A90E2; font-family: Arial, sans-serif; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.panel-bg { fill: #222; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.tab-inactive { fill: #333; }
|
||||
.card { fill: #2a2a2a; stroke: #444; }
|
||||
.card-featured { fill: #2a2a2a; stroke: #00D4FF; }
|
||||
.sidebar-active { fill: #2a3a4a; }
|
||||
.button { fill: #00D4FF; }
|
||||
.tab-active { fill: #00D4FF; }
|
||||
.tag { fill: #1a2a3a; stroke: #00D4FF; }
|
||||
.tag-text { fill: #00D4FF; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Sources - Prompts & Templates Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Header -->
|
||||
<rect x="30" y="50" width="840" height="50" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="92" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="55" y="82" font-size="16" font-weight="600" class="main-text">Sources</text>
|
||||
|
||||
<!-- Search Bar -->
|
||||
<g transform="translate(580, 60)">
|
||||
<rect x="0" y="0" width="200" height="32" rx="16" class="input-field"/>
|
||||
<text x="20" y="21" font-size="13" class="secondary-text">🔍 Search...</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="30" y1="100" x2="870" y2="100" class="divider"/>
|
||||
|
||||
<!-- Tabs -->
|
||||
<g transform="translate(50, 110)">
|
||||
<rect x="0" y="0" width="90" height="32" rx="4" class="tab-active"/>
|
||||
<text x="45" y="21" text-anchor="middle" font-size="12" fill="#fff">Prompts</text>
|
||||
|
||||
<rect x="100" y="0" width="95" height="32" rx="4" class="tab-inactive"/>
|
||||
<text x="147" y="21" text-anchor="middle" font-size="12" class="main-text">Templates</text>
|
||||
|
||||
<rect x="205" y="0" width="105" height="32" rx="4" class="tab-inactive"/>
|
||||
<text x="257" y="21" text-anchor="middle" font-size="12" class="main-text">MCP Servers</text>
|
||||
|
||||
<rect x="320" y="0" width="95" height="32" rx="4" class="tab-inactive"/>
|
||||
<text x="367" y="21" text-anchor="middle" font-size="12" class="main-text">LLM Tools</text>
|
||||
|
||||
<rect x="425" y="0" width="80" height="32" rx="4" class="tab-inactive"/>
|
||||
<text x="465" y="21" text-anchor="middle" font-size="12" class="main-text">Models</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="50" y1="155" x2="850" y2="155" class="divider"/>
|
||||
|
||||
<!-- Left Sidebar - Categories -->
|
||||
<rect x="30" y="155" width="130" height="365" class="panel-bg"/>
|
||||
<line x1="160" y1="155" x2="160" y2="520" class="divider"/>
|
||||
|
||||
<!-- Categories -->
|
||||
<g transform="translate(40, 170)">
|
||||
<text x="0" y="0" font-size="12" font-weight="500" class="secondary-text">Categories</text>
|
||||
<line x1="0" y1="10" x2="110" y2="10" class="divider"/>
|
||||
|
||||
<!-- Category Items -->
|
||||
<rect x="-5" y="20" width="115" height="28" rx="4" class="sidebar-active"/>
|
||||
<text x="5" y="39" font-size="13" class="main-text">📝 Writing</text>
|
||||
|
||||
<text x="5" y="70" font-size="13" class="secondary-text">📊 Analysis</text>
|
||||
<text x="5" y="98" font-size="13" class="secondary-text">💼 Business</text>
|
||||
<text x="5" y="126" font-size="13" class="secondary-text">💻 Code</text>
|
||||
<text x="5" y="154" font-size="13" class="secondary-text">🎨 Creative</text>
|
||||
<text x="5" y="182" font-size="13" class="secondary-text">🤖 Automation</text>
|
||||
<text x="5" y="210" font-size="13" class="secondary-text">📞 Support</text>
|
||||
</g>
|
||||
|
||||
<!-- Main Content Area -->
|
||||
<g transform="translate(175, 170)">
|
||||
<!-- Featured Section -->
|
||||
<text x="0" y="0" font-size="14" font-weight="500" class="main-text">⭐ Featured</text>
|
||||
|
||||
<!-- Card 1: Customer Service -->
|
||||
<rect x="0" y="20" width="210" height="150" rx="8" class="card-featured"/>
|
||||
<text x="15" y="50" font-size="15" font-weight="500" class="main-text">Customer</text>
|
||||
<text x="15" y="70" font-size="15" font-weight="500" class="main-text">Service</text>
|
||||
<line x1="15" y1="80" x2="195" y2="80" class="divider"/>
|
||||
<text x="15" y="100" font-size="12" class="secondary-text">Handle support</text>
|
||||
<text x="15" y="118" font-size="12" class="secondary-text">inquiries with AI</text>
|
||||
<text x="15" y="136" font-size="12" class="secondary-text">assistance</text>
|
||||
<!-- Use Button -->
|
||||
<rect x="15" y="145" width="60" height="24" rx="4" class="button"/>
|
||||
<text x="45" y="162" text-anchor="middle" font-size="11" class="button-text">Use</text>
|
||||
<!-- Star -->
|
||||
<text x="180" y="50" font-size="14" class="star-icon">★</text>
|
||||
|
||||
<!-- Card 2: Sales Assistant -->
|
||||
<rect x="225" y="20" width="210" height="150" rx="8" class="card-featured"/>
|
||||
<text x="240" y="50" font-size="15" font-weight="500" class="main-text">Sales</text>
|
||||
<text x="240" y="70" font-size="15" font-weight="500" class="main-text">Assistant</text>
|
||||
<line x1="240" y1="80" x2="420" y2="80" class="divider"/>
|
||||
<text x="240" y="100" font-size="12" class="secondary-text">Qualify leads and</text>
|
||||
<text x="240" y="118" font-size="12" class="secondary-text">schedule meetings</text>
|
||||
<text x="240" y="136" font-size="12" class="secondary-text">automatically</text>
|
||||
<!-- Use Button -->
|
||||
<rect x="240" y="145" width="60" height="24" rx="4" class="button"/>
|
||||
<text x="270" y="162" text-anchor="middle" font-size="11" class="button-text">Use</text>
|
||||
<!-- Star -->
|
||||
<text x="405" y="50" font-size="14" class="star-icon">★</text>
|
||||
|
||||
<!-- Card 3: Code Helper -->
|
||||
<rect x="450" y="20" width="210" height="150" rx="8" class="card"/>
|
||||
<text x="465" y="50" font-size="15" font-weight="500" class="main-text">Code</text>
|
||||
<text x="465" y="70" font-size="15" font-weight="500" class="main-text">Helper</text>
|
||||
<line x1="465" y1="80" x2="645" y2="80" class="divider"/>
|
||||
<text x="465" y="100" font-size="12" class="secondary-text">Generate and</text>
|
||||
<text x="465" y="118" font-size="12" class="secondary-text">explain code</text>
|
||||
<text x="465" y="136" font-size="12" class="secondary-text">snippets</text>
|
||||
<!-- Use Button -->
|
||||
<rect x="465" y="145" width="60" height="24" rx="4" class="button"/>
|
||||
<text x="495" y="162" text-anchor="middle" font-size="11" class="button-text">Use</text>
|
||||
|
||||
<!-- More Templates Section -->
|
||||
<text x="0" y="200" font-size="14" font-weight="500" class="main-text">All Prompts</text>
|
||||
|
||||
<!-- Smaller Cards Row -->
|
||||
<rect x="0" y="220" width="155" height="100" rx="6" class="card"/>
|
||||
<text x="15" y="245" font-size="13" font-weight="500" class="main-text">Meeting Notes</text>
|
||||
<text x="15" y="265" font-size="11" class="secondary-text">Summarize meetings</text>
|
||||
<rect x="15" y="280" width="50" height="22" rx="3" class="tag"/>
|
||||
<text x="40" y="295" text-anchor="middle" font-size="10" class="tag-text">Writing</text>
|
||||
<rect x="105" y="280" width="40" height="22" rx="4" class="button"/>
|
||||
<text x="125" y="295" text-anchor="middle" font-size="10" class="button-text">Use</text>
|
||||
|
||||
<rect x="165" y="220" width="155" height="100" rx="6" class="card"/>
|
||||
<text x="180" y="245" font-size="13" font-weight="500" class="main-text">Data Analyst</text>
|
||||
<text x="180" y="265" font-size="11" class="secondary-text">Analyze datasets</text>
|
||||
<rect x="180" y="280" width="55" height="22" rx="3" class="tag"/>
|
||||
<text x="207" y="295" text-anchor="middle" font-size="10" class="tag-text">Analysis</text>
|
||||
<rect x="270" y="280" width="40" height="22" rx="4" class="button"/>
|
||||
<text x="290" y="295" text-anchor="middle" font-size="10" class="button-text">Use</text>
|
||||
|
||||
<rect x="330" y="220" width="155" height="100" rx="6" class="card"/>
|
||||
<text x="345" y="245" font-size="13" font-weight="500" class="main-text">Email Writer</text>
|
||||
<text x="345" y="265" font-size="11" class="secondary-text">Draft pro emails</text>
|
||||
<rect x="345" y="280" width="55" height="22" rx="3" class="tag"/>
|
||||
<text x="372" y="295" text-anchor="middle" font-size="10" class="tag-text">Business</text>
|
||||
<rect x="435" y="280" width="40" height="22" rx="4" class="button"/>
|
||||
<text x="455" y="295" text-anchor="middle" font-size="10" class="button-text">Use</text>
|
||||
|
||||
<rect x="495" y="220" width="155" height="100" rx="6" class="card"/>
|
||||
<text x="510" y="245" font-size="13" font-weight="500" class="main-text">Story Writer</text>
|
||||
<text x="510" y="265" font-size="11" class="secondary-text">Creative stories</text>
|
||||
<rect x="510" y="280" width="55" height="22" rx="3" class="tag"/>
|
||||
<text x="537" y="295" text-anchor="middle" font-size="10" class="tag-text">Creative</text>
|
||||
<rect x="600" y="280" width="40" height="22" rx="4" class="button"/>
|
||||
<text x="620" y="295" text-anchor="middle" font-size="10" class="button-text">Use</text>
|
||||
</g>
|
||||
|
||||
<!-- Pagination -->
|
||||
<g transform="translate(400, 500)">
|
||||
<text x="0" y="0" font-size="12" class="secondary-text">Page 1 of 5</text>
|
||||
<rect x="80" y="-15" width="30" height="22" rx="4" class="input-field"/>
|
||||
<text x="95" y="2" text-anchor="middle" font-size="12" class="main-text">←</text>
|
||||
<rect x="115" y="-15" width="30" height="22" rx="4" class="input-field"/>
|
||||
<text x="130" y="2" text-anchor="middle" font-size="12" class="main-text">→</text>
|
||||
</g>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(30, 535)">
|
||||
<text x="0" y="0" font-size="11" class="secondary-text">Tabs: Prompts, Templates, MCP Servers, LLM Tools, Models</text>
|
||||
<text x="350" y="0" font-size="11" class="secondary-text">Categories filter content</text>
|
||||
<text x="550" y="0" font-size="11" class="secondary-text">Click Use to apply prompt/template</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
82
docs/src/assets/chapter-04/suite-main-layout.svg
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<svg width="800" height="500" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.header-bg { fill: #e0e0e0; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ccc; stroke-width: 1; }
|
||||
.button { fill: #fff; stroke: #999; stroke-width: 1; rx: 4; }
|
||||
.input-field { fill: #fff; stroke: #bbb; stroke-width: 1; }
|
||||
.accent { fill: #4A90E2; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.button { fill: #333; stroke: #555; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.accent { fill: #00D4FF; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="400" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Suite - Main Layout</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="50" y="50" width="700" height="420" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Header Bar -->
|
||||
<rect x="50" y="50" width="700" height="50" rx="8" class="header-bg"/>
|
||||
<rect x="50" y="92" width="700" height="8" class="bg"/>
|
||||
|
||||
<!-- Logo/Brand -->
|
||||
<circle cx="85" cy="75" r="15" class="accent"/>
|
||||
<text x="85" y="80" text-anchor="middle" font-size="14" fill="#fff">🤖</text>
|
||||
<text x="115" y="80" font-size="16" font-weight="600" class="main-text">General Bots</text>
|
||||
|
||||
<!-- Header Right Controls -->
|
||||
<g transform="translate(620, 60)">
|
||||
<!-- Apps Menu -->
|
||||
<rect x="0" y="0" width="35" height="30" rx="4" class="button"/>
|
||||
<text x="17" y="20" text-anchor="middle" font-size="12" class="icon-text">⋮⋮⋮</text>
|
||||
|
||||
<!-- Theme Toggle -->
|
||||
<rect x="45" y="0" width="35" height="30" rx="4" class="button"/>
|
||||
<text x="62" y="20" text-anchor="middle" font-size="14" class="icon-text">🌙</text>
|
||||
|
||||
<!-- User Avatar -->
|
||||
<circle cx="110" cy="15" r="15" class="accent"/>
|
||||
<text x="110" y="20" text-anchor="middle" font-size="12" fill="#fff">U</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="50" y1="100" x2="750" y2="100" class="divider"/>
|
||||
|
||||
<!-- Main Chat Area -->
|
||||
<rect x="70" y="120" width="660" height="280" rx="6" class="input-field"/>
|
||||
|
||||
<!-- Chat Area Label -->
|
||||
<text x="400" y="200" text-anchor="middle" font-size="24" class="secondary-text">💬</text>
|
||||
<text x="400" y="240" text-anchor="middle" font-size="18" class="main-text">Chat</text>
|
||||
<text x="400" y="265" text-anchor="middle" font-size="14" class="secondary-text">(Main Area)</text>
|
||||
|
||||
<!-- Message Input -->
|
||||
<rect x="70" y="420" width="580" height="40" rx="20" class="input-field"/>
|
||||
<text x="100" y="445" font-size="14" class="secondary-text">Type your message here...</text>
|
||||
|
||||
<!-- Send Button -->
|
||||
<rect x="660" y="420" width="70" height="40" rx="20" class="accent"/>
|
||||
<text x="695" y="445" text-anchor="middle" font-size="14" fill="#fff">Send</text>
|
||||
|
||||
<!-- Legend -->
|
||||
<g transform="translate(50, 480)">
|
||||
<text x="0" y="0" font-size="11" class="secondary-text">Header: Navigation, Apps Menu, Theme, Profile</text>
|
||||
<text x="400" y="0" font-size="11" class="secondary-text">Main: AI Chat Interface with Message Input</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
151
docs/src/assets/chapter-04/tasks-interface.svg
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
<svg width="900" height="550" xmlns="http://www.w3.org/2000/svg">
|
||||
<style>
|
||||
.bg { fill: #f5f5f5; }
|
||||
.frame { fill: none; stroke: #333; stroke-width: 2; }
|
||||
.header-bg { fill: #e8e8e8; }
|
||||
.main-text { fill: #1a1a1a; font-family: Arial, sans-serif; }
|
||||
.secondary-text { fill: #666; font-family: Arial, sans-serif; }
|
||||
.icon-text { fill: #333; font-family: Arial, sans-serif; }
|
||||
.divider { stroke: #ddd; stroke-width: 1; }
|
||||
.button { fill: #4A90E2; }
|
||||
.button-text { fill: #fff; font-family: Arial, sans-serif; }
|
||||
.input-field { fill: #fff; stroke: #ccc; stroke-width: 1; }
|
||||
.tab-active { fill: #4A90E2; }
|
||||
.tab-inactive { fill: #e0e0e0; }
|
||||
.priority-high { fill: #E74C3C; }
|
||||
.priority-medium { fill: #F5A623; }
|
||||
.priority-low { fill: #7ED321; }
|
||||
.checkbox { fill: #fff; stroke: #999; stroke-width: 1.5; }
|
||||
.checkbox-done { fill: #7ED321; stroke: #5cb318; stroke-width: 1.5; }
|
||||
.task-done { fill: #999; text-decoration: line-through; }
|
||||
.row-bg { fill: #fff; }
|
||||
.row-hover { fill: #f0f7ff; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #1a1a1a; }
|
||||
.frame { stroke: #555; }
|
||||
.header-bg { fill: #2a2a2a; }
|
||||
.main-text { fill: #ffffff; }
|
||||
.secondary-text { fill: #aaa; }
|
||||
.icon-text { fill: #ddd; }
|
||||
.divider { stroke: #444; }
|
||||
.input-field { fill: #2a2a2a; stroke: #444; }
|
||||
.tab-inactive { fill: #333; }
|
||||
.checkbox { fill: #2a2a2a; stroke: #666; }
|
||||
.row-bg { fill: #222; }
|
||||
.row-hover { fill: #2a3a4a; }
|
||||
.button { fill: #00D4FF; }
|
||||
.tab-active { fill: #00D4FF; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="30" text-anchor="middle" font-size="20" font-weight="600" class="main-text">Tasks - To-Do Management Interface</text>
|
||||
|
||||
<!-- Main Frame -->
|
||||
<rect x="30" y="50" width="840" height="470" rx="8" class="frame bg"/>
|
||||
|
||||
<!-- Header -->
|
||||
<rect x="30" y="50" width="840" height="50" rx="8" class="header-bg"/>
|
||||
<rect x="30" y="92" width="840" height="8" class="bg"/>
|
||||
|
||||
<!-- Title and Stats -->
|
||||
<text x="55" y="82" font-size="18" font-weight="600" class="main-text">✓ Tasks</text>
|
||||
<g transform="translate(600, 65)">
|
||||
<text x="0" y="15" font-size="12" class="secondary-text">Total: </text>
|
||||
<text x="40" y="15" font-size="12" font-weight="600" class="main-text">12</text>
|
||||
<text x="70" y="15" font-size="12" class="secondary-text">Active: </text>
|
||||
<text x="115" y="15" font-size="12" font-weight="600" class="main-text">5</text>
|
||||
<text x="140" y="15" font-size="12" class="secondary-text">Done: </text>
|
||||
<text x="180" y="15" font-size="12" font-weight="600" class="main-text">7</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="30" y1="100" x2="870" y2="100" class="divider"/>
|
||||
|
||||
<!-- Add Task Input -->
|
||||
<g transform="translate(50, 115)">
|
||||
<rect x="0" y="0" width="620" height="40" rx="6" class="input-field"/>
|
||||
<text x="15" y="26" font-size="14" class="secondary-text">What needs to be done?</text>
|
||||
|
||||
<!-- Category Dropdown -->
|
||||
<rect x="640" y="0" width="100" height="40" rx="6" class="input-field"/>
|
||||
<text x="655" y="26" font-size="13" class="secondary-text">Category ▼</text>
|
||||
|
||||
<!-- Add Button -->
|
||||
<rect x="755" y="0" width="60" height="40" rx="6" class="button"/>
|
||||
<text x="785" y="26" text-anchor="middle" font-size="13" class="button-text">+ Add</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="50" y1="170" x2="850" y2="170" class="divider"/>
|
||||
|
||||
<!-- Filter Tabs -->
|
||||
<g transform="translate(50, 185)">
|
||||
<rect x="0" y="0" width="90" height="32" rx="4" class="tab-active"/>
|
||||
<text x="45" y="21" text-anchor="middle" font-size="13" fill="#fff">📋 All (12)</text>
|
||||
|
||||
<rect x="100" y="0" width="100" height="32" rx="4" class="tab-inactive"/>
|
||||
<text x="150" y="21" text-anchor="middle" font-size="13" class="main-text">⏳ Active (5)</text>
|
||||
|
||||
<rect x="210" y="0" width="120" height="32" rx="4" class="tab-inactive"/>
|
||||
<text x="270" y="21" text-anchor="middle" font-size="13" class="main-text">✓ Completed (7)</text>
|
||||
|
||||
<rect x="340" y="0" width="100" height="32" rx="4" class="tab-inactive"/>
|
||||
<text x="390" y="21" text-anchor="middle" font-size="13" class="main-text">⚡ Priority</text>
|
||||
</g>
|
||||
|
||||
<!-- Divider -->
|
||||
<line x1="50" y1="230" x2="850" y2="230" class="divider"/>
|
||||
|
||||
<!-- Task List -->
|
||||
<g transform="translate(50, 245)">
|
||||
<!-- Task 1 - High Priority -->
|
||||
<rect x="0" y="0" width="800" height="45" rx="4" class="row-hover"/>
|
||||
<rect x="15" y="12" width="20" height="20" rx="4" class="checkbox"/>
|
||||
<text x="50" y="28" font-size="14" class="main-text">Review quarterly report</text>
|
||||
<text x="550" y="28" font-size="13" class="secondary-text">📅 Today</text>
|
||||
<circle cx="780" cy="22" r="8" class="priority-high"/>
|
||||
|
||||
<!-- Task 2 - Medium Priority -->
|
||||
<rect x="0" y="50" width="800" height="45" rx="4" class="row-bg"/>
|
||||
<rect x="15" y="62" width="20" height="20" rx="4" class="checkbox"/>
|
||||
<text x="50" y="78" font-size="14" class="main-text">Call client about proposal</text>
|
||||
<text x="550" y="78" font-size="13" class="secondary-text">📅 Today</text>
|
||||
<circle cx="780" cy="72" r="8" class="priority-medium"/>
|
||||
|
||||
<!-- Task 3 - Low Priority -->
|
||||
<rect x="0" y="100" width="800" height="45" rx="4" class="row-bg"/>
|
||||
<rect x="15" y="112" width="20" height="20" rx="4" class="checkbox"/>
|
||||
<text x="50" y="128" font-size="14" class="main-text">Update project documentation</text>
|
||||
<text x="550" y="128" font-size="13" class="secondary-text">📅 Tomorrow</text>
|
||||
<circle cx="780" cy="122" r="8" class="priority-low"/>
|
||||
|
||||
<!-- Task 4 - Completed -->
|
||||
<rect x="0" y="150" width="800" height="45" rx="4" class="row-bg"/>
|
||||
<rect x="15" y="162" width="20" height="20" rx="4" class="checkbox-done"/>
|
||||
<text x="22" y="178" font-size="12" fill="#fff">✓</text>
|
||||
<text x="50" y="178" font-size="14" class="task-done">Send meeting notes</text>
|
||||
<text x="550" y="178" font-size="13" class="secondary-text">✓ Done</text>
|
||||
|
||||
<!-- Task 5 - Completed -->
|
||||
<rect x="0" y="200" width="800" height="45" rx="4" class="row-bg"/>
|
||||
<rect x="15" y="212" width="20" height="20" rx="4" class="checkbox-done"/>
|
||||
<text x="22" y="228" font-size="12" fill="#fff">✓</text>
|
||||
<text x="50" y="228" font-size="14" class="task-done">Complete expense report</text>
|
||||
<text x="550" y="228" font-size="13" class="secondary-text">✓ Done</text>
|
||||
</g>
|
||||
|
||||
<!-- Priority Legend -->
|
||||
<g transform="translate(50, 505)">
|
||||
<text x="0" y="0" font-size="12" font-weight="500" class="secondary-text">Priority:</text>
|
||||
<circle cx="70" cy="-4" r="6" class="priority-high"/>
|
||||
<text x="82" y="0" font-size="11" class="secondary-text">High</text>
|
||||
<circle cx="130" cy="-4" r="6" class="priority-medium"/>
|
||||
<text x="142" y="0" font-size="11" class="secondary-text">Medium</text>
|
||||
<circle cx="210" cy="-4" r="6" class="priority-low"/>
|
||||
<text x="222" y="0" font-size="11" class="secondary-text">Low</text>
|
||||
|
||||
<text x="350" y="0" font-size="11" class="secondary-text">Shortcuts: Enter = Add | Space = Toggle | Delete = Remove</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7 KiB |
228
docs/src/assets/suite/analytics-screen.svg
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#22c55e"/>
|
||||
<stop offset="100%" stop-color="#4ade80"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="purpleGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#8b5cf6"/>
|
||||
<stop offset="100%" stop-color="#a78bfa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#f97316"/>
|
||||
<stop offset="100%" stop-color="#fb923c"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.green-text { fill: #22c55e; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.red-text { fill: #ef4444; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.card-bg { fill: #f8fafc; }
|
||||
.chart-line { stroke: #3b82f6; stroke-width: 2; fill: none; }
|
||||
.chart-area { fill: #3b82f6; opacity: 0.1; }
|
||||
.grid-line { stroke: #e2e8f0; stroke-width: 1; }
|
||||
.bar-blue { fill: #3b82f6; }
|
||||
.bar-green { fill: #22c55e; }
|
||||
.bar-purple { fill: #8b5cf6; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.green-text { fill: #4ade80; }
|
||||
.red-text { fill: #f87171; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.card-bg { fill: #1e293b; }
|
||||
.grid-line { stroke: #334155; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Analytics - Dashboard</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Header -->
|
||||
<g transform="translate(50, 55)">
|
||||
<text x="0" y="20" font-size="20" font-weight="600" class="main-text">Dashboard Overview</text>
|
||||
<text x="0" y="40" font-size="13" class="secondary-text">Last 30 days • Updated 5 min ago</text>
|
||||
|
||||
<!-- Date Range Selector -->
|
||||
<g transform="translate(550, 5)">
|
||||
<rect x="0" y="0" width="120" height="32" rx="6" class="icon-btn"/>
|
||||
<rect x="0" y="0" width="120" height="32" rx="6" class="border"/>
|
||||
<text x="15" y="21" font-size="12">📅</text>
|
||||
<text x="35" y="21" font-size="12" class="secondary-text">Last 30 days</text>
|
||||
<text x="105" y="21" font-size="10" class="muted-text">▾</text>
|
||||
|
||||
<rect x="130" y="0" width="80" height="32" rx="6" fill="url(#accentGrad)"/>
|
||||
<text x="170" y="21" text-anchor="middle" font-size="12" class="white-text">Export</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Metric Cards Row -->
|
||||
<g transform="translate(50, 105)">
|
||||
<!-- Card 1: Total Users -->
|
||||
<rect x="0" y="0" width="185" height="95" rx="10" class="card-bg"/>
|
||||
<rect x="0" y="0" width="185" height="95" rx="10" class="border"/>
|
||||
<text x="15" y="25" font-size="12" class="secondary-text">Total Users</text>
|
||||
<text x="15" y="55" font-size="26" font-weight="700" class="main-text">12,847</text>
|
||||
<text x="15" y="78" font-size="12" class="green-text">↑ 12.5%</text>
|
||||
<text x="65" y="78" font-size="11" class="muted-text">vs last month</text>
|
||||
<rect x="145" y="15" width="28" height="28" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="159" y="34" text-anchor="middle" font-size="14" class="white-text">👥</text>
|
||||
|
||||
<!-- Card 2: Active Sessions -->
|
||||
<rect x="200" y="0" width="185" height="95" rx="10" class="card-bg"/>
|
||||
<rect x="200" y="0" width="185" height="95" rx="10" class="border"/>
|
||||
<text x="215" y="25" font-size="12" class="secondary-text">Active Sessions</text>
|
||||
<text x="215" y="55" font-size="26" font-weight="700" class="main-text">1,429</text>
|
||||
<text x="215" y="78" font-size="12" class="green-text">↑ 8.2%</text>
|
||||
<text x="260" y="78" font-size="11" class="muted-text">vs last month</text>
|
||||
<rect x="345" y="15" width="28" height="28" rx="8" fill="url(#greenGrad)"/>
|
||||
<text x="359" y="34" text-anchor="middle" font-size="14" class="white-text">📊</text>
|
||||
|
||||
<!-- Card 3: Avg Response Time -->
|
||||
<rect x="400" y="0" width="185" height="95" rx="10" class="card-bg"/>
|
||||
<rect x="400" y="0" width="185" height="95" rx="10" class="border"/>
|
||||
<text x="415" y="25" font-size="12" class="secondary-text">Avg Response Time</text>
|
||||
<text x="415" y="55" font-size="26" font-weight="700" class="main-text">1.2s</text>
|
||||
<text x="415" y="78" font-size="12" class="green-text">↓ 15%</text>
|
||||
<text x="455" y="78" font-size="11" class="muted-text">faster</text>
|
||||
<rect x="545" y="15" width="28" height="28" rx="8" fill="url(#purpleGrad)"/>
|
||||
<text x="559" y="34" text-anchor="middle" font-size="14" class="white-text">⚡</text>
|
||||
|
||||
<!-- Card 4: Satisfaction -->
|
||||
<rect x="600" y="0" width="185" height="95" rx="10" class="card-bg"/>
|
||||
<rect x="600" y="0" width="185" height="95" rx="10" class="border"/>
|
||||
<text x="615" y="25" font-size="12" class="secondary-text">Satisfaction Rate</text>
|
||||
<text x="615" y="55" font-size="26" font-weight="700" class="main-text">94.2%</text>
|
||||
<text x="615" y="78" font-size="12" class="green-text">↑ 2.1%</text>
|
||||
<text x="658" y="78" font-size="11" class="muted-text">vs last month</text>
|
||||
<rect x="745" y="15" width="28" height="28" rx="8" fill="url(#orangeGrad)"/>
|
||||
<text x="759" y="34" text-anchor="middle" font-size="14" class="white-text">⭐</text>
|
||||
</g>
|
||||
|
||||
<!-- Charts Row -->
|
||||
<g transform="translate(50, 220)">
|
||||
<!-- Line Chart: User Activity -->
|
||||
<rect x="0" y="0" width="480" height="200" rx="10" class="card-bg"/>
|
||||
<rect x="0" y="0" width="480" height="200" rx="10" class="border"/>
|
||||
<text x="20" y="25" font-size="14" font-weight="600" class="main-text">User Activity</text>
|
||||
<text x="380" y="25" font-size="11" class="muted-text">Daily active users</text>
|
||||
|
||||
<!-- Grid Lines -->
|
||||
<line x1="50" y1="50" x2="460" y2="50" class="grid-line"/>
|
||||
<line x1="50" y1="85" x2="460" y2="85" class="grid-line"/>
|
||||
<line x1="50" y1="120" x2="460" y2="120" class="grid-line"/>
|
||||
<line x1="50" y1="155" x2="460" y2="155" class="grid-line"/>
|
||||
|
||||
<!-- Y-axis labels -->
|
||||
<text x="40" y="54" text-anchor="end" font-size="10" class="muted-text">2k</text>
|
||||
<text x="40" y="89" text-anchor="end" font-size="10" class="muted-text">1.5k</text>
|
||||
<text x="40" y="124" text-anchor="end" font-size="10" class="muted-text">1k</text>
|
||||
<text x="40" y="159" text-anchor="end" font-size="10" class="muted-text">500</text>
|
||||
|
||||
<!-- Line Chart Path -->
|
||||
<path d="M50,130 L100,110 L150,125 L200,95 L250,80 L300,90 L350,65 L400,75 L460,55" class="chart-line"/>
|
||||
<path d="M50,130 L100,110 L150,125 L200,95 L250,80 L300,90 L350,65 L400,75 L460,55 L460,155 L50,155 Z" class="chart-area"/>
|
||||
|
||||
<!-- Data Points -->
|
||||
<circle cx="50" cy="130" r="4" fill="#3b82f6"/>
|
||||
<circle cx="100" cy="110" r="4" fill="#3b82f6"/>
|
||||
<circle cx="150" cy="125" r="4" fill="#3b82f6"/>
|
||||
<circle cx="200" cy="95" r="4" fill="#3b82f6"/>
|
||||
<circle cx="250" cy="80" r="4" fill="#3b82f6"/>
|
||||
<circle cx="300" cy="90" r="4" fill="#3b82f6"/>
|
||||
<circle cx="350" cy="65" r="4" fill="#3b82f6"/>
|
||||
<circle cx="400" cy="75" r="4" fill="#3b82f6"/>
|
||||
<circle cx="460" cy="55" r="4" fill="#3b82f6"/>
|
||||
|
||||
<!-- X-axis labels -->
|
||||
<text x="50" y="175" text-anchor="middle" font-size="10" class="muted-text">1</text>
|
||||
<text x="150" y="175" text-anchor="middle" font-size="10" class="muted-text">8</text>
|
||||
<text x="250" y="175" text-anchor="middle" font-size="10" class="muted-text">15</text>
|
||||
<text x="350" y="175" text-anchor="middle" font-size="10" class="muted-text">22</text>
|
||||
<text x="460" y="175" text-anchor="middle" font-size="10" class="muted-text">30</text>
|
||||
<text x="255" y="193" text-anchor="middle" font-size="10" class="muted-text">March 2024</text>
|
||||
|
||||
<!-- Bar Chart: Messages by Type -->
|
||||
<rect x="500" y="0" width="285" height="200" rx="10" class="card-bg"/>
|
||||
<rect x="500" y="0" width="285" height="200" rx="10" class="border"/>
|
||||
<text x="520" y="25" font-size="14" font-weight="600" class="main-text">Messages by Type</text>
|
||||
|
||||
<!-- Bars -->
|
||||
<rect x="540" y="55" width="40" height="100" rx="4" class="bar-blue"/>
|
||||
<rect x="600" y="75" width="40" height="80" rx="4" class="bar-green"/>
|
||||
<rect x="660" y="95" width="40" height="60" rx="4" class="bar-purple"/>
|
||||
<rect x="720" y="115" width="40" height="40" rx="4" fill="#f97316"/>
|
||||
|
||||
<!-- Bar labels -->
|
||||
<text x="560" y="170" text-anchor="middle" font-size="10" class="muted-text">Chat</text>
|
||||
<text x="620" y="170" text-anchor="middle" font-size="10" class="muted-text">Email</text>
|
||||
<text x="680" y="170" text-anchor="middle" font-size="10" class="muted-text">Voice</text>
|
||||
<text x="740" y="170" text-anchor="middle" font-size="10" class="muted-text">Other</text>
|
||||
|
||||
<!-- Bar values -->
|
||||
<text x="560" y="50" text-anchor="middle" font-size="11" class="main-text">4.2k</text>
|
||||
<text x="620" y="70" text-anchor="middle" font-size="11" class="main-text">3.1k</text>
|
||||
<text x="680" y="90" text-anchor="middle" font-size="11" class="main-text">1.8k</text>
|
||||
<text x="740" y="110" text-anchor="middle" font-size="11" class="main-text">890</text>
|
||||
</g>
|
||||
|
||||
<!-- Bottom Row -->
|
||||
<g transform="translate(50, 440)">
|
||||
<!-- Top Queries Table -->
|
||||
<rect x="0" y="0" width="380" height="115" rx="10" class="card-bg"/>
|
||||
<rect x="0" y="0" width="380" height="115" rx="10" class="border"/>
|
||||
<text x="20" y="25" font-size="14" font-weight="600" class="main-text">Top Queries</text>
|
||||
|
||||
<text x="20" y="50" font-size="12" class="main-text">1. "How do I reset my password?"</text>
|
||||
<text x="330" y="50" text-anchor="end" font-size="12" class="secondary-text">847</text>
|
||||
|
||||
<text x="20" y="70" font-size="12" class="main-text">2. "Billing information"</text>
|
||||
<text x="330" y="70" text-anchor="end" font-size="12" class="secondary-text">623</text>
|
||||
|
||||
<text x="20" y="90" font-size="12" class="main-text">3. "Schedule a meeting"</text>
|
||||
<text x="330" y="90" text-anchor="end" font-size="12" class="secondary-text">512</text>
|
||||
|
||||
<text x="330" y="108" text-anchor="end" font-size="11" class="accent-text">View all →</text>
|
||||
|
||||
<!-- Performance Metrics -->
|
||||
<rect x="400" y="0" width="385" height="115" rx="10" class="card-bg"/>
|
||||
<rect x="400" y="0" width="385" height="115" rx="10" class="border"/>
|
||||
<text x="420" y="25" font-size="14" font-weight="600" class="main-text">Performance</text>
|
||||
|
||||
<text x="420" y="50" font-size="12" class="secondary-text">Resolution Rate</text>
|
||||
<rect x="420" y="58" width="200" height="8" rx="4" fill="#e2e8f0"/>
|
||||
<rect x="420" y="58" width="170" height="8" rx="4" fill="#22c55e"/>
|
||||
<text x="640" y="67" font-size="12" class="main-text">85%</text>
|
||||
|
||||
<text x="420" y="85" font-size="12" class="secondary-text">Escalation Rate</text>
|
||||
<rect x="420" y="93" width="200" height="8" rx="4" fill="#e2e8f0"/>
|
||||
<rect x="420" y="93" width="30" height="8" rx="4" fill="#f97316"/>
|
||||
<text x="640" y="102" font-size="12" class="main-text">15%</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(50, 565)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">R = Refresh | E = Export | D = Date range | F = Filter</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
265
docs/src/assets/suite/calendar-screen.svg
Normal file
|
|
@ -0,0 +1,265 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.today-circle { fill: #3b82f6; }
|
||||
.event-blue { fill: #3b82f6; }
|
||||
.event-green { fill: #22c55e; }
|
||||
.event-purple { fill: #8b5cf6; }
|
||||
.event-orange { fill: #f97316; }
|
||||
.grid-line { stroke: #e2e8f0; stroke-width: 1; }
|
||||
.hour-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.grid-line { stroke: #334155; }
|
||||
.hour-text { fill: #64748b; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Calendar - Schedule Manager</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<rect x="30" y="40" width="180" height="530" class="sidebar-bg"/>
|
||||
<line x1="210" y1="40" x2="210" y2="570" class="border"/>
|
||||
|
||||
<!-- New Event Button -->
|
||||
<g transform="translate(45, 55)">
|
||||
<rect x="0" y="0" width="150" height="40" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="75" y="26" text-anchor="middle" font-size="14" font-weight="500" class="white-text">+ New Event</text>
|
||||
</g>
|
||||
|
||||
<!-- Mini Calendar -->
|
||||
<g transform="translate(45, 115)">
|
||||
<g transform="translate(0, 0)">
|
||||
<text x="0" y="0" font-size="11">◀</text>
|
||||
<text x="75" y="0" text-anchor="middle" font-size="13" font-weight="500" class="main-text">March 2024</text>
|
||||
<text x="150" y="0" font-size="11">▶</text>
|
||||
</g>
|
||||
|
||||
<!-- Weekday Headers -->
|
||||
<g transform="translate(0, 20)">
|
||||
<text x="10" y="0" text-anchor="middle" font-size="10" class="muted-text">S</text>
|
||||
<text x="32" y="0" text-anchor="middle" font-size="10" class="muted-text">M</text>
|
||||
<text x="54" y="0" text-anchor="middle" font-size="10" class="muted-text">T</text>
|
||||
<text x="76" y="0" text-anchor="middle" font-size="10" class="muted-text">W</text>
|
||||
<text x="98" y="0" text-anchor="middle" font-size="10" class="muted-text">T</text>
|
||||
<text x="120" y="0" text-anchor="middle" font-size="10" class="muted-text">F</text>
|
||||
<text x="142" y="0" text-anchor="middle" font-size="10" class="muted-text">S</text>
|
||||
</g>
|
||||
|
||||
<!-- Week 1 -->
|
||||
<g transform="translate(0, 38)">
|
||||
<text x="10" y="0" text-anchor="middle" font-size="11" class="muted-text">25</text>
|
||||
<text x="32" y="0" text-anchor="middle" font-size="11" class="muted-text">26</text>
|
||||
<text x="54" y="0" text-anchor="middle" font-size="11" class="muted-text">27</text>
|
||||
<text x="76" y="0" text-anchor="middle" font-size="11" class="muted-text">28</text>
|
||||
<text x="98" y="0" text-anchor="middle" font-size="11" class="muted-text">29</text>
|
||||
<text x="120" y="0" text-anchor="middle" font-size="11" class="main-text">1</text>
|
||||
<text x="142" y="0" text-anchor="middle" font-size="11" class="main-text">2</text>
|
||||
</g>
|
||||
|
||||
<!-- Week 2 -->
|
||||
<g transform="translate(0, 56)">
|
||||
<text x="10" y="0" text-anchor="middle" font-size="11" class="main-text">3</text>
|
||||
<text x="32" y="0" text-anchor="middle" font-size="11" class="main-text">4</text>
|
||||
<text x="54" y="0" text-anchor="middle" font-size="11" class="main-text">5</text>
|
||||
<text x="76" y="0" text-anchor="middle" font-size="11" class="main-text">6</text>
|
||||
<text x="98" y="0" text-anchor="middle" font-size="11" class="main-text">7</text>
|
||||
<text x="120" y="0" text-anchor="middle" font-size="11" class="main-text">8</text>
|
||||
<text x="142" y="0" text-anchor="middle" font-size="11" class="main-text">9</text>
|
||||
</g>
|
||||
|
||||
<!-- Week 3 -->
|
||||
<g transform="translate(0, 74)">
|
||||
<text x="10" y="0" text-anchor="middle" font-size="11" class="main-text">10</text>
|
||||
<text x="32" y="0" text-anchor="middle" font-size="11" class="main-text">11</text>
|
||||
<text x="54" y="0" text-anchor="middle" font-size="11" class="main-text">12</text>
|
||||
<text x="76" y="0" text-anchor="middle" font-size="11" class="main-text">13</text>
|
||||
<text x="98" y="0" text-anchor="middle" font-size="11" class="main-text">14</text>
|
||||
<circle cx="120" cy="-4" r="12" class="today-circle"/>
|
||||
<text x="120" y="0" text-anchor="middle" font-size="11" class="white-text">15</text>
|
||||
<text x="142" y="0" text-anchor="middle" font-size="11" class="main-text">16</text>
|
||||
</g>
|
||||
|
||||
<!-- Week 4 -->
|
||||
<g transform="translate(0, 92)">
|
||||
<text x="10" y="0" text-anchor="middle" font-size="11" class="main-text">17</text>
|
||||
<text x="32" y="0" text-anchor="middle" font-size="11" class="main-text">18</text>
|
||||
<text x="54" y="0" text-anchor="middle" font-size="11" class="main-text">19</text>
|
||||
<text x="76" y="0" text-anchor="middle" font-size="11" class="main-text">20</text>
|
||||
<text x="98" y="0" text-anchor="middle" font-size="11" class="main-text">21</text>
|
||||
<text x="120" y="0" text-anchor="middle" font-size="11" class="main-text">22</text>
|
||||
<text x="142" y="0" text-anchor="middle" font-size="11" class="main-text">23</text>
|
||||
</g>
|
||||
|
||||
<!-- Week 5 -->
|
||||
<g transform="translate(0, 110)">
|
||||
<text x="10" y="0" text-anchor="middle" font-size="11" class="main-text">24</text>
|
||||
<text x="32" y="0" text-anchor="middle" font-size="11" class="main-text">25</text>
|
||||
<text x="54" y="0" text-anchor="middle" font-size="11" class="main-text">26</text>
|
||||
<text x="76" y="0" text-anchor="middle" font-size="11" class="main-text">27</text>
|
||||
<text x="98" y="0" text-anchor="middle" font-size="11" class="main-text">28</text>
|
||||
<text x="120" y="0" text-anchor="middle" font-size="11" class="main-text">29</text>
|
||||
<text x="142" y="0" text-anchor="middle" font-size="11" class="main-text">30</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Calendars List -->
|
||||
<g transform="translate(45, 270)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">MY CALENDARS</text>
|
||||
|
||||
<g transform="translate(0, 20)">
|
||||
<rect x="0" y="0" width="14" height="14" rx="3" fill="#3b82f6"/>
|
||||
<text x="22" y="12" font-size="13" class="main-text">Personal</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 45)">
|
||||
<rect x="0" y="0" width="14" height="14" rx="3" fill="#22c55e"/>
|
||||
<text x="22" y="12" font-size="13" class="main-text">Work</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 70)">
|
||||
<rect x="0" y="0" width="14" height="14" rx="3" fill="#8b5cf6"/>
|
||||
<text x="22" y="12" font-size="13" class="main-text">Family</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Main Calendar Area -->
|
||||
<!-- Header -->
|
||||
<g transform="translate(230, 55)">
|
||||
<rect x="0" y="0" width="70" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="35" y="21" text-anchor="middle" font-size="12" class="secondary-text">Today</text>
|
||||
|
||||
<text x="90" y="21" font-size="16" class="secondary-text">◀</text>
|
||||
<text x="115" y="21" font-size="16" class="secondary-text">▶</text>
|
||||
|
||||
<text x="150" y="22" font-size="16" font-weight="600" class="main-text">March 15, 2024</text>
|
||||
|
||||
<!-- View Selector -->
|
||||
<g transform="translate(380, 0)">
|
||||
<rect x="0" y="0" width="180" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="30" y="21" text-anchor="middle" font-size="12" class="muted-text">Day</text>
|
||||
<rect x="55" y="4" width="50" height="24" rx="4" fill="#3b82f6"/>
|
||||
<text x="80" y="21" text-anchor="middle" font-size="12" class="white-text">Week</text>
|
||||
<text x="130" y="21" text-anchor="middle" font-size="12" class="muted-text">Month</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Week Header -->
|
||||
<g transform="translate(230, 100)">
|
||||
<rect x="40" y="0" width="610" height="35" class="sidebar-bg"/>
|
||||
<line x1="230" y1="35" x2="850" y2="35" class="grid-line"/>
|
||||
|
||||
<text x="80" y="15" text-anchor="middle" font-size="11" class="secondary-text">SUN</text>
|
||||
<text x="80" y="30" text-anchor="middle" font-size="14" class="secondary-text">10</text>
|
||||
|
||||
<text x="165" y="15" text-anchor="middle" font-size="11" class="secondary-text">MON</text>
|
||||
<text x="165" y="30" text-anchor="middle" font-size="14" class="secondary-text">11</text>
|
||||
|
||||
<text x="250" y="15" text-anchor="middle" font-size="11" class="secondary-text">TUE</text>
|
||||
<text x="250" y="30" text-anchor="middle" font-size="14" class="secondary-text">12</text>
|
||||
|
||||
<text x="335" y="15" text-anchor="middle" font-size="11" class="secondary-text">WED</text>
|
||||
<text x="335" y="30" text-anchor="middle" font-size="14" class="secondary-text">13</text>
|
||||
|
||||
<text x="420" y="15" text-anchor="middle" font-size="11" class="secondary-text">THU</text>
|
||||
<text x="420" y="30" text-anchor="middle" font-size="14" class="secondary-text">14</text>
|
||||
|
||||
<text x="505" y="15" text-anchor="middle" font-size="11" class="accent-text">FRI</text>
|
||||
<circle cx="505" cy="26" r="12" class="today-circle"/>
|
||||
<text x="505" y="31" text-anchor="middle" font-size="14" class="white-text">15</text>
|
||||
|
||||
<text x="590" y="15" text-anchor="middle" font-size="11" class="secondary-text">SAT</text>
|
||||
<text x="590" y="30" text-anchor="middle" font-size="14" class="secondary-text">16</text>
|
||||
</g>
|
||||
|
||||
<!-- Time Grid -->
|
||||
<g transform="translate(230, 140)">
|
||||
<!-- Time labels -->
|
||||
<text x="20" y="12" text-anchor="middle" font-size="10" class="hour-text">8 AM</text>
|
||||
<text x="20" y="52" text-anchor="middle" font-size="10" class="hour-text">9 AM</text>
|
||||
<text x="20" y="92" text-anchor="middle" font-size="10" class="hour-text">10 AM</text>
|
||||
<text x="20" y="132" text-anchor="middle" font-size="10" class="hour-text">11 AM</text>
|
||||
<text x="20" y="172" text-anchor="middle" font-size="10" class="hour-text">12 PM</text>
|
||||
<text x="20" y="212" text-anchor="middle" font-size="10" class="hour-text">1 PM</text>
|
||||
<text x="20" y="252" text-anchor="middle" font-size="10" class="hour-text">2 PM</text>
|
||||
<text x="20" y="292" text-anchor="middle" font-size="10" class="hour-text">3 PM</text>
|
||||
<text x="20" y="332" text-anchor="middle" font-size="10" class="hour-text">4 PM</text>
|
||||
<text x="20" y="372" text-anchor="middle" font-size="10" class="hour-text">5 PM</text>
|
||||
|
||||
<!-- Grid lines -->
|
||||
<line x1="40" y1="5" x2="620" y2="5" class="grid-line"/>
|
||||
<line x1="40" y1="45" x2="620" y2="45" class="grid-line"/>
|
||||
<line x1="40" y1="85" x2="620" y2="85" class="grid-line"/>
|
||||
<line x1="40" y1="125" x2="620" y2="125" class="grid-line"/>
|
||||
<line x1="40" y1="165" x2="620" y2="165" class="grid-line"/>
|
||||
<line x1="40" y1="205" x2="620" y2="205" class="grid-line"/>
|
||||
<line x1="40" y1="245" x2="620" y2="245" class="grid-line"/>
|
||||
<line x1="40" y1="285" x2="620" y2="285" class="grid-line"/>
|
||||
<line x1="40" y1="325" x2="620" y2="325" class="grid-line"/>
|
||||
<line x1="40" y1="365" x2="620" y2="365" class="grid-line"/>
|
||||
|
||||
<!-- Column dividers -->
|
||||
<line x1="125" y1="0" x2="125" y2="385" class="grid-line"/>
|
||||
<line x1="210" y1="0" x2="210" y2="385" class="grid-line"/>
|
||||
<line x1="295" y1="0" x2="295" y2="385" class="grid-line"/>
|
||||
<line x1="380" y1="0" x2="380" y2="385" class="grid-line"/>
|
||||
<line x1="465" y1="0" x2="465" y2="385" class="grid-line"/>
|
||||
<line x1="550" y1="0" x2="550" y2="385" class="grid-line"/>
|
||||
|
||||
<!-- Events -->
|
||||
<!-- Monday 9-10 AM -->
|
||||
<rect x="127" y="45" width="80" height="40" rx="4" class="event-green"/>
|
||||
<text x="135" y="62" font-size="10" font-weight="500" class="white-text">Team Standup</text>
|
||||
<text x="135" y="75" font-size="9" class="white-text">9:00 - 10:00</text>
|
||||
|
||||
<!-- Wednesday 11 AM - 12 PM -->
|
||||
<rect x="297" y="125" width="80" height="40" rx="4" class="event-blue"/>
|
||||
<text x="305" y="142" font-size="10" font-weight="500" class="white-text">Client Call</text>
|
||||
<text x="305" y="155" font-size="9" class="white-text">11:00 - 12:00</text>
|
||||
|
||||
<!-- Friday (Today) 2-3 PM -->
|
||||
<rect x="467" y="245" width="80" height="40" rx="4" class="event-purple"/>
|
||||
<text x="475" y="262" font-size="10" font-weight="500" class="white-text">Project Review</text>
|
||||
<text x="475" y="275" font-size="9" class="white-text">2:00 - 3:00</text>
|
||||
|
||||
<!-- Friday 4-5 PM -->
|
||||
<rect x="467" y="325" width="80" height="40" rx="4" class="event-orange"/>
|
||||
<text x="475" y="342" font-size="10" font-weight="500" class="white-text">1:1 Meeting</text>
|
||||
<text x="475" y="355" font-size="9" class="white-text">4:00 - 5:00</text>
|
||||
|
||||
<!-- Current time indicator (Friday ~2:30 PM) -->
|
||||
<line x1="465" y1="265" x2="550" y2="265" stroke="#ef4444" stroke-width="2"/>
|
||||
<circle cx="465" cy="265" r="4" fill="#ef4444"/>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(230, 555)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Click to create | Drag to reschedule | Double-click to edit</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
182
docs/src/assets/suite/chat-screen.svg
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||
<stop offset="100%" style="stop-color:#60a5fa"/>
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-opacity="0.1"/>
|
||||
</filter>
|
||||
<filter id="shadowSm" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.header-bg { fill: rgba(255,255,255,0.8); }
|
||||
.main-text { fill: #1e293b; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.card-bg { fill: #f8fafc; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.user-bubble { fill: #3b82f6; }
|
||||
.bot-bubble { fill: #f8fafc; stroke: #e2e8f0; stroke-width: 1; }
|
||||
.input-bg { fill: #f1f5f9; }
|
||||
.suggestion { fill: #eff6ff; stroke: #3b82f6; stroke-width: 1; }
|
||||
.online-dot { fill: #22c55e; }
|
||||
.typing-dot { fill: #94a3b8; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.header-bg { fill: rgba(15,23,42,0.8); }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.card-bg { fill: #1e293b; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #1e293b; }
|
||||
.user-bubble { fill: #3b82f6; }
|
||||
.bot-bubble { fill: #1e293b; stroke: #334155; }
|
||||
.input-bg { fill: #1e293b; }
|
||||
.suggestion { fill: #1e3a5f; stroke: #3b82f6; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Chat - AI Assistant</text>
|
||||
|
||||
<!-- Background -->
|
||||
<rect x="30" y="40" width="840" height="530" rx="12" class="bg" filter="url(#shadow)"/>
|
||||
|
||||
<!-- Floating Header (Glass morphism) -->
|
||||
<rect x="50" y="55" width="800" height="56" rx="12" class="header-bg" filter="url(#shadowSm)"/>
|
||||
<rect x="50" y="55" width="800" height="56" rx="12" class="border"/>
|
||||
|
||||
<!-- Logo -->
|
||||
<rect x="70" y="70" width="28" height="28" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="84" y="89" text-anchor="middle" font-size="14" class="white-text">GB</text>
|
||||
<text x="110" y="90" font-size="15" font-weight="600" class="main-text">General Bots</text>
|
||||
|
||||
<!-- Header Right: Theme, Apps Grid, Avatar -->
|
||||
<g transform="translate(650, 68)">
|
||||
<!-- Theme dropdown -->
|
||||
<rect x="0" y="0" width="70" height="32" rx="8" class="icon-btn"/>
|
||||
<text x="15" y="21" font-size="12">☀️</text>
|
||||
<text x="32" y="22" font-size="12" class="secondary-text">Light</text>
|
||||
|
||||
<!-- Apps grid button -->
|
||||
<rect x="80" y="0" width="40" height="32" rx="8" class="icon-btn"/>
|
||||
<g transform="translate(91, 8)">
|
||||
<circle cx="4" cy="4" r="2.5" fill="#64748b"/>
|
||||
<circle cx="13" cy="4" r="2.5" fill="#64748b"/>
|
||||
<circle cx="4" cy="13" r="2.5" fill="#64748b"/>
|
||||
<circle cx="13" cy="13" r="2.5" fill="#64748b"/>
|
||||
</g>
|
||||
|
||||
<!-- User avatar -->
|
||||
<circle cx="155" cy="16" r="16" fill="url(#accentGrad)"/>
|
||||
<text x="155" y="21" text-anchor="middle" font-size="11" font-weight="500" class="white-text">JD</text>
|
||||
</g>
|
||||
|
||||
<!-- Chat Layout Container -->
|
||||
<rect x="50" y="125" width="800" height="430" rx="0" class="bg"/>
|
||||
|
||||
<!-- Connection Status -->
|
||||
<g transform="translate(60, 135)">
|
||||
<circle cx="5" cy="5" r="4" class="online-dot"/>
|
||||
</g>
|
||||
|
||||
<!-- Messages Area -->
|
||||
<g transform="translate(80, 160)">
|
||||
<!-- Bot Welcome Message -->
|
||||
<g transform="translate(0, 0)">
|
||||
<rect x="0" y="0" width="420" height="70" rx="16" class="bot-bubble"/>
|
||||
<text x="20" y="25" font-size="12" font-weight="500" class="accent-text">AI Assistant</text>
|
||||
<text x="20" y="48" font-size="14" class="main-text">Hello! I'm your AI assistant. How can I help</text>
|
||||
<text x="20" y="65" font-size="14" class="main-text">you today?</text>
|
||||
</g>
|
||||
|
||||
<!-- User Message -->
|
||||
<g transform="translate(280, 90)">
|
||||
<rect x="0" y="0" width="420" height="50" rx="16" class="user-bubble"/>
|
||||
<text x="20" y="32" font-size="14" class="white-text">What meetings do I have scheduled for today?</text>
|
||||
</g>
|
||||
|
||||
<!-- Bot Response with structured data -->
|
||||
<g transform="translate(0, 160)">
|
||||
<rect x="0" y="0" width="480" height="120" rx="16" class="bot-bubble"/>
|
||||
<text x="20" y="25" font-size="12" font-weight="500" class="accent-text">AI Assistant</text>
|
||||
<text x="20" y="50" font-size="14" class="main-text">You have 3 meetings scheduled for today:</text>
|
||||
<text x="20" y="72" font-size="13" class="secondary-text">📅 10:00 AM - Team Standup (30 min)</text>
|
||||
<text x="20" y="92" font-size="13" class="secondary-text">📅 2:00 PM - Client Call with Acme Corp (1 hr)</text>
|
||||
<text x="20" y="112" font-size="13" class="secondary-text">📅 4:30 PM - 1:1 with Manager (30 min)</text>
|
||||
</g>
|
||||
|
||||
<!-- Typing Indicator -->
|
||||
<g transform="translate(0, 300)">
|
||||
<rect x="0" y="0" width="80" height="40" rx="16" class="bot-bubble"/>
|
||||
<circle cx="25" cy="20" r="5" class="typing-dot">
|
||||
<animate attributeName="opacity" values="0.4;1;0.4" dur="1.2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="42" cy="20" r="5" class="typing-dot">
|
||||
<animate attributeName="opacity" values="0.4;1;0.4" dur="1.2s" begin="0.2s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
<circle cx="59" cy="20" r="5" class="typing-dot">
|
||||
<animate attributeName="opacity" values="0.4;1;0.4" dur="1.2s" begin="0.4s" repeatCount="indefinite"/>
|
||||
</circle>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Suggestions Container -->
|
||||
<g transform="translate(80, 460)">
|
||||
<rect x="0" y="0" width="130" height="36" rx="18" class="suggestion"/>
|
||||
<text x="65" y="23" text-anchor="middle" font-size="12" class="accent-text">📧 Check my email</text>
|
||||
|
||||
<rect x="145" y="0" width="125" height="36" rx="18" class="suggestion"/>
|
||||
<text x="207" y="23" text-anchor="middle" font-size="12" class="accent-text">✓ Show my tasks</text>
|
||||
|
||||
<rect x="285" y="0" width="140" height="36" rx="18" class="suggestion"/>
|
||||
<text x="355" y="23" text-anchor="middle" font-size="12" class="accent-text">🔍 Search documents</text>
|
||||
|
||||
<rect x="440" y="0" width="100" height="36" rx="18" class="suggestion"/>
|
||||
<text x="490" y="23" text-anchor="middle" font-size="12" class="accent-text">📝 Take notes</text>
|
||||
</g>
|
||||
|
||||
<!-- Input Container (Footer) -->
|
||||
<g transform="translate(80, 505)">
|
||||
<!-- Input Field -->
|
||||
<rect x="0" y="0" width="620" height="50" rx="25" class="input-bg"/>
|
||||
<rect x="0" y="0" width="620" height="50" rx="25" class="border"/>
|
||||
<text x="25" y="31" font-size="14" class="muted-text">Message...</text>
|
||||
|
||||
<!-- Voice Button -->
|
||||
<g transform="translate(530, 9)">
|
||||
<rect x="0" y="0" width="36" height="32" rx="16" class="icon-btn"/>
|
||||
<text x="18" y="22" text-anchor="middle" font-size="16">🎤</text>
|
||||
</g>
|
||||
|
||||
<!-- Send Button -->
|
||||
<g transform="translate(640, 0)">
|
||||
<rect x="0" y="0" width="60" height="50" rx="25" fill="url(#accentGrad)"/>
|
||||
<text x="30" y="32" text-anchor="middle" font-size="18" class="white-text">↑</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Scroll to Bottom Button -->
|
||||
<g transform="translate(800, 440)">
|
||||
<circle cx="0" cy="0" r="20" class="icon-btn" filter="url(#shadowSm)"/>
|
||||
<circle cx="0" cy="0" r="20" class="border"/>
|
||||
<text x="0" y="6" text-anchor="middle" font-size="16" class="secondary-text">↓</text>
|
||||
</g>
|
||||
|
||||
<!-- Keyboard Shortcuts Legend -->
|
||||
<g transform="translate(80, 565)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">💡 Tip: Use / for commands, @ to mention, # for tags</text>
|
||||
<text x="450" y="0" font-size="10" class="muted-text">Enter = Send | Shift+Enter = New line | ↑ = Edit last</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.3 KiB |
279
docs/src/assets/suite/compliance-screen.svg
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#22c55e"/>
|
||||
<stop offset="100%" stop-color="#4ade80"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="redGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ef4444"/>
|
||||
<stop offset="100%" stop-color="#f87171"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#f97316"/>
|
||||
<stop offset="100%" stop-color="#fb923c"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.green-text { fill: #22c55e; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.red-text { fill: #ef4444; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.orange-text { fill: #f97316; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.card-bg { fill: #f8fafc; }
|
||||
.status-pass { fill: #dcfce7; }
|
||||
.status-fail { fill: #fee2e2; }
|
||||
.status-warn { fill: #fef3c7; }
|
||||
.progress-bg { fill: #e2e8f0; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.green-text { fill: #4ade80; }
|
||||
.red-text { fill: #f87171; }
|
||||
.orange-text { fill: #fb923c; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.card-bg { fill: #1e293b; }
|
||||
.status-pass { fill: #14532d; }
|
||||
.status-fail { fill: #450a0a; }
|
||||
.status-warn { fill: #451a03; }
|
||||
.progress-bg { fill: #334155; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Compliance - Security Scanner</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<rect x="30" y="40" width="180" height="530" class="sidebar-bg"/>
|
||||
<line x1="210" y1="40" x2="210" y2="570" class="border"/>
|
||||
|
||||
<!-- Scan Button -->
|
||||
<g transform="translate(45, 55)">
|
||||
<rect x="0" y="0" width="150" height="40" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="75" y="26" text-anchor="middle" font-size="14" font-weight="500" class="white-text">🔍 Run Scan</text>
|
||||
</g>
|
||||
|
||||
<!-- Navigation -->
|
||||
<g transform="translate(45, 115)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">SCAN TYPES</text>
|
||||
|
||||
<g transform="translate(0, 20)">
|
||||
<rect x="-10" y="-8" width="160" height="36" rx="6" fill="#eff6ff"/>
|
||||
<text x="0" y="14" font-size="14">🛡️</text>
|
||||
<text x="25" y="14" font-size="13" class="accent-text">All Checks</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 60)">
|
||||
<text x="0" y="14" font-size="14">🔐</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Security</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 95)">
|
||||
<text x="0" y="14" font-size="14">📋</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Privacy</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 130)">
|
||||
<text x="0" y="14" font-size="14">⚡</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Performance</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 165)">
|
||||
<text x="0" y="14" font-size="14">♿</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Accessibility</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Compliance Frameworks -->
|
||||
<g transform="translate(45, 320)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">FRAMEWORKS</text>
|
||||
|
||||
<g transform="translate(0, 20)">
|
||||
<rect x="0" y="0" width="14" height="14" rx="3" fill="#22c55e"/>
|
||||
<text x="22" y="12" font-size="12" class="main-text">GDPR</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 40)">
|
||||
<rect x="0" y="0" width="14" height="14" rx="3" fill="#22c55e"/>
|
||||
<text x="22" y="12" font-size="12" class="main-text">SOC 2</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 60)">
|
||||
<rect x="0" y="0" width="14" height="14" rx="3" fill="#f97316"/>
|
||||
<text x="22" y="12" font-size="12" class="main-text">HIPAA</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 80)">
|
||||
<rect x="0" y="0" width="14" height="14" rx="3" fill="#22c55e"/>
|
||||
<text x="22" y="12" font-size="12" class="main-text">ISO 27001</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Main Content Header -->
|
||||
<g transform="translate(230, 55)">
|
||||
<text x="0" y="20" font-size="18" font-weight="600" class="main-text">Security Overview</text>
|
||||
<text x="0" y="40" font-size="13" class="secondary-text">Last scan: 5 minutes ago • Next scheduled: 24 hours</text>
|
||||
|
||||
<!-- Export Button -->
|
||||
<g transform="translate(500, 5)">
|
||||
<rect x="0" y="0" width="100" height="32" rx="6" class="icon-btn"/>
|
||||
<rect x="0" y="0" width="100" height="32" rx="6" class="border"/>
|
||||
<text x="50" y="21" text-anchor="middle" font-size="12" class="secondary-text">📄 Export</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Score Cards Row -->
|
||||
<g transform="translate(230, 100)">
|
||||
<!-- Overall Score -->
|
||||
<rect x="0" y="0" width="145" height="90" rx="10" class="card-bg"/>
|
||||
<rect x="0" y="0" width="145" height="90" rx="10" class="border"/>
|
||||
<text x="15" y="25" font-size="11" class="secondary-text">Overall Score</text>
|
||||
<text x="15" y="60" font-size="32" font-weight="700" class="green-text">87</text>
|
||||
<text x="60" y="60" font-size="14" class="muted-text">/100</text>
|
||||
<text x="15" y="78" font-size="11" class="green-text">↑ 5 pts</text>
|
||||
|
||||
<!-- Passed -->
|
||||
<rect x="160" y="0" width="145" height="90" rx="10" class="card-bg"/>
|
||||
<rect x="160" y="0" width="145" height="90" rx="10" class="border"/>
|
||||
<text x="175" y="25" font-size="11" class="secondary-text">Passed</text>
|
||||
<text x="175" y="60" font-size="32" font-weight="700" class="main-text">42</text>
|
||||
<rect x="245" y="20" width="45" height="20" rx="10" class="status-pass"/>
|
||||
<text x="267" y="34" text-anchor="middle" font-size="10" class="green-text">89%</text>
|
||||
|
||||
<!-- Warnings -->
|
||||
<rect x="320" y="0" width="145" height="90" rx="10" class="card-bg"/>
|
||||
<rect x="320" y="0" width="145" height="90" rx="10" class="border"/>
|
||||
<text x="335" y="25" font-size="11" class="secondary-text">Warnings</text>
|
||||
<text x="335" y="60" font-size="32" font-weight="700" class="orange-text">4</text>
|
||||
<rect x="405" y="20" width="45" height="20" rx="10" class="status-warn"/>
|
||||
<text x="427" y="34" text-anchor="middle" font-size="10" class="orange-text">8%</text>
|
||||
|
||||
<!-- Critical -->
|
||||
<rect x="480" y="0" width="145" height="90" rx="10" class="card-bg"/>
|
||||
<rect x="480" y="0" width="145" height="90" rx="10" class="border"/>
|
||||
<text x="495" y="25" font-size="11" class="secondary-text">Critical</text>
|
||||
<text x="495" y="60" font-size="32" font-weight="700" class="red-text">1</text>
|
||||
<rect x="565" y="20" width="45" height="20" rx="10" class="status-fail"/>
|
||||
<text x="587" y="34" text-anchor="middle" font-size="10" class="red-text">2%</text>
|
||||
</g>
|
||||
|
||||
<!-- Issues List -->
|
||||
<g transform="translate(230, 210)">
|
||||
<text x="0" y="0" font-size="14" font-weight="600" class="main-text">Issues Found</text>
|
||||
|
||||
<!-- Critical Issue -->
|
||||
<g transform="translate(0, 20)">
|
||||
<rect x="0" y="0" width="610" height="65" rx="8" class="status-fail"/>
|
||||
<rect x="0" y="0" width="610" height="65" rx="8" class="border"/>
|
||||
<rect x="15" y="15" width="35" height="35" rx="8" fill="url(#redGrad)"/>
|
||||
<text x="32" y="38" text-anchor="middle" font-size="14" class="white-text">🚨</text>
|
||||
<text x="65" y="28" font-size="13" font-weight="600" class="main-text">Insecure API Endpoint Detected</text>
|
||||
<text x="65" y="48" font-size="12" class="secondary-text">/api/v1/users lacks authentication • Found 2 hours ago</text>
|
||||
<rect x="500" y="18" width="55" height="24" rx="6" class="icon-btn"/>
|
||||
<text x="527" y="35" text-anchor="middle" font-size="11" class="red-text">Critical</text>
|
||||
<rect x="560" y="18" width="35" height="24" rx="6" fill="url(#accentGrad)"/>
|
||||
<text x="577" y="35" text-anchor="middle" font-size="11" class="white-text">Fix</text>
|
||||
</g>
|
||||
|
||||
<!-- Warning Issue 1 -->
|
||||
<g transform="translate(0, 95)">
|
||||
<rect x="0" y="0" width="610" height="65" rx="8" class="status-warn"/>
|
||||
<rect x="0" y="0" width="610" height="65" rx="8" class="border"/>
|
||||
<rect x="15" y="15" width="35" height="35" rx="8" fill="url(#orangeGrad)"/>
|
||||
<text x="32" y="38" text-anchor="middle" font-size="14" class="white-text">⚠️</text>
|
||||
<text x="65" y="28" font-size="13" font-weight="600" class="main-text">Outdated SSL Certificate</text>
|
||||
<text x="65" y="48" font-size="12" class="secondary-text">Certificate expires in 15 days • Renewal recommended</text>
|
||||
<rect x="500" y="18" width="55" height="24" rx="6" class="icon-btn"/>
|
||||
<text x="527" y="35" text-anchor="middle" font-size="11" class="orange-text">Warning</text>
|
||||
<rect x="560" y="18" width="35" height="24" rx="6" fill="url(#accentGrad)"/>
|
||||
<text x="577" y="35" text-anchor="middle" font-size="11" class="white-text">Fix</text>
|
||||
</g>
|
||||
|
||||
<!-- Warning Issue 2 -->
|
||||
<g transform="translate(0, 170)">
|
||||
<rect x="0" y="0" width="610" height="65" rx="8" class="status-warn"/>
|
||||
<rect x="0" y="0" width="610" height="65" rx="8" class="border"/>
|
||||
<rect x="15" y="15" width="35" height="35" rx="8" fill="url(#orangeGrad)"/>
|
||||
<text x="32" y="38" text-anchor="middle" font-size="14" class="white-text">⚠️</text>
|
||||
<text x="65" y="28" font-size="13" font-weight="600" class="main-text">Missing Content Security Policy</text>
|
||||
<text x="65" y="48" font-size="12" class="secondary-text">CSP header not configured • XSS vulnerability risk</text>
|
||||
<rect x="500" y="18" width="55" height="24" rx="6" class="icon-btn"/>
|
||||
<text x="527" y="35" text-anchor="middle" font-size="11" class="orange-text">Warning</text>
|
||||
<rect x="560" y="18" width="35" height="24" rx="6" fill="url(#accentGrad)"/>
|
||||
<text x="577" y="35" text-anchor="middle" font-size="11" class="white-text">Fix</text>
|
||||
</g>
|
||||
|
||||
<!-- Passed Item -->
|
||||
<g transform="translate(0, 245)">
|
||||
<rect x="0" y="0" width="610" height="55" rx="8" class="status-pass"/>
|
||||
<rect x="0" y="0" width="610" height="55" rx="8" class="border"/>
|
||||
<rect x="15" y="10" width="35" height="35" rx="8" fill="url(#greenGrad)"/>
|
||||
<text x="32" y="33" text-anchor="middle" font-size="14" class="white-text">✓</text>
|
||||
<text x="65" y="25" font-size="13" font-weight="500" class="main-text">Data Encryption at Rest</text>
|
||||
<text x="65" y="43" font-size="12" class="secondary-text">AES-256 encryption enabled • All data stores compliant</text>
|
||||
<rect x="520" y="15" width="55" height="24" rx="6" class="icon-btn"/>
|
||||
<text x="547" y="32" text-anchor="middle" font-size="11" class="green-text">Passed</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Compliance Progress Section -->
|
||||
<g transform="translate(230, 480)">
|
||||
<text x="0" y="0" font-size="14" font-weight="600" class="main-text">Compliance Progress</text>
|
||||
|
||||
<!-- GDPR Progress -->
|
||||
<g transform="translate(0, 20)">
|
||||
<text x="0" y="12" font-size="12" class="main-text">GDPR</text>
|
||||
<rect x="80" y="3" width="200" height="12" rx="6" class="progress-bg"/>
|
||||
<rect x="80" y="3" width="190" height="12" rx="6" fill="#22c55e"/>
|
||||
<text x="290" y="14" font-size="12" class="green-text">95%</text>
|
||||
</g>
|
||||
|
||||
<!-- SOC 2 Progress -->
|
||||
<g transform="translate(0, 45)">
|
||||
<text x="0" y="12" font-size="12" class="main-text">SOC 2</text>
|
||||
<rect x="80" y="3" width="200" height="12" rx="6" class="progress-bg"/>
|
||||
<rect x="80" y="3" width="180" height="12" rx="6" fill="#22c55e"/>
|
||||
<text x="290" y="14" font-size="12" class="green-text">90%</text>
|
||||
</g>
|
||||
|
||||
<!-- HIPAA Progress -->
|
||||
<g transform="translate(350, 20)">
|
||||
<text x="0" y="12" font-size="12" class="main-text">HIPAA</text>
|
||||
<rect x="80" y="3" width="200" height="12" rx="6" class="progress-bg"/>
|
||||
<rect x="80" y="3" width="150" height="12" rx="6" fill="#f97316"/>
|
||||
<text x="290" y="14" font-size="12" class="orange-text">75%</text>
|
||||
</g>
|
||||
|
||||
<!-- ISO 27001 Progress -->
|
||||
<g transform="translate(350, 45)">
|
||||
<text x="0" y="12" font-size="12" class="main-text">ISO 27001</text>
|
||||
<rect x="80" y="3" width="200" height="12" rx="6" class="progress-bg"/>
|
||||
<rect x="80" y="3" width="176" height="12" rx="6" fill="#22c55e"/>
|
||||
<text x="290" y="14" font-size="12" class="green-text">88%</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(230, 560)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">R = Rescan | F = Filter by severity | E = Export report | Enter = View details</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
279
docs/src/assets/suite/designer-screen.svg
Normal file
|
|
@ -0,0 +1,279 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#22c55e"/>
|
||||
<stop offset="100%" stop-color="#4ade80"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="purpleGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#8b5cf6"/>
|
||||
<stop offset="100%" stop-color="#a78bfa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#f97316"/>
|
||||
<stop offset="100%" stop-color="#fb923c"/>
|
||||
</linearGradient>
|
||||
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
|
||||
<polygon points="0 0, 10 3.5, 0 7" fill="#94a3b8"/>
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.canvas-bg { fill: #f1f5f9; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.node { fill: #ffffff; stroke: #e2e8f0; stroke-width: 2; }
|
||||
.node-selected { fill: #ffffff; stroke: #3b82f6; stroke-width: 2; }
|
||||
.connector { stroke: #94a3b8; stroke-width: 2; fill: none; }
|
||||
.grid-dot { fill: #cbd5e1; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.canvas-bg { fill: #0f172a; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.node { fill: #1e293b; stroke: #334155; }
|
||||
.node-selected { fill: #1e293b; stroke: #3b82f6; }
|
||||
.connector { stroke: #64748b; }
|
||||
.grid-dot { fill: #334155; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Designer - Visual Flow Builder</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Left Toolbox Sidebar -->
|
||||
<rect x="30" y="40" width="160" height="530" class="sidebar-bg"/>
|
||||
<line x1="190" y1="40" x2="190" y2="570" class="border"/>
|
||||
|
||||
<text x="45" y="65" font-size="13" font-weight="600" class="main-text">Components</text>
|
||||
|
||||
<!-- Component Categories -->
|
||||
<g transform="translate(45, 85)">
|
||||
<!-- Triggers -->
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">TRIGGERS</text>
|
||||
|
||||
<g transform="translate(0, 15)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="url(#greenGrad)"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">▶</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">Start</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 55)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="url(#accentGrad)"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">💬</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">Message</text>
|
||||
</g>
|
||||
|
||||
<!-- Actions -->
|
||||
<text x="0" y="110" font-size="11" font-weight="600" class="muted-text">ACTIONS</text>
|
||||
|
||||
<g transform="translate(0, 125)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="url(#purpleGrad)"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">🤖</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">AI Response</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 165)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="url(#orangeGrad)"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">📧</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">Send Email</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 205)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="#ef4444"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">⚡</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">API Call</text>
|
||||
</g>
|
||||
|
||||
<!-- Logic -->
|
||||
<text x="0" y="260" font-size="11" font-weight="600" class="muted-text">LOGIC</text>
|
||||
|
||||
<g transform="translate(0, 275)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="#06b6d4"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">◇</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">Condition</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 315)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="#ec4899"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">⏱</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">Wait</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 355)">
|
||||
<rect x="0" y="0" width="130" height="35" rx="6" class="icon-btn"/>
|
||||
<rect x="10" y="8" width="20" height="20" rx="4" fill="#64748b"/>
|
||||
<text x="20" y="22" text-anchor="middle" font-size="10" class="white-text">⬤</text>
|
||||
<text x="40" y="23" font-size="11" class="main-text">End</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Canvas Area -->
|
||||
<rect x="200" y="50" width="550" height="510" class="canvas-bg"/>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<g transform="translate(210, 60)">
|
||||
<rect x="0" y="0" width="530" height="40" rx="8" class="bg"/>
|
||||
<rect x="0" y="0" width="530" height="40" rx="8" class="border"/>
|
||||
|
||||
<rect x="10" y="7" width="70" height="26" rx="6" fill="url(#accentGrad)"/>
|
||||
<text x="45" y="25" text-anchor="middle" font-size="11" class="white-text">+ Add</text>
|
||||
|
||||
<rect x="90" y="7" width="32" height="26" rx="6" class="icon-btn"/>
|
||||
<text x="106" y="25" text-anchor="middle" font-size="14" class="secondary-text">↩</text>
|
||||
|
||||
<rect x="130" y="7" width="32" height="26" rx="6" class="icon-btn"/>
|
||||
<text x="146" y="25" text-anchor="middle" font-size="14" class="secondary-text">↪</text>
|
||||
|
||||
<line x1="175" y1="10" x2="175" y2="30" class="border"/>
|
||||
|
||||
<rect x="185" y="7" width="32" height="26" rx="6" class="icon-btn"/>
|
||||
<text x="201" y="25" text-anchor="middle" font-size="12" class="secondary-text">🔍</text>
|
||||
|
||||
<rect x="225" y="7" width="32" height="26" rx="6" class="icon-btn"/>
|
||||
<text x="241" y="24" text-anchor="middle" font-size="14" class="secondary-text">+</text>
|
||||
|
||||
<rect x="265" y="7" width="32" height="26" rx="6" class="icon-btn"/>
|
||||
<text x="281" y="25" text-anchor="middle" font-size="14" class="secondary-text">−</text>
|
||||
|
||||
<text x="320" y="25" font-size="11" class="muted-text">100%</text>
|
||||
|
||||
<line x1="360" y1="10" x2="360" y2="30" class="border"/>
|
||||
|
||||
<rect x="370" y="7" width="70" height="26" rx="6" class="icon-btn"/>
|
||||
<text x="405" y="25" text-anchor="middle" font-size="11" class="secondary-text">▶ Test</text>
|
||||
|
||||
<rect x="450" y="7" width="70" height="26" rx="6" fill="url(#greenGrad)"/>
|
||||
<text x="485" y="25" text-anchor="middle" font-size="11" class="white-text">Deploy</text>
|
||||
</g>
|
||||
|
||||
<!-- Grid Dots (decorative) -->
|
||||
<g transform="translate(210, 110)">
|
||||
<!-- Row 1 -->
|
||||
<circle cx="20" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="60" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="100" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="140" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="180" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="220" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="260" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="300" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="340" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="380" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="420" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="460" cy="20" r="1.5" class="grid-dot"/>
|
||||
<circle cx="500" cy="20" r="1.5" class="grid-dot"/>
|
||||
</g>
|
||||
|
||||
<!-- Flow Nodes and Connections -->
|
||||
<!-- Connection Lines -->
|
||||
<path d="M310,195 L310,240" class="connector" marker-end="url(#arrowhead)"/>
|
||||
<path d="M310,315 L310,360" class="connector" marker-end="url(#arrowhead)"/>
|
||||
<path d="M310,435 L240,435 L240,480" class="connector" marker-end="url(#arrowhead)"/>
|
||||
<path d="M310,435 L380,435 L380,480" class="connector" marker-end="url(#arrowhead)"/>
|
||||
|
||||
<!-- Start Node -->
|
||||
<g transform="translate(235, 140)">
|
||||
<rect x="0" y="0" width="150" height="55" rx="10" class="node"/>
|
||||
<rect x="10" y="12" width="32" height="32" rx="8" fill="url(#greenGrad)"/>
|
||||
<text x="26" y="34" text-anchor="middle" font-size="16" class="white-text">▶</text>
|
||||
<text x="55" y="28" font-size="13" font-weight="500" class="main-text">Start</text>
|
||||
<text x="55" y="44" font-size="10" class="secondary-text">User message</text>
|
||||
</g>
|
||||
|
||||
<!-- AI Response Node (Selected) -->
|
||||
<g transform="translate(235, 250)">
|
||||
<rect x="0" y="0" width="150" height="65" rx="10" class="node-selected"/>
|
||||
<rect x="10" y="12" width="32" height="32" rx="8" fill="url(#purpleGrad)"/>
|
||||
<text x="26" y="34" text-anchor="middle" font-size="14" class="white-text">🤖</text>
|
||||
<text x="55" y="28" font-size="13" font-weight="500" class="main-text">AI Response</text>
|
||||
<text x="55" y="44" font-size="10" class="secondary-text">GPT-4 Model</text>
|
||||
<circle cx="140" cy="32" r="6" fill="#3b82f6"/>
|
||||
</g>
|
||||
|
||||
<!-- Condition Node -->
|
||||
<g transform="translate(235, 370)">
|
||||
<rect x="0" y="0" width="150" height="65" rx="10" class="node"/>
|
||||
<rect x="10" y="12" width="32" height="32" rx="8" fill="#06b6d4"/>
|
||||
<text x="26" y="34" text-anchor="middle" font-size="14" class="white-text">◇</text>
|
||||
<text x="55" y="28" font-size="13" font-weight="500" class="main-text">Condition</text>
|
||||
<text x="55" y="44" font-size="10" class="secondary-text">intent == support</text>
|
||||
</g>
|
||||
|
||||
<!-- Send Email Node -->
|
||||
<g transform="translate(165, 490)">
|
||||
<rect x="0" y="0" width="150" height="55" rx="10" class="node"/>
|
||||
<rect x="10" y="12" width="32" height="32" rx="8" fill="url(#orangeGrad)"/>
|
||||
<text x="26" y="34" text-anchor="middle" font-size="12" class="white-text">📧</text>
|
||||
<text x="55" y="28" font-size="13" font-weight="500" class="main-text">Send Email</text>
|
||||
<text x="55" y="44" font-size="10" class="secondary-text">Support team</text>
|
||||
</g>
|
||||
|
||||
<!-- End Node -->
|
||||
<g transform="translate(305, 490)">
|
||||
<rect x="0" y="0" width="150" height="55" rx="10" class="node"/>
|
||||
<rect x="10" y="12" width="32" height="32" rx="8" fill="#64748b"/>
|
||||
<text x="26" y="34" text-anchor="middle" font-size="14" class="white-text">⬤</text>
|
||||
<text x="55" y="28" font-size="13" font-weight="500" class="main-text">End</text>
|
||||
<text x="55" y="44" font-size="10" class="secondary-text">Complete flow</text>
|
||||
</g>
|
||||
|
||||
<!-- Right Properties Panel -->
|
||||
<rect x="760" y="40" width="110" height="530" class="sidebar-bg"/>
|
||||
<line x1="760" y1="40" x2="760" y2="570" class="border"/>
|
||||
|
||||
<text x="775" y="65" font-size="13" font-weight="600" class="main-text">Properties</text>
|
||||
|
||||
<g transform="translate(770, 85)">
|
||||
<text x="0" y="0" font-size="10" font-weight="500" class="muted-text">NODE</text>
|
||||
<text x="0" y="18" font-size="12" class="main-text">AI Response</text>
|
||||
|
||||
<text x="0" y="50" font-size="10" font-weight="500" class="muted-text">MODEL</text>
|
||||
<rect x="0" y="58" width="90" height="28" rx="6" class="icon-btn"/>
|
||||
<rect x="0" y="58" width="90" height="28" rx="6" class="border"/>
|
||||
<text x="10" y="77" font-size="11" class="main-text">GPT-4</text>
|
||||
<text x="75" y="77" font-size="10" class="muted-text">▾</text>
|
||||
|
||||
<text x="0" y="110" font-size="10" font-weight="500" class="muted-text">PROMPT</text>
|
||||
<rect x="0" y="118" width="90" height="60" rx="6" class="icon-btn"/>
|
||||
<rect x="0" y="118" width="90" height="60" rx="6" class="border"/>
|
||||
<text x="8" y="136" font-size="10" class="secondary-text">You are a</text>
|
||||
<text x="8" y="150" font-size="10" class="secondary-text">helpful</text>
|
||||
<text x="8" y="164" font-size="10" class="secondary-text">assistant...</text>
|
||||
|
||||
<text x="0" y="200" font-size="10" font-weight="500" class="muted-text">TEMPERATURE</text>
|
||||
<rect x="0" y="208" width="90" height="8" rx="4" fill="#e2e8f0"/>
|
||||
<rect x="0" y="208" width="60" height="8" rx="4" fill="#3b82f6"/>
|
||||
<text x="0" y="232" font-size="10" class="main-text">0.7</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(210, 555)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Drag to connect | Del = Delete | Ctrl+D = Duplicate | Ctrl+Z = Undo</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
187
docs/src/assets/suite/drive-screen.svg
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.header-bg { fill: #ffffff; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.folder-yellow { fill: #fbbf24; }
|
||||
.file-blue { fill: #3b82f6; }
|
||||
.file-green { fill: #22c55e; }
|
||||
.file-red { fill: #ef4444; }
|
||||
.row-hover { fill: #f1f5f9; }
|
||||
.row-selected { fill: #eff6ff; }
|
||||
.checkbox { fill: #ffffff; stroke: #cbd5e1; stroke-width: 1.5; }
|
||||
.checkbox-checked { fill: #3b82f6; stroke: #3b82f6; stroke-width: 1.5; }
|
||||
.storage-bar-bg { fill: #e2e8f0; }
|
||||
.storage-bar-fill { fill: #3b82f6; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.header-bg { fill: #0f172a; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.row-hover { fill: #1e293b; }
|
||||
.row-selected { fill: #1e3a5f; }
|
||||
.checkbox { fill: #1e293b; stroke: #475569; }
|
||||
.storage-bar-bg { fill: #334155; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Drive - File Manager</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<rect x="30" y="40" width="180" height="530" class="sidebar-bg"/>
|
||||
<line x1="210" y1="40" x2="210" y2="570" class="border"/>
|
||||
|
||||
<g transform="translate(45, 60)">
|
||||
<rect x="0" y="0" width="150" height="40" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="75" y="26" text-anchor="middle" font-size="14" font-weight="500" class="white-text">+ New</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(45, 120)">
|
||||
<rect x="-5" y="-8" width="160" height="36" rx="6" class="row-selected"/>
|
||||
<text x="25" y="12" font-size="14" class="main-text">My Drive</text>
|
||||
<text x="0" y="12" font-size="14">📁</text>
|
||||
|
||||
<text x="0" y="52" font-size="14">👥</text>
|
||||
<text x="25" y="52" font-size="14" class="secondary-text">Shared with me</text>
|
||||
|
||||
<text x="0" y="92" font-size="14">⭐</text>
|
||||
<text x="25" y="92" font-size="14" class="secondary-text">Starred</text>
|
||||
|
||||
<text x="0" y="132" font-size="14">🕐</text>
|
||||
<text x="25" y="132" font-size="14" class="secondary-text">Recent</text>
|
||||
|
||||
<text x="0" y="172" font-size="14">🗑️</text>
|
||||
<text x="25" y="172" font-size="14" class="secondary-text">Trash</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(45, 340)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">STORAGE</text>
|
||||
<rect x="0" y="15" width="140" height="6" rx="3" class="storage-bar-bg"/>
|
||||
<rect x="0" y="15" width="91" height="6" rx="3" class="storage-bar-fill"/>
|
||||
<text x="0" y="38" font-size="11" class="secondary-text">6.5 GB of 10 GB</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 55)">
|
||||
<rect x="0" y="0" width="300" height="36" rx="18" class="icon-btn"/>
|
||||
<rect x="0" y="0" width="300" height="36" rx="18" class="border"/>
|
||||
<text x="20" y="23" font-size="14">🔍</text>
|
||||
<text x="45" y="24" font-size="13" class="muted-text">Search files...</text>
|
||||
|
||||
<rect x="320" y="0" width="36" height="36" rx="8" class="icon-btn"/>
|
||||
<text x="338" y="24" text-anchor="middle" font-size="14">⊞</text>
|
||||
|
||||
<rect x="365" y="0" width="36" height="36" rx="8" class="icon-btn"/>
|
||||
<text x="383" y="24" text-anchor="middle" font-size="14">≡</text>
|
||||
|
||||
<rect x="410" y="0" width="36" height="36" rx="8" class="icon-btn"/>
|
||||
<text x="428" y="24" text-anchor="middle" font-size="14">⚙</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 105)">
|
||||
<text x="0" y="0" font-size="13" class="secondary-text">My Drive</text>
|
||||
<text x="55" y="0" font-size="13" class="muted-text">/</text>
|
||||
<text x="65" y="0" font-size="13" class="secondary-text">Projects</text>
|
||||
<text x="115" y="0" font-size="13" class="muted-text">/</text>
|
||||
<text x="125" y="0" font-size="13" font-weight="500" class="main-text">2024</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 125)">
|
||||
<rect x="0" y="0" width="620" height="32" rx="4" class="row-hover"/>
|
||||
<text x="35" y="21" font-size="12" font-weight="500" class="secondary-text">Name</text>
|
||||
<text x="350" y="21" font-size="12" font-weight="500" class="secondary-text">Modified</text>
|
||||
<text x="480" y="21" font-size="12" font-weight="500" class="secondary-text">Size</text>
|
||||
<text x="560" y="21" font-size="12" font-weight="500" class="secondary-text">Owner</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 165)">
|
||||
<rect x="0" y="0" width="620" height="44" rx="4" class="row-selected"/>
|
||||
<rect x="10" y="14" width="16" height="16" rx="3" class="checkbox-checked"/>
|
||||
<text x="18" y="26" text-anchor="middle" font-size="10" class="white-text">✓</text>
|
||||
<text x="35" y="28" font-size="18">📁</text>
|
||||
<text x="60" y="28" font-size="14" class="main-text">Reports</text>
|
||||
<text x="350" y="28" font-size="13" class="secondary-text">Today, 10:30 AM</text>
|
||||
<text x="480" y="28" font-size="13" class="secondary-text">—</text>
|
||||
<text x="560" y="28" font-size="13" class="secondary-text">me</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 215)">
|
||||
<rect x="0" y="0" width="620" height="44" rx="4" class="row-hover"/>
|
||||
<rect x="10" y="14" width="16" height="16" rx="3" class="checkbox"/>
|
||||
<text x="35" y="28" font-size="18">📁</text>
|
||||
<text x="60" y="28" font-size="14" class="main-text">Presentations</text>
|
||||
<text x="350" y="28" font-size="13" class="secondary-text">Yesterday</text>
|
||||
<text x="480" y="28" font-size="13" class="secondary-text">—</text>
|
||||
<text x="560" y="28" font-size="13" class="secondary-text">me</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 265)">
|
||||
<rect x="10" y="14" width="16" height="16" rx="3" class="checkbox"/>
|
||||
<text x="35" y="28" font-size="18">📊</text>
|
||||
<text x="60" y="28" font-size="14" class="main-text">Budget-2024.xlsx</text>
|
||||
<text x="350" y="28" font-size="13" class="secondary-text">Mar 15, 2024</text>
|
||||
<text x="480" y="28" font-size="13" class="secondary-text">245 KB</text>
|
||||
<text x="560" y="28" font-size="13" class="secondary-text">me</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 310)">
|
||||
<rect x="10" y="14" width="16" height="16" rx="3" class="checkbox"/>
|
||||
<text x="35" y="28" font-size="18">📄</text>
|
||||
<text x="60" y="28" font-size="14" class="main-text">Project-Proposal.docx</text>
|
||||
<text x="350" y="28" font-size="13" class="secondary-text">Mar 14, 2024</text>
|
||||
<text x="480" y="28" font-size="13" class="secondary-text">128 KB</text>
|
||||
<text x="560" y="28" font-size="13" class="secondary-text">me</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 355)">
|
||||
<rect x="10" y="14" width="16" height="16" rx="3" class="checkbox"/>
|
||||
<text x="35" y="28" font-size="18">📕</text>
|
||||
<text x="60" y="28" font-size="14" class="main-text">Contract-Signed.pdf</text>
|
||||
<text x="350" y="28" font-size="13" class="secondary-text">Mar 10, 2024</text>
|
||||
<text x="480" y="28" font-size="13" class="secondary-text">1.2 MB</text>
|
||||
<text x="560" y="28" font-size="13" class="secondary-text">John D.</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 400)">
|
||||
<rect x="10" y="14" width="16" height="16" rx="3" class="checkbox"/>
|
||||
<text x="35" y="28" font-size="18">🖼️</text>
|
||||
<text x="60" y="28" font-size="14" class="main-text">Logo-Final.png</text>
|
||||
<text x="350" y="28" font-size="13" class="secondary-text">Mar 8, 2024</text>
|
||||
<text x="480" y="28" font-size="13" class="secondary-text">89 KB</text>
|
||||
<text x="560" y="28" font-size="13" class="secondary-text">me</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 470)">
|
||||
<rect x="0" y="0" width="620" height="70" rx="8" fill="none" stroke="#3b82f6" stroke-width="2" stroke-dasharray="8,4"/>
|
||||
<text x="310" y="30" text-anchor="middle" font-size="18">📤</text>
|
||||
<text x="310" y="55" text-anchor="middle" font-size="13" class="secondary-text">Drop files here or click + New to upload</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(230, 555)">
|
||||
<text x="0" y="0" font-size="11" class="muted-text">1 item selected</text>
|
||||
<text x="100" y="0" font-size="11" class="muted-text">•</text>
|
||||
<text x="115" y="0" font-size="11" class="muted-text">6 items</text>
|
||||
<text x="350" y="0" font-size="10" class="muted-text">Ctrl+U Upload | Del Delete | Ctrl+C Copy</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9 KiB |
192
docs/src/assets/suite/mail-screen.svg
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.panel-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.row-hover { fill: #f1f5f9; }
|
||||
.row-selected { fill: #eff6ff; }
|
||||
.unread-bg { fill: #eff6ff; }
|
||||
.unread-dot { fill: #3b82f6; }
|
||||
.nav-active { fill: #eff6ff; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.panel-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.row-hover { fill: #1e293b; }
|
||||
.row-selected { fill: #1e3a5f; }
|
||||
.unread-bg { fill: #1e3a5f; }
|
||||
.nav-active { fill: #1e3a5f; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Mail - Email Client</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Sidebar Panel -->
|
||||
<rect x="30" y="40" width="160" height="530" class="panel-bg"/>
|
||||
<line x1="190" y1="40" x2="190" y2="570" class="border"/>
|
||||
|
||||
<!-- Compose Button -->
|
||||
<g transform="translate(45, 55)">
|
||||
<rect x="0" y="0" width="130" height="40" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="65" y="26" text-anchor="middle" font-size="14" font-weight="500" class="white-text">✏ Compose</text>
|
||||
</g>
|
||||
|
||||
<!-- Folder Navigation -->
|
||||
<g transform="translate(45, 115)">
|
||||
<rect x="-10" y="-8" width="150" height="36" rx="6" class="nav-active"/>
|
||||
<text x="0" y="14" font-size="14">📥</text>
|
||||
<text x="25" y="14" font-size="14" class="accent-text">Inbox</text>
|
||||
<text x="115" y="14" font-size="12" class="accent-text">3</text>
|
||||
|
||||
<text x="0" y="54" font-size="14">📤</text>
|
||||
<text x="25" y="54" font-size="14" class="secondary-text">Sent</text>
|
||||
|
||||
<text x="0" y="94" font-size="14">📝</text>
|
||||
<text x="25" y="94" font-size="14" class="secondary-text">Drafts</text>
|
||||
|
||||
<text x="0" y="134" font-size="14">⭐</text>
|
||||
<text x="25" y="134" font-size="14" class="secondary-text">Starred</text>
|
||||
|
||||
<text x="0" y="174" font-size="14">🗑️</text>
|
||||
<text x="25" y="174" font-size="14" class="secondary-text">Trash</text>
|
||||
</g>
|
||||
|
||||
<!-- Mail List Panel -->
|
||||
<rect x="190" y="40" width="280" height="530" class="bg"/>
|
||||
<line x1="470" y1="40" x2="470" y2="570" class="border"/>
|
||||
|
||||
<!-- Mail List Header -->
|
||||
<g transform="translate(205, 55)">
|
||||
<text x="0" y="15" font-size="16" font-weight="600" class="main-text">Inbox</text>
|
||||
<rect x="200" y="0" width="50" height="28" rx="6" class="icon-btn"/>
|
||||
<text x="225" y="19" text-anchor="middle" font-size="12">🔄</text>
|
||||
</g>
|
||||
|
||||
<line x1="190" y1="85" x2="470" y2="85" class="border"/>
|
||||
|
||||
<!-- Mail Items -->
|
||||
<g transform="translate(200, 95)">
|
||||
<!-- Unread Email 1 -->
|
||||
<rect x="0" y="0" width="260" height="75" rx="4" class="unread-bg"/>
|
||||
<circle cx="12" cy="37" r="4" class="unread-dot"/>
|
||||
<text x="25" y="20" font-size="13" font-weight="600" class="main-text">Sarah Johnson</text>
|
||||
<text x="180" y="20" font-size="11" class="muted-text">10:30 AM</text>
|
||||
<text x="25" y="40" font-size="12" font-weight="500" class="main-text">Project Update</text>
|
||||
<text x="25" y="58" font-size="12" class="secondary-text">Hi team, here is the latest...</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(200, 175)">
|
||||
<!-- Unread Email 2 -->
|
||||
<rect x="0" y="0" width="260" height="75" rx="4" class="unread-bg"/>
|
||||
<circle cx="12" cy="37" r="4" class="unread-dot"/>
|
||||
<text x="25" y="20" font-size="13" font-weight="600" class="main-text">Marketing Team</text>
|
||||
<text x="180" y="20" font-size="11" class="muted-text">9:15 AM</text>
|
||||
<text x="25" y="40" font-size="12" font-weight="500" class="main-text">Q4 Campaign Results</text>
|
||||
<text x="25" y="58" font-size="12" class="secondary-text">The numbers are in and...</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(200, 255)">
|
||||
<!-- Selected Email -->
|
||||
<rect x="0" y="0" width="260" height="75" rx="4" class="row-selected"/>
|
||||
<rect x="0" y="0" width="3" height="75" rx="1" fill="#3b82f6"/>
|
||||
<text x="25" y="20" font-size="13" font-weight="500" class="main-text">John Smith</text>
|
||||
<text x="180" y="20" font-size="11" class="muted-text">Yesterday</text>
|
||||
<text x="25" y="40" font-size="12" class="main-text">Meeting Notes</text>
|
||||
<text x="25" y="58" font-size="12" class="secondary-text">Thanks for the call today...</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(200, 335)">
|
||||
<!-- Read Email -->
|
||||
<text x="25" y="20" font-size="13" class="secondary-text">Alex Chen</text>
|
||||
<text x="180" y="20" font-size="11" class="muted-text">Mar 15</text>
|
||||
<text x="25" y="40" font-size="12" class="secondary-text">Re: Budget Review</text>
|
||||
<text x="25" y="58" font-size="12" class="muted-text">Looks good to me...</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(200, 405)">
|
||||
<text x="25" y="20" font-size="13" class="secondary-text">HR Department</text>
|
||||
<text x="180" y="20" font-size="11" class="muted-text">Mar 14</text>
|
||||
<text x="25" y="40" font-size="12" class="secondary-text">Benefits Enrollment</text>
|
||||
<text x="25" y="58" font-size="12" class="muted-text">Open enrollment starts...</text>
|
||||
</g>
|
||||
|
||||
<!-- Mail Content Panel -->
|
||||
<rect x="470" y="40" width="400" height="530" class="bg"/>
|
||||
|
||||
<!-- Email Header -->
|
||||
<g transform="translate(490, 60)">
|
||||
<text x="0" y="20" font-size="18" font-weight="600" class="main-text">Meeting Notes</text>
|
||||
|
||||
<g transform="translate(0, 40)">
|
||||
<circle cx="18" cy="18" r="18" fill="#22c55e"/>
|
||||
<text x="18" y="23" text-anchor="middle" font-size="12" font-weight="600" class="white-text">JS</text>
|
||||
|
||||
<text x="45" y="12" font-size="14" font-weight="500" class="main-text">John Smith</text>
|
||||
<text x="45" y="30" font-size="12" class="secondary-text">john.smith@company.com</text>
|
||||
|
||||
<text x="300" y="20" font-size="12" class="muted-text">Yesterday, 3:45 PM</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<line x1="490" y1="145" x2="850" y2="145" class="border"/>
|
||||
|
||||
<!-- Email Actions -->
|
||||
<g transform="translate(490, 155)">
|
||||
<rect x="0" y="0" width="70" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="35" y="21" text-anchor="middle" font-size="12" class="secondary-text">↩ Reply</text>
|
||||
|
||||
<rect x="80" y="0" width="80" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="120" y="21" text-anchor="middle" font-size="12" class="secondary-text">↩↩ Reply All</text>
|
||||
|
||||
<rect x="170" y="0" width="75" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="207" y="21" text-anchor="middle" font-size="12" class="secondary-text">↪ Forward</text>
|
||||
|
||||
<rect x="255" y="0" width="60" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="285" y="21" text-anchor="middle" font-size="12" class="secondary-text">🗑 Delete</text>
|
||||
</g>
|
||||
|
||||
<line x1="490" y1="200" x2="850" y2="200" class="border"/>
|
||||
|
||||
<!-- Email Body -->
|
||||
<g transform="translate(490, 220)">
|
||||
<text x="0" y="0" font-size="14" class="main-text">Hi Team,</text>
|
||||
<text x="0" y="30" font-size="14" class="main-text">Thanks for joining the call today. Here are the key</text>
|
||||
<text x="0" y="50" font-size="14" class="main-text">takeaways from our discussion:</text>
|
||||
|
||||
<text x="0" y="90" font-size="14" class="main-text">• Project timeline updated to Q2 launch</text>
|
||||
<text x="0" y="115" font-size="14" class="main-text">• Budget approved for additional resources</text>
|
||||
<text x="0" y="140" font-size="14" class="main-text">• Next review scheduled for Friday</text>
|
||||
|
||||
<text x="0" y="180" font-size="14" class="main-text">Let me know if you have any questions.</text>
|
||||
|
||||
<text x="0" y="220" font-size="14" class="main-text">Best regards,</text>
|
||||
<text x="0" y="240" font-size="14" class="main-text">John</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(490, 540)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Ctrl+R Reply | Ctrl+Shift+R Reply All | Ctrl+F Forward | Del Delete</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.6 KiB |
185
docs/src/assets/suite/meet-screen.svg
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="endCallGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#ef4444"/>
|
||||
<stop offset="100%" stop-color="#dc2626"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #0f172a; }
|
||||
.panel-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #60a5fa; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #334155; stroke-width: 1; fill: none; }
|
||||
.control-btn { fill: #334155; }
|
||||
.control-btn-active { fill: #3b82f6; }
|
||||
.control-btn-off { fill: #ef4444; }
|
||||
.video-bg { fill: #1e293b; }
|
||||
.participant-border { stroke: #3b82f6; stroke-width: 2; fill: none; }
|
||||
.speaking-border { stroke: #22c55e; stroke-width: 3; fill: none; }
|
||||
.muted-indicator { fill: #ef4444; }
|
||||
.online-dot { fill: #22c55e; }
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.bg { fill: #f1f5f9; }
|
||||
.panel-bg { fill: #ffffff; }
|
||||
.main-text { fill: #1e293b; }
|
||||
.secondary-text { fill: #64748b; }
|
||||
.muted-text { fill: #94a3b8; }
|
||||
.border { stroke: #e2e8f0; }
|
||||
.control-btn { fill: #e2e8f0; }
|
||||
.video-bg { fill: #e2e8f0; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Meet - Video Conference</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Main Video Area -->
|
||||
<rect x="45" y="55" width="620" height="420" rx="12" class="video-bg"/>
|
||||
<rect x="45" y="55" width="620" height="420" rx="12" class="speaking-border"/>
|
||||
|
||||
<!-- Main Speaker Avatar/Video -->
|
||||
<circle cx="355" cy="230" r="80" fill="#3b82f6"/>
|
||||
<text x="355" y="250" text-anchor="middle" font-size="48" class="white-text">JS</text>
|
||||
<text x="355" y="340" text-anchor="middle" font-size="18" font-weight="500" class="main-text">John Smith</text>
|
||||
<text x="355" y="365" text-anchor="middle" font-size="14" class="secondary-text">Speaking...</text>
|
||||
|
||||
<!-- Speaking indicator animation dots -->
|
||||
<g transform="translate(315, 380)">
|
||||
<circle cx="0" cy="0" r="4" class="online-dot" opacity="0.4"/>
|
||||
<circle cx="15" cy="0" r="4" class="online-dot" opacity="0.7"/>
|
||||
<circle cx="30" cy="0" r="4" class="online-dot"/>
|
||||
<circle cx="45" cy="0" r="4" class="online-dot" opacity="0.7"/>
|
||||
<circle cx="60" cy="0" r="4" class="online-dot" opacity="0.4"/>
|
||||
</g>
|
||||
|
||||
<!-- Meeting Info Overlay -->
|
||||
<g transform="translate(60, 70)">
|
||||
<rect x="0" y="0" width="200" height="30" rx="6" fill="#0f172a" opacity="0.7"/>
|
||||
<circle cx="15" cy="15" r="5" class="online-dot"/>
|
||||
<text x="28" y="20" font-size="13" class="main-text">Team Standup • 15:32</text>
|
||||
</g>
|
||||
|
||||
<!-- Self View (Picture-in-Picture) -->
|
||||
<rect x="500" y="370" width="150" height="95" rx="8" class="video-bg"/>
|
||||
<rect x="500" y="370" width="150" height="95" rx="8" class="participant-border"/>
|
||||
<circle cx="575" cy="410" r="25" fill="#8b5cf6"/>
|
||||
<text x="575" y="418" text-anchor="middle" font-size="18" class="white-text">ME</text>
|
||||
<text x="575" y="455" text-anchor="middle" font-size="11" class="main-text">You</text>
|
||||
|
||||
<!-- Participants Sidebar -->
|
||||
<rect x="680" y="55" width="175" height="420" rx="8" class="panel-bg"/>
|
||||
<rect x="680" y="55" width="175" height="420" rx="8" class="border"/>
|
||||
|
||||
<text x="695" y="80" font-size="14" font-weight="600" class="main-text">Participants (5)</text>
|
||||
|
||||
<!-- Participant 1 -->
|
||||
<g transform="translate(695, 95)">
|
||||
<rect x="0" y="0" width="145" height="65" rx="6" class="video-bg"/>
|
||||
<circle cx="35" cy="32" r="20" fill="#22c55e"/>
|
||||
<text x="35" y="40" text-anchor="middle" font-size="14" class="white-text">SW</text>
|
||||
<text x="65" y="28" font-size="12" class="main-text">Sarah Wilson</text>
|
||||
<circle cx="65" cy="42" r="4" class="online-dot"/>
|
||||
<text x="75" y="45" font-size="10" class="secondary-text">Online</text>
|
||||
</g>
|
||||
|
||||
<!-- Participant 2 -->
|
||||
<g transform="translate(695, 170)">
|
||||
<rect x="0" y="0" width="145" height="65" rx="6" class="video-bg"/>
|
||||
<circle cx="35" cy="32" r="20" fill="#f97316"/>
|
||||
<text x="35" y="40" text-anchor="middle" font-size="14" class="white-text">MJ</text>
|
||||
<text x="65" y="28" font-size="12" class="main-text">Mike Johnson</text>
|
||||
<circle cx="65" cy="42" r="4" class="muted-indicator"/>
|
||||
<text x="75" y="45" font-size="10" class="secondary-text">Muted</text>
|
||||
</g>
|
||||
|
||||
<!-- Participant 3 -->
|
||||
<g transform="translate(695, 245)">
|
||||
<rect x="0" y="0" width="145" height="65" rx="6" class="video-bg"/>
|
||||
<circle cx="35" cy="32" r="20" fill="#ec4899"/>
|
||||
<text x="35" y="40" text-anchor="middle" font-size="14" class="white-text">AC</text>
|
||||
<text x="65" y="28" font-size="12" class="main-text">Alex Chen</text>
|
||||
<circle cx="65" cy="42" r="4" class="online-dot"/>
|
||||
<text x="75" y="45" font-size="10" class="secondary-text">Online</text>
|
||||
</g>
|
||||
|
||||
<!-- Participant 4 -->
|
||||
<g transform="translate(695, 320)">
|
||||
<rect x="0" y="0" width="145" height="65" rx="6" class="video-bg"/>
|
||||
<circle cx="35" cy="32" r="20" fill="#14b8a6"/>
|
||||
<text x="35" y="40" text-anchor="middle" font-size="14" class="white-text">EM</text>
|
||||
<text x="65" y="28" font-size="12" class="main-text">Emma Miller</text>
|
||||
<circle cx="65" cy="42" r="4" class="online-dot"/>
|
||||
<text x="75" y="45" font-size="10" class="secondary-text">Online</text>
|
||||
</g>
|
||||
|
||||
<!-- Invite Button -->
|
||||
<g transform="translate(695, 400)">
|
||||
<rect x="0" y="0" width="145" height="35" rx="6" fill="url(#accentGrad)"/>
|
||||
<text x="72" y="23" text-anchor="middle" font-size="12" font-weight="500" class="white-text">+ Invite People</text>
|
||||
</g>
|
||||
|
||||
<!-- Bottom Control Bar -->
|
||||
<rect x="45" y="490" width="810" height="70" rx="8" class="panel-bg"/>
|
||||
<rect x="45" y="490" width="810" height="70" rx="8" class="border"/>
|
||||
|
||||
<!-- Left Controls - Meeting Info -->
|
||||
<g transform="translate(70, 510)">
|
||||
<text x="0" y="15" font-size="14" font-weight="500" class="main-text">Team Standup</text>
|
||||
<text x="0" y="35" font-size="12" class="secondary-text">5 participants • 15:32</text>
|
||||
</g>
|
||||
|
||||
<!-- Center Controls -->
|
||||
<g transform="translate(350, 505)">
|
||||
<!-- Microphone (On) -->
|
||||
<circle cx="0" cy="25" r="25" class="control-btn"/>
|
||||
<text x="0" y="32" text-anchor="middle" font-size="18">🎤</text>
|
||||
|
||||
<!-- Camera (On) -->
|
||||
<circle cx="65" cy="25" r="25" class="control-btn"/>
|
||||
<text x="65" y="32" text-anchor="middle" font-size="18">📹</text>
|
||||
|
||||
<!-- Screen Share -->
|
||||
<circle cx="130" cy="25" r="25" class="control-btn"/>
|
||||
<text x="130" y="32" text-anchor="middle" font-size="18">🖥</text>
|
||||
|
||||
<!-- Chat -->
|
||||
<circle cx="195" cy="25" r="25" class="control-btn"/>
|
||||
<text x="195" y="32" text-anchor="middle" font-size="18">💬</text>
|
||||
|
||||
<!-- End Call -->
|
||||
<circle cx="275" cy="25" r="28" fill="url(#endCallGrad)"/>
|
||||
<text x="275" y="32" text-anchor="middle" font-size="18">📞</text>
|
||||
</g>
|
||||
|
||||
<!-- Right Controls -->
|
||||
<g transform="translate(720, 505)">
|
||||
<!-- More Options -->
|
||||
<circle cx="0" cy="25" r="22" class="control-btn"/>
|
||||
<text x="0" y="32" text-anchor="middle" font-size="16">⚙</text>
|
||||
|
||||
<!-- Fullscreen -->
|
||||
<circle cx="55" cy="25" r="22" class="control-btn"/>
|
||||
<text x="55" y="32" text-anchor="middle" font-size="16">⛶</text>
|
||||
|
||||
<!-- Layout -->
|
||||
<circle cx="110" cy="25" r="22" class="control-btn"/>
|
||||
<text x="110" y="32" text-anchor="middle" font-size="16">⊞</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(50, 575)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">M = Mute | V = Camera | S = Share Screen | C = Chat | L = Leave</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.1 KiB |
209
docs/src/assets/suite/paper-screen.svg
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.toolbar-bg { fill: #f8fafc; }
|
||||
.ai-suggestion { fill: #eff6ff; stroke: #3b82f6; stroke-width: 1; }
|
||||
.cursor { stroke: #3b82f6; stroke-width: 2; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.toolbar-bg { fill: #1e293b; }
|
||||
.ai-suggestion { fill: #1e3a5f; stroke: #3b82f6; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Paper - AI Writing Assistant</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<rect x="30" y="40" width="180" height="530" class="sidebar-bg"/>
|
||||
<line x1="210" y1="40" x2="210" y2="570" class="border"/>
|
||||
|
||||
<!-- New Document Button -->
|
||||
<g transform="translate(45, 55)">
|
||||
<rect x="0" y="0" width="150" height="40" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="75" y="26" text-anchor="middle" font-size="14" font-weight="500" class="white-text">+ New Document</text>
|
||||
</g>
|
||||
|
||||
<!-- Documents List -->
|
||||
<g transform="translate(45, 115)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">RECENT DOCUMENTS</text>
|
||||
|
||||
<g transform="translate(0, 20)">
|
||||
<rect x="-10" y="-8" width="160" height="40" rx="6" fill="#eff6ff"/>
|
||||
<text x="0" y="8" font-size="13">📄</text>
|
||||
<text x="22" y="8" font-size="13" class="accent-text">Project Proposal</text>
|
||||
<text x="22" y="24" font-size="10" class="muted-text">Edited 2 min ago</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 70)">
|
||||
<text x="0" y="8" font-size="13">📄</text>
|
||||
<text x="22" y="8" font-size="13" class="main-text">Meeting Notes</text>
|
||||
<text x="22" y="24" font-size="10" class="muted-text">Edited yesterday</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 115)">
|
||||
<text x="0" y="8" font-size="13">📄</text>
|
||||
<text x="22" y="8" font-size="13" class="main-text">Q4 Report Draft</text>
|
||||
<text x="22" y="24" font-size="10" class="muted-text">Edited Mar 14</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 160)">
|
||||
<text x="0" y="8" font-size="13">📄</text>
|
||||
<text x="22" y="8" font-size="13" class="main-text">Blog Post Ideas</text>
|
||||
<text x="22" y="24" font-size="10" class="muted-text">Edited Mar 12</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Templates Section -->
|
||||
<g transform="translate(45, 340)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">TEMPLATES</text>
|
||||
|
||||
<g transform="translate(0, 20)">
|
||||
<text x="0" y="12" font-size="13">📝</text>
|
||||
<text x="22" y="12" font-size="13" class="secondary-text">Blank Document</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 45)">
|
||||
<text x="0" y="12" font-size="13">📋</text>
|
||||
<text x="22" y="12" font-size="13" class="secondary-text">Meeting Notes</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 70)">
|
||||
<text x="0" y="12" font-size="13">📊</text>
|
||||
<text x="22" y="12" font-size="13" class="secondary-text">Report</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 95)">
|
||||
<text x="0" y="12" font-size="13">📧</text>
|
||||
<text x="22" y="12" font-size="13" class="secondary-text">Email Draft</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Main Editor Area -->
|
||||
<rect x="220" y="50" width="640" height="510" class="bg"/>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<rect x="230" y="55" width="620" height="45" rx="8" class="toolbar-bg"/>
|
||||
<rect x="230" y="55" width="620" height="45" rx="8" class="border"/>
|
||||
|
||||
<g transform="translate(245, 65)">
|
||||
<!-- Format buttons -->
|
||||
<rect x="0" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="14" y="18" text-anchor="middle" font-size="14" font-weight="700" class="main-text">B</text>
|
||||
|
||||
<rect x="35" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="49" y="18" text-anchor="middle" font-size="14" font-style="italic" class="main-text">I</text>
|
||||
|
||||
<rect x="70" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="84" y="18" text-anchor="middle" font-size="14" text-decoration="underline" class="main-text">U</text>
|
||||
|
||||
<line x1="110" y1="3" x2="110" y2="23" class="border"/>
|
||||
|
||||
<!-- Heading selector -->
|
||||
<rect x="120" y="0" width="70" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="155" y="18" text-anchor="middle" font-size="12" class="secondary-text">Heading ▾</text>
|
||||
|
||||
<line x1="200" y1="3" x2="200" y2="23" class="border"/>
|
||||
|
||||
<!-- List buttons -->
|
||||
<rect x="210" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="224" y="18" text-anchor="middle" font-size="14" class="main-text">≡</text>
|
||||
|
||||
<rect x="245" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="259" y="18" text-anchor="middle" font-size="14" class="main-text">☰</text>
|
||||
|
||||
<line x1="285" y1="3" x2="285" y2="23" class="border"/>
|
||||
|
||||
<!-- Link and Image -->
|
||||
<rect x="295" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="309" y="18" text-anchor="middle" font-size="12" class="main-text">🔗</text>
|
||||
|
||||
<rect x="330" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="344" y="18" text-anchor="middle" font-size="12" class="main-text">🖼</text>
|
||||
|
||||
<line x1="370" y1="3" x2="370" y2="23" class="border"/>
|
||||
|
||||
<!-- AI Assist Button -->
|
||||
<rect x="380" y="0" width="100" height="26" rx="13" fill="url(#accentGrad)"/>
|
||||
<text x="430" y="18" text-anchor="middle" font-size="12" font-weight="500" class="white-text">✨ AI Assist</text>
|
||||
|
||||
<!-- More options -->
|
||||
<rect x="560" y="0" width="28" height="26" rx="4" class="icon-btn"/>
|
||||
<text x="574" y="18" text-anchor="middle" font-size="14" class="secondary-text">⋮</text>
|
||||
</g>
|
||||
|
||||
<!-- Document Title -->
|
||||
<g transform="translate(250, 120)">
|
||||
<text x="0" y="0" font-size="28" font-weight="700" class="main-text">Project Proposal</text>
|
||||
<text x="0" y="25" font-size="13" class="muted-text">Last edited 2 minutes ago • Auto-saved</text>
|
||||
</g>
|
||||
|
||||
<!-- Document Content -->
|
||||
<g transform="translate(250, 180)">
|
||||
<text x="0" y="0" font-size="18" font-weight="600" class="main-text">Executive Summary</text>
|
||||
|
||||
<text x="0" y="35" font-size="14" class="main-text">This proposal outlines a comprehensive strategy for improving</text>
|
||||
<text x="0" y="55" font-size="14" class="main-text">our customer engagement platform. The project aims to increase</text>
|
||||
<text x="0" y="75" font-size="14" class="main-text">user retention by 25% and reduce support tickets by 40%.</text>
|
||||
|
||||
<text x="0" y="115" font-size="18" font-weight="600" class="main-text">Key Objectives</text>
|
||||
|
||||
<text x="0" y="150" font-size="14" class="main-text">• Redesign the onboarding experience for new users</text>
|
||||
<text x="0" y="175" font-size="14" class="main-text">• Implement AI-powered customer support chatbot</text>
|
||||
<text x="0" y="200" font-size="14" class="main-text">• Create personalized dashboard for each user segment</text>
|
||||
<text x="0" y="225" font-size="14" class="main-text">• Integrate analytics tracking for better insights|</text>
|
||||
|
||||
<!-- Cursor -->
|
||||
<line x1="445" y1="213" x2="445" y2="230" class="cursor"/>
|
||||
</g>
|
||||
|
||||
<!-- AI Suggestion Panel -->
|
||||
<g transform="translate(250, 420)">
|
||||
<rect x="0" y="0" width="580" height="90" rx="8" class="ai-suggestion"/>
|
||||
<text x="15" y="22" font-size="12" font-weight="600" class="accent-text">✨ AI Suggestion</text>
|
||||
<text x="15" y="45" font-size="13" class="main-text">Consider adding a timeline section to outline project milestones</text>
|
||||
<text x="15" y="63" font-size="13" class="main-text">and deliverables. This helps stakeholders understand the scope.</text>
|
||||
|
||||
<g transform="translate(15, 70)">
|
||||
<rect x="0" y="0" width="70" height="24" rx="12" fill="url(#accentGrad)"/>
|
||||
<text x="35" y="16" text-anchor="middle" font-size="11" class="white-text">Insert</text>
|
||||
|
||||
<rect x="80" y="0" width="70" height="24" rx="12" class="icon-btn"/>
|
||||
<text x="115" y="16" text-anchor="middle" font-size="11" class="secondary-text">Dismiss</text>
|
||||
|
||||
<rect x="160" y="0" width="90" height="24" rx="12" class="icon-btn"/>
|
||||
<text x="205" y="16" text-anchor="middle" font-size="11" class="secondary-text">Regenerate</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Word Count Footer -->
|
||||
<g transform="translate(250, 530)">
|
||||
<text x="0" y="0" font-size="11" class="muted-text">324 words • 1,847 characters • Reading time: 2 min</text>
|
||||
<text x="380" y="0" font-size="10" class="muted-text">Ctrl+S Save | Ctrl+B Bold | Ctrl+I Italic | Ctrl+K Link</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.5 KiB |
203
docs/src/assets/suite/player-screen.svg
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="overlayGrad" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" stop-color="rgba(0,0,0,0)"/>
|
||||
<stop offset="100%" stop-color="rgba(0,0,0,0.7)"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #60a5fa; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #334155; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.control-btn { fill: rgba(255,255,255,0.1); }
|
||||
.progress-bg { fill: #334155; }
|
||||
.progress-fill { fill: #3b82f6; }
|
||||
.thumbnail { fill: #1e293b; stroke: #334155; stroke-width: 1; }
|
||||
.thumbnail-active { fill: #1e293b; stroke: #3b82f6; stroke-width: 2; }
|
||||
.viewer-bg { fill: #0f172a; }
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
.bg { fill: #f8fafc; }
|
||||
.sidebar-bg { fill: #ffffff; }
|
||||
.main-text { fill: #1e293b; }
|
||||
.secondary-text { fill: #64748b; }
|
||||
.muted-text { fill: #94a3b8; }
|
||||
.accent-text { fill: #3b82f6; }
|
||||
.border { stroke: #e2e8f0; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.control-btn { fill: rgba(0,0,0,0.1); }
|
||||
.progress-bg { fill: #e2e8f0; }
|
||||
.thumbnail { fill: #f1f5f9; stroke: #e2e8f0; }
|
||||
.thumbnail-active { fill: #f1f5f9; stroke: #3b82f6; }
|
||||
.viewer-bg { fill: #1e293b; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Player - Media Viewer</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Main Viewer Area -->
|
||||
<rect x="40" y="50" width="640" height="400" rx="8" class="viewer-bg"/>
|
||||
|
||||
<!-- Slide Content Area -->
|
||||
<rect x="60" y="70" width="600" height="340" rx="4" fill="#1e293b"/>
|
||||
|
||||
<!-- Slide Content (Presentation) -->
|
||||
<g transform="translate(80, 90)">
|
||||
<text x="0" y="0" font-size="28" font-weight="700" class="white-text">Q4 2024 Business Review</text>
|
||||
<text x="0" y="40" font-size="16" class="secondary-text">Strategic Initiatives and Key Metrics</text>
|
||||
|
||||
<!-- Bullet points -->
|
||||
<text x="0" y="100" font-size="18" class="main-text">• Revenue grew 23% year-over-year</text>
|
||||
<text x="0" y="135" font-size="18" class="main-text">• Customer satisfaction score: 94%</text>
|
||||
<text x="0" y="170" font-size="18" class="main-text">• New product launches: 3</text>
|
||||
<text x="0" y="205" font-size="18" class="main-text">• Market expansion into APAC region</text>
|
||||
|
||||
<!-- Simple chart representation -->
|
||||
<rect x="350" y="80" width="180" height="150" rx="8" fill="#334155"/>
|
||||
<text x="440" y="105" text-anchor="middle" font-size="12" class="secondary-text">Revenue Trend</text>
|
||||
|
||||
<!-- Bar chart -->
|
||||
<rect x="375" y="170" width="25" height="40" rx="2" fill="#3b82f6"/>
|
||||
<rect x="410" y="150" width="25" height="60" rx="2" fill="#3b82f6"/>
|
||||
<rect x="445" y="130" width="25" height="80" rx="2" fill="#3b82f6"/>
|
||||
<rect x="480" y="115" width="25" height="95" rx="2" fill="#22c55e"/>
|
||||
|
||||
<text x="387" y="225" text-anchor="middle" font-size="9" class="muted-text">Q1</text>
|
||||
<text x="422" y="225" text-anchor="middle" font-size="9" class="muted-text">Q2</text>
|
||||
<text x="457" y="225" text-anchor="middle" font-size="9" class="muted-text">Q3</text>
|
||||
<text x="492" y="225" text-anchor="middle" font-size="9" class="muted-text">Q4</text>
|
||||
</g>
|
||||
|
||||
<!-- Slide Number -->
|
||||
<rect x="580" y="375" width="60" height="25" rx="12" fill="rgba(0,0,0,0.5)"/>
|
||||
<text x="610" y="392" text-anchor="middle" font-size="12" class="white-text">5 / 12</text>
|
||||
|
||||
<!-- Video Controls Bar -->
|
||||
<rect x="40" y="460" width="640" height="60" rx="8" class="sidebar-bg"/>
|
||||
<rect x="40" y="460" width="640" height="60" rx="8" class="border"/>
|
||||
|
||||
<!-- Play/Pause Controls -->
|
||||
<g transform="translate(60, 475)">
|
||||
<!-- Previous -->
|
||||
<circle cx="0" cy="15" r="15" class="control-btn"/>
|
||||
<text x="0" y="20" text-anchor="middle" font-size="14" class="main-text">⏮</text>
|
||||
|
||||
<!-- Play/Pause -->
|
||||
<circle cx="50" cy="15" r="20" fill="url(#accentGrad)"/>
|
||||
<text x="50" y="22" text-anchor="middle" font-size="18" class="white-text">▶</text>
|
||||
|
||||
<!-- Next -->
|
||||
<circle cx="100" cy="15" r="15" class="control-btn"/>
|
||||
<text x="100" y="20" text-anchor="middle" font-size="14" class="main-text">⏭</text>
|
||||
</g>
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<g transform="translate(180, 480)">
|
||||
<text x="0" y="12" font-size="11" class="muted-text">2:34</text>
|
||||
<rect x="40" y="5" width="360" height="6" rx="3" class="progress-bg"/>
|
||||
<rect x="40" y="5" width="150" height="6" rx="3" class="progress-fill"/>
|
||||
<circle cx="190" cy="8" r="8" fill="#3b82f6"/>
|
||||
<text x="410" y="12" font-size="11" class="muted-text">5:48</text>
|
||||
</g>
|
||||
|
||||
<!-- Right Controls -->
|
||||
<g transform="translate(590, 475)">
|
||||
<!-- Volume -->
|
||||
<circle cx="0" cy="15" r="15" class="control-btn"/>
|
||||
<text x="0" y="20" text-anchor="middle" font-size="12" class="main-text">🔊</text>
|
||||
|
||||
<!-- Fullscreen -->
|
||||
<circle cx="45" cy="15" r="15" class="control-btn"/>
|
||||
<text x="45" y="20" text-anchor="middle" font-size="12" class="main-text">⛶</text>
|
||||
|
||||
<!-- Settings -->
|
||||
<circle cx="90" cy="15" r="15" class="control-btn"/>
|
||||
<text x="90" y="20" text-anchor="middle" font-size="12" class="main-text">⚙</text>
|
||||
</g>
|
||||
|
||||
<!-- Right Sidebar - Playlist -->
|
||||
<rect x="695" y="50" width="165" height="470" class="sidebar-bg"/>
|
||||
<rect x="695" y="50" width="165" height="470" rx="8" class="border"/>
|
||||
|
||||
<text x="710" y="75" font-size="14" font-weight="600" class="main-text">Playlist</text>
|
||||
<text x="710" y="95" font-size="11" class="secondary-text">12 slides • 5:48 total</text>
|
||||
|
||||
<!-- Thumbnail List -->
|
||||
<g transform="translate(705, 115)">
|
||||
<!-- Thumbnail 1 -->
|
||||
<g transform="translate(0, 0)">
|
||||
<rect x="0" y="0" width="145" height="55" rx="6" class="thumbnail"/>
|
||||
<rect x="5" y="5" width="45" height="45" rx="4" fill="#334155"/>
|
||||
<text x="27" y="33" text-anchor="middle" font-size="16" class="muted-text">1</text>
|
||||
<text x="58" y="20" font-size="11" class="main-text">Title Slide</text>
|
||||
<text x="58" y="35" font-size="10" class="muted-text">0:00</text>
|
||||
</g>
|
||||
|
||||
<!-- Thumbnail 2 -->
|
||||
<g transform="translate(0, 65)">
|
||||
<rect x="0" y="0" width="145" height="55" rx="6" class="thumbnail"/>
|
||||
<rect x="5" y="5" width="45" height="45" rx="4" fill="#334155"/>
|
||||
<text x="27" y="33" text-anchor="middle" font-size="16" class="muted-text">2</text>
|
||||
<text x="58" y="20" font-size="11" class="main-text">Agenda</text>
|
||||
<text x="58" y="35" font-size="10" class="muted-text">0:30</text>
|
||||
</g>
|
||||
|
||||
<!-- Thumbnail 3 -->
|
||||
<g transform="translate(0, 130)">
|
||||
<rect x="0" y="0" width="145" height="55" rx="6" class="thumbnail"/>
|
||||
<rect x="5" y="5" width="45" height="45" rx="4" fill="#334155"/>
|
||||
<text x="27" y="33" text-anchor="middle" font-size="16" class="muted-text">3</text>
|
||||
<text x="58" y="20" font-size="11" class="main-text">Overview</text>
|
||||
<text x="58" y="35" font-size="10" class="muted-text">1:15</text>
|
||||
</g>
|
||||
|
||||
<!-- Thumbnail 4 -->
|
||||
<g transform="translate(0, 195)">
|
||||
<rect x="0" y="0" width="145" height="55" rx="6" class="thumbnail"/>
|
||||
<rect x="5" y="5" width="45" height="45" rx="4" fill="#334155"/>
|
||||
<text x="27" y="33" text-anchor="middle" font-size="16" class="muted-text">4</text>
|
||||
<text x="58" y="20" font-size="11" class="main-text">Metrics</text>
|
||||
<text x="58" y="35" font-size="10" class="muted-text">1:58</text>
|
||||
</g>
|
||||
|
||||
<!-- Thumbnail 5 - Active -->
|
||||
<g transform="translate(0, 260)">
|
||||
<rect x="0" y="0" width="145" height="55" rx="6" class="thumbnail-active"/>
|
||||
<rect x="5" y="5" width="45" height="45" rx="4" fill="#3b82f6"/>
|
||||
<text x="27" y="33" text-anchor="middle" font-size="16" class="white-text">5</text>
|
||||
<text x="58" y="20" font-size="11" class="accent-text">Q4 Review</text>
|
||||
<text x="58" y="35" font-size="10" class="accent-text">▶ Playing</text>
|
||||
</g>
|
||||
|
||||
<!-- Thumbnail 6 -->
|
||||
<g transform="translate(0, 325)">
|
||||
<rect x="0" y="0" width="145" height="55" rx="6" class="thumbnail"/>
|
||||
<rect x="5" y="5" width="45" height="45" rx="4" fill="#334155"/>
|
||||
<text x="27" y="33" text-anchor="middle" font-size="16" class="muted-text">6</text>
|
||||
<text x="58" y="20" font-size="11" class="main-text">Roadmap</text>
|
||||
<text x="58" y="35" font-size="10" class="muted-text">3:20</text>
|
||||
</g>
|
||||
|
||||
<!-- More indicator -->
|
||||
<text x="72" y="400" text-anchor="middle" font-size="11" class="muted-text">+ 6 more slides</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(50, 555)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Space = Play/Pause | ← → = Navigate | F = Fullscreen | M = Mute</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.3 KiB |
160
docs/src/assets/suite/research-screen.svg
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.result-card { fill: #f8fafc; stroke: #e2e8f0; stroke-width: 1; }
|
||||
.source-tag { fill: #dbeafe; }
|
||||
.ai-response { fill: #eff6ff; stroke: #3b82f6; stroke-width: 1; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.result-card { fill: #1e293b; stroke: #334155; }
|
||||
.source-tag { fill: #1e3a5f; }
|
||||
.ai-response { fill: #1e3a5f; stroke: #3b82f6; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Research - AI-Powered Search</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Search Header -->
|
||||
<g transform="translate(50, 60)">
|
||||
<rect x="0" y="0" width="700" height="50" rx="25" class="icon-btn"/>
|
||||
<rect x="0" y="0" width="700" height="50" rx="25" class="border"/>
|
||||
<text x="25" y="32" font-size="16">🔍</text>
|
||||
<text x="55" y="32" font-size="15" class="main-text">How does machine learning work in recommendation systems?</text>
|
||||
|
||||
<rect x="620" y="8" width="70" height="34" rx="17" fill="url(#accentGrad)"/>
|
||||
<text x="655" y="30" text-anchor="middle" font-size="13" class="white-text">Search</text>
|
||||
</g>
|
||||
|
||||
<!-- Filter Pills -->
|
||||
<g transform="translate(50, 125)">
|
||||
<rect x="0" y="0" width="60" height="28" rx="14" fill="url(#accentGrad)"/>
|
||||
<text x="30" y="19" text-anchor="middle" font-size="11" class="white-text">All</text>
|
||||
|
||||
<rect x="70" y="0" width="70" height="28" rx="14" class="icon-btn"/>
|
||||
<text x="105" y="19" text-anchor="middle" font-size="11" class="secondary-text">Articles</text>
|
||||
|
||||
<rect x="150" y="0" width="65" height="28" rx="14" class="icon-btn"/>
|
||||
<text x="182" y="19" text-anchor="middle" font-size="11" class="secondary-text">Papers</text>
|
||||
|
||||
<rect x="225" y="0" width="55" height="28" rx="14" class="icon-btn"/>
|
||||
<text x="252" y="19" text-anchor="middle" font-size="11" class="secondary-text">Web</text>
|
||||
|
||||
<rect x="290" y="0" width="65" height="28" rx="14" class="icon-btn"/>
|
||||
<text x="322" y="19" text-anchor="middle" font-size="11" class="secondary-text">Videos</text>
|
||||
|
||||
<text x="600" y="19" font-size="12" class="muted-text">23 results • 1.2s</text>
|
||||
</g>
|
||||
|
||||
<!-- AI Summary -->
|
||||
<g transform="translate(50, 170)">
|
||||
<rect x="0" y="0" width="600" height="140" rx="12" class="ai-response"/>
|
||||
<text x="20" y="25" font-size="13" font-weight="600" class="accent-text">✨ AI Summary</text>
|
||||
|
||||
<text x="20" y="50" font-size="13" class="main-text">Machine learning powers recommendation systems through several key techniques:</text>
|
||||
|
||||
<text x="20" y="75" font-size="13" class="main-text">• Collaborative filtering analyzes user behavior patterns to find similar users</text>
|
||||
<text x="20" y="95" font-size="13" class="main-text">• Content-based filtering matches item attributes to user preferences</text>
|
||||
<text x="20" y="115" font-size="13" class="main-text">• Deep learning models can capture complex non-linear relationships</text>
|
||||
|
||||
<text x="20" y="135" font-size="11" class="muted-text">Based on 5 sources</text>
|
||||
</g>
|
||||
|
||||
<!-- Search Results -->
|
||||
<g transform="translate(50, 325)">
|
||||
<!-- Result 1 -->
|
||||
<rect x="0" y="0" width="600" height="90" rx="8" class="result-card"/>
|
||||
<text x="15" y="25" font-size="14" font-weight="600" class="accent-text">Understanding Recommendation Systems: A Complete Guide</text>
|
||||
<text x="15" y="45" font-size="12" class="secondary-text">ml-guide.com • Published 2024</text>
|
||||
<text x="15" y="68" font-size="13" class="main-text">A comprehensive overview of how modern recommendation systems</text>
|
||||
<text x="15" y="85" font-size="13" class="main-text">leverage machine learning algorithms to personalize content...</text>
|
||||
|
||||
<rect x="520" y="60" width="65" height="22" rx="11" class="source-tag"/>
|
||||
<text x="552" y="75" text-anchor="middle" font-size="10" class="accent-text">Article</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(50, 425)">
|
||||
<!-- Result 2 -->
|
||||
<rect x="0" y="0" width="600" height="90" rx="8" class="result-card"/>
|
||||
<text x="15" y="25" font-size="14" font-weight="600" class="accent-text">Collaborative Filtering vs Content-Based: A Comparison</text>
|
||||
<text x="15" y="45" font-size="12" class="secondary-text">research.ai • IEEE 2023</text>
|
||||
<text x="15" y="68" font-size="13" class="main-text">This paper compares the effectiveness of collaborative filtering and</text>
|
||||
<text x="15" y="85" font-size="13" class="main-text">content-based approaches in various recommendation scenarios...</text>
|
||||
|
||||
<rect x="520" y="60" width="65" height="22" rx="11" class="source-tag"/>
|
||||
<text x="552" y="75" text-anchor="middle" font-size="10" class="accent-text">Paper</text>
|
||||
</g>
|
||||
|
||||
<!-- Sources Panel -->
|
||||
<rect x="670" y="170" width="185" height="345" rx="8" class="sidebar-bg"/>
|
||||
<rect x="670" y="170" width="185" height="345" rx="8" class="border"/>
|
||||
|
||||
<text x="685" y="195" font-size="13" font-weight="600" class="main-text">Sources (5)</text>
|
||||
|
||||
<g transform="translate(685, 215)">
|
||||
<rect x="0" y="0" width="155" height="55" rx="6" class="icon-btn"/>
|
||||
<text x="10" y="20" font-size="11" font-weight="500" class="main-text">ML Guide</text>
|
||||
<text x="10" y="38" font-size="10" class="muted-text">ml-guide.com</text>
|
||||
<circle cx="140" cy="27" r="8" fill="#22c55e"/>
|
||||
<text x="140" y="31" text-anchor="middle" font-size="8" class="white-text">✓</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(685, 280)">
|
||||
<rect x="0" y="0" width="155" height="55" rx="6" class="icon-btn"/>
|
||||
<text x="10" y="20" font-size="11" font-weight="500" class="main-text">IEEE Research</text>
|
||||
<text x="10" y="38" font-size="10" class="muted-text">ieee.org</text>
|
||||
<circle cx="140" cy="27" r="8" fill="#22c55e"/>
|
||||
<text x="140" y="31" text-anchor="middle" font-size="8" class="white-text">✓</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(685, 345)">
|
||||
<rect x="0" y="0" width="155" height="55" rx="6" class="icon-btn"/>
|
||||
<text x="10" y="20" font-size="11" font-weight="500" class="main-text">ArXiv Paper</text>
|
||||
<text x="10" y="38" font-size="10" class="muted-text">arxiv.org</text>
|
||||
<circle cx="140" cy="27" r="8" fill="#22c55e"/>
|
||||
<text x="140" y="31" text-anchor="middle" font-size="8" class="white-text">✓</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(685, 410)">
|
||||
<rect x="0" y="0" width="155" height="55" rx="6" class="icon-btn"/>
|
||||
<text x="10" y="20" font-size="11" font-weight="500" class="main-text">Medium Article</text>
|
||||
<text x="10" y="38" font-size="10" class="muted-text">medium.com</text>
|
||||
<circle cx="140" cy="27" r="8" fill="#22c55e"/>
|
||||
<text x="140" y="31" text-anchor="middle" font-size="8" class="white-text">✓</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(685, 475)">
|
||||
<rect x="0" y="0" width="155" height="30" rx="6" fill="url(#accentGrad)"/>
|
||||
<text x="77" y="20" text-anchor="middle" font-size="11" class="white-text">+ Add Source</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(50, 555)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Enter = Search | Tab = Next result | S = Save | E = Export citations</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.2 KiB |
248
docs/src/assets/suite/sources-screen.svg
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="purpleGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#8b5cf6"/>
|
||||
<stop offset="100%" stop-color="#a78bfa"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="greenGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#22c55e"/>
|
||||
<stop offset="100%" stop-color="#4ade80"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="orangeGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#f97316"/>
|
||||
<stop offset="100%" stop-color="#fb923c"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.card { fill: #ffffff; stroke: #e2e8f0; stroke-width: 1; }
|
||||
.card-hover { fill: #f8fafc; stroke: #3b82f6; stroke-width: 2; }
|
||||
.tag { fill: #dbeafe; }
|
||||
.tag-green { fill: #dcfce7; }
|
||||
.tag-purple { fill: #f3e8ff; }
|
||||
.tag-orange { fill: #ffedd5; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.card { fill: #1e293b; stroke: #334155; }
|
||||
.card-hover { fill: #1e293b; stroke: #3b82f6; }
|
||||
.tag { fill: #1e3a5f; }
|
||||
.tag-green { fill: #14532d; }
|
||||
.tag-purple { fill: #3b0764; }
|
||||
.tag-orange { fill: #431407; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Sources - Prompts & Templates</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<rect x="30" y="40" width="180" height="530" class="sidebar-bg"/>
|
||||
<line x1="210" y1="40" x2="210" y2="570" class="border"/>
|
||||
|
||||
<!-- New Source Button -->
|
||||
<g transform="translate(45, 55)">
|
||||
<rect x="0" y="0" width="150" height="40" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="75" y="26" text-anchor="middle" font-size="14" font-weight="500" class="white-text">+ New Source</text>
|
||||
</g>
|
||||
|
||||
<!-- Categories -->
|
||||
<g transform="translate(45, 115)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">CATEGORIES</text>
|
||||
|
||||
<g transform="translate(0, 20)">
|
||||
<rect x="-10" y="-8" width="160" height="36" rx="6" fill="#eff6ff"/>
|
||||
<text x="0" y="14" font-size="14">📚</text>
|
||||
<text x="25" y="14" font-size="13" class="accent-text">All Sources</text>
|
||||
<text x="130" y="14" font-size="12" class="accent-text">24</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 60)">
|
||||
<text x="0" y="14" font-size="14">💬</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Prompts</text>
|
||||
<text x="130" y="14" font-size="12" class="muted-text">12</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 95)">
|
||||
<text x="0" y="14" font-size="14">📄</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Templates</text>
|
||||
<text x="130" y="14" font-size="12" class="muted-text">8</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 130)">
|
||||
<text x="0" y="14" font-size="14">🔗</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Integrations</text>
|
||||
<text x="130" y="14" font-size="12" class="muted-text">4</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 165)">
|
||||
<text x="0" y="14" font-size="14">⭐</text>
|
||||
<text x="25" y="14" font-size="13" class="secondary-text">Favorites</text>
|
||||
<text x="130" y="14" font-size="12" class="muted-text">6</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Tags Section -->
|
||||
<g transform="translate(45, 330)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">TAGS</text>
|
||||
|
||||
<g transform="translate(0, 18)">
|
||||
<rect x="0" y="0" width="70" height="24" rx="12" class="tag"/>
|
||||
<text x="35" y="16" text-anchor="middle" font-size="11" class="accent-text">customer</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(78, 18)">
|
||||
<rect x="0" y="0" width="55" height="24" rx="12" class="tag-green"/>
|
||||
<text x="27" y="16" text-anchor="middle" font-size="11" fill="#16a34a">sales</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 50)">
|
||||
<rect x="0" y="0" width="60" height="24" rx="12" class="tag-purple"/>
|
||||
<text x="30" y="16" text-anchor="middle" font-size="11" fill="#7c3aed">support</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(68, 50)">
|
||||
<rect x="0" y="0" width="65" height="24" rx="12" class="tag-orange"/>
|
||||
<text x="32" y="16" text-anchor="middle" font-size="11" fill="#c2410c">technical</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Main Content Header -->
|
||||
<g transform="translate(230, 55)">
|
||||
<text x="0" y="20" font-size="18" font-weight="600" class="main-text">All Sources</text>
|
||||
<text x="0" y="42" font-size="13" class="secondary-text">24 items • Manage your prompts and templates</text>
|
||||
|
||||
<!-- Search and Filter -->
|
||||
<g transform="translate(380, 5)">
|
||||
<rect x="0" y="0" width="200" height="36" rx="18" class="icon-btn"/>
|
||||
<rect x="0" y="0" width="200" height="36" rx="18" class="border"/>
|
||||
<text x="20" y="23" font-size="14">🔍</text>
|
||||
<text x="45" y="24" font-size="13" class="muted-text">Search sources...</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Cards Grid -->
|
||||
<!-- Row 1 -->
|
||||
<g transform="translate(230, 100)">
|
||||
<!-- Card 1 - Selected -->
|
||||
<rect x="0" y="0" width="185" height="140" rx="10" class="card-hover"/>
|
||||
<rect x="15" y="15" width="40" height="40" rx="10" fill="url(#accentGrad)"/>
|
||||
<text x="35" y="42" text-anchor="middle" font-size="20" class="white-text">💬</text>
|
||||
<text x="15" y="75" font-size="14" font-weight="600" class="main-text">Customer Support</text>
|
||||
<text x="15" y="95" font-size="12" class="secondary-text">Handle support inquiries</text>
|
||||
<text x="15" y="110" font-size="12" class="secondary-text">with empathy and clarity</text>
|
||||
<rect x="15" y="120" width="55" height="18" rx="9" class="tag"/>
|
||||
<text x="42" y="132" text-anchor="middle" font-size="10" class="accent-text">prompt</text>
|
||||
|
||||
<!-- Card 2 -->
|
||||
<rect x="200" y="0" width="185" height="140" rx="10" class="card"/>
|
||||
<rect x="215" y="15" width="40" height="40" rx="10" fill="url(#purpleGrad)"/>
|
||||
<text x="235" y="42" text-anchor="middle" font-size="20" class="white-text">📝</text>
|
||||
<text x="215" y="75" font-size="14" font-weight="600" class="main-text">Email Template</text>
|
||||
<text x="215" y="95" font-size="12" class="secondary-text">Professional email</text>
|
||||
<text x="215" y="110" font-size="12" class="secondary-text">response format</text>
|
||||
<rect x="215" y="120" width="60" height="18" rx="9" class="tag-purple"/>
|
||||
<text x="245" y="132" text-anchor="middle" font-size="10" fill="#7c3aed">template</text>
|
||||
|
||||
<!-- Card 3 -->
|
||||
<rect x="400" y="0" width="185" height="140" rx="10" class="card"/>
|
||||
<rect x="415" y="15" width="40" height="40" rx="10" fill="url(#greenGrad)"/>
|
||||
<text x="435" y="42" text-anchor="middle" font-size="20" class="white-text">🤖</text>
|
||||
<text x="415" y="75" font-size="14" font-weight="600" class="main-text">Sales Assistant</text>
|
||||
<text x="415" y="95" font-size="12" class="secondary-text">Product recommendations</text>
|
||||
<text x="415" y="110" font-size="12" class="secondary-text">and pricing help</text>
|
||||
<rect x="415" y="120" width="55" height="18" rx="9" class="tag"/>
|
||||
<text x="442" y="132" text-anchor="middle" font-size="10" class="accent-text">prompt</text>
|
||||
</g>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<g transform="translate(230, 255)">
|
||||
<!-- Card 4 -->
|
||||
<rect x="0" y="0" width="185" height="140" rx="10" class="card"/>
|
||||
<rect x="15" y="15" width="40" height="40" rx="10" fill="url(#orangeGrad)"/>
|
||||
<text x="35" y="42" text-anchor="middle" font-size="20" class="white-text">📊</text>
|
||||
<text x="15" y="75" font-size="14" font-weight="600" class="main-text">Report Generator</text>
|
||||
<text x="15" y="95" font-size="12" class="secondary-text">Create structured</text>
|
||||
<text x="15" y="110" font-size="12" class="secondary-text">analysis reports</text>
|
||||
<rect x="15" y="120" width="60" height="18" rx="9" class="tag-purple"/>
|
||||
<text x="45" y="132" text-anchor="middle" font-size="10" fill="#7c3aed">template</text>
|
||||
|
||||
<!-- Card 5 -->
|
||||
<rect x="200" y="0" width="185" height="140" rx="10" class="card"/>
|
||||
<rect x="215" y="15" width="40" height="40" rx="10" fill="#ef4444"/>
|
||||
<text x="235" y="42" text-anchor="middle" font-size="20" class="white-text">🔗</text>
|
||||
<text x="215" y="75" font-size="14" font-weight="600" class="main-text">CRM Integration</text>
|
||||
<text x="215" y="95" font-size="12" class="secondary-text">Connect with your</text>
|
||||
<text x="215" y="110" font-size="12" class="secondary-text">CRM system</text>
|
||||
<rect x="215" y="120" width="70" height="18" rx="9" class="tag-orange"/>
|
||||
<text x="250" y="132" text-anchor="middle" font-size="10" fill="#c2410c">integration</text>
|
||||
|
||||
<!-- Card 6 -->
|
||||
<rect x="400" y="0" width="185" height="140" rx="10" class="card"/>
|
||||
<rect x="415" y="15" width="40" height="40" rx="10" fill="#06b6d4"/>
|
||||
<text x="435" y="42" text-anchor="middle" font-size="20" class="white-text">🎯</text>
|
||||
<text x="415" y="75" font-size="14" font-weight="600" class="main-text">FAQ Bot</text>
|
||||
<text x="415" y="95" font-size="12" class="secondary-text">Automated answers</text>
|
||||
<text x="415" y="110" font-size="12" class="secondary-text">for common questions</text>
|
||||
<rect x="415" y="120" width="55" height="18" rx="9" class="tag"/>
|
||||
<text x="442" y="132" text-anchor="middle" font-size="10" class="accent-text">prompt</text>
|
||||
</g>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<g transform="translate(230, 410)">
|
||||
<!-- Card 7 -->
|
||||
<rect x="0" y="0" width="185" height="140" rx="10" class="card"/>
|
||||
<rect x="15" y="15" width="40" height="40" rx="10" fill="#ec4899"/>
|
||||
<text x="35" y="42" text-anchor="middle" font-size="20" class="white-text">✍️</text>
|
||||
<text x="15" y="75" font-size="14" font-weight="600" class="main-text">Content Writer</text>
|
||||
<text x="15" y="95" font-size="12" class="secondary-text">Blog posts and</text>
|
||||
<text x="15" y="110" font-size="12" class="secondary-text">marketing copy</text>
|
||||
<rect x="15" y="120" width="55" height="18" rx="9" class="tag"/>
|
||||
<text x="42" y="132" text-anchor="middle" font-size="10" class="accent-text">prompt</text>
|
||||
|
||||
<!-- Card 8 -->
|
||||
<rect x="200" y="0" width="185" height="140" rx="10" class="card"/>
|
||||
<rect x="215" y="15" width="40" height="40" rx="10" fill="#14b8a6"/>
|
||||
<text x="235" y="42" text-anchor="middle" font-size="20" class="white-text">📋</text>
|
||||
<text x="215" y="75" font-size="14" font-weight="600" class="main-text">Meeting Notes</text>
|
||||
<text x="215" y="95" font-size="12" class="secondary-text">Structured meeting</text>
|
||||
<text x="215" y="110" font-size="12" class="secondary-text">summary template</text>
|
||||
<rect x="215" y="120" width="60" height="18" rx="9" class="tag-purple"/>
|
||||
<text x="245" y="132" text-anchor="middle" font-size="10" fill="#7c3aed">template</text>
|
||||
|
||||
<!-- Add New Card -->
|
||||
<rect x="400" y="0" width="185" height="140" rx="10" class="icon-btn" stroke-dasharray="6,4"/>
|
||||
<rect x="400" y="0" width="185" height="140" rx="10" class="border" stroke-dasharray="6,4"/>
|
||||
<circle cx="492" cy="50" r="20" class="icon-btn"/>
|
||||
<text x="492" y="58" text-anchor="middle" font-size="24" class="secondary-text">+</text>
|
||||
<text x="492" y="90" text-anchor="middle" font-size="13" class="secondary-text">Add New Source</text>
|
||||
<text x="492" y="110" text-anchor="middle" font-size="11" class="muted-text">Create from scratch</text>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(230, 565)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Click to edit | Del = Delete | D = Duplicate | Enter = Use in chat</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 12 KiB |
220
docs/src/assets/suite/suite-screen.svg
Normal file
|
|
@ -0,0 +1,220 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#3b82f6"/>
|
||||
<stop offset="100%" style="stop-color:#60a5fa"/>
|
||||
</linearGradient>
|
||||
<filter id="shadow" x="-20%" y="-20%" width="140%" height="140%">
|
||||
<feDropShadow dx="0" dy="4" stdDeviation="8" flood-opacity="0.1"/>
|
||||
</filter>
|
||||
<filter id="shadowSm" x="-10%" y="-10%" width="120%" height="120%">
|
||||
<feDropShadow dx="0" dy="2" stdDeviation="4" flood-opacity="0.08"/>
|
||||
</filter>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.header-bg { fill: rgba(255,255,255,0.8); }
|
||||
.main-text { fill: #1e293b; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; }
|
||||
.card-bg { fill: #f8fafc; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.icon-btn:hover { fill: #e2e8f0; }
|
||||
.user-bubble { fill: #3b82f6; }
|
||||
.bot-bubble { fill: #f8fafc; stroke: #e2e8f0; stroke-width: 1; }
|
||||
.input-bg { fill: #f1f5f9; }
|
||||
.suggestion { fill: #eff6ff; stroke: #3b82f6; stroke-width: 1; }
|
||||
.apps-grid-bg { fill: #ffffff; }
|
||||
.app-item-hover { fill: #f1f5f9; }
|
||||
.online-dot { fill: #22c55e; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.header-bg { fill: rgba(15,23,42,0.8); }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.card-bg { fill: #1e293b; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #1e293b; }
|
||||
.user-bubble { fill: #3b82f6; }
|
||||
.bot-bubble { fill: #1e293b; stroke: #334155; }
|
||||
.input-bg { fill: #1e293b; }
|
||||
.suggestion { fill: #1e3a5f; stroke: #3b82f6; }
|
||||
.apps-grid-bg { fill: #1e293b; }
|
||||
.app-item-hover { fill: #334155; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Title -->
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Suite - Main Interface</text>
|
||||
|
||||
<!-- Background -->
|
||||
<rect x="30" y="40" width="840" height="530" rx="12" class="bg" filter="url(#shadow)"/>
|
||||
|
||||
<!-- Floating Header (Glass morphism) -->
|
||||
<rect x="50" y="55" width="800" height="56" rx="12" class="header-bg" filter="url(#shadowSm)"/>
|
||||
<rect x="50" y="55" width="800" height="56" rx="12" class="border"/>
|
||||
|
||||
<!-- Logo -->
|
||||
<rect x="70" y="70" width="28" height="28" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="84" y="89" text-anchor="middle" font-size="14" class="white-text">GB</text>
|
||||
<text x="110" y="90" font-size="15" font-weight="600" class="main-text">General Bots</text>
|
||||
|
||||
<!-- Header Right: Theme, Apps Grid, Avatar -->
|
||||
<g transform="translate(650, 68)">
|
||||
<!-- Theme dropdown -->
|
||||
<rect x="0" y="0" width="70" height="32" rx="8" class="icon-btn"/>
|
||||
<text x="15" y="21" font-size="12">☀️</text>
|
||||
<text x="32" y="22" font-size="12" class="secondary-text">Light</text>
|
||||
|
||||
<!-- Apps grid button -->
|
||||
<rect x="80" y="0" width="40" height="32" rx="8" class="icon-btn"/>
|
||||
<g transform="translate(91, 8)">
|
||||
<circle cx="4" cy="4" r="2.5" class="secondary-text" fill="#64748b"/>
|
||||
<circle cx="13" cy="4" r="2.5" class="secondary-text" fill="#64748b"/>
|
||||
<circle cx="4" cy="13" r="2.5" class="secondary-text" fill="#64748b"/>
|
||||
<circle cx="13" cy="13" r="2.5" class="secondary-text" fill="#64748b"/>
|
||||
</g>
|
||||
|
||||
<!-- User avatar -->
|
||||
<circle cx="155" cy="16" r="16" fill="url(#accentGrad)"/>
|
||||
<text x="155" y="21" text-anchor="middle" font-size="11" font-weight="500" class="white-text">JD</text>
|
||||
</g>
|
||||
|
||||
<!-- Apps Dropdown (shown expanded) -->
|
||||
<rect x="580" y="120" width="260" height="280" rx="12" class="apps-grid-bg" filter="url(#shadow)"/>
|
||||
<rect x="580" y="120" width="260" height="280" rx="12" class="border"/>
|
||||
|
||||
<text x="600" y="148" font-size="13" font-weight="600" class="main-text">Applications</text>
|
||||
|
||||
<!-- App Grid Items -->
|
||||
<g transform="translate(595, 160)">
|
||||
<!-- Row 1 -->
|
||||
<g transform="translate(0, 0)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" class="app-item-hover"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#3b82f6"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">💬</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Chat</text>
|
||||
</g>
|
||||
<g transform="translate(75, 0)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#f59e0b"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">📁</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Drive</text>
|
||||
</g>
|
||||
<g transform="translate(150, 0)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#10b981"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">✓</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Tasks</text>
|
||||
</g>
|
||||
|
||||
<!-- Row 2 -->
|
||||
<g transform="translate(0, 70)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#ef4444"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">✉</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Mail</text>
|
||||
</g>
|
||||
<g transform="translate(75, 70)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#8b5cf6"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">📅</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Calendar</text>
|
||||
</g>
|
||||
<g transform="translate(150, 70)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#06b6d4"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">🎥</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Meet</text>
|
||||
</g>
|
||||
|
||||
<!-- Row 3 -->
|
||||
<g transform="translate(0, 140)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#ec4899"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">📝</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Paper</text>
|
||||
</g>
|
||||
<g transform="translate(75, 140)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#14b8a6"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">🔍</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Research</text>
|
||||
</g>
|
||||
<g transform="translate(150, 140)">
|
||||
<rect x="0" y="0" width="70" height="65" rx="8" fill="none"/>
|
||||
<rect x="20" y="10" width="30" height="30" rx="8" fill="#f97316"/>
|
||||
<text x="35" y="30" text-anchor="middle" font-size="14" class="white-text">📊</text>
|
||||
<text x="35" y="55" text-anchor="middle" font-size="11" class="main-text">Analytics</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Main Content Area (Chat shown as default) -->
|
||||
<rect x="50" y="125" width="510" height="430" rx="12" class="card-bg"/>
|
||||
<rect x="50" y="125" width="510" height="430" rx="12" class="border"/>
|
||||
|
||||
<!-- Connection status -->
|
||||
<g transform="translate(70, 140)">
|
||||
<circle cx="5" cy="5" r="4" class="online-dot"/>
|
||||
<text x="15" y="9" font-size="11" class="secondary-text">Connected</text>
|
||||
</g>
|
||||
|
||||
<!-- Chat Messages -->
|
||||
<g transform="translate(70, 170)">
|
||||
<!-- Bot message -->
|
||||
<rect x="0" y="0" width="350" height="60" rx="12" class="bot-bubble"/>
|
||||
<text x="15" y="22" font-size="11" class="accent-text">AI Assistant</text>
|
||||
<text x="15" y="42" font-size="13" class="main-text">Hello! How can I help you today?</text>
|
||||
|
||||
<!-- User message -->
|
||||
<rect x="140" y="75" width="330" height="40" rx="12" class="user-bubble"/>
|
||||
<text x="155" y="100" font-size="13" class="white-text">What's on my calendar for today?</text>
|
||||
|
||||
<!-- Bot response -->
|
||||
<rect x="0" y="130" width="380" height="90" rx="12" class="bot-bubble"/>
|
||||
<text x="15" y="152" font-size="11" class="accent-text">AI Assistant</text>
|
||||
<text x="15" y="175" font-size="13" class="main-text">You have 2 meetings today:</text>
|
||||
<text x="15" y="195" font-size="12" class="secondary-text">• 2:00 PM - Team Standup</text>
|
||||
<text x="15" y="212" font-size="12" class="secondary-text">• 4:00 PM - Project Review</text>
|
||||
</g>
|
||||
|
||||
<!-- Suggestions -->
|
||||
<g transform="translate(70, 430)">
|
||||
<rect x="0" y="0" width="110" height="30" rx="15" class="suggestion"/>
|
||||
<text x="55" y="20" text-anchor="middle" font-size="11" class="accent-text">📧 Check email</text>
|
||||
|
||||
<rect x="120" y="0" width="100" height="30" rx="15" class="suggestion"/>
|
||||
<text x="170" y="20" text-anchor="middle" font-size="11" class="accent-text">✓ My tasks</text>
|
||||
|
||||
<rect x="230" y="0" width="120" height="30" rx="15" class="suggestion"/>
|
||||
<text x="290" y="20" text-anchor="middle" font-size="11" class="accent-text">📁 Recent files</text>
|
||||
</g>
|
||||
|
||||
<!-- Input Area -->
|
||||
<g transform="translate(70, 475)">
|
||||
<rect x="0" y="0" width="400" height="48" rx="24" class="input-bg"/>
|
||||
<text x="20" y="30" font-size="13" class="muted-text">Message...</text>
|
||||
|
||||
<rect x="320" y="8" width="32" height="32" rx="16" class="icon-btn"/>
|
||||
<text x="336" y="30" text-anchor="middle" font-size="14">🎤</text>
|
||||
|
||||
<rect x="360" y="8" width="32" height="32" rx="16" fill="url(#accentGrad)"/>
|
||||
<text x="376" y="30" text-anchor="middle" font-size="14" class="white-text">↑</text>
|
||||
</g>
|
||||
|
||||
<!-- Scroll to bottom button -->
|
||||
<circle cx="520" cy="510" r="18" class="icon-btn" filter="url(#shadowSm)"/>
|
||||
<text x="520" y="516" text-anchor="middle" font-size="16" class="secondary-text">↓</text>
|
||||
|
||||
<!-- Keyboard shortcuts legend -->
|
||||
<g transform="translate(50, 565)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Enter = Send | Shift+Enter = New line | / = Commands | @ = Mention</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 11 KiB |
211
docs/src/assets/suite/tasks-screen.svg
Normal file
|
|
@ -0,0 +1,211 @@
|
|||
<svg width="900" height="600" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="accentGrad" x1="0%" y1="0%" x2="100%" y2="100%">
|
||||
<stop offset="0%" stop-color="#3b82f6"/>
|
||||
<stop offset="100%" stop-color="#60a5fa"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<style>
|
||||
.bg { fill: #ffffff; }
|
||||
.sidebar-bg { fill: #f8fafc; }
|
||||
.main-text { fill: #1e293b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.secondary-text { fill: #64748b; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.muted-text { fill: #94a3b8; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.white-text { fill: #ffffff; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.accent-text { fill: #3b82f6; font-family: system-ui, -apple-system, sans-serif; }
|
||||
.border { stroke: #e2e8f0; stroke-width: 1; fill: none; }
|
||||
.icon-btn { fill: #f1f5f9; }
|
||||
.checkbox { fill: #ffffff; stroke: #cbd5e1; stroke-width: 2; }
|
||||
.checkbox-checked { fill: #22c55e; stroke: #22c55e; stroke-width: 2; }
|
||||
.task-row { fill: #ffffff; }
|
||||
.task-row-hover { fill: #f8fafc; }
|
||||
.priority-high { fill: #ef4444; }
|
||||
.priority-medium { fill: #f97316; }
|
||||
.priority-low { fill: #22c55e; }
|
||||
.nav-active { fill: #eff6ff; }
|
||||
.tag-blue { fill: #dbeafe; }
|
||||
.tag-green { fill: #dcfce7; }
|
||||
.tag-purple { fill: #f3e8ff; }
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.bg { fill: #0f172a; }
|
||||
.sidebar-bg { fill: #1e293b; }
|
||||
.main-text { fill: #e2e8f0; }
|
||||
.secondary-text { fill: #94a3b8; }
|
||||
.muted-text { fill: #64748b; }
|
||||
.accent-text { fill: #60a5fa; }
|
||||
.border { stroke: #334155; }
|
||||
.icon-btn { fill: #334155; }
|
||||
.checkbox { fill: #1e293b; stroke: #475569; }
|
||||
.task-row { fill: #0f172a; }
|
||||
.task-row-hover { fill: #1e293b; }
|
||||
.nav-active { fill: #1e3a5f; }
|
||||
.tag-blue { fill: #1e3a5f; }
|
||||
.tag-green { fill: #14532d; }
|
||||
.tag-purple { fill: #3b0764; }
|
||||
}
|
||||
</style>
|
||||
|
||||
<text x="450" y="25" text-anchor="middle" font-size="16" font-weight="600" class="main-text">Tasks - To-Do Manager</text>
|
||||
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="bg"/>
|
||||
<rect x="30" y="40" width="840" height="530" rx="8" class="border"/>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<rect x="30" y="40" width="180" height="530" class="sidebar-bg"/>
|
||||
<line x1="210" y1="40" x2="210" y2="570" class="border"/>
|
||||
|
||||
<!-- Add Task Button -->
|
||||
<g transform="translate(45, 55)">
|
||||
<rect x="0" y="0" width="150" height="40" rx="8" fill="url(#accentGrad)"/>
|
||||
<text x="75" y="26" text-anchor="middle" font-size="14" font-weight="500" class="white-text">+ Add Task</text>
|
||||
</g>
|
||||
|
||||
<!-- Navigation -->
|
||||
<g transform="translate(45, 115)">
|
||||
<rect x="-10" y="-8" width="160" height="36" rx="6" class="nav-active"/>
|
||||
<text x="0" y="14" font-size="14">📋</text>
|
||||
<text x="25" y="14" font-size="14" class="accent-text">All Tasks</text>
|
||||
<text x="130" y="14" font-size="12" class="accent-text">12</text>
|
||||
|
||||
<text x="0" y="54" font-size="14">📅</text>
|
||||
<text x="25" y="54" font-size="14" class="secondary-text">Today</text>
|
||||
<text x="130" y="54" font-size="12" class="secondary-text">5</text>
|
||||
|
||||
<text x="0" y="94" font-size="14">📆</text>
|
||||
<text x="25" y="94" font-size="14" class="secondary-text">Upcoming</text>
|
||||
<text x="130" y="94" font-size="12" class="secondary-text">8</text>
|
||||
|
||||
<text x="0" y="134" font-size="14">⭐</text>
|
||||
<text x="25" y="134" font-size="14" class="secondary-text">Important</text>
|
||||
<text x="130" y="134" font-size="12" class="secondary-text">3</text>
|
||||
|
||||
<text x="0" y="174" font-size="14">✅</text>
|
||||
<text x="25" y="174" font-size="14" class="secondary-text">Completed</text>
|
||||
<text x="130" y="174" font-size="12" class="secondary-text">24</text>
|
||||
</g>
|
||||
|
||||
<!-- Projects Section -->
|
||||
<g transform="translate(45, 320)">
|
||||
<text x="0" y="0" font-size="11" font-weight="600" class="muted-text">PROJECTS</text>
|
||||
|
||||
<g transform="translate(0, 20)">
|
||||
<circle cx="7" cy="7" r="5" fill="#3b82f6"/>
|
||||
<text x="20" y="11" font-size="13" class="main-text">Work</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 45)">
|
||||
<circle cx="7" cy="7" r="5" fill="#22c55e"/>
|
||||
<text x="20" y="11" font-size="13" class="main-text">Personal</text>
|
||||
</g>
|
||||
|
||||
<g transform="translate(0, 70)">
|
||||
<circle cx="7" cy="7" r="5" fill="#8b5cf6"/>
|
||||
<text x="20" y="11" font-size="13" class="main-text">Learning</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Main Content Header -->
|
||||
<g transform="translate(230, 55)">
|
||||
<text x="0" y="20" font-size="18" font-weight="600" class="main-text">All Tasks</text>
|
||||
<text x="0" y="40" font-size="13" class="secondary-text">12 tasks, 5 due today</text>
|
||||
|
||||
<!-- Filter/Sort -->
|
||||
<g transform="translate(450, 5)">
|
||||
<rect x="0" y="0" width="80" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="40" y="21" text-anchor="middle" font-size="12" class="secondary-text">Filter ▾</text>
|
||||
|
||||
<rect x="90" y="0" width="70" height="32" rx="6" class="icon-btn"/>
|
||||
<text x="125" y="21" text-anchor="middle" font-size="12" class="secondary-text">Sort ▾</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Task Input -->
|
||||
<g transform="translate(230, 100)">
|
||||
<rect x="0" y="0" width="620" height="45" rx="8" class="icon-btn"/>
|
||||
<rect x="0" y="0" width="620" height="45" rx="8" class="border"/>
|
||||
<circle cx="25" cy="22" r="10" class="checkbox"/>
|
||||
<text x="50" y="27" font-size="14" class="muted-text">Add a new task...</text>
|
||||
</g>
|
||||
|
||||
<!-- Task List -->
|
||||
<g transform="translate(230, 160)">
|
||||
<!-- Task 1 - High Priority -->
|
||||
<g transform="translate(0, 0)">
|
||||
<rect x="0" y="0" width="620" height="55" rx="6" class="task-row-hover"/>
|
||||
<circle cx="25" cy="27" r="10" class="checkbox"/>
|
||||
<rect x="45" y="20" width="4" height="16" rx="2" class="priority-high"/>
|
||||
<text x="60" y="25" font-size="14" font-weight="500" class="main-text">Complete quarterly report</text>
|
||||
<rect x="60" y="32" width="45" height="18" rx="9" class="tag-blue"/>
|
||||
<text x="82" y="44" text-anchor="middle" font-size="10" class="accent-text">Work</text>
|
||||
<text x="500" y="30" font-size="12" class="priority-high">Today</text>
|
||||
<text x="580" y="30" font-size="14" class="muted-text">⭐</text>
|
||||
</g>
|
||||
|
||||
<!-- Task 2 - High Priority -->
|
||||
<g transform="translate(0, 60)">
|
||||
<rect x="0" y="0" width="620" height="55" rx="6" class="task-row"/>
|
||||
<circle cx="25" cy="27" r="10" class="checkbox"/>
|
||||
<rect x="45" y="20" width="4" height="16" rx="2" class="priority-high"/>
|
||||
<text x="60" y="25" font-size="14" font-weight="500" class="main-text">Review pull request #234</text>
|
||||
<rect x="60" y="32" width="45" height="18" rx="9" class="tag-blue"/>
|
||||
<text x="82" y="44" text-anchor="middle" font-size="10" class="accent-text">Work</text>
|
||||
<text x="500" y="30" font-size="12" class="priority-high">Today</text>
|
||||
<text x="580" y="30" font-size="14" class="secondary-text">☆</text>
|
||||
</g>
|
||||
|
||||
<!-- Task 3 - Medium Priority -->
|
||||
<g transform="translate(0, 120)">
|
||||
<rect x="0" y="0" width="620" height="55" rx="6" class="task-row"/>
|
||||
<circle cx="25" cy="27" r="10" class="checkbox"/>
|
||||
<rect x="45" y="20" width="4" height="16" rx="2" class="priority-medium"/>
|
||||
<text x="60" y="25" font-size="14" font-weight="500" class="main-text">Prepare presentation slides</text>
|
||||
<rect x="60" y="32" width="45" height="18" rx="9" class="tag-blue"/>
|
||||
<text x="82" y="44" text-anchor="middle" font-size="10" class="accent-text">Work</text>
|
||||
<text x="500" y="30" font-size="12" class="secondary-text">Tomorrow</text>
|
||||
<text x="580" y="30" font-size="14" class="secondary-text">☆</text>
|
||||
</g>
|
||||
|
||||
<!-- Task 4 - Low Priority -->
|
||||
<g transform="translate(0, 180)">
|
||||
<rect x="0" y="0" width="620" height="55" rx="6" class="task-row"/>
|
||||
<circle cx="25" cy="27" r="10" class="checkbox"/>
|
||||
<rect x="45" y="20" width="4" height="16" rx="2" class="priority-low"/>
|
||||
<text x="60" y="25" font-size="14" font-weight="500" class="main-text">Read documentation updates</text>
|
||||
<rect x="60" y="32" width="55" height="18" rx="9" class="tag-purple"/>
|
||||
<text x="87" y="44" text-anchor="middle" font-size="10" fill="#7c3aed">Learning</text>
|
||||
<text x="500" y="30" font-size="12" class="secondary-text">Mar 20</text>
|
||||
<text x="580" y="30" font-size="14" class="secondary-text">☆</text>
|
||||
</g>
|
||||
|
||||
<!-- Task 5 - Completed -->
|
||||
<g transform="translate(0, 240)">
|
||||
<rect x="0" y="0" width="620" height="55" rx="6" class="task-row"/>
|
||||
<circle cx="25" cy="27" r="10" class="checkbox-checked"/>
|
||||
<text x="25" y="32" text-anchor="middle" font-size="12" class="white-text">✓</text>
|
||||
<text x="60" y="25" font-size="14" class="muted-text" text-decoration="line-through">Update team meeting notes</text>
|
||||
<rect x="60" y="32" width="45" height="18" rx="9" class="tag-blue"/>
|
||||
<text x="82" y="44" text-anchor="middle" font-size="10" class="muted-text">Work</text>
|
||||
<text x="500" y="30" font-size="12" class="muted-text">Completed</text>
|
||||
<text x="580" y="30" font-size="14" class="muted-text">☆</text>
|
||||
</g>
|
||||
|
||||
<!-- Task 6 - Completed -->
|
||||
<g transform="translate(0, 300)">
|
||||
<rect x="0" y="0" width="620" height="55" rx="6" class="task-row"/>
|
||||
<circle cx="25" cy="27" r="10" class="checkbox-checked"/>
|
||||
<text x="25" y="32" text-anchor="middle" font-size="12" class="white-text">✓</text>
|
||||
<text x="60" y="25" font-size="14" class="muted-text" text-decoration="line-through">Schedule dentist appointment</text>
|
||||
<rect x="60" y="32" width="55" height="18" rx="9" class="tag-green"/>
|
||||
<text x="87" y="44" text-anchor="middle" font-size="10" fill="#16a34a">Personal</text>
|
||||
<text x="500" y="30" font-size="12" class="muted-text">Completed</text>
|
||||
<text x="580" y="30" font-size="14" class="muted-text">☆</text>
|
||||
</g>
|
||||
</g>
|
||||
|
||||
<!-- Footer -->
|
||||
<g transform="translate(230, 555)">
|
||||
<text x="0" y="0" font-size="10" class="muted-text">Enter = Add task | Space = Toggle complete | Del = Delete | S = Star</text>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 9.9 KiB |
209
docs/src/assets/wa-chat.css
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
/**
|
||||
* WhatsApp-style Chat Conversation CSS
|
||||
*
|
||||
* Standard styling for conversation examples in General Bots documentation.
|
||||
* Include this CSS in your mdBook or HTML output to style <div class="wa-chat"> elements.
|
||||
*
|
||||
* Usage:
|
||||
* <div class="wa-chat">
|
||||
* <div class="wa-message bot">
|
||||
* <div class="wa-bubble">
|
||||
* <p>Bot message here</p>
|
||||
* <div class="wa-time">10:30</div>
|
||||
* </div>
|
||||
* </div>
|
||||
* <div class="wa-message user">
|
||||
* <div class="wa-bubble">
|
||||
* <p>User message here</p>
|
||||
* <div class="wa-time">10:31</div>
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
*/
|
||||
|
||||
/* ============================================
|
||||
Chat Container
|
||||
============================================ */
|
||||
.wa-chat {
|
||||
background-color: #e5ddd5;
|
||||
border-radius: 8px;
|
||||
padding: 20px 15px;
|
||||
margin: 20px 0;
|
||||
max-width: 500px;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* Dark mode support */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wa-chat {
|
||||
background-color: #0b141a;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Message Container
|
||||
============================================ */
|
||||
.wa-message {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.wa-message:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* User messages align right */
|
||||
.wa-message.user {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Message Bubbles
|
||||
============================================ */
|
||||
.wa-bubble {
|
||||
padding: 8px 12px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 0.5px rgba(0, 0, 0, 0.13);
|
||||
max-width: 85%;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* Bot bubble - white/light */
|
||||
.wa-message.bot .wa-bubble {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* User bubble - green (WhatsApp style) */
|
||||
.wa-message.user .wa-bubble {
|
||||
background-color: #dcf8c6;
|
||||
}
|
||||
|
||||
/* Dark mode bubbles */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wa-message.bot .wa-bubble {
|
||||
background-color: #202c33;
|
||||
}
|
||||
|
||||
.wa-message.user .wa-bubble {
|
||||
background-color: #005c4b;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Message Text
|
||||
============================================ */
|
||||
.wa-bubble p {
|
||||
margin: 0 0 4px 0;
|
||||
line-height: 1.4;
|
||||
color: #303030;
|
||||
}
|
||||
|
||||
.wa-bubble p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Bold text in messages */
|
||||
.wa-bubble strong {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Dark mode text */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wa-bubble p {
|
||||
color: #e9edef;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Timestamp
|
||||
============================================ */
|
||||
.wa-time {
|
||||
font-size: 11px;
|
||||
color: #8696a0;
|
||||
text-align: right;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Special Elements
|
||||
============================================ */
|
||||
|
||||
/* Links in messages */
|
||||
.wa-bubble a {
|
||||
color: #00a884;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.wa-bubble a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Code in messages */
|
||||
.wa-bubble code {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
font-family: 'SF Mono', Consolas, 'Liberation Mono', Menlo, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wa-bubble code {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
/* Action buttons in messages */
|
||||
.wa-bubble .wa-action {
|
||||
display: inline-block;
|
||||
background-color: #e8f4fd;
|
||||
color: #4a90e2;
|
||||
padding: 4px 10px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
margin: 4px 4px 0 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.wa-bubble .wa-action:hover {
|
||||
background-color: #d0e8fc;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.wa-bubble .wa-action {
|
||||
background-color: #1a3a4a;
|
||||
color: #00d4ff;
|
||||
}
|
||||
|
||||
.wa-bubble .wa-action:hover {
|
||||
background-color: #2a4a5a;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
Variants
|
||||
============================================ */
|
||||
|
||||
/* Full-width variant */
|
||||
.wa-chat.wa-full-width {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* Compact variant (less padding) */
|
||||
.wa-chat.wa-compact {
|
||||
padding: 12px 10px;
|
||||
}
|
||||
|
||||
.wa-chat.wa-compact .wa-message {
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.wa-chat.wa-compact .wa-bubble {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
/* No-time variant (hide timestamps) */
|
||||
.wa-chat.wa-no-time .wa-time {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your business intelligence center**
|
||||
|
||||
<img src="../../assets/suite/analytics-flow.svg" alt="Analytics Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/analytics-screen.svg" alt="Analytics Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your personal scheduling assistant**
|
||||
|
||||

|
||||
<img src="../../assets/suite/calendar-screen.svg" alt="Calendar Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your intelligent conversation partner**
|
||||
|
||||
<img src="../../assets/suite/chat-flow.svg" alt="Chat Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/chat-screen.svg" alt="Chat Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your privacy and security guardian**
|
||||
|
||||
<img src="../../assets/suite/compliance-flow.svg" alt="Compliance Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/compliance-screen.svg" alt="Compliance Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your no-code bot building studio**
|
||||
|
||||
<img src="../../assets/suite/designer-flow.svg" alt="Designer Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/designer-screen.svg" alt="Designer Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your cloud storage workspace**
|
||||
|
||||
<img src="../../assets/suite/drive-flow.svg" alt="Drive Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/drive-screen.svg" alt="Drive Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your intelligent inbox**
|
||||
|
||||

|
||||
<img src="../../assets/suite/mail-screen.svg" alt="Mail Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your virtual meeting room**
|
||||
|
||||

|
||||
<img src="../../assets/suite/meet-screen.svg" alt="Meet Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your intelligent document editor**
|
||||
|
||||
<img src="../../assets/suite/paper-flow.svg" alt="Paper Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/paper-screen.svg" alt="Paper Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Integrated viewing for documents, audio, video, and presentations**
|
||||
|
||||
<img src="../../assets/suite/player-flow.svg" alt="Player Flow" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/player-screen.svg" alt="Player Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your intelligent research assistant**
|
||||
|
||||
<img src="../../assets/suite/research-flow.svg" alt="Research Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/research-screen.svg" alt="Research Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Your bot configuration hub**
|
||||
|
||||
<img src="../../assets/suite/sources-flow.svg" alt="Sources Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/sources-screen.svg" alt="Sources Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Complete productivity suite with integrated applications**
|
||||
|
||||
<img src="../../assets/suite/suite-layout.svg" alt="Suite Layout" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/suite-screen.svg" alt="Suite Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
> **Track what needs to be done**
|
||||
|
||||
<img src="../../assets/suite/tasks-flow.svg" alt="Tasks Flow Diagram" style="max-width: 100%; height: auto;">
|
||||
<img src="../../assets/suite/tasks-screen.svg" alt="Tasks Interface Screen" style="max-width: 100%; height: auto;">
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -30,18 +30,7 @@ General Bots Suite is your all-in-one workspace that combines communication, pro
|
|||
|
||||
When the Suite opens, you see:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🤖 General Bots [Apps Menu ⋮⋮⋮] [Theme 🌙] [U] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ │
|
||||
│ 💬 Chat (Main Area) │
|
||||
│ │
|
||||
│ Type your message here... │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/suite-main-layout.svg" alt="Suite Main Layout" style="max-width: 100%; height: auto;">
|
||||
|
||||
### The Apps Menu
|
||||
|
||||
|
|
@ -136,22 +125,7 @@ Drive is your file storage - like Google Drive or OneDrive. Store documents, ima
|
|||
|
||||
### The Drive Interface
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ [+ New ▼] 🔍 Search files... [⊞] [≡] │
|
||||
├──────────────┬──────────────────────────────────────────────────┤
|
||||
│ │ 📁 My Drive > Projects > 2024 │
|
||||
│ My Drive │ ─────────────────────────────────────────────────│
|
||||
│ ⭐ Starred │ [☐] Name Size Modified │
|
||||
│ 🕐 Recent │ ─────────────────────────────────────────────── │
|
||||
│ 🗑 Trash │ 📁 Reports - Today │
|
||||
│ │ 📁 Presentations - Yesterday │
|
||||
│ ───────────│ 📄 Budget.xlsx 245 KB Mar 15 │
|
||||
│ Labels │ 📄 Notes.docx 12 KB Mar 14 │
|
||||
│ 🔵 Work │ 🖼 Logo.png 89 KB Mar 10 │
|
||||
│ 🟢 Personal│ │
|
||||
└──────────────┴──────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/drive-interface.svg" alt="Drive Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Creating and Uploading
|
||||
|
||||
|
|
@ -213,23 +187,7 @@ Tasks helps you track what needs to be done. Create to-do lists, set due dates,
|
|||
|
||||
### The Tasks Interface
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ✓ Tasks Total: 12 Active: 5 Done: 7│
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ┌─────────────────────────────────────────────────────────┐ │
|
||||
│ │ What needs to be done? [Category ▼] [+ Add]│ │
|
||||
│ └─────────────────────────────────────────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ [📋 All (12)] [⏳ Active (5)] [✓ Completed (7)] [⚡ Priority] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ☐ Review quarterly report 📅 Today 🔴 │
|
||||
│ ☐ Call client about proposal 📅 Today 🟡 │
|
||||
│ ☐ Update project documentation 📅 Tomorrow 🟢 │
|
||||
│ ☑ Send meeting notes ✓ Done │
|
||||
│ ☑ Complete expense report ✓ Done │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/tasks-interface.svg" alt="Tasks Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Adding a Task
|
||||
|
||||
|
|
@ -281,24 +239,7 @@ Mail connects to your email accounts so you can read, write, and organize emails
|
|||
|
||||
### The Mail Interface
|
||||
|
||||
```
|
||||
┌────────────────┬───────────────────┬────────────────────────────┐
|
||||
│ │ │ │
|
||||
│ [✏ Compose] │ Inbox │ From: john@company.com │
|
||||
│ │ │ Subject: Project Update │
|
||||
│ 📥 Inbox (3) │ ● Project Update │ ──────────────────────── │
|
||||
│ 📤 Sent │ from John │ │
|
||||
│ 📝 Drafts │ 10:30 AM │ Hi, │
|
||||
│ 🗑 Trash │ │ │
|
||||
│ │ ○ Meeting Notes │ Here's the latest update │
|
||||
│ │ from Sarah │ on our project... │
|
||||
│ │ Yesterday │ │
|
||||
│ │ │ Best, │
|
||||
│ │ ○ Invoice #1234 │ John │
|
||||
│ │ from Vendor │ │
|
||||
│ │ Mar 15 │ [Reply] [Forward] [Delete]│
|
||||
└────────────────┴───────────────────┴────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/mail-interface.svg" alt="Mail Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Reading Email
|
||||
|
||||
|
|
@ -364,25 +305,7 @@ Calendar shows your schedule, meetings, and events. Plan your day, week, or mont
|
|||
|
||||
### The Calendar Interface
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ ◄ March 2024 ► [Day] [Week] [Month] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Mon Tue Wed Thu Fri Sat Sun │
|
||||
│ ────────────────────────────────────────────────────────────── │
|
||||
│ 1 2 3 │
|
||||
│ │
|
||||
│ 4 5 6 7 8 9 10 │
|
||||
│ ████ │
|
||||
│ Team │
|
||||
│ Meeting │
|
||||
│ │
|
||||
│ 11 12 13 14 15 16 17 │
|
||||
│ ████ ████ │
|
||||
│ Project Review │
|
||||
│ Demo 1:1 │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/calendar-interface.svg" alt="Calendar Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Creating an Event
|
||||
|
||||
|
|
@ -457,24 +380,7 @@ Bot: Meeting scheduled:
|
|||
|
||||
### The Meeting Interface
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Meeting Room 00:15:32 [👥 3] [💬] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ┌─────────────────────┐ ┌─────────────────────┐ │
|
||||
│ │ │ │ │ │
|
||||
│ │ 👤 You │ │ 👤 John │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ (Camera) │ │ (Camera) │ │
|
||||
│ └─────────────────────┘ └─────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─────────────────────┐ │
|
||||
│ │ 👤 Sarah │ │
|
||||
│ └─────────────────────┘ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ [🎤 Mute] [📹 Video] [🖥 Share] [🔴 Record] [📞 End] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/meet-interface.svg" alt="Meet Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Meeting Controls
|
||||
|
||||
|
|
@ -536,24 +442,7 @@ Paper is your writing space with AI assistance. Write documents, notes, reports
|
|||
|
||||
### The Paper Interface
|
||||
|
||||
```
|
||||
┌────────────────┬────────────────────────────────────────────────┐
|
||||
│ │ [B] [I] [U] H1 H2 • ― 🔗 📷 [AI ✨] │
|
||||
│ 📄 Notes ├────────────────────────────────────────────────┤
|
||||
│ ──────────── │ │
|
||||
│ Meeting Notes │ Project Proposal │
|
||||
│ Project Plan │ ════════════════ │
|
||||
│ Ideas │ │
|
||||
│ │ Introduction │
|
||||
│ ──────────── │ ──────────── │
|
||||
│ Quick Start │ │
|
||||
│ [📄 Blank] │ This document outlines our proposal for │
|
||||
│ [📋 Meeting] │ the upcoming project. We aim to... │
|
||||
│ [✓ To-Do] │ │
|
||||
│ [🔬 Research] │ | │
|
||||
│ │ │
|
||||
└────────────────┴────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/paper-interface.svg" alt="Paper Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Creating a Document
|
||||
|
||||
|
|
@ -620,28 +509,7 @@ Research is like having a research assistant. Search the web, your documents, an
|
|||
|
||||
### The Research Interface
|
||||
|
||||
```
|
||||
┌────────────────┬────────────────────────────────────────────────┐
|
||||
│ │ │
|
||||
│ 🔍 Research │ ┌────────────────────────────────────────┐ │
|
||||
│ │ │ What are the best practices for... │ │
|
||||
│ ──────────── │ └────────────────────────────────────────┘ │
|
||||
│ Focus: │ │
|
||||
│ [🌐 All] │ AI Answer: │
|
||||
│ [📚 Academic] │ ══════════ │
|
||||
│ [💻 Code] │ Based on multiple sources, here are the │
|
||||
│ [🏠 Internal] │ key best practices: │
|
||||
│ │ │
|
||||
│ Collections: │ 1. Start with clear requirements │
|
||||
│ 📁 Project A │ 2. Use iterative development │
|
||||
│ 📁 References │ 3. Test early and often │
|
||||
│ │ │
|
||||
│ Recent: │ Sources: │
|
||||
│ • market size │ [1] industry-guide.com │
|
||||
│ • competitors │ [2] techblog.dev │
|
||||
│ │ [3] your-docs/guidelines.pdf │
|
||||
└────────────────┴────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/research-interface.svg" alt="Research Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Search Modes
|
||||
|
||||
|
|
@ -692,26 +560,7 @@ Analytics shows you reports about usage, conversations, and performance. Underst
|
|||
|
||||
### The Analytics Interface
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 📊 Analytics Dashboard [Last 24h ▼] [⟳ Refresh] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ 1,234 │ │ 89% │ │ 2.3s │ │ 45 │ │
|
||||
│ │ Messages │ │ Success │ │ Avg Time │ │ Users │ │
|
||||
│ │ +12% │ │ Rate │ │ Response │ │ Today │ │
|
||||
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Messages Over Time Top Questions │
|
||||
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ ╭─╮ │ │ 1. How do I reset... │ │
|
||||
│ │ ╭╯ ╰╮ ╭─╮ │ │ 2. What is the status...│ │
|
||||
│ │ ╭╯ ╰────╯ ╰╮ │ │ 3. Where can I find... │ │
|
||||
│ │ ─╯ ╰── │ │ 4. Help with login │ │
|
||||
│ └─────────────────────────┘ └─────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/analytics-interface.svg" alt="Analytics Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Key Metrics
|
||||
|
||||
|
|
@ -750,29 +599,7 @@ Designer lets you create bot conversations visually - like VB6 form designer, bu
|
|||
|
||||
### The Designer Interface
|
||||
|
||||
```
|
||||
┌──────────────┬──────────────────────────────────────┬──────────┐
|
||||
│ Toolbox │ Canvas │Properties│
|
||||
├──────────────┼──────────────────────────────────────┼──────────┤
|
||||
│ │ │ │
|
||||
│ 💬 TALK │ ┌─────────┐ │ Node: │
|
||||
│ 👂 HEAR │ │💬 TALK │ │ TALK │
|
||||
│ 📝 SET │ │"Hello!" │───┐ │ │
|
||||
│ │ └─────────┘ │ │ Message: │
|
||||
│ ───────── │ ▼ │ [Hello!] │
|
||||
│ │ ┌─────────┐ ┌─────────┐ │ │
|
||||
│ 🔀 IF │ │👂 HEAR │──▶│🔀 IF │ │ │
|
||||
│ 🔄 FOR │ │ name │ │name="Jo"│ │ │
|
||||
│ 🔃 SWITCH │ └─────────┘ └────┬────┘ │ │
|
||||
│ │ ┌───┴───┐ │ │
|
||||
│ ───────── │ Yes No │ │
|
||||
│ │ │ │ │ │
|
||||
│ 📞 CALL │ ┌────┴──┐ ┌──┴────┐ │ │
|
||||
│ 📧 SEND │ │💬TALK │ │💬TALK │ │ │
|
||||
│ 💾 SAVE │ │"Hi Jo"│ │"Hello"│ │ │
|
||||
│ │ └───────┘ └───────┘ │ │
|
||||
└──────────────┴──────────────────────────────────────┴──────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/designer-interface.svg" alt="Designer Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Building a Dialog
|
||||
|
||||
|
|
@ -809,27 +636,7 @@ Designer lets you create bot conversations visually - like VB6 form designer, bu
|
|||
|
||||
### Example: Simple Greeting Dialog
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ 💬 TALK │
|
||||
│ "What's your │
|
||||
│ name?" │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 👂 HEAR │
|
||||
│ as: name │
|
||||
│ type: STRING │
|
||||
└────────┬────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ 💬 TALK │
|
||||
│ "Nice to meet │
|
||||
│ you, {name}!" │
|
||||
└─────────────────┘
|
||||
```
|
||||
The Designer canvas shows flow diagrams like the one in the interface above. A simple greeting dialog flows from a TALK node ("What's your name?") to a HEAR node (capturing the name as a string variable) to another TALK node ("Nice to meet you, {name}!").
|
||||
|
||||
**Generated Code:**
|
||||
```basic
|
||||
|
|
@ -861,25 +668,7 @@ Sources is your library of prompts, templates, tools, and AI models. Find and us
|
|||
|
||||
### The Sources Interface
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Sources 🔍 Search... │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ [Prompts] [Templates] [MCP Servers] [LLM Tools] [Models] │
|
||||
├──────────────┬──────────────────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ Categories │ ⭐ Featured │
|
||||
│ ─────────── │ ┌────────────┐ ┌────────────┐ │
|
||||
│ 📝 Writing │ │ Customer │ │ Sales │ │
|
||||
│ 📊 Analysis │ │ Service │ │ Assistant │ │
|
||||
│ 💼 Business │ │ ──────────│ │ ──────────│ │
|
||||
│ 💻 Code │ │ Handle │ │ Qualify │ │
|
||||
│ 🎨 Creative │ │ support │ │ leads and │ │
|
||||
│ │ │ inquiries │ │ schedule │ │
|
||||
│ │ │ [Use] │ │ [Use] │ │
|
||||
│ │ └────────────┘ └────────────┘ │
|
||||
└──────────────┴──────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/sources-interface.svg" alt="Sources Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
### Tabs Explained
|
||||
|
||||
|
|
@ -921,24 +710,7 @@ Sources is your library of prompts, templates, tools, and AI models. Find and us
|
|||
|
||||
### Compliance Scanner
|
||||
|
||||
Check your bot dialogs for security issues:
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 🛡 Compliance Scanner [Scan] [Export] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ │
|
||||
│ │ 2 │ │ 5 │ │ 3 │ │ 1 │ │ 0 │ │
|
||||
│ │🔴Crit│ │🟠High│ │🟡Med │ │🟢Low │ │ℹInfo│ │
|
||||
│ └──────┘ └──────┘ └──────┘ └──────┘ └──────┘ │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Severity │ Issue │ File │ Action │
|
||||
│ ───────────────────────────────────────────────────────────── │
|
||||
│ 🔴 Critical│ Hardcoded password │ start.bas:15 │ [Fix] │
|
||||
│ 🔴 Critical│ API key exposed │ api.bas:42 │ [Fix] │
|
||||
│ 🟠 High │ SQL injection risk │ data.bas:28 │ [Review] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
<img src="../assets/chapter-04/compliance-interface.svg" alt="Compliance Scanner Interface" style="max-width: 100%; height: auto;">
|
||||
|
||||
**What It Checks:**
|
||||
- Hardcoded passwords
|
||||
|
|
|
|||
BIN
docs/src/favicon.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
852
src/security/cert_pinning.rs
Normal file
|
|
@ -0,0 +1,852 @@
|
|||
//! Certificate Pinning Module
|
||||
//!
|
||||
//! Provides certificate pinning functionality to prevent man-in-the-middle attacks
|
||||
//! by validating server certificates against pre-configured SHA-256 fingerprints.
|
||||
//!
|
||||
//! # Overview
|
||||
//!
|
||||
//! Certificate pinning adds an additional layer of security beyond standard TLS
|
||||
//! certificate validation. Even if an attacker obtains a valid certificate from
|
||||
//! a trusted CA, the connection will be rejected if the certificate's fingerprint
|
||||
//! doesn't match the pinned value.
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! use botserver::security::cert_pinning::{CertPinningConfig, CertPinningManager, PinnedCert};
|
||||
//!
|
||||
//! let mut config = CertPinningConfig::default();
|
||||
//! config.add_pin(PinnedCert::new(
|
||||
//! "api.example.com",
|
||||
//! "sha256//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||
//! ));
|
||||
//!
|
||||
//! let manager = CertPinningManager::new(config);
|
||||
//! let client = manager.create_pinned_client("api.example.com")?;
|
||||
//! ```
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
|
||||
use reqwest::{Certificate, Client, ClientBuilder};
|
||||
use ring::digest::{digest, SHA256};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use x509_parser::prelude::*;
|
||||
|
||||
/// Configuration for certificate pinning
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CertPinningConfig {
|
||||
/// Enable certificate pinning globally
|
||||
pub enabled: bool,
|
||||
|
||||
/// Pinned certificates by hostname
|
||||
pub pins: HashMap<String, Vec<PinnedCert>>,
|
||||
|
||||
/// Whether to fail if no pin is configured for a host
|
||||
pub require_pins: bool,
|
||||
|
||||
/// Allow backup pins (multiple pins per host for rotation)
|
||||
pub allow_backup_pins: bool,
|
||||
|
||||
/// Report-only mode (log violations but don't block)
|
||||
pub report_only: bool,
|
||||
|
||||
/// Path to store/load pin configuration
|
||||
pub config_path: Option<PathBuf>,
|
||||
|
||||
/// Pin validation cache TTL in seconds
|
||||
pub cache_ttl_secs: u64,
|
||||
}
|
||||
|
||||
impl Default for CertPinningConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
pins: HashMap::new(),
|
||||
require_pins: false,
|
||||
allow_backup_pins: true,
|
||||
report_only: false,
|
||||
config_path: None,
|
||||
cache_ttl_secs: 3600,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CertPinningConfig {
|
||||
/// Create a new config with pinning enabled
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Create a strict config that requires pins for all hosts
|
||||
pub fn strict() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
pins: HashMap::new(),
|
||||
require_pins: true,
|
||||
allow_backup_pins: true,
|
||||
report_only: false,
|
||||
config_path: None,
|
||||
cache_ttl_secs: 3600,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a report-only config for testing
|
||||
pub fn report_only() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
pins: HashMap::new(),
|
||||
require_pins: false,
|
||||
allow_backup_pins: true,
|
||||
report_only: true,
|
||||
config_path: None,
|
||||
cache_ttl_secs: 3600,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a pinned certificate
|
||||
pub fn add_pin(&mut self, pin: PinnedCert) {
|
||||
let hostname = pin.hostname.clone();
|
||||
self.pins.entry(hostname).or_default().push(pin);
|
||||
}
|
||||
|
||||
/// Add multiple pins for a hostname (primary + backups)
|
||||
pub fn add_pins(&mut self, hostname: &str, pins: Vec<PinnedCert>) {
|
||||
self.pins.insert(hostname.to_string(), pins);
|
||||
}
|
||||
|
||||
/// Remove all pins for a hostname
|
||||
pub fn remove_pins(&mut self, hostname: &str) {
|
||||
self.pins.remove(hostname);
|
||||
}
|
||||
|
||||
/// Get pins for a hostname
|
||||
pub fn get_pins(&self, hostname: &str) -> Option<&Vec<PinnedCert>> {
|
||||
self.pins.get(hostname)
|
||||
}
|
||||
|
||||
/// Load configuration from file
|
||||
pub fn load_from_file(path: &Path) -> Result<Self> {
|
||||
let content = fs::read_to_string(path)
|
||||
.with_context(|| format!("Failed to read pin config from {:?}", path))?;
|
||||
|
||||
let config: Self =
|
||||
serde_json::from_str(&content).context("Failed to parse pin configuration")?;
|
||||
|
||||
info!("Loaded certificate pinning config from {:?}", path);
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Save configuration to file
|
||||
pub fn save_to_file(&self, path: &Path) -> Result<()> {
|
||||
let content =
|
||||
serde_json::to_string_pretty(self).context("Failed to serialize pin configuration")?;
|
||||
|
||||
fs::write(path, content)
|
||||
.with_context(|| format!("Failed to write pin config to {:?}", path))?;
|
||||
|
||||
info!("Saved certificate pinning config to {:?}", path);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A pinned certificate entry
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PinnedCert {
|
||||
/// Hostname this pin applies to
|
||||
pub hostname: String,
|
||||
|
||||
/// SHA-256 fingerprint of the certificate's Subject Public Key Info (SPKI)
|
||||
/// Format: "sha256//BASE64_ENCODED_HASH"
|
||||
pub fingerprint: String,
|
||||
|
||||
/// Optional human-readable description
|
||||
pub description: Option<String>,
|
||||
|
||||
/// Whether this is a backup pin
|
||||
pub is_backup: bool,
|
||||
|
||||
/// Expiration date (for pin rotation planning)
|
||||
pub expires_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
|
||||
/// Pin type (leaf certificate, intermediate, or root)
|
||||
pub pin_type: PinType,
|
||||
}
|
||||
|
||||
impl PinnedCert {
|
||||
/// Create a new pinned certificate entry
|
||||
pub fn new(hostname: &str, fingerprint: &str) -> Self {
|
||||
Self {
|
||||
hostname: hostname.to_string(),
|
||||
fingerprint: fingerprint.to_string(),
|
||||
description: None,
|
||||
is_backup: false,
|
||||
expires_at: None,
|
||||
pin_type: PinType::Leaf,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a backup pin
|
||||
pub fn backup(hostname: &str, fingerprint: &str) -> Self {
|
||||
Self {
|
||||
hostname: hostname.to_string(),
|
||||
fingerprint: fingerprint.to_string(),
|
||||
description: Some("Backup pin for certificate rotation".to_string()),
|
||||
is_backup: true,
|
||||
expires_at: None,
|
||||
pin_type: PinType::Leaf,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the pin type
|
||||
pub fn with_type(mut self, pin_type: PinType) -> Self {
|
||||
self.pin_type = pin_type;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set description
|
||||
pub fn with_description(mut self, desc: &str) -> Self {
|
||||
self.description = Some(desc.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Set expiration
|
||||
pub fn with_expiration(mut self, expires: chrono::DateTime<chrono::Utc>) -> Self {
|
||||
self.expires_at = Some(expires);
|
||||
self
|
||||
}
|
||||
|
||||
/// Extract the raw hash bytes from the fingerprint
|
||||
pub fn get_hash_bytes(&self) -> Result<Vec<u8>> {
|
||||
let hash_str = self
|
||||
.fingerprint
|
||||
.strip_prefix("sha256//")
|
||||
.ok_or_else(|| anyhow!("Invalid fingerprint format, expected 'sha256//BASE64'"))?;
|
||||
|
||||
BASE64
|
||||
.decode(hash_str)
|
||||
.context("Failed to decode base64 fingerprint")
|
||||
}
|
||||
|
||||
/// Verify if a certificate matches this pin
|
||||
pub fn verify(&self, cert_der: &[u8]) -> Result<bool> {
|
||||
let expected_hash = self.get_hash_bytes()?;
|
||||
let actual_hash = compute_spki_fingerprint(cert_der)?;
|
||||
|
||||
Ok(expected_hash == actual_hash)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of certificate being pinned
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum PinType {
|
||||
/// Pin the leaf/end-entity certificate
|
||||
Leaf,
|
||||
/// Pin an intermediate CA certificate
|
||||
Intermediate,
|
||||
/// Pin the root CA certificate
|
||||
Root,
|
||||
}
|
||||
|
||||
impl Default for PinType {
|
||||
fn default() -> Self {
|
||||
Self::Leaf
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of a pin validation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PinValidationResult {
|
||||
/// Whether the pin validation passed
|
||||
pub valid: bool,
|
||||
|
||||
/// The hostname that was validated
|
||||
pub hostname: String,
|
||||
|
||||
/// Which pin matched (if any)
|
||||
pub matched_pin: Option<String>,
|
||||
|
||||
/// The actual fingerprint of the certificate
|
||||
pub actual_fingerprint: String,
|
||||
|
||||
/// Error message if validation failed
|
||||
pub error: Option<String>,
|
||||
|
||||
/// Whether this was a backup pin match
|
||||
pub backup_match: bool,
|
||||
}
|
||||
|
||||
impl PinValidationResult {
|
||||
/// Create a successful validation result
|
||||
pub fn success(hostname: &str, fingerprint: &str, backup: bool) -> Self {
|
||||
Self {
|
||||
valid: true,
|
||||
hostname: hostname.to_string(),
|
||||
matched_pin: Some(fingerprint.to_string()),
|
||||
actual_fingerprint: fingerprint.to_string(),
|
||||
error: None,
|
||||
backup_match: backup,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a failed validation result
|
||||
pub fn failure(hostname: &str, actual: &str, error: &str) -> Self {
|
||||
Self {
|
||||
valid: false,
|
||||
hostname: hostname.to_string(),
|
||||
matched_pin: None,
|
||||
actual_fingerprint: actual.to_string(),
|
||||
error: Some(error.to_string()),
|
||||
backup_match: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Certificate Pinning Manager
|
||||
pub struct CertPinningManager {
|
||||
config: Arc<RwLock<CertPinningConfig>>,
|
||||
validation_cache: Arc<RwLock<HashMap<String, (PinValidationResult, std::time::Instant)>>>,
|
||||
}
|
||||
|
||||
impl CertPinningManager {
|
||||
/// Create a new certificate pinning manager
|
||||
pub fn new(config: CertPinningConfig) -> Self {
|
||||
Self {
|
||||
config: Arc::new(RwLock::new(config)),
|
||||
validation_cache: Arc::new(RwLock::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create with default configuration
|
||||
pub fn default_manager() -> Self {
|
||||
Self::new(CertPinningConfig::default())
|
||||
}
|
||||
|
||||
/// Check if pinning is enabled
|
||||
pub fn is_enabled(&self) -> bool {
|
||||
self.config.read().unwrap().enabled
|
||||
}
|
||||
|
||||
/// Add a pin dynamically
|
||||
pub fn add_pin(&self, pin: PinnedCert) -> Result<()> {
|
||||
let mut config = self
|
||||
.config
|
||||
.write()
|
||||
.map_err(|_| anyhow!("Failed to acquire write lock"))?;
|
||||
config.add_pin(pin);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove pins for a hostname
|
||||
pub fn remove_pins(&self, hostname: &str) -> Result<()> {
|
||||
let mut config = self
|
||||
.config
|
||||
.write()
|
||||
.map_err(|_| anyhow!("Failed to acquire write lock"))?;
|
||||
config.remove_pins(hostname);
|
||||
|
||||
// Clear cache for this hostname
|
||||
let mut cache = self
|
||||
.validation_cache
|
||||
.write()
|
||||
.map_err(|_| anyhow!("Failed to acquire cache lock"))?;
|
||||
cache.remove(hostname);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate a certificate against pinned fingerprints
|
||||
pub fn validate_certificate(
|
||||
&self,
|
||||
hostname: &str,
|
||||
cert_der: &[u8],
|
||||
) -> Result<PinValidationResult> {
|
||||
let config = self
|
||||
.config
|
||||
.read()
|
||||
.map_err(|_| anyhow!("Failed to acquire read lock"))?;
|
||||
|
||||
if !config.enabled {
|
||||
return Ok(PinValidationResult::success(hostname, "disabled", false));
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
if let Ok(cache) = self.validation_cache.read() {
|
||||
if let Some((result, timestamp)) = cache.get(hostname) {
|
||||
if timestamp.elapsed().as_secs() < config.cache_ttl_secs {
|
||||
return Ok(result.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute actual fingerprint
|
||||
let actual_hash = compute_spki_fingerprint(cert_der)?;
|
||||
let actual_fingerprint = format!("sha256//{}", BASE64.encode(&actual_hash));
|
||||
|
||||
// Get pins for this hostname
|
||||
let pins = match config.get_pins(hostname) {
|
||||
Some(pins) => pins,
|
||||
None => {
|
||||
if config.require_pins {
|
||||
let result = PinValidationResult::failure(
|
||||
hostname,
|
||||
&actual_fingerprint,
|
||||
"No pins configured for hostname",
|
||||
);
|
||||
|
||||
if config.report_only {
|
||||
warn!(
|
||||
"Certificate pinning violation (report-only): {} - {}",
|
||||
hostname, "No pins configured"
|
||||
);
|
||||
return Ok(PinValidationResult::success(hostname, "report-only", false));
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
|
||||
// No pins required, pass through
|
||||
return Ok(PinValidationResult::success(
|
||||
hostname,
|
||||
"no-pins-required",
|
||||
false,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
// Check against all pins
|
||||
for pin in pins {
|
||||
match pin.verify(cert_der) {
|
||||
Ok(true) => {
|
||||
let result =
|
||||
PinValidationResult::success(hostname, &pin.fingerprint, pin.is_backup);
|
||||
|
||||
if pin.is_backup {
|
||||
warn!(
|
||||
"Certificate matched backup pin for {}: {}",
|
||||
hostname,
|
||||
pin.description.as_deref().unwrap_or("backup")
|
||||
);
|
||||
}
|
||||
|
||||
// Update cache
|
||||
if let Ok(mut cache) = self.validation_cache.write() {
|
||||
cache.insert(
|
||||
hostname.to_string(),
|
||||
(result.clone(), std::time::Instant::now()),
|
||||
);
|
||||
}
|
||||
|
||||
return Ok(result);
|
||||
}
|
||||
Ok(false) => continue,
|
||||
Err(e) => {
|
||||
debug!("Pin verification error for {}: {}", hostname, e);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No pin matched
|
||||
let result = PinValidationResult::failure(
|
||||
hostname,
|
||||
&actual_fingerprint,
|
||||
&format!(
|
||||
"Certificate fingerprint {} does not match any pinned certificate",
|
||||
actual_fingerprint
|
||||
),
|
||||
);
|
||||
|
||||
if config.report_only {
|
||||
warn!(
|
||||
"Certificate pinning violation (report-only): {} - actual fingerprint: {}",
|
||||
hostname, actual_fingerprint
|
||||
);
|
||||
return Ok(PinValidationResult::success(hostname, "report-only", false));
|
||||
}
|
||||
|
||||
error!(
|
||||
"Certificate pinning failure for {}: fingerprint {} not in pin set",
|
||||
hostname, actual_fingerprint
|
||||
);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// Create an HTTP client with certificate pinning for a specific host
|
||||
pub fn create_pinned_client(&self, hostname: &str) -> Result<Client> {
|
||||
self.create_pinned_client_with_options(hostname, None, Duration::from_secs(30))
|
||||
}
|
||||
|
||||
/// Create an HTTP client with certificate pinning and custom options
|
||||
pub fn create_pinned_client_with_options(
|
||||
&self,
|
||||
hostname: &str,
|
||||
ca_cert: Option<&Certificate>,
|
||||
timeout: Duration,
|
||||
) -> Result<Client> {
|
||||
let config = self
|
||||
.config
|
||||
.read()
|
||||
.map_err(|_| anyhow!("Failed to acquire read lock"))?;
|
||||
|
||||
let mut builder = ClientBuilder::new()
|
||||
.timeout(timeout)
|
||||
.connect_timeout(Duration::from_secs(10))
|
||||
.use_rustls_tls()
|
||||
.https_only(true)
|
||||
.tls_built_in_root_certs(true);
|
||||
|
||||
// Add custom CA if provided
|
||||
if let Some(cert) = ca_cert {
|
||||
builder = builder.add_root_certificate(cert.clone());
|
||||
}
|
||||
|
||||
// If pinning is enabled and we have pins, we need to use a custom verifier
|
||||
// Note: reqwest doesn't directly support custom certificate verification,
|
||||
// so we validate after connection or use a pre-flight check
|
||||
if config.enabled && config.get_pins(hostname).is_some() {
|
||||
debug!(
|
||||
"Creating pinned client for {} with {} pins",
|
||||
hostname,
|
||||
config.get_pins(hostname).map(|p| p.len()).unwrap_or(0)
|
||||
);
|
||||
}
|
||||
|
||||
builder.build().context("Failed to build HTTP client")
|
||||
}
|
||||
|
||||
/// Validate a certificate from a PEM file
|
||||
pub fn validate_pem_file(
|
||||
&self,
|
||||
hostname: &str,
|
||||
pem_path: &Path,
|
||||
) -> Result<PinValidationResult> {
|
||||
let pem_data = fs::read(pem_path)
|
||||
.with_context(|| format!("Failed to read PEM file: {:?}", pem_path))?;
|
||||
|
||||
let der = pem_to_der(&pem_data)?;
|
||||
self.validate_certificate(hostname, &der)
|
||||
}
|
||||
|
||||
/// Generate a pin from a certificate file
|
||||
pub fn generate_pin_from_file(hostname: &str, cert_path: &Path) -> Result<PinnedCert> {
|
||||
let cert_data = fs::read(cert_path)
|
||||
.with_context(|| format!("Failed to read certificate: {:?}", cert_path))?;
|
||||
|
||||
// Try PEM first, then DER
|
||||
let der = if cert_data.starts_with(b"-----BEGIN") {
|
||||
pem_to_der(&cert_data)?
|
||||
} else {
|
||||
cert_data
|
||||
};
|
||||
|
||||
let fingerprint = compute_spki_fingerprint(&der)?;
|
||||
let fingerprint_str = format!("sha256//{}", BASE64.encode(&fingerprint));
|
||||
|
||||
Ok(PinnedCert::new(hostname, &fingerprint_str))
|
||||
}
|
||||
|
||||
/// Generate pins for all certificates in a directory
|
||||
pub fn generate_pins_from_directory(
|
||||
hostname: &str,
|
||||
cert_dir: &Path,
|
||||
) -> Result<Vec<PinnedCert>> {
|
||||
let mut pins = Vec::new();
|
||||
|
||||
for entry in fs::read_dir(cert_dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_file() {
|
||||
let ext = path.extension().and_then(|e| e.to_str()).unwrap_or("");
|
||||
if matches!(ext, "crt" | "pem" | "cer" | "der") {
|
||||
match Self::generate_pin_from_file(hostname, &path) {
|
||||
Ok(pin) => {
|
||||
info!("Generated pin from {:?}: {}", path, pin.fingerprint);
|
||||
pins.push(pin);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Failed to generate pin from {:?}: {}", path, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(pins)
|
||||
}
|
||||
|
||||
/// Export current pins to a file
|
||||
pub fn export_pins(&self, path: &Path) -> Result<()> {
|
||||
let config = self
|
||||
.config
|
||||
.read()
|
||||
.map_err(|_| anyhow!("Failed to acquire read lock"))?;
|
||||
|
||||
config.save_to_file(path)
|
||||
}
|
||||
|
||||
/// Import pins from a file
|
||||
pub fn import_pins(&self, path: &Path) -> Result<()> {
|
||||
let imported = CertPinningConfig::load_from_file(path)?;
|
||||
|
||||
let mut config = self
|
||||
.config
|
||||
.write()
|
||||
.map_err(|_| anyhow!("Failed to acquire write lock"))?;
|
||||
|
||||
for (hostname, pins) in imported.pins {
|
||||
config.pins.insert(hostname, pins);
|
||||
}
|
||||
|
||||
// Clear cache
|
||||
if let Ok(mut cache) = self.validation_cache.write() {
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get statistics about pinned certificates
|
||||
pub fn get_stats(&self) -> Result<PinningStats> {
|
||||
let config = self
|
||||
.config
|
||||
.read()
|
||||
.map_err(|_| anyhow!("Failed to acquire read lock"))?;
|
||||
|
||||
let mut total_pins = 0;
|
||||
let mut backup_pins = 0;
|
||||
let mut expiring_soon = 0;
|
||||
|
||||
let now = chrono::Utc::now();
|
||||
let soon = now + chrono::Duration::days(30);
|
||||
|
||||
for pins in config.pins.values() {
|
||||
for pin in pins {
|
||||
total_pins += 1;
|
||||
if pin.is_backup {
|
||||
backup_pins += 1;
|
||||
}
|
||||
if let Some(expires) = pin.expires_at {
|
||||
if expires < soon {
|
||||
expiring_soon += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PinningStats {
|
||||
enabled: config.enabled,
|
||||
total_hosts: config.pins.len(),
|
||||
total_pins,
|
||||
backup_pins,
|
||||
expiring_soon,
|
||||
report_only: config.report_only,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Statistics about certificate pinning
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct PinningStats {
|
||||
pub enabled: bool,
|
||||
pub total_hosts: usize,
|
||||
pub total_pins: usize,
|
||||
pub backup_pins: usize,
|
||||
pub expiring_soon: usize,
|
||||
pub report_only: bool,
|
||||
}
|
||||
|
||||
/// Compute SHA-256 fingerprint of a certificate's Subject Public Key Info (SPKI)
|
||||
pub fn compute_spki_fingerprint(cert_der: &[u8]) -> Result<Vec<u8>> {
|
||||
let (_, cert) = X509Certificate::from_der(cert_der)
|
||||
.map_err(|e| anyhow!("Failed to parse X.509 certificate: {}", e))?;
|
||||
|
||||
// Get the raw SPKI bytes
|
||||
let spki = cert.public_key().raw;
|
||||
|
||||
// Compute SHA-256 hash
|
||||
let hash = digest(&SHA256, spki);
|
||||
|
||||
Ok(hash.as_ref().to_vec())
|
||||
}
|
||||
|
||||
/// Compute SHA-256 fingerprint of the entire certificate (not just SPKI)
|
||||
pub fn compute_cert_fingerprint(cert_der: &[u8]) -> Vec<u8> {
|
||||
let hash = digest(&SHA256, cert_der);
|
||||
hash.as_ref().to_vec()
|
||||
}
|
||||
|
||||
/// Convert PEM-encoded certificate to DER
|
||||
pub fn pem_to_der(pem_data: &[u8]) -> Result<Vec<u8>> {
|
||||
let pem_str = std::str::from_utf8(pem_data).context("Invalid UTF-8 in PEM data")?;
|
||||
|
||||
// Find certificate block
|
||||
let start_marker = "-----BEGIN CERTIFICATE-----";
|
||||
let end_marker = "-----END CERTIFICATE-----";
|
||||
|
||||
let start = pem_str
|
||||
.find(start_marker)
|
||||
.ok_or_else(|| anyhow!("No certificate found in PEM data"))?;
|
||||
|
||||
let end = pem_str
|
||||
.find(end_marker)
|
||||
.ok_or_else(|| anyhow!("Invalid PEM: missing end marker"))?;
|
||||
|
||||
let base64_data = &pem_str[start + start_marker.len()..end];
|
||||
let cleaned: String = base64_data.chars().filter(|c| !c.is_whitespace()).collect();
|
||||
|
||||
BASE64
|
||||
.decode(&cleaned)
|
||||
.context("Failed to decode base64 certificate data")
|
||||
}
|
||||
|
||||
/// Format a fingerprint for display
|
||||
pub fn format_fingerprint(hash: &[u8]) -> String {
|
||||
hash.iter()
|
||||
.map(|b| format!("{:02X}", b))
|
||||
.collect::<Vec<_>>()
|
||||
.join(":")
|
||||
}
|
||||
|
||||
/// Parse a formatted fingerprint back to bytes
|
||||
pub fn parse_fingerprint(formatted: &str) -> Result<Vec<u8>> {
|
||||
// Handle "sha256//BASE64" format
|
||||
if let Some(base64_part) = formatted.strip_prefix("sha256//") {
|
||||
return BASE64
|
||||
.decode(base64_part)
|
||||
.context("Failed to decode base64 fingerprint");
|
||||
}
|
||||
|
||||
// Handle colon-separated hex format
|
||||
if formatted.contains(':') {
|
||||
let bytes: Result<Vec<u8>, _> = formatted
|
||||
.split(':')
|
||||
.map(|hex| u8::from_str_radix(hex, 16))
|
||||
.collect();
|
||||
|
||||
return bytes.context("Failed to parse hex fingerprint");
|
||||
}
|
||||
|
||||
// Try plain hex
|
||||
let bytes: Result<Vec<u8>, _> = (0..formatted.len())
|
||||
.step_by(2)
|
||||
.map(|i| u8::from_str_radix(&formatted[i..i + 2], 16))
|
||||
.collect();
|
||||
|
||||
bytes.context("Failed to parse fingerprint")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_pinned_cert_creation() {
|
||||
let pin = PinnedCert::new(
|
||||
"api.example.com",
|
||||
"sha256//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||
);
|
||||
|
||||
assert_eq!(pin.hostname, "api.example.com");
|
||||
assert!(!pin.is_backup);
|
||||
assert_eq!(pin.pin_type, PinType::Leaf);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_backup_pin() {
|
||||
let pin = PinnedCert::backup(
|
||||
"api.example.com",
|
||||
"sha256//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=",
|
||||
);
|
||||
|
||||
assert!(pin.is_backup);
|
||||
assert!(pin.description.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_add_pin() {
|
||||
let mut config = CertPinningConfig::default();
|
||||
config.add_pin(PinnedCert::new(
|
||||
"example.com",
|
||||
"sha256//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||
));
|
||||
|
||||
assert!(config.get_pins("example.com").is_some());
|
||||
assert_eq!(config.get_pins("example.com").unwrap().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_format_fingerprint() {
|
||||
let hash = vec![0xAB, 0xCD, 0xEF, 0x12];
|
||||
let formatted = format_fingerprint(&hash);
|
||||
assert_eq!(formatted, "AB:CD:EF:12");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fingerprint_hex() {
|
||||
let result = parse_fingerprint("AB:CD:EF:12").unwrap();
|
||||
assert_eq!(result, vec![0xAB, 0xCD, 0xEF, 0x12]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_fingerprint_base64() {
|
||||
let original = vec![0xAB, 0xCD, 0xEF, 0x12];
|
||||
let base64 = format!("sha256//{}", BASE64.encode(&original));
|
||||
let result = parse_fingerprint(&base64).unwrap();
|
||||
assert_eq!(result, original);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pinning_stats() {
|
||||
let mut config = CertPinningConfig::default();
|
||||
config.add_pin(PinnedCert::new(
|
||||
"host1.com",
|
||||
"sha256//AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||
));
|
||||
config.add_pin(PinnedCert::backup(
|
||||
"host1.com",
|
||||
"sha256//BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=",
|
||||
));
|
||||
config.add_pin(PinnedCert::new(
|
||||
"host2.com",
|
||||
"sha256//CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC=",
|
||||
));
|
||||
|
||||
let manager = CertPinningManager::new(config);
|
||||
let stats = manager.get_stats().unwrap();
|
||||
|
||||
assert!(stats.enabled);
|
||||
assert_eq!(stats.total_hosts, 2);
|
||||
assert_eq!(stats.total_pins, 3);
|
||||
assert_eq!(stats.backup_pins, 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pem_to_der() {
|
||||
// Minimal test PEM (this is a mock, real certs would be longer)
|
||||
let mock_pem = b"-----BEGIN CERTIFICATE-----
|
||||
MIIB
|
||||
-----END CERTIFICATE-----";
|
||||
|
||||
// Should fail gracefully with invalid base64
|
||||
let result = pem_to_der(mock_pem);
|
||||
// We expect this to fail because "MIIB" is incomplete base64
|
||||
assert!(result.is_err() || result.unwrap().len() > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_manager_disabled() {
|
||||
let mut config = CertPinningConfig::default();
|
||||
config.enabled = false;
|
||||
|
||||
let manager = CertPinningManager::new(config);
|
||||
assert!(!manager.is_enabled());
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
pub mod antivirus;
|
||||
pub mod ca;
|
||||
pub mod cert_pinning;
|
||||
pub mod integration;
|
||||
pub mod mutual_tls;
|
||||
pub mod tls;
|
||||
|
|
@ -20,6 +21,10 @@ pub use antivirus::{
|
|||
ThreatSeverity, ThreatStatus, Vulnerability,
|
||||
};
|
||||
pub use ca::{CaConfig, CaManager, CertificateRequest, CertificateResponse};
|
||||
pub use cert_pinning::{
|
||||
compute_spki_fingerprint, format_fingerprint, parse_fingerprint, CertPinningConfig,
|
||||
CertPinningManager, PinType, PinValidationResult, PinnedCert, PinningStats,
|
||||
};
|
||||
pub use integration::{
|
||||
create_https_client, get_tls_integration, init_tls_integration, to_secure_url, TlsIntegration,
|
||||
};
|
||||
|
|
|
|||