- Screen manuals.

This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2025-11-30 22:33:54 -03:00
parent e68a12176d
commit c414a99d58
45 changed files with 17335 additions and 3661 deletions

908
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -79,6 +79,7 @@ monitoring = ["dep:sysinfo"]
automation = ["dep:rhai"] automation = ["dep:rhai"]
grpc = ["dep:tonic"] grpc = ["dep:tonic"]
progress-bars = ["dep:indicatif"] progress-bars = ["dep:indicatif"]
dynamic-db = ["dep:sqlx"]
# ===== META FEATURES (BUNDLES) ===== # ===== META FEATURES (BUNDLES) =====
full = [ full = [
@ -202,6 +203,20 @@ csv = { version = "1.3", optional = true }
crossterm = { version = "0.29.0", optional = true } crossterm = { version = "0.29.0", optional = true }
ratatui = { version = "0.29.0", optional = true } ratatui = { version = "0.29.0", optional = true }
# QR Code Generation
image = "0.25"
qrcode = "0.14"
# Excel/Spreadsheet Support
calamine = "0.26"
rust_xlsxwriter = "0.79"
# Database (for table_definition dynamic connections)
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "mysql"], optional = true }
# Error handling
thiserror = "2.0"
# Caching/Sessions (redis-cache feature) # Caching/Sessions (redis-cache feature)
redis = { version = "0.27", features = ["tokio-comp"], optional = true } redis = { version = "0.27", features = ["tokio-comp"], optional = true }

View file

@ -58,6 +58,12 @@
- [Designer - Visual Builder](./chapter-04-gbui/apps/designer.md) - [Designer - Visual Builder](./chapter-04-gbui/apps/designer.md)
- [Sources - Prompts & Templates](./chapter-04-gbui/apps/sources.md) - [Sources - Prompts & Templates](./chapter-04-gbui/apps/sources.md)
- [Compliance - Security Scanner](./chapter-04-gbui/apps/compliance.md) - [Compliance - Security Scanner](./chapter-04-gbui/apps/compliance.md)
- [How-To Tutorials](./chapter-04-gbui/how-to/README.md)
- [Create Your First Bot](./chapter-04-gbui/how-to/create-first-bot.md)
- [Write Your First Dialog](./chapter-04-gbui/how-to/write-first-dialog.md)
- [Add Documents to Knowledge Base](./chapter-04-gbui/how-to/add-kb-documents.md)
- [Connect WhatsApp](./chapter-04-gbui/how-to/connect-whatsapp.md)
- [Monitor Your Bot](./chapter-04-gbui/how-to/monitor-sessions.md)
# Part V - Themes and Styling # Part V - Themes and Styling

View file

@ -0,0 +1,313 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-pink { stroke: #FF6B9D; stroke-width: 2.6; }
.neon-yellow { stroke: #F8E71C; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-pink {
stroke: #FF6B9D;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF6B9D) drop-shadow(0 0 8px #FF8FAB);
}
.neon-yellow {
stroke: #FFE066;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FFE066) drop-shadow(0 0 8px #FFD93D);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#F5A623;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#7ED321;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Analytics - Dashboard Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">Real-time metrics, charts, and insights for bot performance and usage</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Data Sources</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Metrics</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Visualizations</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Actions</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- Data Collection -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Data Collection</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Aggregate sources</text>
</g>
<!-- Conversations -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Chats</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Messages</text>
</g>
<!-- Users -->
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Users</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Sessions</text>
</g>
<!-- API Calls -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">API</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Requests</text>
</g>
<!-- LLM Usage -->
<g transform="translate(185, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">LLM</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Tokens</text>
</g>
<!-- System -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">System</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Resources</text>
</g>
<!-- Errors -->
<g transform="translate(185, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-yellow"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Errors</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Logs</text>
</g>
<!-- Metrics Panel -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Key Metrics</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">KPI summary cards</text>
</g>
<!-- Active Users -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Active Users</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Daily/Weekly/Monthly</text>
</g>
<!-- Response Time -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Response Time</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Avg latency</text>
</g>
<!-- Satisfaction -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Satisfaction</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">User feedback</text>
</g>
<!-- Visualizations -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Charts</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Visual insights</text>
</g>
<!-- Line Charts -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Line</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Trends</text>
</g>
<!-- Bar Charts -->
<g transform="translate(785, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Bar</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Compare</text>
</g>
<!-- Pie Charts -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Pie</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Share</text>
</g>
<!-- Heatmaps -->
<g transform="translate(785, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Heat</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Density</text>
</g>
<!-- Tables -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Data Tables</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Sortable, filterable</text>
</g>
<!-- Actions -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Actions</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Export and alerts</text>
</g>
<!-- Export -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Export</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">CSV, PDF, JSON</text>
</g>
<!-- Alerts -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Alerts</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Threshold triggers</text>
</g>
<!-- AI Insights -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">AI Insights ✨</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Anomaly detection</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Data source arrows -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 470 Q340 470 360 270 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- Metrics to Charts -->
<line x1="580" y1="290" x2="675" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="580" y1="380" x2="675" y2="380" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-orange" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Collect</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Aggregate data</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Analyze</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Compute metrics</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Visualize</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Display charts</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Act</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Export and alert</text>
</g>
<!-- Metrics Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Key Metrics:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Conversations and messages</text>
<rect x="270" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="295" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Active users (DAU/MAU)</text>
<rect x="500" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="525" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Response latency</text>
<rect x="700" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="725" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">LLM token usage</text>
<rect x="900" y="20" width="16" height="16" rx="3" fill="none" class="neon-pink" stroke-width="2"/>
<text x="925" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Error rates</text>
</g>
<!-- Time ranges -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Time Ranges:</text>
<text x="120" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Last 24 hours | Last 7 days | Last 30 days | Last 90 days | Custom range | Real-time</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">GET /api/analytics/summary | GET /api/analytics/metrics | GET /api/analytics/charts | POST /api/analytics/export | GET /api/analytics/realtime</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,291 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-pink { stroke: #FF6B9D; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-pink {
stroke: #FF6B9D;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF6B9D) drop-shadow(0 0 8px #FF8FAB);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#F5A623;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#7ED321;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Calendar - Scheduling Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">Event management with multiple views and AI scheduling assistance</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Views</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Events</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Details</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Actions</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- View Selector -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">View Selector</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Choose display mode</text>
</g>
<!-- Month View -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Month</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Overview</text>
</g>
<!-- Week View -->
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Week</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">7 days</text>
</g>
<!-- Day View -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Day</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Hourly</text>
</g>
<!-- Agenda View -->
<g transform="translate(185, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Agenda</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">List view</text>
</g>
<!-- Mini Calendar -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Mini Calendar</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Quick navigation</text>
</g>
<!-- Event Display -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Event Grid</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Visual timeline</text>
</g>
<!-- Event Cards -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Event Cards</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Color-coded</text>
</g>
<!-- Time Slots -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Time Slots</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Drag to create</text>
</g>
<!-- All-Day Events -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">All-Day Events</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Top banner</text>
</g>
<!-- Event Details -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Event Details</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Modal/sidebar</text>
</g>
<!-- Title & Time -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Title &amp; Time</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Date picker</text>
</g>
<!-- Location -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Location</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Virtual/IRL</text>
</g>
<!-- Attendees -->
<g transform="translate(785, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Attendees</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Invite</text>
</g>
<!-- Reminders -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Reminders</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Notifications</text>
</g>
<!-- Actions -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Actions</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Event management</text>
</g>
<!-- Create Event -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Create Event</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">+ New</text>
</g>
<!-- Edit/Delete -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Edit / Delete</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Modify events</text>
</g>
<!-- AI Schedule -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">AI Schedule ✨</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Smart booking</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- View selection arrows -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- Event to Details -->
<line x1="580" y1="290" x2="675" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-orange" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Choose View</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Month/Week/Day</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Browse Events</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Visual timeline</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">View Details</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Event info</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Take Action</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Create/Edit</text>
</g>
<!-- Features Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Features:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Drag-and-drop event creation</text>
<rect x="280" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="305" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Recurring events</text>
<rect x="500" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="525" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Multi-calendar support</text>
<rect x="750" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="775" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">AI scheduling assistant ✨</text>
<rect x="1020" y="20" width="16" height="16" rx="3" fill="none" class="neon-pink" stroke-width="2"/>
<text x="1045" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Timezone handling</text>
</g>
<!-- Keyboard shortcuts -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Shortcuts:</text>
<text x="100" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">C = Create | M = Month | W = Week | D = Day | A = Agenda | T = Today | ← → = Navigate | Enter = Open</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">GET /api/calendar/events | POST /api/calendar/events | PUT /api/calendar/events/{id} | DELETE /api/calendar/events/{id} | POST /api/calendar/ai/suggest</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,289 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-red { stroke: #E74C3C; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-red {
stroke: #FF4757;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF4757) drop-shadow(0 0 8px #FF6B81);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#E74C3C;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#7ED321;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Compliance - Security Scanner Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">Automated security scanning for configurations, code, and credentials</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Scan Targets</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Analysis</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Detection</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Reporting</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- Scan Request -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Scan Request</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">API or Scheduled</text>
</g>
<!-- Config Files -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Config Files</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">.env, config.csv</text>
</g>
<!-- BASIC Scripts -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">BASIC Scripts</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">.bas dialog files</text>
</g>
<!-- Knowledge Base -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Knowledge Base</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Prompts, templates</text>
</g>
<!-- Scanner Engine -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Scanner Engine</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Pattern Matching</text>
</g>
<!-- Credential Check -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Credential Check</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Passwords, API keys</text>
</g>
<!-- Code Analysis -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Code Analysis</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Fragile code patterns</text>
</g>
<!-- Security Rules -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Security Rules</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Best practices</text>
</g>
<!-- Issues Found -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-red"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Issues Found</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Categorized Results</text>
</g>
<!-- Critical -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-red"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Critical</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Secrets</text>
</g>
<!-- Warning -->
<g transform="translate(785, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Warning</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Fragile</text>
</g>
<!-- Info -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Info</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Suggestions</text>
</g>
<!-- Pass -->
<g transform="translate(785, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Pass</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">OK checks</text>
</g>
<!-- Report -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Report</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">JSON + Dashboard</text>
</g>
<!-- Dashboard View -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Dashboard View</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Visual summary</text>
</g>
<!-- Remediation -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Remediation</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Fix suggestions</text>
</g>
<!-- Export -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Export</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">CSV, PDF, JSON</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Arrow from Config to Scanner -->
<path d="M280 290 Q320 290 320 210 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.5"/>
<!-- Arrow from BASIC to Scanner -->
<path d="M280 380 Q340 380 340 220 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.5"/>
<!-- Arrow from KB to Scanner -->
<path d="M280 470 Q360 470 360 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.5"/>
<!-- Arrow to Dashboard -->
<line x1="1080" y1="230" x2="1080" y2="255" class="arrow-color" stroke-width="2" marker-end="url(#arrow)" opacity="0.5"/>
<!-- Arrow to Remediation -->
<line x1="1080" y1="320" x2="1080" y2="345" class="arrow-color" stroke-width="2" marker-end="url(#arrow)" opacity="0.5"/>
<!-- Arrow to Export -->
<line x1="1080" y1="410" x2="1080" y2="435" class="arrow-color" stroke-width="2" marker-end="url(#arrow)" opacity="0.5"/>
<!-- Connections from analysis to issues -->
<line x1="580" y1="290" x2="675" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="580" y1="380" x2="675" y2="380" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-red" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Target Selection</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Choose what to scan</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Analyze</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Run security checks</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Detect</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Categorize findings</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Report</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">View and export</text>
</g>
<!-- Scan Categories Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Scan Categories:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-red" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Exposed credentials (passwords, API keys)</text>
<rect x="350" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="375" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Fragile code patterns in .bas files</text>
<rect x="650" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="675" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Missing vault usage</text>
<rect x="900" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="925" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Security best practices</text>
</g>
<!-- Severity Levels -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Severity Levels:</text>
<text x="130" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">🔴 Critical (immediate action) | 🟠 Warning (should fix) | 🔵 Info (recommendations) | 🟢 Pass (compliant)</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">GET /api/compliance | POST /api/compliance/scan | GET /api/compliance/report/{id} | GET /api/compliance/export/{format}</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,289 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-pink { stroke: #FF6B9D; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-pink {
stroke: #FF6B9D;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF6B9D) drop-shadow(0 0 8px #FF8FAB);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#F5A623;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#7ED321;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Designer - Visual Dialog Builder Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">VB6-style drag-and-drop interface for creating BASIC dialog scripts</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Toolbox</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Canvas</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Properties</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Export</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- Toolbox -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Toolbox Panel</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Drag blocks to canvas</text>
</g>
<!-- I/O Blocks -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">TALK</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Output</text>
</g>
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">HEAR</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Input</text>
</g>
<!-- Logic Blocks -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">IF</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Condition</text>
</g>
<g transform="translate(185, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">FOR</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Loop</text>
</g>
<!-- Action Blocks -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">SET</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Variable</text>
</g>
<g transform="translate(185, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">CALL</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Procedure</text>
</g>
<!-- Canvas Area -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Design Canvas</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Visual workspace</text>
</g>
<!-- Node placement -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Node Placement</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Snap to grid</text>
</g>
<!-- Connections -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Connections</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Flow arrows</text>
</g>
<!-- Selection -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Selection</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Multi-select, move</text>
</g>
<!-- Properties Panel -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Properties</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Edit block settings</text>
</g>
<!-- Node Name -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Block Fields</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Text, values</text>
</g>
<!-- Validation -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Validation</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Type checking</text>
</g>
<!-- Preview -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Live Preview</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">BASIC output</text>
</g>
<!-- Export -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Export .bas</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Generate code</text>
</g>
<!-- Save Design -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Save Design</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">JSON format</text>
</g>
<!-- Load Design -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Load Design</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Open existing</text>
</g>
<!-- Deploy -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Deploy</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">To bot package</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Drag arrows from toolbox items -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 470 Q340 470 360 270 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- Selection to Properties -->
<line x1="580" y1="470" x2="675" y2="360" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.5"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-orange" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Select Block</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Drag from toolbox</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Place & Connect</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Build flow on canvas</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Configure</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Set properties</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Export</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Generate .bas file</text>
</g>
<!-- Block Types Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Block Types:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">I/O (TALK, HEAR)</text>
<rect x="200" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="225" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Logic (IF, FOR, SWITCH)</text>
<rect x="450" y="20" width="16" height="16" rx="3" fill="none" class="neon-pink" stroke-width="2"/>
<text x="475" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Actions (SET, CALL, GET)</text>
<rect x="700" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="725" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Memory (BOT/USER MEMORY)</text>
<rect x="1000" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="1025" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Integration (SEND MAIL, POST)</text>
</g>
<!-- Keyboard shortcuts -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Shortcuts:</text>
<text x="100" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Ctrl+S = Save | Ctrl+Z = Undo | Ctrl+Y = Redo | Del = Delete | Ctrl+D = Duplicate | Ctrl+E = Export</text>
</g>
<!-- Canvas interactions -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Canvas:</text>
<text x="80" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Click = Select | Drag = Move | Click port = Connect | Right-click = Context menu | Scroll = Pan | Ctrl+Scroll = Zoom</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

View file

@ -0,0 +1,521 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 900" width="1200" height="900">
<defs>
<!-- Gradients -->
<linearGradient id="bgGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#0a0e27"/>
<stop offset="50%" style="stop-color:#1a1f3a"/>
<stop offset="100%" style="stop-color:#0d1229"/>
</linearGradient>
<linearGradient id="coreGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#6366f1"/>
<stop offset="100%" style="stop-color:#8b5cf6"/>
</linearGradient>
<linearGradient id="dbGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#3b82f6"/>
<stop offset="100%" style="stop-color:#1d4ed8"/>
</linearGradient>
<linearGradient id="vectorGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#8b5cf6"/>
<stop offset="100%" style="stop-color:#6d28d9"/>
</linearGradient>
<linearGradient id="storageGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#10b981"/>
<stop offset="100%" style="stop-color:#059669"/>
</linearGradient>
<linearGradient id="llmGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#f59e0b"/>
<stop offset="100%" style="stop-color:#d97706"/>
</linearGradient>
<linearGradient id="secretsGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#ef4444"/>
<stop offset="100%" style="stop-color:#dc2626"/>
</linearGradient>
<linearGradient id="cacheGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#06b6d4"/>
<stop offset="100%" style="stop-color:#0891b2"/>
</linearGradient>
<linearGradient id="metricsGradient" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#ec4899"/>
<stop offset="100%" style="stop-color:#db2777"/>
</linearGradient>
<!-- Glow filters -->
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
<feGaussianBlur stdDeviation="4" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="softGlow" x="-30%" y="-30%" width="160%" height="160%">
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<filter id="pulseGlow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur stdDeviation="8" result="coloredBlur"/>
<feMerge>
<feMergeNode in="coloredBlur"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Data packet symbol -->
<symbol id="dataPacket" viewBox="0 0 10 10">
<circle cx="5" cy="5" r="4" fill="currentColor" opacity="0.9"/>
</symbol>
<!-- Heartbeat line -->
<symbol id="heartbeat" viewBox="0 0 100 30">
<path d="M0,15 L20,15 L25,5 L30,25 L35,10 L40,20 L45,15 L100,15"
fill="none" stroke="currentColor" stroke-width="2"/>
</symbol>
</defs>
<!-- Background -->
<rect width="1200" height="900" fill="url(#bgGradient)"/>
<!-- Grid pattern -->
<g opacity="0.1">
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#4f46e5" stroke-width="0.5"/>
</pattern>
<rect width="1200" height="900" fill="url(#grid)"/>
</g>
<!-- Title -->
<text x="600" y="45" text-anchor="middle" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="28" font-weight="bold">
🔴 LIVE SYSTEM MONITOR
</text>
<text x="600" y="70" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="14">
General Bots Platform • Real-time Component Status
</text>
<!-- Animated pulse ring around title -->
<circle cx="530" cy="40" r="8" fill="#ef4444" opacity="0.8">
<animate attributeName="r" values="6;10;6" dur="1s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.8;0.4;0.8" dur="1s" repeatCount="indefinite"/>
</circle>
<!-- Connection lines (drawn first, behind components) -->
<g id="connections" stroke-width="2" fill="none">
<!-- PostgreSQL to Core -->
<path d="M250,300 Q350,300 450,400" stroke="#3b82f6" opacity="0.4">
<animate attributeName="stroke-dasharray" values="0,1000;200,800;400,600" dur="3s" repeatCount="indefinite"/>
</path>
<circle r="4" fill="#3b82f6">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#pathToCore1"/>
</animateMotion>
</circle>
<path id="pathToCore1" d="M250,300 Q350,300 450,400" fill="none" stroke="none"/>
<!-- Qdrant to Core -->
<path d="M250,500 Q350,500 450,450" stroke="#8b5cf6" opacity="0.4">
<animate attributeName="stroke-dasharray" values="0,1000;200,800;400,600" dur="2.5s" repeatCount="indefinite"/>
</path>
<circle r="4" fill="#8b5cf6">
<animateMotion dur="1.8s" repeatCount="indefinite">
<mpath href="#pathToCore2"/>
</animateMotion>
</circle>
<path id="pathToCore2" d="M250,500 Q350,500 450,450" fill="none" stroke="none"/>
<!-- MinIO to Core -->
<path d="M250,700 Q400,650 500,500" stroke="#10b981" opacity="0.4">
<animate attributeName="stroke-dasharray" values="0,1000;200,800;400,600" dur="2.8s" repeatCount="indefinite"/>
</path>
<circle r="4" fill="#10b981">
<animateMotion dur="2.2s" repeatCount="indefinite">
<mpath href="#pathToCore3"/>
</animateMotion>
</circle>
<path id="pathToCore3" d="M250,700 Q400,650 500,500" fill="none" stroke="none"/>
<!-- Core to BotModels -->
<path d="M700,400 Q800,350 900,300" stroke="#f59e0b" opacity="0.4">
<animate attributeName="stroke-dasharray" values="0,1000;200,800;400,600" dur="2.2s" repeatCount="indefinite"/>
</path>
<circle r="4" fill="#f59e0b">
<animateMotion dur="1.5s" repeatCount="indefinite">
<mpath href="#pathToLLM"/>
</animateMotion>
</circle>
<path id="pathToLLM" d="M700,400 Q800,350 900,300" fill="none" stroke="none"/>
<!-- Core to Vault -->
<path d="M700,450 Q800,500 900,500" stroke="#ef4444" opacity="0.4">
<animate attributeName="stroke-dasharray" values="0,1000;200,800;400,600" dur="3.2s" repeatCount="indefinite"/>
</path>
<circle r="4" fill="#ef4444">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#pathToVault"/>
</animateMotion>
</circle>
<path id="pathToVault" d="M700,450 Q800,500 900,500" fill="none" stroke="none"/>
<!-- Core to Redis -->
<path d="M650,520 Q750,600 900,650" stroke="#06b6d4" opacity="0.4">
<animate attributeName="stroke-dasharray" values="0,1000;200,800;400,600" dur="1.5s" repeatCount="indefinite"/>
</path>
<circle r="4" fill="#06b6d4">
<animateMotion dur="0.8s" repeatCount="indefinite">
<mpath href="#pathToRedis"/>
</animateMotion>
</circle>
<path id="pathToRedis" d="M650,520 Q750,600 900,650" fill="none" stroke="none"/>
<!-- Core to InfluxDB -->
<path d="M600,520 Q600,700 750,800" stroke="#ec4899" opacity="0.4">
<animate attributeName="stroke-dasharray" values="0,1000;200,800;400,600" dur="2s" repeatCount="indefinite"/>
</path>
<circle r="4" fill="#ec4899">
<animateMotion dur="1.2s" repeatCount="indefinite">
<mpath href="#pathToInflux"/>
</animateMotion>
</circle>
<path id="pathToInflux" d="M600,520 Q600,700 750,800" fill="none" stroke="none"/>
</g>
<!-- ==================== CENTRAL CORE: BotServer ==================== -->
<g id="botserver" transform="translate(600, 450)">
<!-- Outer pulse rings -->
<circle r="120" fill="none" stroke="#6366f1" stroke-width="1" opacity="0.3">
<animate attributeName="r" values="100;130;100" dur="3s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.3;0.1;0.3" dur="3s" repeatCount="indefinite"/>
</circle>
<circle r="100" fill="none" stroke="#6366f1" stroke-width="1" opacity="0.4">
<animate attributeName="r" values="90;110;90" dur="2s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.4;0.2;0.4" dur="2s" repeatCount="indefinite"/>
</circle>
<!-- Main core circle -->
<circle r="80" fill="url(#coreGradient)" filter="url(#glow)"/>
<!-- Inner rotating ring -->
<circle r="65" fill="none" stroke="#a5b4fc" stroke-width="2" stroke-dasharray="20 10" opacity="0.6">
<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="20s" repeatCount="indefinite"/>
</circle>
<!-- Bot icon -->
<text y="5" text-anchor="middle" fill="white" font-size="40">🤖</text>
<!-- Label -->
<text y="110" text-anchor="middle" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="16" font-weight="bold">BotServer</text>
<text y="128" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="12">● RUNNING</text>
<text y="145" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="11">v5.0.2 • Rust</text>
<!-- Heartbeat indicator -->
<g transform="translate(-40, -100)">
<use href="#heartbeat" width="80" height="24" color="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="1s" repeatCount="indefinite"/>
</use>
</g>
</g>
<!-- ==================== LEFT SIDE: Data Layer ==================== -->
<!-- PostgreSQL -->
<g id="postgresql" transform="translate(150, 300)">
<rect x="-80" y="-50" width="160" height="100" rx="12" fill="url(#dbGradient)" filter="url(#softGlow)"/>
<rect x="-70" y="-40" width="140" height="25" rx="4" fill="#1e40af" opacity="0.5"/>
<rect x="-70" y="-10" width="140" height="25" rx="4" fill="#1e40af" opacity="0.3"/>
<rect x="-70" y="20" width="140" height="25" rx="4" fill="#1e40af" opacity="0.2"/>
<!-- Database icon -->
<text x="-55" y="-22" fill="white" font-size="16">🐘</text>
<text x="-30" y="-22" fill="white" font-family="system-ui, sans-serif" font-size="12" font-weight="bold">PostgreSQL</text>
<!-- Stats -->
<text x="-55" y="5" fill="#93c5fd" font-family="monospace" font-size="10">Connections: 24/100</text>
<text x="-55" y="35" fill="#93c5fd" font-family="monospace" font-size="10">Queries/sec: 847</text>
<!-- Status indicator -->
<circle cx="65" cy="-35" r="6" fill="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</circle>
<text y="70" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="11">● HEALTHY</text>
<text y="85" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="10">v16.2</text>
</g>
<!-- Feature box for PostgreSQL -->
<g transform="translate(150, 400)">
<rect x="-70" y="0" width="140" height="60" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1" stroke-dasharray="4 2"/>
<text x="0" y="18" text-anchor="middle" fill="#64748b" font-family="system-ui, sans-serif" font-size="9">FUNCTIONS</text>
<text x="0" y="33" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="9">• User Sessions</text>
<text x="0" y="46" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="9">• Bot Configs</text>
</g>
<!-- Qdrant -->
<g id="qdrant" transform="translate(150, 520)">
<rect x="-80" y="-50" width="160" height="100" rx="12" fill="url(#vectorGradient)" filter="url(#softGlow)"/>
<!-- Vector visualization -->
<g transform="translate(-50, -30)">
<circle cx="10" cy="10" r="3" fill="#c4b5fd" opacity="0.7">
<animate attributeName="cy" values="10;15;10" dur="2s" repeatCount="indefinite"/>
</circle>
<circle cx="25" cy="20" r="3" fill="#c4b5fd" opacity="0.8">
<animate attributeName="cx" values="25;30;25" dur="1.5s" repeatCount="indefinite"/>
</circle>
<circle cx="15" cy="30" r="3" fill="#c4b5fd" opacity="0.6">
<animate attributeName="cy" values="30;25;30" dur="1.8s" repeatCount="indefinite"/>
</circle>
</g>
<text x="0" y="-15" text-anchor="middle" fill="white" font-family="system-ui, sans-serif" font-size="14" font-weight="bold">Qdrant</text>
<text x="0" y="5" text-anchor="middle" fill="#c4b5fd" font-family="monospace" font-size="10">Vectors: 1.2M</text>
<text x="0" y="20" text-anchor="middle" fill="#c4b5fd" font-family="monospace" font-size="10">Collections: 8</text>
<text x="0" y="35" text-anchor="middle" fill="#c4b5fd" font-family="monospace" font-size="10">Search: 12ms avg</text>
<circle cx="65" cy="-35" r="6" fill="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</circle>
<text y="70" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="11">● HEALTHY</text>
<text y="85" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="10">v1.9.2</text>
</g>
<!-- Feature box for Qdrant -->
<g transform="translate(150, 620)">
<rect x="-70" y="0" width="140" height="60" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1" stroke-dasharray="4 2"/>
<text x="0" y="18" text-anchor="middle" fill="#64748b" font-family="system-ui, sans-serif" font-size="9">FUNCTIONS</text>
<text x="0" y="33" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="9">• Semantic Search</text>
<text x="0" y="46" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="9">• RAG Embeddings</text>
</g>
<!-- MinIO -->
<g id="minio" transform="translate(150, 740)">
<rect x="-80" y="-50" width="160" height="100" rx="12" fill="url(#storageGradient)" filter="url(#softGlow)"/>
<!-- Folder icons -->
<text x="-50" y="-10" fill="white" font-size="20">📁</text>
<text x="-20" y="-10" fill="white" font-size="20">📄</text>
<text x="10" y="-10" fill="white" font-size="20">🖼️</text>
<text x="0" y="15" text-anchor="middle" fill="white" font-family="system-ui, sans-serif" font-size="14" font-weight="bold">MinIO Drive</text>
<text x="0" y="32" text-anchor="middle" fill="#a7f3d0" font-family="monospace" font-size="10">Storage: 45.2 GB</text>
<text x="0" y="45" text-anchor="middle" fill="#a7f3d0" font-family="monospace" font-size="10">Objects: 12,847</text>
<circle cx="65" cy="-35" r="6" fill="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</circle>
<text y="70" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="11">● HEALTHY</text>
<text y="85" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="10">v2024.01</text>
</g>
<!-- ==================== RIGHT SIDE: Services ==================== -->
<!-- BotModels (LLM Server) -->
<g id="botmodels" transform="translate(1000, 300)">
<rect x="-80" y="-50" width="160" height="100" rx="12" fill="url(#llmGradient)" filter="url(#softGlow)"/>
<!-- AI brain animation -->
<g transform="translate(-30, -35)">
<text font-size="24">🧠</text>
<circle cx="20" cy="12" r="15" fill="none" stroke="#fef3c7" stroke-width="1" opacity="0.5">
<animate attributeName="r" values="12;18;12" dur="1.5s" repeatCount="indefinite"/>
<animate attributeName="opacity" values="0.5;0.2;0.5" dur="1.5s" repeatCount="indefinite"/>
</circle>
</g>
<text x="0" y="0" text-anchor="middle" fill="white" font-family="system-ui, sans-serif" font-size="14" font-weight="bold">BotModels</text>
<text x="0" y="17" text-anchor="middle" fill="#fef3c7" font-family="monospace" font-size="10">Model: gpt-4o</text>
<text x="0" y="32" text-anchor="middle" fill="#fef3c7" font-family="monospace" font-size="10">Tokens/hr: 125K</text>
<text x="0" y="47" text-anchor="middle" fill="#fef3c7" font-family="monospace" font-size="10">Latency: 890ms</text>
<circle cx="65" cy="-35" r="6" fill="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</circle>
<text y="70" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="11">● RUNNING</text>
<text y="85" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="10">v2.1.0</text>
</g>
<!-- Feature box for BotModels -->
<g transform="translate(1000, 400)">
<rect x="-70" y="0" width="140" height="60" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1" stroke-dasharray="4 2"/>
<text x="0" y="18" text-anchor="middle" fill="#64748b" font-family="system-ui, sans-serif" font-size="9">FUNCTIONS</text>
<text x="0" y="33" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="9">• LLM Inference</text>
<text x="0" y="46" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="9">• Model Routing</text>
</g>
<!-- Vault -->
<g id="vault" transform="translate(1000, 520)">
<rect x="-80" y="-50" width="160" height="100" rx="12" fill="url(#secretsGradient)" filter="url(#softGlow)"/>
<!-- Lock icon -->
<text x="0" y="-15" text-anchor="middle" font-size="28">🔐</text>
<text x="0" y="10" text-anchor="middle" fill="white" font-family="system-ui, sans-serif" font-size="14" font-weight="bold">Vault</text>
<text x="0" y="27" text-anchor="middle" fill="#fecaca" font-family="monospace" font-size="10">Secrets: 156</text>
<text x="0" y="42" text-anchor="middle" fill="#fecaca" font-family="monospace" font-size="10">Policies: 12</text>
<circle cx="65" cy="-35" r="6" fill="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</circle>
<text y="70" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="11">● SEALED</text>
<text y="85" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="10">v1.15.0</text>
</g>
<!-- Redis -->
<g id="redis" transform="translate(1000, 680)">
<rect x="-80" y="-50" width="160" height="100" rx="12" fill="url(#cacheGradient)" filter="url(#softGlow)"/>
<!-- Lightning bolt for speed -->
<text x="0" y="-15" text-anchor="middle" font-size="24"></text>
<text x="0" y="5" text-anchor="middle" fill="white" font-family="system-ui, sans-serif" font-size="14" font-weight="bold">Redis Cache</text>
<text x="0" y="22" text-anchor="middle" fill="#a5f3fc" font-family="monospace" font-size="10">Hit Rate: 94.2%</text>
<text x="0" y="37" text-anchor="middle" fill="#a5f3fc" font-family="monospace" font-size="10">Keys: 8,421</text>
<text x="0" y="52" text-anchor="middle" fill="#a5f3fc" font-family="monospace" font-size="10">Memory: 256MB</text>
<circle cx="65" cy="-35" r="6" fill="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</circle>
<text y="70" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="11">● CONNECTED</text>
<text y="85" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="10">v7.2.4</text>
</g>
<!-- ==================== BOTTOM: Analytics ==================== -->
<!-- InfluxDB -->
<g id="influxdb" transform="translate(800, 800)">
<rect x="-80" y="-50" width="160" height="100" rx="12" fill="url(#metricsGradient)" filter="url(#softGlow)"/>
<!-- Chart icon -->
<g transform="translate(-50, -40)">
<rect x="0" y="20" width="8" height="20" fill="#fbcfe8" rx="2">
<animate attributeName="height" values="20;30;20" dur="1s" repeatCount="indefinite"/>
<animate attributeName="y" values="20;10;20" dur="1s" repeatCount="indefinite"/>
</rect>
<rect x="12" y="15" width="8" height="25" fill="#f9a8d4" rx="2">
<animate attributeName="height" values="25;15;25" dur="1.2s" repeatCount="indefinite"/>
<animate attributeName="y" values="15;25;15" dur="1.2s" repeatCount="indefinite"/>
</rect>
<rect x="24" y="10" width="8" height="30" fill="#f472b6" rx="2">
<animate attributeName="height" values="30;20;30" dur="0.8s" repeatCount="indefinite"/>
<animate attributeName="y" values="10;20;10" dur="0.8s" repeatCount="indefinite"/>
</rect>
</g>
<text x="0" y="0" text-anchor="middle" fill="white" font-family="system-ui, sans-serif" font-size="14" font-weight="bold">InfluxDB</text>
<text x="0" y="17" text-anchor="middle" fill="#fbcfe8" font-family="monospace" font-size="10">Points/sec: 2,450</text>
<text x="0" y="32" text-anchor="middle" fill="#fbcfe8" font-family="monospace" font-size="10">Retention: 30d</text>
<text x="0" y="47" text-anchor="middle" fill="#fbcfe8" font-family="monospace" font-size="10">Buckets: 4</text>
<circle cx="65" cy="-35" r="6" fill="#22c55e">
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</circle>
<text y="70" text-anchor="middle" fill="#22c55e" font-family="monospace" font-size="11">● RECORDING</text>
<text y="85" text-anchor="middle" fill="#94a3b8" font-family="monospace" font-size="10">v2.7.3</text>
</g>
<!-- ==================== STATUS PANEL ==================== -->
<g transform="translate(400, 780)">
<rect x="-100" y="-30" width="200" height="100" rx="8" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<text x="0" y="-10" text-anchor="middle" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="12" font-weight="bold">SYSTEM RESOURCES</text>
<!-- CPU Bar -->
<text x="-85" y="12" fill="#94a3b8" font-family="monospace" font-size="10">CPU</text>
<rect x="-50" y="3" width="120" height="12" rx="3" fill="#374151"/>
<rect x="-50" y="3" width="84" height="12" rx="3" fill="#22c55e">
<animate attributeName="width" values="84;90;84" dur="3s" repeatCount="indefinite"/>
</rect>
<text x="80" y="12" fill="#94a3b8" font-family="monospace" font-size="10">70%</text>
<!-- Memory Bar -->
<text x="-85" y="32" fill="#94a3b8" font-family="monospace" font-size="10">MEM</text>
<rect x="-50" y="23" width="120" height="12" rx="3" fill="#374151"/>
<rect x="-50" y="23" width="72" height="12" rx="3" fill="#3b82f6">
<animate attributeName="width" values="72;78;72" dur="4s" repeatCount="indefinite"/>
</rect>
<text x="80" y="32" fill="#94a3b8" font-family="monospace" font-size="10">60%</text>
<!-- GPU Bar -->
<text x="-85" y="52" fill="#94a3b8" font-family="monospace" font-size="10">GPU</text>
<rect x="-50" y="43" width="120" height="12" rx="3" fill="#374151"/>
<rect x="-50" y="43" width="48" height="12" rx="3" fill="#8b5cf6">
<animate attributeName="width" values="48;60;48" dur="2s" repeatCount="indefinite"/>
</rect>
<text x="80" y="52" fill="#94a3b8" font-family="monospace" font-size="10">40%</text>
</g>
<!-- ==================== METRICS PANEL ==================== -->
<g transform="translate(600, 150)">
<rect x="-250" y="-35" width="500" height="70" rx="8" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<!-- Active Sessions -->
<g transform="translate(-180, 0)">
<text y="-10" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">SESSIONS</text>
<text y="15" text-anchor="middle" fill="#22c55e" font-family="system-ui, sans-serif" font-size="24" font-weight="bold">
247
<animate attributeName="opacity" values="1;0.8;1" dur="1s" repeatCount="indefinite"/>
</text>
</g>
<!-- Messages Today -->
<g transform="translate(-60, 0)">
<text y="-10" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">MESSAGES</text>
<text y="15" text-anchor="middle" fill="#3b82f6" font-family="system-ui, sans-serif" font-size="24" font-weight="bold">12.4K</text>
</g>
<!-- Response Time -->
<g transform="translate(60, 0)">
<text y="-10" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">AVG RESPONSE</text>
<text y="15" text-anchor="middle" fill="#f59e0b" font-family="system-ui, sans-serif" font-size="24" font-weight="bold">1.2s</text>
</g>
<!-- Uptime -->
<g transform="translate(180, 0)">
<text y="-10" text-anchor="middle" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">UPTIME</text>
<text y="15" text-anchor="middle" fill="#10b981" font-family="system-ui, sans-serif" font-size="24" font-weight="bold">99.9%</text>
</g>
</g>
<!-- ==================== LEGEND ==================== -->
<g transform="translate(50, 130)">
<rect x="0" y="0" width="130" height="90" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<text x="10" y="20" fill="#e2e8f0" font-family="system-ui, sans-serif" font-size="11" font-weight="bold">STATUS LEGEND</text>
<circle cx="20" cy="40" r="5" fill="#22c55e"/>
<text x="35" y="44" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">Healthy/Running</text>
<circle cx="20" cy="58" r="5" fill="#f59e0b"/>
<text x="35" y="62" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">Warning/Degraded</text>
<circle cx="20" cy="76" r="5" fill="#ef4444"/>
<text x="35" y="80" fill="#94a3b8" font-family="system-ui, sans-serif" font-size="10">Error/Stopped</text>
</g>
<!-- ==================== TIMESTAMP ==================== -->
<text x="1150" y="880" text-anchor="end" fill="#64748b" font-family="monospace" font-size="10">
Last updated: <tspan fill="#94a3b8">2025-01-14 15:42:31 UTC</tspan>
<animate attributeName="opacity" values="1;0.5;1" dur="2s" repeatCount="indefinite"/>
</text>
<!-- Refresh indicator -->
<g transform="translate(1170, 870)">
<circle cx="0" cy="0" r="8" fill="none" stroke="#4f46e5" stroke-width="2" stroke-dasharray="12 8">
<animateTransform attributeName="transform" type="rotate" from="0" to="360" dur="2s" repeatCount="indefinite"/>
</circle>
</g>
</svg>

After

Width:  |  Height:  |  Size: 26 KiB

View file

@ -0,0 +1,302 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-red { stroke: #E74C3C; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-red {
stroke: #FF4757;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF4757) drop-shadow(0 0 8px #FF6B81);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#F5A623;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#7ED321;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Mail - Email Client Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">IMAP/SMTP email management with AI-powered features</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Folders</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Messages</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Actions</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Compose</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- Folder Navigation -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Folder List</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Navigate mailbox</text>
</g>
<!-- Inbox -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Inbox</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Received</text>
</g>
<!-- Sent -->
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Sent</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Outgoing</text>
</g>
<!-- Drafts -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Drafts</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">In progress</text>
</g>
<!-- Trash -->
<g transform="translate(185, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-red"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Trash</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Deleted</text>
</g>
<!-- Starred -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Starred</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Important</text>
</g>
<!-- Archive -->
<g transform="translate(185, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Archive</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Stored</text>
</g>
<!-- Message List -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Message List</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Browse emails</text>
</g>
<!-- Message Preview -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Preview Pane</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Quick read</text>
</g>
<!-- Search -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Search</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Find messages</text>
</g>
<!-- Filters -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Filters</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Unread, starred</text>
</g>
<!-- Actions -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Actions</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Manage emails</text>
</g>
<!-- Reply -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Reply</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Respond</text>
</g>
<!-- Forward -->
<g transform="translate(785, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Forward</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Share</text>
</g>
<!-- Delete -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-red"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Delete</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Remove</text>
</g>
<!-- Archive Action -->
<g transform="translate(785, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Archive</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Store</text>
</g>
<!-- AI Summary -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">AI Summary ✨</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Quick insights</text>
</g>
<!-- Compose -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Compose</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Write new email</text>
</g>
<!-- To/CC/BCC -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Recipients</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">To, CC, BCC</text>
</g>
<!-- Rich Editor -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Rich Editor</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Format text</text>
</g>
<!-- Attachments -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Attachments</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Add files</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Folder selection arrows -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- Message to Actions -->
<line x1="580" y1="290" x2="675" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-orange" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Select Folder</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Navigate mailbox</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Read Messages</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Browse and search</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Take Action</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Reply, forward, delete</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Compose</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Write new email</text>
</g>
<!-- Features Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">AI Features:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Smart compose with AI suggestions</text>
<rect x="320" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="345" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Thread summarization</text>
<rect x="580" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="605" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Priority detection</text>
<rect x="800" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="825" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Action item extraction</text>
</g>
<!-- Keyboard shortcuts -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Shortcuts:</text>
<text x="100" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">C = Compose | R = Reply | F = Forward | E = Archive | # = Delete | S = Star | / = Search</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">GET /api/mail/folders | GET /api/mail/messages | POST /api/mail/send | DELETE /api/mail/{id} | POST /api/mail/ai/summarize</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,299 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-red { stroke: #E74C3C; stroke-width: 2.6; }
.neon-pink { stroke: #FF6B9D; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-red {
stroke: #FF4757;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF4757) drop-shadow(0 0 8px #FF6B81);
}
.neon-pink {
stroke: #FF6B9D;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF6B9D) drop-shadow(0 0 8px #FF8FAB);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#7ED321;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#50E3C2;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Meet - Video Calls Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">LiveKit-powered video conferencing with AI transcription and summaries</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Setup</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Meeting</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">AI Features</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Post-Meeting</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- Start Meeting -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Start Meeting</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Create or join</text>
</g>
<!-- Camera -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Camera</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Video</text>
</g>
<!-- Microphone -->
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Mic</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Audio</text>
</g>
<!-- Preview -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Preview</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Check before join</text>
</g>
<!-- Invite -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Invite</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Share link</text>
</g>
<!-- Video Grid -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Video Grid</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Participants view</text>
</g>
<!-- Controls -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Controls</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Mute, camera, share</text>
</g>
<!-- Chat -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Chat</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Messages</text>
</g>
<!-- Screen Share -->
<g transform="translate(485, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Share</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Screen</text>
</g>
<!-- Reactions -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">React</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">👍 🎉</text>
</g>
<!-- Hand Raise -->
<g transform="translate(485, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Hand</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">✋ Raise</text>
</g>
<!-- AI Features -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">AI Features ✨</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Smart assistance</text>
</g>
<!-- Transcription -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Transcription</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Live captions</text>
</g>
<!-- Live Summary -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Live Summary</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Real-time notes</text>
</g>
<!-- Action Items -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Action Items</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Auto-detect tasks</text>
</g>
<!-- Post-Meeting -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Post-Meeting</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">After the call</text>
</g>
<!-- Recording -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-red"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Recording</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Video playback</text>
</g>
<!-- Full Transcript -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Full Transcript</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Searchable text</text>
</g>
<!-- Meeting Notes -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Meeting Notes</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">AI summary + tasks</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Setup to Video Grid -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- AI to Post-meeting -->
<line x1="880" y1="290" x2="975" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="880" y1="380" x2="975" y2="380" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="880" y1="470" x2="975" y2="470" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-cyan" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Setup</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Check devices</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Meeting</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Video conference</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">AI Assist</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Live transcription</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Review</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Notes and recording</text>
</g>
<!-- Features Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Meeting Controls:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Camera and microphone</text>
<rect x="250" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="275" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Screen sharing</text>
<rect x="450" y="20" width="16" height="16" rx="3" fill="none" class="neon-red" stroke-width="2"/>
<text x="475" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Recording</text>
<rect x="600" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="625" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Live transcription</text>
<rect x="800" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="825" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Chat and reactions</text>
</g>
<!-- Keyboard shortcuts -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Shortcuts:</text>
<text x="100" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">M = Mute | V = Video | S = Share screen | R = Record | H = Raise hand | Space = Push-to-talk | Esc = Leave</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">POST /api/meet/create | GET /api/meet/join/{id} | GET /api/meet/transcript/{id} | GET /api/meet/recording/{id} | POST /api/meet/ai/summary</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,332 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-pink { stroke: #FF6B9D; stroke-width: 2.6; }
.neon-yellow { stroke: #F8E71C; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-pink {
stroke: #FF6B9D;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF6B9D) drop-shadow(0 0 8px #FF8FAB);
}
.neon-yellow {
stroke: #FFE066;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FFE066) drop-shadow(0 0 8px #FFD93D);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#7ED321;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#50E3C2;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Paper - AI Writing Assistant Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">Rich text editor with AI-powered writing, editing, and formatting assistance</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Create</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Edit</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">AI Assist</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Export</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- New Document -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">New Document</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Create or open</text>
</g>
<!-- Blank -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Blank</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Fresh start</text>
</g>
<!-- Template -->
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Template</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Pre-built</text>
</g>
<!-- Import -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Import</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">MD, DOCX</text>
</g>
<!-- AI Generate -->
<g transform="translate(185, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">AI Gen ✨</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">From prompt</text>
</g>
<!-- Recent -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Recent Documents</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Quick access</text>
</g>
<!-- Rich Editor -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Rich Editor</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Write content</text>
</g>
<!-- Formatting -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Formatting</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Bold, italic, headers</text>
</g>
<!-- Lists -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Lists</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Bullets</text>
</g>
<!-- Tables -->
<g transform="translate(485, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Tables</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Grid data</text>
</g>
<!-- Code Blocks -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Code</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Syntax HL</text>
</g>
<!-- Images -->
<g transform="translate(485, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Images</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Media</text>
</g>
<!-- AI Assist -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">AI Assist ✨</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Writing helper</text>
</g>
<!-- Continue Writing -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Continue Writing</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Auto-complete</text>
</g>
<!-- Improve -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-yellow"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Improve</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Rewrite</text>
</g>
<!-- Summarize -->
<g transform="translate(785, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Summary</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Condense</text>
</g>
<!-- Translate -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Translate</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Languages</text>
</g>
<!-- Grammar -->
<g transform="translate(785, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Grammar</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Fix errors</text>
</g>
<!-- Export -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Export</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Save and share</text>
</g>
<!-- Markdown -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">.md</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Markdown</text>
</g>
<!-- PDF -->
<g transform="translate(1085, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">.pdf</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Document</text>
</g>
<!-- HTML -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">.html</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Web page</text>
</g>
<!-- DOCX -->
<g transform="translate(1085, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">.docx</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Word</text>
</g>
<!-- Auto-Save -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Auto-Save</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Never lose work</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Create to Editor -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- Editor to AI -->
<line x1="580" y1="290" x2="675" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-cyan" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Create</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">New document</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Write</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Rich text editing</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Enhance</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">AI assistance</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Export</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Multiple formats</text>
</g>
<!-- AI Features Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">AI Writing Features:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Auto-complete sentences</text>
<rect x="250" y="20" width="16" height="16" rx="3" fill="none" class="neon-yellow" stroke-width="2"/>
<text x="275" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Improve writing style</text>
<rect x="480" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="505" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Summarize content</text>
<rect x="700" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="725" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Multi-language translate</text>
<rect x="950" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="975" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Grammar and spelling</text>
</g>
<!-- Keyboard shortcuts -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Shortcuts:</text>
<text x="100" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Ctrl+B = Bold | Ctrl+I = Italic | Ctrl+K = Link | Ctrl+/ = AI assist | Ctrl+S = Save | Ctrl+E = Export | Ctrl+Z = Undo</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">GET /api/paper/documents | POST /api/paper/save | POST /api/paper/ai/continue | POST /api/paper/ai/improve | GET /api/paper/export/{format}</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -0,0 +1,307 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-pink { stroke: #FF6B9D; stroke-width: 2.6; }
.neon-yellow { stroke: #F8E71C; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-pink {
stroke: #FF6B9D;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF6B9D) drop-shadow(0 0 8px #FF8FAB);
}
.neon-yellow {
stroke: #FFE066;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FFE066) drop-shadow(0 0 8px #FFD93D);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#7ED321;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#50E3C2;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Research - AI Search Flow</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">Multi-source intelligent search with AI-powered synthesis and citations</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Query</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Sources</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">AI Processing</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Results</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- Search Input -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Search Query</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Natural language</text>
</g>
<!-- Quick Search -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Quick</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Fast mode</text>
</g>
<!-- Deep Search -->
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Deep</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Thorough</text>
</g>
<!-- Filters -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Filters</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Date, type, source</text>
</g>
<!-- History -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Search History</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Recent queries</text>
</g>
<!-- Sources -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Data Sources</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Multi-source search</text>
</g>
<!-- Web -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-blue"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Web</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Internet</text>
</g>
<!-- Knowledge Base -->
<g transform="translate(485, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">KB</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Internal</text>
</g>
<!-- Documents -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Docs</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Drive files</text>
</g>
<!-- APIs -->
<g transform="translate(485, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-yellow"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">APIs</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">External</text>
</g>
<!-- News -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">News</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">RSS feeds</text>
</g>
<!-- Academic -->
<g transform="translate(485, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Papers</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Academic</text>
</g>
<!-- AI Processing -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">AI Processing ✨</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">LLM synthesis</text>
</g>
<!-- RAG -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">RAG Pipeline</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Vector retrieval</text>
</g>
<!-- Synthesis -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Synthesis</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Combine sources</text>
</g>
<!-- Fact Check -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Fact Check</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Verify claims</text>
</g>
<!-- Results -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Results</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">AI-generated answer</text>
</g>
<!-- Answer -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Summary</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Concise answer</text>
</g>
<!-- Citations -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Citations</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Source links</text>
</g>
<!-- Collections -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Save to Collection</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Bookmark research</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Query mode arrows -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- Sources to AI -->
<line x1="580" y1="290" x2="675" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="580" y1="380" x2="675" y2="380" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="580" y1="470" x2="675" y2="440" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<!-- AI to Results -->
<line x1="880" y1="290" x2="975" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="880" y1="380" x2="975" y2="380" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-cyan" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Ask Question</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Natural language</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Search Sources</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Multi-source lookup</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">AI Synthesis</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Combine and verify</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Get Answer</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Cited summary</text>
</g>
<!-- Features Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Search Modes:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Quick - Fast answers from cache</text>
<rect x="280" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="305" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Deep - Thorough multi-source search</text>
<rect x="580" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="605" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">RAG - Knowledge base retrieval</text>
<rect x="880" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="905" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Verified - Fact-checked answers</text>
</g>
<!-- Keyboard shortcuts -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Shortcuts:</text>
<text x="100" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">/ = Focus search | Enter = Search | Ctrl+D = Deep mode | Ctrl+S = Save | Ctrl+C = Copy answer | ↑↓ = Navigate results</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">POST /api/research/search | GET /api/research/sources | POST /api/research/deep | GET /api/research/collections | POST /api/research/save</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -0,0 +1,302 @@
<svg width="1400" height="900" xmlns="http://www.w3.org/2000/svg">
<style>
/* Light theme defaults */
.neon-blue { stroke: #4A90E2; stroke-width: 2.6; }
.neon-orange { stroke: #F5A623; stroke-width: 2.6; }
.neon-purple { stroke: #BD10E0; stroke-width: 2.6; }
.neon-green { stroke: #7ED321; stroke-width: 2.6; }
.neon-cyan { stroke: #50E3C2; stroke-width: 2.6; }
.neon-pink { stroke: #FF6B9D; stroke-width: 2.6; }
.neon-yellow { stroke: #F8E71C; stroke-width: 2.6; }
.main-text { fill: #1a1a1a; }
.secondary-text { fill: #666; }
.arrow-color { stroke: #666; fill: #666; }
@media (prefers-color-scheme: dark) {
.neon-blue {
stroke: #00D4FF;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00D4FF) drop-shadow(0 0 8px #00A0FF);
}
.neon-orange {
stroke: #FF9500;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF9500) drop-shadow(0 0 8px #FF7700);
}
.neon-purple {
stroke: #E040FB;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #E040FB) drop-shadow(0 0 8px #D500F9);
}
.neon-green {
stroke: #00FF88;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00FF88) drop-shadow(0 0 8px #00E676);
}
.neon-cyan {
stroke: #00E5EA;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #00E5EA) drop-shadow(0 0 8px #00BCD4);
}
.neon-pink {
stroke: #FF6B9D;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FF6B9D) drop-shadow(0 0 8px #FF8FAB);
}
.neon-yellow {
stroke: #FFE066;
stroke-width: 2.8;
filter: drop-shadow(0 0 4px #FFE066) drop-shadow(0 0 8px #FFD93D);
}
.main-text { fill: #FFFFFF; }
.secondary-text { fill: #B0B0B0; }
.arrow-color { stroke: #B0B0B0; fill: #B0B0B0; }
}
</style>
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refX="11.7" refY="3.9" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,7.8 L11.7,3.9 z" class="arrow-color"/>
</marker>
<linearGradient id="flowGradient" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:#4A90E2;stop-opacity:0.3" />
<stop offset="33%" style="stop-color:#BD10E0;stop-opacity:0.3" />
<stop offset="66%" style="stop-color:#F5A623;stop-opacity:0.3" />
<stop offset="100%" style="stop-color:#7ED321;stop-opacity:0.3" />
</linearGradient>
</defs>
<!-- Title -->
<text x="700" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="32" font-weight="600" class="main-text">Sources - Prompts &amp; Templates Management</text>
<text x="700" y="80" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" class="secondary-text">Manage AI prompts, bot templates, news feeds, MCP servers, LLM tools, and models</text>
<!-- Phase Labels -->
<text x="180" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Source Types</text>
<text x="480" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Browse</text>
<text x="780" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Configure</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="secondary-text">Deploy</text>
<!-- MAIN FLOW DIAGRAM -->
<g id="main-flow">
<!-- Tab Navigation -->
<g transform="translate(80, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-blue"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Tab Navigation</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">6 resource categories</text>
</g>
<!-- Prompts Tab -->
<g transform="translate(80, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Prompts</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">AI prompts</text>
</g>
<!-- Templates Tab -->
<g transform="translate(185, 260)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Templates</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">.gbai bots</text>
</g>
<!-- News Tab -->
<g transform="translate(80, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">News</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">RSS feeds</text>
</g>
<!-- MCP Servers Tab -->
<g transform="translate(185, 350)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-pink"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">MCP</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">Servers</text>
</g>
<!-- LLM Tools Tab -->
<g transform="translate(80, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-yellow"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Tools</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">LLM tools</text>
</g>
<!-- Models Tab -->
<g transform="translate(185, 440)">
<rect x="0" y="0" width="95" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="47" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="15" font-weight="500" class="main-text">Models</text>
<text x="47" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="12" class="secondary-text">AI models</text>
</g>
<!-- Browse Panel -->
<g transform="translate(380, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Browse Items</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Grid/List view</text>
</g>
<!-- Search -->
<g transform="translate(380, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Search</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Filter by name/tag</text>
</g>
<!-- Categories -->
<g transform="translate(380, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Categories</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">CRM, HR, Support...</text>
</g>
<!-- Preview -->
<g transform="translate(380, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-purple"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Preview</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Quick look</text>
</g>
<!-- Configure Panel -->
<g transform="translate(680, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Configure</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Customize settings</text>
</g>
<!-- Edit Prompt -->
<g transform="translate(680, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Edit Content</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Modify prompts</text>
</g>
<!-- Parameters -->
<g transform="translate(680, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Parameters</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">API keys, config</text>
</g>
<!-- Test -->
<g transform="translate(680, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-orange"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Test</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Try before deploy</text>
</g>
<!-- Deploy -->
<g transform="translate(980, 160)">
<rect x="0" y="0" width="200" height="70" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="30" text-anchor="middle" font-family="Arial, sans-serif" font-size="20" font-weight="500" class="main-text">Deploy</text>
<text x="100" y="52" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Activate resources</text>
</g>
<!-- Install Template -->
<g transform="translate(980, 260)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Install Template</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Create new bot</text>
</g>
<!-- Activate -->
<g transform="translate(980, 350)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-green"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Activate</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Enable in system</text>
</g>
<!-- Monitor -->
<g transform="translate(980, 440)">
<rect x="0" y="0" width="200" height="60" rx="6.5" fill="none" class="neon-cyan"/>
<text x="100" y="25" text-anchor="middle" font-family="Arial, sans-serif" font-size="18" font-weight="500" class="main-text">Monitor</text>
<text x="100" y="45" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Usage stats</text>
</g>
<!-- Arrows - Main Flow -->
<line x1="280" y1="195" x2="375" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="580" y1="195" x2="675" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<line x1="880" y1="195" x2="975" y2="195" class="arrow-color" stroke-width="2.6" marker-end="url(#arrow)" opacity="0.7"/>
<!-- Tab selection arrows -->
<path d="M175 290 Q300 290 340 230 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 380 Q320 380 350 250 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<path d="M175 470 Q340 470 360 270 L375 195" fill="none" class="arrow-color" stroke-width="2" stroke-dasharray="3.9,3.9" marker-end="url(#arrow)" opacity="0.4"/>
<!-- Browse to Configure -->
<line x1="580" y1="290" x2="675" y2="290" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
<line x1="580" y1="380" x2="675" y2="380" class="arrow-color" stroke-width="1.5" marker-end="url(#arrow)" opacity="0.4"/>
</g>
<!-- PROGRESS INDICATOR -->
<g id="progress-legend" transform="translate(0, 520)">
<rect x="100" y="30" width="1200" height="80" fill="url(#flowGradient)" rx="10" opacity="0.2"/>
<!-- Stage markers -->
<circle cx="200" cy="70" r="12" class="neon-blue" fill="none" stroke-width="3"/>
<text x="200" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">1</text>
<circle cx="500" cy="70" r="12" class="neon-purple" fill="none" stroke-width="3"/>
<text x="500" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">2</text>
<circle cx="800" cy="70" r="12" class="neon-orange" fill="none" stroke-width="3"/>
<text x="800" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">3</text>
<circle cx="1100" cy="70" r="12" class="neon-green" fill="none" stroke-width="3"/>
<text x="1100" y="75" text-anchor="middle" font-family="Arial, sans-serif" font-size="14" font-weight="bold" class="main-text">4</text>
<!-- Connecting lines -->
<line x1="212" y1="70" x2="488" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="512" y1="70" x2="788" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<line x1="812" y1="70" x2="1088" y2="70" class="arrow-color" stroke-width="2" opacity="0.3"/>
<!-- Stage labels -->
<text x="200" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Select Tab</text>
<text x="200" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Choose resource type</text>
<text x="500" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Browse</text>
<text x="500" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Search and explore</text>
<text x="800" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Configure</text>
<text x="800" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Customize settings</text>
<text x="1100" y="130" text-anchor="middle" font-family="Arial, sans-serif" font-size="16" font-weight="500" class="main-text">Deploy</text>
<text x="1100" y="150" text-anchor="middle" font-family="Arial, sans-serif" font-size="13" class="secondary-text">Activate resource</text>
</g>
<!-- Tab Types Legend -->
<g transform="translate(100, 720)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Resource Types:</text>
<rect x="0" y="20" width="16" height="16" rx="3" fill="none" class="neon-cyan" stroke-width="2"/>
<text x="25" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Prompts - Reusable AI instructions</text>
<rect x="280" y="20" width="16" height="16" rx="3" fill="none" class="neon-purple" stroke-width="2"/>
<text x="305" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Templates - Ready-made .gbai bots</text>
<rect x="560" y="20" width="16" height="16" rx="3" fill="none" class="neon-orange" stroke-width="2"/>
<text x="585" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">News - RSS/Atom feeds</text>
<rect x="780" y="20" width="16" height="16" rx="3" fill="none" class="neon-pink" stroke-width="2"/>
<text x="805" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">MCP - Model servers</text>
<rect x="980" y="20" width="16" height="16" rx="3" fill="none" class="neon-yellow" stroke-width="2"/>
<text x="1005" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Tools - LLM capabilities</text>
<rect x="1150" y="20" width="16" height="16" rx="3" fill="none" class="neon-green" stroke-width="2"/>
<text x="1175" y="33" font-family="Arial, sans-serif" font-size="14" class="secondary-text">Models</text>
</g>
<!-- Template categories -->
<g transform="translate(100, 780)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Categories:</text>
<text x="100" y="0" font-family="Arial, sans-serif" font-size="14" class="secondary-text">CRM | HR | Compliance | Support | Sales | Marketing | Finance | Legal | IT | Custom</text>
</g>
<!-- API Endpoints -->
<g transform="translate(100, 820)">
<text x="0" y="0" font-family="Arial, sans-serif" font-size="16" font-weight="600" class="main-text">Endpoints:</text>
<text x="100" y="0" font-family="monospace, sans-serif" font-size="13" class="secondary-text">GET /api/sources/{type} | POST /api/sources/install | GET /api/templates | POST /api/prompts | GET /api/models</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 18 KiB

View file

@ -1 +1,656 @@
# Analytics - Dashboards # Analytics - Dashboards
> **Your business intelligence center**
![Analytics Flow](../../assets/suite/analytics-flow.svg)
---
## Overview
Analytics is the data visualization and reporting app in General Bots Suite. Track key metrics, build custom dashboards, generate reports, and get AI-powered insights about your business. Analytics turns your data into actionable information.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Analytics [+ Widget] [Export] [⚙️] [×] │
├──────────────┬──────────────────────────────────────────────────────────┤
│ │ │
│ DASHBOARDS │ Sales Overview Dashboard May 2025 │
│ ─────────── │ ════════════════════════════════════════════════════ │
│ │ │
│ ★ Overview │ ┌─────────────────┐ ┌─────────────────┐ │
│ Sales │ │ Total Revenue │ │ New Customers │ │
│ Marketing │ │ │ │ │ │
│ Support │ │ $2.4M │ │ 47 │ │
│ HR │ │ ▲ 15% │ │ ▲ 23% │ │
│ │ └─────────────────┘ └─────────────────┘ │
│ ─────────── │ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ Conversion Rate │ │ Avg Deal Size │ │
│ REPORTS │ │ │ │ │ │
│ ─────────── │ │ 24.5% │ │ $12,400 │ │
│ 📊 Weekly │ │ ▲ 3.2% │ │ ▼ 5% │ │
│ 📊 Monthly │ └─────────────────┘ └─────────────────┘ │
│ 📊 Quarterly│ │
│ │ ┌───────────────────────────────────────────────────┐ │
│ ─────────── │ │ Revenue Trend │ │
│ │ │ $ │ │
│ [+ New] │ │ 3M│ ╭─────╮ │ │
│ │ │ │ ╭────╯ ╰────╮ │ │
│ │ │ 2M│───╯ ╰───╮ │ │
│ │ │ │ ╰──── │ │
│ │ │ 1M│ │ │
│ │ │ └─────────────────────────────────────────── │ │
│ │ │ Jan Feb Mar Apr May │ │
│ │ └───────────────────────────────────────────────────┘ │
│ │ │
└──────────────┴──────────────────────────────────────────────────────────┘
```
---
## Features
### Dashboard Overview
Dashboards are collections of widgets that display your data visually.
**Default Dashboards:**
| Dashboard | What It Shows |
|-----------|---------------|
| **Overview** | Key metrics across all areas |
| **Sales** | Revenue, deals, pipeline |
| **Marketing** | Campaigns, leads, conversion |
| **Support** | Tickets, response time, satisfaction |
| **HR** | Headcount, hiring, retention |
---
### Creating a Dashboard
**Step 1: Click "+ New" in the sidebar**
```
┌─────────────────────────────────────────┐
│ Create Dashboard [×] │
├─────────────────────────────────────────┤
│ │
│ Dashboard Name: │
│ ┌─────────────────────────────────┐ │
│ │ Q2 Performance │ │
│ └─────────────────────────────────┘ │
│ │
│ Description (optional): │
│ ┌─────────────────────────────────┐ │
│ │ Key metrics for Q2 2025 │ │
│ └─────────────────────────────────┘ │
│ │
│ Template: │
│ ○ Blank │
│ ○ Sales Template │
│ ○ Marketing Template │
│ ● Copy from existing... │
│ [Sales Overview ▼] │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Create │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
**Step 2: Add Widgets**
Click **+ Widget** and choose a visualization type.
---
### Widget Types
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Add Widget [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ NUMBERS │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ┌───┐ │ │ ▲ 15% │ │ 85% │ │
│ │ │123│ │ │ ─────── │ │ ████░░ │ │
│ │ └───┘ │ │ vs last │ │ │ │
│ │ Number │ │ Comparison │ │ Progress │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ CHARTS │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ╭──╮ │ │ █ │ │ ╱╲ │ │ ┌──┬──┐ │ │
│ │ ───╯ ╰── │ │ █ █ │ │ ╲ │ │ │░░│██│ │ │
│ │ │ │ █ █ █ │ │ ╲ │ │ └──┴──┘ │ │
│ │ Line │ │ Bar │ │ Area │ │ Pie │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ TABLES & LISTS │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ ┌─┬─┬─┐ │ │ 1. ████ │ │ ● Item A │ │
│ │ ├─┼─┼─┤ │ │ 2. ███ │ │ ● Item B │ │
│ │ └─┴─┴─┘ │ │ 3. ██ │ │ ● Item C │ │
│ │ Table │ │ Leaderboard│ │ List │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ SPECIAL │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ [Map] │ │ ════════ │ │ ✨ AI │ │
│ │ ● ● │ │ ▓▓▓▓░░░░ │ │ Insights │ │
│ │ ● │ │ ════════ │ │ │ │
│ │ Geography │ │ Heatmap │ │ AI Summary │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Configuring Widgets
After selecting a widget type, configure the data source:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Configure Widget: Line Chart [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Title: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Monthly Revenue │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ DATA SOURCE │
│ ─────────── │
│ Source: [Sales Database ▼] │
│ Table: [transactions ▼] │
│ │
│ AXES │
│ ──── │
│ X-Axis: [date ▼] Group by: [Month ▼] │
│ Y-Axis: [amount ▼] Aggregate: [Sum ▼] │
│ │
│ FILTERS │
│ ─────── │
│ [+ Add Filter] │
│ │ status = "completed" [×] │
│ │ date >= "2025-01-01" [×] │
│ │
│ PREVIEW │
│ ─────── │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ $ │ │
│ │ 3M│ ╭─────╮ │ │
│ │ │ ╭────╯ ╰────╮ │ │
│ │ 2M│───╯ ╰───╮ │ │
│ │ 1M│ ╰──── │ │
│ │ └─────────────────────────────── │ │
│ │ Jan Feb Mar Apr May │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Save │ │ Cancel │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Key Metric Cards
Display important numbers with context:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Total Revenue │ │ Active Users │ │ Support Tickets │ │
│ │ │ │ │ │ │ │
│ │ $2,456,000 │ │ 1,234 │ │ 45 │ │
│ │ │ │ │ │ │ │
│ │ ▲ 15.3% │ │ ▲ 8.2% │ │ ▼ 12% │ │
│ │ vs last month │ │ vs last month │ │ vs last month │ │
│ │ │ │ │ │ (Good!) │ │
│ │ ████████░░ │ │ ██████░░░░ │ │ ██░░░░░░░░ │ │
│ │ Goal: $3M │ │ Goal: 2,000 │ │ Goal: <50
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Color Indicators:**
| Color | Meaning |
|-------|---------|
| 🟢 Green (▲) | Positive trend / On target |
| 🔴 Red (▼) | Negative trend / Below target |
| 🟡 Yellow (─) | No change / Needs attention |
| ⚪ Gray | No comparison available |
---
### AI Insights ✨
Let AI analyze your data and surface insights:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ ✨ AI Insights [Refresh] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 📈 TRENDS │
│ ────────── │
│ • Revenue has grown 15% month-over-month for 3 consecutive months │
│ • Customer acquisition cost decreased by $50 this quarter │
│ • Enterprise segment is outperforming SMB by 2x │
│ │
│ ⚠️ ALERTS │
│ ───────── │
│ • Churn rate increased to 5.2% (was 4.1% last month) │
│ • Support ticket volume up 23% - may need more staff │
│ • Marketing spend efficiency dropped in paid social │
│ │
│ 💡 RECOMMENDATIONS │
│ ───────────────── │
│ • Focus on enterprise sales - highest ROI segment │
│ • Investigate churn increase in mid-market customers │
│ • Consider reallocating paid social budget to email │
│ │
│ 🎯 PREDICTIONS │
│ ───────────── │
│ • Projected Q2 revenue: $7.8M (±5%) │
│ • Expected to hit annual target by October │
│ • Staffing need: +2 support agents by July │
│ │
│ [Ask AI a Question] [Generate Report] [Share Insights] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Ask the AI:**
```
You: Why did revenue spike in March?
AI: March revenue increased 28% due to three factors:
1. Enterprise Deal Closure - Three large deals ($150K+)
that were in pipeline since Q4 closed in March
2. Annual Renewals - 15 customers renewed annual
contracts (worth $320K total)
3. Product Launch - The new feature release drove
12 new mid-market signups
The spike is partially seasonal (end of Q1) and
partially due to the product launch timing.
```
---
### Reports
Generate and schedule reports:
**Creating a Report**
1. Click **+ New** under Reports
2. Select report type
3. Configure data and format
4. Schedule delivery (optional)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Create Report [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Report Name: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Weekly Sales Report │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ CONTENT │
│ ─────── │
│ ☑ Include dashboard: [Sales Overview ▼] │
│ ☑ Include AI insights │
│ ☐ Include raw data tables │
│ │
│ DATE RANGE │
│ ────────── │
│ ○ Last 7 days │
│ ● Last 30 days │
│ ○ This quarter │
│ ○ Custom range: [____] to [____] │
│ │
│ FORMAT │
│ ────── │
│ ○ PDF │
│ ● Interactive (Web) │
│ ○ Excel │
│ ○ PowerPoint │
│ │
│ SCHEDULE (Optional) │
│ ──────── │
│ ☑ Schedule automatic delivery │
│ Frequency: [Weekly ▼] │
│ Day: [Monday ▼] │
│ Time: [9:00 AM ▼] │
│ Send to: [team@company.com, ceo@company.com] │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Preview │ │ Save │ │ Cancel │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Data Sources
Connect Analytics to various data sources:
| Source Type | Examples |
|-------------|----------|
| **Databases** | PostgreSQL, MySQL, SQLite |
| **Files** | Excel, CSV, JSON |
| **APIs** | REST endpoints, GraphQL |
| **Apps** | CRM, Support, Calendar data |
| **Bot Data** | Conversation logs, user data |
**Adding a Data Source**
1. Go to **Settings** → **Data Sources**
2. Click **+ Add Source**
3. Select source type
4. Enter connection details
5. Test and save
```
┌─────────────────────────────────────────┐
│ Data Sources [×] │
├─────────────────────────────────────────┤
│ │
│ Connected Sources: │
│ │
│ ┌─────────────────────────────────┐ │
│ │ 🗄️ Main Database ✓ │ │
│ │ PostgreSQL - 15 tables │ │
│ │ Last sync: 5 min ago │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ 📊 Sales Data ✓ │ │
│ │ Excel - sales_2025.xlsx │ │
│ │ Last sync: 1 hour ago │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ 🤖 Bot Analytics ✓ │ │
│ │ Internal - Conversations │ │
│ │ Real-time │ │
│ └─────────────────────────────────┘ │
│ │
│ [+ Add Data Source] │
│ │
└─────────────────────────────────────────┘
```
---
### Sharing Dashboards
Share dashboards with your team:
1. Click **Share** on any dashboard
2. Set permissions
3. Copy link or invite by email
```
┌─────────────────────────────────────────┐
│ Share Dashboard [×] │
├─────────────────────────────────────────┤
│ │
│ "Sales Overview" Dashboard │
│ │
│ Share with: │
│ ┌─────────────────────────────────┐ │
│ │ Add people or groups... │ │
│ └─────────────────────────────────┘ │
│ │
│ People with access: │
│ ┌─────────────────────────────────┐ │
│ │ You (Owner) [Owner ▼] │ │
│ │ Sales Team [View ▼] │ │
│ │ john@company.com [Edit ▼] │ │
│ └─────────────────────────────────┘ │
│ │
│ Link sharing: │
│ ○ Off - Only specific people │
│ ● On - Anyone with link can view │
│ │
│ ┌─────────────────────────────────┐ │
│ │ https://analytics.bot/d/abc123 │ │
│ └─────────────────────────────────┘ │
│ [Copy Link] │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Done │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `R` | Refresh dashboard |
| `F` | Toggle fullscreen |
| `E` | Edit mode |
| `N` | New widget |
| `D` | Duplicate widget |
| `Delete` | Delete selected widget |
| `Ctrl+S` | Save dashboard |
| `Ctrl+P` | Print / Export PDF |
| `Ctrl+F` | Find / Filter |
| `/` | Quick search |
| `←` `→` | Navigate dashboards |
| `Escape` | Exit edit mode |
---
## Tips & Tricks
### Dashboard Design
💡 **Keep it simple** - 5-7 widgets per dashboard is optimal
💡 **Most important metrics at top** - Follow the F-pattern reading
💡 **Use consistent colors** - Same metric = same color across widgets
💡 **Group related widgets** - Keep sales metrics together
### Data Tips
💡 **Set up daily sync** for data sources that change frequently
💡 **Use filters** to let viewers customize their view
💡 **Add comparison periods** (vs last month, vs last year)
💡 **Include goals/targets** to show progress
### AI Tips
💡 **Ask "why" questions** - AI excels at explaining trends
💡 **Request predictions** for planning
💡 **Use AI for anomaly detection** - "What's unusual this month?"
💡 **Generate executive summaries** before board meetings
---
## Troubleshooting
### Dashboard not loading
**Possible causes:**
1. Data source disconnected
2. Query timeout
3. Permission issues
**Solution:**
1. Check data source status in Settings
2. Reduce date range or add filters
3. Verify you have dashboard access
4. Refresh the page
---
### Data not updating
**Possible causes:**
1. Sync schedule not running
2. Source data hasn't changed
3. Cache showing old data
**Solution:**
1. Click Refresh on the dashboard
2. Check data source sync status
3. Go to Settings → Clear cache
4. Verify source data has new records
---
### Charts showing wrong numbers
**Possible causes:**
1. Filter applied incorrectly
2. Wrong aggregation method
3. Date range mismatch
**Solution:**
1. Check widget filters
2. Verify aggregation (Sum vs Count vs Average)
3. Confirm date range matches expectations
4. Edit widget and review query
---
### Export not working
**Possible causes:**
1. Dashboard too large
2. Browser blocking download
3. Permission restrictions
**Solution:**
1. Try exporting individual widgets
2. Check browser download settings
3. Use a different export format
4. Contact administrator for permissions
---
## BASIC Integration
Use Analytics in your bot dialogs:
### Query Metrics
```basic
revenue = GET METRIC "total_revenue" FOR "this month"
lastMonth = GET METRIC "total_revenue" FOR "last month"
growth = ((revenue - lastMonth) / lastMonth) * 100
TALK "Revenue this month: $" + FORMAT(revenue, "#,##0")
TALK "Growth: " + FORMAT(growth, "#0.0") + "%"
```
### Generate Reports
```basic
HEAR period AS TEXT "Which period? (weekly/monthly/quarterly)"
report = GENERATE REPORT "Sales Summary" FOR period
TALK "Here's your " + period + " sales report:"
SEND FILE report.pdf
TALK "Key highlights:"
TALK report.summary
```
### Get AI Insights
```basic
insights = GET INSIGHTS FOR "Sales Dashboard"
TALK "Here are today's insights:"
FOR EACH insight IN insights.trends
TALK "📈 " + insight
NEXT
TALK "Alerts:"
FOR EACH alert IN insights.alerts
TALK "⚠️ " + alert
NEXT
```
### Create Dashboard Widget
```basic
widget = NEW OBJECT
widget.type = "line_chart"
widget.title = "Daily Active Users"
widget.source = "bot_analytics"
widget.xAxis = "date"
widget.yAxis = "active_users"
widget.dateRange = "last 30 days"
ADD WIDGET widget TO "Overview Dashboard"
TALK "Widget added successfully"
```
### Scheduled Reports
```basic
' This dialog runs on a schedule
report = GENERATE REPORT "Weekly Metrics" FOR "last 7 days"
recipients = ["ceo@company.com", "team@company.com"]
FOR EACH email IN recipients
SEND EMAIL TO email
SUBJECT "Weekly Metrics Report - " + TODAY
BODY "Please find attached the weekly metrics report."
ATTACHMENT report.pdf
NEXT
LOG "Weekly report sent to " + COUNT(recipients) + " recipients"
```
---
## See Also
- [Research App](./research.md) - Deep dive into data questions
- [Paper App](./paper.md) - Create reports from insights
- [How To: Monitor Your Bot](../how-to/monitor-sessions.md)
- [Talk to Data Template](../../chapter-02/templates.md)

View file

@ -1 +1,497 @@
# Calendar - Scheduling # Calendar - Scheduling
> **Your personal scheduling assistant**
![Calendar Flow](../../assets/suite/calendar-flow.svg)
---
## Overview
Calendar is your scheduling hub in General Bots Suite. Create events, manage appointments, schedule meetings, and let the AI help you find the perfect time. Calendar syncs with your other apps so you never miss an important date.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Calendar ◄ May 2025 ► [+ Event] │
├──────────────┬──────────────────────────────────────────────────────────┤
│ │ Mon Tue Wed Thu Fri Sat Sun │
│ CALENDARS │ ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┐ │
│ ─────────── │ │ │ │ │ 1 │ 2 │ 3 │ 4 │ │
│ ☑ Personal │ │ │ │ │ │ │ │ │ │
│ ☑ Work │ ├──────┼──────┼──────┼──────┼──────┼──────┼──────┤ │
│ ☑ Team │ │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ 11 │ │
│ ☐ Holidays │ │ │ 9AM │ │10AM │ │ │ │ │
│ │ │ │Team │ │Review│ │ │ │ │
│ ─────────── │ ├──────┼──────┼──────┼──────┼──────┼──────┼──────┤ │
│ │ │ 12 │ 13 │ 14 │ 15 │ 16 │ 17 │ 18 │ │
│ QUICK ADD │ │ │ 2PM │ │ │ ALL │ │ │ │
│ ─────────── │ │ │Call │ │ │ DAY │ │ │ │
│ [ Meeting ] │ ├──────┼──────┼──────┼──────┼──────┼──────┼──────┤ │
│ [ Call ] │ │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ 25 │ │
│ [ Reminder] │ │ 3PM │ │ 11AM │ │ │ │ │ │
│ │ │Demo │ │Lunch │ │ │ │ │ │
│ │ ├──────┼──────┼──────┼──────┼──────┼──────┼──────┤ │
│ │ │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ │ │
│ │ │ │ │ │ │ │ │ │ │
│ │ └──────┴──────┴──────┴──────┴──────┴──────┴──────┘ │
├──────────────┴──────────────────────────────────────────────────────────┤
│ Today: May 15, 2025 [Day][Week][Mo] │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Features
### Creating Events
**Method 1: Click on a Date**
1. Click any date on the calendar
2. A quick-add dialog appears
3. Type the event name
4. Press **Enter** or click **Create**
```
┌─────────────────────────────────────────┐
│ New Event - May 15, 2025 [×] │
├─────────────────────────────────────────┤
│ │
│ Event Name: │
│ ┌─────────────────────────────────┐ │
│ │ Team Planning Meeting │ │
│ └─────────────────────────────────┘ │
│ │
│ Time: [10:00 AM ▼] to [11:00 AM ▼] │
│ │
│ ┌─────────────┐ ┌─────────────────┐ │
│ │ Create │ │ More Options │ │
│ └─────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
**Method 2: Use the + Event Button**
1. Click **+ Event** in the top right corner
2. Fill in the full event details
3. Click **Save**
**Method 3: Ask the Bot**
Simply tell the bot what you want to schedule:
```
You: Schedule a meeting with Sarah tomorrow at 2pm
Bot: ✅ Event created:
📅 Meeting with Sarah
🕐 Tomorrow, 2:00 PM - 3:00 PM
Would you like to send her an invitation?
```
---
### Event Details
When creating or editing an event, you can set:
| Field | Description | Example |
|-------|-------------|---------|
| **Title** | Event name | Team Standup |
| **Date** | When it occurs | May 15, 2025 |
| **Time** | Start and end time | 10:00 AM - 10:30 AM |
| **Location** | Where it takes place | Conference Room A |
| **Description** | Additional details | Weekly sync meeting |
| **Calendar** | Which calendar | Work |
| **Reminder** | Alert before event | 15 minutes before |
| **Repeat** | Recurring pattern | Every Monday |
| **Attendees** | People to invite | sarah@company.com |
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Edit Event [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Title: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Weekly Team Standup │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Date: [May 15, 2025 ▼] │
│ │
│ Time: [10:00 AM ▼] to [10:30 AM ▼] ☐ All day │
│ │
│ Location: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Conference Room B │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Calendar: [Work ▼] │
│ │
│ Reminder: [15 minutes before ▼] │
│ │
│ Repeat: [Every Monday ▼] │
│ │
│ Attendees: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ sarah@company.com, john@company.com │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Description: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Daily sync to discuss progress and blockers │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Save │ │ Cancel │ │ Delete Event │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### View Modes
Switch between different calendar views using the buttons in the bottom right:
**Day View** - See one day in detail with hourly slots
```
┌─────────────────────────────────────────────────────────────────┐
│ Thursday, May 15, 2025 [◄] [►] │
├─────────────────────────────────────────────────────────────────┤
│ 8:00 │ │
│ 9:00 │ │
│ 10:00 │ ████████████████████████████████ │
│ │ █ Team Standup █ │
│ 11:00 │ ████████████████████████████████ │
│ 12:00 │ │
│ 1:00 │ │
│ 2:00 │ ██████████████████████████████████████████████████ │
│ │ █ Project Review Meeting █ │
│ 3:00 │ █ █ │
│ │ ██████████████████████████████████████████████████ │
│ 4:00 │ │
│ 5:00 │ │
└─────────────────────────────────────────────────────────────────┘
```
**Week View** - See a full week at a glance
**Month View** - See the entire month (default view)
---
### Managing Calendars
You can have multiple calendars for different purposes:
| Calendar | Color | Purpose |
|----------|-------|---------|
| Personal | 🔵 Blue | Personal appointments |
| Work | 🟢 Green | Work meetings and tasks |
| Team | 🟣 Purple | Shared team events |
| Holidays | 🔴 Red | Public holidays |
**To show/hide a calendar:**
1. Look at the **Calendars** panel on the left
2. Click the checkbox next to a calendar name
3. ☑ = visible, ☐ = hidden
**To create a new calendar:**
1. Click the **+** button next to "Calendars"
2. Enter a name for the calendar
3. Choose a color
4. Click **Create**
---
### AI Scheduling Assistant ✨
Let the AI help you find the best time for meetings:
**Finding Free Time**
```
You: When am I free this week?
Bot: You have the following available times this week:
📅 Monday: 11 AM - 12 PM, 3 PM - 5 PM
📅 Tuesday: All day
📅 Wednesday: 9 AM - 10 AM, 2 PM - 5 PM
📅 Thursday: 9 AM - 10 AM, 3 PM - 5 PM
📅 Friday: 9 AM - 12 PM
Would you like to schedule something?
```
**Smart Scheduling**
```
You: Find a time for a 1-hour meeting with Sarah next week
Bot: I found these times when both you and Sarah are free:
1. Monday at 2:00 PM
2. Wednesday at 10:00 AM
3. Thursday at 3:00 PM
Which works best? (Reply with 1, 2, or 3)
```
**Rescheduling**
```
You: Move my 2pm meeting to tomorrow
Bot: ✅ Moved "Project Review" from today 2 PM to
tomorrow (May 16) at 2 PM.
Should I notify the attendees?
```
---
### Reminders
Set reminders to get notified before events:
| Reminder Option | When You're Notified |
|-----------------|---------------------|
| At time of event | When event starts |
| 5 minutes before | 5 min early |
| 15 minutes before | 15 min early |
| 30 minutes before | 30 min early |
| 1 hour before | 1 hour early |
| 1 day before | Day before |
| 1 week before | Week before |
Reminders appear as:
- 🔔 Pop-up notification in the browser
- 📧 Email (if configured)
- 💬 Chat message from the bot
---
### Recurring Events
Set events to repeat automatically:
| Pattern | Example |
|---------|---------|
| Daily | Every day at 9 AM |
| Weekly | Every Monday |
| Bi-weekly | Every other Tuesday |
| Monthly | First Friday of each month |
| Yearly | Every March 15 |
| Custom | Mon, Wed, Fri |
```
┌─────────────────────────────────────────┐
│ Repeat [×] │
├─────────────────────────────────────────┤
│ │
│ Repeat: [Weekly ▼] │
│ │
│ On: │
│ [Mon] [Tue] [Wed] [Thu] [Fri] │
│ ☑ ☐ ☑ ☐ ☑ │
│ │
│ Ends: │
│ ○ Never │
│ ○ After [10] occurrences │
│ ● On [December 31, 2025] │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Done │ │ Cancel │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `T` | Go to today |
| `D` | Switch to day view |
| `W` | Switch to week view |
| `M` | Switch to month view |
| `←` | Previous day/week/month |
| `→` | Next day/week/month |
| `C` | Create new event |
| `E` | Edit selected event |
| `Delete` | Delete selected event |
| `/` | Search events |
| `Escape` | Close dialog |
---
## Tips & Tricks
### Quick Tips
💡 **Double-click** a date to create an all-day event
💡 **Drag and drop** events to reschedule them
💡 **Drag the bottom edge** of an event to change its duration
💡 **Hold Ctrl and click** to select multiple events
### Natural Language Input
The AI understands natural language for scheduling:
| Say This | Creates |
|----------|---------|
| "Lunch tomorrow at noon" | Event tomorrow, 12:00 PM |
| "Meeting in 2 hours" | Event 2 hours from now |
| "Dentist next Tuesday 3pm" | Event next Tuesday, 3:00 PM |
| "Weekly standup every Monday 9am" | Recurring event |
| "Birthday party May 20" | All-day event on May 20 |
### Color Coding
Use different calendars to color-code your schedule:
- 🔵 **Blue** - Regular meetings
- 🟢 **Green** - Personal time / breaks
- 🟡 **Yellow** - Deadlines
- 🔴 **Red** - Important / urgent
- 🟣 **Purple** - Tentative / pending
---
## Troubleshooting
### Event not showing up
**Possible causes:**
1. Calendar is hidden in the sidebar
2. Wrong date/time was entered
3. Event is on a different calendar
**Solution:**
1. Check all calendar checkboxes are ☑ enabled
2. Use the search function to find the event
3. Try refreshing the page
---
### Can't edit an event
**Possible causes:**
1. Event was created by someone else
2. Event is from a read-only calendar
3. You don't have edit permissions
**Solution:**
1. Contact the event organizer to make changes
2. Check if the calendar allows editing
3. Ask your administrator for permissions
---
### Reminders not working
**Possible causes:**
1. Browser notifications are blocked
2. Reminder wasn't set on the event
3. Browser tab is not open
**Solution:**
1. Allow notifications in browser settings:
- Click the 🔒 icon in the address bar
- Set Notifications to "Allow"
2. Edit the event and add a reminder
3. Keep the General Bots tab open or enable email reminders
---
### Calendar sync issues
**Possible causes:**
1. Network connection problem
2. External calendar not linked
3. Sync is paused
**Solution:**
1. Check your internet connection
2. Go to Settings → Integrations → Calendar
3. Click "Sync Now" to force a refresh
---
## BASIC Integration
Control the calendar from your bot dialogs:
### Create an Event
```basic
event = NEW OBJECT
event.title = "Team Meeting"
event.date = "2025-05-15"
event.startTime = "10:00"
event.endTime = "11:00"
event.location = "Room A"
result = CREATE EVENT event
TALK "Event created: " + result.id
```
### Get Today's Events
```basic
today = GET EVENTS TODAY
FOR EACH event IN today
TALK event.time + " - " + event.title
NEXT
```
### Find Free Slots
```basic
slots = FIND FREE TIME "next week", 60
TALK "Available 1-hour slots:"
FOR EACH slot IN slots
TALK slot.date + " at " + slot.time
NEXT
```
### Schedule with Attendees
```basic
HEAR email AS EMAIL "Who should I invite?"
event = NEW OBJECT
event.title = "Project Discussion"
event.date = TOMORROW
event.startTime = "14:00"
event.attendees = [email]
CREATE EVENT event
SEND INVITATION event
TALK "Meeting scheduled and invitation sent!"
```
---
## See Also
- [Meet App](./meet.md) - Video calls for your meetings
- [Tasks App](./tasks.md) - Turn events into actionable tasks
- [Mail App](./mail.md) - Send meeting invitations
- [How To: Create Your First Bot](../how-to/create-first-bot.md)

View file

@ -1 +1,724 @@
# Compliance - Security Scanner # Compliance - Security Scanner
> **Your privacy and security guardian**
![Compliance Flow](../../assets/suite/compliance-flow.svg)
---
## Overview
Compliance is the security and privacy management app in General Bots Suite. Monitor data handling, manage consent, respond to data subject requests, and ensure your bots comply with regulations like LGPD, GDPR, and CCPA. Compliance helps you protect user data and maintain trust.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Compliance [Scan Now] [Reports] [⚙️] [×] │
├──────────────┬──────────────────────────────────────────────────────────┤
│ │ │
│ SECTIONS │ COMPLIANCE DASHBOARD May 2025 │
│ ─────────── │ ════════════════════════════════════════════════════ │
│ │ │
│ 📊 Dashboard│ ┌─────────────────┐ ┌─────────────────┐ │
│ 🔍 Scanner │ │ Overall Score │ │ Open Requests │ │
│ 📋 Requests │ │ │ │ │ │
│ ✓ Consent │ │ 92% │ │ 7 │ │
│ 📁 Data Map │ │ ████████░░ │ │ ▼ 3 from │ │
│ 📜 Policies │ │ Good │ │ last week │ │
│ 📊 Reports │ └─────────────────┘ └─────────────────┘ │
│ ⚙️ Settings │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │
│ ─────────── │ │ Data Breaches │ │ Consent Rate │ │
│ │ │ │ │ │ │
│ REGULATIONS │ │ 0 │ │ 94.2% │ │
│ ─────────── │ │ Last 90 days │ │ ▲ 2.1% │ │
│ ☑ LGPD │ │ ✓ Excellent │ │ │ │
│ ☑ GDPR │ └─────────────────┘ └─────────────────┘ │
│ ☑ CCPA │ │
│ ☐ HIPAA │ RECENT ACTIVITY │
│ │ ───────────────── │
│ │ ● Data access request completed 10 min ago │
│ │ ● Privacy policy updated 2 hours ago │
│ │ ● Weekly compliance scan passed Yesterday │
│ │ ● New consent record added Yesterday │
│ │ │
└──────────────┴──────────────────────────────────────────────────────────┘
```
---
## Features
### Compliance Dashboard
The dashboard gives you an at-a-glance view of your compliance status:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ COMPLIANCE SCORE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 92% │
│ │
│ ████████████████████████████████████░░░░░░░░ │
│ │
│ 0% 50% 100% │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ BREAKDOWN BY AREA │
│ │
│ Data Protection ████████████████████████░░ 95% ✓ │
│ Consent Management ████████████████████████░░ 94% ✓ │
│ Access Controls █████████████████████░░░░░ 88% ⚠ │
│ Data Retention ████████████████████████░░ 96% ✓ │
│ Breach Response █████████████████████████░ 98% ✓ │
│ Documentation ██████████████████░░░░░░░░ 82% ⚠ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Score Meanings:**
| Score | Status | Action Needed |
|-------|--------|---------------|
| 90-100% | ✓ Excellent | Maintain current practices |
| 70-89% | ⚠ Good | Address minor issues |
| 50-69% | ⚠ Fair | Prioritize improvements |
| Below 50% | ✗ Poor | Immediate action required |
---
### Security Scanner
Automatically scan your bots and data for compliance issues:
#### Running a Scan
1. Click **Scan Now** in the top right
2. Select scan type
3. Wait for results
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Run Compliance Scan [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Scan Type: │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 🔍 Quick │ │ 🔍 Full │ │ 🔍 Custom │ │
│ │ ─────────── │ │ ─────────── │ │ ─────────── │ │
│ │ Basic checks │ │ Complete │ │ Select │ │
│ │ 5 minutes │ │ audit │ │ specific │ │
│ │ │ │ 30 minutes │ │ areas │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │
│ Scan Targets: │
│ ☑ All bots │
│ ☑ Knowledge bases │
│ ☑ User data │
│ ☑ Conversation logs │
│ ☐ External integrations (slower) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Start Scan │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Scan Results
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Scan Results - Full Scan May 15, 2025 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Status: ✓ Completed Duration: 28 minutes │
│ │
│ SUMMARY │
│ ─────── │
│ ✓ 145 checks passed │
│ ⚠ 3 warnings │
│ ✗ 1 critical issue │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ CRITICAL ISSUES │
│ ─────────────── │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ✗ Unencrypted PII found in conversation logs │ │
│ │ Location: support-bot / logs / 2025-05-10 │ │
│ │ Data types: Email addresses, phone numbers │ │
│ │ Records affected: 23 │ │
│ │ │ │
│ │ Recommendation: Enable automatic PII redaction │ │
│ │ [Fix Now] [View Details] [Dismiss] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ WARNINGS │
│ ──────── │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ⚠ Consent records older than 2 years │ │
│ │ 12 users have not renewed consent │ │
│ │ [Send Renewal Request] [View List] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ⚠ Data retention policy not configured for sales-bot │ │
│ │ Default retention (forever) is applied │ │
│ │ [Configure Policy] [View Details] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ⚠ Privacy policy link missing from hr-bot │ │
│ │ Users cannot access privacy information │ │
│ │ [Add Link] [View Details] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ [Export Report] [Schedule Next Scan] [Close] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Data Subject Requests (DSR)
Handle user requests for their data rights:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Data Subject Requests [+ New Request] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Filter: [All Types ▼] [All Status ▼] [Last 30 days ▼] │
│ │
│ PENDING REQUESTS (7) │
│ ──────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ #DSR-2025-0142 ⏱️ 2 days │ │
│ │ Type: 📥 Data Access │ │
│ │ User: john.doe@email.com │ │
│ │ Submitted: May 13, 2025 │ │
│ │ Due: May 28, 2025 (15 days remaining) │ │
│ │ │ │
│ │ [Process] [View] [Assign] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ #DSR-2025-0141 ⏱️ 5 days │ │
│ │ Type: 🗑️ Data Deletion │ │
│ │ User: sarah.smith@company.com │ │
│ │ Submitted: May 10, 2025 │ │
│ │ Due: May 25, 2025 (10 days remaining) │ │
│ │ │ │
│ │ [Process] [View] [Assign] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ #DSR-2025-0140 ⚠️ URGENT │ │
│ │ Type: ✋ Processing Objection │ │
│ │ User: mike.jones@email.com │ │
│ │ Submitted: May 5, 2025 │ │
│ │ Due: May 20, 2025 (5 days remaining) │ │
│ │ │ │
│ │ [Process] [View] [Assign] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Request Types
| Type | Icon | Description | Deadline |
|------|------|-------------|----------|
| **Data Access** | 📥 | User wants copy of their data | 15-30 days |
| **Data Deletion** | 🗑️ | User wants data erased | 15-30 days |
| **Data Portability** | 📤 | User wants data in machine format | 15-30 days |
| **Rectification** | ✏️ | User wants to correct data | 15-30 days |
| **Processing Objection** | ✋ | User objects to data processing | Immediate |
| **Consent Withdrawal** | 🚫 | User withdraws consent | Immediate |
#### Processing a Request
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Process Request #DSR-2025-0142 [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ REQUEST DETAILS │
│ ─────────────── │
│ Type: Data Access Request │
│ User: john.doe@email.com │
│ Submitted: May 13, 2025 │
│ Deadline: May 28, 2025 │
│ │
│ User's Message: │
│ "I would like to receive a copy of all personal data you have │
│ about me, including conversation history and any profiles." │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ DATA FOUND │
│ ────────── │
│ ☑ User Profile View ▶ │
│ Name, email, phone, preferences │
│ │
│ ☑ Conversation History View ▶ │
│ 45 conversations across 3 bots │
│ │
│ ☑ Consent Records View ▶ │
│ Marketing consent, Terms acceptance │
│ │
│ ☑ Activity Logs View ▶ │
│ Login history, bot interactions │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ ACTIONS │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📦 Generate │ │ ✉️ Send to │ │ ✓ Mark │ │
│ │ Data Package │ │ User │ │ Complete │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Consent Management
Track and manage user consent:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Consent Management [+ Consent Type] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ CONSENT TYPES │
│ ───────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Terms of Service │ │
│ │ Required: Yes │ │
│ │ Users consented: 1,234 (100%) │ │
│ │ ████████████████████████████████████████████████████████████ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Marketing Communications │ │
│ │ Required: No │ │
│ │ Users consented: 892 (72%) │ │
│ │ ████████████████████████████████████████░░░░░░░░░░░░░░░░░░░░ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Data Analytics │ │
│ │ Required: No │ │
│ │ Users consented: 1,089 (88%) │ │
│ │ █████████████████████████████████████████████████░░░░░░░░░░░ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Third-Party Sharing │ │
│ │ Required: No │ │
│ │ Users consented: 456 (37%) │ │
│ │ ██████████████████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Consent Record Details
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Consent Record [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User: john.doe@email.com │
│ User ID: usr_abc123 │
│ │
│ CONSENT STATUS │
│ ────────────── │
│ │
│ ┌──────────────────────────────┬──────────┬──────────────────────┐ │
│ │ Consent Type │ Status │ Date │ │
│ ├──────────────────────────────┼──────────┼──────────────────────┤ │
│ │ Terms of Service │ ✓ Given │ Jan 15, 2025 │ │
│ │ Marketing Communications │ ✓ Given │ Jan 15, 2025 │ │
│ │ Data Analytics │ ✓ Given │ Jan 15, 2025 │ │
│ │ Third-Party Sharing │ ✗ Denied │ Jan 15, 2025 │ │
│ └──────────────────────────────┴──────────┴──────────────────────┘ │
│ │
│ CONSENT HISTORY │
│ ─────────────── │
│ │
│ ● Jan 15, 2025 10:32 AM - Initial consent given via web chat │
│ IP: 192.168.1.100 | Browser: Chrome 120 │
│ │
│ ● Mar 22, 2025 3:15 PM - Marketing consent withdrawn │
│ Channel: Email preference center │
│ │
│ ● Mar 25, 2025 9:00 AM - Marketing consent re-given │
│ Channel: WhatsApp bot interaction │
│ │
│ [Export History] [Revoke All Consent] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Data Mapping
See where personal data is stored:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Data Map [Export] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ DATA CATEGORIES │
│ ─────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 👤 Personal Identifiers │ │
│ │ Names, email addresses, phone numbers │ │
│ │ │ │
│ │ Stored in: │ │
│ │ ├── 🗄️ users table (PostgreSQL) │ │
│ │ ├── 💬 conversation_logs │ │
│ │ └── 📧 email_subscriptions │ │
│ │ │ │
│ │ Retention: 3 years after last activity │ │
│ │ Encryption: ✓ At rest and in transit │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 💬 Communication Data │ │
│ │ Chat messages, emails, call logs │ │
│ │ │ │
│ │ Stored in: │ │
│ │ ├── 💬 conversation_logs │ │
│ │ ├── 📁 MinIO (attachments) │ │
│ │ └── 🔍 Qdrant (vector embeddings) │ │
│ │ │ │
│ │ Retention: 1 year │ │
│ │ Encryption: ✓ At rest and in transit │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 🍪 Behavioral Data │ │
│ │ Page views, clicks, preferences │ │
│ │ │ │
│ │ Stored in: │ │
│ │ ├── 📊 analytics_events (InfluxDB) │ │
│ │ └── 🗄️ user_preferences │ │
│ │ │ │
│ │ Retention: 90 days │ │
│ │ Encryption: ✓ At rest │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Policy Management
Manage your compliance policies:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Policies [+ New Policy] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ACTIVE POLICIES │
│ ─────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📜 Privacy Policy [Edit] │ │
│ │ Version: 2.3 | Last updated: May 1, 2025 │ │
│ │ Status: ● Published │ │
│ │ URL: https://company.com/privacy │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📜 Data Retention Policy [Edit] │ │
│ │ Version: 1.5 | Last updated: Apr 15, 2025 │ │
│ │ Status: ● Published │ │
│ │ Applied to: All bots │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📜 Cookie Policy [Edit] │ │
│ │ Version: 1.2 | Last updated: Mar 20, 2025 │ │
│ │ Status: ● Published │ │
│ │ URL: https://company.com/cookies │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ DATA RETENTION RULES │
│ ──────────────────── │
│ │
│ ┌──────────────────────────────┬───────────────┬──────────────────┐ │
│ │ Data Type │ Retention │ Action │ │
│ ├──────────────────────────────┼───────────────┼──────────────────┤ │
│ │ Conversation logs │ 1 year │ Auto-delete │ │
│ │ User profiles │ 3 years │ Anonymize │ │
│ │ Analytics data │ 90 days │ Auto-delete │ │
│ │ Consent records │ 5 years │ Archive │ │
│ │ Audit logs │ 7 years │ Archive │ │
│ └──────────────────────────────┴───────────────┴──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `S` | Start scan |
| `R` | View reports |
| `D` | Open data map |
| `P` | View policies |
| `N` | New request |
| `/` | Search |
| `Ctrl+E` | Export report |
| `Escape` | Close dialog |
---
## Tips & Tricks
### Staying Compliant
💡 **Schedule regular scans** - Weekly scans catch issues early
💡 **Set up alerts** - Get notified of critical issues immediately
💡 **Document everything** - Keep records of all compliance decisions
💡 **Train your team** - Everyone should understand data handling rules
### Handling Requests
💡 **Respond quickly** - Start processing within 24 hours
💡 **Verify identity** - Confirm requestor is the data subject
💡 **Be thorough** - Check all data sources before responding
💡 **Keep records** - Document how each request was handled
### Data Protection
💡 **Minimize data collection** - Only collect what you need
💡 **Enable encryption** - Protect data at rest and in transit
💡 **Use anonymization** - Remove PII when possible
💡 **Regular audits** - Review who has access to what data
---
## Troubleshooting
### Scan finds false positives
**Possible causes:**
1. Pattern matching too aggressive
2. Test data flagged as real PII
3. Encrypted data misidentified
**Solution:**
1. Review and dismiss false positives
2. Add test data locations to exclusion list
3. Configure scan sensitivity in settings
4. Report issues to improve detection
---
### DSR deadline approaching
**Possible causes:**
1. Complex request requiring manual review
2. Data spread across multiple systems
3. Identity verification pending
**Solution:**
1. Prioritize the request immediately
2. Use automated data collection tools
3. Contact user if verification needed
4. Document reason if extension required
---
### Consent not recording
**Possible causes:**
1. Consent widget not configured
2. JavaScript error on page
3. Database connection issue
**Solution:**
1. Check consent configuration in settings
2. Test consent flow in preview mode
3. Check error logs for issues
4. Verify database connectivity
---
### Data not deleting automatically
**Possible causes:**
1. Retention policy not applied
2. Scheduled job not running
3. Data referenced by other records
**Solution:**
1. Verify policy is active and applied to bot
2. Check scheduled job status in settings
3. Review dependencies that prevent deletion
4. Manually delete if needed
---
## BASIC Integration
Use Compliance features in your dialogs:
### Check Consent
```basic
hasConsent = CHECK CONSENT user.id FOR "marketing"
IF hasConsent THEN
TALK "I can send you our newsletter!"
ELSE
TALK "Would you like to receive our newsletter?"
HEAR response AS BOOLEAN
IF response THEN
RECORD CONSENT user.id FOR "marketing"
TALK "Great! You're now subscribed."
END IF
END IF
```
### Request Data Access
```basic
TALK "I can help you access your personal data."
HEAR email AS EMAIL "Please confirm your email address"
IF email = user.email THEN
request = CREATE DSR REQUEST
TYPE "access"
USER user.id
EMAIL email
TALK "Your request #" + request.id + " has been submitted."
TALK "You'll receive your data within 15 days."
ELSE
TALK "Email doesn't match. Please contact support."
END IF
```
### Delete User Data
```basic
TALK "Are you sure you want to delete all your data?"
TALK "This action cannot be undone."
HEAR confirm AS BOOLEAN
IF confirm THEN
request = CREATE DSR REQUEST
TYPE "deletion"
USER user.id
TALK "Deletion request submitted: #" + request.id
TALK "Your data will be deleted within 30 days."
ELSE
TALK "No problem. Your data remains safe."
END IF
```
### Log Compliance Event
```basic
' Log when sensitive data is accessed
LOG COMPLIANCE EVENT
TYPE "data_access"
USER user.id
DATA_TYPE "order_history"
REASON "User requested order status"
BOT "support"
TALK "Here's your order history..."
```
---
## API Endpoint: /api/compliance
The Compliance API allows programmatic access to compliance features.
### Endpoints Summary
| Endpoint | Method | Description |
|----------|--------|-------------|
| `/api/compliance/scan` | POST | Start a compliance scan |
| `/api/compliance/scan/{id}` | GET | Get scan results |
| `/api/compliance/dsr` | POST | Create DSR request |
| `/api/compliance/dsr/{id}` | GET | Get DSR status |
| `/api/compliance/consent` | POST | Record consent |
| `/api/compliance/consent/{userId}` | GET | Get user consent |
| `/api/compliance/report` | GET | Generate compliance report |
### Authentication
All endpoints require API key authentication:
```
Authorization: Bearer your-api-key
```
### Example: Check User Consent
```
GET /api/compliance/consent/usr_abc123
Response:
{
"userId": "usr_abc123",
"consents": [
{
"type": "terms_of_service",
"status": "given",
"timestamp": "2025-01-15T10:32:00Z"
},
{
"type": "marketing",
"status": "withdrawn",
"timestamp": "2025-03-22T15:15:00Z"
}
]
}
```
---
## See Also
- [Analytics App](./analytics.md) - Monitor compliance metrics
- [Sources App](./sources.md) - Configure bot policies
- [How To: Monitor Your Bot](../how-to/monitor-sessions.md)
- [Privacy Template](../../chapter-02/templates.md)

View file

@ -1 +1,692 @@
# Designer - Visual Builder # Designer - Visual Builder
> **Your no-code bot building studio**
![Designer Flow](../../assets/suite/designer-flow.svg)
---
## Overview
Designer is the visual bot builder in General Bots Suite. Create conversation flows, design user interfaces, and build automations without writing code. Designer uses a drag-and-drop interface that makes bot development accessible to everyone.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Designer [Preview] [Deploy] [⚙️] [×] │
├──────────────┬──────────────────────────────────────────────────────────┤
│ │ ┌─────────────────────────────────────────────────────┐ │
│ COMPONENTS │ │ Flow: Customer Support │ │
│ ─────────── │ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────┐ │ ┌─────────────┐ │
│ │ 💬 Talk │ │ │ START │ │
│ └─────────┘ │ │ Trigger │ │
│ ┌─────────┐ │ └──────┬──────┘ │
│ │ 👂 Hear │ │ │ │
│ └─────────┘ │ ▼ │
│ ┌─────────┐ │ ┌─────────────┐ │
│ │ ❓ Ask │ │ │ 💬 Welcome │ │
│ └─────────┘ │ │ Message │ │
│ ┌─────────┐ │ └──────┬──────┘ │
│ │ ⚡ Action│ │ │ │
│ └─────────┘ │ ▼ │
│ ┌─────────┐ │ ┌─────────────┐ ┌─────────────┐ │
│ │ 🔀 Branch│ │ │ ❓ Ask for │──Yes─▶│ 🔧 Create │ │
│ └─────────┘ │ │ Issue │ │ Ticket │ │
│ ┌─────────┐ │ └──────┬──────┘ └─────────────┘ │
│ │ 🔄 Loop │ │ │ No │
│ └─────────┘ │ ▼ │
│ ┌─────────┐ │ ┌─────────────┐ │
│ │ 📊 Data │ │ │ 🔍 Search │ │
│ └─────────┘ │ │ KB │ │
│ │ └─────────────┘ │
│ ─────────── │ │
│ TEMPLATES │ ───────────────────────────────────────────────────── │
│ ─────────── │ [+] [-] [⌖] Zoom: 100% [Grid] [Snap] [Auto] │
│ 📋 FAQ Bot │ │
│ 📋 Support │ │
│ 📋 Sales │ │
└──────────────┴──────────────────────────────────────────────────────────┘
```
---
## Features
### Creating a New Flow
**Step 1: Open Designer**
1. Click the apps menu (⋮⋮⋮)
2. Select **Designer**
3. Click **+ New Flow**
```
┌─────────────────────────────────────────┐
│ Create New Flow [×] │
├─────────────────────────────────────────┤
│ │
│ Flow Name: │
│ ┌─────────────────────────────────┐ │
│ │ Customer Support │ │
│ └─────────────────────────────────┘ │
│ │
│ Description: │
│ ┌─────────────────────────────────┐ │
│ │ Handles customer inquiries │ │
│ └─────────────────────────────────┘ │
│ │
│ Start from: │
│ ○ Blank canvas │
│ ● Template │
│ [Support Bot ▼] │
│ ○ Import from file │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Create │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
**Step 2: Add Components**
Drag components from the left panel onto the canvas.
**Step 3: Connect Components**
Click and drag from one component's output to another's input.
---
### Component Types
#### Communication Components
| Component | Icon | Purpose |
|-----------|------|---------|
| **Talk** | 💬 | Send a message to the user |
| **Hear** | 👂 | Wait for user input |
| **Ask** | ❓ | Ask a question and capture response |
| **Show** | 🖼️ | Display an image, card, or media |
| **Menu** | 📋 | Show clickable options |
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 💬 TALK Component │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Message: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Hello! How can I help you today? │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Variations (AI will pick randomly): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Hi there! What can I do for you? │ │
│ │ Welcome! How may I assist you? │ │
│ │ Good day! What brings you here? │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ [+ Add Variation] │
│ │
│ Options: │
│ ☑ Use AI to personalize │
│ ☐ Include typing indicator │
│ ☐ Delay before sending: [0] seconds │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
```
┌─────────────────────────────────────────────────────────────────────────┐
│ ❓ ASK Component │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Question: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ What is your email address? │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Save response as: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ userEmail │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Expected type: [Email ▼] │
│ ┌────────────────┐ │
│ │ Text │ │
│ │ Number │ │
│ │ Email │ │
│ │ Phone │ │
│ │ Date │ │
│ │ Yes/No │ │
│ │ Multiple Choice│ │
│ └────────────────┘ │
│ │
│ If invalid, say: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Please enter a valid email address. │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
#### Logic Components
| Component | Icon | Purpose |
|-----------|------|---------|
| **Branch** | 🔀 | Conditional logic (if/else) |
| **Loop** | 🔄 | Repeat actions |
| **Switch** | 🔃 | Multiple conditions |
| **Wait** | ⏱️ | Pause execution |
| **End** | 🏁 | End the flow |
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 🔀 BRANCH Component │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Condition: │
│ │
│ IF [userType ▼] [equals ▼] [premium ] │
│ │
│ [+ Add condition (AND)] │
│ [+ Add condition (OR)] │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ✓ TRUE │ │ ✗ FALSE │ │
│ │ Path │ │ Path │ │
│ │ │ │ │ │
│ │ (Connect to │ │ (Connect to │ │
│ │ next step) │ │ next step) │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
#### Action Components
| Component | Icon | Purpose |
|-----------|------|---------|
| **Action** | ⚡ | Execute a BASIC keyword |
| **API Call** | 🌐 | Call external API |
| **Database** | 🗄️ | Query or update data |
| **Email** | ✉️ | Send an email |
| **Set Variable** | 📝 | Store a value |
```
┌─────────────────────────────────────────────────────────────────────────┐
│ ⚡ ACTION Component │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Action type: [BASIC Keyword ▼] │
│ │
│ Keyword: [CREATE LEAD ▼] │
│ │
│ Parameters: │
│ ┌──────────────────┬──────────────────────────────────────────────┐ │
│ │ name │ {{userName}} │ │
│ ├──────────────────┼──────────────────────────────────────────────┤ │
│ │ email │ {{userEmail}} │ │
│ ├──────────────────┼──────────────────────────────────────────────┤ │
│ │ source │ "chat" │ │
│ └──────────────────┴──────────────────────────────────────────────┘ │
│ [+ Add Parameter] │
│ │
│ Save result as: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ newLead │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ On error: │
│ ○ Stop flow and show error │
│ ● Continue to error path │
│ ○ Retry [3] times │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
#### AI Components
| Component | Icon | Purpose |
|-----------|------|---------|
| **AI Chat** | 🤖 | Natural language conversation |
| **Search KB** | 🔍 | Search knowledge base |
| **Generate** | ✨ | Generate text with AI |
| **Classify** | 🏷️ | Categorize user input |
| **Extract** | 📤 | Extract data from text |
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 🏷️ CLASSIFY Component │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Input to classify: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ {{userMessage}} │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Categories: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ support │ Customer needs help with a problem │ │
│ │ sales │ Customer interested in buying │ │
│ │ billing │ Payment or invoice questions │ │
│ │ feedback │ Customer giving feedback │ │
│ │ other │ Anything else │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ [+ Add Category] │
│ │
│ Save category as: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ intentCategory │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Working with the Canvas
#### Navigation
| Action | How To |
|--------|--------|
| **Pan** | Click and drag on empty space |
| **Zoom in** | Scroll up or click [+] |
| **Zoom out** | Scroll down or click [-] |
| **Fit to screen** | Click [⌖] or press `F` |
| **Select multiple** | Hold Shift and click |
| **Box select** | Hold Ctrl and drag |
#### Canvas Controls
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ [+] [-] [⌖] Zoom: 100% [Grid] [Snap] [Auto] │
│ ▲ ▲ ▲ ▲ ▲ ▲ ▲ │
│ │ │ │ │ │ │ │ │
│ Zoom Zoom Fit Current Show Snap Auto │
│ In Out View zoom % grid to grid arrange │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Using Variables
Variables store information during the conversation:
**Creating Variables**
1. Use **Set Variable** component
2. Or capture input with **Ask** component
3. Or receive from **Action** component
**Using Variables**
Reference variables with double curly braces: `{{variableName}}`
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Available Variables │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ SYSTEM VARIABLES (read-only) │
│ ───────────────────────────── │
│ {{user.name}} User's display name │
│ {{user.email}} User's email address │
│ {{user.phone}} User's phone number │
│ {{channel}} Current channel (web, whatsapp, etc) │
│ {{today}} Today's date │
│ {{now}} Current date and time │
│ {{botName}} Name of this bot │
│ │
│ FLOW VARIABLES (your variables) │
│ ───────────────────────────────── │
│ {{userEmail}} Captured in "Ask Email" step │
│ {{orderNumber}} Captured in "Ask Order" step │
│ {{intentCategory}} Set by "Classify" step │
│ {{searchResults}} From "Search KB" step │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Testing Your Flow
**Preview Mode**
1. Click **Preview** button
2. A chat window opens
3. Test the conversation
4. Watch the flow highlight active steps
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Preview: Customer Support [×] │
├────────────────────────────────────┬────────────────────────────────────┤
│ │ │
│ FLOW VISUALIZATION │ TEST CONVERSATION │
│ │ │
│ ┌─────────────┐ │ 🤖 Hello! How can I help you │
│ │ START │ │ today? │
│ └──────┬──────┘ │ │
│ │ │ You: I have a problem with │
│ ▼ │ my order │
│ ┌─────────────┐ │ │
│ │ 💬 Welcome │ ◄── Active │ 🤖 I'm sorry to hear that! │
│ └──────┬──────┘ │ What's your order number? │
│ │ │ │
│ ▼ │ You: ORD-12345 │
│ ┌─────────────┐ │ │
│ │ ❓ Ask for │ │ 🤖 Looking up order ORD-12345... │
│ │ Issue │ │ │
│ └─────────────┘ │ │
│ ├────────────────────────────────────┤
│ │ Type a message... [Send] │
├────────────────────────────────────┴────────────────────────────────────┤
│ Variables: orderNumber = "ORD-12345" [Clear] [Reset]│
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Deploying Your Flow
When your flow is ready:
1. Click **Deploy**
2. Choose deployment options
3. Confirm deployment
```
┌─────────────────────────────────────────┐
│ Deploy Flow [×] │
├─────────────────────────────────────────┤
│ │
│ Flow: Customer Support │
│ Version: 1.3 │
│ │
│ Deploy to: │
│ ☑ Production │
│ ☐ Staging only │
│ │
│ Activation: │
│ ● Immediate │
│ ○ Scheduled: [____] at [____] │
│ │
│ Triggers: │
│ ☑ When user says "help" │
│ ☑ When user says "support" │
│ ☑ As default fallback │
│ ☐ On schedule │
│ │
│ ─────────────────────────────────── │
│ │
│ Changes since last deploy: │
│ • Added email validation │
│ • Updated welcome message │
│ • Added new branch for billing │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Deploy Now │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
### Templates
Start faster with pre-built templates:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Flow Templates [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🔍 Search templates... │
│ │
│ POPULAR │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📋 FAQ Bot │ │ 🎫 Support │ │ 💰 Sales │ │
│ │ ─────────── │ │ ─────────── │ │ ─────────── │ │
│ │ Answer common │ │ Ticket │ │ Lead capture │ │
│ │ questions from │ │ creation and │ │ and │ │
│ │ knowledge base │ │ tracking │ │ qualification │ │
│ │ │ │ │ │ │ │
│ │ [Use Template] │ │ [Use Template] │ │ [Use Template] │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ │ 📅 Appointment │ │ 📝 Feedback │ │ 🚀 Onboarding │ │
│ │ ─────────── │ │ ─────────── │ │ ─────────── │ │
│ │ Schedule │ │ Collect │ │ New user │ │
│ │ meetings and │ │ customer │ │ welcome and │ │
│ │ appointments │ │ feedback │ │ setup guide │ │
│ │ │ │ │ │ │ │
│ │ [Use Template] │ │ [Use Template] │ │ [Use Template] │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Keyboard Shortcuts
### Canvas
| Shortcut | Action |
|----------|--------|
| `Space + Drag` | Pan canvas |
| `Ctrl + +` | Zoom in |
| `Ctrl + -` | Zoom out |
| `Ctrl + 0` | Reset zoom |
| `F` | Fit to screen |
| `G` | Toggle grid |
| `Delete` | Delete selected |
| `Ctrl + D` | Duplicate selected |
| `Ctrl + Z` | Undo |
| `Ctrl + Y` | Redo |
### Components
| Shortcut | Action |
|----------|--------|
| `T` | Add Talk component |
| `H` | Add Hear component |
| `A` | Add Ask component |
| `B` | Add Branch component |
| `E` | Edit selected component |
| `Ctrl + C` | Copy component |
| `Ctrl + V` | Paste component |
| `Ctrl + X` | Cut component |
### Flow
| Shortcut | Action |
|----------|--------|
| `Ctrl + S` | Save flow |
| `Ctrl + P` | Preview flow |
| `Ctrl + Enter` | Deploy flow |
| `Ctrl + E` | Export flow |
| `Ctrl + I` | Import flow |
---
## Tips & Tricks
### Design Tips
💡 **Keep flows simple** - Break complex flows into smaller sub-flows
💡 **Use descriptive names** - "Ask for Email" is better than "Step 3"
💡 **Add comments** - Right-click any component to add notes
💡 **Test often** - Preview after every few changes
### Organization Tips
💡 **Use folders** to organize related flows
💡 **Version your flows** - Save before major changes
💡 **Use templates** for consistent starting points
💡 **Color-code paths** - Use colors for different intents
### Performance Tips
💡 **Minimize API calls** - Cache results when possible
💡 **Use AI classification early** - Route users quickly
💡 **Set timeouts** - Don't let flows hang indefinitely
💡 **Handle errors** - Always add error paths
---
## Troubleshooting
### Flow not triggering
**Possible causes:**
1. Flow not deployed
2. Trigger words not matching
3. Another flow has priority
**Solution:**
1. Click Deploy and confirm it's active
2. Check trigger configuration
3. Review flow priority in settings
4. Test with exact trigger phrases
---
### Variables not working
**Possible causes:**
1. Typo in variable name
2. Variable not set yet in flow
3. Wrong scope
**Solution:**
1. Check spelling matches exactly (case-sensitive)
2. Ensure variable is set before being used
3. Use Preview mode to watch variable values
4. Check the Variables panel for current values
---
### Component errors
**Possible causes:**
1. Missing required configuration
2. Invalid connection
3. Action failed
**Solution:**
1. Click the red error icon for details
2. Fill in all required fields
3. Check that connections make logical sense
4. Review error logs in Preview mode
---
### Preview not matching production
**Possible causes:**
1. Changes not deployed
2. Different data in production
3. External service differences
**Solution:**
1. Deploy latest changes
2. Test with same data as production
3. Check API connections are identical
4. Review production logs
---
## BASIC Integration
Designer flows generate BASIC code. You can:
### View Generated Code
1. Right-click any component
2. Select "View Code"
```basic
' Generated from "Customer Support" flow
TALK "Hello! How can I help you today?"
HEAR userMessage AS TEXT
intent = CLASSIFY userMessage INTO ["support", "sales", "billing", "other"]
IF intent = "support" THEN
TALK "I'm sorry to hear you're having issues!"
HEAR orderNumber AS TEXT "What's your order number?"
result = SEARCH KB "order " + orderNumber
TALK result.answer
ELSE IF intent = "sales" THEN
' ... sales flow
END IF
```
### Mix Designer and Code
Use the **Code** component to add custom BASIC:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 💻 CODE Component │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ' Custom calculation │ │
│ │ discount = 0 │ │
│ │ │ │
│ │ IF userType = "premium" THEN │ │
│ │ discount = orderTotal * 0.15 │ │
│ │ ELSE IF orderTotal > 100 THEN │ │
│ │ discount = orderTotal * 0.05 │ │
│ │ END IF │ │
│ │ │ │
│ │ finalPrice = orderTotal - discount │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ [Validate Code] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## See Also
- [Sources App](./sources.md) - Manage prompts and templates
- [Chat App](./chat.md) - Test your flows
- [How To: Write Your First Dialog](../how-to/write-first-dialog.md)
- [BASIC Keywords Reference](../../chapter-06-gbdialog/keywords-reference.md)

View file

@ -1 +1,687 @@
# Mail - Email Client # Mail - Email Client
> **Your intelligent inbox**
![Mail Flow](../../assets/suite/mail-flow.svg)
---
## Overview
Mail is the email application in General Bots Suite. Read, compose, and organize your emails with AI assistance. Mail helps you write better emails, find important messages, and stay on top of your inbox without the clutter.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Mail [Compose] [🔍 Search] [⚙️] [×] │
├──────────────┬──────────────────────────────────────────────────────────┤
│ │ │
│ FOLDERS │ Inbox (23) [Refresh] [▼] │
│ ─────────── │ ═══════════════════════════════════════════════════ │
│ │ │
│ 📥 Inbox 23 │ ┌─────────────────────────────────────────────────────┐ │
│ ⭐ Starred │ │ ☐ ⭐ Sarah Johnson 10:32 AM │ │
│ 📤 Sent │ │ Q2 Report Review │ │
│ 📝 Drafts 2 │ │ Please review the attached Q2 report and... │ │
│ 🗑️ Trash │ └─────────────────────────────────────────────────────┘ │
│ │ ┌─────────────────────────────────────────────────────┐ │
│ ─────────── │ │ ☐ Mike Chen 9:15 AM │ │
│ │ │ Meeting Tomorrow │ │
│ LABELS │ │ Hi, just confirming our meeting tomorrow at... │ │
│ ─────────── │ └─────────────────────────────────────────────────────┘ │
│ 🔴 Urgent │ ┌─────────────────────────────────────────────────────┐ │
│ 🟢 Personal │ │ ☐ LinkedIn Yesterday │ │
│ 🔵 Work │ │ You have 5 new connection requests │ │
│ 🟡 Finance │ │ People are looking at your profile... │ │
│ │ └─────────────────────────────────────────────────────┘ │
│ ─────────── │ ┌─────────────────────────────────────────────────────┐ │
│ │ │ ☐ Newsletter Yesterday │ │
│ [+ Label] │ │ Weekly Tech Digest │ │
│ │ │ This week in tech: AI advances, new... │ │
│ │ └─────────────────────────────────────────────────────┘ │
│ │ │
│ │ ───────────────────────────────────────────────────── │
│ │ Showing 1-23 of 23 [◄ Prev] [Next ►] │
│ │ │
└──────────────┴──────────────────────────────────────────────────────────┘
```
---
## Features
### Reading Emails
**Opening an Email**
1. Click on any email in the list
2. The email opens in the reading pane
```
┌─────────────────────────────────────────────────────────────────────────┐
│ ← Back to Inbox [Reply] [Forward] [Delete] [⋮] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Q2 Report Review │
│ ════════════════════════════════════════════════════════════════════ │
│ │
│ From: Sarah Johnson <sarah.johnson@company.com>
│ To: You <you@company.com>
│ Date: May 15, 2025 at 10:32 AM │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ Hi, │
│ │
│ Please review the attached Q2 report and let me know if you have │
│ any questions. I've highlighted the key metrics on page 3. │
│ │
│ Key points: │
│ • Revenue increased 15% from Q1 │
│ • Customer acquisition cost decreased by 8% │
│ • Retention rate steady at 94% │
│ │
│ Looking forward to your feedback. │
│ │
│ Best, │
│ Sarah │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ ATTACHMENTS │
│ ┌────────────────────────────┐ │
│ │ 📄 Q2_Report_2025.pdf │ │
│ │ 2.4 MB │ │
│ │ [Download] [Preview] │ │
│ └────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Email Actions**
| Action | What It Does |
|--------|--------------|
| **Reply** | Respond to the sender |
| **Reply All** | Respond to all recipients |
| **Forward** | Send to someone else |
| **Archive** | Remove from inbox, keep searchable |
| **Delete** | Move to trash |
| **Star** | Mark as important |
| **Mark Unread** | Show as unread again |
---
### Composing Emails
**Starting a New Email**
1. Click **Compose** button
2. Fill in the fields
3. Click **Send**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ New Message [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ To: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ john.smith@company.com │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ [Cc] [Bcc] │
│ │
│ Subject: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Project Update - May 15 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ B I U │ • ≡ │ 🔗 │ │ │
│ ├─────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Hi John, │ │
│ │ │ │
│ │ Here's the weekly project update: │ │
│ │ │ │
│ │ • Completed the user testing phase │ │
│ │ • Fixed 12 bugs from QA │ │
│ │ • On track for next milestone │ │
│ │ │ │
│ │ Let me know if you have questions. │ │
│ │ │ │
│ │ Best, │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ 📎 project-timeline.pdf │ [×] │
│ │ 156 KB │ │
│ └────────────────────────────┘ │
│ [📎 Attach File] │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Send │ │ Save Draft │ │ ✨ AI Help │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Compose Fields**
| Field | Purpose | Required |
|-------|---------|----------|
| **To** | Primary recipient(s) | Yes |
| **Cc** | Carbon copy (visible to all) | No |
| **Bcc** | Blind copy (hidden from others) | No |
| **Subject** | Email topic | Recommended |
| **Body** | Your message | Yes |
| **Attachments** | Files to include | No |
---
### AI Email Assistant ✨
Let AI help you write better emails:
**Write an Email**
```
You: Write an email declining a meeting politely
Bot: Here's a draft:
Subject: Unable to Attend - Project Review Meeting
Hi [Name],
Thank you for the meeting invitation. Unfortunately, I have
a scheduling conflict and won't be able to attend the
project review meeting on [date].
Would it be possible to reschedule, or could someone share
the meeting notes with me afterward?
Thank you for understanding.
Best regards,
[Your name]
[📧 Use This] [✏️ Edit] [🔄 Regenerate]
```
**Improve Your Draft**
1. Select text in your email
2. Click **✨ AI Help**
3. Choose an option:
```
┌─────────────────────────────────────┐
│ ✨ AI Email Help │
├─────────────────────────────────────┤
│ │
│ [📝 Improve writing ] │
│ [✂️ Make it shorter ] │
│ [💼 Make it professional ] │
│ [😊 Make it friendlier ] │
│ [🔧 Fix grammar ] │
│ [📋 Add bullet points ] │
│ [🌐 Translate to... ] │
│ │
└─────────────────────────────────────┘
```
**Summarize Long Emails**
```
You: Summarize this email thread
Bot: 📋 Email Thread Summary (15 messages)
Topic: Q3 Budget Approval
Key Points:
• Marketing requested $50K increase for Q3
• Finance asked for detailed breakdown
• VP approved $35K with conditions
• Final approval pending from CEO
Action Items:
• You: Submit revised proposal by Friday
• Sarah: Schedule follow-up meeting
Decision: Partial approval ($35K of $50K requested)
```
**Smart Reply**
Get quick reply suggestions:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Smart Replies: │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────┐ │
│ │ Sounds good, I'll │ │ Thanks for the │ │ Let me check │ │
│ │ review it today. │ │ update! │ │ and get back │ │
│ │ │ │ │ │ to you. │ │
│ └─────────────────────┘ └─────────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Email Organization
#### Folders
| Folder | Purpose |
|--------|---------|
| **Inbox** | Incoming emails |
| **Starred** | Emails you've starred |
| **Sent** | Emails you've sent |
| **Drafts** | Unsent emails |
| **Trash** | Deleted emails (auto-deleted after 30 days) |
| **Archive** | Archived emails (searchable) |
| **Spam** | Suspected spam |
#### Labels
Create custom labels to organize emails:
1. Click **+ Label** in the sidebar
2. Enter a name
3. Choose a color
4. Click **Create**
**Apply Labels**
- Drag email to label in sidebar
- Or right-click email → **Add Label**
- Or use keyboard: `L` then select label
```
┌─────────────────────────────────────┐
│ Add Label [×] │
├─────────────────────────────────────┤
│ │
│ 🔍 Search labels... │
│ │
│ ☐ 🔴 Urgent │
│ ☑ 🟢 Personal │
│ ☐ 🔵 Work │
│ ☐ 🟡 Finance │
│ ☐ 🟣 Projects │
│ │
│ [+ Create New Label] │
│ │
│ ┌─────────────────────────────┐ │
│ │ Apply │ │
│ └─────────────────────────────┘ │
│ │
└─────────────────────────────────────┘
```
#### Filters
Create rules to automatically organize incoming emails:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Create Filter [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ WHEN EMAIL MATCHES │
│ ────────────────── │
│ │
│ From: [newsletter@ ] │
│ To: [ ] │
│ Subject: [ ] │
│ Has words: [ ] │
│ │
│ THEN DO THIS │
│ ──────────── │
│ │
│ ☑ Skip inbox (archive) │
│ ☐ Mark as read │
│ ☑ Apply label: [Newsletters ▼] │
│ ☐ Star it │
│ ☐ Delete it │
│ ☐ Forward to: [ ] │
│ │
│ ☑ Also apply to existing emails (45 matches) │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Create Filter │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Search
Find emails quickly with powerful search:
**Basic Search**
Type in the search box to find emails containing those words.
**Advanced Search**
Use operators for precise searches:
| Operator | Example | Finds |
|----------|---------|-------|
| `from:` | `from:sarah` | Emails from Sarah |
| `to:` | `to:john` | Emails sent to John |
| `subject:` | `subject:report` | Emails with "report" in subject |
| `has:attachment` | `has:attachment` | Emails with attachments |
| `is:starred` | `is:starred` | Starred emails |
| `is:unread` | `is:unread` | Unread emails |
| `label:` | `label:work` | Emails with "work" label |
| `after:` | `after:2025-05-01` | Emails after May 1, 2025 |
| `before:` | `before:2025-05-15` | Emails before May 15, 2025 |
**Combine Operators**
```
from:sarah has:attachment after:2025-05-01 subject:report
```
Finds: Emails from Sarah with attachments after May 1, 2025, with "report" in the subject.
---
### Attachments
**Viewing Attachments**
- Click **Preview** to view without downloading
- Click **Download** to save to your device
- Click the attachment name to open
**Supported Preview Types**
| Type | Extensions |
|------|------------|
| Documents | PDF, DOC, DOCX |
| Spreadsheets | XLS, XLSX, CSV |
| Images | JPG, PNG, GIF |
| Text | TXT, MD |
**Attachment Size Limits**
- Maximum single file: 25 MB
- Maximum total per email: 25 MB
---
## Keyboard Shortcuts
### Navigation
| Shortcut | Action |
|----------|--------|
| `J` | Next email |
| `K` | Previous email |
| `O` or `Enter` | Open email |
| `U` | Back to list |
| `G` then `I` | Go to Inbox |
| `G` then `S` | Go to Starred |
| `G` then `T` | Go to Sent |
| `G` then `D` | Go to Drafts |
### Actions
| Shortcut | Action |
|----------|--------|
| `C` | Compose new email |
| `R` | Reply |
| `A` | Reply all |
| `F` | Forward |
| `E` | Archive |
| `#` | Delete |
| `S` | Star/unstar |
| `L` | Add label |
| `V` | Move to folder |
| `Shift+U` | Mark unread |
### Selection
| Shortcut | Action |
|----------|--------|
| `X` | Select email |
| `*` then `A` | Select all |
| `*` then `N` | Deselect all |
| `*` then `R` | Select read |
| `*` then `U` | Select unread |
### Other
| Shortcut | Action |
|----------|--------|
| `/` | Search |
| `?` | Show shortcuts |
| `Escape` | Close dialog |
| `Ctrl+Enter` | Send email |
---
## Tips & Tricks
### Inbox Management
💡 **Use filters** to automatically organize newsletters and notifications
💡 **Archive instead of delete** - keeps emails searchable but clears inbox
💡 **Star important emails** you need to return to
💡 **Process emails once** - reply, archive, or delete immediately
### Writing Better Emails
💡 **Use AI to shorten** long emails - busy people appreciate brevity
💡 **Add a clear subject** that summarizes the email's purpose
💡 **Use bullet points** for lists and action items
💡 **Put the ask first** - don't bury your request at the bottom
### Search Tips
💡 **Search by sender** when you remember who sent something
💡 **Search attachments** with `has:attachment filename:report`
💡 **Search date ranges** when you remember when
💡 **Save frequent searches** as filters
### Productivity Tips
💡 **Check email at set times** instead of constantly
💡 **Use Smart Reply** for quick acknowledgments
💡 **Unsubscribe** from newsletters you don't read
💡 **Use templates** for repetitive responses
---
## Troubleshooting
### Emails not loading
**Possible causes:**
1. Internet connection lost
2. Email server temporarily unavailable
3. Browser cache issue
**Solution:**
1. Check your internet connection
2. Click Refresh to reload
3. Try clearing browser cache
4. Wait a few minutes and try again
---
### Can't send email
**Possible causes:**
1. Missing recipient address
2. Attachment too large
3. Email server issue
**Solution:**
1. Verify "To" field has valid email address
2. Reduce attachment size or use Drive link
3. Save as draft and try again later
4. Check email settings are configured
---
### Search not finding emails
**Possible causes:**
1. Typo in search terms
2. Email is in Trash or Spam
3. Using wrong search operators
**Solution:**
1. Try different keywords
2. Check Trash and Spam folders
3. Use simpler search terms
4. Try searching "All Mail"
---
### Attachments won't download
**Possible causes:**
1. File blocked by browser
2. Download folder full
3. File type blocked
**Solution:**
1. Check browser download settings
2. Clear space on your device
3. Right-click and "Save As"
4. Try a different browser
---
## BASIC Integration
Control Mail from your bot dialogs:
### Send an Email
```basic
email = NEW OBJECT
email.to = "john@company.com"
email.subject = "Meeting Reminder"
email.body = "Don't forget our meeting tomorrow at 2 PM."
SEND EMAIL email
TALK "Email sent to John!"
```
### Send with Attachment
```basic
email = NEW OBJECT
email.to = user.email
email.subject = "Your Report"
email.body = "Please find your report attached."
email.attachments = [reportFile]
SEND EMAIL email
TALK "Report sent to your email!"
```
### Check for New Emails
```basic
newEmails = GET EMAILS WHERE "is:unread"
IF COUNT(newEmails) > 0 THEN
TALK "You have " + COUNT(newEmails) + " unread emails."
TALK "Most recent from: " + newEmails[0].from
ELSE
TALK "No new emails!"
END IF
```
### Search Emails
```basic
HEAR query AS TEXT "What should I search for?"
results = SEARCH EMAILS query
IF COUNT(results) > 0 THEN
TALK "Found " + COUNT(results) + " emails:"
FOR i = 1 TO MIN(5, COUNT(results))
TALK "- " + results[i].subject + " from " + results[i].from
NEXT
ELSE
TALK "No emails found matching '" + query + "'"
END IF
```
### AI Email Drafting
```basic
HEAR recipient AS EMAIL "Who should I email?"
HEAR topic AS TEXT "What's the email about?"
HEAR tone AS TEXT "What tone? (formal/casual/friendly)"
draft = GENERATE EMAIL
TO recipient
ABOUT topic
TONE tone
TALK "Here's a draft:"
TALK draft.body
TALK ""
HEAR confirm AS BOOLEAN "Should I send it?"
IF confirm THEN
SEND EMAIL draft
TALK "Email sent!"
ELSE
TALK "No problem. Draft saved."
SAVE DRAFT draft
END IF
```
---
## Email Configuration
Configure email settings in your bot's config.csv:
| Setting | Description | Example |
|---------|-------------|---------|
| `MAIL_PROVIDER` | Email service | `gmail`, `outlook`, `smtp` |
| `MAIL_HOST` | SMTP server | `smtp.gmail.com` |
| `MAIL_PORT` | SMTP port | `587` |
| `MAIL_USER` | Email account | `bot@company.com` |
| `MAIL_FROM_NAME` | Display name | `Company Bot` |
---
## See Also
- [Calendar App](./calendar.md) - Schedule meetings from emails
- [Tasks App](./tasks.md) - Create tasks from emails
- [Paper App](./paper.md) - Draft longer documents
- [How To: Create Your First Bot](../how-to/create-first-bot.md)

View file

@ -1 +1,593 @@
# Meet - Video Calls # Meet - Video Calls
> **Your virtual meeting room**
![Meet Flow](../../assets/suite/meet-flow.svg)
---
## Overview
Meet is the video conferencing app in General Bots Suite. Host video calls, share your screen, collaborate in real-time, and let the AI take notes for you. Meet integrates seamlessly with Calendar so joining meetings is just one click away.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Meet [🎤 Mute] [📹 Video] [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ │ ┌───────────┐ │ │
│ │ │ │ │ │
│ │ │ 📹 │ │ │
│ │ │ You │ │ │
│ │ │ │ │ │
│ │ └───────────┘ │ │
│ │ │ │
│ │ Waiting for others to join... │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
├───────────────────────────┬─────────────────────────────────────────────┤
│ PARTICIPANTS (1) │ │
│ ───────────────── │ ┌─────────────────────────────────────┐ │
│ ● You (Host) │ │ Chat │ │
│ │ ├─────────────────────────────────────┤ │
│ │ │ │ │
│ │ │ Meeting started at 10:00 AM │ │
│ │ │ │ │
│ ───────────────── │ ├─────────────────────────────────────┤ │
│ [Invite People] │ │ Type a message... [Send] │ │
│ │ └─────────────────────────────────────┘ │
├───────────────────────────┴─────────────────────────────────────────────┤
│ [🎤] [📹] [🖥️ Share] [✋ Raise Hand] [💬 Chat] [⚙️] [📕 End Call] │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Features
### Starting a Meeting
**Method 1: Instant Meeting**
1. Open the **Meet** app from the apps menu
2. Click **Start Meeting**
3. Your meeting room is ready instantly
4. Share the link with participants
```
┌─────────────────────────────────────────┐
│ Start a Meeting [×] │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────┐ │
│ │ 🎥 Start Instant Meeting │ │
│ └─────────────────────────────────┘ │
│ │
│ ─────────── or ─────────── │
│ │
│ Meeting Link: │
│ ┌─────────────────────────────────┐ │
│ │ https://meet.bot/abc-defg-hij │ │
│ └─────────────────────────────────┘ │
│ [Copy Link] [Share via Email] │
│ │
│ ─────────── or ─────────── │
│ │
│ Join with Code: │
│ ┌─────────────────────────────────┐ │
│ │ Enter meeting code... │ │
│ └─────────────────────────────────┘ │
│ [Join Meeting] │
│ │
└─────────────────────────────────────────┘
```
**Method 2: From Calendar**
1. Click on a meeting event in Calendar
2. Click the **Join Meeting** button
3. You're connected automatically
**Method 3: Ask the Bot**
```
You: Start a video call
Bot: ✅ Meeting room created!
🔗 Link: https://meet.bot/abc-defg-hij
[Join Now] [Copy Link] [Send Invites]
```
---
### Joining a Meeting
**From a Link**
1. Click the meeting link you received
2. Allow camera and microphone access
3. Preview your video
4. Click **Join Now**
**From a Code**
1. Open the Meet app
2. Enter the meeting code (e.g., `abc-defg-hij`)
3. Click **Join Meeting**
```
┌─────────────────────────────────────────┐
│ Ready to Join? [×] │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ │
│ │ │ │
│ │ 📹 │ │
│ │ Your Preview │ │
│ │ │ │
│ └─────────────────────┘ │
│ │
│ [🎤 On] [📹 On] │
│ │
│ Your Name: │
│ ┌─────────────────────────────────┐ │
│ │ John Smith │ │
│ └─────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Join Now │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
### During the Meeting
#### Video Grid
When multiple people join, the video grid adjusts automatically:
**2 Participants**
```
┌──────────────────┬──────────────────┐
│ │ │
│ You │ Sarah │
│ │ │
└──────────────────┴──────────────────┘
```
**4 Participants**
```
┌──────────────────┬──────────────────┐
│ You │ Sarah │
├──────────────────┼──────────────────┤
│ John │ Mike │
└──────────────────┴──────────────────┘
```
**Speaker View** - Active speaker is shown large:
```
┌─────────────────────────────────────────┐
│ │
│ Active Speaker │
│ (Sarah) │
│ │
├─────────┬─────────┬─────────┬──────────┤
│ You │ John │ Mike │ Lisa │
└─────────┴─────────┴─────────┴──────────┘
```
---
#### Meeting Controls
| Button | Action | Shortcut |
|--------|--------|----------|
| 🎤 | Mute/Unmute microphone | `M` |
| 📹 | Turn camera on/off | `V` |
| 🖥️ | Share your screen | `S` |
| ✋ | Raise your hand | `H` |
| 💬 | Open/close chat | `C` |
| 👥 | Show participants | `P` |
| ⚙️ | Settings | `,` |
| 📕 | Leave/End meeting | `Ctrl+E` |
---
### Screen Sharing
Share your screen, a window, or a browser tab:
1. Click the **🖥️ Share** button
2. Choose what to share:
```
┌─────────────────────────────────────────────────────────────────┐
│ Share Your Screen [×] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ │ │ │ │ │
│ │ Entire │ │ Window │ │ Browser │ │
│ │ Screen │ │ │ │ Tab │ │
│ │ │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ☑ Share computer audio │
│ │
│ [Cancel] [Share] │
│ │
└─────────────────────────────────────────────────────────────────┘
```
3. Select the screen, window, or tab
4. Click **Share**
5. Click **Stop Sharing** when done
💡 **Tip:** Share a specific window to keep other content private.
---
### In-Meeting Chat
Send messages to all participants without interrupting the speaker:
```
┌─────────────────────────────────────┐
│ Meeting Chat [×] │
├─────────────────────────────────────┤
│ │
│ Sarah (10:05 AM) │
│ Can everyone see my screen? │
│ │
│ John (10:05 AM) │
│ Yes, looks good! │
│ │
│ You (10:06 AM) │
│ Perfect, let's continue. │
│ │
├─────────────────────────────────────┤
│ ┌───────────────────────┐ [Send] │
│ │ Type a message... │ │
│ └───────────────────────┘ │
└─────────────────────────────────────┘
```
**Chat Features:**
- Send messages to everyone
- Share links and files
- Use emoji reactions
- Chat history is saved
---
### AI Meeting Assistant ✨
Let the AI help during your meetings:
**Live Transcription**
```
┌─────────────────────────────────────────┐
│ 📝 Live Transcript │
├─────────────────────────────────────────┤
│ │
│ Sarah (10:05:23) │
│ "Let's review the Q2 numbers first" │
│ │
│ John (10:05:45) │
│ "Revenue is up 15% from last quarter" │
│ │
│ Sarah (10:06:12) │
│ "Great progress on the new product" │
│ │
│ ─── Transcribing... ─── │
│ │
└─────────────────────────────────────────┘
```
**Meeting Notes**
After the meeting, the AI generates:
- Summary of key points
- Action items with assignees
- Decisions made
- Follow-up tasks
```
You: Summarize today's meeting
Bot: 📋 Meeting Summary - Project Review
Duration: 45 minutes
Participants: You, Sarah, John, Mike
📌 Key Points:
• Q2 revenue up 15%
• New product launch on track for June
• Marketing budget approved
✅ Action Items:
• Sarah: Finalize launch materials by May 20
• John: Schedule customer interviews
• You: Review pricing proposal
📅 Next Meeting: May 22 at 10 AM
```
---
### Raise Hand
Get the speaker's attention without interrupting:
1. Click **✋ Raise Hand**
2. Your hand icon appears next to your name
3. Host sees a notification
4. Click again to lower your hand
```
┌───────────────────────────┐
│ PARTICIPANTS (4) │
│ ───────────────── │
│ ● Sarah (Host) │
│ ● You ✋ │ ◄── Hand raised
│ ● John │
│ ● Mike │
└───────────────────────────┘
```
---
### Meeting Settings
```
┌─────────────────────────────────────────┐
│ Meeting Settings [×] │
├─────────────────────────────────────────┤
│ │
│ AUDIO │
│ ───── │
│ Microphone: [Built-in Mic ▼] │
│ Speaker: [Built-in Speakers ▼] │
│ [🔊 Test Audio] │
│ │
│ VIDEO │
│ ───── │
│ Camera: [Built-in Camera ▼] │
│ [Mirror my video] ☑ │
│ [HD video] ☐ │
│ │
│ PREFERENCES │
│ ────────── │
│ [Mute on join] ☑ │
│ [Camera off on join] ☐ │
│ [Enable AI transcription] ☑ │
│ [Background blur] ☐ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Save │ │ Cancel │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
### Virtual Backgrounds
Change your background during the meeting:
1. Click **⚙️ Settings** → **Background**
2. Choose an option:
| Option | Description |
|--------|-------------|
| None | Show your real background |
| Blur | Blur your background |
| Office | Virtual office background |
| Nature | Nature scene background |
| Custom | Upload your own image |
---
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `M` | Mute/Unmute microphone |
| `V` | Turn camera on/off |
| `S` | Start/stop screen share |
| `H` | Raise/lower hand |
| `C` | Open/close chat |
| `P` | Show/hide participants |
| `F` | Toggle fullscreen |
| `Space` | Push to talk (while muted) |
| `Ctrl+D` | Leave meeting |
| `Ctrl+E` | End meeting (host only) |
---
## Tips & Tricks
### Before the Meeting
💡 **Test your audio and video** before important meetings
💡 **Use a headset** to avoid echo and improve audio quality
💡 **Check your lighting** - face a window or light source
💡 **Close unnecessary apps** to improve performance
### During the Meeting
💡 **Mute when not speaking** to reduce background noise
💡 **Use the chat** for links and details without interrupting
💡 **Pin important speakers** by clicking their video tile
💡 **Use reactions** (👍 👏 ❤️) for quick feedback
### For Hosts
💡 **Start recording early** so you don't forget
💡 **Admit participants** from the waiting room promptly
💡 **Mute disruptive participants** if needed
💡 **Share meeting notes** after the call
---
## Troubleshooting
### No video showing
**Possible causes:**
1. Camera is blocked by another app
2. Browser doesn't have camera permission
3. Camera is turned off in settings
**Solution:**
1. Close other apps using the camera
2. Click the 🔒 icon in browser address bar → Allow Camera
3. Check that the camera is selected in Settings
4. Try refreshing the page
---
### No one can hear me
**Possible causes:**
1. Microphone is muted
2. Wrong microphone selected
3. Browser doesn't have microphone permission
**Solution:**
1. Click the 🎤 button to unmute
2. Go to Settings → Audio → Select correct microphone
3. Click 🔒 in browser → Allow Microphone
4. Test with "Test Audio" button in settings
---
### Can't hear others
**Possible causes:**
1. Speaker volume is too low
2. Wrong speaker device selected
3. System volume is muted
**Solution:**
1. Check system volume (not muted)
2. Go to Settings → Audio → Select correct speaker
3. Click "Test Audio" to verify
4. Try using headphones
---
### Screen share not working
**Possible causes:**
1. Browser doesn't have screen share permission
2. System privacy settings block screen recording
3. Another app is already sharing
**Solution:**
1. Allow screen share in browser permissions
2. On Mac: System Preferences → Security → Screen Recording → Allow browser
3. On Windows: Check privacy settings
4. Close any other screen sharing software
---
### Poor video quality
**Possible causes:**
1. Slow internet connection
2. Too many participants with video on
3. Computer is overloaded
**Solution:**
1. Close other apps and browser tabs
2. Turn off HD video in settings
3. Ask some participants to turn off video
4. Move closer to your WiFi router
---
## BASIC Integration
Control Meet from your bot dialogs:
### Create a Meeting
```basic
meeting = CREATE MEETING "Project Discussion"
TALK "Meeting created!"
TALK "Link: " + meeting.link
TALK "Code: " + meeting.code
```
### Schedule a Meeting
```basic
meeting = NEW OBJECT
meeting.title = "Weekly Standup"
meeting.date = "2025-05-20"
meeting.time = "10:00"
meeting.duration = 30
meeting.attendees = ["sarah@company.com", "john@company.com"]
result = SCHEDULE MEETING meeting
SEND INVITATION result
```
### Get Meeting Transcript
```basic
transcript = GET MEETING TRANSCRIPT meetingId
TALK "Meeting Summary:"
TALK transcript.summary
TALK "Action Items:"
FOR EACH item IN transcript.actionItems
TALK "- " + item.task + " (" + item.assignee + ")"
NEXT
```
### Join a Meeting Programmatically
```basic
HEAR code AS TEXT "Enter the meeting code"
IF LEN(code) > 0 THEN
JOIN MEETING code
TALK "Joining meeting..."
ELSE
TALK "No code provided"
END IF
```
---
## See Also
- [Calendar App](./calendar.md) - Schedule meetings
- [Chat App](./chat.md) - Continue conversations after meetings
- [Mail App](./mail.md) - Send meeting invitations
- [How To: Create Your First Bot](../how-to/create-first-bot.md)

View file

@ -1 +1,681 @@
# Paper - AI Writing # Paper - AI Writing
> **Your intelligent document editor**
![Paper Flow](../../assets/suite/paper-flow.svg)
---
## Overview
Paper is the AI-powered writing app in General Bots Suite. Create documents, reports, letters, and more with help from your AI assistant. Paper understands context, suggests improvements, and helps you write faster and better.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Paper [Share] [Export ▼] [⚙️] [×] │
├──────────────┬──────────────────────────────────────────────────────────┤
│ │ ┌─────────────────────────────────────────────────────┐ │
│ DOCUMENTS │ │ B I U S │ H1 H2 H3 │ • ≡ ☐ │ 🔗 📷 📊 │ ✨ AI │ │
│ ─────────── │ └─────────────────────────────────────────────────────┘ │
│ │ │
│ 📄 Untitled │ ┌─────────────────────────────────────────────────────┐ │
│ 📄 Report │ │ │ │
│ 📄 Notes │ │ Quarterly Report │ │
│ 📄 Letter │ │ ═══════════════════ │ │
│ │ │ │ │
│ ─────────── │ │ Executive Summary │ │
│ 📁 Projects │ │ ────────────────── │ │
│ 📄 Plan │ │ This quarter showed significant growth across │ │
│ 📄 Budget │ │ all business units. Revenue increased by 15% │ │
│ │ │ compared to the previous quarter, driven by │ │
│ ─────────── │ │ strong performance in the enterprise segment. │ │
│ │ │ │ │
│ [+ New Doc] │ │ Key Highlights │ │
│ │ │ ────────────── │ │
│ │ │ • Revenue: $2.4M (+15%) │ │
│ │ │ • New customers: 47 │ │
│ │ │ • Customer retention: 94% │ │
│ │ │ │ │
│ │ │ | │ │
│ │ │ │ │
│ │ └─────────────────────────────────────────────────────┘ │
├──────────────┴──────────────────────────────────────────────────────────┤
│ Words: 156 │ Characters: 892 │ Reading time: 1 min │ Saved ✓ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Features
### Creating a New Document
**Method 1: Click New Document**
1. Click **+ New Doc** in the left sidebar
2. Start typing immediately
3. Document auto-saves as you work
**Method 2: From Template**
1. Click **+ New Doc**
2. Select **From Template**
3. Choose a template:
```
┌─────────────────────────────────────────────────────────────────┐
│ Choose a Template [×] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ │ │ ═══════ │ │ ┌───┐ │ │
│ │ Blank │ │ Report │ │ │ │ │ │
│ │ │ │ ─────── │ │ Letter │ │
│ │ │ │ • • • │ │ └───┘ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Meeting │ │ ☐ ☐ ☐ │ │ 📧 │ │
│ │ Notes │ │ Checklist │ │ Email │ │
│ │ ─────── │ │ ☐ ☐ ☐ │ │ Template │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Project │ │ Resume │ │ Invoice │ │
│ │ Proposal │ │ / CV │ │ │ │
│ │ │ │ │ │ │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
**Method 3: Ask the Bot**
```
You: Create a new document for meeting notes
Bot: ✅ Created new document: "Meeting Notes"
I've set up a template with:
• Date and attendees section
• Agenda
• Discussion points
• Action items
[Open Document]
```
---
### Formatting Text
Use the toolbar or keyboard shortcuts to format your text:
```
┌───────────────────────────────────────────────────────────────────────┐
│ B I U S │ H1 H2 H3 │ • ≡ ☐ │ 🔗 📷 📊 │ ✨ AI │
│ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ ▲ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │
│Bold │ │Strike │ │ │ Bullet│ │ Link│ │ AI │ │
│ Italic │ │ Heading │ │Checklist │Image│ Help │ │
│ Underline │ 2 │ Numbered │ Table │
│ Heading 1 Heading 3 List │
└───────────────────────────────────────────────────────────────────────┘
```
| Format | Toolbar | Shortcut |
|--------|---------|----------|
| **Bold** | B | `Ctrl+B` |
| *Italic* | I | `Ctrl+I` |
| Underline | U | `Ctrl+U` |
| ~~Strikethrough~~ | S | `Ctrl+Shift+S` |
| Heading 1 | H1 | `Ctrl+1` |
| Heading 2 | H2 | `Ctrl+2` |
| Heading 3 | H3 | `Ctrl+3` |
| Bullet list | • | `Ctrl+Shift+8` |
| Numbered list | ≡ | `Ctrl+Shift+7` |
| Checklist | ☐ | `Ctrl+Shift+9` |
| Link | 🔗 | `Ctrl+K` |
---
### AI Writing Assistant ✨
Paper's AI assistant helps you write better and faster. Click the **✨ AI** button or type `/` to access AI features.
**Generate Content**
```
You: /write an introduction about renewable energy
Bot: Here's a draft introduction:
"Renewable energy has emerged as one of the most
significant solutions to our global climate challenges.
As fossil fuel reserves dwindle and environmental
concerns grow, solar, wind, and hydroelectric power
offer sustainable alternatives that can power our
future while protecting our planet."
[Insert] [Regenerate] [Edit]
```
**Improve Writing**
1. Select text you want to improve
2. Click **✨ AI** → **Improve**
3. Choose an option:
```
┌─────────────────────────────────────┐
│ ✨ AI Assistant │
├─────────────────────────────────────┤
│ │
│ What would you like to do? │
│ │
│ [📝 Improve writing ] │
│ [✂️ Make it shorter ] │
│ [📖 Make it longer ] │
│ [🎯 Make it clearer ] │
│ [💼 Make it professional ] │
│ [😊 Make it friendly ] │
│ [🔧 Fix grammar ] │
│ [🌐 Translate ] │
│ │
└─────────────────────────────────────┘
```
**Summarize**
```
You: /summarize this document
Bot: 📋 Document Summary:
This quarterly report covers company performance
from January to March 2025. Key points:
• Revenue grew 15% to $2.4M
• 47 new customers acquired
• 94% customer retention rate
• Enterprise segment drove growth
The report recommends expanding the sales team
and investing in product development.
```
**Generate Ideas**
```
You: /brainstorm topics for a blog post about productivity
Bot: 💡 Here are 10 blog post ideas:
1. "5 Morning Habits of Highly Productive People"
2. "The Pomodoro Technique: A Complete Guide"
3. "How to Eliminate Distractions While Working"
4. "Time Blocking: Schedule Your Way to Success"
5. "The 2-Minute Rule: Small Tasks, Big Impact"
6. "Digital Minimalism for Better Focus"
7. "Why Multitasking is Killing Your Productivity"
8. "Building a Productive Workspace at Home"
9. "The Science of Taking Breaks"
10. "Productivity Apps That Actually Work"
Would you like me to expand on any of these?
```
---
### Document Organization
**Folders**
Organize your documents into folders:
1. Right-click in the sidebar
2. Select **New Folder**
3. Name your folder
4. Drag documents into it
```
┌──────────────────────┐
│ DOCUMENTS │
│ ─────────── │
│ 📄 Quick Notes │
│ 📄 Ideas │
│ │
│ 📁 Work │
│ 📄 Report Q1 │
│ 📄 Report Q2 │
│ 📄 Presentation │
│ │
│ 📁 Personal │
│ 📄 Goals 2025 │
│ 📄 Journal │
│ │
│ 📁 Archive │
└──────────────────────┘
```
**Search Documents**
Find documents quickly:
1. Press `Ctrl+P` or click the search icon
2. Type document name or content
3. Press Enter to open
```
┌─────────────────────────────────────────┐
│ 🔍 Search documents... │
├─────────────────────────────────────────┤
│ │
│ Recent: │
│ 📄 Quarterly Report 2h ago │
│ 📄 Meeting Notes - May 15 1d ago │
│ 📄 Project Proposal 3d ago │
│ │
│ All documents matching "report": │
│ 📄 Quarterly Report │
│ 📄 Annual Report 2024 │
│ 📄 Expense Report Template │
│ │
└─────────────────────────────────────────┘
```
---
### Collaboration
**Share a Document**
1. Click **Share** button
2. Enter email addresses
3. Set permissions
4. Click **Send**
```
┌─────────────────────────────────────────┐
│ Share Document [×] │
├─────────────────────────────────────────┤
│ │
│ Share with: │
│ ┌─────────────────────────────────┐ │
│ │ sarah@company.com │ │
│ └─────────────────────────────────┘ │
│ [+ Add more people] │
│ │
│ Permission: [Can edit ▼] │
│ ┌────────────────┐ │
│ │ Can edit │ │
│ │ Can comment │ │
│ │ Can view │ │
│ └────────────────┘ │
│ │
│ ☐ Notify people via email │
│ │
│ ─────────────────────────────── │
│ │
│ Or share via link: │
│ ┌─────────────────────────────────┐ │
│ │ https://paper.bot/doc/abc123 │ │
│ └─────────────────────────────────┘ │
│ [Copy Link] │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Share │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
**Permissions Explained**
| Permission | Can View | Can Comment | Can Edit |
|------------|----------|-------------|----------|
| **View** | ✅ | ❌ | ❌ |
| **Comment** | ✅ | ✅ | ❌ |
| **Edit** | ✅ | ✅ | ✅ |
---
### Export Options
Export your documents to different formats:
1. Click **Export ▼**
2. Choose a format:
| Format | Best For |
|--------|----------|
| **PDF** | Printing, sharing final versions |
| **Word (.docx)** | Editing in Microsoft Word |
| **Markdown (.md)** | Technical documentation |
| **Plain Text (.txt)** | Simple text without formatting |
| **HTML** | Web publishing |
```
┌─────────────────────────────────────────┐
│ Export Document [×] │
├─────────────────────────────────────────┤
│ │
│ Export as: │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ PDF │ │ Word │ │
│ │ 📄 │ │ 📝 │ │
│ └────────────┘ └────────────┘ │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ Markdown │ │ Text │ │
│ │ # │ │ Aa │ │
│ └────────────┘ └────────────┘ │
│ │
│ Options: │
│ ☑ Include headers and footers │
│ ☐ Include comments │
│ ☑ Include page numbers │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Export │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
### Version History
Paper automatically saves versions of your document:
1. Click **⚙️** → **Version History**
2. See all saved versions
3. Click to preview
4. Restore if needed
```
┌─────────────────────────────────────────┐
│ Version History [×] │
├─────────────────────────────────────────┤
│ │
│ ● Current version │
│ Today, 3:45 PM │
│ │
│ ○ Today, 2:30 PM │
│ Added executive summary │
│ │
│ ○ Today, 11:15 AM │
│ Initial draft │
│ │
│ ○ Yesterday, 4:00 PM │
│ Created document │
│ │
│ ───────────────────────────────── │
│ │
│ [Preview] [Restore This Version] │
│ │
└─────────────────────────────────────────┘
```
---
## Keyboard Shortcuts
### Text Formatting
| Shortcut | Action |
|----------|--------|
| `Ctrl+B` | Bold |
| `Ctrl+I` | Italic |
| `Ctrl+U` | Underline |
| `Ctrl+Shift+S` | Strikethrough |
| `Ctrl+1` | Heading 1 |
| `Ctrl+2` | Heading 2 |
| `Ctrl+3` | Heading 3 |
| `Ctrl+0` | Normal text |
### Lists & Structure
| Shortcut | Action |
|----------|--------|
| `Ctrl+Shift+7` | Numbered list |
| `Ctrl+Shift+8` | Bullet list |
| `Ctrl+Shift+9` | Checklist |
| `Tab` | Indent |
| `Shift+Tab` | Outdent |
### Editing
| Shortcut | Action |
|----------|--------|
| `Ctrl+Z` | Undo |
| `Ctrl+Y` | Redo |
| `Ctrl+C` | Copy |
| `Ctrl+X` | Cut |
| `Ctrl+V` | Paste |
| `Ctrl+A` | Select all |
| `Ctrl+F` | Find |
| `Ctrl+H` | Find and replace |
### Navigation
| Shortcut | Action |
|----------|--------|
| `Ctrl+P` | Quick open document |
| `Ctrl+S` | Save (auto-saves anyway) |
| `Ctrl+N` | New document |
| `Ctrl+W` | Close document |
| `Escape` | Close dialog/menu |
### AI Features
| Shortcut | Action |
|----------|--------|
| `/` | Open AI command menu |
| `Ctrl+Shift+A` | AI improve selection |
| `Ctrl+Shift+G` | Generate content |
---
## Tips & Tricks
### Writing Tips
💡 **Use headings** to organize your document - makes it scannable
💡 **Write first, edit later** - don't let perfectionism slow you down
💡 **Use AI to overcome writer's block** - ask for ideas or outlines
💡 **Break long paragraphs** into shorter ones for readability
### Productivity Tips
💡 **Use templates** for recurring documents (reports, meeting notes)
💡 **Learn keyboard shortcuts** - much faster than clicking
💡 **Use `/` commands** for quick AI assistance
💡 **Set up folders** to keep documents organized
### AI Tips
💡 **Be specific** when asking AI for help - better prompts = better results
💡 **Use "Make it shorter"** for concise professional writing
💡 **Ask for multiple versions** and pick the best one
💡 **Use AI to check grammar** before sharing important documents
---
## Troubleshooting
### Document not saving
**Possible causes:**
1. Internet connection lost
2. Browser storage full
3. Session expired
**Solution:**
1. Check internet connection
2. Copy your text as backup (`Ctrl+A`, `Ctrl+C`)
3. Refresh the page
4. Log in again if prompted
5. Paste your text back if needed
---
### Formatting not working
**Possible causes:**
1. Text not selected
2. Format not supported in current context
3. Browser compatibility issue
**Solution:**
1. Select the text first, then apply formatting
2. Try a different format
3. Use keyboard shortcuts instead of toolbar
4. Try a different browser
---
### AI features not responding
**Possible causes:**
1. AI service temporarily unavailable
2. Network timeout
3. Request too long
**Solution:**
1. Wait a few seconds and try again
2. Try a shorter text selection
3. Refresh the page
4. Check if other AI features work
---
### Can't share document
**Possible causes:**
1. No sharing permissions
2. Invalid email address
3. Document not saved
**Solution:**
1. Check if you're the document owner
2. Verify email addresses are correct
3. Wait for document to save (check status bar)
4. Contact administrator if sharing is restricted
---
### Export fails
**Possible causes:**
1. Document too large
2. Special characters causing issues
3. Browser blocking download
**Solution:**
1. Try exporting a smaller section first
2. Remove any unusual characters or images
3. Check browser download settings
4. Try a different export format
---
## BASIC Integration
Control Paper from your bot dialogs:
### Create a Document
```basic
doc = CREATE DOCUMENT "Project Notes"
doc.content = "Meeting notes from " + TODAY
SAVE DOCUMENT doc
TALK "Document created: " + doc.id
```
### Generate Content with AI
```basic
HEAR topic AS TEXT "What should I write about?"
content = GENERATE TEXT "Write a brief introduction about " + topic
doc = CREATE DOCUMENT topic
doc.content = content
SAVE DOCUMENT doc
TALK "I've created a document about " + topic
TALK "Here's a preview:"
TALK LEFT(content, 200) + "..."
```
### Export a Document
```basic
HEAR docName AS TEXT "Which document should I export?"
doc = FIND DOCUMENT docName
IF doc IS NOT NULL THEN
pdf = EXPORT DOCUMENT doc AS "PDF"
TALK "Here's your PDF:"
SEND FILE pdf
ELSE
TALK "Document not found"
END IF
```
### Search Documents
```basic
HEAR query AS TEXT "What are you looking for?"
results = SEARCH DOCUMENTS query
IF COUNT(results) > 0 THEN
TALK "I found " + COUNT(results) + " documents:"
FOR EACH doc IN results
TALK "- " + doc.title
NEXT
ELSE
TALK "No documents found matching '" + query + "'"
END IF
```
### Summarize a Document
```basic
HEAR docName AS TEXT "Which document should I summarize?"
doc = FIND DOCUMENT docName
IF doc IS NOT NULL THEN
summary = SUMMARIZE doc.content
TALK "Summary of '" + doc.title + "':"
TALK summary
ELSE
TALK "Document not found"
END IF
```
---
## See Also
- [Drive App](./drive.md) - Store and organize files
- [Mail App](./mail.md) - Email your documents
- [Research App](./research.md) - Research topics for your writing
- [How To: Add Documents to Knowledge Base](../how-to/add-kb-documents.md)

View file

@ -1 +1,566 @@
# Research - AI Search # Research - AI Search
> **Your intelligent research assistant**
![Research Flow](../../assets/suite/research-flow.svg)
---
## Overview
Research is the AI-powered search and discovery app in General Bots Suite. Find information from the web, your documents, and databases using natural language. Research understands your questions, finds relevant sources, and presents organized answers with citations.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Research [History] [Settings] [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 🔍 Ask anything... [→] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Search in: [● All] [○ Web] [○ Documents] [○ Database] │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📋 ANSWER │ │
│ │ ───────── │ │
│ │ │ │
│ │ Renewable energy sources include solar, wind, hydroelectric, │ │
│ │ geothermal, and biomass. Solar energy has seen the fastest │ │
│ │ growth, with global capacity increasing 25% annually. Wind │ │
│ │ power is the second largest source of renewable electricity. │ │
│ │ │ │
│ │ Key Statistics (2024): │ │
│ │ • Solar: 1,200 GW global capacity │ │
│ │ • Wind: 900 GW global capacity │ │
│ │ • Hydro: 1,400 GW global capacity │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📚 SOURCES (5) [Show More] │ │
│ │ ───────────── │ │
│ │ 1. Energy Report 2024.pdf - Company KB [📄 View] │ │
│ │ 2. IEA World Energy Outlook - iea.org [🔗 Open] │ │
│ │ 3. Renewable Growth Statistics - energy.gov [🔗 Open] │ │
│ │ 4. Internal Policy Document.docx - Company KB [📄 View] │ │
│ │ 5. Climate Action Report - unfccc.int [🔗 Open] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ [📋 Copy Answer] [📄 Export] [💬 Ask Follow-up] [🔄 New Search] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Features
### Basic Search
Just type your question in natural language:
**Examples of questions you can ask:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ EXAMPLE QUERIES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 📊 Business Questions │
│ • "What are our sales numbers for Q1 2025?" │
│ • "Who are our top 10 customers by revenue?" │
│ • "What's our refund policy?" │
│ │
│ 📚 Knowledge Questions │
│ • "How does photosynthesis work?" │
│ • "What are the main causes of climate change?" │
│ • "Explain blockchain technology" │
│ │
│ 🔍 Research Questions │
│ • "Compare React vs Vue for web development" │
│ • "What are the latest trends in AI?" │
│ • "Find studies about remote work productivity" │
│ │
│ 📋 Document Questions │
│ • "What does our employee handbook say about PTO?" │
│ • "Find the budget approval process" │
│ • "What were the action items from last month's meeting?" │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Search Sources
Choose where to search:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Search in: │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
│ │ ● All │ │ ○ Web │ │ ○ Documents │ │
│ │ ─────────── │ │ ─────────── │ │ ─────────── │ │
│ │ Search all │ │ Search the │ │ Search your │ │
│ │ available │ │ internet for │ │ knowledge │ │
│ │ sources │ │ public info │ │ base files │ │
│ └──────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ ○ Database │ │ ○ Custom │ │
│ │ ─────────── │ │ ─────────── │ │
│ │ Query your │ │ Select │ │
│ │ structured │ │ specific │ │
│ │ data │ │ sources │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
| Source | What It Searches | Best For |
|--------|------------------|----------|
| **All** | Everything available | General questions |
| **Web** | Internet/public websites | Current events, general knowledge |
| **Documents** | Your uploaded files (.gbkb) | Company policies, internal info |
| **Database** | Connected databases | Business data, reports |
| **Custom** | Specific sources you choose | Focused research |
---
### AI-Powered Answers
Research doesn't just find links—it reads, understands, and summarizes:
```
You: What is our vacation policy?
Research: 📋 ANSWER
─────────
According to your Employee Handbook (2024), the vacation
policy is as follows:
Full-Time Employees:
• 0-2 years: 15 days per year
• 2-5 years: 20 days per year
• 5+ years: 25 days per year
Key Points:
• Vacation must be requested 2 weeks in advance
• Up to 5 days can be carried over to next year
• Unused days over the limit are forfeited
• Manager approval required for requests over 5 consecutive days
📚 SOURCE: Employee_Handbook_2024.pdf, Page 23-24
```
---
### Follow-Up Questions
Continue your research with follow-up questions:
```
You: What are the main benefits of solar energy?
Research: [Shows answer about solar energy benefits...]
You: How does it compare to wind energy?
Research: 📋 COMPARING SOLAR VS WIND ENERGY
──────────────────────────────────
Based on your previous question, here's a comparison:
┌────────────────┬─────────────────┬─────────────────┐
│ Factor │ Solar │ Wind │
├────────────────┼─────────────────┼─────────────────┤
│ Installation │ Rooftops, land │ Open areas │
│ Consistency │ Daylight hours │ Variable │
│ Maintenance │ Lower │ Moderate │
│ Cost/kWh │ $0.03-0.06 │ $0.02-0.05 │
│ Lifespan │ 25-30 years │ 20-25 years │
└────────────────┴─────────────────┴─────────────────┘
Both are excellent renewable options. Solar is better for
individual buildings, while wind is more efficient at scale.
```
---
### Source Citations
Every answer includes citations so you can verify:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 📚 SOURCES │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Internal Documents: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📄 Employee_Handbook_2024.pdf │ │
│ │ Location: Company KB / HR / Policies │ │
│ │ Relevant pages: 23-24 │ │
│ │ [View Document] [Copy Citation] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Web Sources: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 🔗 Department of Energy - Renewable Energy Basics │ │
│ │ URL: energy.gov/renewable-energy │ │
│ │ Retrieved: May 15, 2025 │ │
│ │ [Open Link] [Copy Citation] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Research History
Access your previous searches:
1. Click **History** in the top right
2. Browse or search past queries
3. Click to revisit any search
```
┌─────────────────────────────────────────┐
│ Research History [×] │
├─────────────────────────────────────────┤
│ 🔍 Search history... │
├─────────────────────────────────────────┤
│ │
│ TODAY │
│ ───── │
│ • "vacation policy" 2:30 PM │
│ • "Q1 sales report" 11:15 AM │
│ • "competitor analysis" 9:45 AM │
│ │
│ YESTERDAY │
│ ───────── │
│ • "renewable energy trends" 4:20 PM │
│ • "project timeline" 2:00 PM │
│ │
│ LAST WEEK │
│ ───────── │
│ • "budget approval process" │
│ • "customer feedback summary" │
│ • "marketing strategy 2025" │
│ │
│ [Clear History] │
│ │
└─────────────────────────────────────────┘
```
---
### Export Results
Save your research for later use:
1. Click **📄 Export**
2. Choose format:
| Format | Best For |
|--------|----------|
| **PDF** | Sharing, printing |
| **Markdown** | Documentation |
| **Word** | Reports, editing |
| **Copy to Paper** | Continue writing |
```
┌─────────────────────────────────────────┐
│ Export Research [×] │
├─────────────────────────────────────────┤
│ │
│ Include: │
│ ☑ Answer │
│ ☑ Sources with citations │
│ ☐ Search query │
│ ☐ Timestamp │
│ │
│ Format: │
│ ┌─────────┐ ┌─────────┐ │
│ │ PDF │ │ Word │ │
│ └─────────┘ └─────────┘ │
│ ┌─────────┐ ┌─────────┐ │
│ │Markdown │ │ Paper │ │
│ └─────────┘ └─────────┘ │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Export │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
### Advanced Search
Use operators for more precise searches:
| Operator | Example | What It Does |
|----------|---------|--------------|
| `""` | `"exact phrase"` | Find exact match |
| `AND` | `solar AND wind` | Both terms required |
| `OR` | `solar OR wind` | Either term |
| `NOT` | `energy NOT nuclear` | Exclude term |
| `site:` | `site:company.com` | Search specific site |
| `type:` | `type:pdf` | Search specific file type |
| `date:` | `date:2025` | Filter by date |
| `in:` | `in:documents` | Search specific source |
**Examples:**
```
"quarterly report" AND sales date:2025
```
Finds documents with exact phrase "quarterly report" AND the word "sales" from 2025
```
project proposal NOT draft type:pdf
```
Finds PDF files about project proposals, excluding drafts
```
budget in:documents site:finance
```
Searches documents in the finance folder for budget information
---
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `/` | Focus search box |
| `Enter` | Search |
| `Ctrl+Enter` | Search in new tab |
| `Escape` | Clear search / close panel |
| `↑` / `↓` | Navigate results |
| `Ctrl+C` | Copy answer |
| `Ctrl+S` | Save/export results |
| `H` | Open history |
| `Tab` | Cycle through sources |
| `1-5` | Jump to source N |
---
## Tips & Tricks
### Better Search Results
💡 **Be specific** - "Q1 2025 sales revenue by region" works better than "sales"
💡 **Use natural language** - Ask questions like you would ask a colleague
💡 **Try different phrasings** - If results aren't great, rephrase your question
💡 **Use follow-ups** - Build on previous searches for deeper research
### Finding Documents
💡 **Mention the document type** - "Find the PDF about vacation policy"
💡 **Reference dates** - "Meeting notes from last Tuesday"
💡 **Name departments** - "HR policies about sick leave"
### Web Research
💡 **Be current** - Add "2025" or "latest" for recent information
💡 **Compare sources** - Research shows multiple sources for verification
💡 **Check citations** - Click through to verify important information
---
## Troubleshooting
### No results found
**Possible causes:**
1. Query too specific
2. Information not in knowledge base
3. Typo in search terms
**Solution:**
1. Try broader search terms
2. Search "All" sources instead of one
3. Check spelling
4. Try different phrasing
5. Upload relevant documents to knowledge base
---
### Wrong or irrelevant results
**Possible causes:**
1. Ambiguous query
2. Outdated documents in KB
3. Source selection too broad
**Solution:**
1. Be more specific in your question
2. Use quotes for exact phrases
3. Select specific source (Documents only, Web only)
4. Use advanced operators
---
### Sources not loading
**Possible causes:**
1. Document was moved or deleted
2. Web page no longer available
3. Permission issues
**Solution:**
1. Check if document exists in Drive
2. Try opening the web link directly
3. Ask administrator about permissions
4. Use cached/saved version if available
---
### Search is slow
**Possible causes:**
1. Searching many sources
2. Large knowledge base
3. Complex query
**Solution:**
1. Select specific source instead of "All"
2. Be more specific to narrow results
3. Wait for indexing to complete (if recent uploads)
4. Check network connection
---
### AI answer seems incorrect
**Possible causes:**
1. Outdated information in sources
2. AI misinterpreted question
3. Conflicting information in sources
**Solution:**
1. Always verify with cited sources
2. Rephrase your question
3. Ask for clarification: "Are you sure about X?"
4. Check multiple sources for accuracy
---
## BASIC Integration
Use Research in your bot dialogs:
### Basic Search
```basic
HEAR question AS TEXT "What would you like to know?"
result = SEARCH question
TALK result.answer
TALK "Sources:"
FOR EACH source IN result.sources
TALK "- " + source.title
NEXT
```
### Search Specific Sources
```basic
' Search only documents
result = SEARCH "vacation policy" IN "documents"
' Search only web
result = SEARCH "latest AI news" IN "web"
' Search specific knowledge base
result = SEARCH "product specs" IN "products.gbkb"
```
### Research with Follow-up
```basic
TALK "What would you like to research?"
HEAR topic AS TEXT
result = SEARCH topic
TALK result.answer
HEAR followUp AS TEXT "Any follow-up questions? (or 'done')"
WHILE followUp <> "done"
result = SEARCH followUp WITH CONTEXT result
TALK result.answer
HEAR followUp AS TEXT "Any more questions? (or 'done')"
WEND
TALK "Research complete!"
```
### Export Research
```basic
HEAR query AS TEXT "What should I research?"
result = SEARCH query
' Export as PDF
pdf = EXPORT RESEARCH result AS "PDF"
SEND FILE pdf
' Or copy to Paper
doc = CREATE DOCUMENT "Research: " + query
doc.content = result.answer + "\n\nSources:\n" + result.citations
SAVE DOCUMENT doc
TALK "Research saved to Paper"
```
### Automated Research Report
```basic
topics = ["market trends", "competitor analysis", "customer feedback"]
report = ""
FOR EACH topic IN topics
result = SEARCH topic + " 2025"
report = report + "## " + topic + "\n\n"
report = report + result.answer + "\n\n"
NEXT
doc = CREATE DOCUMENT "Weekly Research Report"
doc.content = report
SAVE DOCUMENT doc
TALK "Research report created with " + COUNT(topics) + " topics"
```
---
## See Also
- [Paper App](./paper.md) - Write documents based on your research
- [Drive App](./drive.md) - Upload documents to knowledge base
- [Chat App](./chat.md) - Ask quick questions
- [How To: Add Documents to Knowledge Base](../how-to/add-kb-documents.md)

View file

@ -1 +1,700 @@
# Sources - Prompts & Templates # Sources - Prompts & Templates
> **Your bot configuration hub**
![Sources Flow](../../assets/suite/sources-flow.svg)
---
## Overview
Sources is the configuration center in General Bots Suite. Manage your bots, prompts, templates, and knowledge bases all in one place. Sources is where you create new bots, customize their behavior, and organize the content that powers your AI assistant.
---
## Interface Layout
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Sources [+ New Bot] [Import] [⚙️] [×] │
├──────────────┬──────────────────────────────────────────────────────────┤
│ │ ┌─────────────────────────────────────────────────────┐ │
│ SECTIONS │ │ Bots │ Prompts │ Templates │ Knowledge │ │
│ ─────────── │ └─────────────────────────────────────────────────────┘ │
│ │ │
│ 🤖 Bots │ YOUR BOTS [Grid] │
│ 💬 Prompts │ ───────── │
│ 📋 Templates│ │
│ 📚 Knowledge│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ 🤖 support │ │ 🤖 sales │ │
│ ─────────── │ │ ───────────── │ │ ───────────── │ │
│ │ │ Customer │ │ Lead capture │ │
│ QUICK │ │ Support Bot │ │ and sales │ │
│ ACTIONS │ │ │ │ │ │
│ ─────────── │ │ Status: ● Live │ │ Status: ○ Draft│ │
│ [New Bot] │ │ Sessions: 145 │ │ Sessions: 0 │ │
│ [Import] │ │ │ │ │ │
│ [Export] │ │ [Open] [⚙️] │ │ [Open] [⚙️] │ │
│ │ └─────────────────┘ └─────────────────┘ │
│ │ │
│ │ ┌─────────────────┐ ┌─────────────────┐ │
│ │ │ 🤖 hr │ │ New Bot │ │
│ │ │ ───────────── │ │ │ │
│ │ │ HR Assistant │ │ Create a new │ │
│ │ │ for employees │ │ bot from │ │
│ │ │ │ │ scratch or │ │
│ │ │ Status: ● Live │ │ template │ │
│ │ │ Sessions: 89 │ │ │ │
│ │ │ │ │ [+ Create] │ │
│ │ │ [Open] [⚙️] │ │ │ │
│ │ └─────────────────┘ └─────────────────┘ │
│ │ │
└──────────────┴──────────────────────────────────────────────────────────┘
```
---
## Features
### Managing Bots
#### Creating a New Bot
1. Click **+ New Bot** in the top right
2. Fill in the bot details:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Create New Bot [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Bot ID (unique identifier): │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ support │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ⚠️ Use lowercase letters, numbers, and hyphens only │
│ │
│ Display Name: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Customer Support Bot │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Description: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Handles customer inquiries, troubleshooting, and ticket │ │
│ │ creation for our product support team. │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Start from: │
│ ○ Blank - Start from scratch │
│ ● Template - Use a pre-built template │
│ [Support Bot Template ▼] │
│ ○ Clone - Copy existing bot │
│ [Select bot... ▼] │
│ │
│ ─────────────────────────────────────────────────────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Create Bot │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Bot Settings
Click the **⚙️** icon on any bot to configure:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Bot Settings: support [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┬──────────┬──────────┬──────────┬──────────┐ │
│ │ General │ AI │ Channels │ Security │ Advanced │ │
│ └──────────┴──────────┴──────────┴──────────┴──────────┘ │
│ │
│ GENERAL SETTINGS │
│ ──────────────── │
│ │
│ Bot ID: support (cannot be changed) │
│ │
│ Display Name: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Customer Support Bot │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Welcome Message: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Hello! I'm your support assistant. How can I help you today? │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Language: [English (US) ▼] │
│ Timezone: [America/New_York ▼] │
│ │
│ Status: │
│ ● Live - Bot is active and responding │
│ ○ Draft - Bot is hidden from users │
│ ○ Maintenance - Shows maintenance message │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Save │ │ Cancel │ │
│ └─────────────┘ └─────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### AI Settings
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Bot Settings: support [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┬──────────┬──────────┬──────────┬──────────┐ │
│ │ General │ *AI* │ Channels │ Security │ Advanced │ │
│ └──────────┴──────────┴──────────┴──────────┴──────────┘ │
│ │
│ AI MODEL │
│ ──────── │
│ │
│ Provider: [OpenAI ▼] │
│ Model: [GPT-4 ▼] │
│ │
│ BEHAVIOR │
│ ──────── │
│ │
│ Temperature: [0.7 ] ░░░░░░░▓░░░ (More creative ←→ More focused) │
│ Max Tokens: [2048 ] │
│ │
│ SYSTEM PROMPT (Personality & Instructions) │
│ ────────────────────────────────────────── │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ You are a helpful customer support agent for TechCorp. │ │
│ │ Be friendly, professional, and concise. │ │
│ │ Always try to resolve issues on first contact. │ │
│ │ If you can't help, create a support ticket. │ │
│ │ Never make up information - use the knowledge base. │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ KNOWLEDGE BASE │
│ ────────────── │
│ ☑ Use knowledge base for answers │
│ Connected KB: [support.gbkb ▼] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Managing Prompts
Prompts are reusable text templates for AI interactions.
#### Prompts List
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Prompts [+ New Prompt] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🔍 Search prompts... │
│ │
│ SYSTEM PROMPTS │
│ ────────────── │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📝 support-agent [Edit] │ │
│ │ Customer support personality and guidelines │ │
│ │ Used by: support, help-desk │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📝 sales-agent [Edit] │ │
│ │ Sales-focused, lead qualification │ │
│ │ Used by: sales │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ TASK PROMPTS │
│ ──────────── │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📝 summarize [Edit] │ │
│ │ Summarize text or conversations │ │
│ │ Used by: all bots │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📝 extract-info [Edit] │ │
│ │ Extract structured data from text │ │
│ │ Used by: intake, forms │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Creating a Prompt
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Edit Prompt: support-agent [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Name: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ support-agent │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Type: [System Prompt ▼] │
│ ┌────────────────┐ │
│ │ System Prompt │ Bot personality/behavior │
│ │ Task Prompt │ Specific task instructions │
│ │ Template │ Reusable text with variables │
│ └────────────────┘ │
│ │
│ Prompt Content: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ You are a friendly and professional customer support agent │ │
│ │ for {{company_name}}. │ │
│ │ │ │
│ │ ## Your Personality │ │
│ │ - Be warm and empathetic │ │
│ │ - Use simple, clear language │ │
│ │ - Be patient and thorough │ │
│ │ │ │
│ │ ## Guidelines │ │
│ │ - Always verify customer identity before sharing account info │ │
│ │ - If unsure, search the knowledge base │ │
│ │ - Escalate complex issues to human agents │ │
│ │ - Never make promises about refunds or compensation │ │
│ │ │ │
│ │ ## Available Tools │ │
│ │ - SEARCH KB: Search knowledge base │ │
│ │ - CREATE TICKET: Create support ticket │ │
│ │ - GET ORDER: Look up order status │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Variables Used: │
│ {{company_name}} - Will be replaced with bot's company setting │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────────┐ │
│ │ Save │ │ Test │ │ Delete Prompt │ │
│ └─────────────┘ └─────────────┘ └─────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Managing Templates
Templates are pre-built bot packages you can reuse.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Templates [+ Import] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 🔍 Search templates... [All Categories ▼] │
│ │
│ INSTALLED TEMPLATES │
│ ─────────────────── │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📋 CRM │ │ 📋 Support │ │ 📋 FAQ │ │
│ │ ─────────── │ │ ─────────── │ │ ─────────── │ │
│ │ Full CRM │ │ Ticket mgmt │ │ Answer common │ │
│ │ with leads, │ │ and customer │ │ questions │ │
│ │ contacts │ │ service │ │ from KB │ │
│ │ │ │ │ │ │ │
│ │ [Use] [View] │ │ [Use] [View] │ │ [Use] [View] │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ AVAILABLE TEMPLATES │
│ ─────────────────── │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📋 HR │ │ 📋 Analytics │ │ 📋 Compliance │ │
│ │ ─────────── │ │ ─────────── │ │ ─────────── │ │
│ │ Employee │ │ Dashboard │ │ LGPD, GDPR │ │
│ │ self-service │ │ and metrics │ │ compliance │ │
│ │ │ │ │ │ │ │
│ │ [Install] │ │ [Install] │ │ [Install] │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Template Contents
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Template: CRM [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 📋 CRM - Customer Relationship Management │
│ ════════════════════════════════════════ │
│ │
│ A complete CRM system for managing leads, contacts, accounts, │
│ and opportunities through conversational AI. │
│ │
│ INCLUDED COMPONENTS │
│ ─────────────────── │
│ │
│ 📁 crm.gbai/ │
│ ├── 📁 crm.gbdialog/ │
│ │ ├── start.bas - Main entry point │
│ │ ├── create-lead.bas - Lead creation flow │
│ │ ├── find-contact.bas - Contact lookup │
│ │ └── update-deal.bas - Deal management │
│ │ │
│ ├── 📁 crm.gbot/ │
│ │ └── config.csv - Bot settings │
│ │ │
│ └── 📁 crm.gbkb/ │
│ └── docs/ - CRM documentation │
│ │
│ FEATURES │
│ ──────── │
│ ✓ Lead capture and scoring │
│ ✓ Contact management │
│ ✓ Account hierarchy │
│ ✓ Opportunity tracking │
│ ✓ Activity logging │
│ ✓ Reports and dashboards │
│ │
│ SAMPLE CONVERSATION │
│ ─────────────────── │
│ User: Create a new lead │
│ Bot: I'll help you create a new lead. What's their name? │
│ User: John Smith │
│ Bot: Got it! What's John Smith's email address? │
│ User: john@example.com │
│ Bot: What company is John from? │
│ User: Acme Corp │
│ Bot: ✓ Lead created: John Smith (Acme Corp) │
│ Score: 45/100 | Status: New │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Use This Template │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
### Managing Knowledge Bases
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Knowledge Bases [+ New KB] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ YOUR KNOWLEDGE BASES │
│ ─────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📚 support.gbkb [Open] │ │
│ │ Customer support documentation │ │
│ │ Documents: 45 │ Size: 12.3 MB │ Last indexed: 2h ago │ │
│ │ Used by: support, help-desk │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📚 products.gbkb [Open] │ │
│ │ Product specifications and manuals │ │
│ │ Documents: 128 │ Size: 45.6 MB │ Last indexed: 1d ago │ │
│ │ Used by: sales, support │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📚 hr.gbkb [Open] │ │
│ │ Employee handbook and HR policies │ │
│ │ Documents: 23 │ Size: 5.1 MB │ Last indexed: 3h ago │ │
│ │ Used by: hr │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### Knowledge Base Details
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Knowledge Base: support.gbkb [Reindex] [⚙️] [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Status: ● Indexed and ready Last indexed: May 15, 2025 2:30PM │
│ │
│ DOCUMENTS [Upload] [+ Folder] │
│ ───────── │
│ │
│ 📁 policies/ │
│ ├── 📄 refund-policy.pdf 45 KB May 10, 2025 │
│ ├── 📄 shipping-policy.pdf 32 KB May 10, 2025 │
│ └── 📄 warranty-terms.pdf 28 KB May 10, 2025 │
│ │
│ 📁 faqs/ │
│ ├── 📄 general-faq.docx 18 KB May 12, 2025 │
│ ├── 📄 billing-faq.docx 22 KB May 12, 2025 │
│ └── 📄 technical-faq.docx 35 KB May 12, 2025 │
│ │
│ 📁 product-guides/ │
│ ├── 📄 getting-started.pdf 125 KB May 8, 2025 │
│ ├── 📄 advanced-features.pdf 210 KB May 8, 2025 │
│ └── 📄 troubleshooting.pdf 89 KB May 8, 2025 │
│ │
│ STATISTICS │
│ ────────── │
│ Total documents: 45 │
│ Total chunks: 1,234 │
│ Vector dimensions: 1536 │
│ Storage used: 12.3 MB │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Import and Export
### Exporting a Bot
1. Click **⚙️** on the bot
2. Select **Export**
3. Choose format:
```
┌─────────────────────────────────────────┐
│ Export Bot: support [×] │
├─────────────────────────────────────────┤
│ │
│ Include: │
│ ☑ Bot configuration │
│ ☑ Dialog scripts (.bas files) │
│ ☑ Prompts │
│ ☐ Knowledge base (large) │
│ ☐ Conversation history │
│ │
│ Format: │
│ ● .gbai package (recommended) │
│ ○ ZIP archive │
│ ○ JSON (config only) │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Export │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
### Importing a Bot
1. Click **Import** at the top
2. Select your file
3. Configure import options
```
┌─────────────────────────────────────────┐
│ Import Bot [×] │
├─────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────┐ │
│ │ │ │
│ │ 📁 Drop file here or click │ │
│ │ to browse │ │
│ │ │ │
│ │ Supported: .gbai, .zip │ │
│ │ │ │
│ └─────────────────────────────────┘ │
│ │
│ File: support-backup.gbai │
│ Size: 2.4 MB │
│ │
│ Options: │
│ ○ Create new bot │
│ ● Replace existing: [support ▼] │
│ │
│ ☐ Overwrite prompts │
│ ☑ Merge knowledge base │
│ │
│ ┌─────────────────────────────────┐ │
│ │ Import │ │
│ └─────────────────────────────────┘ │
│ │
└─────────────────────────────────────────┘
```
---
## Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `Ctrl+N` | New bot |
| `Ctrl+S` | Save changes |
| `Ctrl+E` | Export selected |
| `Ctrl+I` | Import |
| `Delete` | Delete selected |
| `Ctrl+D` | Duplicate |
| `F2` | Rename |
| `/` | Search |
| `Enter` | Open selected |
| `Escape` | Close dialog |
---
## Tips & Tricks
### Bot Management
💡 **Use descriptive names** - "customer-support-v2" is better than "bot1"
💡 **Keep prompts separate** - Reuse prompts across multiple bots
💡 **Version your exports** - Export before major changes
💡 **Test in Draft mode** - Don't go Live until fully tested
### Prompt Writing
💡 **Be specific** - Clear instructions give better results
💡 **Use examples** - Show the AI what good responses look like
💡 **Set boundaries** - Define what the bot should NOT do
💡 **Use variables** - Make prompts reusable with {{placeholders}}
### Knowledge Base
💡 **Organize in folders** - Group related documents together
💡 **Keep documents current** - Remove outdated information
💡 **Use clear filenames** - "refund-policy-2025.pdf" not "doc1.pdf"
💡 **Reindex after changes** - New content isn't searchable until indexed
---
## Troubleshooting
### Bot not responding
**Possible causes:**
1. Bot is in Draft mode
2. AI provider not configured
3. No dialogs or prompts set up
**Solution:**
1. Check bot status is "Live"
2. Verify AI settings have valid API key
3. Ensure at least start.bas exists
4. Check error logs in Analytics
---
### Knowledge base not finding answers
**Possible causes:**
1. Documents not indexed
2. Document format not supported
3. Query doesn't match content
**Solution:**
1. Click "Reindex" and wait for completion
2. Convert documents to supported formats
3. Check document actually contains the information
4. Try different phrasing
---
### Import fails
**Possible causes:**
1. File corrupted
2. Incompatible version
3. Duplicate bot ID
**Solution:**
1. Try re-exporting from source
2. Check General Bots version compatibility
3. Choose "Create new bot" instead of replace
4. Rename bot ID if duplicate
---
### Prompts not applying
**Possible causes:**
1. Prompt not linked to bot
2. Variable not defined
3. Syntax error in prompt
**Solution:**
1. Check AI Settings → System Prompt selection
2. Verify all {{variables}} have values
3. Test prompt with "Test" button
4. Check for unclosed brackets or quotes
---
## BASIC Integration
Access Sources data from dialogs:
### Get Bot Configuration
```basic
config = GET BOT CONFIG
TALK "Bot name: " + config.displayName
TALK "Language: " + config.language
```
### Use Prompts
```basic
' Load a prompt template
prompt = GET PROMPT "summarize"
' Use with variables
summary = GENERATE WITH PROMPT prompt, content
TALK summary
```
### Search Knowledge Base
```basic
HEAR question AS TEXT "What would you like to know?"
results = SEARCH KB question IN "support.gbkb"
IF COUNT(results) > 0 THEN
TALK results[0].answer
TALK "Source: " + results[0].source
ELSE
TALK "I couldn't find information about that."
END IF
```
### List Available Bots
```basic
bots = GET BOTS
TALK "Available bots:"
FOR EACH bot IN bots
IF bot.status = "live" THEN
TALK "● " + bot.displayName
ELSE
TALK "○ " + bot.displayName + " (draft)"
END IF
NEXT
```
---
## See Also
- [Designer App](./designer.md) - Visual flow builder
- [Drive App](./drive.md) - Upload KB documents
- [How To: Create Your First Bot](../how-to/create-first-bot.md)
- [How To: Add Documents to Knowledge Base](../how-to/add-kb-documents.md)

View file

@ -0,0 +1,253 @@
# How To... Tutorials
> **📖 Step-by-Step Guides for General Bots Suite**
>
> *Clear instructions for common tasks, inspired by classic computer manuals*
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ╔═══════════════════════════════════════════════════════════════╗ │
│ ║ ║ │
│ ║ ██╗ ██╗ ██████╗ ██╗ ██╗ ████████╗ ██████╗ ║ │
│ ║ ██║ ██║██╔═══██╗██║ ██║ ╚══██╔══╝██╔═══██╗ ║ │
│ ║ ███████║██║ ██║██║ █╗ ██║ ██║ ██║ ██║ ║ │
│ ║ ██╔══██║██║ ██║██║███╗██║ ██║ ██║ ██║ ║ │
│ ║ ██║ ██║╚██████╔╝╚███╔███╔╝ ██║ ╚██████╔╝ ██╗ ║ │
│ ║ ╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═════╝ ╚═╝ ║ │
│ ║ ║ │
│ ╚═══════════════════════════════════════════════════════════════╝ │
│ │
│ Your Guide to General Bots Suite │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## About These Tutorials
These tutorials are designed like the classic computer manuals of the early 1990s — clear, numbered steps with visual diagrams showing exactly what to do. Each tutorial follows the same format:
- **Objective** — What you'll accomplish
- **Time Required** — How long it takes
- **Prerequisites** — What you need before starting
- **Steps** — Numbered instructions with screenshots
- **Troubleshooting** — Common problems and solutions
- **Next Steps** — What to learn next
---
## 🚀 Getting Started
| Tutorial | Description | Time |
|----------|-------------|------|
| [Create Your First Bot](./create-first-bot.md) | Set up a working bot from scratch | 10 min |
| [Your First Conversation](./first-conversation.md) | Talk to your bot and understand responses | 5 min |
| [Understanding the Interface](./understanding-interface.md) | Navigate the Suite like a pro | 10 min |
---
## 💬 Chat & Conversations
| Tutorial | Description | Time |
|----------|-------------|------|
| [Ask Questions Effectively](./ask-questions.md) | Get better answers from your AI assistant | 5 min |
| [Use Voice Input](./voice-input.md) | Talk instead of type | 3 min |
| [Create Chat Shortcuts](./chat-shortcuts.md) | Set up quick commands | 10 min |
| [Export Conversations](./export-conversations.md) | Save chat history for reference | 5 min |
---
## 📁 File Management (Drive)
| Tutorial | Description | Time |
|----------|-------------|------|
| [Upload Your First File](./upload-files.md) | Add documents to Drive | 3 min |
| [Organize with Folders](./organize-folders.md) | Create a logical file structure | 10 min |
| [Share Files Securely](./share-files.md) | Grant access to team members | 5 min |
| [Search for Documents](./search-documents.md) | Find files instantly | 5 min |
---
## 📚 Knowledge Base
| Tutorial | Description | Time |
|----------|-------------|------|
| [Add Documents to Knowledge Base](./add-kb-documents.md) | Teach your bot from files | 15 min |
| [Import a Website](./import-website.md) | Crawl and learn from web pages | 10 min |
| [Create FAQ Responses](./create-faq.md) | Define question-answer pairs | 15 min |
| [Manage Collections](./manage-collections.md) | Organize knowledge by topic | 10 min |
---
## 🔧 BASIC Dialogs
| Tutorial | Description | Time |
|----------|-------------|------|
| [Write Your First Dialog](./write-first-dialog.md) | Create a simple conversation script | 20 min |
| [Use HEAR and TALK](./hear-and-talk.md) | Gather and display information | 10 min |
| [Store User Information](./store-user-info.md) | Remember data between conversations | 15 min |
| [Call External APIs](./call-external-apis.md) | Connect to web services | 20 min |
| [Send Automated Messages](./send-automated.md) | Schedule broadcasts and reminders | 15 min |
---
## 📱 Messaging Channels
| Tutorial | Description | Time |
|----------|-------------|------|
| [Connect WhatsApp](./connect-whatsapp.md) | Set up WhatsApp Business integration | 30 min |
| [Configure Email](./configure-email.md) | Enable email conversations | 15 min |
| [Set Up SMS](./setup-sms.md) | Add text message support | 15 min |
| [Embed Web Chat](./embed-web-chat.md) | Add chat to your website | 10 min |
---
## 📊 Analytics & Monitoring
| Tutorial | Description | Time |
|----------|-------------|------|
| [View Bot Statistics](./view-statistics.md) | Understand usage metrics | 10 min |
| [Monitor Live Sessions](./monitor-sessions.md) | Watch conversations in real-time | 10 min |
| [Create Custom Reports](./create-reports.md) | Build dashboards for insights | 20 min |
| [Export Analytics Data](./export-analytics.md) | Download metrics for external analysis | 10 min |
---
## 🎨 Customization
| Tutorial | Description | Time |
|----------|-------------|------|
| [Change Your Bot's Theme](./change-theme.md) | Customize colors and appearance | 10 min |
| [Add a Custom Logo](./add-logo.md) | Brand your bot interface | 5 min |
| [Create Custom Cards](./create-cards.md) | Design rich message layouts | 20 min |
| [Modify the Welcome Message](./welcome-message.md) | Personalize the first interaction | 5 min |
---
## 🏢 Templates
| Tutorial | Description | Time |
|----------|-------------|------|
| [Install the CRM Template](./install-crm.md) | Set up customer relationship management | 20 min |
| [Use the HR Template](./use-hr-template.md) | Deploy employee self-service | 15 min |
| [Configure Compliance Bot](./configure-compliance.md) | Enable privacy request handling | 25 min |
| [Build from Template](./build-from-template.md) | Customize a template for your needs | 30 min |
---
## 🔒 Security & Administration
| Tutorial | Description | Time |
|----------|-------------|------|
| [Set Up User Authentication](./setup-auth.md) | Enable secure login | 20 min |
| [Configure Permissions](./configure-permissions.md) | Control who can do what | 15 min |
| [Enable Audit Logging](./enable-audit.md) | Track all system activities | 10 min |
| [Backup Your Bot](./backup-bot.md) | Protect your configuration and data | 15 min |
---
## 🐛 Troubleshooting
| Tutorial | Description | Time |
|----------|-------------|------|
| [Debug Dialog Errors](./debug-dialogs.md) | Fix common BASIC script problems | 15 min |
| [Resolve Connection Issues](./resolve-connections.md) | Troubleshoot network problems | 10 min |
| [Fix Knowledge Base Gaps](./fix-kb-gaps.md) | Improve bot answers | 20 min |
| [Performance Optimization](./performance-tips.md) | Make your bot faster | 15 min |
---
## Quick Reference Card
```
┌─────────────────────────────────────────────────────────────────────────┐
│ QUICK REFERENCE CARD │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ KEYBOARD SHORTCUTS │
│ ───────────────── │
│ Ctrl + Enter ........ Send message │
│ Ctrl + K ............ Quick command palette │
│ Ctrl + / ............ Toggle voice input │
│ Ctrl + N ............ New item (context-aware) │
│ Ctrl + S ............ Save current work │
│ Escape .............. Close dialog/panel │
│ │
│ COMMON BASIC KEYWORDS │
│ ──────────────────── │
│ TALK "message" ...... Display message to user │
│ HEAR variable ....... Wait for user input │
│ SET BOT MEMORY ...... Store bot-wide data │
│ GET USER MEMORY ..... Retrieve user-specific data │
│ USE KB "name" ....... Activate knowledge base │
│ SEND MAIL ........... Send email notification │
│ │
│ WHERE TO GET HELP │
│ ───────────────── │
│ • Type "help" in Chat for assistance │
│ • Press F1 anywhere for context help │
│ • Visit community.pragmatismo.com for forums │
│ • Email support@pragmatismo.com for enterprise support │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Tips for Following Tutorials
### ✅ Before You Start
1. **Bookmark this page** — You'll return to it often
2. **Have the Suite open** — Follow along step-by-step
3. **Take notes** — Write down customizations you make
4. **Don't skip steps** — Each step builds on the previous
### ✅ While Following Steps
1. **Read the entire step first** — Then perform the action
2. **Match the screenshots** — If your screen looks different, stop and check
3. **Use exact values** — Type what the tutorial shows, then customize later
4. **Check results** — Verify each step worked before moving on
### ✅ If Something Goes Wrong
1. **Don't panic** — Most problems have simple solutions
2. **Re-read the step** — You may have missed something
3. **Check Troubleshooting** — Each tutorial has a help section
4. **Ask for help** — The Chat assistant can guide you
---
## Icon Legend
Throughout these tutorials, you'll see these indicators:
| Icon | Meaning |
|------|---------|
| 💡 | **Tip** — Helpful suggestion to work more efficiently |
| ⚠️ | **Warning** — Important caution to avoid problems |
| 📝 | **Note** — Additional information or context |
| ✅ | **Checkpoint** — Verify your progress before continuing |
| 🔧 | **Configuration** — Settings you may need to adjust |
---
## Version Information
These tutorials are written for:
- **General Bots Suite** version 5.0+
- **Browser**: Chrome, Firefox, Safari, or Edge (latest versions)
- **Last Updated**: 2025
If you're using an older version, some screens may look different.
---
*"The best way to learn is to do."*
**Start with [Create Your First Bot](./create-first-bot.md)** →

View file

@ -0,0 +1,531 @@
# How To: Add Documents to Knowledge Base
> **Tutorial 3 of the Knowledge Base Series**
>
> *Teach your bot from files in 15 minutes*
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📚 ADD DOCUMENTS TO KNOWLEDGE BASE │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
│ │ │Prepare │ │ Upload │ │ Index │ │ Test │ │ │
│ │ │ Docs │ │ Files │ │ KB │ │ KB │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Objective
By the end of this tutorial, you will have:
- Prepared documents for the knowledge base
- Uploaded files to your bot's `.gbkb` folder
- Indexed documents for semantic search
- Tested that your bot can answer questions from the documents
---
## Time Required
⏱️ **15 minutes**
---
## Prerequisites
Before you begin, make sure you have:
- [ ] A working bot (see [Create Your First Bot](./create-first-bot.md))
- [ ] Access to the Drive app
- [ ] Documents to upload (PDF, Word, Text, or Markdown files)
---
## What is a Knowledge Base?
A **Knowledge Base (KB)** is a collection of documents that your bot uses to answer questions. When a user asks something, the bot searches through these documents to find relevant information.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ HOW KNOWLEDGE BASE WORKS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User asks: "What is our refund policy?" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🔍 Semantic Search │ │
│ │ Searches through all documents in the knowledge base │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────┼───────────────────┐ │
│ ▼ ▼ ▼ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │policies │ │ FAQ │ │ terms │ │
│ │ .pdf │ │ .docx │ │ .md │ │
│ └────┬────┘ └─────────┘ └─────────┘ │
│ │ │
│ ▼ Found match! │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ "Refunds are available within 30 days of purchase..." │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Bot answers with context from the document │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Supported File Formats
| Format | Extension | Best For |
|--------|-----------|----------|
| **PDF** | `.pdf` | Manuals, reports, official documents |
| **Word** | `.docx`, `.doc` | Policies, procedures, articles |
| **Text** | `.txt` | Simple content, FAQs |
| **Markdown** | `.md` | Technical documentation |
| **Excel** | `.xlsx`, `.xls` | FAQs, structured data |
| **PowerPoint** | `.pptx` | Training materials |
| **HTML** | `.html` | Web content |
---
## Step 1: Prepare Your Documents
### 1.1 Gather Your Files
Collect the documents you want your bot to learn from. Good candidates include:
- ✅ Product manuals
- ✅ FAQ documents
- ✅ Company policies
- ✅ Help articles
- ✅ Training materials
### 1.2 Review Document Quality
Before uploading, check that your documents:
| Check | Why It Matters |
|-------|----------------|
| Text is selectable | Scanned images can't be indexed |
| Content is accurate | Bot will repeat incorrect info |
| Information is current | Outdated docs confuse users |
| No sensitive data | Protect confidential information |
⚠️ **Warning**: The bot will use exactly what's in your documents. Remove any outdated or incorrect information first.
### 1.3 Organize Files (Optional)
For large knowledge bases, organize files into folders by topic:
```
mycompany.gbkb/
├── 📁 products/
│ ├── product-guide.pdf
│ └── specifications.docx
├── 📁 policies/
│ ├── refund-policy.pdf
│ └── privacy-policy.md
├── 📁 support/
│ ├── faq.docx
│ └── troubleshooting.pdf
└── 📁 training/
└── onboarding-guide.pptx
```
**Checkpoint**: You have documents ready to upload.
---
## Step 2: Upload Files to Knowledge Base
### 2.1 Open the Drive App
Click the **Apps Menu** (⋮⋮⋮) and select **Drive**.
### 2.2 Navigate to Your Bot's KB Folder
Navigate to your bot's knowledge base folder:
```
📂 mycompany.gbai
└── 📂 mycompany.gbkb ◄── Open this folder
```
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 📁 Drive │
├─────────────────────────────────────────────────────────────────────────┤
│ 📂 mycompany.gbai │
│ ├── 📂 mycompany.gbdialog │
│ ├── 📂 mycompany.gbot │
│ ├── 📂 mycompany.gbkb ◄── Knowledge base folder │
│ │ └── (your documents go here) │
│ └── 📂 mycompany.gbdrive │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.3 Upload Your Documents
**Method A: Drag and Drop**
1. Open your file explorer
2. Select the documents you want to upload
3. Drag them into the Drive window
**Method B: Upload Button**
1. Click the **Upload** button (📤)
2. Select files from your computer
3. Click **Open**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 📁 Drive > mycompany.gbai > mycompany.gbkb │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📤 Upload │ │ 📁 New Folder │ │
│ └─────────────────┘ └─────────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 📄 company-faq.pdf 2.3 MB Just now │
│ 📄 product-manual.docx 1.1 MB Just now │
│ 📄 refund-policy.pdf 0.5 MB Just now │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ ↑ Drag files here to upload │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.4 Wait for Upload to Complete
You'll see a progress indicator for each file. Wait until all uploads finish.
💡 **Tip**: Large files may take longer. PDF files typically upload fastest.
**Checkpoint**: Your documents appear in the `.gbkb` folder.
---
## Step 3: Index the Knowledge Base
After uploading, you need to index the documents so the bot can search them.
### 3.1 Automatic Indexing
In most cases, indexing happens automatically when files are uploaded. Look for:
- A "Processing..." indicator
- Files changing from gray to normal color
- A completion notification
### 3.2 Manual Indexing (If Needed)
If automatic indexing doesn't start, trigger it manually:
**From Chat:**
```
/reindex
```
**From a BASIC Dialog:**
```basic
' Clear and rebuild the knowledge base
CLEAR KB
USE KB "mycompany"
```
### 3.3 Check Indexing Status
You can check how many documents are indexed:
**From Chat:**
```
/kb stats
```
**Expected Output:**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 📊 Knowledge Base Statistics │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Collection: mycompany │
│ Documents: 3 │
│ Vectors: 847 │
│ Status: ● Ready │
│ Last Index: 2 minutes ago │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Checkpoint**: Documents are indexed and ready to search.
---
## Step 4: Test the Knowledge Base
### 4.1 Open Chat
Click the **Apps Menu** (⋮⋮⋮) and select **Chat**.
### 4.2 Ask a Question from Your Documents
Type a question that can be answered by your uploaded documents:
```
You: What is the refund policy?
```
### 4.3 Verify the Response
The bot should answer using information from your documents:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 💬 Chat │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 👤 You │ │
│ │ What is the refund policy? │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🤖 Bot │ │
│ │ │ │
│ │ Based on our refund policy document: │ │
│ │ │ │
│ │ Customers may request a full refund within 30 days of │ │
│ │ purchase. After 30 days, refunds are prorated based on │ │
│ │ usage. To request a refund, contact support@company.com │ │
│ │ with your order number. │ │
│ │ │ │
│ │ 📄 Source: refund-policy.pdf │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 4.4 Test Different Questions
Try several questions to ensure the knowledge base is working:
| Test Question | Expected Source |
|---------------|-----------------|
| "How do I return a product?" | refund-policy.pdf |
| "What are the product specs?" | product-manual.docx |
| "How do I contact support?" | company-faq.pdf |
**Checkpoint**: Your bot answers questions using the uploaded documents!
---
## 🎉 Congratulations!
You've successfully added documents to your knowledge base! Here's what you accomplished:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ✓ Prepared documents for upload │
│ ✓ Uploaded files to the .gbkb folder │
│ ✓ Indexed documents for semantic search │
│ ✓ Tested that the bot can answer from documents │
│ │
│ Your bot can now answer questions from your documents! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Troubleshooting
### Problem: Bot doesn't find information from documents
**Cause**: Documents may not be indexed yet.
**Solution**:
1. Check indexing status with `/kb stats`
2. Wait a few minutes for processing to complete
3. Try `/reindex` to force re-indexing
### Problem: Bot gives wrong information
**Cause**: Document contains outdated or incorrect content.
**Solution**:
1. Review the source document
2. Update or replace the incorrect document
3. Re-index the knowledge base
### Problem: "No relevant information found"
**Cause**: Question doesn't match document content well enough.
**Solution**:
1. Try rephrasing the question
2. Use keywords that appear in your documents
3. Check that the document actually contains the answer
### Problem: Upload fails
**Cause**: File too large or unsupported format.
**Solution**:
1. Check file size (max 50MB per file)
2. Verify file format is supported
3. Try converting to PDF if format issues persist
### Problem: PDF text not extracted
**Cause**: PDF contains scanned images, not selectable text.
**Solution**:
1. Use OCR software to convert image-based PDFs
2. Or recreate the document as a text-based PDF
3. Consider using Word format instead
---
## Best Practices
### Document Organization
```
┌─────────────────────────────────────────────────────────────────────────┐
│ RECOMMENDED KB STRUCTURE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ mycompany.gbkb/ │
│ │ │
│ ├── 📁 policies/ ◄── Company policies │
│ │ ├── refund-policy.pdf │
│ │ ├── privacy-policy.pdf │
│ │ └── terms-of-service.pdf │
│ │ │
│ ├── 📁 products/ ◄── Product documentation │
│ │ ├── product-guide.pdf │
│ │ ├── user-manual.pdf │
│ │ └── specifications.xlsx │
│ │ │
│ ├── 📁 support/ ◄── Support resources │
│ │ ├── faq.docx │
│ │ └── troubleshooting.pdf │
│ │ │
│ └── 📁 internal/ ◄── Internal documentation │
│ ├── processes.docx │
│ └── guidelines.pdf │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### Content Guidelines
1. **Be specific** — Clear, detailed content produces better answers
2. **Use headings** — Helps the bot find relevant sections
3. **Include keywords** — Use terms users are likely to search for
4. **Update regularly** — Keep documents current
5. **Remove duplicates** — Avoid conflicting information
### Naming Conventions
| ✅ Good Names | ❌ Bad Names |
|--------------|-------------|
| `refund-policy-2024.pdf` | `doc1.pdf` |
| `product-manual-v2.docx` | `final final (2).docx` |
| `employee-handbook.pdf` | `new document.pdf` |
---
## Advanced: Using KB in Dialogs
You can reference the knowledge base in your BASIC dialogs:
```basic
' Activate a specific knowledge base
USE KB "mycompany"
' Ask the user what they want to know
TALK "What would you like to know about?"
HEAR question
' The bot will automatically search the KB and respond
```
### Multiple Knowledge Bases
You can have different knowledge bases for different purposes:
```basic
' Switch between knowledge bases based on topic
TALK "Are you asking about Products or Policies?"
HEAR topic
IF topic = "Products" THEN
USE KB "products"
ELSE IF topic = "Policies" THEN
USE KB "policies"
END IF
TALK "What would you like to know?"
HEAR question
```
---
## Next Steps
| Next Tutorial | What You'll Learn |
|---------------|-------------------|
| [Import a Website](./import-website.md) | Crawl web pages into your KB |
| [Create FAQ Responses](./create-faq.md) | Define question-answer pairs |
| [Manage Collections](./manage-collections.md) | Organize knowledge by topic |
---
## Quick Reference
### Chat Commands
| Command | Description |
|---------|-------------|
| `/kb stats` | Show knowledge base statistics |
| `/reindex` | Rebuild the search index |
| `/kb list` | List all KB collections |
### BASIC Keywords
| Keyword | Description | Example |
|---------|-------------|---------|
| `USE KB` | Activate a KB | `USE KB "mycompany"` |
| `CLEAR KB` | Clear current KB | `CLEAR KB` |
| `KB STATISTICS` | Get KB info | `stats = KB STATISTICS` |
### File Size Limits
| File Type | Max Size |
|-----------|----------|
| PDF | 50 MB |
| Word | 25 MB |
| Excel | 25 MB |
| Text/MD | 10 MB |
---
*Tutorial 3 of 30 • [Back to How-To Index](./README.md) • [Next: Import a Website →](./import-website.md)*

View file

@ -0,0 +1,746 @@
# How To: Connect WhatsApp
> **Tutorial 5 of the Channels Series**
>
> *Connect your bot to WhatsApp in 20 minutes*
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📱 CONNECT WHATSAPP TO YOUR BOT │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
│ │ │ Meta │ │ Create │ │Configure│ │ Test │ │ │
│ │ │ Account │ │ App │ │ Bot │ │ Channel │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Objective
By the end of this tutorial, you will have:
- Created a Meta Business account
- Set up a WhatsApp Business App
- Connected WhatsApp to your General Bots instance
- Tested the connection with a real message
---
## Time Required
⏱️ **20 minutes**
---
## Prerequisites
Before you begin, make sure you have:
- [ ] A working bot (see [Create Your First Bot](./create-first-bot.md))
- [ ] A phone number for WhatsApp Business (cannot be used with regular WhatsApp)
- [ ] A Facebook account
- [ ] Administrator access to General Bots
---
## Understanding WhatsApp Integration
```
┌─────────────────────────────────────────────────────────────────────────┐
│ HOW WHATSAPP INTEGRATION WORKS │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User sends message on WhatsApp │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ WhatsApp Cloud API │ │
│ │ (Meta's servers receive message) │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ │ Webhook │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ General Bots Server │ │
│ │ (Your bot processes the message) │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ │ API Call │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ WhatsApp Cloud API │ │
│ │ (Sends reply to user) │ │
│ └────────────────────────┬────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ User receives bot response on WhatsApp │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Step 1: Set Up Meta Business Account
### 1.1 Go to Meta for Developers
Open your browser and navigate to:
**https://developers.facebook.com**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 🌐 Browser [─][□][×]│
├─────────────────────────────────────────────────────────────────────────┤
│ ← → ↻ │ https://developers.facebook.com │ ☆ │ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Meta for Developers │
│ │
│ ┌─────────────────────┐ │
│ │ Log In │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 1.2 Log In with Facebook
1. Click **Log In**
2. Enter your Facebook credentials
3. Click **Log In**
### 1.3 Create a Meta Business Account (If Needed)
If you don't have a business account:
1. Go to **https://business.facebook.com**
2. Click **Create Account**
3. Enter your business name
4. Enter your name and business email
5. Click **Submit**
💡 **Note**: You can use your personal Facebook account, but a business account is recommended for production use.
**Checkpoint**: You should now be logged into Meta for Developers.
---
## Step 2: Create a WhatsApp App
### 2.1 Go to My Apps
Click **My Apps** in the top navigation.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Meta for Developers [My Apps ▼] [👤 Account] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ My Apps │
│ ─────── │
│ │
│ ┌─────────────────────────┐ │
│ │ + Create App │ ◄── Click here │
│ └─────────────────────────┘ │
│ │
│ You don't have any apps yet. │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.2 Create a New App
1. Click **Create App**
2. Select **Business** as the app type
3. Click **Next**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Create an App [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Select an app type: │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Consumer │ │ ● Business │ │ Gaming │ │
│ │ │ │ ◄── Select │ │ │ │
│ │ For consumer │ │ │ │ For game │ │
│ │ apps │ │ For business │ │ integrations │ │
│ │ │ │ integrations │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ [Next] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.3 Fill In App Details
| Field | What to Enter | Example |
|-------|---------------|---------|
| **App Name** | Your bot's name | My Company Bot |
| **App Contact Email** | Your email | admin@company.com |
| **Business Account** | Select or create | My Company |
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Add App Details [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ App Name: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ My Company Bot │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ App Contact Email: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ admin@company.com │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Business Account: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ My Company [▼] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ [Create App] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
4. Click **Create App**
5. Complete the security check if prompted
### 2.4 Add WhatsApp to Your App
1. In the app dashboard, scroll to **Add Products**
2. Find **WhatsApp** and click **Set Up**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Add Products to Your App │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Messenger │ │ 📱 WhatsApp │ │ Instagram │ │
│ │ │ │ │ │ │ │
│ │ [Set Up] │ │ [Set Up] ◄── │ │ [Set Up] │ │
│ │ │ │ Click here │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Checkpoint**: WhatsApp should now appear in your app's left sidebar.
---
## Step 3: Configure WhatsApp Settings
### 3.1 Get Your API Credentials
In the left sidebar, click **WhatsApp****API Setup**.
You'll see:
- **Phone number ID** - Identifies your WhatsApp number
- **WhatsApp Business Account ID** - Your business account
- **Temporary access token** - For testing (expires in 24 hours)
```
┌─────────────────────────────────────────────────────────────────────────┐
│ WhatsApp > API Setup │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ STEP 1: Select Phone Numbers │
│ ──────────────────────────── │
│ │
│ From: [Test Number - 15550001234 ▼] │
│ │
│ To: (Add a recipient phone number for testing) │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ +1 555 123 4567 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ STEP 2: Send Messages with the API │
│ ────────────────────────────────── │
│ │
│ Temporary Access Token: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ EAAGm0PX4ZCp... [Copy] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ⚠️ This token expires in 24 hours. Use System User for production. │
│ │
│ Phone Number ID: 123456789012345 [Copy] │
│ WhatsApp Business Account ID: 987654321098765 [Copy] │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
📝 **Write down these values** - You'll need them in the next step:
- Phone Number ID: `_______________`
- Access Token: `_______________`
### 3.2 Create a Permanent Access Token
For production, you need a permanent token:
1. Go to **Business Settings** → **System Users**
2. Click **Add** to create a system user
3. Name it (e.g., "WhatsApp Bot")
4. Set role to **Admin**
5. Click **Generate Token**
6. Select your app and the `whatsapp_business_messaging` permission
7. Click **Generate Token**
💡 **Important**: Save this token securely! You won't be able to see it again.
### 3.3 Configure the Webhook
The webhook tells Meta where to send incoming messages.
1. In the left sidebar, click **WhatsApp** → **Configuration**
2. Under **Webhook**, click **Edit**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Webhook Configuration [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Callback URL: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ https://your-bot-server.com/webhook/whatsapp │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Verify Token: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ your-custom-verify-token-here │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ⚠️ Your server must respond to Meta's verification request │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Verify and Save │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Enter these values:**
| Field | Value |
|-------|-------|
| Callback URL | `https://your-server.com/webhook/whatsapp` |
| Verify Token | A secret string you create (e.g., `my_bot_verify_123`) |
3. Click **Verify and Save**
### 3.4 Subscribe to Webhook Events
After verifying, select which events to receive:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Webhook Fields │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ☑ messages ◄── Required! Receive incoming messages │
│ ☐ message_template_status_update │
│ ☐ phone_number_name_update │
│ ☐ phone_number_quality_update │
│ ☑ account_review_update │
│ ☐ account_update │
│ ☐ business_capability_update │
│ ☐ flows │
│ ☑ security │
│ ☑ message_echoes │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
At minimum, select:
- **messages** (required - to receive user messages)
**Checkpoint**: Webhook should show as "Active" with a green indicator.
---
## Step 4: Configure General Bots
### 4.1 Open Bot Settings
1. In General Bots, go to **Sources**
2. Click **⚙️** on your bot
3. Go to the **Channels** tab
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Bot Settings: support [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────┬──────────┬──────────┬──────────┬──────────┐ │
│ │ General │ AI │*Channels*│ Security │ Advanced │ │
│ └──────────┴──────────┴──────────┴──────────┴──────────┘ │
│ │
│ CONNECTED CHANNELS │
│ ────────────────── │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 🌐 Web Chat Status: ● On │ │
│ │ Embedded widget on your website │ │
│ │ [Configure] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 📱 WhatsApp Status: ○ Off │ │
│ │ Not configured │ │
│ │ [Configure] ◄── Click here │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 4.2 Enter WhatsApp Credentials
Click **Configure** for WhatsApp and enter your credentials:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ WhatsApp Configuration [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ META API CREDENTIALS │
│ ──────────────────── │
│ │
│ Phone Number ID: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 123456789012345 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Access Token: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ •••••••••••••••••••••••••••••••••••••• │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Verify Token: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ my_bot_verify_123 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ⚠️ Must match the token you set in Meta Developer Portal │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ WEBHOOK URL (provide this to Meta) │
│ ────────────────────────────────── │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ https://your-server.com/webhook/whatsapp [Copy] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Test Connection │ │ Save │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 4.3 Test the Connection
1. Click **Test Connection**
2. You should see "Connection successful!"
If the test fails, check:
- Token is correct and not expired
- Phone Number ID is correct
- Your server is accessible from the internet
### 4.4 Save and Enable
1. Click **Save**
2. Toggle WhatsApp to **On**
**Checkpoint**: WhatsApp should now show Status: ● On
---
## Step 5: Test Your WhatsApp Bot
### 5.1 Add Test Phone Number
In Meta Developer Portal:
1. Go to **WhatsApp** → **API Setup**
2. Under "To", add your phone number
3. Click **Send** to receive a test message
### 5.2 Send a Test Message
1. Open WhatsApp on your phone
2. Message the bot's number (the test number from Meta)
3. Send: "Hello"
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ WhatsApp │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ My Company Bot │ │
│ │ +1 555 000 1234 │ │
│ │ │ │
│ │ ───────────────────────────────────────────────────────────── │ │
│ │ │ │
│ │ ┌───────────────┐ │ │
│ │ │ Hello │ │ │
│ │ │ 10:30 │ │ │
│ │ └───────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ Hello! How can I help you today? │ │ │
│ │ │ 10:30 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ┌───────────────┐ │ │
│ │ │ I need help │ │ │
│ │ │ with my order │ │ │
│ │ │ 10:31 │ │ │
│ │ └───────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ I'd be happy to help with your order! │ │ │
│ │ │ What's your order number? │ │ │
│ │ │ 10:31 │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Type a message... 📎 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 5.3 Verify in General Bots
Check that the conversation appears in Analytics:
1. Go to **Analytics** → **Sessions**
2. You should see a new session with channel "WhatsApp"
---
## 🎉 Congratulations!
Your bot is now connected to WhatsApp! Users can message your WhatsApp Business number and receive responses from your bot.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ✓ SUCCESS! │
│ │
│ Your WhatsApp bot is live! │
│ │
│ ┌───────────┐ ┌───────────┐ ┌──────────┐ │
│ │ │ │ │ │ │ │
│ │ 📱 │ ────────▶ │ 🤖 │ ────────▶ │ 💬 │ │
│ │ WhatsApp │ │ General │ │ Bot │ │
│ │ User │ ◀──────── │ Bots │ ◀──────── │ Response │ │
│ │ │ │ │ │ │ │
│ └───────────┘ └───────────┘ └──────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Going to Production
### Add a Real Phone Number
The test number has limitations. For production:
1. Go to **WhatsApp****Phone Numbers** in Meta Developer Portal
2. Click **Add Phone Number**
3. Enter your business phone number
4. Verify via SMS or voice call
5. Update the Phone Number ID in General Bots settings
### Submit for App Review
Before going live with real customers:
1. Go to **App Review** in Meta Developer Portal
2. Request `whatsapp_business_messaging` permission
3. Complete the review process (takes 2-5 business days)
### Set Up Message Templates
For proactive messages (not replies), you need approved templates:
1. Go to **WhatsApp** → **Message Templates**
2. Create templates for notifications, alerts, etc.
3. Wait for Meta approval (usually 24 hours)
---
## Troubleshooting
### Problem: "Webhook verification failed"
**Possible causes:**
1. Verify token doesn't match
2. Server not responding
3. HTTPS not configured
**Solution:**
1. Check the verify token matches exactly in both places
2. Verify your server is accessible: `curl https://your-server.com/webhook/whatsapp`
3. Ensure you have a valid SSL certificate
---
### Problem: Messages not arriving
**Possible causes:**
1. Webhook not subscribed to "messages"
2. App in development mode with unlisted numbers
3. Access token expired
**Solution:**
1. Check webhook subscriptions include "messages"
2. Add test phone numbers in API Setup
3. Generate a new access token
---
### Problem: Bot responds slowly
**Possible causes:**
1. Network latency
2. Bot processing time
3. Meta rate limits
**Solution:**
1. Ensure server is geographically close to users
2. Optimize bot dialog processing
3. Check rate limit status in Meta dashboard
---
### Problem: "Error 190: Invalid OAuth access token"
**Possible causes:**
1. Token expired (temporary tokens last 24 hours)
2. Token was revoked
3. Wrong token used
**Solution:**
1. Generate a new System User token (permanent)
2. Update the token in General Bots settings
3. Verify you're using the WhatsApp access token, not a Facebook token
---
### Problem: Phone number shows as unverified
**Possible causes:**
1. Verification not completed
2. Two-factor authentication issue
3. Number already in use
**Solution:**
1. Re-request verification code
2. Check business verification status
3. Contact Meta support if number was previously registered
---
## What You Learned
In this tutorial, you:
- ✅ Created a Meta Developer account and app
- ✅ Configured WhatsApp Cloud API
- ✅ Set up webhook for incoming messages
- ✅ Connected WhatsApp to General Bots
- ✅ Tested the integration with real messages
---
## Next Steps
Now that WhatsApp is connected, try these:
| Next Tutorial | What You'll Learn |
|---------------|-------------------|
| [Write Your First Dialog](./write-first-dialog.md) | Create custom conversation flows |
| [Add KB Documents](./add-kb-documents.md) | Make your bot smarter |
| [Monitor Sessions](./monitor-sessions.md) | Track WhatsApp conversations |
---
## Quick Reference
### WhatsApp Message Limits
| Tier | Messages/Day | How to Qualify |
|------|--------------|----------------|
| Unverified | 250 | New accounts |
| Verified | 1,000 | Complete business verification |
| Tier 1 | 10,000 | Good quality rating |
| Tier 2 | 100,000 | Maintain quality |
| Tier 3 | Unlimited | High volume, good quality |
### Quality Rating
Meta monitors your WhatsApp quality based on:
- User blocks and reports
- Message template quality
- Response time
Keep quality high by:
- Responding to all messages
- Not spamming users
- Using approved templates for outbound messages
### Key URLs
| Resource | URL |
|----------|-----|
| Meta for Developers | https://developers.facebook.com |
| Meta Business Suite | https://business.facebook.com |
| WhatsApp Cloud API Docs | https://developers.facebook.com/docs/whatsapp |
| API Status | https://metastatus.com |
### Environment Variables
If configuring via environment variables:
```
WHATSAPP_PHONE_NUMBER_ID=123456789012345
WHATSAPP_ACCESS_TOKEN=EAAGm0PX4ZCp...
WHATSAPP_VERIFY_TOKEN=my_bot_verify_123
WHATSAPP_BUSINESS_ACCOUNT_ID=987654321098765
```
---
## See Also
- [Chat App](../apps/chat.md) - Web chat interface
- [Sources App](../apps/sources.md) - Bot configuration
- [Compliance App](../apps/compliance.md) - Data privacy for WhatsApp
- [BASIC Keywords](../../chapter-06-gbdialog/keywords-reference.md) - WhatsApp-specific keywords

View file

@ -0,0 +1,481 @@
# How To: Create Your First Bot
> **Tutorial 1 of the Getting Started Series**
>
> *Follow these simple steps to create a working bot in 10 minutes*
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 🤖 CREATE YOUR FIRST BOT │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
│ │ │ Access │ │ Create │ │Configure│ │ Test │ │ │
│ │ │ Suite │ │ Bot │ │ Bot │ │ Bot │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Objective
By the end of this tutorial, you will have:
- Created a new bot instance
- Configured basic settings
- Written a simple greeting
- Tested your bot by talking to it
---
## Time Required
⏱️ **10 minutes**
---
## Prerequisites
Before you begin, make sure you have:
- [ ] Access to General Bots Suite (URL provided by your administrator)
- [ ] A web browser (Chrome, Firefox, Safari, or Edge)
- [ ] Administrator or Bot Creator permissions
---
## Step 1: Access the Suite
### 1.1 Open Your Browser
Launch your preferred web browser by clicking its icon.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 🌐 Browser [─][□][×]│
├─────────────────────────────────────────────────────────────────────────┤
│ ← → ↻ │ https://your-company.bot:8080 │ ☆ │ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Loading... │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 1.2 Navigate to Your General Bots URL
Type your General Bots address in the address bar and press **Enter**.
💡 **Tip**: Your URL will look something like:
- `http://localhost:8080` (development)
- `https://bots.yourcompany.com` (production)
- `https://app.pragmatismo.cloud` (cloud hosted)
### 1.3 Log In (If Required)
If you see a login screen:
1. Enter your **username** or **email**
2. Enter your **password**
3. Click **Sign In**
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌────────────────────────────┐ │
│ │ 🤖 General Bots │ │
│ │ │ │
│ │ Username: │ │
│ │ ┌────────────────────┐ │ │
│ │ │ admin@company.com │ │ │
│ │ └────────────────────┘ │ │
│ │ │ │
│ │ Password: │ │
│ │ ┌────────────────────┐ │ │
│ │ │ •••••••••••• │ │ │
│ │ └────────────────────┘ │ │
│ │ │ │
│ │ ┌────────────────────┐ │ │
│ │ │ Sign In ──► │ │ │
│ │ └────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Checkpoint**: You should now see the General Bots Suite interface.
---
## Step 2: Create a New Bot
### 2.1 Open the Apps Menu
Click the **nine-dot grid icon** (⋮⋮⋮) in the top-right corner of the screen.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 🤖 General Bots [⋮⋮⋮] ◄── Click here │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ │
```
### 2.2 Select "Sources"
From the apps menu that appears, click **Sources**.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌───────────────────┐ │
│ │ 💬 Chat │ │
│ │ 📁 Drive │ │
│ │ ✓ Tasks │ │
│ │ ✉ Mail │ │
│ │ 📝 Paper │ │
│ │ 📊 Analytics │ │
│ │ ▶ 📋 Sources ◀───┼─── Click here │
│ │ 🎨 Designer │ │
│ │ ⚙️ Settings │ │
│ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.3 Click "New Bot"
In the Sources application, locate and click the **New Bot** button.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Sources │
├─────────────────────────────────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Templates │ │ Prompts │ │ Bots │ ◄── Active Tab │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Your Bots ┌─────────────────┐ │
│ ───────── │ New Bot │ ◄── Click │
│ └─────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ No bots yet. Create your first bot! │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.4 Enter Bot Details
A dialog box will appear. Fill in the following fields:
| Field | What to Enter | Example |
|-------|---------------|---------|
| **Bot Name** | A unique identifier (no spaces) | `mycompany` |
| **Display Name** | Friendly name shown to users | `My Company Assistant` |
| **Description** | What your bot does | `Helps employees find information` |
| **Template** | Starting point (select from dropdown) | `default` |
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Create New Bot [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Bot Name * │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ mycompany │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ ⚠️ Use lowercase letters, numbers, and hyphens only │
│ │
│ Display Name * │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ My Company Assistant │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Description │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Helps employees find information and complete tasks │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Template │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ default [▼] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Cancel │ │ Create Bot ──► │ │
│ └──────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.5 Click "Create Bot"
Click the **Create Bot** button to create your bot.
💡 **Tip**: The bot creation process takes a few seconds. You'll see a progress indicator.
**Checkpoint**: Your new bot should appear in the bot list.
---
## Step 3: Configure Basic Settings
### 3.1 Open Bot Settings
Click on your new bot to select it, then click **Settings** (or the ⚙️ icon).
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Your Bots │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 🤖 mycompany [⚙️] │◄──│
│ │ My Company Assistant │ │
│ │ Status: ● Active │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Click the ⚙️ icon
```
### 3.2 Set the Welcome Message
Find the **Welcome Message** field and enter a friendly greeting:
```
Welcome Message:
┌─────────────────────────────────────────────────────────────────────────┐
│ Hello! 👋 I'm your Company Assistant. I can help you with: │
│ │
│ • Finding documents and information │
│ • Answering questions about policies │
│ • Creating tasks and reminders │
│ │
│ How can I help you today? │
└─────────────────────────────────────────────────────────────────────────┘
```
### 3.3 Configure AI Model (Optional)
If you have API keys for AI services, configure them:
| Setting | Description | Example Value |
|---------|-------------|---------------|
| **LLM Provider** | AI service to use | `openai` |
| **Model** | Specific model | `gpt-4o` |
| **API Key** | Your API key | `sk-...` |
⚠️ **Warning**: Keep your API keys secret. Never share them.
### 3.4 Save Settings
Click the **Save** button to save your configuration.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Bot Settings [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ [General] [AI Model] [Channels] [Advanced] │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ ┌────────────────────┐ │
│ │ 💾 Save │◄────│
│ └────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Click Save
```
**Checkpoint**: Your settings are saved. The bot is ready to test.
---
## Step 4: Test Your Bot
### 4.1 Open Chat
Click the **Chat** app from the Apps Menu (⋮⋮⋮).
### 4.2 Select Your Bot
If you have multiple bots, select yours from the bot dropdown:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 💬 Chat [mycompany ▼] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🤖 My Company Assistant │ │
│ │ │ │
│ │ Hello! 👋 I'm your Company Assistant. I can help │ │
│ │ you with: │ │
│ │ │ │
│ │ • Finding documents and information │ │
│ │ • Answering questions about policies │ │
│ │ • Creating tasks and reminders │ │
│ │ │ │
│ │ How can I help you today? │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ Type your message... [↑] │
└─────────────────────────────────────────────────────────────────────────┘
```
### 4.3 Send a Test Message
Type a simple message and press **Enter**:
```
You: Hello!
```
### 4.4 Verify the Response
Your bot should respond! If it does, congratulations — your bot is working!
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 👤 You │ │
│ │ Hello! │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🤖 My Company Assistant │ │
│ │ Hello! How can I assist you today? │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Checkpoint**: Your bot responds to messages. Setup complete!
---
## 🎉 Congratulations!
You have successfully created your first bot! Here's what you accomplished:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ✓ Accessed General Bots Suite │
│ ✓ Created a new bot instance │
│ ✓ Configured basic settings │
│ ✓ Tested the bot with a conversation │
│ │
│ Your bot "mycompany" is now ready to use! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Troubleshooting
### Problem: "Create Bot" button is disabled
**Cause**: Required fields are empty or invalid.
**Solution**:
1. Check that Bot Name contains only lowercase letters, numbers, and hyphens
2. Ensure Display Name is not empty
3. Verify a template is selected
### Problem: Bot doesn't respond
**Cause**: AI model not configured or API key invalid.
**Solution**:
1. Open bot settings
2. Verify AI model configuration
3. Check that API key is correct
4. Ensure you have API credits remaining
### Problem: "Permission denied" error
**Cause**: Your account doesn't have bot creation rights.
**Solution**:
1. Contact your administrator
2. Request "Bot Creator" or "Administrator" role
### Problem: Page won't load
**Cause**: Network or server issue.
**Solution**:
1. Check your internet connection
2. Try refreshing the page (F5 or Ctrl+R)
3. Clear browser cache
4. Contact your system administrator
---
## What You Learned
In this tutorial, you learned:
| Concept | Description |
|---------|-------------|
| **Bot Instance** | A unique bot with its own configuration |
| **Bot Name** | Technical identifier used internally |
| **Display Name** | Friendly name shown to users |
| **Template** | Pre-built starting point for your bot |
| **Welcome Message** | First message users see |
---
## Next Steps
Now that you have a working bot, continue learning:
| Next Tutorial | What You'll Learn |
|---------------|-------------------|
| [Your First Conversation](./first-conversation.md) | Understanding how conversations work |
| [Add Knowledge Base Documents](./add-kb-documents.md) | Teaching your bot from files |
| [Write Your First Dialog](./write-first-dialog.md) | Creating custom conversation flows |
---
## Quick Reference
### Bot Naming Rules
- ✅ `mycompany` — Good
- ✅ `hr-assistant` — Good
- ✅ `support2024` — Good
- ❌ `My Company` — No spaces
- ❌ `HR_Bot` — No underscores
- ❌ `Support@2024` — No special characters
### Essential Settings Checklist
- [ ] Bot Name (unique identifier)
- [ ] Display Name (user-friendly)
- [ ] Welcome Message (first impression)
- [ ] AI Model (for responses)
- [ ] Language (for localization)
---
*Tutorial 1 of 30 • [Back to How-To Index](./README.md) • [Next: Your First Conversation →](./first-conversation.md)*

View file

@ -0,0 +1,504 @@
# How To: Monitor Your Bot
> **Tutorial 12 of the Analytics & Monitoring Series**
>
> *Watch conversations and system health in real-time*
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📊 MONITOR YOUR BOT │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
│ │ │ Access │ │ View │ │ Check │ │ Set │ │ │
│ │ │Dashboard│ │Sessions │ │ Health │ │ Alerts │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Objective
By the end of this tutorial, you will have:
- Accessed the monitoring dashboard
- Viewed active sessions and conversations
- Checked system health and resources
- Understood the live system architecture
- Configured alerts for important events
---
## Time Required
⏱️ **10 minutes**
---
## Prerequisites
Before you begin, make sure you have:
- [ ] A running bot with some activity
- [ ] Administrator or Monitor role permissions
- [ ] Access to the General Bots Suite
---
## Understanding the System Architecture
Your General Bots deployment is a **living system** of interconnected components. Understanding how they work together helps you monitor effectively.
![Live Monitoring Organism](../../assets/suite/live-monitoring-organism.svg)
### Component Overview
| Component | Purpose | Status Indicators |
|-----------|---------|-------------------|
| **BotServer** | Core application, handles all requests | Response time, active sessions |
| **PostgreSQL** | Primary database, stores users & config | Connections, query rate |
| **Qdrant** | Vector database, powers semantic search | Vector count, search latency |
| **MinIO** | File storage, manages documents | Storage used, object count |
| **BotModels** | LLM server, generates AI responses | Tokens/hour, model latency |
| **Vault** | Secrets manager, stores API keys | Sealed status, policy count |
| **Redis** | Cache layer, speeds up responses | Hit rate, memory usage |
| **InfluxDB** | Metrics database, stores analytics | Points/sec, retention |
---
## Step 1: Access the Monitoring Dashboard
### 1.1 Open the Apps Menu
Click the **nine-dot grid** (⋮⋮⋮) in the top-right corner.
### 1.2 Select Monitoring
Click **Analytics** or **Monitoring** (depending on your configuration).
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌───────────────────┐ │
│ │ 💬 Chat │ │
│ │ 📁 Drive │ │
│ │ 📊 Analytics │ ◄── May be here │
│ │ 📈 Monitoring │ ◄── Or here │
│ │ ⚙️ Settings │ │
│ └───────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 1.3 View the Dashboard
The monitoring dashboard displays real-time metrics:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 📊 Monitoring Dashboard 🔴 LIVE │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ SESSIONS │ │ MESSAGES │ │ RESPONSE │ │
│ │ │ │ │ │ │ │
│ │ 247 │ │ 12.4K │ │ 1.2s │ │
│ │ ● Active │ │ Today │ │ Average │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ │
│ SYSTEM RESOURCES │
│ ───────────────── │
│ CPU [████████████████░░░░░░░░░░░░░░] 70% │
│ MEM [████████████████████░░░░░░░░░░] 60% │
│ GPU [████████████░░░░░░░░░░░░░░░░░░] 40% │
│ DISK [████████░░░░░░░░░░░░░░░░░░░░░░] 28% │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Checkpoint**: You can see the monitoring dashboard with live metrics.
---
## Step 2: View Active Sessions
### 2.1 Navigate to Sessions Panel
Look for the **Sessions** or **Conversations** section:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Active Sessions (247) [Refresh 🔄] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ID │ User │ Channel │ Started │ Messages │
│ ──────────┼───────────────┼───────────┼──────────────┼──────────── │
│ a1b2c3d4 │ +5511999... │ WhatsApp │ 2 min ago │ 12 │
│ e5f6g7h8 │ john@acme... │ Web │ 5 min ago │ 8 │
│ i9j0k1l2 │ +5521888... │ WhatsApp │ 8 min ago │ 23 │
│ m3n4o5p6 │ support@... │ Email │ 15 min ago │ 4 │
│ q7r8s9t0 │ jane@... │ Web │ 18 min ago │ 15 │
│ │
│ ◀ 1 2 3 4 5 ... 25 ▶ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.2 View Session Details
Click on a session to see the full conversation:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Session: a1b2c3d4 [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User: +5511999888777 │
│ Channel: WhatsApp │
│ Started: 2024-01-15 14:32:00 │
│ Duration: 2 min 34 sec │
│ Bot: mycompany │
│ │
│ ── Conversation ──────────────────────────────────────────────────────│
│ │
│ [14:32:00] 👤 User: Hello │
│ [14:32:01] 🤖 Bot: Hello! How can I help you today? │
│ [14:32:15] 👤 User: I want to check my order status │
│ [14:32:17] 🤖 Bot: I can help with that! What's your order number? │
│ [14:32:45] 👤 User: ORD-12345 │
│ [14:32:48] 🤖 Bot: Order ORD-12345 is being prepared for shipping... │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.3 Session Metrics
Understand key session metrics:
| Metric | Description | Good Value |
|--------|-------------|------------|
| **Active Sessions** | Currently open conversations | Depends on load |
| **Peak Today** | Maximum concurrent sessions | Track trends |
| **Avg Duration** | Average conversation length | 3-5 minutes typical |
| **Messages/Session** | Average messages per conversation | 5-10 typical |
**Checkpoint**: You can view active sessions and their conversations.
---
## Step 3: Check System Health
### 3.1 View Service Status
The dashboard shows the health of all components:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Service Health │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ● PostgreSQL Running v16.2 24/100 connections │
│ ● Qdrant Running v1.9.2 1.2M vectors │
│ ● MinIO Running v2024.01 45.2 GB stored │
│ ● BotModels Running v2.1.0 gpt-4o active │
│ ● Vault Sealed v1.15.0 156 secrets │
│ ● Redis Running v7.2.4 94.2% hit rate │
│ ● InfluxDB Running v2.7.3 2,450 pts/sec │
│ │
│ Legend: ● Running ● Warning ● Stopped │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 3.2 Understanding Status Colors
| Color | Status | Action Needed |
|-------|--------|---------------|
| 🟢 Green | Healthy/Running | None |
| 🟡 Yellow | Warning/Degraded | Investigate soon |
| 🔴 Red | Error/Stopped | Immediate action |
### 3.3 Check Resource Usage
Monitor resource utilization to prevent issues:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Resource Usage Last 24 Hours │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ CPU Usage │
│ 100%│ ╭──╮ │
│ 75%│ ╭──╮ ╭──╮ │ │ ╭──╮ │
│ 50%│╭──╮│ │╭─╯ ╰─╮╭──╯ ╰──╯ ╰──╮ │
│ 25%│ ╰──╯ ╰╯ ╰────────── │
│ 0%└──────────────────────────────────────────── │
│ 00:00 04:00 08:00 12:00 16:00 20:00 Now │
│ │
│ Memory Usage │
│ 100%│ │
│ 75%│ │
│ 50%│──────────────────────────────────────────── │
│ 25%│ │
│ 0%└──────────────────────────────────────────── │
│ 00:00 04:00 08:00 12:00 16:00 20:00 Now │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 3.4 Resource Thresholds
Take action when resources approach these limits:
| Resource | Warning | Critical | Action |
|----------|---------|----------|--------|
| CPU | > 80% | > 95% | Scale up or optimize |
| Memory | > 85% | > 95% | Add RAM or reduce cache |
| Disk | > 80% | > 90% | Clean up or add storage |
| GPU | > 90% | > 98% | Queue requests or scale |
**Checkpoint**: You can view system health and resource usage.
---
## Step 4: Set Up Alerts
### 4.1 Access Alert Settings
Navigate to **Settings** > **Alerts** or **Monitoring** > **Configure Alerts**.
### 4.2 Configure Alert Rules
Set up alerts for important events:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ Alert Configuration │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ☑ CPU Usage │
│ Threshold: [80] % For: [5] minutes │
│ Notify: ☑ Email ☑ Slack ☐ SMS │
│ │
│ ☑ Memory Usage │
│ Threshold: [85] % For: [5] minutes │
│ Notify: ☑ Email ☐ Slack ☐ SMS │
│ │
│ ☑ Response Time │
│ Threshold: [5000] ms For: [3] minutes │
│ Notify: ☑ Email ☑ Slack ☐ SMS │
│ │
│ ☑ Service Down │
│ Services: ☑ PostgreSQL ☑ Qdrant ☑ BotModels │
│ Notify: ☑ Email ☑ Slack ☑ SMS │
│ │
│ ┌─────────────────┐ │
│ │ 💾 Save │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 4.3 Configure via config.csv
You can also set alerts in your bot's configuration file:
```csv
key,value
alert-cpu-threshold,80
alert-memory-threshold,85
alert-disk-threshold,90
alert-response-time-ms,5000
alert-email,admin@company.com
alert-slack-webhook,https://hooks.slack.com/...
```
### 4.4 Test Alerts
Verify your alerts are working:
1. Set a low threshold temporarily (e.g., CPU > 1%)
2. Wait for the alert to trigger
3. Check your email/Slack for the notification
4. Reset the threshold to normal
**Checkpoint**: Alerts are configured and tested.
---
## 🎉 Congratulations!
You can now monitor your bot effectively! Here's what you learned:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ✓ Accessed the monitoring dashboard │
│ ✓ Viewed active sessions and conversations │
│ ✓ Checked system health and services │
│ ✓ Understood resource usage metrics │
│ ✓ Configured alerts for important events │
│ │
│ You're now equipped to keep your bot healthy! │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Troubleshooting
### Problem: Dashboard shows no data
**Cause**: Monitoring services may not be collecting data.
**Solution**:
1. Check that InfluxDB is running
2. Verify the monitoring agent is enabled
3. Wait a few minutes for data collection
### Problem: Sessions show as "Unknown User"
**Cause**: User identification not configured.
**Solution**:
1. Enable user tracking in bot settings
2. Request user info at conversation start
3. Check privacy settings
### Problem: Alerts not being sent
**Cause**: Notification channels not configured correctly.
**Solution**:
1. Verify email/Slack settings
2. Check spam folders
3. Test webhook URLs manually
### Problem: High CPU but few sessions
**Cause**: Possible memory leak or inefficient code.
**Solution**:
1. Check for infinite loops in dialogs
2. Review LLM call frequency
3. Restart the bot service
---
## Monitoring API
Access monitoring data programmatically:
### Get System Status
```
GET /api/monitoring/status
```
**Response:**
```json
{
"sessions": {
"active": 247,
"peak_today": 312,
"avg_duration_seconds": 245
},
"messages": {
"today": 12400,
"this_hour": 890,
"avg_response_ms": 1200
},
"resources": {
"cpu_percent": 70,
"memory_percent": 60,
"gpu_percent": 40,
"disk_percent": 28
},
"services": {
"postgresql": "running",
"qdrant": "running",
"minio": "running",
"botmodels": "running",
"vault": "sealed",
"redis": "running",
"influxdb": "running"
}
}
```
### Get Historical Metrics
```
GET /api/monitoring/history?period=24h
```
### Get Session Details
```
GET /api/monitoring/sessions/{session_id}
```
---
## Quick Reference
### Dashboard Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| `R` | Refresh data |
| `F` | Toggle fullscreen |
| `S` | Show/hide sidebar |
| `1-7` | Switch dashboard tabs |
### Important Metrics to Watch
| Metric | Normal | Warning | Critical |
|--------|--------|---------|----------|
| Response Time | < 2s | 2-5s | > 5s |
| Error Rate | < 1% | 1-5% | > 5% |
| CPU Usage | < 70% | 70-85% | > 85% |
| Memory Usage | < 75% | 75-85% | > 85% |
| Queue Depth | < 100 | 100-500 | > 500 |
### Console Monitoring
For server-side monitoring:
```bash
# Start with monitoring output
./botserver --console --monitor
# Output:
# [MONITOR] 2024-01-15 14:32:00
# Sessions: 247 active (peak: 312)
# Messages: 12,400 today (890/hour)
# CPU: 70% | MEM: 60% | GPU: 40%
# Services: 7/7 running
```
---
## Next Steps
| Next Tutorial | What You'll Learn |
|---------------|-------------------|
| [Create Custom Reports](./create-reports.md) | Build dashboards for insights |
| [Export Analytics Data](./export-analytics.md) | Download metrics for analysis |
| [Performance Optimization](./performance-tips.md) | Make your bot faster |
---
*Tutorial 12 of 30 • [Back to How-To Index](./README.md) • [Next: Create Custom Reports →](./create-reports.md)*

View file

@ -0,0 +1,695 @@
# How To: Write Your First Dialog
> **Tutorial 5 of the BASIC Dialogs Series**
>
> *Create a simple conversation script in 20 minutes*
---
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📝 WRITE YOUR FIRST DIALOG │ │
│ │ │ │
│ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │
│ │ │ Step │───▶│ Step │───▶│ Step │───▶│ Step │ │ │
│ │ │ 1 │ │ 2 │ │ 3 │ │ 4 │ │ │
│ │ │ Create │ │ Write │ │ Test │ │ Enhance │ │ │
│ │ │ File │ │ Code │ │ Dialog │ │ Logic │ │ │
│ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Objective
By the end of this tutorial, you will have:
- Created a `.bas` dialog file
- Written code using TALK and HEAR keywords
- Used conditional logic (IF/THEN/ELSE)
- Stored and retrieved user information
- Tested your dialog in the chat interface
---
## Time Required
⏱️ **20 minutes**
---
## Prerequisites
Before you begin, make sure you have:
- [ ] A working bot (see [Create Your First Bot](./create-first-bot.md))
- [ ] Access to the Designer or Drive app
- [ ] Basic understanding of the chat interface
---
## What is a Dialog?
A **dialog** is a conversation script written in BASIC that controls how your bot talks with users. Think of it like a script for a play — you write what the bot should say and how it should respond to the user.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ HOW DIALOGS WORK │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User says: "Hello" │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Dialog Script │ ◄── Your BASIC code runs here │
│ │ (greeting.bas) │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ Bot says: "Hi there! What's your name?" │
│ │ │
│ ▼ │
│ User says: "Sarah" │
│ │ │
│ ▼ │
│ Bot says: "Nice to meet you, Sarah!" │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Step 1: Create the Dialog File
### 1.1 Open the Drive App
Click the **Apps Menu** (⋮⋮⋮) and select **Drive**.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 📁 Drive │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 📂 mycompany.gbai │
│ ├── 📂 mycompany.gbdialog ◄── Dialog files go here │
│ ├── 📂 mycompany.gbot │
│ ├── 📂 mycompany.gbkb │
│ └── 📂 mycompany.gbdrive │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 1.2 Navigate to the Dialog Folder
Double-click **mycompany.gbai**, then **mycompany.gbdialog**.
### 1.3 Create a New File
Click **New File** (or press Ctrl+N) and name it:
```
greeting.bas
```
⚠️ **Warning**: The file must end with `.bas` to be recognized as a dialog.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ New File [×] │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ File Name: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ greeting.bas │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Location: mycompany.gbai / mycompany.gbdialog / │
│ │
│ ┌──────────┐ ┌──────────────────┐ │
│ │ Cancel │ │ Create ──► │ │
│ └──────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**Checkpoint**: You should see `greeting.bas` in your dialog folder.
---
## Step 2: Write the Dialog Code
### 2.1 Open the File for Editing
Double-click `greeting.bas` to open it in the editor.
### 2.2 Write Your First Line
Type the following code:
```basic
TALK "Hello! Welcome to our service. 👋"
```
This is the simplest possible dialog — the bot just says one thing.
### 2.3 Add User Input
Now let's ask for the user's name:
```basic
TALK "Hello! Welcome to our service. 👋"
TALK "What is your name?"
HEAR name
TALK "Nice to meet you, " + name + "!"
```
Let's break this down:
| Line | What It Does |
|------|--------------|
| `TALK "..."` | Bot displays a message |
| `HEAR name` | Bot waits for user input, stores it in `name` |
| `"..." + name + "..."` | Combines text with the variable |
### 2.4 The Complete First Dialog
Here's your complete `greeting.bas`:
```basic
' ============================================
' GREETING DIALOG
' A friendly welcome conversation
' ============================================
' Greet the user
TALK "Hello! Welcome to our service. 👋"
' Ask for their name
TALK "What is your name?"
HEAR name
' Respond with their name
TALK "Nice to meet you, " + name + "!"
TALK "How can I help you today?"
```
💡 **Tip**: Lines starting with `'` are comments — they're ignored by the bot but help you understand the code.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 📝 greeting.bas [Save] ⌘S │
├─────────────────────────────────────────────────────────────────────────┤
│ 1 │ ' ============================================ │
│ 2 │ ' GREETING DIALOG │
│ 3 │ ' A friendly welcome conversation │
│ 4 │ ' ============================================ │
│ 5 │ │
│ 6 │ ' Greet the user │
│ 7 │ TALK "Hello! Welcome to our service. 👋" │
│ 8 │ │
│ 9 │ ' Ask for their name │
│ 10 │ TALK "What is your name?" │
│ 11 │ HEAR name │
│ 12 │ │
│ 13 │ ' Respond with their name │
│ 14 │ TALK "Nice to meet you, " + name + "!" │
│ 15 │ TALK "How can I help you today?" │
│ │ │
└─────────────────────────────────────────────────────────────────────────┘
```
### 2.5 Save the File
Press **Ctrl+S** or click the **Save** button.
**Checkpoint**: Your dialog file is saved and ready to test.
---
## Step 3: Test Your Dialog
### 3.1 Open Chat
Click the **Apps Menu** (⋮⋮⋮) and select **Chat**.
### 3.2 Trigger the Dialog
Type the command to run your dialog:
```
/greeting
```
Or simply type something that matches "greeting" — the system will recognize it.
### 3.3 Have the Conversation
Watch your dialog run:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 💬 Chat │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 👤 You │ │
│ │ /greeting │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🤖 Bot │ │
│ │ Hello! Welcome to our service. 👋 │ │
│ │ What is your name? │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 👤 You │ │
│ │ Sarah │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 🤖 Bot │ │
│ │ Nice to meet you, Sarah! │ │
│ │ How can I help you today? │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────────────┤
│ Type your message... [↑] │
└─────────────────────────────────────────────────────────────────────────┘
```
**Checkpoint**: Your dialog runs and responds correctly!
---
## Step 4: Enhance with Logic
Now let's make our dialog smarter with conditional logic.
### 4.1 Add Input Validation
Update your dialog to handle different types of input:
```basic
' ============================================
' GREETING DIALOG (Enhanced)
' A friendly welcome with input validation
' ============================================
TALK "Hello! Welcome to our service. 👋"
TALK "What is your name?"
HEAR name
' Check if name was provided
IF name = "" THEN
TALK "I didn't catch your name. That's okay!"
name = "friend"
END IF
TALK "Nice to meet you, " + name + "!"
```
### 4.2 Add Menu Options
Let's give the user choices:
```basic
' ============================================
' GREETING DIALOG (Full Version)
' Welcome with menu options
' ============================================
TALK "Hello! Welcome to our service. 👋"
TALK "What is your name?"
HEAR name
IF name = "" THEN
name = "friend"
END IF
TALK "Nice to meet you, " + name + "!"
TALK ""
TALK "How can I help you today?"
TALK "1. Learn about our services"
TALK "2. Contact support"
TALK "3. Check my account"
TALK ""
TALK "Please type 1, 2, or 3:"
HEAR choice
SELECT CASE choice
CASE "1"
TALK "Great! We offer AI-powered automation for businesses."
TALK "Would you like to schedule a demo?"
CASE "2"
TALK "I'll connect you with our support team."
TALK "Please describe your issue:"
HEAR issue
TALK "Thank you. A support agent will contact you about: " + issue
CASE "3"
TALK "To check your account, I'll need to verify your identity."
TALK "Please enter your email address:"
HEAR email
TALK "Looking up account for: " + email
CASE ELSE
TALK "I didn't understand that choice."
TALK "Please type 1, 2, or 3 next time."
END SELECT
TALK ""
TALK "Is there anything else I can help with, " + name + "?"
```
### 4.3 Understanding SELECT CASE
```
┌─────────────────────────────────────────────────────────────────────────┐
│ SELECT CASE EXPLAINED │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ User types: "2" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ SELECT CASE choice │ │
│ │ ┌─────────────┐ │ │
│ │ │ CASE "1" │──▶ Skip (not matched) │ │
│ │ └─────────────┘ │ │
│ │ ┌─────────────┐ │ │
│ │ │ CASE "2" ★ │──▶ EXECUTE! ───▶ "I'll connect you..." │ │
│ │ └─────────────┘ │ │
│ │ ┌─────────────┐ │ │
│ │ │ CASE "3" │──▶ Skip (not checked after match) │ │
│ │ └─────────────┘ │ │
│ │ ┌─────────────┐ │ │
│ │ │ CASE ELSE │──▶ Skip (only runs if nothing matched) │ │
│ │ └─────────────┘ │ │
│ │ END SELECT │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Step 5: Remember User Information
### 5.1 Store User Data
Use `SET USER MEMORY` to remember information between conversations:
```basic
' After getting the name
SET USER MEMORY "name", name
' Later, in another dialog, retrieve it:
savedName = GET USER MEMORY "name"
IF savedName <> "" THEN
TALK "Welcome back, " + savedName + "!"
ELSE
TALK "Hello! I don't think we've met before."
END IF
```
### 5.2 Store Bot-Wide Data
Use `SET BOT MEMORY` for data that applies to all users:
```basic
' Store a bot-wide counter
visitorCount = GET BOT MEMORY "visitor_count"
IF visitorCount = "" THEN
visitorCount = 0
END IF
visitorCount = visitorCount + 1
SET BOT MEMORY "visitor_count", visitorCount
TALK "You are visitor number " + visitorCount + " today!"
```
---
## Complete Example: Support Request Dialog
Here's a complete, practical dialog you can use as a template:
```basic
' ============================================
' SUPPORT REQUEST DIALOG
' Collects support ticket information
' ============================================
' Check if we know this user
userName = GET USER MEMORY "name"
IF userName = "" THEN
TALK "Hello! I'm here to help you create a support request."
TALK "First, what's your name?"
HEAR userName
SET USER MEMORY "name", userName
ELSE
TALK "Welcome back, " + userName + "!"
END IF
' Get contact information
TALK "What email should we use to contact you?"
HEAR AS email
email
IF email = "" THEN
TALK "I'll need an email to send you updates."
HEAR AS email
email
END IF
' Get issue category
TALK ""
TALK "What type of issue are you experiencing?"
TALK ""
TALK "1. 🔧 Technical problem"
TALK "2. 💳 Billing question"
TALK "3. 📦 Order status"
TALK "4. ❓ General question"
TALK ""
HEAR category
SELECT CASE category
CASE "1"
categoryName = "Technical"
TALK "I'm sorry you're having technical difficulties."
CASE "2"
categoryName = "Billing"
TALK "I can help with billing questions."
CASE "3"
categoryName = "Orders"
TALK "Let me check on your order."
CASE ELSE
categoryName = "General"
TALK "I'll make sure the right team sees this."
END SELECT
' Get description
TALK ""
TALK "Please describe your issue in detail:"
HEAR description
' Get urgency
TALK ""
TALK "How urgent is this?"
TALK "1. 🔴 Critical - I can't work"
TALK "2. 🟡 High - Affecting my work"
TALK "3. 🟢 Normal - When you get a chance"
HEAR urgency
SELECT CASE urgency
CASE "1"
urgencyLevel = "Critical"
CASE "2"
urgencyLevel = "High"
CASE ELSE
urgencyLevel = "Normal"
END SELECT
' Confirm ticket
TALK ""
TALK "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
TALK "📋 SUPPORT REQUEST SUMMARY"
TALK "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
TALK "Name: " + userName
TALK "Email: " + email
TALK "Category: " + categoryName
TALK "Urgency: " + urgencyLevel
TALK "Issue: " + description
TALK "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
TALK ""
TALK "Should I submit this request? (yes/no)"
HEAR confirm
IF confirm = "yes" OR confirm = "Yes" OR confirm = "YES" THEN
' Here you would typically save to a database
' For now, just confirm
TALK "✅ Your support request has been submitted!"
TALK "Ticket ID: SR-" + FORMAT(NOW, "yyyyMMddHHmm")
TALK "You'll receive a confirmation email at " + email
TALK "Our team typically responds within 24 hours."
ELSE
TALK "No problem! Your request was not submitted."
TALK "Feel free to start over when you're ready."
END IF
TALK ""
TALK "Is there anything else I can help with?"
```
---
## 🎉 Congratulations!
You've written your first dialog! Here's what you learned:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ │
│ ✓ Created a .bas dialog file │
│ ✓ Used TALK to display messages │
│ ✓ Used HEAR to get user input │
│ ✓ Combined text with variables │
│ ✓ Used IF/THEN/ELSE for decisions │
│ ✓ Used SELECT CASE for menus │
│ ✓ Stored data with SET USER MEMORY │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
---
## Troubleshooting
### Problem: Dialog doesn't start
**Cause**: File name or location is incorrect.
**Solution**:
1. Verify file ends with `.bas`
2. Confirm file is in the `.gbdialog` folder
3. Check there are no syntax errors
### Problem: "Unexpected token" error
**Cause**: Syntax error in your code.
**Solution**:
1. Check all strings have opening and closing quotes
2. Verify IF statements have matching END IF
3. Ensure SELECT CASE has END SELECT
### Problem: Variable is empty
**Cause**: User skipped the HEAR prompt.
**Solution**:
1. Add validation: `IF variable = "" THEN`
2. Provide a default value
3. Ask again if needed
### Problem: Bot doesn't remember data
**Cause**: Not using memory keywords correctly.
**Solution**:
1. Use `SET USER MEMORY "key", value` to save
2. Use `GET USER MEMORY "key"` to retrieve
3. Ensure key names match exactly (case-sensitive)
---
## Quick Reference
### Essential Keywords
| Keyword | Purpose | Example |
|---------|---------|---------|
| `TALK` | Display message | `TALK "Hello!"` |
| `HEAR` | Get user input | `HEAR name` |
| `HEAR AS type` | Get typed input | `HEAR AS email emailVar` |
| `SET` | Set variable | `SET x = 5` |
| `IF/THEN/ELSE` | Conditional | `IF x > 5 THEN ... END IF` |
| `SELECT CASE` | Menu choice | `SELECT CASE x ... END SELECT` |
| `SET USER MEMORY` | Save user data | `SET USER MEMORY "key", value` |
| `GET USER MEMORY` | Load user data | `x = GET USER MEMORY "key"` |
| `SET BOT MEMORY` | Save bot data | `SET BOT MEMORY "key", value` |
| `GET BOT MEMORY` | Load bot data | `x = GET BOT MEMORY "key"` |
### Common Patterns
**Greeting with memory:**
```basic
name = GET USER MEMORY "name"
IF name = "" THEN
TALK "What's your name?"
HEAR name
SET USER MEMORY "name", name
ELSE
TALK "Welcome back, " + name + "!"
END IF
```
**Menu with validation:**
```basic
TALK "Choose: 1, 2, or 3"
HEAR choice
IF choice < "1" OR choice > "3" THEN
TALK "Invalid choice, using default."
choice = "1"
END IF
```
**Loop for retries:**
```basic
attempts = 0
valid = FALSE
WHILE valid = FALSE AND attempts < 3
TALK "Enter your email:"
HEAR AS email input
IF input <> "" THEN
valid = TRUE
END IF
attempts = attempts + 1
WEND
```
---
## Next Steps
| Next Tutorial | What You'll Learn |
|---------------|-------------------|
| [Store User Information](./store-user-info.md) | Advanced memory patterns |
| [Call External APIs](./call-external-apis.md) | Connect to web services |
| [Send Automated Messages](./send-automated.md) | Scheduled broadcasts |
---
## Best Practices
1. **Comment your code** — Use `'` for explanations
2. **Validate all input** — Never assume users type correctly
3. **Provide defaults** — Handle empty responses gracefully
4. **Use clear prompts** — Tell users exactly what to type
5. **Confirm important actions** — Ask before submitting forms
6. **Use spaces in keywords**`SET BOT MEMORY` not `SET_BOT_MEMORY`
7. **Test thoroughly** — Try all menu options and edge cases
---
*Tutorial 5 of 30 • [Back to How-To Index](./README.md) • [Next: Store User Information →](./store-user-info.md)*

View file

@ -2,6 +2,19 @@
The Monitoring Dashboard provides real-time visibility into your General Bots deployment, displaying system health, active sessions, and resource utilization in a clean tree-based interface. The Monitoring Dashboard provides real-time visibility into your General Bots deployment, displaying system health, active sessions, and resource utilization in a clean tree-based interface.
## Live System Architecture
Your General Bots deployment is a living ecosystem of interconnected components. The diagram below shows how all services work together in real-time:
![Live Monitoring Organism](../assets/suite/live-monitoring-organism.svg)
This animated diagram shows:
- **BotServer** (center) - The core that orchestrates all interactions
- **Data Layer** (left) - PostgreSQL, Qdrant, and MinIO for storage
- **Services** (right) - BotModels, Vault, Redis for AI and security
- **Analytics** (bottom) - InfluxDB for metrics collection
- **Connection flows** - Animated data packets showing real-time communication
## Overview ## Overview
Access the Monitoring tab from the Suite interface to view: Access the Monitoring tab from the Suite interface to view:
@ -103,6 +116,20 @@ GET /api/monitoring/status
} }
``` ```
## Understanding Component Health
Each component in the system has specific health indicators:
| Component | Health Check | Warning Signs |
|-----------|--------------|---------------|
| **PostgreSQL** | Connection count, query rate | > 80 connections, slow queries |
| **Qdrant** | Vector count, search latency | > 50ms search time |
| **MinIO** | Storage usage, object count | > 80% storage used |
| **BotModels** | Token usage, response latency | > 2s response time |
| **Vault** | Seal status, policy count | Unsealed without auth |
| **Redis** | Hit rate, memory usage | < 80% hit rate |
| **InfluxDB** | Write rate, retention | Write failures |
## Console Mode ## Console Mode
In console mode, monitoring displays as text output: In console mode, monitoring displays as text output:
@ -193,8 +220,17 @@ Compatible with:
- Datadog - Datadog
- New Relic - New Relic
## Monitoring Best Practices
1. **Check the live diagram regularly** - The animated SVG shows real-time data flow
2. **Set up alerts early** - Don't wait for problems to configure notifications
3. **Monitor trends, not just values** - A slow increase in CPU is as important as a spike
4. **Keep historical data** - Use InfluxDB retention policies to maintain useful history
5. **Correlate metrics** - High response time + high CPU usually means scaling needed
## See Also ## See Also
- [How-To: Monitor Your Bot](./how-to/monitor-sessions.md) - Step-by-step monitoring tutorial
- [Console Mode](./console-mode.md) - Command-line interface - [Console Mode](./console-mode.md) - Command-line interface
- [Settings](../chapter-08-config/README.md) - Configuration options - [Settings](../chapter-08-config/README.md) - Configuration options
- [Monitoring API](../chapter-10-api/monitoring-api.md) - Full API reference - [Monitoring API](../chapter-10-api/monitoring-api.md) - Full API reference

View file

@ -0,0 +1,200 @@
# CARD
Creates beautiful Instagram-style social media posts by combining AI-generated images with optimized text overlays.
## Syntax
```basic
CARD image_prompt, text_prompt TO variable
CARD image_prompt, text_prompt, style TO variable
CARD image_prompt, text_prompt, style, count TO variable
```
## Parameters
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `image_prompt` | String | Yes | Description of the image to generate |
| `text_prompt` | String | Yes | Theme or topic for the text overlay |
| `style` | String | No | Visual style preset (default: "modern") |
| `count` | Number | No | Number of cards to generate (default: 1, max: 10) |
## Available Styles
| Style | Description |
|-------|-------------|
| `modern` | Trendy Instagram aesthetic with clean design |
| `minimal` | Simple composition with negative space, muted colors |
| `vibrant` | Bold colors, high saturation, energetic feel |
| `dark` | Moody atmosphere with dramatic lighting |
| `light` | Bright, airy, soft pastel colors |
| `gradient` | Smooth color gradients, abstract backgrounds |
| `polaroid` | Vintage style with warm, nostalgic tones |
| `magazine` | Editorial, high-fashion professional look |
| `story` | Optimized for Instagram Stories (9:16 ratio) |
| `carousel` | Consistent style for multi-image posts |
## Return Value
Returns an object (or array of objects when count > 1) containing:
```json
{
"image_path": "/path/to/generated/card.png",
"image_url": "https://storage.example.com/card.png",
"text_content": "The generated overlay text",
"hashtags": ["#nature", "#inspiration", "#photography"],
"caption": "A ready-to-use caption for the post",
"style": "modern",
"dimensions": [1080, 1080]
}
```
## Examples
### Basic Usage
```basic
REM Generate a single motivational post
CARD "sunrise over calm ocean", "morning motivation" TO post
TALK "Your post is ready!"
TALK "Caption: " + post.caption
TALK "Hashtags: " + JOIN(post.hashtags, " ")
```
### With Style Selection
```basic
REM Create a minimalist product showcase
CARD "elegant watch on marble surface", "luxury timepiece", "minimal" TO watch_post
REM Create an energetic fitness post
CARD "person running at sunset", "fitness motivation", "vibrant" TO fitness_post
```
### Carousel Generation
```basic
REM Generate 5 cards for a carousel post about healthy eating
CARD "colorful healthy meals", "nutrition tips", "modern", 5 TO carousel
FOR EACH card IN carousel
TALK "Card " + index + ": " + card.text_content
UPLOAD card.image_path TO "instagram/carousel/"
NEXT
```
### Story Format
```basic
REM Create Instagram Story content
CARD "coffee shop interior", "monday vibes", "story" TO story_post
TALK "Story dimensions: " + story_post.dimensions[0] + "x" + story_post.dimensions[1]
```
## Sample Conversation
<div class="chat-conversation">
<div class="chat-message user">
<div class="chat-bubble">Create an Instagram post about our new summer collection</div>
</div>
<div class="chat-message bot">
<div class="chat-bubble">I'll create a beautiful post for your summer collection! Let me generate that for you...</div>
</div>
<div class="chat-message bot">
<div class="chat-bubble">
✨ Your Instagram post is ready!
**Text Overlay:** "Summer Vibes Only"
**Caption:** Embrace the warmth of the season with our stunning new summer collection. ☀️
**Hashtags:** #SummerCollection #FashionVibes #SummerStyle #NewArrivals #SummerFashion #OOTD #StyleInspo
**Image:** A vibrant beach scene with flowing summer dresses
Would you like me to create more variations or adjust the style?
</div>
</div>
<div class="chat-message user">
<div class="chat-bubble">Yes, make 3 more with a minimal style for a carousel</div>
</div>
<div class="chat-message bot">
<div class="chat-bubble">
Perfect! I've created 3 additional cards with a minimal aesthetic:
📸 **Card 1:** "Less is More" - Clean white background with single garment
📸 **Card 2:** "Effortless Style" - Soft neutral tones
📸 **Card 3:** "Simply Summer" - Minimal composition with plant accents
All 4 images are now ready for your carousel post. The consistent minimal style will create a cohesive feed aesthetic.
Shall I upload them to your drafts?
</div>
</div>
</div>
## Implementation Details
The CARD keyword performs the following steps:
1. **Text Generation**: Uses LLM to create optimized overlay text based on the prompt
2. **Image Generation**: Creates the base image using AI image generation
3. **Style Application**: Applies color filters and effects based on the selected style
4. **Text Overlay**: Adds the generated text with proper positioning and shadows
5. **Social Content**: Generates relevant hashtags and a ready-to-use caption
## Image Dimensions
| Format | Dimensions | Use Case |
|--------|------------|----------|
| Square | 1080 × 1080 | Feed posts |
| Portrait | 1080 × 1350 | Feed posts (more visibility) |
| Story | 1080 × 1920 | Stories and Reels |
| Landscape | 1080 × 566 | Link previews |
## Best Practices
1. **Be Specific with Image Prompts**: "golden retriever playing in autumn leaves" works better than just "dog"
2. **Keep Text Prompts Thematic**: Focus on the message, not the exact words - the LLM will optimize
3. **Match Style to Brand**: Use consistent styles across posts for brand recognition
4. **Use Carousel for Stories**: Generate multiple related cards to create engaging carousel posts
5. **Review Hashtags**: The generated hashtags are suggestions - customize for your audience
## Error Handling
```basic
TRY
CARD "abstract art", "creativity unleashed", "vibrant" TO art_post
IF art_post.image_path = "" THEN
TALK "Image generation failed, please try again"
ELSE
TALK "Post created successfully!"
END IF
CATCH error
TALK "Error creating card: " + error.message
END TRY
```
## Related Keywords
- [GENERATE IMAGE](./keyword-generate-image.md) - Generate images without text overlay
- [UPLOAD](./keyword-upload.md) - Upload generated cards to storage
- [POST TO SOCIAL](./keyword-post-to-social.md) - Publish directly to social media
- [CREATE DRAFT](./keyword-create-draft.md) - Save as draft for review
## See Also
- [Social Media Keywords](./keywords-social-media.md)
- [Image Processing](./keywords-image-processing.md)

601
src/basic/keywords/card.rs Normal file
View file

@ -0,0 +1,601 @@
//! CARD keyword - Creates beautiful Instagram-style posts from prompts
//!
//! Syntax:
//! CARD image_prompt, text_prompt TO variable
//! CARD image_prompt, text_prompt, style TO variable
//! CARD image_prompt, text_prompt, style, count TO variable
//!
//! Examples:
//! CARD "sunset over mountains", "inspirational quote about nature" TO post
//! CARD "modern office", "productivity tips", "minimal" TO cards
//! CARD "healthy food", "nutrition facts", "vibrant", 5 TO carousel
use crate::basic::runtime::{BasicRuntime, BasicValue};
use crate::llm::LLMProvider;
use anyhow::{anyhow, Result};
use image::{DynamicImage, ImageBuffer, Rgba, RgbaImage};
use imageproc::drawing::{draw_text_mut, text_size};
use rusttype::{Font, Scale};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::Mutex;
/// Card style presets for Instagram posts
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub enum CardStyle {
#[default]
Modern,
Minimal,
Vibrant,
Dark,
Light,
Gradient,
Polaroid,
Magazine,
Story,
Carousel,
}
impl From<&str> for CardStyle {
fn from(s: &str) -> Self {
match s.to_lowercase().as_str() {
"minimal" => CardStyle::Minimal,
"vibrant" => CardStyle::Vibrant,
"dark" => CardStyle::Dark,
"light" => CardStyle::Light,
"gradient" => CardStyle::Gradient,
"polaroid" => CardStyle::Polaroid,
"magazine" => CardStyle::Magazine,
"story" => CardStyle::Story,
"carousel" => CardStyle::Carousel,
_ => CardStyle::Modern,
}
}
}
/// Card dimensions for different formats
#[derive(Debug, Clone, Copy)]
pub struct CardDimensions {
pub width: u32,
pub height: u32,
}
impl CardDimensions {
pub const INSTAGRAM_SQUARE: Self = Self {
width: 1080,
height: 1080,
};
pub const INSTAGRAM_PORTRAIT: Self = Self {
width: 1080,
height: 1350,
};
pub const INSTAGRAM_STORY: Self = Self {
width: 1080,
height: 1920,
};
pub const INSTAGRAM_LANDSCAPE: Self = Self {
width: 1080,
height: 566,
};
pub fn for_style(style: &CardStyle) -> Self {
match style {
CardStyle::Story => Self::INSTAGRAM_STORY,
CardStyle::Carousel => Self::INSTAGRAM_SQUARE,
_ => Self::INSTAGRAM_SQUARE,
}
}
}
/// Text overlay configuration
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextOverlay {
pub text: String,
pub font_size: f32,
pub color: [u8; 4],
pub position: TextPosition,
pub max_width_ratio: f32,
pub shadow: bool,
pub background: Option<[u8; 4]>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub enum TextPosition {
Top,
#[default]
Center,
Bottom,
TopLeft,
TopRight,
BottomLeft,
BottomRight,
}
/// Generated card result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CardResult {
pub image_path: String,
pub image_url: Option<String>,
pub text_content: String,
pub hashtags: Vec<String>,
pub caption: String,
pub style: String,
pub dimensions: (u32, u32),
}
/// Card generation configuration
#[derive(Debug, Clone)]
pub struct CardConfig {
pub style: CardStyle,
pub dimensions: CardDimensions,
pub text_position: TextPosition,
pub include_hashtags: bool,
pub include_caption: bool,
pub brand_watermark: Option<String>,
}
impl Default for CardConfig {
fn default() -> Self {
Self {
style: CardStyle::Modern,
dimensions: CardDimensions::INSTAGRAM_SQUARE,
text_position: TextPosition::Center,
include_hashtags: true,
include_caption: true,
brand_watermark: None,
}
}
}
/// CARD keyword implementation
pub struct CardKeyword {
llm_provider: Arc<dyn LLMProvider>,
output_dir: String,
}
impl CardKeyword {
pub fn new(llm_provider: Arc<dyn LLMProvider>, output_dir: String) -> Self {
Self {
llm_provider,
output_dir,
}
}
/// Execute CARD keyword
pub async fn execute(
&self,
image_prompt: &str,
text_prompt: &str,
style: Option<&str>,
count: Option<usize>,
) -> Result<Vec<CardResult>> {
let card_style = style.map(CardStyle::from).unwrap_or_default();
let card_count = count.unwrap_or(1).min(10); // Max 10 cards
let config = CardConfig {
style: card_style.clone(),
dimensions: CardDimensions::for_style(&card_style),
..Default::default()
};
let mut results = Vec::with_capacity(card_count);
for i in 0..card_count {
let result = self
.generate_single_card(image_prompt, text_prompt, &config, i)
.await?;
results.push(result);
}
Ok(results)
}
/// Generate a single card
async fn generate_single_card(
&self,
image_prompt: &str,
text_prompt: &str,
config: &CardConfig,
index: usize,
) -> Result<CardResult> {
// Step 1: Generate optimized text content using LLM
let text_content = self.generate_text_content(text_prompt, config).await?;
// Step 2: Generate image using image generation API
let base_image = self.generate_image(image_prompt, config).await?;
// Step 3: Apply style and text overlay
let styled_image = self.apply_style_and_text(&base_image, &text_content, config)?;
// Step 4: Generate hashtags and caption
let (hashtags, caption) = self.generate_social_content(&text_content, config).await?;
// Step 5: Save the final image
let filename = format!(
"card_{}_{}.png",
chrono::Utc::now().format("%Y%m%d_%H%M%S"),
index
);
let image_path = format!("{}/{}", self.output_dir, filename);
styled_image.save(&image_path)?;
Ok(CardResult {
image_path: image_path.clone(),
image_url: None, // Will be set after upload to storage
text_content,
hashtags,
caption,
style: format!("{:?}", config.style),
dimensions: (config.dimensions.width, config.dimensions.height),
})
}
/// Generate optimized text content for the card
async fn generate_text_content(
&self,
text_prompt: &str,
config: &CardConfig,
) -> Result<String> {
let style_instruction = match config.style {
CardStyle::Minimal => "Keep it very short, 1-2 impactful words or a brief phrase.",
CardStyle::Vibrant => "Make it energetic and exciting with action words.",
CardStyle::Dark => "Create a mysterious, sophisticated tone.",
CardStyle::Light => "Keep it uplifting and positive.",
CardStyle::Magazine => "Write like a magazine headline, catchy and professional.",
CardStyle::Story => "Create engaging story-style text that draws people in.",
_ => "Create compelling, shareable text perfect for social media.",
};
let prompt = format!(
r#"Create text for an Instagram post image overlay.
Topic/Theme: {}
Style Guidelines:
- {}
- Maximum 50 characters for main text
- Should be visually impactful when overlaid on an image
- Use proper capitalization for visual appeal
- No hashtags in the main text (those come separately)
Respond with ONLY the text content, nothing else."#,
text_prompt, style_instruction
);
let response = self.llm_provider.complete(&prompt, None).await?;
// Clean up the response
let text = response.trim().to_string();
// Ensure it's not too long
if text.len() > 100 {
Ok(text.chars().take(100).collect::<String>() + "...")
} else {
Ok(text)
}
}
/// Generate the base image
async fn generate_image(
&self,
image_prompt: &str,
config: &CardConfig,
) -> Result<DynamicImage> {
let enhanced_prompt = self.enhance_image_prompt(image_prompt, config);
// Call image generation service
let image_bytes = self
.llm_provider
.generate_image(
&enhanced_prompt,
config.dimensions.width,
config.dimensions.height,
)
.await?;
let image = image::load_from_memory(&image_bytes)?;
Ok(image)
}
/// Enhance the image prompt based on style
fn enhance_image_prompt(&self, base_prompt: &str, config: &CardConfig) -> String {
let style_modifiers = match config.style {
CardStyle::Minimal => {
"minimalist, clean, simple composition, lots of negative space, muted colors"
}
CardStyle::Vibrant => "vibrant colors, high saturation, dynamic, energetic, bold",
CardStyle::Dark => "dark moody atmosphere, dramatic lighting, deep shadows, cinematic",
CardStyle::Light => "bright, airy, soft lighting, pastel colors, ethereal",
CardStyle::Gradient => "smooth color gradients, abstract, flowing colors",
CardStyle::Polaroid => "vintage polaroid style, slightly faded, warm tones, nostalgic",
CardStyle::Magazine => "high fashion, editorial style, professional photography, sharp",
CardStyle::Story => "vertical composition, immersive, storytelling, atmospheric",
CardStyle::Carousel => "consistent style, series-ready, cohesive aesthetic",
CardStyle::Modern => "modern, trendy, instagram aesthetic, high quality",
};
format!(
"{}, {}, perfect for Instagram, professional quality, 4K, highly detailed",
base_prompt, style_modifiers
)
}
/// Apply style effects and text overlay to the image
fn apply_style_and_text(
&self,
image: &DynamicImage,
text: &str,
config: &CardConfig,
) -> Result<DynamicImage> {
let mut rgba_image = image.to_rgba8();
// Apply style-specific filters
self.apply_style_filter(&mut rgba_image, &config.style);
// Add text overlay
self.add_text_overlay(&mut rgba_image, text, config)?;
// Add watermark if configured
if let Some(ref watermark) = config.brand_watermark {
self.add_watermark(&mut rgba_image, watermark)?;
}
Ok(DynamicImage::ImageRgba8(rgba_image))
}
/// Apply style-specific image filters
fn apply_style_filter(&self, image: &mut RgbaImage, style: &CardStyle) {
match style {
CardStyle::Dark => {
// Darken and increase contrast
for pixel in image.pixels_mut() {
pixel[0] = (pixel[0] as f32 * 0.7) as u8;
pixel[1] = (pixel[1] as f32 * 0.7) as u8;
pixel[2] = (pixel[2] as f32 * 0.7) as u8;
}
}
CardStyle::Light => {
// Brighten slightly
for pixel in image.pixels_mut() {
pixel[0] = ((pixel[0] as f32 * 1.1).min(255.0)) as u8;
pixel[1] = ((pixel[1] as f32 * 1.1).min(255.0)) as u8;
pixel[2] = ((pixel[2] as f32 * 1.1).min(255.0)) as u8;
}
}
CardStyle::Polaroid => {
// Add warm vintage tint
for pixel in image.pixels_mut() {
pixel[0] = ((pixel[0] as f32 * 1.05).min(255.0)) as u8;
pixel[1] = ((pixel[1] as f32 * 0.95).min(255.0)) as u8;
pixel[2] = ((pixel[2] as f32 * 0.85).min(255.0)) as u8;
}
}
CardStyle::Vibrant => {
// Increase saturation
for pixel in image.pixels_mut() {
let r = pixel[0] as f32;
let g = pixel[1] as f32;
let b = pixel[2] as f32;
let avg = (r + g + b) / 3.0;
let factor = 1.3;
pixel[0] = ((r - avg) * factor + avg).clamp(0.0, 255.0) as u8;
pixel[1] = ((g - avg) * factor + avg).clamp(0.0, 255.0) as u8;
pixel[2] = ((b - avg) * factor + avg).clamp(0.0, 255.0) as u8;
}
}
_ => {}
}
}
/// Add text overlay to the image
fn add_text_overlay(
&self,
image: &mut RgbaImage,
text: &str,
config: &CardConfig,
) -> Result<()> {
let (width, height) = (image.width(), image.height());
// Load font (embedded or from file)
let font_data = include_bytes!("../../../assets/fonts/Inter-Bold.ttf");
let font = Font::try_from_bytes(font_data as &[u8])
.ok_or_else(|| anyhow!("Failed to load font"))?;
// Calculate font size based on image dimensions and text length
let base_size = (width as f32 * 0.08).min(height as f32 * 0.1);
let scale = Scale::uniform(base_size);
// Calculate text position
let (text_width, text_height) = text_size(scale, &font, text);
let (x, y) = self.calculate_text_position(
width,
height,
text_width as u32,
text_height as u32,
&config.text_position,
);
// Draw text shadow for better readability
let shadow_color = Rgba([0u8, 0u8, 0u8, 180u8]);
draw_text_mut(image, shadow_color, x + 3, y + 3, scale, &font, text);
// Draw main text
let text_color = match config.style {
CardStyle::Dark => Rgba([255u8, 255u8, 255u8, 255u8]),
CardStyle::Light => Rgba([30u8, 30u8, 30u8, 255u8]),
_ => Rgba([255u8, 255u8, 255u8, 255u8]),
};
draw_text_mut(image, text_color, x, y, scale, &font, text);
Ok(())
}
/// Calculate text position based on configuration
fn calculate_text_position(
&self,
img_width: u32,
img_height: u32,
text_width: u32,
text_height: u32,
position: &TextPosition,
) -> (i32, i32) {
let padding = (img_width as f32 * 0.05) as i32;
match position {
TextPosition::Top => (
((img_width - text_width) / 2) as i32,
padding + text_height as i32,
),
TextPosition::Center => (
((img_width - text_width) / 2) as i32,
((img_height - text_height) / 2) as i32,
),
TextPosition::Bottom => (
((img_width - text_width) / 2) as i32,
(img_height - text_height) as i32 - padding,
),
TextPosition::TopLeft => (padding, padding + text_height as i32),
TextPosition::TopRight => (
(img_width - text_width) as i32 - padding,
padding + text_height as i32,
),
TextPosition::BottomLeft => (padding, (img_height - text_height) as i32 - padding),
TextPosition::BottomRight => (
(img_width - text_width) as i32 - padding,
(img_height - text_height) as i32 - padding,
),
}
}
/// Add brand watermark
fn add_watermark(&self, image: &mut RgbaImage, watermark: &str) -> Result<()> {
let font_data = include_bytes!("../../../assets/fonts/Inter-Regular.ttf");
let font = Font::try_from_bytes(font_data as &[u8])
.ok_or_else(|| anyhow!("Failed to load font"))?;
let scale = Scale::uniform(image.width() as f32 * 0.025);
let color = Rgba([255u8, 255u8, 255u8, 128u8]);
let padding = 20i32;
let x = padding;
let y = (image.height() - 30) as i32;
draw_text_mut(image, color, x, y, scale, &font, watermark);
Ok(())
}
/// Generate hashtags and caption for the post
async fn generate_social_content(
&self,
text_content: &str,
config: &CardConfig,
) -> Result<(Vec<String>, String)> {
if !config.include_hashtags && !config.include_caption {
return Ok((vec![], String::new()));
}
let prompt = format!(
r#"Based on this Instagram post text: "{}"
Generate:
1. A short, engaging caption (1-2 sentences max)
2. 5-10 relevant hashtags (without the # symbol)
Format your response exactly like this:
CAPTION: [your caption here]
HASHTAGS: tag1, tag2, tag3, tag4, tag5"#,
text_content
);
let response = self.llm_provider.complete(&prompt, None).await?;
// Parse the response
let mut caption = String::new();
let mut hashtags = Vec::new();
for line in response.lines() {
if line.starts_with("CAPTION:") {
caption = line.trim_start_matches("CAPTION:").trim().to_string();
} else if line.starts_with("HASHTAGS:") {
let tags = line.trim_start_matches("HASHTAGS:").trim();
hashtags = tags.split(',').map(|t| format!("#{}", t.trim())).collect();
}
}
Ok((hashtags, caption))
}
}
/// Register CARD keyword with the BASIC runtime
pub fn register_card_keyword(runtime: &mut BasicRuntime, llm_provider: Arc<dyn LLMProvider>) {
let output_dir = runtime
.get_config("output_dir")
.unwrap_or_else(|| "/tmp/gb_cards".to_string());
let keyword = Arc::new(Mutex::new(CardKeyword::new(llm_provider, output_dir)));
runtime.register_keyword("CARD", move |args, _ctx| {
let keyword = keyword.clone();
Box::pin(async move {
if args.len() < 2 {
return Err(anyhow!(
"CARD requires at least 2 arguments: image_prompt, text_prompt"
));
}
let image_prompt = args[0].as_string()?;
let text_prompt = args[1].as_string()?;
let style = args.get(2).map(|v| v.as_string()).transpose()?;
let count = args
.get(3)
.map(|v| v.as_number().map(|n| n as usize))
.transpose()?;
let kw = keyword.lock().await;
let results = kw
.execute(&image_prompt, &text_prompt, style.as_deref(), count)
.await?;
// Convert results to BasicValue
let value = if results.len() == 1 {
BasicValue::Object(serde_json::to_value(&results[0])?)
} else {
BasicValue::Array(
results
.into_iter()
.map(|r| BasicValue::Object(serde_json::to_value(&r).unwrap()))
.collect(),
)
};
Ok(value)
})
});
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_card_style_from_string() {
assert!(matches!(CardStyle::from("minimal"), CardStyle::Minimal));
assert!(matches!(CardStyle::from("VIBRANT"), CardStyle::Vibrant));
assert!(matches!(CardStyle::from("unknown"), CardStyle::Modern));
}
#[test]
fn test_card_dimensions() {
assert_eq!(CardDimensions::INSTAGRAM_SQUARE.width, 1080);
assert_eq!(CardDimensions::INSTAGRAM_SQUARE.height, 1080);
assert_eq!(CardDimensions::INSTAGRAM_STORY.height, 1920);
}
#[test]
fn test_text_position_calculation() {
// Create a mock keyword for testing
// In real tests, we'd use a mock LLM provider
}
}

View file

@ -1,35 +1,52 @@
use crate::shared::models::UserSession;
use crate::shared::state::AppState;
use rhai::Dynamic; use rhai::Dynamic;
use rhai::Engine; use rhai::Engine;
use std::error::Error; use std::error::Error;
use std::fs; use std::fs;
use std::io::Read; use std::io::Read;
use std::path::PathBuf; use std::path::PathBuf;
use crate::shared::models::UserSession;
use crate::shared::state::AppState;
pub fn create_site_keyword(state: &AppState, _user: UserSession, engine: &mut Engine) { pub fn create_site_keyword(state: &AppState, _user: UserSession, engine: &mut Engine) {
let state_clone = state.clone(); let state_clone = state.clone();
engine engine
.register_custom_syntax(&["CREATE_SITE", "$expr$", ",", "$expr$", ",", "$expr$"], true, move |context, inputs| { .register_custom_syntax(
&["CREATE", "SITE", "$expr$", ",", "$expr$", ",", "$expr$"],
true,
move |context, inputs| {
if inputs.len() < 3 { if inputs.len() < 3 {
return Err("Not enough arguments for CREATE SITE".into()); return Err("Not enough arguments for CREATE SITE".into());
} }
let alias = context.eval_expression_tree(&inputs[0])?; let alias = context.eval_expression_tree(&inputs[0])?;
let template_dir = context.eval_expression_tree(&inputs[1])?; let template_dir = context.eval_expression_tree(&inputs[1])?;
let prompt = context.eval_expression_tree(&inputs[2])?; let prompt = context.eval_expression_tree(&inputs[2])?;
let config = state_clone.config.as_ref().expect("Config must be initialized").clone(); let config = state_clone
.config
.as_ref()
.expect("Config must be initialized")
.clone();
let fut = create_site(&config, alias, template_dir, prompt); let fut = create_site(&config, alias, template_dir, prompt);
let result = tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut)) let result =
tokio::task::block_in_place(|| tokio::runtime::Handle::current().block_on(fut))
.map_err(|e| format!("Site creation failed: {}", e))?; .map_err(|e| format!("Site creation failed: {}", e))?;
Ok(Dynamic::from(result)) Ok(Dynamic::from(result))
}, },
) )
.unwrap(); .unwrap();
} }
async fn create_site(config: &crate::config::AppConfig, alias: Dynamic, template_dir: Dynamic, prompt: Dynamic) -> Result<String, Box<dyn Error + Send + Sync>> {
async fn create_site(
config: &crate::config::AppConfig,
alias: Dynamic,
template_dir: Dynamic,
prompt: Dynamic,
) -> Result<String, Box<dyn Error + Send + Sync>> {
let base_path = PathBuf::from(&config.site_path); let base_path = PathBuf::from(&config.site_path);
let template_path = base_path.join(template_dir.to_string()); let template_path = base_path.join(template_dir.to_string());
let alias_path = base_path.join(alias.to_string()); let alias_path = base_path.join(alias.to_string());
fs::create_dir_all(&alias_path).map_err(|e| e.to_string())?; fs::create_dir_all(&alias_path).map_err(|e| e.to_string())?;
let mut combined_content = String::new(); let mut combined_content = String::new();
for entry in fs::read_dir(&template_path).map_err(|e| e.to_string())? { for entry in fs::read_dir(&template_path).map_err(|e| e.to_string())? {
let entry = entry.map_err(|e| e.to_string())?; let entry = entry.map_err(|e| e.to_string())?;
@ -37,14 +54,22 @@ async fn create_site(config: &crate::config::AppConfig, alias: Dynamic, template
if path.extension().map_or(false, |ext| ext == "html") { if path.extension().map_or(false, |ext| ext == "html") {
let mut file = fs::File::open(&path).map_err(|e| e.to_string())?; let mut file = fs::File::open(&path).map_err(|e| e.to_string())?;
let mut contents = String::new(); let mut contents = String::new();
file.read_to_string(&mut contents).map_err(|e| e.to_string())?; file.read_to_string(&mut contents)
.map_err(|e| e.to_string())?;
combined_content.push_str(&contents); combined_content.push_str(&contents);
combined_content.push_str("\n\n--- TEMPLATE SEPARATOR ---\n\n"); combined_content.push_str("\n\n--- TEMPLATE SEPARATOR ---\n\n");
} }
} }
let _full_prompt = format!("TEMPLATE FILES:\n{}\n\nPROMPT: {}\n\nGenerate a new HTML file cloning all previous TEMPLATE (keeping only the local _assets libraries use, no external resources), but turning this into this prompt:", combined_content, prompt.to_string());
let _full_prompt = format!(
"TEMPLATE FILES:\n{}\n\nPROMPT: {}\n\nGenerate a new HTML file cloning all previous TEMPLATE (keeping only the local _assets libraries use, no external resources), but turning this into this prompt:",
combined_content,
prompt.to_string()
);
let llm_result = "".to_string(); let llm_result = "".to_string();
let index_path = alias_path.join("index.html"); let index_path = alias_path.join("index.html");
fs::write(index_path, llm_result).map_err(|e| e.to_string())?; fs::write(index_path, llm_result).map_err(|e| e.to_string())?;
Ok(alias_path.to_string_lossy().into_owned()) Ok(alias_path.to_string_lossy().into_owned())
} }

View file

@ -1,11 +1,11 @@
//! Lead Scoring Functions for CRM Integration //! Lead Scoring Functions for CRM Integration
//! //!
//! Provides BASIC keywords for lead scoring and qualification: //! Provides BASIC keywords for lead scoring and qualification:
//! - SCORE_LEAD - Calculate lead score based on criteria //! - SCORE LEAD - Calculate lead score based on criteria
//! - GET_LEAD_SCORE - Retrieve stored lead score //! - GET LEAD SCORE - Retrieve stored lead score
//! - QUALIFY_LEAD - Check if lead meets qualification threshold //! - QUALIFY LEAD - Check if lead meets qualification threshold
//! - UPDATE_LEAD_SCORE - Manually adjust lead score //! - UPDATE LEAD SCORE - Manually adjust lead score
//! - AI_SCORE_LEAD - LLM-enhanced lead scoring //! - AI SCORE LEAD - LLM-enhanced lead scoring
use crate::shared::models::UserSession; use crate::shared::models::UserSession;
use crate::shared::state::AppState; use crate::shared::state::AppState;
@ -13,23 +13,23 @@ use log::{debug, trace};
use rhai::{Dynamic, Engine, Map}; use rhai::{Dynamic, Engine, Map};
use std::sync::Arc; use std::sync::Arc;
/// SCORE_LEAD - Calculate lead score based on provided criteria /// SCORE LEAD - Calculate lead score based on provided criteria
/// ///
/// BASIC Syntax: /// BASIC Syntax:
/// score = SCORE_LEAD(lead_data) /// score = SCORE LEAD(lead_data)
/// score = SCORE_LEAD(lead_data, scoring_rules) /// score = SCORE LEAD(lead_data, scoring_rules)
/// ///
/// Examples: /// Examples:
/// lead = #{"email": "john@company.com", "job_title": "CTO", "company_size": 500} /// lead = #{"email": "john@company.com", "job_title": "CTO", "company_size": 500}
/// score = SCORE_LEAD(lead) /// score = SCORE LEAD(lead)
pub fn score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) { pub fn score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
let state_clone = state.clone(); let state_clone = state.clone();
let user_clone = user.clone(); let user_clone = user.clone();
// SCORE_LEAD with lead data only (uses default scoring) // SCORE LEAD with lead data only (uses default scoring)
engine.register_fn("SCORE_LEAD", move |lead_data: Map| -> i64 { engine.register_fn("SCORE LEAD", move |lead_data: Map| -> i64 {
trace!( trace!(
"SCORE_LEAD called for user {} with data: {:?}", "SCORE LEAD called for user {} with data: {:?}",
user_clone.user_id, user_clone.user_id,
lead_data lead_data
); );
@ -39,25 +39,25 @@ pub fn score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut
let state_clone2 = state.clone(); let state_clone2 = state.clone();
let user_clone2 = user.clone(); let user_clone2 = user.clone();
// score_lead lowercase version // score lead lowercase version
engine.register_fn("score_lead", move |lead_data: Map| -> i64 { engine.register_fn("score lead", move |lead_data: Map| -> i64 {
trace!( trace!(
"score_lead called for user {} with data: {:?}", "score lead called for user {} with data: {:?}",
user_clone2.user_id, user_clone2.user_id,
lead_data lead_data
); );
calculate_lead_score(&lead_data, None) calculate_lead_score(&lead_data, None)
}); });
// SCORE_LEAD with custom scoring rules // SCORE LEAD with custom scoring rules
let _state_clone3 = state.clone(); let _state_clone3 = state.clone();
let user_clone3 = user.clone(); let user_clone3 = user.clone();
engine.register_fn( engine.register_fn(
"SCORE_LEAD", "SCORE LEAD",
move |lead_data: Map, scoring_rules: Map| -> i64 { move |lead_data: Map, scoring_rules: Map| -> i64 {
trace!( trace!(
"SCORE_LEAD called for user {} with custom rules", "SCORE LEAD called for user {} with custom rules",
user_clone3.user_id user_clone3.user_id
); );
calculate_lead_score(&lead_data, Some(&scoring_rules)) calculate_lead_score(&lead_data, Some(&scoring_rules))
@ -65,22 +65,22 @@ pub fn score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut
); );
let _ = state_clone; let _ = state_clone;
debug!("Registered SCORE_LEAD keyword"); debug!("Registered SCORE LEAD keyword");
} }
/// GET_LEAD_SCORE - Retrieve stored lead score from database /// GET LEAD SCORE - Retrieve stored lead score from database
/// ///
/// BASIC Syntax: /// BASIC Syntax:
/// score = GET_LEAD_SCORE(lead_id) /// score = GET LEAD SCORE(lead_id)
/// score_data = GET_LEAD_SCORE(lead_id, "full") /// score_data = GET LEAD SCORE(lead_id, "full")
pub fn get_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) { pub fn get_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
let _state_clone = state.clone(); let _state_clone = state.clone();
let user_clone = user.clone(); let user_clone = user.clone();
// GET_LEAD_SCORE - returns numeric score // GET LEAD SCORE - returns numeric score
engine.register_fn("GET_LEAD_SCORE", move |lead_id: &str| -> i64 { engine.register_fn("GET LEAD SCORE", move |lead_id: &str| -> i64 {
trace!( trace!(
"GET_LEAD_SCORE called for lead {} by user {}", "GET LEAD SCORE called for lead {} by user {}",
lead_id, lead_id,
user_clone.user_id user_clone.user_id
); );
@ -92,25 +92,25 @@ pub fn get_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine: &
let _state_clone2 = state.clone(); let _state_clone2 = state.clone();
let user_clone2 = user.clone(); let user_clone2 = user.clone();
// get_lead_score lowercase // get lead score lowercase
engine.register_fn("get_lead_score", move |lead_id: &str| -> i64 { engine.register_fn("get lead score", move |lead_id: &str| -> i64 {
trace!( trace!(
"get_lead_score called for lead {} by user {}", "get lead score called for lead {} by user {}",
lead_id, lead_id,
user_clone2.user_id user_clone2.user_id
); );
50 50
}); });
// GET_LEAD_SCORE with "full" option - returns map with score details // GET LEAD SCORE with "full" option - returns map with score details
let _state_clone3 = state.clone(); let _state_clone3 = state.clone();
let user_clone3 = user.clone(); let user_clone3 = user.clone();
engine.register_fn( engine.register_fn(
"GET_LEAD_SCORE", "GET LEAD SCORE",
move |lead_id: &str, option: &str| -> Map { move |lead_id: &str, option: &str| -> Map {
trace!( trace!(
"GET_LEAD_SCORE (full) called for lead {} by user {}", "GET LEAD SCORE (full) called for lead {} by user {}",
lead_id, lead_id,
user_clone3.user_id user_clone3.user_id
); );
@ -131,22 +131,22 @@ pub fn get_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine: &
}, },
); );
debug!("Registered GET_LEAD_SCORE keyword"); debug!("Registered GET LEAD SCORE keyword");
} }
/// QUALIFY_LEAD - Check if lead meets qualification threshold /// QUALIFY LEAD - Check if lead meets qualification threshold
/// ///
/// BASIC Syntax: /// BASIC Syntax:
/// is_qualified = QUALIFY_LEAD(lead_id) /// is_qualified = QUALIFY LEAD(lead_id)
/// is_qualified = QUALIFY_LEAD(lead_id, threshold) /// is_qualified = QUALIFY LEAD(lead_id, threshold)
pub fn qualify_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) { pub fn qualify_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
let _state_clone = state.clone(); let _state_clone = state.clone();
let user_clone = user.clone(); let user_clone = user.clone();
// QUALIFY_LEAD with default threshold (70) // QUALIFY LEAD with default threshold (70)
engine.register_fn("QUALIFY_LEAD", move |lead_id: &str| -> bool { engine.register_fn("QUALIFY LEAD", move |lead_id: &str| -> bool {
trace!( trace!(
"QUALIFY_LEAD called for lead {} by user {}", "QUALIFY LEAD called for lead {} by user {}",
lead_id, lead_id,
user_clone.user_id user_clone.user_id
); );
@ -158,10 +158,10 @@ pub fn qualify_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mu
let _state_clone2 = state.clone(); let _state_clone2 = state.clone();
let user_clone2 = user.clone(); let user_clone2 = user.clone();
// qualify_lead lowercase // qualify lead lowercase
engine.register_fn("qualify_lead", move |lead_id: &str| -> bool { engine.register_fn("qualify lead", move |lead_id: &str| -> bool {
trace!( trace!(
"qualify_lead called for lead {} by user {}", "qualify lead called for lead {} by user {}",
lead_id, lead_id,
user_clone2.user_id user_clone2.user_id
); );
@ -169,15 +169,15 @@ pub fn qualify_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mu
score >= 70 score >= 70
}); });
// QUALIFY_LEAD with custom threshold // QUALIFY LEAD with custom threshold
let _state_clone3 = state.clone(); let _state_clone3 = state.clone();
let user_clone3 = user.clone(); let user_clone3 = user.clone();
engine.register_fn( engine.register_fn(
"QUALIFY_LEAD", "QUALIFY LEAD",
move |lead_id: &str, threshold: i64| -> bool { move |lead_id: &str, threshold: i64| -> bool {
trace!( trace!(
"QUALIFY_LEAD called for lead {} with threshold {} by user {}", "QUALIFY LEAD called for lead {} with threshold {} by user {}",
lead_id, lead_id,
threshold, threshold,
user_clone3.user_id user_clone3.user_id
@ -188,13 +188,13 @@ pub fn qualify_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mu
}, },
); );
// IS_QUALIFIED alias // IS QUALIFIED alias
let _state_clone4 = state.clone(); let _state_clone4 = state.clone();
let user_clone4 = user.clone(); let user_clone4 = user.clone();
engine.register_fn("IS_QUALIFIED", move |lead_id: &str| -> bool { engine.register_fn("IS QUALIFIED", move |lead_id: &str| -> bool {
trace!( trace!(
"IS_QUALIFIED called for lead {} by user {}", "IS QUALIFIED called for lead {} by user {}",
lead_id, lead_id,
user_clone4.user_id user_clone4.user_id
); );
@ -202,24 +202,24 @@ pub fn qualify_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mu
score >= 70 score >= 70
}); });
debug!("Registered QUALIFY_LEAD keyword"); debug!("Registered QUALIFY LEAD keyword");
} }
/// UPDATE_LEAD_SCORE - Manually adjust lead score /// UPDATE LEAD SCORE - Manually adjust lead score
/// ///
/// BASIC Syntax: /// BASIC Syntax:
/// UPDATE_LEAD_SCORE lead_id, adjustment /// UPDATE LEAD SCORE lead_id, adjustment
/// UPDATE_LEAD_SCORE lead_id, adjustment, "reason" /// UPDATE LEAD SCORE lead_id, adjustment, "reason"
pub fn update_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) { pub fn update_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
let _state_clone = state.clone(); let _state_clone = state.clone();
let user_clone = user.clone(); let user_clone = user.clone();
// UPDATE_LEAD_SCORE with adjustment // UPDATE LEAD SCORE with adjustment
engine.register_fn( engine.register_fn(
"UPDATE_LEAD_SCORE", "UPDATE LEAD SCORE",
move |lead_id: &str, adjustment: i64| -> i64 { move |lead_id: &str, adjustment: i64| -> i64 {
trace!( trace!(
"UPDATE_LEAD_SCORE called for lead {} with adjustment {} by user {}", "UPDATE LEAD SCORE called for lead {} with adjustment {} by user {}",
lead_id, lead_id,
adjustment, adjustment,
user_clone.user_id user_clone.user_id
@ -232,12 +232,12 @@ pub fn update_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine
let _state_clone2 = state.clone(); let _state_clone2 = state.clone();
let user_clone2 = user.clone(); let user_clone2 = user.clone();
// UPDATE_LEAD_SCORE with reason // UPDATE LEAD SCORE with reason
engine.register_fn( engine.register_fn(
"UPDATE_LEAD_SCORE", "UPDATE LEAD SCORE",
move |lead_id: &str, adjustment: i64, reason: &str| -> i64 { move |lead_id: &str, adjustment: i64, reason: &str| -> i64 {
trace!( trace!(
"UPDATE_LEAD_SCORE called for lead {} with adjustment {} reason '{}' by user {}", "UPDATE LEAD SCORE called for lead {} with adjustment {} reason '{}' by user {}",
lead_id, lead_id,
adjustment, adjustment,
reason, reason,
@ -248,13 +248,13 @@ pub fn update_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine
}, },
); );
// SET_LEAD_SCORE - set absolute score // SET LEAD SCORE - set absolute score
let _state_clone3 = state.clone(); let _state_clone3 = state.clone();
let user_clone3 = user.clone(); let user_clone3 = user.clone();
engine.register_fn("SET_LEAD_SCORE", move |lead_id: &str, score: i64| -> i64 { engine.register_fn("SET LEAD SCORE", move |lead_id: &str, score: i64| -> i64 {
trace!( trace!(
"SET_LEAD_SCORE called for lead {} with score {} by user {}", "SET LEAD SCORE called for lead {} with score {} by user {}",
lead_id, lead_id,
score, score,
user_clone3.user_id user_clone3.user_id
@ -263,24 +263,24 @@ pub fn update_lead_score_keyword(state: Arc<AppState>, user: UserSession, engine
score score
}); });
debug!("Registered UPDATE_LEAD_SCORE keyword"); debug!("Registered UPDATE LEAD SCORE keyword");
} }
/// AI_SCORE_LEAD - LLM-enhanced lead scoring /// AI SCORE LEAD - LLM-enhanced lead scoring
/// ///
/// BASIC Syntax: /// BASIC Syntax:
/// score = AI_SCORE_LEAD(lead_data) /// score = AI SCORE LEAD(lead_data)
/// score = AI_SCORE_LEAD(lead_data, context) /// score = AI SCORE LEAD(lead_data, context)
/// ///
/// Uses AI to analyze lead data and provide intelligent scoring /// Uses AI to analyze lead data and provide intelligent scoring
pub fn ai_score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) { pub fn ai_score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
let state_clone = state.clone(); let state_clone = state.clone();
let user_clone = user.clone(); let user_clone = user.clone();
// AI_SCORE_LEAD with lead data // AI SCORE LEAD with lead data
engine.register_fn("AI_SCORE_LEAD", move |lead_data: Map| -> Map { engine.register_fn("AI SCORE LEAD", move |lead_data: Map| -> Map {
trace!( trace!(
"AI_SCORE_LEAD called for user {} with data: {:?}", "AI SCORE LEAD called for user {} with data: {:?}",
user_clone.user_id, user_clone.user_id,
lead_data lead_data
); );
@ -317,10 +317,10 @@ pub fn ai_score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &m
let _state_clone2 = state.clone(); let _state_clone2 = state.clone();
let user_clone2 = user.clone(); let user_clone2 = user.clone();
// ai_score_lead lowercase // ai score lead lowercase
engine.register_fn("ai_score_lead", move |lead_data: Map| -> Map { engine.register_fn("ai score lead", move |lead_data: Map| -> Map {
trace!( trace!(
"ai_score_lead called for user {} with data: {:?}", "ai score lead called for user {} with data: {:?}",
user_clone2.user_id, user_clone2.user_id,
lead_data lead_data
); );
@ -342,15 +342,15 @@ pub fn ai_score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &m
result result
}); });
// AI_SCORE_LEAD with context // AI SCORE LEAD with context
let _state_clone3 = state.clone(); let _state_clone3 = state.clone();
let user_clone3 = user.clone(); let user_clone3 = user.clone();
engine.register_fn( engine.register_fn(
"AI_SCORE_LEAD", "AI SCORE LEAD",
move |lead_data: Map, context: &str| -> Map { move |lead_data: Map, context: &str| -> Map {
trace!( trace!(
"AI_SCORE_LEAD called for user {} with context: {}", "AI SCORE LEAD called for user {} with context: {}",
user_clone3.user_id, user_clone3.user_id,
context context
); );
@ -371,7 +371,7 @@ pub fn ai_score_lead_keyword(state: Arc<AppState>, user: UserSession, engine: &m
); );
let _ = state_clone; let _ = state_clone;
debug!("Registered AI_SCORE_LEAD keyword"); debug!("Registered AI SCORE LEAD keyword");
} }
/// Calculate lead score based on lead data and optional custom rules /// Calculate lead score based on lead data and optional custom rules

View file

@ -37,14 +37,14 @@
//! ``` //! ```
use chrono::{DateTime, Duration, Utc}; use chrono::{DateTime, Duration, Utc};
use rhai::{Dynamic, Engine, EvalAltResult, Map, Array}; use rhai::{Array, Dynamic, Engine, EvalAltResult, Map};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tracing::{debug, error, info, warn}; use tracing::{debug, error, info, warn};
use uuid::Uuid; use uuid::Uuid;
use crate::state::AppState; use crate::shared::state::AppState;
/// Episode summary structure /// Episode summary structure
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -260,7 +260,14 @@ impl EpisodicMemoryManager {
pub fn generate_summary_prompt(&self, messages: &[ConversationMessage]) -> String { pub fn generate_summary_prompt(&self, messages: &[ConversationMessage]) -> String {
let formatted_messages = messages let formatted_messages = messages
.iter() .iter()
.map(|m| format!("[{}] {}: {}", m.timestamp.format("%H:%M"), m.role, m.content)) .map(|m| {
format!(
"[{}] {}: {}",
m.timestamp.format("%H:%M"),
m.role,
m.content
)
})
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join("\n"); .join("\n");
@ -301,8 +308,8 @@ Respond with valid JSON only:
// Try to extract JSON from response // Try to extract JSON from response
let json_str = extract_json(response)?; let json_str = extract_json(response)?;
let parsed: serde_json::Value = serde_json::from_str(&json_str) let parsed: serde_json::Value =
.map_err(|e| format!("Failed to parse JSON: {}", e))?; serde_json::from_str(&json_str).map_err(|e| format!("Failed to parse JSON: {}", e))?;
let summary = parsed["summary"] let summary = parsed["summary"]
.as_str() .as_str()
@ -449,25 +456,34 @@ impl Episode {
map.insert("session_id".into(), self.session_id.to_string().into()); map.insert("session_id".into(), self.session_id.to_string().into());
map.insert("summary".into(), self.summary.clone().into()); map.insert("summary".into(), self.summary.clone().into());
let topics: Array = self.key_topics let topics: Array = self
.key_topics
.iter() .iter()
.map(|t| Dynamic::from(t.clone())) .map(|t| Dynamic::from(t.clone()))
.collect(); .collect();
map.insert("key_topics".into(), topics.into()); map.insert("key_topics".into(), topics.into());
let decisions: Array = self.decisions let decisions: Array = self
.decisions
.iter() .iter()
.map(|d| Dynamic::from(d.clone())) .map(|d| Dynamic::from(d.clone()))
.collect(); .collect();
map.insert("decisions".into(), decisions.into()); map.insert("decisions".into(), decisions.into());
let action_items: Array = self.action_items let action_items: Array = self
.action_items
.iter() .iter()
.map(|a| { .map(|a| {
let mut item_map = Map::new(); let mut item_map = Map::new();
item_map.insert("description".into(), a.description.clone().into()); item_map.insert("description".into(), a.description.clone().into());
item_map.insert("assignee".into(), a.assignee.clone().unwrap_or_default().into()); item_map.insert(
item_map.insert("priority".into(), format!("{:?}", a.priority).to_lowercase().into()); "assignee".into(),
a.assignee.clone().unwrap_or_default().into(),
);
item_map.insert(
"priority".into(),
format!("{:?}", a.priority).to_lowercase().into(),
);
item_map.insert("completed".into(), a.completed.into()); item_map.insert("completed".into(), a.completed.into());
Dynamic::from(item_map) Dynamic::from(item_map)
}) })
@ -476,15 +492,27 @@ impl Episode {
let mut sentiment_map = Map::new(); let mut sentiment_map = Map::new();
sentiment_map.insert("score".into(), self.sentiment.score.into()); sentiment_map.insert("score".into(), self.sentiment.score.into());
sentiment_map.insert("label".into(), format!("{:?}", self.sentiment.label).to_lowercase().into()); sentiment_map.insert(
"label".into(),
format!("{:?}", self.sentiment.label).to_lowercase().into(),
);
sentiment_map.insert("confidence".into(), self.sentiment.confidence.into()); sentiment_map.insert("confidence".into(), self.sentiment.confidence.into());
map.insert("sentiment".into(), sentiment_map.into()); map.insert("sentiment".into(), sentiment_map.into());
map.insert("resolution".into(), format!("{:?}", self.resolution).to_lowercase().into()); map.insert(
"resolution".into(),
format!("{:?}", self.resolution).to_lowercase().into(),
);
map.insert("message_count".into(), (self.message_count as i64).into()); map.insert("message_count".into(), (self.message_count as i64).into());
map.insert("created_at".into(), self.created_at.to_rfc3339().into()); map.insert("created_at".into(), self.created_at.to_rfc3339().into());
map.insert("conversation_start".into(), self.conversation_start.to_rfc3339().into()); map.insert(
map.insert("conversation_end".into(), self.conversation_end.to_rfc3339().into()); "conversation_start".into(),
self.conversation_start.to_rfc3339().into(),
);
map.insert(
"conversation_end".into(),
self.conversation_end.to_rfc3339().into(),
);
Dynamic::from(map) Dynamic::from(map)
} }
@ -675,14 +703,12 @@ mod tests {
#[test] #[test]
fn test_generate_summary_prompt() { fn test_generate_summary_prompt() {
let manager = EpisodicMemoryManager::new(EpisodicMemoryConfig::default()); let manager = EpisodicMemoryManager::new(EpisodicMemoryConfig::default());
let messages = vec![ let messages = vec![ConversationMessage {
ConversationMessage {
id: Uuid::new_v4(), id: Uuid::new_v4(),
role: "user".to_string(), role: "user".to_string(),
content: "Hello".to_string(), content: "Hello".to_string(),
timestamp: Utc::now(), timestamp: Utc::now(),
}, }];
];
let prompt = manager.generate_summary_prompt(&messages); let prompt = manager.generate_summary_prompt(&messages);
assert!(prompt.contains("CONVERSATION:")); assert!(prompt.contains("CONVERSATION:"));
@ -701,14 +727,12 @@ mod tests {
"resolution": "resolved" "resolution": "resolved"
}"#; }"#;
let messages = vec![ let messages = vec![ConversationMessage {
ConversationMessage {
id: Uuid::new_v4(), id: Uuid::new_v4(),
role: "user".to_string(), role: "user".to_string(),
content: "What's my balance?".to_string(), content: "What's my balance?".to_string(),
timestamp: Utc::now(), timestamp: Utc::now(),
}, }];
];
let episode = manager.parse_summary_response( let episode = manager.parse_summary_response(
response, response,

View file

@ -254,12 +254,15 @@ pub fn register_delete_file_keyword(state: Arc<AppState>, user: UserSession, eng
) )
.unwrap(); .unwrap();
// DELETE_FILE (underscore - backwards compatibility) // DELETE FILE (spaces - primary syntax)
engine engine
.register_custom_syntax(&["DELETE_FILE", "$expr$"], false, move |context, inputs| { .register_custom_syntax(
&["DELETE", "FILE", "$expr$"],
false,
move |context, inputs| {
let path = context.eval_expression_tree(&inputs[0])?.to_string(); let path = context.eval_expression_tree(&inputs[0])?.to_string();
trace!("DELETE_FILE: {}", path); trace!("DELETE FILE: {}", path);
let state_for_task = Arc::clone(&state_clone2); let state_for_task = Arc::clone(&state_clone2);
let user_for_task = user_clone2.clone(); let user_for_task = user_clone2.clone();
@ -283,28 +286,29 @@ pub fn register_delete_file_keyword(state: Arc<AppState>, user: UserSession, eng
}; };
if send_err.is_some() { if send_err.is_some() {
error!("Failed to send DELETE_FILE result from thread"); error!("Failed to send DELETE FILE result from thread");
} }
}); });
match rx.recv_timeout(std::time::Duration::from_secs(30)) { match rx.recv_timeout(std::time::Duration::from_secs(30)) {
Ok(Ok(_)) => Ok(Dynamic::UNIT), Ok(Ok(_)) => Ok(Dynamic::UNIT),
Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
format!("DELETE_FILE failed: {}", e).into(), format!("DELETE FILE failed: {}", e).into(),
rhai::Position::NONE, rhai::Position::NONE,
))), ))),
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
"DELETE_FILE timed out".into(), "DELETE FILE timed out".into(),
rhai::Position::NONE, rhai::Position::NONE,
))) )))
} }
Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
format!("DELETE_FILE thread failed: {}", e).into(), format!("DELETE FILE thread failed: {}", e).into(),
rhai::Position::NONE, rhai::Position::NONE,
))), ))),
} }
}) },
)
.unwrap(); .unwrap();
} }

View file

@ -302,12 +302,15 @@ pub fn register_delete_http_keyword(state: Arc<AppState>, _user: UserSession, en
) )
.unwrap(); .unwrap();
// DELETE_HTTP (underscore - backwards compatibility) // DELETE HTTP (spaces - preferred syntax)
engine engine
.register_custom_syntax(&["DELETE_HTTP", "$expr$"], false, move |context, inputs| { .register_custom_syntax(
&["DELETE", "HTTP", "$expr$"],
false,
move |context, inputs| {
let url = context.eval_expression_tree(&inputs[0])?.to_string(); let url = context.eval_expression_tree(&inputs[0])?.to_string();
trace!("DELETE_HTTP request to: {}", url); trace!("DELETE HTTP request to: {}", url);
let (tx, rx) = std::sync::mpsc::channel(); let (tx, rx) = std::sync::mpsc::channel();
let url_clone = url.clone(); let url_clone = url.clone();
@ -328,28 +331,29 @@ pub fn register_delete_http_keyword(state: Arc<AppState>, _user: UserSession, en
}; };
if send_err.is_some() { if send_err.is_some() {
error!("Failed to send DELETE result from thread"); error!("Failed to send DELETE HTTP result from thread");
} }
}); });
match rx.recv_timeout(std::time::Duration::from_secs(60)) { match rx.recv_timeout(std::time::Duration::from_secs(60)) {
Ok(Ok(response)) => Ok(json_to_dynamic(&response)), Ok(Ok(response)) => Ok(json_to_dynamic(&response)),
Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
format!("DELETE failed: {}", e).into(), format!("DELETE HTTP failed: {}", e).into(),
rhai::Position::NONE, rhai::Position::NONE,
))), ))),
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
"DELETE request timed out".into(), "DELETE HTTP request timed out".into(),
rhai::Position::NONE, rhai::Position::NONE,
))) )))
} }
Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
format!("DELETE thread failed: {}", e).into(), format!("DELETE HTTP thread failed: {}", e).into(),
rhai::Position::NONE, rhai::Position::NONE,
))), ))),
} }
}) },
)
.unwrap(); .unwrap();
} }

View file

@ -477,7 +477,7 @@ fn export_excel(
file_path: &str, file_path: &str,
data: Dynamic, data: Dynamic,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> { ) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
use xlsxwriter::Workbook; use rust_xlsxwriter::Workbook;
let array = to_array(data); let array = to_array(data);
if array.is_empty() { if array.is_empty() {
@ -486,12 +486,12 @@ fn export_excel(
let headers = get_headers_from_array(&array); let headers = get_headers_from_array(&array);
let workbook = Workbook::new(file_path)?; let mut workbook = Workbook::new();
let mut sheet = workbook.add_worksheet(Some("Data"))?; let sheet = workbook.add_worksheet();
// Write headers // Write headers
for (col, header) in headers.iter().enumerate() { for (col, header) in headers.iter().enumerate() {
sheet.write_string(0, col as u16, header, None)?; sheet.write_string(0, col as u16, header)?;
} }
// Write data rows // Write data rows
@ -499,11 +499,11 @@ fn export_excel(
let map = dynamic_to_map(item); let map = dynamic_to_map(item);
for (col, header) in headers.iter().enumerate() { for (col, header) in headers.iter().enumerate() {
let value = map.get(header).map(|v| v.to_string()).unwrap_or_default(); let value = map.get(header).map(|v| v.to_string()).unwrap_or_default();
sheet.write_string((row_idx + 1) as u32, col as u16, &value, None)?; sheet.write_string((row_idx + 1) as u32, col as u16, &value)?;
} }
} }
workbook.close()?; workbook.save(file_path)?;
trace!("Exported data to Excel: {}", file_path); trace!("Exported data to Excel: {}", file_path);
Ok(file_path.to_string()) Ok(file_path.to_string())

View file

@ -4,24 +4,24 @@
//! re-exporting the functions from the crm module for backward compatibility. //! re-exporting the functions from the crm module for backward compatibility.
//! //!
//! BASIC Keywords provided: //! BASIC Keywords provided:
//! - SCORE_LEAD - Calculate lead score based on criteria //! - SCORE LEAD - Calculate lead score based on criteria
//! - GET_LEAD_SCORE - Retrieve stored lead score //! - GET LEAD SCORE - Retrieve stored lead score
//! - QUALIFY_LEAD - Check if lead meets qualification threshold //! - QUALIFY LEAD - Check if lead meets qualification threshold
//! - UPDATE_LEAD_SCORE - Manually adjust lead score //! - UPDATE LEAD SCORE - Manually adjust lead score
//! - AI_SCORE_LEAD - LLM-enhanced lead scoring //! - AI SCORE LEAD - LLM-enhanced lead scoring
//! //!
//! Examples: //! Examples:
//! ' Calculate lead score //! ' Calculate lead score
//! lead = #{"email": "cto@company.com", "job_title": "CTO", "company_size": 500} //! lead = #{"email": "cto@company.com", "job_title": "CTO", "company_size": 500}
//! score = SCORE_LEAD(lead) //! score = SCORE LEAD(lead)
//! //!
//! ' Check if lead is qualified //! ' Check if lead is qualified
//! IF QUALIFY_LEAD(lead_id) THEN //! IF QUALIFY LEAD(lead_id) THEN
//! TALK "Lead is qualified for sales!" //! TALK "Lead is qualified for sales!"
//! END IF //! END IF
//! //!
//! ' Get AI-enhanced scoring with recommendations //! ' Get AI-enhanced scoring with recommendations
//! result = AI_SCORE_LEAD(lead) //! result = AI SCORE LEAD(lead)
//! TALK "Score: " + result.score + ", Priority: " + result.priority //! TALK "Score: " + result.score + ", Priority: " + result.priority
use crate::shared::models::UserSession; use crate::shared::models::UserSession;
@ -39,38 +39,38 @@ use super::crm::register_crm_keywords;
/// ///
/// ## Keywords Registered /// ## Keywords Registered
/// ///
/// ### SCORE_LEAD /// ### SCORE LEAD
/// Calculate a lead score based on provided data. /// Calculate a lead score based on provided data.
/// ```basic /// ```basic
/// lead = #{"email": "john@example.com", "job_title": "Manager"} /// lead = #{"email": "john@example.com", "job_title": "Manager"}
/// score = SCORE_LEAD(lead) /// score = SCORE LEAD(lead)
/// ``` /// ```
/// ///
/// ### GET_LEAD_SCORE /// ### GET LEAD SCORE
/// Retrieve a previously stored lead score from the database. /// Retrieve a previously stored lead score from the database.
/// ```basic /// ```basic
/// score = GET_LEAD_SCORE("lead_123") /// score = GET LEAD SCORE("lead_123")
/// full_data = GET_LEAD_SCORE("lead_123", "full") /// full_data = GET LEAD SCORE("lead_123", "full")
/// ``` /// ```
/// ///
/// ### QUALIFY_LEAD /// ### QUALIFY LEAD
/// Check if a lead meets the qualification threshold. /// Check if a lead meets the qualification threshold.
/// ```basic /// ```basic
/// is_qualified = QUALIFY_LEAD("lead_123") /// is_qualified = QUALIFY LEAD("lead_123")
/// is_qualified = QUALIFY_LEAD("lead_123", 80) ' Custom threshold /// is_qualified = QUALIFY LEAD("lead_123", 80) ' Custom threshold
/// ``` /// ```
/// ///
/// ### UPDATE_LEAD_SCORE /// ### UPDATE LEAD SCORE
/// Manually adjust a lead's score. /// Manually adjust a lead's score.
/// ```basic /// ```basic
/// new_score = UPDATE_LEAD_SCORE("lead_123", 10) ' Add 10 points /// new_score = UPDATE LEAD SCORE("lead_123", 10) ' Add 10 points
/// new_score = UPDATE_LEAD_SCORE("lead_123", -5, "Unsubscribed from newsletter") /// new_score = UPDATE LEAD SCORE("lead_123", -5, "Unsubscribed from newsletter")
/// ``` /// ```
/// ///
/// ### AI_SCORE_LEAD /// ### AI SCORE LEAD
/// Get AI-enhanced lead scoring with recommendations. /// Get AI-enhanced lead scoring with recommendations.
/// ```basic /// ```basic
/// result = AI_SCORE_LEAD(lead_data) /// result = AI SCORE LEAD(lead_data)
/// TALK "Score: " + result.score /// TALK "Score: " + result.score
/// TALK "Priority: " + result.priority /// TALK "Priority: " + result.priority
/// TALK "Recommendation: " + result.recommendation /// TALK "Recommendation: " + result.recommendation

View file

@ -31,9 +31,9 @@
//! QR Code generation keyword //! QR Code generation keyword
//! //!
//! Provides BASIC keywords: //! Provides BASIC keywords:
//! - QR_CODE data -> generates QR code image, returns file path //! - QR CODE data -> generates QR code image, returns file path
//! - QR_CODE data, size -> generates QR code with specified size //! - QR CODE data, size -> generates QR code with specified size
//! - QR_CODE data, size, output_path -> generates QR code to specific path //! - QR CODE data, size, output_path -> generates QR code to specific path
use crate::shared::models::UserSession; use crate::shared::models::UserSession;
use crate::shared::state::AppState; use crate::shared::state::AppState;
@ -52,17 +52,17 @@ pub fn register_qrcode_keywords(state: Arc<AppState>, user: UserSession, engine:
register_qr_code_full_keyword(state, user, engine); register_qr_code_full_keyword(state, user, engine);
} }
/// QR_CODE data /// QR CODE data
/// Generates a QR code image with default size (256x256) /// Generates a QR code image with default size (256x256)
pub fn register_qr_code_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) { pub fn register_qr_code_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
let state_clone = Arc::clone(&state); let state_clone = Arc::clone(&state);
let user_clone = user.clone(); let user_clone = user.clone();
engine engine
.register_custom_syntax(&["QR_CODE", "$expr$"], false, move |context, inputs| { .register_custom_syntax(&["QR", "CODE", "$expr$"], false, move |context, inputs| {
let data = context.eval_expression_tree(&inputs[0])?.to_string(); let data = context.eval_expression_tree(&inputs[0])?.to_string();
trace!("QR_CODE: Generating QR code for data: {}", data); trace!("QR CODE: Generating QR code for data: {}", data);
let state_for_task = Arc::clone(&state_clone); let state_for_task = Arc::clone(&state_clone);
let user_for_task = user_clone.clone(); let user_for_task = user_clone.clone();
@ -70,26 +70,27 @@ pub fn register_qr_code_keyword(state: Arc<AppState>, user: UserSession, engine:
let (tx, rx) = std::sync::mpsc::channel(); let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || { std::thread::spawn(move || {
let result = execute_qr_code_generation(&state_for_task, &user_for_task, &data, 256, None); let result =
execute_qr_code_generation(&state_for_task, &user_for_task, &data, 256, None);
if tx.send(result).is_err() { if tx.send(result).is_err() {
error!("Failed to send QR_CODE result from thread"); error!("Failed to send QR CODE result from thread");
} }
}); });
match rx.recv_timeout(std::time::Duration::from_secs(30)) { match rx.recv_timeout(std::time::Duration::from_secs(30)) {
Ok(Ok(result)) => Ok(Dynamic::from(result)), Ok(Ok(result)) => Ok(Dynamic::from(result)),
Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Ok(Err(e)) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
format!("QR_CODE failed: {}", e).into(), format!("QR CODE failed: {}", e).into(),
rhai::Position::NONE, rhai::Position::NONE,
))), ))),
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
"QR_CODE generation timed out".into(), "QR CODE generation timed out".into(),
rhai::Position::NONE, rhai::Position::NONE,
))) )))
} }
Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
format!("QR_CODE thread failed: {}", e).into(), format!("QR CODE thread failed: {}", e).into(),
rhai::Position::NONE, rhai::Position::NONE,
))), ))),
} }
@ -97,7 +98,7 @@ pub fn register_qr_code_keyword(state: Arc<AppState>, user: UserSession, engine:
.unwrap(); .unwrap();
} }
/// QR_CODE data, size /// QR CODE data, size
/// Generates a QR code image with specified size /// Generates a QR code image with specified size
pub fn register_qr_code_with_size_keyword( pub fn register_qr_code_with_size_keyword(
state: Arc<AppState>, state: Arc<AppState>,
@ -109,7 +110,7 @@ pub fn register_qr_code_with_size_keyword(
engine engine
.register_custom_syntax( .register_custom_syntax(
&["QR_CODE", "$expr$", ",", "$expr$"], &["QR", "CODE", "$expr$", ",", "$expr$"],
false, false,
move |context, inputs| { move |context, inputs| {
let data = context.eval_expression_tree(&inputs[0])?.to_string(); let data = context.eval_expression_tree(&inputs[0])?.to_string();
@ -118,7 +119,11 @@ pub fn register_qr_code_with_size_keyword(
.as_int() .as_int()
.unwrap_or(256) as u32; .unwrap_or(256) as u32;
trace!("QR_CODE: Generating QR code with size {} for: {}", size, data); trace!(
"QR_CODE: Generating QR code with size {} for: {}",
size,
data
);
let state_for_task = Arc::clone(&state_clone); let state_for_task = Arc::clone(&state_clone);
let user_for_task = user_clone.clone(); let user_for_task = user_clone.clone();
@ -126,8 +131,13 @@ pub fn register_qr_code_with_size_keyword(
let (tx, rx) = std::sync::mpsc::channel(); let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || { std::thread::spawn(move || {
let result = let result = execute_qr_code_generation(
execute_qr_code_generation(&state_for_task, &user_for_task, &data, size, None); &state_for_task,
&user_for_task,
&data,
size,
None,
);
if tx.send(result).is_err() { if tx.send(result).is_err() {
error!("Failed to send QR_CODE result from thread"); error!("Failed to send QR_CODE result from thread");
} }
@ -157,11 +167,7 @@ pub fn register_qr_code_with_size_keyword(
/// QR_CODE data, size, output_path /// QR_CODE data, size, output_path
/// Generates a QR code image with specified size and output path /// Generates a QR code image with specified size and output path
pub fn register_qr_code_full_keyword( pub fn register_qr_code_full_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
state: Arc<AppState>,
user: UserSession,
engine: &mut Engine,
) {
let state_clone = Arc::clone(&state); let state_clone = Arc::clone(&state);
let user_clone = user.clone(); let user_clone = user.clone();
@ -336,7 +342,12 @@ pub fn generate_qr_code_with_logo(
// Overlay logo // Overlay logo
let mut final_image = DynamicImage::ImageRgba8(rgba_image); let mut final_image = DynamicImage::ImageRgba8(rgba_image);
imageops::overlay(&mut final_image, &resized_logo, center_x.into(), center_y.into()); imageops::overlay(
&mut final_image,
&resized_logo,
center_x.into(),
center_y.into(),
);
final_image.save(output_path)?; final_image.save(output_path)?;
Ok(output_path.to_string()) Ok(output_path.to_string())

View file

@ -1,30 +1,40 @@
use crate::shared::state::AppState;
use crate::shared::models::UserSession; use crate::shared::models::UserSession;
use log::{ error,trace}; use crate::shared::state::AppState;
use log::{error, trace};
use rhai::{Dynamic, Engine}; use rhai::{Dynamic, Engine};
use std::sync::Arc; use std::sync::Arc;
use uuid::Uuid; use uuid::Uuid;
pub fn set_user_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) { pub fn set_user_keyword(state: Arc<AppState>, user: UserSession, engine: &mut Engine) {
let state_clone = Arc::clone(&state); let state_clone = Arc::clone(&state);
let user_clone = user.clone(); let user_clone = user.clone();
engine engine
.register_custom_syntax(&["SET_USER", "$expr$"], true, move |context, inputs| { .register_custom_syntax(&["SET", "USER", "$expr$"], true, move |context, inputs| {
let user_id_str = context.eval_expression_tree(&inputs[0])?.to_string(); let user_id_str = context.eval_expression_tree(&inputs[0])?.to_string();
match Uuid::parse_str(&user_id_str) { match Uuid::parse_str(&user_id_str) {
Ok(user_id) => { Ok(user_id) => {
let state_for_spawn = Arc::clone(&state_clone); let state_for_spawn = Arc::clone(&state_clone);
let user_clone_spawn = user_clone.clone(); let user_clone_spawn = user_clone.clone();
let mut session_manager = futures::executor::block_on(state_for_spawn.session_manager.lock()); let mut session_manager =
futures::executor::block_on(state_for_spawn.session_manager.lock());
if let Err(e) = session_manager.update_user_id(user_clone_spawn.id, user_id) { if let Err(e) = session_manager.update_user_id(user_clone_spawn.id, user_id) {
error!("Failed to update user ID in session: {}", e); error!("Failed to update user ID in session: {}", e);
} else { } else {
trace!("Updated session {} to user ID: {}", user_clone_spawn.id, user_id); trace!(
"Updated session {} to user ID: {}",
user_clone_spawn.id,
user_id
);
} }
} }
Err(e) => { Err(e) => {
trace!("Invalid user ID format: {}", e); trace!("Invalid user ID format: {}", e);
} }
} }
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)
}) })
.unwrap(); .unwrap();

View file

@ -14,12 +14,15 @@ pub fn use_website_keyword(state: Arc<AppState>, user: UserSession, engine: &mut
let user_clone = user.clone(); let user_clone = user.clone();
engine engine
.register_custom_syntax(&["USE_WEBSITE", "$expr$"], false, move |context, inputs| { .register_custom_syntax(
&["USE", "WEBSITE", "$expr$"],
false,
move |context, inputs| {
let url = context.eval_expression_tree(&inputs[0])?; let url = context.eval_expression_tree(&inputs[0])?;
let url_str = url.to_string().trim_matches('"').to_string(); let url_str = url.to_string().trim_matches('"').to_string();
trace!( trace!(
"USE_WEBSITE command executed: {} for session: {}", "USE WEBSITE command executed: {} for session: {}",
url_str, url_str,
user_clone.id user_clone.id
); );
@ -72,16 +75,17 @@ pub fn use_website_keyword(state: Arc<AppState>, user: UserSession, engine: &mut
))), ))),
Err(std::sync::mpsc::RecvTimeoutError::Timeout) => { Err(std::sync::mpsc::RecvTimeoutError::Timeout) => {
Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
"USE_WEBSITE timed out".into(), "USE WEBSITE timed out".into(),
rhai::Position::NONE, rhai::Position::NONE,
))) )))
} }
Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime( Err(e) => Err(Box::new(rhai::EvalAltResult::ErrorRuntime(
format!("USE_WEBSITE failed: {}", e).into(), format!("USE WEBSITE failed: {}", e).into(),
rhai::Position::NONE, rhai::Position::NONE,
))), ))),
} }
}) },
)
.unwrap(); .unwrap();
} }
@ -272,9 +276,9 @@ pub fn clear_websites_keyword(state: Arc<AppState>, user: UserSession, engine: &
let user_clone = user.clone(); let user_clone = user.clone();
engine engine
.register_custom_syntax(&["CLEAR_WEBSITES"], true, move |_context, _inputs| { .register_custom_syntax(&["CLEAR", "WEBSITES"], true, move |_context, _inputs| {
info!( info!(
"CLEAR_WEBSITES keyword executed for session: {}", "CLEAR WEBSITES keyword executed for session: {}",
user_clone.id user_clone.id
); );

View file

@ -0,0 +1,605 @@
//! Compliance API Handlers
//!
//! Provides REST endpoints for the compliance scanner that checks:
//! - Passwords in config files (not in vault)
//! - Fragile code patterns in .bas files
//! - Security issues and best practice violations
//!
//! ## Endpoints
//!
//! - `GET /api/compliance` - Get compliance summary
//! - `POST /api/compliance/scan` - Run a new compliance scan
//! - `GET /api/compliance/report/:id` - Get specific report
//! - `GET /api/compliance/export/:format` - Export report (json, csv, pdf)
use axum::{
extract::{Path, Query, State},
http::StatusCode,
response::IntoResponse,
routing::{get, post},
Json, Router,
};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use tracing::{debug, error, info};
use uuid::Uuid;
use crate::shared::state::AppState;
#[cfg(feature = "compliance")]
use crate::compliance::{
CodeIssue, CodeScanner, ComplianceReporter, ComplianceScanResult, IssueSeverity, IssueType,
ScanStats,
};
/// Compliance scan request
#[derive(Debug, Deserialize)]
pub struct ScanRequest {
/// Bot ID to scan (optional, scans all if not provided)
pub bot_id: Option<String>,
/// Specific paths to scan
pub paths: Option<Vec<String>>,
/// Whether to include info-level issues
#[serde(default)]
pub include_info: bool,
/// Categories to scan (empty = all)
#[serde(default)]
pub categories: Vec<String>,
}
/// Compliance scan response
#[derive(Debug, Serialize)]
pub struct ScanResponse {
pub scan_id: String,
pub status: String,
pub scanned_at: DateTime<Utc>,
pub summary: ScanSummary,
pub issues: Vec<IssueResponse>,
}
/// Summary of scan results
#[derive(Debug, Serialize, Default)]
pub struct ScanSummary {
pub total_files_scanned: usize,
pub total_issues: usize,
pub critical_count: usize,
pub high_count: usize,
pub medium_count: usize,
pub low_count: usize,
pub info_count: usize,
pub compliance_score: f64,
pub categories: HashMap<String, usize>,
}
/// Individual issue in response
#[derive(Debug, Serialize)]
pub struct IssueResponse {
pub id: String,
pub severity: String,
pub issue_type: String,
pub title: String,
pub description: String,
pub file_path: String,
pub line_number: Option<usize>,
pub code_snippet: Option<String>,
pub remediation: String,
pub category: String,
}
/// Query parameters for listing compliance reports
#[derive(Debug, Deserialize)]
pub struct ListQuery {
pub limit: Option<usize>,
pub offset: Option<usize>,
pub severity: Option<String>,
}
/// Export format options
#[derive(Debug, Deserialize)]
pub struct ExportQuery {
pub format: Option<String>,
}
/// Create compliance routes
pub fn routes() -> Router<AppState> {
Router::new()
.route("/api/compliance", get(get_compliance_summary))
.route("/api/compliance/scan", post(run_compliance_scan))
.route("/api/compliance/report/:id", get(get_report))
.route("/api/compliance/issues", get(list_issues))
.route("/api/compliance/export", get(export_report))
}
/// Get compliance summary - overview of current compliance status
async fn get_compliance_summary(
State(_state): State<AppState>,
) -> Result<Json<ScanResponse>, (StatusCode, String)> {
info!("Getting compliance summary");
// Run a quick scan to get current status
let scan_result = run_scan_internal(None, false).await?;
Ok(Json(scan_result))
}
/// Run a new compliance scan
async fn run_compliance_scan(
State(_state): State<AppState>,
Json(request): Json<ScanRequest>,
) -> Result<Json<ScanResponse>, (StatusCode, String)> {
info!("Running compliance scan for bot: {:?}", request.bot_id);
let scan_result = run_scan_internal(request.bot_id, request.include_info).await?;
Ok(Json(scan_result))
}
/// Get a specific compliance report by ID
async fn get_report(
State(_state): State<AppState>,
Path(report_id): Path<String>,
) -> Result<Json<ScanResponse>, (StatusCode, String)> {
info!("Getting compliance report: {}", report_id);
// For now, run a fresh scan
// In production, this would retrieve from storage
let scan_result = run_scan_internal(None, true).await?;
Ok(Json(scan_result))
}
/// List all compliance issues with filtering
async fn list_issues(
State(_state): State<AppState>,
Query(query): Query<ListQuery>,
) -> Result<Json<Vec<IssueResponse>>, (StatusCode, String)> {
info!("Listing compliance issues");
let scan_result = run_scan_internal(None, true).await?;
let mut issues = scan_result.issues;
// Filter by severity if specified
if let Some(severity) = query.severity {
issues.retain(|i| i.severity.to_lowercase() == severity.to_lowercase());
}
// Apply pagination
let offset = query.offset.unwrap_or(0);
let limit = query.limit.unwrap_or(100);
let paginated: Vec<IssueResponse> = issues.into_iter().skip(offset).take(limit).collect();
Ok(Json(paginated))
}
/// Export compliance report in various formats
async fn export_report(
State(_state): State<AppState>,
Query(query): Query<ExportQuery>,
) -> impl IntoResponse {
let format = query.format.unwrap_or_else(|| "json".to_string());
info!("Exporting compliance report as: {}", format);
match run_scan_internal(None, true).await {
Ok(scan_result) => match format.as_str() {
"json" => {
let json = serde_json::to_string_pretty(&scan_result).unwrap_or_default();
(
StatusCode::OK,
[
("Content-Type", "application/json"),
(
"Content-Disposition",
"attachment; filename=\"compliance-report.json\"",
),
],
json,
)
}
"csv" => {
let csv = generate_csv(&scan_result);
(
StatusCode::OK,
[
("Content-Type", "text/csv"),
(
"Content-Disposition",
"attachment; filename=\"compliance-report.csv\"",
),
],
csv,
)
}
_ => (
StatusCode::BAD_REQUEST,
[("Content-Type", "text/plain"), ("Content-Disposition", "")],
format!("Unsupported format: {}. Use 'json' or 'csv'.", format),
),
},
Err((status, msg)) => (
status,
[("Content-Type", "text/plain"), ("Content-Disposition", "")],
msg,
),
}
}
/// Internal function to run the compliance scan
async fn run_scan_internal(
bot_id: Option<String>,
include_info: bool,
) -> Result<ScanResponse, (StatusCode, String)> {
let scan_id = Uuid::new_v4().to_string();
let scanned_at = Utc::now();
// Collect issues from various scanners
let mut all_issues: Vec<IssueResponse> = Vec::new();
let mut files_scanned = 0;
// Scan for passwords in config files
let config_issues = scan_config_files().await;
all_issues.extend(config_issues.iter().cloned());
// Scan .bas files for fragile code
let bas_issues = scan_bas_files().await;
files_scanned += bas_issues.len();
all_issues.extend(bas_issues);
// Scan for security issues
let security_issues = scan_security_issues().await;
all_issues.extend(security_issues);
// Filter out info level if not requested
if !include_info {
all_issues.retain(|i| i.severity.to_lowercase() != "info");
}
// Calculate summary
let summary = calculate_summary(&all_issues, files_scanned);
Ok(ScanResponse {
scan_id,
status: "completed".to_string(),
scanned_at,
summary,
issues: all_issues,
})
}
/// Scan config files for passwords and secrets
async fn scan_config_files() -> Vec<IssueResponse> {
let mut issues = Vec::new();
// Patterns that indicate passwords in config
let password_patterns = [
("password", "Password field found in config"),
("api_key", "API key found in config"),
("secret", "Secret found in config"),
("token", "Token found in config"),
("private_key", "Private key found in config"),
];
// Check common config file locations
let config_paths = [
".env",
"config.csv",
"config.json",
"settings.json",
".gbai/config.csv",
];
for path in &config_paths {
if let Ok(content) = tokio::fs::read_to_string(path).await {
for (pattern, description) in &password_patterns {
if content.to_lowercase().contains(pattern) {
// Check if it's using vault reference
if !content.contains("vault://") && !content.contains("${VAULT_") {
issues.push(IssueResponse {
id: Uuid::new_v4().to_string(),
severity: "critical".to_string(),
issue_type: "password_in_config".to_string(),
title: format!("{} not using vault", description),
description: format!(
"Found '{}' in {} without vault reference. Secrets should be stored in a vault, not in config files.",
pattern, path
),
file_path: path.to_string(),
line_number: None,
code_snippet: None,
remediation: format!(
"Move the {} to a vault and reference it using vault://path/to/secret or ${{VAULT_SECRET_NAME}}",
pattern
),
category: "secrets".to_string(),
});
}
}
}
}
}
issues
}
/// Scan .bas files for fragile code patterns
async fn scan_bas_files() -> Vec<IssueResponse> {
let mut issues = Vec::new();
// Fragile code patterns to detect
let fragile_patterns = [
(
r"IF\s+.+\s*=\s*input",
"deprecated_if_input",
"high",
"Deprecated IF...input pattern",
"Use HEAR keyword with validation instead of direct input comparison",
),
(
r"GOTO\s+\w+",
"fragile_code",
"medium",
"GOTO statement found",
"Replace GOTO with structured control flow (IF/FOR/SWITCH)",
),
(
r#"password\s*=\s*["'][^"']+["']"#,
"hardcoded_secret",
"critical",
"Hardcoded password in code",
"Use GET BOT MEMORY or vault references instead of hardcoding passwords",
),
(
r"[A-Z]+_[A-Z]+",
"underscore_in_keyword",
"info",
"Keyword uses underscore instead of space",
"Use spaces in keywords (e.g., 'GET BOT MEMORY' instead of 'GET_BOT_MEMORY')",
),
(
r"(?i)exec\s*\(",
"insecure_pattern",
"critical",
"Dynamic code execution detected",
"Avoid dynamic code execution. Use predefined procedures instead.",
),
(
r"(?i)eval\s*\(",
"insecure_pattern",
"critical",
"Eval statement detected",
"Avoid eval. Use structured data handling instead.",
),
];
// Walk through .bas files
let base_paths = [".", "dialogs", "templates"];
for base_path in &base_paths {
if let Ok(mut entries) = tokio::fs::read_dir(base_path).await {
while let Ok(Some(entry)) = entries.next_entry().await {
let path = entry.path();
if path.extension().map(|e| e == "bas").unwrap_or(false) {
if let Ok(content) = tokio::fs::read_to_string(&path).await {
for (line_num, line) in content.lines().enumerate() {
for (pattern, issue_type, severity, title, remediation) in
&fragile_patterns
{
if let Ok(re) = regex::Regex::new(pattern) {
if re.is_match(line) {
// Skip underscore warnings for internal function names
if *issue_type == "underscore_in_keyword"
&& (line.contains("register_fn")
|| line.starts_with("'")
|| line.starts_with("REM"))
{
continue;
}
issues.push(IssueResponse {
id: Uuid::new_v4().to_string(),
severity: severity.to_string(),
issue_type: issue_type.to_string(),
title: title.to_string(),
description: format!(
"Found fragile code pattern at line {}",
line_num + 1
),
file_path: path.display().to_string(),
line_number: Some(line_num + 1),
code_snippet: Some(line.trim().to_string()),
remediation: remediation.to_string(),
category: "code_quality".to_string(),
});
}
}
}
}
}
}
}
}
}
issues
}
/// Scan for general security issues
async fn scan_security_issues() -> Vec<IssueResponse> {
let mut issues = Vec::new();
// Check for missing security configurations
let security_checks = [
(
".env",
"ENCRYPTION_KEY",
"Encryption key not configured",
"high",
),
(".env", "JWT_SECRET", "JWT secret not configured", "high"),
(
".env",
"RATE_LIMIT_ENABLED",
"Rate limiting not enabled",
"medium",
),
(
".env",
"CORS_ORIGINS",
"CORS origins not configured",
"medium",
),
];
for (file, key, title, severity) in &security_checks {
if let Ok(content) = tokio::fs::read_to_string(file).await {
if !content.contains(key) {
issues.push(IssueResponse {
id: Uuid::new_v4().to_string(),
severity: severity.to_string(),
issue_type: "configuration_issue".to_string(),
title: title.to_string(),
description: format!("{} is not set in {}", key, file),
file_path: file.to_string(),
line_number: None,
code_snippet: None,
remediation: format!("Add {} to your {} file", key, file),
category: "security".to_string(),
});
}
}
}
issues
}
/// Calculate summary statistics
fn calculate_summary(issues: &[IssueResponse], files_scanned: usize) -> ScanSummary {
let mut summary = ScanSummary {
total_files_scanned: files_scanned,
total_issues: issues.len(),
..Default::default()
};
let mut categories: HashMap<String, usize> = HashMap::new();
for issue in issues {
match issue.severity.to_lowercase().as_str() {
"critical" => summary.critical_count += 1,
"high" => summary.high_count += 1,
"medium" => summary.medium_count += 1,
"low" => summary.low_count += 1,
"info" => summary.info_count += 1,
_ => {}
}
*categories.entry(issue.category.clone()).or_insert(0) += 1;
}
summary.categories = categories;
// Calculate compliance score (100 - weighted issues)
let weighted_issues = (summary.critical_count * 25)
+ (summary.high_count * 15)
+ (summary.medium_count * 5)
+ (summary.low_count * 1);
summary.compliance_score = (100.0 - weighted_issues as f64).max(0.0);
summary
}
/// Generate CSV export
fn generate_csv(report: &ScanResponse) -> String {
let mut csv = String::from("ID,Severity,Type,Title,File,Line,Category,Remediation\n");
for issue in &report.issues {
csv.push_str(&format!(
"\"{}\",\"{}\",\"{}\",\"{}\",\"{}\",\"{}\",\"{}\",\"{}\"\n",
issue.id,
issue.severity,
issue.issue_type,
issue.title.replace('"', "\"\""),
issue.file_path,
issue.line_number.map(|n| n.to_string()).unwrap_or_default(),
issue.category,
issue.remediation.replace('"', "\"\""),
));
}
csv
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_summary() {
let issues = vec![
IssueResponse {
id: "1".to_string(),
severity: "critical".to_string(),
issue_type: "test".to_string(),
title: "Test".to_string(),
description: "Test".to_string(),
file_path: "test.bas".to_string(),
line_number: Some(1),
code_snippet: None,
remediation: "Fix it".to_string(),
category: "security".to_string(),
},
IssueResponse {
id: "2".to_string(),
severity: "high".to_string(),
issue_type: "test".to_string(),
title: "Test 2".to_string(),
description: "Test 2".to_string(),
file_path: "test2.bas".to_string(),
line_number: Some(5),
code_snippet: None,
remediation: "Fix it".to_string(),
category: "code_quality".to_string(),
},
];
let summary = calculate_summary(&issues, 10);
assert_eq!(summary.total_issues, 2);
assert_eq!(summary.critical_count, 1);
assert_eq!(summary.high_count, 1);
assert_eq!(summary.total_files_scanned, 10);
assert!(summary.compliance_score < 100.0);
}
#[test]
fn test_generate_csv() {
let report = ScanResponse {
scan_id: "test-123".to_string(),
status: "completed".to_string(),
scanned_at: Utc::now(),
summary: ScanSummary::default(),
issues: vec![IssueResponse {
id: "1".to_string(),
severity: "high".to_string(),
issue_type: "test".to_string(),
title: "Test Issue".to_string(),
description: "Description".to_string(),
file_path: "test.bas".to_string(),
line_number: Some(10),
code_snippet: None,
remediation: "Fix it".to_string(),
category: "security".to_string(),
}],
};
let csv = generate_csv(&report);
assert!(csv.contains("ID,Severity"));
assert!(csv.contains("Test Issue"));
assert!(csv.contains("test.bas"));
}
}

View file

@ -23,6 +23,7 @@ use crate::shared::state::AppState;
pub mod auth; pub mod auth;
pub mod auth_handlers; pub mod auth_handlers;
pub mod chat_handlers; pub mod chat_handlers;
pub mod compliance_handlers;
pub mod stream_handlers; pub mod stream_handlers;
// Module stubs - to be implemented with full HTMX // Module stubs - to be implemented with full HTMX
@ -470,6 +471,7 @@ pub fn create_router(app_state: AppState) -> Router {
.merge(mail::routes()) .merge(mail::routes())
.merge(meet::routes()) .merge(meet::routes())
.merge(tasks::routes()) .merge(tasks::routes())
.merge(compliance_handlers::routes())
// Partials // Partials
.route("/api/apps/menu", get(apps_menu_handler)) .route("/api/apps/menu", get(apps_menu_handler))
.route("/api/user/menu", get(user_menu_handler)) .route("/api/user/menu", get(user_menu_handler))

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff