Compare commits
No commits in common. "33e2434e714617608858ea432a0d36239e057a6e" and "b4ccf03b8eb72313bcff42e6c2b4be3328b60610" have entirely different histories.
33e2434e71
...
b4ccf03b8e
96
README.md
96
README.md
@ -19,13 +19,17 @@ Aucune génération de code direct n'est effectuée : seules des **explications,
|
|||||||
|
|
||||||
## Objectif
|
## Objectif
|
||||||
|
|
||||||
Reproduire une **table ronde d'IA spécialistes**, avec un nombre d'agents flexible et configurable :
|
Reproduire une **table ronde d'IA spécialistes**, chaque agent IA représentant un rôle spécifique :
|
||||||
|
|
||||||
- **Agents dynamiques** (3-50) avec des noms aléatoires pour la diversité
|
- Lead Architect (Architecte logiciel)
|
||||||
- Chaque agent à une perspective unique sur l'architecture logicielle
|
- Backend Engineer (Développeur backend)
|
||||||
- Les agents se spécialisent naturellement dans différents domaines
|
- Frontend Engineer (Développeur frontend)
|
||||||
|
- UI Designer (Designer UI/UX)
|
||||||
|
- DevOps Engineer (Ingénieur DevOps)
|
||||||
|
- Product Manager (Chef de projet)
|
||||||
|
- Security Specialist (Spécialiste sécurité)
|
||||||
|
|
||||||
L'aspect le plus crucial de ces IA réside dans leur capacité à **collaborer itérativement** pour améliorer collectivement la spécification architecturale. Le processus s'arrête naturellement quand **tous les agents convergent** (plus aucune amélioration n'est proposée sur N tours consécutifs).
|
L'aspect le plus crucial de ces IA réside dans leur capacité à **collaborer itérativement** pour améliorer collectivement la spécification architecturale jusqu'à convergence naturelle (quand plus aucune amélioration n'est proposée).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -53,10 +57,9 @@ Elles discutent pour converger vers un consensus, puis produisent une **fiche co
|
|||||||
|
|
||||||
- **Frontend**: Vue.js 3 + Vite
|
- **Frontend**: Vue.js 3 + Vite
|
||||||
- **Backend**: Node.js + Express
|
- **Backend**: Node.js + Express
|
||||||
- **AI Layer**: Multi-agent système avec Mistral API (mistral-large-2411)
|
- **AI Layer**: Multi-agent système avec Mistral API
|
||||||
- **Storage**: SQLite pour persistance complète
|
- **Storage**: SQLite + JSON backup
|
||||||
- **Real-time**: WebSocket pour streaming temps réel des modifications
|
- **Diagrammes**: Mermaid.js
|
||||||
- **Design**: Glassmorphism avec animations fluides
|
|
||||||
|
|
||||||
### Architecture globale
|
### Architecture globale
|
||||||
|
|
||||||
@ -76,36 +79,28 @@ graph TD
|
|||||||
|
|
||||||
### Conception Collaborative par IA
|
### Conception Collaborative par IA
|
||||||
|
|
||||||
- **N agents dynamiques** (3-50) : Nombre configurable pour chaque session
|
- **7 agents spécialisés** : Architecte, Backend Engineer, Frontend Engineer, UI Designer, DevOps Engineer, Product Manager, Security Specialist
|
||||||
- **Noms aléatoires** : Chaque agent a un nom unique pour meilleure clarté
|
- **Création itérative** : Lead Architect crée le document initial
|
||||||
- **Création itérative** : SYSTEM crée la structure de base
|
- **Tours de table** : Chaque agent revoit et propose des améliorations
|
||||||
- **Tours de table automatiques** : Chaque agent revoit et peut modifier une section
|
|
||||||
- **Document évolutif** : Amélioration progressive à chaque passage
|
- **Document évolutif** : Amélioration progressive à chaque passage
|
||||||
- **Opérations supportées** : Modification, création, ou suppression de sections
|
- **Convergence naturelle** : Arrêt automatique quand tous les agents sont satisfaits
|
||||||
- **Convergence naturelle** : Arrêt automatique quand N agents de suite ne proposent aucun changement
|
- **Traçabilité complète** : Historique de toutes les modifications
|
||||||
- **Traçabilité complète** : Historique de toutes les modifications avec agent, raison, et round
|
|
||||||
- **Format Markdown** : Output toujours en Markdown (.md)
|
- **Format Markdown** : Output toujours en Markdown (.md)
|
||||||
- **Context files** : Support d'upload de fichiers MD/TXT en entrée (optionnel)
|
- **Context files** : Support d'upload de fichiers MD/TXT en entrée (optionnel)
|
||||||
- **Session discovery** : Accès facile aux sessions précédentes avec filtres et stats
|
- **Network monitoring** : Indicateur temps réel de latence et qualité réseau
|
||||||
- **Real-time visualization** : Observation des changements en direct via WebSocket
|
|
||||||
|
|
||||||
### Système multi-agents IA
|
### Système multi-agents IA
|
||||||
- **Intégration Mistral AI** (mistral-large-2411) pour génération intelligente et contextualisée
|
- **Intégration Mistral AI** pour génération de réponses intelligentes et contextuelles
|
||||||
- **System prompts intelligents** : Prompt générique paramétré par agent
|
- **System prompts spécialisés** pour chaque rôle d'agent
|
||||||
- **Contexte intelligent** : Les agents voient le document actuel et sa version antérieure
|
- **Contexte intelligent** : Les agents voient l'historique et le document actuel
|
||||||
- **Section-based editing** : Les agents modifient une section à la fois pour cohérence
|
|
||||||
|
|
||||||
### Interface et visualisation
|
### Interface et visualisation
|
||||||
- **Saisie de prompt** décrivant le projet souhaité
|
- **Saisie de prompt** décrivant le projet souhaité
|
||||||
- **Upload optionnel** de fichiers contexte (MD/TXT)
|
- **Upload optionnel** de fichiers contexte (MD/TXT)
|
||||||
- **Sélection dynamique** du nombre d'agents (3-50)
|
|
||||||
- **Visualisation en temps réel** des modifications via WebSocket
|
- **Visualisation en temps réel** des modifications via WebSocket
|
||||||
- **Timeline interactive** montrant chaque round et les agents ayant participé
|
- **Timeline d'évolution** du document avec historique complet
|
||||||
- **Raised hand animation** montrant l'agent actuel qui réfléchit/modifie
|
- **Affichage interactif** du document Markdown final
|
||||||
- **Affichage interactif** du document Markdown avec syntaxe colorée
|
- **Indicateurs de progression** et statuts du session
|
||||||
- **Session discovery** avec stats (complétées, en cours, total)
|
|
||||||
- **Filtres par statut** pour accéder facilement aux sessions
|
|
||||||
- **Historique des sessions** accessible depuis la page d'accueil
|
|
||||||
|
|
||||||
### Architecture et stockage
|
### Architecture et stockage
|
||||||
- **API REST** pour gestion des sessions collaboratives
|
- **API REST** pour gestion des sessions collaboratives
|
||||||
@ -170,28 +165,15 @@ sequenceDiagram
|
|||||||
|
|
||||||
### Étapes
|
### Étapes
|
||||||
|
|
||||||
1. **Accédez à la page d'accueil** : Consultez les sessions précédentes
|
1. **Décrire le projet** : Entrez une description détaillée
|
||||||
- Visualisez les stats (complétées, en cours, total)
|
2. **Ajouter un contexte** (optionnel) : Upload un fichier MD ou TXT
|
||||||
- Filtrez par statut si nécessaire
|
3. **Sélectionner** : Nombre d'agents (3, 5, ou 7)
|
||||||
- Cliquez sur une session pour la reprendre
|
4. **Lancer** : Start Design Session
|
||||||
2. **Ou créez une nouvelle session** :
|
5. **Suivre** :
|
||||||
- Décrivez le projet en détail
|
|
||||||
- Ajouter un contexte (optionnel) : Upload un fichier MD ou TXT
|
|
||||||
- Sélectionnez le nombre d'agents (3-50)
|
|
||||||
- Lancez "Start Design Session"
|
|
||||||
3. **Observez la collaboration** :
|
|
||||||
- Visualisez le document en temps réel
|
- Visualisez le document en temps réel
|
||||||
- Consultez la timeline avec les rounds et les agents ayant participé
|
- Consultez la timeline
|
||||||
- Voir l'animation "main levée" pour l'agent actuel
|
- Lancez les tours avec "Next Review Round"
|
||||||
4. **Processus automatique** :
|
6. **Résultat** : Document Markdown téléchargeable
|
||||||
- Chaque agent revoit le document
|
|
||||||
- Peut modifier, créer, ou supprimer une section
|
|
||||||
- Les rounds se font automatiquement
|
|
||||||
- Le processus s'arrête quand consensus est atteint
|
|
||||||
5. **Résultat** :
|
|
||||||
- Document Markdown téléchargeable
|
|
||||||
- Historique complet des modifications
|
|
||||||
- Session reste accessible pour consultation
|
|
||||||
|
|
||||||
### Exemple
|
### Exemple
|
||||||
|
|
||||||
@ -201,12 +183,16 @@ Plateforme gestion projets temps réel, équipes distribuées,
|
|||||||
gestion tâches, communication, partage fichiers, 10K+ users
|
gestion tâches, communication, partage fichiers, 10K+ users
|
||||||
```
|
```
|
||||||
|
|
||||||
Processus (avec 7 agents aux noms aléatoires):
|
Processus:
|
||||||
- Round 1: SYSTEM crée la structure de base
|
- Round 1: Lead Architect - Architecture générale
|
||||||
- Round 2: Chaque agent revoit et propose des améliorations
|
- Round 2: Backend - APIs et base de données
|
||||||
- Round 3+: Itération jusqu'à convergence
|
- Round 3: Frontend - Structure UI
|
||||||
|
- Round 4: UI Designer - Guidelines UX
|
||||||
|
- Round 5: DevOps - Infrastructure
|
||||||
|
- Round 6: Product Manager - Besoins métier
|
||||||
|
- Round 7: Security - Mesures sécurité
|
||||||
|
|
||||||
Résultat: Spécification architecturale Markdown complète avec traçabilité des changements
|
Sortie: Spécification architecturale Markdown complète
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import dotenv from 'dotenv'
|
import dotenv from 'dotenv'
|
||||||
|
import { Readable } from 'stream'
|
||||||
|
|
||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
@ -68,6 +69,69 @@ async function callMistralAPI(messages, options = {}) {
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse streaming response
|
||||||
|
*/
|
||||||
|
async function* parseStreamResponse(reader) {
|
||||||
|
const decoder = new TextDecoder()
|
||||||
|
let buffer = ''
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read()
|
||||||
|
if (done) break
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true })
|
||||||
|
const lines = buffer.split('\n')
|
||||||
|
buffer = lines.pop() || ''
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.startsWith('data: ')) {
|
||||||
|
const data = line.slice(6)
|
||||||
|
if (data === '[DONE]') continue
|
||||||
|
try {
|
||||||
|
const json = JSON.parse(data)
|
||||||
|
if (json.choices?.[0]?.delta?.content) {
|
||||||
|
yield json.choices[0].delta.content
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Skip invalid JSON
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate agent response with streaming thoughts
|
||||||
|
*/
|
||||||
|
export async function* generateAgentResponse(agentName, prompt, currentDocument = '', onThought = null) {
|
||||||
|
const systemPrompt = getAgentPrompt(agentName)
|
||||||
|
|
||||||
|
const messages = [
|
||||||
|
{ role: 'system', content: systemPrompt },
|
||||||
|
{ role: 'user', content: `Project description: ${prompt}\n\nCurrent document:\n${currentDocument}` }
|
||||||
|
]
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await callMistralAPI(messages, { stream: true })
|
||||||
|
const reader = response.body.getReader()
|
||||||
|
let fullContent = ''
|
||||||
|
|
||||||
|
for await (const chunk of parseStreamResponse(reader)) {
|
||||||
|
fullContent += chunk
|
||||||
|
if (onThought) {
|
||||||
|
onThought(chunk)
|
||||||
|
}
|
||||||
|
yield chunk
|
||||||
|
}
|
||||||
|
|
||||||
|
return fullContent
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error generating response from ${agentName}:`, error)
|
||||||
|
return `Error: ${error.message}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate agent response (non-streaming version for simpler integration)
|
* Generate agent response (non-streaming version for simpler integration)
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -31,3 +31,7 @@ export function getRandomNames(count) {
|
|||||||
const shuffled = [...allNames].sort(() => Math.random() - 0.5)
|
const shuffled = [...allNames].sort(() => Math.random() - 0.5)
|
||||||
return shuffled.slice(0, count)
|
return shuffled.slice(0, count)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRandomName() {
|
||||||
|
return allNames[Math.floor(Math.random() * allNames.length)]
|
||||||
|
}
|
||||||
|
|||||||
@ -12,8 +12,6 @@ const agentCount = ref(7)
|
|||||||
const isCreating = ref(false)
|
const isCreating = ref(false)
|
||||||
const previousSessions = ref([])
|
const previousSessions = ref([])
|
||||||
const loadingPreviousSessions = ref(false)
|
const loadingPreviousSessions = ref(false)
|
||||||
const showAllSessions = ref(false)
|
|
||||||
const sessionStatusFilter = ref('all') // 'all', 'completed', 'ongoing', 'created'
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
loadingPreviousSessions.value = true
|
loadingPreviousSessions.value = true
|
||||||
@ -39,28 +37,6 @@ const agentOptions = computed(() => {
|
|||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
const filteredSessions = computed(() => {
|
|
||||||
let filtered = previousSessions.value
|
|
||||||
|
|
||||||
if (sessionStatusFilter.value !== 'all') {
|
|
||||||
filtered = filtered.filter(s => s.status === sessionStatusFilter.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
return filtered
|
|
||||||
})
|
|
||||||
|
|
||||||
const displayedSessions = computed(() => {
|
|
||||||
return showAllSessions.value ? filteredSessions.value : filteredSessions.value.slice(0, 6)
|
|
||||||
})
|
|
||||||
|
|
||||||
const completedCount = computed(() => {
|
|
||||||
return previousSessions.value.filter(s => s.status === 'completed').length
|
|
||||||
})
|
|
||||||
|
|
||||||
const ongoingCount = computed(() => {
|
|
||||||
return previousSessions.value.filter(s => s.status === 'ongoing').length
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleFileSelect = (event) => {
|
const handleFileSelect = (event) => {
|
||||||
const file = event.target.files?.[0]
|
const file = event.target.files?.[0]
|
||||||
if (file) {
|
if (file) {
|
||||||
@ -136,88 +112,6 @@ const removeFile = () => {
|
|||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- Quick Access Section -->
|
|
||||||
<div v-if="previousSessions.length > 0" class="quick-access-section">
|
|
||||||
<div class="stats-bar">
|
|
||||||
<div class="stat">
|
|
||||||
<span class="stat-label">Completed</span>
|
|
||||||
<span class="stat-value">{{ completedCount }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat">
|
|
||||||
<span class="stat-label">In Progress</span>
|
|
||||||
<span class="stat-value">{{ ongoingCount }}</span>
|
|
||||||
</div>
|
|
||||||
<div class="stat">
|
|
||||||
<span class="stat-label">Total</span>
|
|
||||||
<span class="stat-value">{{ previousSessions.length }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Status Filter -->
|
|
||||||
<div class="filter-tabs">
|
|
||||||
<button
|
|
||||||
@click="sessionStatusFilter = 'all'"
|
|
||||||
class="filter-btn"
|
|
||||||
:class="{ active: sessionStatusFilter === 'all' }"
|
|
||||||
>
|
|
||||||
All Sessions
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="sessionStatusFilter = 'completed'"
|
|
||||||
class="filter-btn"
|
|
||||||
:class="{ active: sessionStatusFilter === 'completed' }"
|
|
||||||
>
|
|
||||||
Completed
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
@click="sessionStatusFilter = 'ongoing'"
|
|
||||||
class="filter-btn"
|
|
||||||
:class="{ active: sessionStatusFilter === 'ongoing' }"
|
|
||||||
>
|
|
||||||
In Progress
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Recent Sessions Grid -->
|
|
||||||
<div class="recent-sessions-scroll">
|
|
||||||
<div class="sessions-carousel">
|
|
||||||
<div
|
|
||||||
v-for="session in displayedSessions"
|
|
||||||
:key="session.sessionId"
|
|
||||||
@click="handleOpenSession(session.sessionId)"
|
|
||||||
class="session-card-horizontal"
|
|
||||||
:class="{ 'status-' + session.status }"
|
|
||||||
:title="session.prompt"
|
|
||||||
>
|
|
||||||
<div class="card-left">
|
|
||||||
<div class="session-id">#{{ session.sessionId }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-main">
|
|
||||||
<p class="session-prompt-short">{{ session.prompt.substring(0, 75) }}{{ session.prompt.length > 75 ? '...' : '' }}</p>
|
|
||||||
<p class="session-meta">{{ new Date(session.createdAt).toLocaleDateString() }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-right">
|
|
||||||
<span class="status-badge">{{ session.status }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="filteredSessions.length > 6" class="view-all-container">
|
|
||||||
<button
|
|
||||||
@click="showAllSessions = !showAllSessions"
|
|
||||||
class="view-all-btn"
|
|
||||||
>
|
|
||||||
{{ showAllSessions ? 'Show Less' : `View All (${filteredSessions.length} sessions)` }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Divider -->
|
|
||||||
<div v-if="previousSessions.length > 0" class="divider">
|
|
||||||
<span>Or start a new session</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form @submit.prevent="handleCreateSession" class="form">
|
<form @submit.prevent="handleCreateSession" class="form">
|
||||||
<!-- Project Description -->
|
<!-- Project Description -->
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
@ -307,6 +201,27 @@ const removeFile = () => {
|
|||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<!-- Previous Sessions -->
|
||||||
|
<div v-if="previousSessions.length > 0" class="previous-sessions-section">
|
||||||
|
<h2>Recent Sessions</h2>
|
||||||
|
<div class="sessions-grid">
|
||||||
|
<div
|
||||||
|
v-for="session in previousSessions.slice(0, 6)"
|
||||||
|
:key="session.sessionId"
|
||||||
|
@click="handleOpenSession(session.sessionId)"
|
||||||
|
class="session-card"
|
||||||
|
:class="{ 'status-completed': session.status === 'completed' }"
|
||||||
|
>
|
||||||
|
<div class="session-header">
|
||||||
|
<span class="session-id">#{{ session.sessionId }}</span>
|
||||||
|
<span class="session-status">{{ session.status }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="session-prompt">{{ session.prompt.substring(0, 100) }}...</p>
|
||||||
|
<p class="session-date">{{ new Date(session.createdAt).toLocaleDateString() }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<p>Typical session: 2-5 rounds for complete consensus. Output format: Markdown</p>
|
<p>Typical session: 2-5 rounds for complete consensus. Output format: Markdown</p>
|
||||||
</footer>
|
</footer>
|
||||||
@ -702,225 +617,6 @@ const removeFile = () => {
|
|||||||
font-size: 0.85rem;
|
font-size: 0.85rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Quick Access Section */
|
|
||||||
.quick-access-section {
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stats-bar {
|
|
||||||
display: flex;
|
|
||||||
gap: 1.5rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
padding: 1rem 1.5rem;
|
|
||||||
background: rgba(102, 126, 234, 0.08);
|
|
||||||
border: 1px solid rgba(102, 126, 234, 0.2);
|
|
||||||
border-radius: 12px;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-label {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: rgba(255, 255, 255, 0.5);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.stat-value {
|
|
||||||
font-size: 1.8rem;
|
|
||||||
color: rgba(102, 126, 234, 0.95);
|
|
||||||
font-weight: 700;
|
|
||||||
margin-top: 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-tabs {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.75rem;
|
|
||||||
margin-bottom: 1.5rem;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-btn {
|
|
||||||
padding: 0.6rem 1.2rem;
|
|
||||||
background: rgba(255, 255, 255, 0.05);
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
color: rgba(255, 255, 255, 0.6);
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
backdrop-filter: blur(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-btn:hover {
|
|
||||||
background: rgba(102, 126, 234, 0.2);
|
|
||||||
border-color: rgba(102, 126, 234, 0.4);
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter-btn.active {
|
|
||||||
background: rgba(102, 126, 234, 0.3);
|
|
||||||
border-color: rgba(102, 126, 234, 0.6);
|
|
||||||
color: rgba(255, 255, 255, 0.95);
|
|
||||||
box-shadow: 0 0 15px rgba(102, 126, 234, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.recent-sessions-scroll {
|
|
||||||
overflow-x: auto;
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding-bottom: 0.5rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sessions-carousel {
|
|
||||||
display: flex;
|
|
||||||
gap: 1rem;
|
|
||||||
min-width: min-content;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-card-horizontal {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 1rem;
|
|
||||||
min-width: 350px;
|
|
||||||
padding: 1rem;
|
|
||||||
background: rgba(102, 126, 234, 0.1);
|
|
||||||
border: 1px solid rgba(102, 126, 234, 0.3);
|
|
||||||
border-radius: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-card-horizontal:hover {
|
|
||||||
background: rgba(102, 126, 234, 0.15);
|
|
||||||
border-color: rgba(102, 126, 234, 0.5);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-card-horizontal.status-completed {
|
|
||||||
border-color: rgba(76, 175, 80, 0.3);
|
|
||||||
background: rgba(76, 175, 80, 0.05);
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-card-horizontal.status-completed:hover {
|
|
||||||
background: rgba(76, 175, 80, 0.12);
|
|
||||||
border-color: rgba(76, 175, 80, 0.5);
|
|
||||||
box-shadow: 0 8px 20px rgba(76, 175, 80, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-left {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-main {
|
|
||||||
flex: 1;
|
|
||||||
min-width: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-prompt-short {
|
|
||||||
margin: 0;
|
|
||||||
color: rgba(255, 255, 255, 0.8);
|
|
||||||
font-size: 0.95rem;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-meta {
|
|
||||||
margin: 0.25rem 0 0 0;
|
|
||||||
color: rgba(255, 255, 255, 0.4);
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-right {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status-badge {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 0.4rem 0.8rem;
|
|
||||||
background: rgba(76, 175, 80, 0.2);
|
|
||||||
color: rgba(76, 175, 80, 0.9);
|
|
||||||
border-radius: 20px;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: capitalize;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-card-horizontal.status-ongoing .status-badge {
|
|
||||||
background: rgba(102, 126, 234, 0.2);
|
|
||||||
color: rgba(102, 126, 234, 0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-all-container {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-all-btn {
|
|
||||||
padding: 0.7rem 1.5rem;
|
|
||||||
background: rgba(102, 126, 234, 0.15);
|
|
||||||
border: 1px solid rgba(102, 126, 234, 0.3);
|
|
||||||
color: rgba(102, 126, 234, 0.9);
|
|
||||||
border-radius: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
backdrop-filter: blur(5px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.view-all-btn:hover {
|
|
||||||
background: rgba(102, 126, 234, 0.25);
|
|
||||||
border-color: rgba(102, 126, 234, 0.5);
|
|
||||||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.15);
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider {
|
|
||||||
text-align: center;
|
|
||||||
margin: 2rem 0;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider span {
|
|
||||||
background: linear-gradient(
|
|
||||||
-45deg,
|
|
||||||
#0f0c29 0%,
|
|
||||||
#302b63 25%,
|
|
||||||
#24243e 50%,
|
|
||||||
#302b63 75%,
|
|
||||||
#0f0c29 100%
|
|
||||||
);
|
|
||||||
padding: 0 1rem;
|
|
||||||
color: rgba(255, 255, 255, 0.4);
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.divider::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
height: 1px;
|
|
||||||
background: linear-gradient(to right, transparent, rgba(102, 126, 234, 0.2), transparent);
|
|
||||||
z-index: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.collaborative-input {
|
.collaborative-input {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ export const useCollaborationStore = defineStore('collaboration', () => {
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const error = ref(null)
|
const error = ref(null)
|
||||||
const currentDocument = ref('')
|
const currentDocument = ref('')
|
||||||
|
const documentVersions = ref([])
|
||||||
const currentRound = ref(0)
|
const currentRound = ref(0)
|
||||||
const conversationHistory = ref([])
|
const conversationHistory = ref([])
|
||||||
|
|
||||||
@ -137,6 +138,23 @@ export const useCollaborationStore = defineStore('collaboration', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update document from WebSocket message
|
||||||
|
*/
|
||||||
|
function updateDocumentFromMessage(message) {
|
||||||
|
if (message.type === 'initial_document_created') {
|
||||||
|
currentDocument.value = message.content
|
||||||
|
currentRound.value = 1
|
||||||
|
} else if (message.type === 'document_modified') {
|
||||||
|
currentDocument.value = message.content
|
||||||
|
} else if (message.type === 'round_complete') {
|
||||||
|
conversationHistory.value.push({
|
||||||
|
roundNumber: message.roundNumber,
|
||||||
|
agentsMadeChanges: message.agentsMadeChanges
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Complete a session
|
* Complete a session
|
||||||
*/
|
*/
|
||||||
@ -166,6 +184,7 @@ export const useCollaborationStore = defineStore('collaboration', () => {
|
|||||||
function clearCurrentSession() {
|
function clearCurrentSession() {
|
||||||
currentSession.value = null
|
currentSession.value = null
|
||||||
currentDocument.value = ''
|
currentDocument.value = ''
|
||||||
|
documentVersions.value = []
|
||||||
currentRound.value = 0
|
currentRound.value = 0
|
||||||
conversationHistory.value = []
|
conversationHistory.value = []
|
||||||
}
|
}
|
||||||
@ -194,12 +213,14 @@ export const useCollaborationStore = defineStore('collaboration', () => {
|
|||||||
loading,
|
loading,
|
||||||
error,
|
error,
|
||||||
currentDocument,
|
currentDocument,
|
||||||
|
documentVersions,
|
||||||
currentRound,
|
currentRound,
|
||||||
conversationHistory,
|
conversationHistory,
|
||||||
createSession,
|
createSession,
|
||||||
startSession,
|
startSession,
|
||||||
runRound,
|
runRound,
|
||||||
getSession,
|
getSession,
|
||||||
|
updateDocumentFromMessage,
|
||||||
completeSession,
|
completeSession,
|
||||||
clearCurrentSession,
|
clearCurrentSession,
|
||||||
downloadDocument
|
downloadDocument
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user