Complete L1 router and template migration
- Full implementation of runs, recipes, cache routers with templates - Auth and storage routers fully migrated - Jinja2 templates for all L1 pages - Service layer for auth and storage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
48
app/templates/runs/_run_card.html
Normal file
48
app/templates/runs/_run_card.html
Normal file
@@ -0,0 +1,48 @@
|
||||
{# Run card partial - expects 'run' variable #}
|
||||
{% set status_colors = {
|
||||
'completed': 'green',
|
||||
'running': 'blue',
|
||||
'pending': 'yellow',
|
||||
'failed': 'red',
|
||||
'cached': 'purple'
|
||||
} %}
|
||||
{% set color = status_colors.get(run.status, 'gray') %}
|
||||
|
||||
<a href="/run/{{ run.run_id }}"
|
||||
class="block bg-gray-800 border border-gray-700 rounded-lg p-4 hover:border-gray-600 transition-colors">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="font-mono text-sm text-gray-400">{{ run.run_id[:12] }}...</span>
|
||||
<span class="bg-{{ color }}-900 text-{{ color }}-300 px-2 py-0.5 rounded text-xs uppercase">
|
||||
{{ run.status }}
|
||||
</span>
|
||||
{% if run.cached %}
|
||||
<span class="bg-purple-900 text-purple-300 px-2 py-0.5 rounded text-xs">cached</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<span class="text-gray-500 text-sm">{{ run.created_at }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="flex items-center space-x-4 text-sm">
|
||||
<span class="text-gray-400">
|
||||
Recipe: <span class="text-white">{{ run.recipe or 'Unknown' }}</span>
|
||||
</span>
|
||||
{% if run.total_steps %}
|
||||
<span class="text-gray-400">
|
||||
Steps: <span class="text-white">{{ run.executed or 0 }}/{{ run.total_steps }}</span>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if run.output_hash %}
|
||||
<span class="font-mono text-xs text-gray-500">{{ run.output_hash[:16] }}...</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if run.inputs %}
|
||||
<div class="mt-2 text-xs text-gray-500">
|
||||
Inputs: {{ run.inputs | length }} file(s)
|
||||
</div>
|
||||
{% endif %}
|
||||
</a>
|
||||
219
app/templates/runs/detail.html
Normal file
219
app/templates/runs/detail.html
Normal file
@@ -0,0 +1,219 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Run {{ run.run_id[:12] }} - Art-DAG L1{% endblock %}
|
||||
|
||||
{% block head %}
|
||||
{{ super() }}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.23.0/cytoscape.min.js"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% set status_colors = {'completed': 'green', 'running': 'blue', 'pending': 'yellow', 'failed': 'red'} %}
|
||||
{% set color = status_colors.get(run.status, 'gray') %}
|
||||
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center space-x-4 mb-6">
|
||||
<a href="/runs" class="text-gray-400 hover:text-white">← Runs</a>
|
||||
<h1 class="text-2xl font-bold font-mono">{{ run.run_id[:16] }}...</h1>
|
||||
<span class="bg-{{ color }}-900 text-{{ color }}-300 px-3 py-1 rounded text-sm uppercase">
|
||||
{{ run.status }}
|
||||
</span>
|
||||
{% if run.cached %}
|
||||
<span class="bg-purple-900 text-purple-300 px-3 py-1 rounded text-sm">Cached</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Info Grid -->
|
||||
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-6">
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<div class="text-gray-500 text-sm">Recipe</div>
|
||||
<div class="text-white font-medium">{{ run.recipe or 'Unknown' }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<div class="text-gray-500 text-sm">Steps</div>
|
||||
<div class="text-white font-medium">
|
||||
{{ run.executed or 0 }} / {{ run.total_steps or '?' }}
|
||||
{% if run.cached_steps %}
|
||||
<span class="text-purple-400 text-sm">({{ run.cached_steps }} cached)</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<div class="text-gray-500 text-sm">Created</div>
|
||||
<div class="text-white font-medium">{{ run.created_at }}</div>
|
||||
</div>
|
||||
<div class="bg-gray-800 rounded-lg p-4">
|
||||
<div class="text-gray-500 text-sm">User</div>
|
||||
<div class="text-white font-medium">{{ run.username or 'Unknown' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs -->
|
||||
<div class="border-b border-gray-700 mb-6">
|
||||
<nav class="flex space-x-8">
|
||||
<a href="#plan" class="tab-link border-b-2 border-blue-500 text-white pb-3 px-1"
|
||||
onclick="showTab('plan')">Plan</a>
|
||||
<a href="#artifacts" class="tab-link border-b-2 border-transparent text-gray-400 hover:text-white pb-3 px-1"
|
||||
onclick="showTab('artifacts')">Artifacts</a>
|
||||
{% if run.analysis %}
|
||||
<a href="#analysis" class="tab-link border-b-2 border-transparent text-gray-400 hover:text-white pb-3 px-1"
|
||||
onclick="showTab('analysis')">Analysis</a>
|
||||
{% endif %}
|
||||
<a href="#inputs" class="tab-link border-b-2 border-transparent text-gray-400 hover:text-white pb-3 px-1"
|
||||
onclick="showTab('inputs')">Inputs</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<!-- Plan Tab -->
|
||||
<div id="tab-plan" class="tab-content">
|
||||
{% if plan %}
|
||||
<div id="dag-container" class="bg-gray-900 rounded-lg border border-gray-700 h-96 mb-4"></div>
|
||||
|
||||
<div class="space-y-2">
|
||||
{% for step in plan.steps %}
|
||||
{% set step_color = 'green' if step.cached else ('blue' if step.status == 'running' else 'gray') %}
|
||||
<div class="bg-gray-800 rounded p-3 flex items-center justify-between">
|
||||
<div class="flex items-center space-x-3">
|
||||
<span class="w-6 h-6 rounded-full bg-{{ step_color }}-600 flex items-center justify-center text-xs">
|
||||
{{ loop.index }}
|
||||
</span>
|
||||
<span class="font-medium">{{ step.name }}</span>
|
||||
<span class="text-gray-500 text-sm">{{ step.type }}</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-3">
|
||||
{% if step.cached %}
|
||||
<span class="text-purple-400 text-sm">cached</span>
|
||||
{% endif %}
|
||||
{% if step.cache_id %}
|
||||
<a href="/cache/{{ step.cache_id }}" class="font-mono text-xs text-gray-500 hover:text-white">
|
||||
{{ step.cache_id[:12] }}...
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500">No plan available for this run.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Artifacts Tab -->
|
||||
<div id="tab-artifacts" class="tab-content hidden">
|
||||
{% if artifacts %}
|
||||
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
||||
{% for artifact in artifacts %}
|
||||
<a href="/cache/{{ artifact.hash }}"
|
||||
class="bg-gray-800 rounded-lg p-4 hover:bg-gray-750 transition-colors">
|
||||
{% if artifact.media_type and artifact.media_type.startswith('image/') %}
|
||||
<img src="/cache/{{ artifact.hash }}/raw" alt=""
|
||||
class="w-full h-32 object-cover rounded mb-2">
|
||||
{% elif artifact.media_type and artifact.media_type.startswith('video/') %}
|
||||
<video src="/cache/{{ artifact.hash }}/raw"
|
||||
class="w-full h-32 object-cover rounded mb-2" muted></video>
|
||||
{% else %}
|
||||
<div class="w-full h-32 bg-gray-900 rounded mb-2 flex items-center justify-center text-gray-600">
|
||||
{{ artifact.media_type or 'Unknown' }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="font-mono text-xs text-gray-500 truncate">{{ artifact.hash[:16] }}...</div>
|
||||
<div class="text-sm text-gray-400">{{ artifact.step_name }}</div>
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500">No artifacts generated yet.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Analysis Tab -->
|
||||
<div id="tab-analysis" class="tab-content hidden">
|
||||
{% if run.analysis %}
|
||||
<div class="bg-gray-800 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold mb-4">Audio Analysis</h3>
|
||||
<pre class="text-sm text-gray-300 overflow-x-auto">{{ run.analysis | tojson(indent=2) }}</pre>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500">No analysis data available.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Inputs Tab -->
|
||||
<div id="tab-inputs" class="tab-content hidden">
|
||||
{% if run.inputs %}
|
||||
<div class="space-y-2">
|
||||
{% for input_hash in run.inputs %}
|
||||
<a href="/cache/{{ input_hash }}"
|
||||
class="block bg-gray-800 rounded p-3 font-mono text-sm hover:bg-gray-750">
|
||||
{{ input_hash }}
|
||||
</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-gray-500">No inputs recorded.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Output -->
|
||||
{% if run.output_hash %}
|
||||
<div class="mt-8 bg-gray-800 rounded-lg p-6">
|
||||
<h3 class="text-lg font-semibold mb-4">Output</h3>
|
||||
<div class="flex items-center justify-between">
|
||||
<a href="/cache/{{ run.output_hash }}" class="font-mono text-blue-400 hover:text-blue-300">
|
||||
{{ run.output_hash }}
|
||||
</a>
|
||||
{% if run.output_ipfs_cid %}
|
||||
<a href="https://ipfs.io/ipfs/{{ run.output_ipfs_cid }}"
|
||||
target="_blank"
|
||||
class="text-gray-400 hover:text-white text-sm">
|
||||
IPFS: {{ run.output_ipfs_cid[:16] }}...
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function showTab(name) {
|
||||
document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
|
||||
document.querySelectorAll('.tab-link').forEach(el => {
|
||||
el.classList.remove('border-blue-500', 'text-white');
|
||||
el.classList.add('border-transparent', 'text-gray-400');
|
||||
});
|
||||
document.getElementById('tab-' + name).classList.remove('hidden');
|
||||
event.target.classList.add('border-blue-500', 'text-white');
|
||||
event.target.classList.remove('border-transparent', 'text-gray-400');
|
||||
}
|
||||
|
||||
{% if plan %}
|
||||
// Initialize DAG
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const cy = cytoscape({
|
||||
container: document.getElementById('dag-container'),
|
||||
style: [
|
||||
{ selector: 'node', style: {
|
||||
'label': 'data(label)',
|
||||
'background-color': 'data(color)',
|
||||
'color': '#fff',
|
||||
'text-valign': 'center',
|
||||
'font-size': '10px',
|
||||
'width': 40,
|
||||
'height': 40
|
||||
}},
|
||||
{ selector: 'edge', style: {
|
||||
'width': 2,
|
||||
'line-color': '#4b5563',
|
||||
'target-arrow-color': '#4b5563',
|
||||
'target-arrow-shape': 'triangle',
|
||||
'curve-style': 'bezier'
|
||||
}}
|
||||
],
|
||||
elements: {{ dag_elements | tojson }},
|
||||
layout: { name: 'dagre', rankDir: 'LR', padding: 30 }
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
</script>
|
||||
{% endblock %}
|
||||
45
app/templates/runs/list.html
Normal file
45
app/templates/runs/list.html
Normal file
@@ -0,0 +1,45 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Runs - Art-DAG L1{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="max-w-6xl mx-auto">
|
||||
<div class="flex items-center justify-between mb-6">
|
||||
<h1 class="text-3xl font-bold">Execution Runs</h1>
|
||||
<a href="/recipes" class="text-gray-400 hover:text-white">Browse Recipes →</a>
|
||||
</div>
|
||||
|
||||
{% if runs %}
|
||||
<div class="space-y-4" id="runs-list">
|
||||
{% for run in runs %}
|
||||
{% include "runs/_run_card.html" %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{% if has_more %}
|
||||
<div hx-get="/runs?offset={{ offset + limit }}"
|
||||
hx-trigger="revealed"
|
||||
hx-swap="afterend"
|
||||
hx-select="#runs-list > *"
|
||||
class="h-20 flex items-center justify-center text-gray-500">
|
||||
Loading more...
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div class="bg-gray-800 border border-gray-700 rounded-lg p-12 text-center">
|
||||
<div class="text-gray-500 mb-4">
|
||||
<svg class="w-16 h-16 mx-auto mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
|
||||
d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
||||
</svg>
|
||||
<p class="text-xl">No runs yet</p>
|
||||
</div>
|
||||
<p class="text-gray-600 mb-6">Execute a recipe to see your runs here.</p>
|
||||
<a href="/recipes" class="bg-blue-600 hover:bg-blue-700 px-6 py-2 rounded font-medium">
|
||||
Browse Recipes
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user