forked from Rockachopa/Timmy-time-dashboard
Major:
- Extract all inline <style> blocks from 22 Jinja2 templates into
static/css/mission-control.css — single cacheable stylesheet
- Add tox lint check that fails on inline <style> in templates
Minor:
1. Connection status indicator in topbar (green/amber/red dot) reflecting
WebSocket + Ollama reachability, with auto-reconnect
2. Jinja2 {% macro panel(title) %} in macros.html — eliminates repeated
.card.mc-panel markup; index.html converted as example
3. SVG favicon (purple T + orange dot)
4. 30-second TTL cache on _check_ollama() to avoid blocking the event loop
on every health poll (asyncio.to_thread was already in place)
5. Toast notification system (McToast.show) for transient status messages —
wired into connection status for Ollama/WebSocket state changes
Enforcement:
- CLAUDE.md updated with conventions 11-14 (no inline CSS, use panel macro,
use toasts, never block the event loop)
- tox lint + pre-push environments now fail on inline <style> blocks
https://claude.ai/code/session_014FQ785MQdyJQ4BAXrRSo9w
Co-authored-by: Claude <noreply@anthropic.com>
99 lines
4.0 KiB
HTML
99 lines
4.0 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ page_title }}{% endblock %}
|
|
|
|
{% block extra_styles %}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="market-container py-3">
|
|
|
|
<div class="market-header">
|
|
<div class="market-title">AGENT MARKETPLACE</div>
|
|
<div class="market-subtitle">Hire agents with Bitcoin. Lowest bid wins.</div>
|
|
<div class="market-stats">
|
|
<span class="up">{{ active_count }}</span> active ·
|
|
<span>{{ planned_count }}</span> planned
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mc-panel">
|
|
<div class="card-header mc-panel-header">// AVAILABLE AGENTS</div>
|
|
<div class="card-body">
|
|
{% if agents %}
|
|
{% for agent in agents %}
|
|
<div class="market-agent">
|
|
<div class="agent-avatar">{{ agent.name[0] }}</div>
|
|
<div class="agent-info" style="flex:1; min-width:0;">
|
|
<div class="agent-name">
|
|
{{ agent.name | e }}
|
|
<span style="font-size:0.7rem; font-weight:400; color:var(--text-dim); margin-left:6px;">{{ agent.role | e }}</span>
|
|
</div>
|
|
<div class="agent-meta">{{ (agent.description or 'No description') | e }}</div>
|
|
<div class="agent-meta" style="margin-top:4px; display:flex; gap:4px; flex-wrap:wrap;">
|
|
<span class="badge badge-{{ 'success' if agent.status == 'idle' else 'warning' if agent.status == 'busy' else 'danger' if agent.status == 'offline' else 'secondary' }}">
|
|
{{ agent.status }}
|
|
</span>
|
|
{% if agent.capabilities %}
|
|
{% for cap in agent.capabilities.split(',') %}
|
|
<span class="badge badge-secondary">{{ cap.strip() | e }}</span>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
<div class="market-agent-price">
|
|
<div class="price-amount">{% if agent.rate_sats == 0 %}FREE{% else %}{{ agent.rate_sats }} sats{% endif %}</div>
|
|
<div class="price-label">min bid</div>
|
|
<div class="price-stat">{{ agent.tasks_completed }} tasks won</div>
|
|
<div class="price-stat"><span class="earned">{{ agent.total_earned }} sats</span> earned</div>
|
|
<a href="/tasks?assign={{ agent.name | urlencode }}"
|
|
class="btn btn-sm"
|
|
style="margin-top:8px; background:var(--purple); color:#fff; border:none; border-radius:var(--radius-sm); padding:6px 16px; font-size:0.75rem; font-weight:600; letter-spacing:0.05em; display:inline-block; text-decoration:none;">
|
|
HIRE
|
|
</a>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div style="text-align:center; padding:30px; color:var(--text-dim); font-size:12px; letter-spacing:0.08em;">
|
|
NO AGENTS IN THE MARKETPLACE YET
|
|
<div style="margin-top:12px;"><a href="/" class="btn btn-primary btn-sm">Launch Your First Agent</a></div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mc-panel">
|
|
<div class="card-header mc-panel-header">// HOW IT WORKS</div>
|
|
<div class="card-body">
|
|
<div class="grid grid-3">
|
|
<div class="how-step">
|
|
<div class="how-step-num">1</div>
|
|
<h3>Post a Task</h3>
|
|
<p>Describe what you need done at <a href="/swarm/live">/swarm/live</a></p>
|
|
</div>
|
|
<div class="how-step">
|
|
<div class="how-step-num">2</div>
|
|
<h3>Agents Bid</h3>
|
|
<p>15-second auction — lowest bid wins</p>
|
|
</div>
|
|
<div class="how-step">
|
|
<div class="how-step-num">3</div>
|
|
<h3>Pay in Sats</h3>
|
|
<p>Lightning payment to winning agent</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card mc-panel">
|
|
<div class="card-header mc-panel-header">// SPAWN A PERSONA</div>
|
|
<div class="card-body">
|
|
<p style="color:var(--text-dim); font-size:0.85rem;">
|
|
Add a built-in persona agent to the live swarm from the <a href="/swarm/live">Swarm Live</a> dashboard.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
{% endblock %}
|