- commit
- f117635c8e39e2631e94d1e1b0d64fe05d0de580
- parent
- 8549c93aa9be64389700b685ff45ab533d5fa1e7
- author
- dami etoile <dami.etoile@gmail.com>
| M | dashboard/ | 60 | ++++++++++++++++++++++++++++++- |
| M | dashboard/ | 14 | +++++++ |
@@ -293,6 +293,34 @@
293293 detail ? h(ChangeDetailTabs, { detail: detail }) : null294294 );295295 }296+ // ── Task parser ─────────────────────────────────────────────────────297+ function parseTasks(md) {298+ if (!md) return { sections: [], total: 0, done: 0 };299+ var lines = md.split("\n");300+ var sections = [];301+ var current = null;302+ var total = 0, done = 0;303+ for (var i = 0; i < lines.length; i++) {304+ var line = lines[i];305+ var h = line.match(/^##\s+(.+)$/);306+ if (h) {307+ current = { title: h[1].trim(), tasks: [] };308+ sections.push(current);309+ continue;310+ }311+ var t = line.match(/^[-*]\s+\[(.)\]\s+(.+)$/);312+ if (t) {313+ var checked = t[1] === "x" || t[1] === "X";314+ var text = t[2].trim();315+ if (checked) done++;316+ total++;317+ if (current) current.tasks.push({ checked: checked, text: text });318+ else { current = { title: "", tasks: [] }; sections.push(current); current.tasks.push({ checked: checked, text: text }); }319+ }320+ }321+ return { sections: sections, total: total, done: done };322+ }323+296324 function ChangeDetailTabs(_ref9) {297325 var detail = _ref9.detail;298326 var tabs = [];@@ -305,7 +333,9 @@
305333 var activeTab = _tab[0], setActiveTab = _tab[1];306334 var content = activeTab === "specs"307335 ? detail.specs.map(function (s) { return h("div", { key: s.path, className: "os-spec-block" }, h("h4", null, s.path), h(Markdown, { md: s.content })); })308- : h(Markdown, { md: detail[activeTab] });336+ : activeTab === "tasks"337+ ? h(TasksView, { md: detail[activeTab], stats: detail.taskStats })338+ : h(Markdown, { md: detail[activeTab] });309339 return h("div", { className: "os-tabs" },310340 h("div", { className: "os-tabs-bar" }, tabs.map(function (t) {311341 return h("button", {@@ -317,6 +347,34 @@
317347 );318348 }319349 350+ // ── Tasks view (structured checklist) ───────────────────────────────351+ function TasksView(_ref) {352+ var md = _ref.md, stats = _ref.stats;353+ var parsed = useMemo(function () { return parseTasks(md); }, [md]);354+ var total = stats ? stats.total : parsed.total;355+ var done = stats ? stats.done : parsed.done;356+ var pct = total > 0 ? Math.round((done / total) * 100) : 0;357+ return h("div", { className: "os-tasks" },358+ total > 0 ? h("div", { className: "os-tasks-progress" },359+ h("div", { className: "os-tasks-bar" },360+ h("div", { className: "os-tasks-bar-fill", style: { width: pct + "%" } })361+ ),362+ h("span", { className: "os-tasks-count" }, done + " / " + total + " done (" + pct + "%)")363+ ) : null,364+ parsed.sections.map(function (sec, si) {365+ return h("div", { key: si, className: "os-tasks-section" },366+ sec.title ? h("h4", { className: "os-tasks-section-title" }, sec.title) : null,367+ sec.tasks.map(function (t, ti) {368+ return h("div", { key: ti, className: cn("os-task-item", t.checked && "os-task-done") },369+ h("span", { className: "os-task-check" }, t.checked ? "\u2713" : "\u25CB"),370+ h("span", { className: "os-task-text" }, t.text)371+ );372+ })373+ );374+ })375+ );376+ }377+320378 // ── Specs view (spec browser: current / dirty / refs) ────────────────321379 var SORT_MODES = [322380 { value: "alpha", label: "A→Z" },@@ -81,6 +81,20 @@
8181 .os-tab-btn-active { color: var(--color-foreground, #eee); border-bottom-color: var(--color-primary, var(--midground, #eee)); }8282 .os-tab-panel { padding-top: 0.25rem; }8383 84+/* Tasks */85+.os-tasks { display: flex; flex-direction: column; gap: 0.75rem; }86+.os-tasks-progress { display: flex; align-items: center; gap: 0.5rem; }87+.os-tasks-bar { flex: 1; height: 0.375rem; border-radius: 0.1875rem; background: var(--color-secondary, rgba(255,255,255,0.06)); overflow: hidden; }88+.os-tasks-bar-fill { height: 100%; border-radius: 0.1875rem; background: var(--color-primary, #60a5fa); transition: width 0.2s; }89+.os-tasks-count { font-size: 0.7rem; color: var(--color-muted-foreground, #888); white-space: nowrap; }90+.os-tasks-section { display: flex; flex-direction: column; gap: 0.125rem; }91+.os-tasks-section-title { font-size: 0.82rem; font-weight: 600; color: var(--color-foreground, #eee); margin: 0; }92+.os-task-item { display: flex; align-items: flex-start; gap: 0.375rem; padding: 0.1875rem 0; font-size: 0.78rem; line-height: 1.4; }93+.os-task-check { flex-shrink: 0; width: 1rem; text-align: center; font-size: 0.72rem; color: var(--color-muted-foreground, #888); }94+.os-task-text { flex: 1; }95+.os-task-done .os-task-check { color: var(--color-primary, #60a5fa); }96+.os-task-done .os-task-text { color: var(--color-muted-foreground, #888); text-decoration: line-through; }97+8498 /* Markdown */8599 .os-md { font-size: 0.85rem; line-height: 1.6; color: var(--color-foreground, #eee); }86100 .os-md h1, .os-md h2, .os-md h3, .os-md h4 { margin: 0.75rem 0 0.375rem; font-weight: 600; color: var(--color-foreground, #eee); }