Files
skills/orchestrator/templates/page-flow.js
T

68 lines
3.2 KiB
JavaScript
Raw Normal View History

const WORK_CHIPS = {dirty: ["bi-pencil-square", "not committed", "warn"], unpushed: ["bi-cloud-upload", "unpushed", "info"], unmerged: ["bi-signpost-split", "not merged", "neutral"], task: ["bi-card-checklist", "task open", "violet"]};
const FLOW_SIDES = {pull: "pulls before the gate", push: "merges into when done"};
function workChips(reasons) {
if (!reasons || !reasons.length) {
return null;
}
return h("span", {class: "states work", role: "group", "aria-label": "Open work"}, reasons.map((reason) => {
const [name, label, tone] = Object.hasOwn(WORK_CHIPS, reason) ? WORK_CHIPS[reason] : ["bi-question-circle", reason, "neutral"];
return h("span", {class: "state", "data-tone": tone, title: `open work: ${label}`}, icon(name), label);
}));
}
function flowLine(flow, branch) {
if (!flow) {
return null;
}
if (flow.source === "invalid") {
return h("div", {class: "flow invalid", "data-flow": "invalid"}, icon("bi-exclamation-triangle"), "Merge flow invalid: pkgmgr cannot read the flow file");
}
const refs = (side) => (flow[side].length ? flow[side].map((ref) => h("code", {class: "flow-ref", title: `${FLOW_SIDES[side]}: ${ref}`}, ref)) : h("span", {class: "muted"}, "–"));
const arrow = () => h("span", {class: "flow-arrow", ...named("flows to")}, icon("bi-arrow-right"));
return h("div", {class: "flow", "data-flow": flow.source, role: "group", "aria-label": `Merge flow: ${flow.pull.join(", ") || "no source"} to ${branch || "–"} to ${flow.push.join(", ") || "no target"}`},
h("span", {class: "flow-label"}, icon("bi-sign-merge-right"), "Flow"),
h("span", {class: "flow-side"}, refs("pull")), arrow(),
h("code", {class: "flow-ref here", title: "this branch"}, branch || "–"), arrow(),
h("span", {class: "flow-side"}, refs("push")));
}
function flowDetails(flow, branch) {
if (!flow) {
return h("p", {class: "muted"}, "No merge flow set.");
}
if (flow.source === "invalid") {
return flowLine(flow, branch);
}
return [h("h3", {}, "Merge flow"), flowLine(flow, branch), meta([["Pulls before the gate", flow.pull.join(", ")], ["Merges into when done", flow.push.join(", ")], ["Source", flow.source]])];
}
function flowTarget(names, ref) {
if (names.has(ref)) {
return ref;
}
const bare = ref.includes("/") ? ref.slice(ref.indexOf("/") + 1) : "";
return names.has(bare) ? bare : null;
}
function flowEdges(repo) {
const names = new Set(repo.branches.map((entry) => entry.name));
const edges = [];
for (const branch of repo.branches) {
for (const flow of branch.worktrees.map((wt) => wt.flow).filter((found) => found && found.source !== "invalid")) {
for (const ref of flow.pull) {
const from = flowTarget(names, ref);
if (from && from !== branch.name) {
edges.push({from, into: branch.name, side: "pull", ref});
}
}
for (const ref of flow.push) {
const into = flowTarget(names, ref);
if (into && into !== branch.name) {
edges.push({from: branch.name, into, side: "push", ref});
}
}
}
}
return edges;
}