Frontend
Cyphon UI Components
Location:
app/ui/modules/monitor-ui/src/vue/CyphonTest/
This document explains each UI component in the Cyphon feature.
Component Hierarchy
CyphonTest.vue (Main Container)
├── CyphonHero.vue (Welcome screen)
├── CommandBox.vue (Input area)
├── ResultView.vue (Results display)
│ ├── SearchBar.vue (Mini search in header)
│ ├── AISummaryCard.vue (AI-generated summary)
│ └── SearchResults.vue (Per-command results)
│ └── [Dynamic Cards] (Whois, DNS, etc.)
└── CommandsModal.vue (Command reference modal)1. CyphonTest.vue (Main Container)
Purpose: The brain of Cyphon. Manages state and coordinates all other components.
State Machine
data() {
return {
state: "idle", // idle | responding | complete
messages: [], // Conversation history
showCommandsModal: false
};
}| State | What's Shown |
|---|---|
idle |
CyphonHero + CommandBox |
responding |
ResultView with loading spinner |
complete |
ResultView with results + AI summary |
Key Method: handleSubmit(question)
This is the most important method. Here's what happens:
async handleSubmit(question) {
// 1. Update URL (for sharing/bookmarking)
this.updateUrlQuery(question);
// 2. Add user message to conversation
this.messages = [{
role: "user",
type: "text",
content: question
}];
// 3. Add loading placeholder
this.messages.push({
role: "ai",
type: "loading",
content: null
});
this.state = "responding";
// 4. Call backend to get relevant commands
const fullResponse = await discoveryAPI.executeCyphonQuery({
question
});
// 5. Replace loading with actual results
if (fullResponse?.relevantCommands?.length > 0) {
this.messages[loadingIndex] = {
role: "ai",
type: "cards",
content: fullResponse.relevantCommands
};
}
this.state = "complete";
}URL Query Support
Cyphon supports deep linking. If someone visits ?query=whois("google.com"), it auto-runs that search.
checkUrlQuery() {
const urlParams = new URLSearchParams(window.location.search);
const query = urlParams.get("query");
if (query) {
this.handleSubmit(query);
}
}2. CommandBox.vue
Purpose: The input area where users type their questions.
Features
- Auto-resizing textarea
- Enter to send (Shift+Enter for new line)
- Commands dropdown button
- Send button (enabled when text is present)
Key Props/Events
// Emits
$emit("submit", message); // When user sends message
$emit("open-commands"); // When user clicks Commands buttonUser Experience
┌──────────────────────────────────────────────┐
│ ✨ Ask Cyphon about security threats... │
│ │
├──────────────────────────────────────────────┤
│ [/Commands ▼] [Send] │
└──────────────────────────────────────────────┘
Press Enter to send, Shift+Enter for new line3. ResultView.vue
Purpose: Displays the results after a search. This is where the magic happens.
Props
props: {
messages: Array, // Conversation history from parent
isTyping: Boolean // Shows loading state
}Computed Properties
computed: {
userQuery(); // Extracts the user's question
cards(); // Extracts the relevant commands to display
noResultsMessage; // Handles empty results
}The Results Flow
messages (from parent)
│
├── userQuery: "whois google.com"
│
└── cards: [
{ id: "whois", formattedOutput: "whois(\"google.com\")" }
]
│
▼
For each card, render <SearchResults :keyword="cmd.formattedOutput" />AI Summary Generation
When search results come in, ResultView generates an AI summary:
async generateAISummary() {
const allResults = this.collectedResults.flatMap(r => r.results);
const response = await discoveryAPI.summarizeCyphonResults({
userQuery: this.userQuery,
results: allResults
});
this.aiSummary = response; // { summary: "...", highlights: [...] }
}4. SearchResults.vue
Purpose: Executes a single command and renders the results.
Location: app/ui/modules/monitor-ui/src/vue/cyphon/components/SearchResults.vue
How It Works
- Receives a
keyword(e.g.,whois("google.com")) - Calls
intelAPI.search({ keyword }) - Polls for results every 3 seconds (configurable)
- Renders dynamic card components based on
cardType
The Polling Mechanism
async startResultLoader() {
await this.loadResults(); // First call immediately
// Then poll every 3 seconds
this.resultLoader = setInterval(
this.loadResults,
this.resultRefreshInterval // 3000ms default
);
}
async loadResults() {
// Rate limit: max 20 calls
if (this.loadResultsCallCount >= this.rateLimit) {
this.stopResultLoader();
return;
}
const searchResponse = await intelAPI.search({
keyword: this.keyword,
id: this.searchResponse?.id // Pass ID to get updates
});
if (searchResponse.done) {
this.stopResultLoader();
}
}Dynamic Card Rendering
<component
:is="card.cardType"
v-for="card in searchResponse.results"
:key="card.facts.factId"
v-bind="card.facts.data"
/>The cardType determines which Vue component renders:
Whois→Whois.vueIntelOverview→IntelOverview.vueChart→UniChartCard.vue- etc.
5. CommandsModal.vue
Purpose: A modal showing all available Cyphon commands.
Features
- Searchable command list
- Grouped by category
- Click to auto-fill command
Command Structure
{
name: "whois",
description: "Retrieves WHOIS information for the provided domain.",
syntax: "whois(domain)",
example: 'whois("example.com")',
category: "Domain Intelligence",
icon: "mdi:domain"
}Categories
- DNS (txt, passiveDns)
- Domain Intelligence (whois)
- Web Analysis (technologies, pageIntel)
- OSINT (username, Email)
- Reconnaissance (findSubdomain)
- Security (blacklist, domainSpoof)
- Network (host, port)
- Geolocation (geolocateIp)
6. AISummaryCard.vue
Purpose: Displays the AI-generated summary at the top of results.
Props
props: {
summary: Object, // { summary: "...", highlights: [...] }
loading: Boolean,
error: String
}States
| State | Display |
|---|---|
| Loading | Shimmer animation + "Generating AI summary..." |
| Success | Summary text + highlights list |
| Error | Error message |
7. CyphonHero.vue
Purpose: The welcome screen shown in idle state.
Visual Elements
- Animated logo with orbital rings
- Particle effects background
- "Hello, I'm Cyphon" greeting
- Subtitle about cybersecurity
Component Communication
CyphonTest.vue
│
│──── @submit ────► handleSubmit(question)
│ │
│ ▼
│ discoveryAPI.executeCyphonQuery()
│ │
│ ▼
│◄──── messages ──────────┘
│
▼
ResultView.vue
│
│──── creates ────► SearchResults (one per command)
│ │
│ ▼
│ intelAPI.search()
│ │
│◄─── @results ───────────┘
│
▼
generateAISummary()