Spaces:
Running
# Architecture Complète: Système de Rapports Narratifs Militaires avec Garde-fous LLM et Conformité RGPD
Browse files## Architecture Technique Avancée
```mermaid
graph TB
subgraph "Couche API & Sécurité"
A[OpenRouter API] --> B[Routeur Intelligent Multi-Modèles]
B --> C[Gemma-3B]
B --> D[Mistral-24B]
B --> E[Llama-3.2-3B]
F[API Key Vault] --> B
end
subgraph "Couche Garde-fous LLM"
G[Guardrail Classification] --> H[Guardrail RGPD]
I[Guardrail Hallucinations] --> G
J[Guardrail Contextuel] --> G
K[Validation en Temps Réel] --> G
end
subgraph "Couche Mémoire & Conformité"
L[Mem0 Cloud API] --> M[Chiffrement AES-256]
N[Anonymisation RGPD] --> L
O[Journaux Audit] --> P[Blockchain Privée]
end
B --> G
G --> L
L --> Q[Base de Données Militaire Sécurisée]
```
## Implémentation Immédiate avec vos API
### 1. Configuration OpenRouter Multi-Modèles
```python
import os
from openai import OpenAI
from typing import Dict, List, Optional
class MilitaryLLMRouter:
def __init__(self):
self.client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key="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"
)
self.models = {
"gemma": "google/gemma-3b-it:free",
"mistral": "mistralai/mistral-small-3.1-24b-instruct:free",
"llama": "meta-llama/llama-3.2-3b-instruct:free"
}
self.headers = {
"HTTP-Referer": "https://military-narrative-system.com",
"X-Title": "Military Narrative Reporting System"
}
def route_request(self, content: str, model_preference: str = "mistral") -> Dict:
"""Route vers le modèle approprié avec garde-fous intégrés"""
# Garde-fou de classification préalable
classification_result = self.classify_content(content)
if not classification_result["approved"]:
return {"error": "Classification non autorisée", "details": classification_result}
try:
completion = self.client.chat.completions.create(
extra_headers=self.headers,
model=self.models[model_preference],
messages=[
{
"role": "system",
"content": "Vous êtes un assistant militaire spécialisé dans la création de rapports narratifs. Répondez uniquement avec des informations classifiées appropriées et vérifiées."
},
{
"role": "user",
"content": content
}
],
temperature=0.1, # Faible température pour précision militaire
max_tokens=4000
)
# Validation post-génération
validation_result = self.validate_military_content(completion.choices[0].message.content)
return {
"content": completion.choices[0].message.content,
"model": model_preference,
"validation": validation_result,
"usage": completion.usage
}
except Exception as e:
return {"error": str(e), "model": model_preference}
def classify_content(self, content: str) -> Dict:
"""Garde-fou de classification militaire"""
# Implémentation simplifiée - à adapter selon vos critères
sensitive_terms = ["TOP_SECRET", "SECRET", "CONFIDENTIEL"]
detected_level = "PUBLIC"
for term in sensitive_terms:
if term in content.upper():
detected_level = term
return {
"approved": True,
"detected_level": detected_level,
"required_clearance": "CONFIDENTIEL"
}
def validate_military_content(self, content: str) -> Dict:
"""Validation post-génération des rapports"""
# Vérification des hallucinations
hallucination_score = self.detect_hallucinations(content)
# Vérification RGPD
pii_detected = self.detect_pii(content)
return {
"hallucination_score": hallucination_score,
"pii_detected": len(pii_detected),
"approved": hallucination_score < 0.3 and len(pii_detected) == 0,
"pii_entities": pii_detected
}
```
### 2. Intégration Mem0 sans IP (Solution Cloud)
```python
from mem0 import MemoryClient
import os
class SecureMilitaryMemory:
def __init__(self):
# Utilisation de l'API Mem0 Cloud - pas d'IP exposée
self.client = MemoryClient(
api_key="m0-xxxx" # À configurer via Mem0 Dashboard
)
self.config = {
"collection_name": "military_narrative_memory",
"embedding_model": "military-embeddings-v3",
"anonymization": True,
"retention_days": 365
}
def add_secure_memory(self, user_id: str, content: str, metadata: Dict) -> str:
"""Ajoute une mémoire avec chiffrement et anonymisation RGPD"""
# Anonymisation préalable
anonymized_content = self.anonymize_for_gdpr(content)
memory_id = self.client.add(
user_id=user_id,
content=anonymized_content,
metadata={
**metadata,
"classification": metadata.get('classification', 'PUBLIC'),
"timestamp": os.utcnow().isoformat(),
"anonymized": True
}
)
return memory_id
def retrieve_context(self, user_id: str, query: str, filters: Dict) -> List:
"""Récupère le contexte avec filtres de sécurité militaire"""
# Filtres de sécurité par défaut
security_filters = {
"classification": {"$lte": self.get_user_clearance(user_id)},
"access_control": {"$in": self.get_user_roles(user_id)}
}
combined_filters = {**filters, **security_filters}
results = self.client.search(
user_id=user_id,
query=query,
filters=combined_filters,
limit=10
)
return self.anonymize_results(results, user_id)
def anonymize_for_gdpr(self, content: str) -> str:
"""Anonymisation conforme RGPD"""
# Implémentation simplifiée - utiliser une bibliothèque dédiée
import re
# Masquage des noms de personnes
content = re.sub(r'\b[A-Z][a-z]+ [A-Z][a-z]+\b', '[PERSONNE]', content)
# Masquage des coordonnées
content = re.sub(r'\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b', '[IP]', content)
content = re.sub(r'\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b', '[EMAIL]', content)
return content
```
### 3. Système Complet de Garde-fous LLM
```python
class MilitaryLLMGuardrails:
def __init__(self):
self.classification_levels = {
0: "PUBLIC",
1: "RESTREINT",
2: "CONFIDENTIEL",
3: "SECRET",
4: "TOP_SECRET"
}
# Initialisation des modèles de détection
self.hallucination_detector = self.load_hallucination_model()
self.pii_detector = self.load_pii_model()
def comprehensive_check(self, content: str, user_clearance: int) -> Dict:
"""Vérification complète avec tous les garde-fous"""
results = {
"approved": True,
"checks": {},
"violations": []
}
# 1. Garde-fou de classification
classification_check = self.check_classification(content, user_clearance)
results["checks"]["classification"] = classification_check
if not classification_check["approved"]:
results["approved"] = False
results["violations"].append("CLASSIFICATION_VIOLATION")
# 2. Garde-fou d'hallucinations
hallucination_check = self.check_hallucinations(content)
results["checks"]["hallucinations"] = hallucination_check
if hallucination_check["score"] > 0.25:
results["approved"] = False
results["violations"].append("HALLUCINATION_DETECTED")
# 3. Garde-fou RGPD
gdpr_check = self.check_gdpr_compliance(content)
results["checks"]["gdpr"] = gdpr_check
if not gdpr_check["compliant"]:
results["approved"] = False
results["violations"].append("GDPR_VIOLATION")
# 4. Garde-fou contextuel militaire
context_check = self.check_military_context(content)
results["checks"]["context"] = context_check
if not context_check["valid"]:
results["approved"] = False
results["violations"].append("CONTEXT_VIOLATION")
return results
def check_classification(self, content: str, user_clearance: int) -> Dict:
"""Vérification de la classification militaire"""
# Implémentation avec modèle de classification
detected_level = self.classify_military_content(content)
if detected_level["level"] > user_clearance:
return {
"approved": False,
"detected_level": detected_level["name"],
"user_clearance": self.classification_levels.get(user_clearance, "PUBLIC"),
"message": "Niveau de classification insuffisant"
}
return {
"approved": True,
"detected_level": detected_level["name"],
"user_clearance": self.classification_levels.get(user_clearance, "PUBLIC")
}
def check_hallucinations(self, content: str) -> Dict:
"""Détection d'hallucinations avec modèle spécialisé"""
# Utilisation d'un modèle de détection d'hallucinations
hallucination_score = sel
- README.md +8 -5
- components/ops-navbar.js +84 -0
- components/ops-sidebar.js +122 -0
- components/ops-status-card.js +98 -0
- index.html +208 -19
- script.js +178 -0
- style.css +101 -18
|
@@ -1,10 +1,13 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: IronChronicle 🛡️
|
| 3 |
+
colorFrom: purple
|
| 4 |
+
colorTo: red
|
| 5 |
+
emoji: 🐳
|
| 6 |
sdk: static
|
| 7 |
pinned: false
|
| 8 |
+
tags:
|
| 9 |
+
- deepsite-v3
|
| 10 |
---
|
| 11 |
|
| 12 |
+
# Welcome to your new DeepSite project!
|
| 13 |
+
This project was created with [DeepSite](https://huggingface.co/deepsite).
|
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class OpsNavbar extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
|
| 7 |
+
:host {
|
| 8 |
+
display: flex;
|
| 9 |
+
justify-content: space-between;
|
| 10 |
+
align-items: center;
|
| 11 |
+
background-color: #0f172a; /* Slate 900 */
|
| 12 |
+
border-bottom: 1px solid #334155; /* Slate 700 */
|
| 13 |
+
padding: 0.75rem 1.5rem;
|
| 14 |
+
height: 64px;
|
| 15 |
+
z-index: 50;
|
| 16 |
+
}
|
| 17 |
+
.brand {
|
| 18 |
+
display: flex;
|
| 19 |
+
align-items: center;
|
| 20 |
+
gap: 0.75rem;
|
| 21 |
+
font-weight: 700;
|
| 22 |
+
font-size: 1.25rem;
|
| 23 |
+
color: #22c55e; /* Military 500 */
|
| 24 |
+
font-family: 'Inter', sans-serif;
|
| 25 |
+
letter-spacing: -0.025em;
|
| 26 |
+
}
|
| 27 |
+
.brand span {
|
| 28 |
+
color: #fff;
|
| 29 |
+
}
|
| 30 |
+
.user-profile {
|
| 31 |
+
display: flex;
|
| 32 |
+
align-items: center;
|
| 33 |
+
gap: 1rem;
|
| 34 |
+
}
|
| 35 |
+
.status-badge {
|
| 36 |
+
font-size: 0.75rem;
|
| 37 |
+
background: rgba(34, 197, 94, 0.1);
|
| 38 |
+
color: #22c55e;
|
| 39 |
+
padding: 0.25rem 0.75rem;
|
| 40 |
+
border-radius: 9999px;
|
| 41 |
+
border: 1px solid rgba(34, 197, 94, 0.2);
|
| 42 |
+
}
|
| 43 |
+
.avatar {
|
| 44 |
+
width: 36px;
|
| 45 |
+
height: 36px;
|
| 46 |
+
border-radius: 50%;
|
| 47 |
+
border: 2px solid #334155;
|
| 48 |
+
}
|
| 49 |
+
/* Mobile Menu Button */
|
| 50 |
+
.menu-toggle {
|
| 51 |
+
display: none;
|
| 52 |
+
background: none;
|
| 53 |
+
border: none;
|
| 54 |
+
color: white;
|
| 55 |
+
cursor: pointer;
|
| 56 |
+
}
|
| 57 |
+
@media (max-width: 768px) {
|
| 58 |
+
.menu-toggle {
|
| 59 |
+
display: block;
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
</style>
|
| 63 |
+
|
| 64 |
+
<div class="brand">
|
| 65 |
+
<i data-feather="shield"></i>
|
| 66 |
+
Iron<span>Chronicle</span>
|
| 67 |
+
</div>
|
| 68 |
+
|
| 69 |
+
<div class="user-profile">
|
| 70 |
+
<div class="status-badge flex items-center gap-2">
|
| 71 |
+
<div class="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div>
|
| 72 |
+
SECURE CONNECTION
|
| 73 |
+
</div>
|
| 74 |
+
<div class="hidden md:block text-right">
|
| 75 |
+
<div class="text-sm font-semibold text-white">Maj. Carter</div>
|
| 76 |
+
<div class="text-xs text-slate-400">ID: MCX-9921</div>
|
| 77 |
+
</div>
|
| 78 |
+
<img src="http://static.photos/people/200x200/1" alt="User Avatar" class="avatar">
|
| 79 |
+
</div>
|
| 80 |
+
`;
|
| 81 |
+
}
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
customElements.define('ops-navbar', OpsNavbar);
|
|
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class OpsSidebar extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');
|
| 7 |
+
:host {
|
| 8 |
+
width: 250px;
|
| 9 |
+
background-color: #020617; /* Slate 950 */
|
| 10 |
+
border-right: 1px solid #1e293b; /* Slate 800 */
|
| 11 |
+
display: flex;
|
| 12 |
+
flex-direction: column;
|
| 13 |
+
padding: 1.5rem 1rem;
|
| 14 |
+
height: 100%;
|
| 15 |
+
transition: transform 0.3s ease;
|
| 16 |
+
}
|
| 17 |
+
.nav-list {
|
| 18 |
+
list-style: none;
|
| 19 |
+
padding: 0;
|
| 20 |
+
margin: 0;
|
| 21 |
+
display: flex;
|
| 22 |
+
flex-direction: column;
|
| 23 |
+
gap: 0.5rem;
|
| 24 |
+
}
|
| 25 |
+
.nav-item {
|
| 26 |
+
display: flex;
|
| 27 |
+
align-items: center;
|
| 28 |
+
gap: 0.75rem;
|
| 29 |
+
padding: 0.75rem 1rem;
|
| 30 |
+
border-radius: 0.5rem;
|
| 31 |
+
color: #94a3b8; /* Slate 400 */
|
| 32 |
+
text-decoration: none;
|
| 33 |
+
font-weight: 500;
|
| 34 |
+
font-size: 0.95rem;
|
| 35 |
+
transition: all 0.2s ease;
|
| 36 |
+
cursor: pointer;
|
| 37 |
+
}
|
| 38 |
+
.nav-item:hover {
|
| 39 |
+
background-color: #1e293b;
|
| 40 |
+
color: #fff;
|
| 41 |
+
}
|
| 42 |
+
.nav-item.active {
|
| 43 |
+
background-color: #14532d; /* Military 800 with opacity */
|
| 44 |
+
color: #22c55e;
|
| 45 |
+
border-left: 3px solid #22c55e;
|
| 46 |
+
}
|
| 47 |
+
.nav-item i {
|
| 48 |
+
width: 18px;
|
| 49 |
+
height: 18px;
|
| 50 |
+
}
|
| 51 |
+
.sidebar-footer {
|
| 52 |
+
margin-top: auto;
|
| 53 |
+
border-top: 1px solid #1e293b;
|
| 54 |
+
padding-top: 1rem;
|
| 55 |
+
}
|
| 56 |
+
.system-info {
|
| 57 |
+
font-family: 'Courier Prime', monospace;
|
| 58 |
+
font-size: 0.7rem;
|
| 59 |
+
color: #475569;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
@media (max-width: 768px) {
|
| 63 |
+
:host {
|
| 64 |
+
position: absolute;
|
| 65 |
+
z-index: 40;
|
| 66 |
+
transform: translateX(-100%);
|
| 67 |
+
}
|
| 68 |
+
:host.open {
|
| 69 |
+
transform: translateX(0);
|
| 70 |
+
}
|
| 71 |
+
}
|
| 72 |
+
</style>
|
| 73 |
+
|
| 74 |
+
<nav class="nav-list">
|
| 75 |
+
<a href="#" class="nav-item active" data-page="dashboard">
|
| 76 |
+
<i data-feather="grid"></i>
|
| 77 |
+
<span>Dashboard</span>
|
| 78 |
+
</a>
|
| 79 |
+
<a href="#" class="nav-item" data-page="generator">
|
| 80 |
+
<i data-feather="file-text"></i>
|
| 81 |
+
<span>Generator</span>
|
| 82 |
+
</a>
|
| 83 |
+
<a href="#" class="nav-item" data-page="memory">
|
| 84 |
+
<i data-feather="database"></i>
|
| 85 |
+
<span>Secure Memory</span>
|
| 86 |
+
</a>
|
| 87 |
+
<a href="#" class="nav-item" data-page="settings">
|
| 88 |
+
<i data-feather="settings"></i>
|
| 89 |
+
<span>System Config</span>
|
| 90 |
+
</a>
|
| 91 |
+
</nav>
|
| 92 |
+
|
| 93 |
+
<div class="sidebar-footer">
|
| 94 |
+
<div class="system-info">
|
| 95 |
+
<div>SYS.VER: 4.2.0-ALPHA</div>
|
| 96 |
+
<div>ENCRYPTION: AES-256</div>
|
| 97 |
+
<div>NODE: EU-WEST-4</div>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
`;
|
| 101 |
+
|
| 102 |
+
// Event Handling inside Shadow DOM for navigation
|
| 103 |
+
const links = this.shadowRoot.querySelectorAll('.nav-item');
|
| 104 |
+
links.forEach(link => {
|
| 105 |
+
link.addEventListener('click', (e) => {
|
| 106 |
+
e.preventDefault();
|
| 107 |
+
// Update active class visual
|
| 108 |
+
links.forEach(l => l.classList.remove('active'));
|
| 109 |
+
link.classList.add('active');
|
| 110 |
+
|
| 111 |
+
// Dispatch custom event to main document to handle page switching
|
| 112 |
+
this.dispatchEvent(new CustomEvent('nav-change', {
|
| 113 |
+
detail: { page: link.getAttribute('data-page') },
|
| 114 |
+
bubbles: true,
|
| 115 |
+
composed: true
|
| 116 |
+
}));
|
| 117 |
+
});
|
| 118 |
+
});
|
| 119 |
+
}
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
customElements.define('ops-sidebar', OpsSidebar);
|
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class OpsStatusCard extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
|
| 5 |
+
const title = this.getAttribute('title') || 'Unknown';
|
| 6 |
+
const status = this.getAttribute('status') || 'Offline';
|
| 7 |
+
const icon = this.getAttribute('icon') || 'help-circle';
|
| 8 |
+
const color = this.getAttribute('color') || 'text-slate-400';
|
| 9 |
+
const detail = this.getAttribute('detail') || '';
|
| 10 |
+
|
| 11 |
+
this.shadowRoot.innerHTML = `
|
| 12 |
+
<style>
|
| 13 |
+
:host {
|
| 14 |
+
display: block;
|
| 15 |
+
}
|
| 16 |
+
.card {
|
| 17 |
+
background-color: #0f172a; /* Slate 900 */
|
| 18 |
+
border: 1px solid #1e293b; /* Slate 800 */
|
| 19 |
+
border-radius: 0.75rem;
|
| 20 |
+
padding: 1.5rem;
|
| 21 |
+
position: relative;
|
| 22 |
+
overflow: hidden;
|
| 23 |
+
transition: transform 0.2s, box-shadow 0.2s;
|
| 24 |
+
}
|
| 25 |
+
.card:hover {
|
| 26 |
+
transform: translateY(-2px);
|
| 27 |
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
|
| 28 |
+
border-color: #334155;
|
| 29 |
+
}
|
| 30 |
+
.card-header {
|
| 31 |
+
display: flex;
|
| 32 |
+
justify-content: space-between;
|
| 33 |
+
align-items: flex-start;
|
| 34 |
+
margin-bottom: 1rem;
|
| 35 |
+
}
|
| 36 |
+
.icon-box {
|
| 37 |
+
padding: 0.5rem;
|
| 38 |
+
border-radius: 0.5rem;
|
| 39 |
+
background-color: rgba(255, 255, 255, 0.05);
|
| 40 |
+
}
|
| 41 |
+
h3 {
|
| 42 |
+
margin: 0;
|
| 43 |
+
font-size: 0.875rem;
|
| 44 |
+
font-weight: 500;
|
| 45 |
+
color: #94a3b8; /* Slate 400 */
|
| 46 |
+
text-transform: uppercase;
|
| 47 |
+
letter-spacing: 0.05em;
|
| 48 |
+
}
|
| 49 |
+
.status-text {
|
| 50 |
+
font-size: 1.25rem;
|
| 51 |
+
font-weight: 700;
|
| 52 |
+
color: #f8fafc;
|
| 53 |
+
margin-bottom: 0.5rem;
|
| 54 |
+
}
|
| 55 |
+
.detail-text {
|
| 56 |
+
font-size: 0.75rem;
|
| 57 |
+
font-family: 'Courier Prime', monospace;
|
| 58 |
+
color: #64748b;
|
| 59 |
+
}
|
| 60 |
+
/* Decorative bar */
|
| 61 |
+
.card::before {
|
| 62 |
+
content: '';
|
| 63 |
+
position: absolute;
|
| 64 |
+
left: 0;
|
| 65 |
+
top: 0;
|
| 66 |
+
bottom: 0;
|
| 67 |
+
width: 4px;
|
| 68 |
+
background-color: var(--accent-color, #64748b);
|
| 69 |
+
}
|
| 70 |
+
</style>
|
| 71 |
+
|
| 72 |
+
<div class="card" style="--accent-color: ${this.getColorValue(status)}">
|
| 73 |
+
<div class="card-header">
|
| 74 |
+
<div>
|
| 75 |
+
<h3>${title}</h3>
|
| 76 |
+
</div>
|
| 77 |
+
<div class="icon-box ${color}">
|
| 78 |
+
<i data-feather="${icon}" class="w-5 h-5"></i>
|
| 79 |
+
</div>
|
| 80 |
+
</div>
|
| 81 |
+
<div class="status-text">${status}</div>
|
| 82 |
+
<div class="detail-text">${detail}</div>
|
| 83 |
+
</div>
|
| 84 |
+
`;
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
getColorValue(status) {
|
| 88 |
+
if (status === 'Operational' || status === 'Secure' || status === 'Active' || status === 'Strict') {
|
| 89 |
+
return '#22c55e'; // Green
|
| 90 |
+
}
|
| 91 |
+
if (status === 'Warning' || status === 'Checking') {
|
| 92 |
+
return '#f59e0b'; // Amber
|
| 93 |
+
}
|
| 94 |
+
return '#ef4444'; // Red/Gray
|
| 95 |
+
}
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
customElements.define('ops-status-card', OpsStatusCard);
|
|
@@ -1,19 +1,208 @@
|
|
| 1 |
-
<!
|
| 2 |
-
<html>
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en" class="dark">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>IronChronicle | Military Narrative System</title>
|
| 7 |
+
<link rel="icon" type="image/x-icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🛡️</text></svg>">
|
| 8 |
+
<link rel="stylesheet" href="style.css">
|
| 9 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 10 |
+
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
|
| 11 |
+
<script src="https://unpkg.com/feather-icons"></script>
|
| 12 |
+
<script>
|
| 13 |
+
tailwind.config = {
|
| 14 |
+
darkMode: 'class',
|
| 15 |
+
theme: {
|
| 16 |
+
extend: {
|
| 17 |
+
colors: {
|
| 18 |
+
military: {
|
| 19 |
+
50: '#f0fdf4',
|
| 20 |
+
100: '#dcfce7',
|
| 21 |
+
500: '#22c55e', // Primary Green
|
| 22 |
+
600: '#16a34a',
|
| 23 |
+
800: '#14532d',
|
| 24 |
+
900: '#052e16',
|
| 25 |
+
950: '#020617', // Dark BG
|
| 26 |
+
},
|
| 27 |
+
alert: {
|
| 28 |
+
500: '#f59e0b', // Secondary/Warning
|
| 29 |
+
900: '#78350f',
|
| 30 |
+
}
|
| 31 |
+
},
|
| 32 |
+
fontFamily: {
|
| 33 |
+
mono: ['"Courier Prime"', 'Courier', 'monospace'],
|
| 34 |
+
sans: ['"Inter"', 'sans-serif'],
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
</script>
|
| 40 |
+
</head>
|
| 41 |
+
<body class="bg-military-950 text-slate-300 font-sans antialiased overflow-hidden h-screen flex flex-col">
|
| 42 |
+
|
| 43 |
+
<!-- Top Navigation -->
|
| 44 |
+
<ops-navbar></ops-navbar>
|
| 45 |
+
|
| 46 |
+
<div class="flex flex-1 overflow-hidden relative">
|
| 47 |
+
<!-- Sidebar -->
|
| 48 |
+
<ops-sidebar></ops-sidebar>
|
| 49 |
+
|
| 50 |
+
<!-- Main Content Area -->
|
| 51 |
+
<main class="flex-1 overflow-y-auto bg-military-950 p-4 md:p-8 relative z-0" id="main-content">
|
| 52 |
+
|
| 53 |
+
<!-- Dashboard View -->
|
| 54 |
+
<section id="view-dashboard" class="space-y-6 fade-in">
|
| 55 |
+
<header class="mb-8">
|
| 56 |
+
<h1 class="text-3xl font-bold text-white tracking-wide uppercase flex items-center gap-2">
|
| 57 |
+
<i data-feather="monitor" class="text-military-500"></i>
|
| 58 |
+
System Overview
|
| 59 |
+
</h1>
|
| 60 |
+
<p class="text-slate-400 mt-2 font-mono text-sm">REAL-TIME MONITORING // ACTIVE GUARDRAILS</p>
|
| 61 |
+
</header>
|
| 62 |
+
|
| 63 |
+
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
| 64 |
+
<ops-status-card
|
| 65 |
+
title="OpenRouter API"
|
| 66 |
+
status="Operational"
|
| 67 |
+
icon="cpu"
|
| 68 |
+
color="text-blue-400"
|
| 69 |
+
detail="Latency: 124ms"
|
| 70 |
+
></ops-status-card>
|
| 71 |
+
|
| 72 |
+
<ops-status-card
|
| 73 |
+
title="Mem0 Memory"
|
| 74 |
+
status="Secure"
|
| 75 |
+
icon="database"
|
| 76 |
+
color="text-military-500"
|
| 77 |
+
detail="AES-256 Encrypted"
|
| 78 |
+
></ops-status-card>
|
| 79 |
+
|
| 80 |
+
<ops-status-card
|
| 81 |
+
title="GDPR Compliance"
|
| 82 |
+
status="Active"
|
| 83 |
+
icon="shield"
|
| 84 |
+
color="text-alert-500"
|
| 85 |
+
detail="Anonymization: ON"
|
| 86 |
+
></ops-status-card>
|
| 87 |
+
|
| 88 |
+
<ops-status-card
|
| 89 |
+
title="Guardrails"
|
| 90 |
+
status="Strict"
|
| 91 |
+
icon="check-square"
|
| 92 |
+
color="text-purple-400"
|
| 93 |
+
detail="Hallucination Check: ON"
|
| 94 |
+
></ops-status-card>
|
| 95 |
+
</div>
|
| 96 |
+
|
| 97 |
+
<!-- Recent Activity Feed -->
|
| 98 |
+
<div class="mt-8 bg-slate-900/50 backdrop-blur border border-slate-800 rounded-lg p-6">
|
| 99 |
+
<h3 class="text-lg font-semibold text-white mb-4 flex items-center gap-2">
|
| 100 |
+
<i data-feather="activity"></i> Live Audit Logs
|
| 101 |
+
</h3>
|
| 102 |
+
<div class="space-y-3 font-mono text-sm" id="audit-log-container">
|
| 103 |
+
<!-- Logs injected via JS -->
|
| 104 |
+
</div>
|
| 105 |
+
</div>
|
| 106 |
+
</section>
|
| 107 |
+
|
| 108 |
+
<!-- Report Generator View -->
|
| 109 |
+
<section id="view-generator" class="hidden space-y-6 fade-in">
|
| 110 |
+
<header class="mb-6">
|
| 111 |
+
<h1 class="text-3xl font-bold text-white tracking-wide uppercase flex items-center gap-2">
|
| 112 |
+
<i data-feather="edit-3" class="text-military-500"></i>
|
| 113 |
+
Narrative Generation
|
| 114 |
+
</h1>
|
| 115 |
+
<p class="text-slate-400 mt-2 font-mono text-sm">SECURE INPUT // MULTI-MODEL ROUTING</p>
|
| 116 |
+
</header>
|
| 117 |
+
|
| 118 |
+
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
| 119 |
+
<!-- Input Panel -->
|
| 120 |
+
<div class="lg:col-span-1 space-y-4">
|
| 121 |
+
<div class="bg-slate-900/50 border border-slate-800 p-6 rounded-lg">
|
| 122 |
+
<label class="block text-sm font-medium text-slate-400 mb-2">Classification Level</label>
|
| 123 |
+
<select id="classification-level" class="w-full bg-slate-950 border border-slate-700 text-white rounded p-2 focus:border-military-500 focus:outline-none">
|
| 124 |
+
<option value="PUBLIC">PUBLIC</option>
|
| 125 |
+
<option value="RESTREINT">RESTREINT</option>
|
| 126 |
+
<option value="CONFIDENTIEL" selected>CONFIDENTIEL</option>
|
| 127 |
+
<option value="SECRET">SECRET</option>
|
| 128 |
+
<option value="TOP_SECRET">TOP SECRET</option>
|
| 129 |
+
</select>
|
| 130 |
+
</div>
|
| 131 |
+
|
| 132 |
+
<div class="bg-slate-900/50 border border-slate-800 p-6 rounded-lg">
|
| 133 |
+
<label class="block text-sm font-medium text-slate-400 mb-2">AI Model Preference</label>
|
| 134 |
+
<div class="space-y-2">
|
| 135 |
+
<label class="flex items-center space-x-2 cursor-pointer">
|
| 136 |
+
<input type="radio" name="model" value="mistral" checked class="form-radio text-military-500 bg-slate-800 border-slate-600">
|
| 137 |
+
<span class="text-white">Mistral Small 24B (Precise)</span>
|
| 138 |
+
</label>
|
| 139 |
+
<label class="flex items-center space-x-2 cursor-pointer">
|
| 140 |
+
<input type="radio" name="model" value="llama" class="form-radio text-military-500 bg-slate-800 border-slate-600">
|
| 141 |
+
<span class="text-white">Llama 3.2 3B (Fast)</span>
|
| 142 |
+
</label>
|
| 143 |
+
<label class="flex items-center space-x-2 cursor-pointer">
|
| 144 |
+
<input type="radio" name="model" value="gemma" class="form-radio text-military-500 bg-slate-800 border-slate-600">
|
| 145 |
+
<span class="text-white">Gemma 3B (Efficient)</span>
|
| 146 |
+
</label>
|
| 147 |
+
</div>
|
| 148 |
+
</div>
|
| 149 |
+
|
| 150 |
+
<div class="bg-slate-900/50 border border-slate-800 p-6 rounded-lg">
|
| 151 |
+
<label class="block text-sm font-medium text-slate-400 mb-2">Raw Intel Input</label>
|
| 152 |
+
<textarea id="report-input" rows="8" class="w-full bg-slate-950 border border-slate-700 text-slate-300 rounded p-3 focus:border-military-500 focus:outline-none font-mono text-sm resize-none" placeholder="Paste raw data or observations here..."></textarea>
|
| 153 |
+
<button id="generate-btn" class="w-full mt-4 bg-military-600 hover:bg-military-500 text-white font-bold py-3 px-4 rounded transition-all duration-300 flex items-center justify-center gap-2 shadow-lg shadow-military-900/50">
|
| 154 |
+
<i data-feather="zap"></i> Initialize Generation
|
| 155 |
+
</button>
|
| 156 |
+
</div>
|
| 157 |
+
</div>
|
| 158 |
+
|
| 159 |
+
<!-- Output Panel -->
|
| 160 |
+
<div class="lg:col-span-2 bg-slate-900/50 border border-slate-800 rounded-lg flex flex-col h-[600px]">
|
| 161 |
+
<div class="p-4 border-b border-slate-800 flex justify-between items-center bg-slate-900/80">
|
| 162 |
+
<span class="text-sm font-mono text-slate-400">OUTPUT STREAM //</span>
|
| 163 |
+
<div class="flex gap-2">
|
| 164 |
+
<span id="guard-status" class="hidden px-2 py-1 rounded text-xs font-bold bg-yellow-900/50 text-yellow-400 border border-yellow-700/50 animate-pulse">CHECKING GUARDRAILS...</span>
|
| 165 |
+
</div>
|
| 166 |
+
</div>
|
| 167 |
+
|
| 168 |
+
<div id="output-container" class="flex-1 p-6 overflow-y-auto font-mono text-sm leading-relaxed text-slate-300">
|
| 169 |
+
<div class="text-slate-600 italic flex flex-col items-center justify-center h-full">
|
| 170 |
+
<i data-feather="terminal" class="w-12 h-12 mb-4 opacity-50"></i>
|
| 171 |
+
<p>System Ready. Awaiting input...</p>
|
| 172 |
+
</div>
|
| 173 |
+
</div>
|
| 174 |
+
|
| 175 |
+
<!-- Result Footer -->
|
| 176 |
+
<div id="result-footer" class="hidden p-4 border-t border-slate-800 bg-slate-900/80">
|
| 177 |
+
<div class="flex justify-between items-center text-xs font-mono">
|
| 178 |
+
<div class="flex gap-4">
|
| 179 |
+
<span class="text-green-400">PII: <span id="res-pii">None</span></span>
|
| 180 |
+
<span class="text-blue-400">Hallucination: <span id="res-hallucination">0.02</span></span>
|
| 181 |
+
</div>
|
| 182 |
+
<button class="text-military-500 hover:text-white flex items-center gap-1">
|
| 183 |
+
<i data-feather="save"></i> Save to Memory
|
| 184 |
+
</button>
|
| 185 |
+
</div>
|
| 186 |
+
</div>
|
| 187 |
+
</div>
|
| 188 |
+
</div>
|
| 189 |
+
</section>
|
| 190 |
+
|
| 191 |
+
</main>
|
| 192 |
+
</div>
|
| 193 |
+
|
| 194 |
+
<!-- Toast Container -->
|
| 195 |
+
<div id="toast-container" class="fixed bottom-4 right-4 z-50 flex flex-col gap-2"></div>
|
| 196 |
+
|
| 197 |
+
<!-- Web Components Scripts -->
|
| 198 |
+
<script src="components/ops-navbar.js"></script>
|
| 199 |
+
<script src="components/ops-sidebar.js"></script>
|
| 200 |
+
<script src="components/ops-status-card.js"></script>
|
| 201 |
+
<script src="components/ops-modal.js"></script>
|
| 202 |
+
|
| 203 |
+
<!-- Main Logic -->
|
| 204 |
+
<script src="script.js"></script>
|
| 205 |
+
<script>feather.replace();</script>
|
| 206 |
+
<script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
|
| 207 |
+
</body>
|
| 208 |
+
</html>
|
|
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* IronChronicle Main Logic
|
| 3 |
+
* Handles routing, state management, and API simulation.
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 7 |
+
|
| 8 |
+
// --- Navigation Logic ---
|
| 9 |
+
const navLinks = document.querySelectorAll('.nav-link');
|
| 10 |
+
const views = {
|
| 11 |
+
'dashboard': document.getElementById('view-dashboard'),
|
| 12 |
+
'generator': document.getElementById('view-generator')
|
| 13 |
+
};
|
| 14 |
+
|
| 15 |
+
navLinks.forEach(link => {
|
| 16 |
+
link.addEventListener('click', (e) => {
|
| 17 |
+
e.preventDefault();
|
| 18 |
+
const target = link.getAttribute('data-page');
|
| 19 |
+
|
| 20 |
+
// Update Active State
|
| 21 |
+
navLinks.forEach(l => l.classList.remove('text-military-500', 'bg-slate-800'));
|
| 22 |
+
navLinks.forEach(l => l.classList.add('text-slate-400', 'hover:bg-slate-800'));
|
| 23 |
+
link.classList.remove('text-slate-400', 'hover:bg-slate-800');
|
| 24 |
+
link.classList.add('text-military-500', 'bg-slate-800');
|
| 25 |
+
|
| 26 |
+
// Switch View
|
| 27 |
+
Object.values(views).forEach(view => view.classList.add('hidden'));
|
| 28 |
+
views[target].classList.remove('hidden');
|
| 29 |
+
|
| 30 |
+
// Trigger re-anim
|
| 31 |
+
views[target].classList.remove('fade-in');
|
| 32 |
+
void views[target].offsetWidth; // trigger reflow
|
| 33 |
+
views[target].classList.add('fade-in');
|
| 34 |
+
});
|
| 35 |
+
});
|
| 36 |
+
|
| 37 |
+
// --- Audit Log Simulation ---
|
| 38 |
+
const logContainer = document.getElementById('audit-log-container');
|
| 39 |
+
const mockLogs = [
|
| 40 |
+
{ time: '08:42:11', event: 'USER_LOGIN', user: 'Cmdr. Shepard', status: 'SUCCESS' },
|
| 41 |
+
{ time: '08:45:03', event: 'MEMORY_WRITE', user: 'System', status: 'ENCRYPTED' },
|
| 42 |
+
{ time: '08:50:22', event: 'GUARDRAIL_CHECK', user: 'AI_Model_Gemma', status: 'PASSED' },
|
| 43 |
+
{ time: '09:12:44', event: 'REPORT_GENERATE', user: 'Lt. Ripley', status: 'APPROVED' }
|
| 44 |
+
];
|
| 45 |
+
|
| 46 |
+
function renderLogs() {
|
| 47 |
+
logContainer.innerHTML = mockLogs.map(log => `
|
| 48 |
+
<div class="flex items-center justify-between p-2 bg-slate-950/50 rounded border-l-2 ${log.status === 'SUCCESS' || log.status === 'APPROVED' || log.status === 'ENCRYPTED' ? 'border-green-500' : 'border-yellow-500'}">
|
| 49 |
+
<span class="text-slate-500">[${log.time}]</span>
|
| 50 |
+
<span class="text-slate-300 font-bold flex-1 ml-4">${log.event}</span>
|
| 51 |
+
<span class="text-slate-400 mr-4">${log.user}</span>
|
| 52 |
+
<span class="text-xs ${log.status.includes('PASS') || log.status.includes('SUCCESS') ? 'text-green-400' : 'text-yellow-400'}">${log.status}</span>
|
| 53 |
+
</div>
|
| 54 |
+
`).join('');
|
| 55 |
+
}
|
| 56 |
+
renderLogs();
|
| 57 |
+
|
| 58 |
+
// --- Report Generation Simulation ---
|
| 59 |
+
const generateBtn = document.getElementById('generate-btn');
|
| 60 |
+
const inputArea = document.getElementById('report-input');
|
| 61 |
+
const outputContainer = document.getElementById('output-container');
|
| 62 |
+
const guardStatus = document.getElementById('guard-status');
|
| 63 |
+
const resultFooter = document.getElementById('result-footer');
|
| 64 |
+
|
| 65 |
+
// Stats
|
| 66 |
+
const resPii = document.getElementById('res-pii');
|
| 67 |
+
const resHallucination = document.getElementById('res-hallucination');
|
| 68 |
+
|
| 69 |
+
generateBtn.addEventListener('click', async () => {
|
| 70 |
+
const inputText = inputArea.value.trim();
|
| 71 |
+
if(!inputText) {
|
| 72 |
+
showToast('Input required. Please enter raw intel.', 'error');
|
| 73 |
+
return;
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
// UI Loading State
|
| 77 |
+
generateBtn.disabled = true;
|
| 78 |
+
generateBtn.innerHTML = '<i data-feather="loader" class="animate-spin"></i> Processing...';
|
| 79 |
+
feather.replace();
|
| 80 |
+
outputContainer.innerHTML = ''; // Clear previous
|
| 81 |
+
|
| 82 |
+
// Step 1: Guardrails Simulation (Visual feedback)
|
| 83 |
+
guardStatus.classList.remove('hidden');
|
| 84 |
+
guardStatus.innerText = "CHECKING CLASSIFICATION...";
|
| 85 |
+
|
| 86 |
+
await wait(800);
|
| 87 |
+
guardStatus.innerText = "SCANNING PII & GDPR...";
|
| 88 |
+
guardStatus.className = "px-2 py-1 rounded text-xs font-bold bg-blue-900/50 text-blue-400 border border-blue-700/50 animate-pulse";
|
| 89 |
+
|
| 90 |
+
await wait(1000);
|
| 91 |
+
guardStatus.innerText = "RUNNING HALLUCINATION DETECTION...";
|
| 92 |
+
guardStatus.className = "px-2 py-1 rounded text-xs font-bold bg-purple-900/50 text-purple-400 border border-purple-700/50 animate-pulse";
|
| 93 |
+
|
| 94 |
+
await wait(1200);
|
| 95 |
+
|
| 96 |
+
// Step 2: Generation Simulation
|
| 97 |
+
guardStatus.classList.add('hidden');
|
| 98 |
+
resultFooter.classList.add('hidden');
|
| 99 |
+
|
| 100 |
+
const reportText = `CLASSIFIED REPORT // GENERATED AT ${new Date().toLocaleTimeString()}\n\nBased on the provided intel, the operational situation in sector 7G indicates heightened activity. The 3rd Battalion has secured the perimeter, but reconnaissance drones have detected irregular movement patterns consistent with guerrilla tactics.\n\nRECOMMENDATIONS:\n1. Increase surveillance frequency.\n2. Deploy counter-sniper teams to vantage points Alpha and Bravo.\n3. Secure supply lines via Route 66.\n\nNote: Civilian presence detected within the exclusion zone. Coordinates logged and forwarded to Command.`;
|
| 101 |
+
|
| 102 |
+
// Typewriter effect
|
| 103 |
+
outputContainer.innerHTML = '<div class="typing-cursor text-military-500"></div>';
|
| 104 |
+
const cursor = outputContainer.querySelector('.typing-cursor');
|
| 105 |
+
let i = 0;
|
| 106 |
+
|
| 107 |
+
const typeInterval = setInterval(() => {
|
| 108 |
+
cursor.textContent = reportText.substring(0, i);
|
| 109 |
+
i++;
|
| 110 |
+
if (i > reportText.length) {
|
| 111 |
+
clearInterval(typeInterval);
|
| 112 |
+
finalizeReport();
|
| 113 |
+
}
|
| 114 |
+
// Auto scroll
|
| 115 |
+
outputContainer.scrollTop = outputContainer.scrollHeight;
|
| 116 |
+
}, 20); // Speed of typing
|
| 117 |
+
|
| 118 |
+
function finalizeReport() {
|
| 119 |
+
generateBtn.disabled = false;
|
| 120 |
+
generateBtn.innerHTML = '<i data-feather="zap"></i> Initialize Generation';
|
| 121 |
+
feather.replace();
|
| 122 |
+
|
| 123 |
+
// Show footer stats
|
| 124 |
+
resultFooter.classList.remove('hidden');
|
| 125 |
+
|
| 126 |
+
// Random stats for demo
|
| 127 |
+
const pii = Math.random() > 0.8 ? "1 Name Redacted" : "None";
|
| 128 |
+
const hallScore = (Math.random() * 0.1).toFixed(3);
|
| 129 |
+
|
| 130 |
+
resPii.textContent = pii;
|
| 131 |
+
resPii.className = pii === "None" ? "text-green-400" : "text-alert-500";
|
| 132 |
+
|
| 133 |
+
resHallucination.textContent = hallScore;
|
| 134 |
+
|
| 135 |
+
// Add to logs
|
| 136 |
+
mockLogs.unshift({
|
| 137 |
+
time: new Date().toLocaleTimeString(),
|
| 138 |
+
event: 'REPORT_GEN_SUCCESS',
|
| 139 |
+
user: 'CurrentUser',
|
| 140 |
+
status: 'VERIFIED'
|
| 141 |
+
});
|
| 142 |
+
renderLogs();
|
| 143 |
+
|
| 144 |
+
showToast('Narrative report generated successfully.', 'success');
|
| 145 |
+
}
|
| 146 |
+
});
|
| 147 |
+
|
| 148 |
+
function wait(ms) {
|
| 149 |
+
return new Promise(resolve => setTimeout(resolve, ms));
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
// --- Toast System ---
|
| 153 |
+
window.showToast = function(message, type = 'info') {
|
| 154 |
+
const container = document.getElementById('toast-container');
|
| 155 |
+
const toast = document.createElement('div');
|
| 156 |
+
|
| 157 |
+
let colors = type === 'success' ? 'bg-military-600 border-green-400' :
|
| 158 |
+
type === 'error' ? 'bg-red-900/90 border-red-500' :
|
| 159 |
+
'bg-slate-800 border-slate-500';
|
| 160 |
+
|
| 161 |
+
let icon = type === 'success' ? 'check-circle' : type === 'error' ? 'alert-circle' : 'info';
|
| 162 |
+
|
| 163 |
+
toast.className = `toast flex items-center gap-3 px-4 py-3 rounded shadow-lg border-l-4 ${colors} text-white min-w-[300px] backdrop-blur-sm`;
|
| 164 |
+
toast.innerHTML = `
|
| 165 |
+
<i data-feather="${icon}" class="w-5 h-5"></i>
|
| 166 |
+
<span class="text-sm font-medium">${message}</span>
|
| 167 |
+
`;
|
| 168 |
+
|
| 169 |
+
container.appendChild(toast);
|
| 170 |
+
feather.replace();
|
| 171 |
+
|
| 172 |
+
setTimeout(() => {
|
| 173 |
+
toast.style.opacity = '0';
|
| 174 |
+
toast.style.transform = 'translateY(100%)';
|
| 175 |
+
setTimeout(() => toast.remove(), 300);
|
| 176 |
+
}, 4000);
|
| 177 |
+
};
|
| 178 |
+
});
|
|
@@ -1,28 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
body {
|
| 2 |
-
|
| 3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
}
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
}
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
margin: 0 auto;
|
| 21 |
-
padding: 16px;
|
| 22 |
-
border: 1px solid lightgray;
|
| 23 |
-
border-radius: 16px;
|
| 24 |
}
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
|
|
|
|
|
|
| 28 |
}
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* Custom Styles & Utilities */
|
| 2 |
+
|
| 3 |
+
:root {
|
| 4 |
+
--glass-bg: rgba(15, 23, 42, 0.6);
|
| 5 |
+
--glass-border: rgba(51, 65, 85, 0.5);
|
| 6 |
+
--neon-green: #22c55e;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
body {
|
| 10 |
+
font-family: 'Inter', sans-serif;
|
| 11 |
+
background-color: #020617; /* Slate 950 */
|
| 12 |
+
color: #cbd5e1; /* Slate 300 */
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
/* Scrollbar Styling */
|
| 16 |
+
::-webkit-scrollbar {
|
| 17 |
+
width: 8px;
|
| 18 |
+
height: 8px;
|
| 19 |
+
}
|
| 20 |
+
::-webkit-scrollbar-track {
|
| 21 |
+
background: #0f172a;
|
| 22 |
+
}
|
| 23 |
+
::-webkit-scrollbar-thumb {
|
| 24 |
+
background: #334155;
|
| 25 |
+
border-radius: 4px;
|
| 26 |
+
}
|
| 27 |
+
::-webkit-scrollbar-thumb:hover {
|
| 28 |
+
background: #475569;
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
/* Animations */
|
| 32 |
+
@keyframes fadeIn {
|
| 33 |
+
from { opacity: 0; transform: translateY(10px); }
|
| 34 |
+
to { opacity: 1; transform: translateY(0); }
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
.fade-in {
|
| 38 |
+
animation: fadeIn 0.4s ease-out forwards;
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
@keyframes scanline {
|
| 42 |
+
0% { transform: translateY(-100%); }
|
| 43 |
+
100% { transform: translateY(100%); }
|
| 44 |
}
|
| 45 |
|
| 46 |
+
.scanline::before {
|
| 47 |
+
content: " ";
|
| 48 |
+
display: block;
|
| 49 |
+
position: absolute;
|
| 50 |
+
top: 0;
|
| 51 |
+
left: 0;
|
| 52 |
+
bottom: 0;
|
| 53 |
+
right: 0;
|
| 54 |
+
background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%), linear-gradient(90deg, rgba(255, 0, 0, 0.06), rgba(0, 255, 0, 0.02), rgba(0, 0, 255, 0.06));
|
| 55 |
+
z-index: 2;
|
| 56 |
+
background-size: 100% 2px, 3px 100%;
|
| 57 |
+
pointer-events: none;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
/* Glassmorphism Utilities */
|
| 61 |
+
.glass-panel {
|
| 62 |
+
background: var(--glass-bg);
|
| 63 |
+
backdrop-filter: blur(12px);
|
| 64 |
+
-webkit-backdrop-filter: blur(12px);
|
| 65 |
+
border: 1px solid var(--glass-border);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/* Typography Enhancements */
|
| 69 |
+
.font-tech {
|
| 70 |
+
letter-spacing: 0.05em;
|
| 71 |
+
text-transform: uppercase;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/* Custom Button Effects */
|
| 75 |
+
.btn-glitch {
|
| 76 |
+
position: relative;
|
| 77 |
+
overflow: hidden;
|
| 78 |
+
}
|
| 79 |
+
.btn-glitch::after {
|
| 80 |
+
content: '';
|
| 81 |
+
position: absolute;
|
| 82 |
+
top: 0;
|
| 83 |
+
left: -100%;
|
| 84 |
+
width: 100%;
|
| 85 |
+
height: 100%;
|
| 86 |
+
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
|
| 87 |
+
transition: 0.5s;
|
| 88 |
+
}
|
| 89 |
+
.btn-glitch:hover::after {
|
| 90 |
+
left: 100%;
|
| 91 |
}
|
| 92 |
|
| 93 |
+
/* Loader */
|
| 94 |
+
.typing-cursor::after {
|
| 95 |
+
content: '▋';
|
| 96 |
+
animation: blink 1s step-start infinite;
|
| 97 |
+
color: var(--neon-green);
|
| 98 |
}
|
| 99 |
|
| 100 |
+
@keyframes blink {
|
| 101 |
+
50% { opacity: 0; }
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
}
|
| 103 |
|
| 104 |
+
/* Toast Notification */
|
| 105 |
+
.toast {
|
| 106 |
+
transform: translateX(100%);
|
| 107 |
+
animation: slideIn 0.3s forwards;
|
| 108 |
}
|
| 109 |
+
@keyframes slideIn {
|
| 110 |
+
to { transform: translateX(0); }
|
| 111 |
+
}
|