157 lines
4.7 KiB
HTML
157 lines
4.7 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Task {{ task.id }} · Apex Console{% endblock %}
|
|
{% block body %}
|
|
<header class="topbar">
|
|
<div>
|
|
<h1>Task Detail</h1>
|
|
<p><a href="{{ url_for('runtime_console_tasks') }}">← Back to tasks</a></p>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="panel timeline-panel">
|
|
<div class="section-title">
|
|
<h2>{{ task.goal }}</h2>
|
|
<span class="status-pill {{ task.status }}">{{ task.status }}</span>
|
|
</div>
|
|
|
|
<dl class="detail-grid">
|
|
<div>
|
|
<dt>Task ID</dt>
|
|
<dd>{{ task.id }}</dd>
|
|
</div>
|
|
<div>
|
|
<dt>Device</dt>
|
|
<dd>{{ device_name }}</dd>
|
|
</div>
|
|
<div>
|
|
<dt>Created</dt>
|
|
<dd>{{ task.created_at or "-" }}</dd>
|
|
</div>
|
|
<div>
|
|
<dt>Updated</dt>
|
|
<dd>{{ task.updated_at or "-" }}</dd>
|
|
</div>
|
|
{% if task.failure_reason %}
|
|
<div>
|
|
<dt>Failure</dt>
|
|
<dd>{{ task.failure_reason }}</dd>
|
|
</div>
|
|
{% endif %}
|
|
</dl>
|
|
|
|
<div class="timeline-controls">
|
|
<span>Step {{ current_step_index }} of {{ timeline|length }}</span>
|
|
</div>
|
|
|
|
{% if not timeline %}
|
|
<div class="empty-state compact">
|
|
<span>No timeline records captured.</span>
|
|
</div>
|
|
{% else %}
|
|
<form class="timeline-controls" method="get" action="{{ url_for('runtime_console_task_detail', task_id=task.id) }}">
|
|
<label>
|
|
Step
|
|
<select name="step" onchange="this.form.submit()">
|
|
{% for record in timeline %}
|
|
<option
|
|
value="{{ loop.index0 }}"
|
|
{% if loop.index0 == current_step_index %}selected{% endif %}
|
|
>Step {{ loop.index }}{% if record.tool_call and record.tool_call.get('action') %} ({{ record.tool_call.get('action') }}){% endif %}</option>
|
|
{% endfor %}
|
|
</select>
|
|
</label>
|
|
<noscript><button class="icon-text-button" type="submit"><span>Show</span></button></noscript>
|
|
</form>
|
|
|
|
<div class="timeline-stage">
|
|
<div class="evidence-grid">
|
|
<section class="evidence-pane">
|
|
<h3>Before action</h3>
|
|
<div class="screenshot-frame">
|
|
{% if current_step.before_image_base64 %}
|
|
<img
|
|
src="data:image/png;base64,{{ current_step.before_image_base64 }}"
|
|
alt="Screenshot captured before the action"
|
|
/>
|
|
{% else %}
|
|
<span>No screenshot</span>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
<section class="evidence-pane">
|
|
<h3>After action</h3>
|
|
<div class="screenshot-frame">
|
|
{% if current_step.after_image_base64 %}
|
|
<img
|
|
src="data:image/png;base64,{{ current_step.after_image_base64 }}"
|
|
alt="Screenshot captured after the action"
|
|
/>
|
|
{% else %}
|
|
<span>No screenshot</span>
|
|
{% endif %}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
<div class="step-data">
|
|
<div>
|
|
<h3>Operation</h3>
|
|
<dl class="operation-grid">
|
|
<div>
|
|
<dt>Action</dt>
|
|
<dd>{{ current_step.tool_call.get('action', '-') }}</dd>
|
|
</div>
|
|
<div>
|
|
<dt>Description</dt>
|
|
<dd>{{ current_step.tool_call.get('description', '-') }}</dd>
|
|
</div>
|
|
</dl>
|
|
<pre>{{ current_step.tool_call | tojson(indent=2) }}</pre>
|
|
</div>
|
|
<div>
|
|
<h3>Execution result</h3>
|
|
<pre>{{ current_step.result | tojson(indent=2) }}</pre>
|
|
</div>
|
|
<div>
|
|
<h3>OCR results</h3>
|
|
{% if ocr_results %}
|
|
<ul class="ocr-results">
|
|
{% for ocr in ocr_results %}
|
|
<li>
|
|
<strong>{{ ocr.get('text') or '-' }}</strong>
|
|
<span>{{ ocr.get('bounds') | tojson }}</span>
|
|
{% if ocr.get('confidence') is not none %}
|
|
<span>confidence {{ '%.3f' | format(ocr.get('confidence')) }}</span>
|
|
{% endif %}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
{% else %}
|
|
<p class="muted">No OCR output captured for this step.</p>
|
|
{% endif %}
|
|
</div>
|
|
{% if ui_tree_nodes %}
|
|
<div>
|
|
<h3>UI tree</h3>
|
|
<details class="ui-tree" open>
|
|
<summary>{{ ui_tree_nodes|length }} normalized nodes</summary>
|
|
<ul class="ui-tree-nodes">
|
|
{% for node in ui_tree_nodes %}
|
|
<li>
|
|
<strong>{{ node.get('type') or 'unknown' }}</strong>
|
|
<span>{{ node.get('text') or node.get('id') or '-' }}</span>
|
|
<code>{{ node.get('bounds') | tojson }}</code>
|
|
{% if node.get('confidence') is not none %}
|
|
<span>confidence {{ '%.3f' | format(node.get('confidence')) }}</span>
|
|
{% endif %}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</details>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</section>
|
|
{% endblock %}
|