ontoref-code/crates/ontoref-daemon/templates/pages/roadmap.html

96 lines
3.5 KiB
HTML
Raw Permalink Normal View History

{% extends "base.html" %}
{% block title %}Positioning Roadmap — Ontoref{% endblock title %}
{% block nav_group_knowledge %}active{% endblock nav_group_knowledge %}
{% block main_class %}container mx-auto px-4 py-6{% endblock main_class %}
{% block head %}
<link rel="stylesheet" href="{{ base_url }}/assets/vendor/vis-timeline.min.css" />
<style>
#roadmap-root {
width: 100%;
height: calc(100vh - 240px);
min-height: 380px;
border-radius: 0.5rem;
overflow: hidden;
border: 1px solid oklch(var(--bc) / 0.12);
}
.vis-item { border-radius: 4px; font-size: 12px; }
.vis-item.orphan { opacity: 0.45; border-style: dashed; }
.vis-label { font-size: 12px; color: oklch(var(--bc) / 0.85); }
.vis-timeline { background: oklch(var(--b1)); color: oklch(var(--bc)); }
.vis-time-axis .vis-text { color: oklch(var(--bc) / 0.6); }
.vis-panel.vis-center, .vis-panel.vis-left { border-color: oklch(var(--bc) / 0.12); }
#orphan-warn {
padding: 0.5rem 1rem; background: oklch(var(--wa) / 0.15);
border: 1px solid oklch(var(--wa) / 0.4); border-radius: 0.5rem;
font-size: 0.75rem; color: oklch(var(--wac));
}
</style>
{% endblock head %}
{% block content %}
<div class="flex items-center justify-between mb-4 gap-4">
<h1 class="text-xl font-semibold">Positioning Roadmap</h1>
<button class="btn btn-ghost btn-sm" onclick="timeline.fit()">Fit</button>
</div>
{% if orphan_count > 0 %}
<div id="orphan-warn" class="mb-3">
⚠ {{ orphan_count }} milestone(s) don't anchor any differentiator — run
<code>onre coder chronicle --as-proof-candidates</code> to generate proof candidates.
</div>
{% endif %}
<div id="roadmap-root"></div>
<div class="mt-4 grid grid-cols-2 md:grid-cols-4 gap-3 text-sm">
{% for m in mechanisms %}
<div class="card bg-base-200 p-3">
<p class="font-mono text-xs opacity-60">{{ m.id }}</p>
<p class="font-medium text-sm mt-0.5">{{ m.label | default(value=m.id) }}</p>
<p class="text-xs opacity-60 mt-1">{{ m.status | default(value="—") }}</p>
</div>
{% endfor %}
</div>
{% endblock content %}
{% block scripts %}
<script src="{{ base_url }}/assets/vendor/vis-timeline.min.js"></script>
<script>
const DATA = {{ roadmap_json | safe }};
const orphanIds = new Set((DATA.orphan_milestones || []).map(o => o.id));
const groups = (DATA.mechanisms || []).map(m => ({
id: m.id,
content: `<span class="text-xs font-mono" title="${m.label||m.id}">${m.id}</span>`,
}));
if (!groups.length) groups.push({ id: "__unassigned", content: "unassigned" });
const items = (DATA.milestones || []).map((m, i) => {
const start = m.date || m.target_date || "1970-01-01";
const grp = m.mechanism || m.refs?.[0] || "__unassigned";
const isOrphan = orphanIds.has(m.id);
return {
id: m.id || String(i),
group: grp,
start,
content: `<span style="max-width:200px;display:inline-block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${m.title}</span>`,
title: `${m.title}${isOrphan ? " ⚠ orphan" : ""}`,
className: isOrphan ? "orphan" : "",
style: isOrphan ? "background:#6b7280;border-color:#6b7280;color:#fff;" : "background:#f59e0b;border-color:#f59e0b;color:#000;",
};
});
const container = document.getElementById('roadmap-root');
const timeline = new vis.Timeline(container, new vis.DataSet(items), new vis.DataSet(groups), {
stack: true,
orientation: { axis: "top" },
selectable: false,
zoomMin: 1000 * 60 * 60 * 24 * 14,
zoomMax: 1000 * 60 * 60 * 24 * 365 * 5,
});
timeline.fit();
</script>
{% endblock scripts %}