feat: refactor dashboard with enhanced wiki, agents, and usage views

- Added comprehensive wiki backend with CRUD operations (GET, POST, PUT, DELETE)
- Enhanced agents backend with workload tracking, task history, and capabilities
- Implemented usage tracking with SQLite database and statistics endpoints
- Added wiki frontend with markdown editor, page management, and search
- Enhanced agents frontend with search, filtering, task assignment, and detailed modals
- Enhanced usage frontend with charts (Chart.js), date filtering, and export functionality
- Updated styles for all new components with responsive design
- Added new API endpoints: /api/wiki/*, /api/usage/stats, /api/usage/agents, /api/usage/export
- Enhanced /api/agents with workload and task history data
- Maintained backwards compatibility with existing task management features

The refactoring includes:
- Wiki: Full CRUD operations with markdown support
- Agents: Search, workload tracking, task assignment modals
- Usage: Charts, statistics, date range filtering, CSV/JSON export
- Styles: Responsive design with dark theme support
This commit is contained in:
2026-03-03 22:48:34 -08:00
parent d4bbe42f3e
commit e05dfef5ab
8 changed files with 3440 additions and 779 deletions

File diff suppressed because it is too large Load Diff

279
public/app.js.backup Normal file
View File

@@ -0,0 +1,279 @@
// Navigation
const navLinks = document.querySelectorAll('.nav-link');
const pages = document.querySelectorAll('.page');
navLinks.forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
const targetPage = link.dataset.page;
// Update active nav link
navLinks.forEach(l => l.classList.remove('active'));
link.classList.add('active');
// Show target page
pages.forEach(page => {
page.classList.remove('active');
if (page.id === `page-${targetPage}`) {
page.classList.add('active');
}
});
// Load page data
if (targetPage === 'wiki') loadWiki();
if (targetPage === 'agents') loadAgents();
if (targetPage === 'usage') loadUsage();
});
});
// Task Dashboard
const COLUMNS = {
'Backlog': { title: '📋 Backlog', tasks: [] },
'Todo': { title: '📝 Todo', tasks: [] },
'In Progress': { title: '🔄 In Progress', tasks: [] },
'Review': { title: '👀 Review', tasks: [] },
'Done': { title: '✅ Done', tasks: [] }
};
async function loadTasks() {
const res = await fetch('/api/tasks');
const tasks = await res.json();
// Reset columns
Object.keys(COLUMNS).forEach(status => {
COLUMNS[status].tasks = [];
});
// Group tasks by status
tasks.forEach(task => {
if (COLUMNS[task.status]) {
COLUMNS[task.status].tasks.push(task);
}
});
renderBoard();
}
function renderBoard() {
const board = document.getElementById('board');
board.innerHTML = '';
Object.entries(COLUMNS).forEach(([status, column]) => {
const columnEl = document.createElement('div');
columnEl.className = 'column';
columnEl.innerHTML = `
<div class="column-header">
<h3>${column.title}</h3>
<span class="column-count">${column.tasks.length}</span>
</div>
<div class="cards"></div>
`;
const cardsEl = columnEl.querySelector('.cards');
column.tasks.forEach(task => {
const cardEl = document.createElement('div');
cardEl.className = 'card';
cardEl.innerHTML = `
<div class="card-head">
<h3 class="card-title">${escapeHtml(task.title)}</h3>
<span class="badge priority-${task.priority}">${task.priority}</span>
</div>
<p class="card-desc">${escapeHtml(task.description || '')}</p>
<p class="meta assignee">${task.assignee || 'Unassigned'}</p>
<p class="meta tags">${task.tags.map(t => `<span class="tag">${escapeHtml(t)}</span>`).join(' ')}</p>
<label>
<input type="checkbox" class="card-check" ${task.status === 'Done' ? 'checked' : ''} />
Mark Complete
</label>
`;
// Checkbox handler
const checkbox = cardEl.querySelector('.card-check');
checkbox.addEventListener('change', async () => {
await fetch(`/api/tasks/${task.id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ status: 'Done' })
});
loadTasks();
});
cardsEl.appendChild(cardEl);
});
board.appendChild(columnEl);
});
}
// Task form
document.getElementById('task-form').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const task = {
title: formData.get('title'),
description: formData.get('description'),
assignee: formData.get('assignee'),
priority: formData.get('priority'),
status: formData.get('status'),
tags: formData.get('tags').split(',').map(t => t.trim()).filter(t => t)
};
await fetch('/api/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(task)
});
e.target.reset();
loadTasks();
});
// Populate agent dropdown
async function populateAgentDropdown() {
try {
const res = await fetch('/api/agents');
const agents = await res.json();
const select = document.getElementById('assignee');
if (!select) return;
// Keep the first option ("Select agent...")
const firstOption = select.options[0];
select.innerHTML = '';
select.appendChild(firstOption);
// Add agent options
agents.forEach(agent => {
const option = document.createElement('option');
option.value = agent.name;
option.textContent = agent.name;
select.appendChild(option);
});
} catch (err) {
console.error('Failed to load agents for dropdown:', err);
}
}
// Populate dropdown on page load
document.addEventListener('DOMContentLoaded', () => {
populateAgentDropdown();
});
// Wiki
async function loadWiki() {
const res = await fetch('/api/wiki');
const pages = await res.json();
const wikiList = document.getElementById('wiki-list');
wikiList.innerHTML = '';
pages.forEach(page => {
const itemEl = document.createElement('div');
itemEl.className = 'wiki-item';
itemEl.innerHTML = `
<h4 class="wiki-title">${escapeHtml(page.filename.replace('.md', ''))}</h4>
<p class="wiki-date">${new Date(page.created).toLocaleDateString()}</p>
`;
itemEl.addEventListener('click', async () => {
// Mark active
wikiList.querySelectorAll('.wiki-item').forEach(i => i.classList.remove('active'));
itemEl.classList.add('active');
// Load content
const contentRes = await fetch(`/api/wiki/${page.filename}`);
const contentData = await contentRes.json();
const wikiContent = document.getElementById('wiki-content');
wikiContent.innerHTML = `<pre>${escapeHtml(contentData.content)}</pre>`;
});
wikiList.appendChild(itemEl);
});
}
// Agents
async function loadAgents() {
const res = await fetch('/api/agents');
const agents = await res.json();
const grid = document.getElementById('agents-grid');
grid.innerHTML = '';
agents.forEach(agent => {
const cardEl = document.createElement('div');
cardEl.className = 'agent-card';
cardEl.innerHTML = `
<div class="agent-header">
<h3 class="agent-name">${escapeHtml(agent.name)}</h3>
<span class="agent-status">${agent.status}</span>
</div>
<div class="agent-body">
<div class="agent-section">
<h4>📋 Current Task</h4>
<p class="agent-task">${agent.currentTask || 'No active task'}</p>
</div>
<div class="agent-section">
<h4>🛠️ Tools</h4>
<div class="agent-tools">
${agent.tools.map(tool => `<span class="tool-tag">${escapeHtml(tool)}</span>`).join('')}
</div>
</div>
<div class="agent-section">
<h4>📄 Workspace Files</h4>
<div class="agent-files">
${agent.files.map(file => `<span class="file-tag">${escapeHtml(file)}</span>`).join('')}
</div>
</div>
</div>
`;
grid.appendChild(cardEl);
});
}
// Usage
async function loadUsage() {
const res = await fetch('/api/usage');
const usage = await res.json();
const usageData = document.getElementById('usage-data');
usageData.innerHTML = `
<h3>📊 Provider Usage</h3>
<div class="usage-grid">
${usage.providers.map(provider => `
<div class="provider-card">
<h4>${escapeHtml(provider.name)}</h4>
<div class="model-list">
${provider.models.map(model => `
<div class="model-item">
<span class="model-name">${escapeHtml(model.name)}</span>
<span class="model-type">${escapeHtml(model.type)}</span>
<span class="model-context">${escapeHtml(model.contextWindow)}</span>
</div>
`).join('')}
</div>
</div>
`).join('')}
</div>
`;
}
// Helper function to escape HTML
function escapeHtml(text) {
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return text.replace(/[&<>"']/g, m => map[m]);
}
// Initial load
loadTasks();

View File

@@ -1,146 +1,221 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenClaw Agent Fleet Dashboard</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<nav class="navbar">
<div class="nav-brand">
<h1>🦞 OpenClaw Fleet Dashboard</h1>
</div>
<div class="nav-links">
<a href="#" class="nav-link active" data-page="dashboard">📋 Tasks</a>
<a href="#" class="nav-link" data-page="wiki">📚 Wiki</a>
<a href="#" class="nav-link" data-page="agents">🤖 Agents</a>
<a href="#" class="nav-link" data-page="usage">📊 Usage</a>
</div>
</nav>
<link rel="stylesheet" href="styles.css">
<!-- Marked.js for markdown rendering -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- Chart.js for usage charts -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body>
<div class="container">
<header>
<h1>🦞 OpenClaw Agent Fleet Dashboard</h1>
<nav>
<a href="#tasks" class="nav-link active" data-page="tasks">Tasks</a>
<a href="#wiki" class="nav-link" data-page="wiki">Wiki</a>
<a href="#agents" class="nav-link" data-page="agents">Agents</a>
<a href="#usage" class="nav-link" data-page="usage">Usage</a>
</nav>
</header>
<!-- Dashboard Page -->
<div id="page-dashboard" class="page active">
<header class="topbar">
<h2>Task Dashboard</h2>
<p>Real-time task coordination board</p>
</header>
<main>
<!-- TASKS PAGE -->
<section id="page-tasks" class="page active">
<div class="composer">
<h2>Create Task</h2>
<form id="task-form">
<input id="title" name="title" placeholder="Task title" required />
<select id="assignee" name="assignee">
<option value="">Select agent...</option>
</select>
<select id="priority" name="priority">
<option>Low</option>
<option selected>Medium</option>
<option>High</option>
<option>Critical</option>
</select>
<textarea id="description" name="description" placeholder="Task description" rows="3"></textarea>
<input id="tags" name="tags" placeholder="Tags (comma-separated)" />
<button type="submit">Create Task</button>
</form>
</div>
<section class="composer">
<h3>Create Task</h3>
<form id="task-form">
<input id="title" name="title" placeholder="Task title" required />
<select id="assignee" name="assignee">
<option value="">Select agent...</option>
</select>
<select id="priority" name="priority">
<option>Low</option>
<option selected>Medium</option>
<option>High</option>
<option>Critical</option>
</select>
<select id="status" name="status">
<option selected>Backlog</option>
<option>Todo</option>
<option>In Progress</option>
<option>Review</option>
<option>Done</option>
</select>
<input id="tags" name="tags" placeholder="tags, comma, separated" />
<textarea id="description" name="description" placeholder="Description"></textarea>
<button type="submit">Add Task</button>
</form>
</section>
<div id="board">
<div class="column" data-status="Backlog">
<div class="column-header">
<h3>📋 Backlog</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="Todo">
<div class="column-header">
<h3>📝 Todo</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="In Progress">
<div class="column-header">
<h3>🔄 In Progress</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="Review">
<div class="column-header">
<h3>👀 Review</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="Done">
<div class="column-header">
<h3>✅ Done</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
</div>
</section>
<main id="board" class="board"></main>
<!-- WIKI PAGE -->
<section id="page-wiki" class="page">
<div class="wiki-container">
<div class="wiki-sidebar">
<div class="wiki-actions">
<button id="wiki-new-btn" class="btn-primary">+ New Page</button>
</div>
<div class="wiki-search">
<input type="text" id="wiki-search" placeholder="Search wiki...">
</div>
<div id="wiki-list" class="wiki-list"></div>
</div>
<div class="wiki-main">
<div class="wiki-toolbar">
<div class="wiki-page-title" id="wiki-page-title">Select a page</div>
<div class="wiki-page-actions" id="wiki-page-actions" style="display: none;">
<button id="wiki-edit-btn" class="btn-secondary">Edit</button>
<button id="wiki-delete-btn" class="btn-danger">Delete</button>
</div>
</div>
<div id="wiki-content" class="wiki-content">
<div class="wiki-placeholder">
<p>📚 Select a wiki page from the sidebar or create a new one.</p>
</div>
</div>
<div id="wiki-editor" class="wiki-editor" style="display: none;">
<input type="text" id="wiki-edit-title" placeholder="Page title">
<textarea id="wiki-edit-content" placeholder="Markdown content..."></textarea>
<div class="editor-actions">
<button id="wiki-save-btn" class="btn-primary">Save</button>
<button id="wiki-cancel-btn" class="btn-secondary">Cancel</button>
</div>
</div>
</div>
</div>
</section>
<!-- AGENTS PAGE -->
<section id="page-agents" class="page">
<div class="agents-header">
<h2>Agent Fleet</h2>
<div class="agents-controls">
<input type="text" id="agent-search" placeholder="Search agents...">
<select id="agent-status-filter">
<option value="">All Statuses</option>
<option value="active">Active</option>
<option value="busy">Busy</option>
<option value="idle">Idle</option>
</select>
</div>
</div>
<div id="agents-grid" class="agents-grid"></div>
<!-- Agent Details Modal -->
<div id="agent-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3 id="modal-agent-name">Agent Details</h3>
<button class="modal-close" id="modal-close">&times;</button>
</div>
<div class="modal-body" id="modal-agent-body"></div>
</div>
</div>
<!-- Assign Task Modal -->
<div id="assign-modal" class="modal">
<div class="modal-content">
<div class="modal-header">
<h3>Assign Task to <span id="assign-agent-name"></span></h3>
<button class="modal-close" id="assign-modal-close">&times;</button>
</div>
<div class="modal-body">
<select id="assign-task-select">
<option value="">Select a task...</option>
</select>
<button id="confirm-assign-btn" class="btn-primary">Assign Task</button>
</div>
</div>
</div>
</section>
<!-- USAGE PAGE -->
<section id="page-usage" class="page">
<div class="usage-header">
<h2>API Usage & Statistics</h2>
<div class="usage-controls">
<div class="date-range">
<label>From:</label>
<input type="date" id="usage-from">
<label>To:</label>
<input type="date" id="usage-to">
<button id="usage-apply-filter" class="btn-secondary">Apply</button>
</div>
<div class="export-actions">
<button id="export-json" class="btn-secondary">Export JSON</button>
<button id="export-csv" class="btn-secondary">Export CSV</button>
</div>
</div>
</div>
<div class="usage-stats">
<div class="stat-card">
<h4>Total Requests</h4>
<div class="stat-value" id="stat-requests">0</div>
</div>
<div class="stat-card">
<h4>Total Tokens</h4>
<div class="stat-value" id="stat-tokens">0</div>
</div>
<div class="stat-card">
<h4>Estimated Cost</h4>
<div class="stat-value" id="stat-cost">$0.00</div>
</div>
</div>
<div class="usage-charts">
<div class="chart-container">
<h4>Usage by Provider</h4>
<canvas id="chart-provider"></canvas>
</div>
<div class="chart-container">
<h4>Usage by Agent</h4>
<canvas id="chart-agent"></canvas>
</div>
</div>
<div id="usage-data" class="usage-details">
<h3>Provider Details</h3>
<div class="usage-grid" id="provider-grid"></div>
</div>
</section>
</main>
</div>
<!-- Wiki Page -->
<div id="page-wiki" class="page">
<header class="topbar">
<h2>📚 Wiki</h2>
<p>Task documentation and implementation details</p>
</header>
<div class="wiki-container">
<div class="wiki-list" id="wiki-list"></div>
<div class="wiki-content" id="wiki-content">
<p>Select a wiki page to view documentation</p>
</div>
</div>
</div>
<!-- Agents Page -->
<div id="page-agents" class="page">
<header class="topbar">
<h2>🤖 Agents</h2>
<p>Fleet agent workspace and configuration</p>
</header>
<div class="agents-grid" id="agents-grid"></div>
</div>
<!-- Usage Page -->
<div id="page-usage" class="page">
<header class="topbar">
<h2>📊 Usage & Quotas</h2>
<p>Provider models, quotas, and limits</p>
</header>
<div class="usage-container" id="usage-container"></div>
</div>
<template id="task-template">
<article class="card">
<div class="card-head">
<h3 class="card-title"></h3>
<span class="badge priority"></span>
</div>
<p class="card-desc"></p>
<p class="meta assignee"></p>
<p class="meta tags"></p>
<label>
<input type="checkbox" class="card-check" />
Mark Complete
</label>
</article>
</template>
<template id="wiki-item-template">
<div class="wiki-item">
<h4 class="wiki-title"></h4>
<p class="wiki-date"></p>
</div>
</template>
<template id="agent-card-template">
<div class="agent-card">
<div class="agent-header">
<h3 class="agent-name"></h3>
<span class="agent-status"></span>
</div>
<div class="agent-body">
<div class="agent-section">
<h4>📋 Current Task</h4>
<p class="agent-task"></p>
</div>
<div class="agent-section">
<h4>🛠️ Tools</h4>
<div class="agent-tools"></div>
</div>
<div class="agent-section">
<h4>📄 Workspace Files</h4>
<div class="agent-files"></div>
</div>
</div>
</div>
</template>
<template id="usage-card-template">
<div class="usage-card">
<h3 class="provider-name"></h3>
<div class="provider-models"></div>
<div class="provider-quota"></div>
</div>
</template>
<script src="/app.js"></script>
</body>
<script src="app.js"></script>
</body>
</html>

97
public/index.html.backup Normal file
View File

@@ -0,0 +1,97 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OpenClaw Agent Fleet Dashboard</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="container">
<header>
<h1>🦞 OpenClaw Agent Fleet Dashboard</h1>
<nav>
<a href="#tasks" class="nav-link active" data-page="tasks">Tasks</a>
<a href="#wiki" class="nav-link" data-page="wiki">Wiki</a>
<a href="#agents" class="nav-link" data-page="agents">Agents</a>
<a href="#usage" class="nav-link" data-page="usage">Usage</a>
</nav>
</header>
<main>
<section id="page-tasks" class="page active">
<div class="composer">
<h2>Create Task</h2>
<form id="task-form">
<input id="title" name="title" placeholder="Task title" required />
<select id="assignee" name="assignee">
<option value="">Select agent...</option>
</select>
<select id="priority" name="priority">
<option>Low</option>
<option selected>Medium</option>
<option>High</option>
<option>Critical</option>
</select>
<textarea id="description" name="description" placeholder="Task description" rows="3"></textarea>
<input id="tags" name="tags" placeholder="Tags (comma-separated)" />
<button type="submit">Create Task</button>
</form>
</div>
<div id="board">
<div class="column" data-status="Backlog">
<div class="column-header">
<h3>📋 Backlog</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="Todo">
<div class="column-header">
<h3>📝 Todo</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="In Progress">
<div class="column-header">
<h3>🔄 In Progress</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="Review">
<div class="column-header">
<h3>👀 Review</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
<div class="column" data-status="Done">
<div class="column-header">
<h3>✅ Done</h3>
<span class="column-count">0</span>
</div>
<div class="cards"></div>
</div>
</div>
</section>
<section id="page-wiki" class="page">
<div id="wiki-list"></div>
<div id="wiki-content"></div>
</section>
<section id="page-agents" class="page">
<div id="agents-grid"></div>
</section>
<section id="page-usage" class="page">
<div id="usage-data"></div>
</section>
</main>
</div>
<script src="app.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

451
public/styles.css.backup Normal file
View File

@@ -0,0 +1,451 @@
:root {
--bg: #1a1a1a;
--fg: #e0e0e0;
--border: #444;
--primary: #3498db;
--secondary: #2ecc71;
--danger: #e74c3c;
--warning: #f39c12;
--dark: #121212;
--light: #f0f0f0;
--card-bg: #2a2a2a;
--card-fg: #e0e0e0;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg);
color: var(--fg);
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background-color: var(--card-bg);
padding: 20px;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
header h1 {
color: var(--primary);
margin-bottom: 15px;
}
nav {
display: flex;
gap: 15px;
}
.nav-link {
color: var(--fg);
text-decoration: none;
padding: 8px 16px;
border-radius: 6px;
transition: background-color 0.3s ease;
}
.nav-link:hover {
background-color: var(--border);
}
.nav-link.active {
background-color: var(--primary);
color: white;
}
.composer {
background-color: var(--card-bg);
padding: 20px;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.composer h2 {
color: var(--primary);
margin-bottom: 15px;
}
#task-form {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
}
#task-form input,
#task-form textarea,
#task-form select {
padding: 12px;
border: 1px solid var(--border);
border-radius: 8px;
background-color: var(--bg);
color: var(--fg);
font-size: 1rem;
}
#task-form textarea {
grid-column: span 2;
resize: vertical;
min-height: 100px;
}
#task-form button {
grid-column: span 2;
padding: 12px 24px;
background-color: var(--primary);
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
cursor: pointer;
transition: background-color 0.3s ease;
}
#task-form button:hover {
background-color: #2980b9;
}
#board {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.column {
background-color: var(--card-bg);
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
overflow: hidden;
}
.column-header {
padding: 15px;
background-color: var(--dark);
display: flex;
justify-content: space-between;
align-items: center;
}
.column-header h3 {
color: var(--fg);
}
.column-count {
background-color: var(--border);
color: var(--fg);
padding: 4px 8px;
border-radius: 4px;
font-size: 0.9rem;
}
.cards {
padding: 15px;
min-height: 200px;
}
.card {
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.card-head {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.card-title {
color: var(--card-fg);
font-size: 1.1rem;
}
.badge {
padding: 4px 8px;
border-radius: 4px;
font-size: 0.8rem;
font-weight: bold;
}
.badge.priority-Low {
background-color: var(--secondary);
color: white;
}
.badge.priority-Medium {
background-color: var(--warning);
color: white;
}
.badge.priority-High {
background-color: var(--danger);
color: white;
}
.badge.priority-Critical {
background-color: #9c27b0;
color: white;
}
.card-desc {
color: var(--fg);
margin-bottom: 10px;
}
.meta {
font-size: 0.9rem;
color: var(--border);
margin-bottom: 5px;
}
.meta.assignee {
font-weight: bold;
}
.tag {
display: inline-block;
background-color: var(--border);
color: var(--fg);
padding: 2px 6px;
border-radius: 4px;
margin-right: 5px;
margin-bottom: 5px;
}
.card-check {
margin-right: 10px;
}
/* Wiki */
#page-wiki {
background-color: var(--card-bg);
padding: 20px;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.wiki-item {
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 15px;
margin-bottom: 10px;
cursor: pointer;
transition: background-color 0.2s ease;
}
.wiki-item:hover {
background-color: var(--dark);
}
.wiki-item.active {
background-color: var(--primary);
color: white;
}
.wiki-title {
color: var(--card-fg);
margin-bottom: 5px;
}
.wiki-date {
color: var(--border);
font-size: 0.9rem;
}
#wiki-content {
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 15px;
margin-top: 20px;
white-space: pre-wrap;
color: var(--fg);
}
/* Agents */
#page-agents {
background-color: var(--card-bg);
padding: 20px;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.agent-card {
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 15px;
margin-bottom: 15px;
}
.agent-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.agent-name {
color: var(--card-fg);
font-size: 1.1rem;
}
.agent-status {
background-color: var(--secondary);
color: white;
padding: 4px 8px;
border-radius: 4px;
font-size: 0.8rem;
}
.agent-section {
margin-bottom: 15px;
}
.agent-section h4 {
color: var(--primary);
margin-bottom: 8px;
}
.agent-task {
color: var(--fg);
}
.agent-tools,
.agent-files {
display: flex;
flex-wrap: wrap;
gap: 5px;
}
.tool-tag,
.file-tag {
background-color: var(--border);
color: var(--fg);
padding: 2px 6px;
border-radius: 4px;
font-size: 0.8rem;
}
/* Usage */
#page-usage {
background-color: var(--card-bg);
padding: 20px;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.usage-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
}
.provider-card {
background-color: var(--bg);
border: 1px solid var(--border);
border-radius: 8px;
padding: 15px;
}
.provider-card h4 {
color: var(--card-fg);
margin-bottom: 10px;
}
.model-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.model-item {
display: flex;
justify-content: space-between;
background-color: var(--dark);
padding: 8px;
border-radius: 4px;
}
.model-name {
color: var(--primary);
font-weight: bold;
}
.model-type,
.model-context {
color: var(--fg);
font-size: 0.9rem;
}
/* Agent Dropdown Styling */
#task-form select {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border);
border-radius: 8px;
font-size: 1rem;
background: var(--bg);
color: var(--fg);
cursor: pointer;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
#task-form select:hover {
border-color: var(--primary);
}
#task-form select:focus {
outline: none;
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
}
#task-form select option {
padding: 0.5rem;
}
/* Responsive Design */
@media (max-width: 768px) {
#task-form {
grid-template-columns: 1fr;
}
#task-form textarea {
grid-column: span 1;
}
#task-form button {
grid-column: span 1;
}
.usage-grid {
grid-template-columns: 1fr;
}
}