1
0

ProcessGit skatītājs MI stratēģijai

- viewer/MI_strategijas_datukopa.html (+ template.html): pīlāri, visas rīcības, principi un resursi, Par
- data/processgit.viewer.json: pppa-mis-*.xml → skatītājs
- tools/build_viewer.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016679RwmHsuTFfxt26wP6rk
This commit is contained in:
2026-10-09 22:26:05 +00:00
parent 7a5448113c
commit 1870316eb5
6 changed files with 1147 additions and 0 deletions

294
viewer/template.html Normal file
View File

@@ -0,0 +1,294 @@
<!doctype html>
<html lang="lv">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MI stratēģijas datukopa</title>
<style>
:root{--bg:#f6f7f9;--panel:#fff;--ink:#16202c;--muted:#5d6b7a;--line:#dde3ea;--acc:#1f5fbf;--acc-bg:#e8f0fc;--tag:#7a4a00;--tag-bg:#fff2d6;--rec:#6b2d8a;--rec-bg:#f3e8fa;--src:#0d6b4f;--src-bg:#e3f5ee;--hl:#fff7c2;--mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]){--bg:#11161d;--panel:#18202a;--ink:#e6ebf1;--muted:#9aa8b8;--line:#2a3542;--acc:#7fb0ff;--acc-bg:#1d2b40;--tag:#ffcf7a;--tag-bg:#3a2d12;--rec:#d9a8f5;--rec-bg:#33224a;--src:#7fe0bd;--src-bg:#123328;--hl:#4a4415}}
:root[data-theme="dark"]{--bg:#11161d;--panel:#18202a;--ink:#e6ebf1;--muted:#9aa8b8;--line:#2a3542;--acc:#7fb0ff;--acc-bg:#1d2b40;--tag:#ffcf7a;--tag-bg:#3a2d12;--rec:#d9a8f5;--rec-bg:#33224a;--src:#7fe0bd;--src-bg:#123328;--hl:#4a4415}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--ink);font:15px/1.5 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
.wrap{max-width:1080px;margin:0 auto;padding:0 16px}
header{background:var(--panel);border-bottom:1px solid var(--line);padding:18px 0 0}
.meta{display:flex;flex-wrap:wrap;gap:6px 10px;align-items:center;font-size:12.5px;color:var(--muted)}
.chip{text-transform:none;letter-spacing:0;font-family:var(--mono);font-size:11.5px;background:var(--acc-bg);color:var(--acc);border-radius:6px;padding:1px 7px;white-space:nowrap}
h1{margin:6px 0 2px;font-size:26px;line-height:1.2}
.slogan{font-weight:600;color:var(--acc);letter-spacing:.02em}
.vision{color:var(--muted);max-width:860px;margin:8px 0 12px}
.tabs{display:flex;gap:2px;overflow-x:auto;scrollbar-width:none;margin-top:6px}
.tabs button{border:0;background:none;color:var(--muted);font:inherit;font-weight:600;padding:10px 14px;border-bottom:3px solid transparent;cursor:pointer;white-space:nowrap}
.tabs button.on{color:var(--ink);border-bottom-color:var(--acc)}
main.wrap{padding-top:18px;padding-bottom:40px}
.phead{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px 18px;margin-bottom:16px}
.kicker{font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.08em;color:var(--acc);margin:0}
.phead h2{margin:4px 0 6px;font-size:21px}
.phead p{margin:0}
.goal{margin:22px 0 10px;display:flex;gap:10px;align-items:baseline}
.goal .n{font-size:20px;font-weight:800;color:var(--acc)}
.goal h3{margin:0;font-size:17px}
.goal p{margin:4px 0 0;color:var(--muted)}
.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:12px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:14px 16px;display:flex;flex-direction:column;gap:7px;min-width:0;scroll-margin-top:14px;transition:background .6s}
.card.flash{background:var(--hl)}
.top{display:flex;flex-wrap:wrap;gap:6px;align-items:center}
.num{font-weight:800}
.dl{margin-left:auto;font-size:12.5px;font-weight:600;white-space:nowrap}
.dl.open{color:var(--tag)}
.tag{font-size:11.5px;font-weight:600;border-radius:999px;padding:1px 8px;background:var(--tag-bg);color:var(--tag)}
.tag.rec{background:var(--rec-bg);color:var(--rec)}
.tag.src{background:var(--src-bg);color:var(--src)}
.card h4{margin:0;font-size:15.5px;line-height:1.35}
.card p{margin:0}
.measure{font-size:13.5px;background:var(--bg);border-radius:8px;padding:7px 10px}
.measure b{display:block;font-size:11.5px;text-transform:uppercase;letter-spacing:.05em;color:var(--muted)}
.ref{font-size:13px;color:var(--muted);overflow-wrap:anywhere}
.ref b{font-weight:600}
a{color:var(--acc)}
a.act{font-weight:600;text-decoration:none;border-bottom:1px dotted}
.resp{font-size:12.5px;color:var(--muted);font-style:italic}
.tools{display:flex;flex-wrap:wrap;gap:8px;align-items:center;margin-bottom:12px}
input[type=search],select{font:inherit;padding:7px 10px;border:1px solid var(--line);border-radius:8px;background:var(--panel);color:var(--ink);min-width:0}
input[type=search]{flex:1 1 220px}
table{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--line);border-radius:12px;overflow:hidden;font-size:14px}
th,td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--line);vertical-align:top}
th{font-size:12px;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;cursor:pointer;user-select:none;white-space:nowrap}
tr:last-child td{border-bottom:0}
tbody tr{cursor:pointer}
tbody tr:hover{background:var(--bg)}
.tw{overflow-x:auto}
.box{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px 18px;margin-bottom:14px}
.box h2{margin:0 0 8px;font-size:18px}
.box h3{margin:14px 0 6px;font-size:15px}
.tiles{display:grid;grid-template-columns:repeat(auto-fill,minmax(130px,1fr));gap:10px;margin:10px 0}
.tile{background:var(--bg);border-radius:10px;padding:10px 12px}
.tile b{display:block;font-size:24px;line-height:1.1}
.tile span{font-size:12.5px;color:var(--muted)}
.prin{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px}
.prin div{background:var(--bg);border-radius:10px;padding:10px 12px}
.prin b{display:block;font-size:12.5px;color:var(--acc);text-transform:uppercase;letter-spacing:.05em}
pre{font-family:var(--mono);font-size:12px;background:var(--bg);border-radius:8px;padding:10px;overflow-x:auto;margin:6px 0}
code{font-family:var(--mono);font-size:12.5px}
details summary{cursor:pointer;font-weight:600}
.pgvbar{font-family:var(--mono);font-size:11.5px;color:var(--muted)}
.hidden{display:none!important}
.empty{padding:40px 0;text-align:center;color:var(--muted)}
@media (max-width:560px){h1{font-size:21px}.grid{grid-template-columns:1fr}.dl{margin-left:0}}
</style>
</head>
<body>
<header>
<div class="wrap">
<div class="meta" id="meta"></div>
<h1 id="title">MI stratēģijas datukopa</h1>
<div class="slogan" id="slogan"></div>
<p class="vision" id="vision"></p>
<nav class="tabs" id="tabs"></nav>
</div>
</header>
<main class="wrap" id="main"><div class="empty">Ielādē…</div></main>
<script id="embedded" type="application/xml">__XML__</script>
<script id="xsd" type="application/xml">__XSD__</script>
<script>
"use strict";
const NS = "urn:pppa:cac:mi-strategija:0.1";
const $ = id => document.getElementById(id);
const esc = s => String(s ?? "").replace(/[&<>"]/g, c => ({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c]));
const kids = (el, name) => el ? [...el.children].filter(c => c.localName === name) : [];
const kid = (el, name) => kids(el, name)[0] || null;
const tx = (el, name) => { const k = kid(el, name); return k ? k.textContent : ""; };
let S = null, tab = "p1", ctx = {};
function parse(text){
const doc = new DOMParser().parseFromString(text, "application/xml");
const r = doc.documentElement;
if (doc.querySelector("parsererror") || r.localName !== "AIStrategy") throw new Error("Nav MI stratēģijas XML (sagaidīts AIStrategy).");
const s = {id: r.getAttribute("id"), version: r.getAttribute("version"), title: tx(r, "Title"), author: tx(r, "Author"),
status: tx(r, "Status"), updated: tx(r, "Updated"), opened: tx(r, "ProposalsOpened"), url: tx(r, "SourceUrl"),
slogan: kids(kid(r, "Slogan"), "Word").map(w => w.textContent), vision: tx(r, "Vision"), pillars: [], actions: {}, goals: 0};
for (const p of kids(r, "Pillar")) {
const P = {id: p.getAttribute("id"), n: p.getAttribute("number"), name: tx(p, "Name"), short: tx(p, "ShortTitle"),
heading: tx(p, "Heading"), summary: tx(p, "Summary"), desc: tx(p, "Description"), goals: []};
for (const g of kids(p, "Goal")) {
const G = {id: g.getAttribute("id"), n: g.getAttribute("number"), title: tx(g, "Title"), desc: tx(g, "Description"), actions: []};
s.goals++;
for (const a of kids(g, "Action")) {
const dl = kid(a, "Deadline");
const A = {id: a.getAttribute("id"), n: +a.getAttribute("number"), title: tx(a, "Title"), text: tx(a, "Text"),
measure: tx(a, "Measurement"), deadline: dl ? dl.textContent : "", dv: dl ? dl.getAttribute("value") || "" : "",
tags: kids(a, "Tag").map(t => ({t: t.textContent, kind: t.getAttribute("kind") || ""})),
resp: kids(a, "Responsible").map(x => ({t: x.textContent, org: x.getAttribute("org") || ""})),
refs: kids(a, "Reference").map(x => ({t: x.textContent, kind: x.getAttribute("kind") || "",
actions: (x.getAttribute("actions") || "").split(/\s+/).filter(Boolean),
href: (x.getAttribute("href") || "").split(/\s+/).filter(Boolean)})),
pillar: P, goal: G};
A.proposal = A.refs.some(x => x.kind === "avots");
G.actions.push(A); s.actions[A.id] = A;
}
P.goals.push(G);
}
s.pillars.push(P);
}
const pr = kid(r, "Principles");
s.principles = pr ? {heading: tx(pr, "Heading"), intro: tx(pr, "Intro"),
items: kids(pr, "Principle").map(x => ({id: x.getAttribute("id"), label: tx(x, "Label"), text: tx(x, "Text")}))} : null;
const pn = kid(r, "ProposalsNote");
s.proposals = pn ? {text: pn.textContent, actions: (pn.getAttribute("actions") || "").split(/\s+/).filter(Boolean)} : null;
s.resources = kids(kid(r, "Resources"), "Resource").map(x => ({id: x.getAttribute("id"), href: x.getAttribute("href"),
country: tx(x, "Country"), title: tx(x, "Title"), text: tx(x, "Text")}));
return s;
}
/* Rīcību atsauces „Rīcība 10 (…)” un „(19)” → klikšķināmas saites uz rīcību */
function linkActs(text, ids){
let h = esc(text);
const byN = Object.fromEntries(ids.map(id => [S.actions[id] ? S.actions[id].n : null, id]));
h = h.replace(/Rīcība (\d+)/g, (m, n) => byN[n] ? `<a class="act" href="#${byN[n]}" data-act="${byN[n]}" title="${esc(S.actions[byN[n]].title)}">${m}</a>` : m);
h = h.replace(/\((\d+)\)/g, (m, n) => byN[n] ? `(<a class="act" href="#${byN[n]}" data-act="${byN[n]}" title="${esc(S.actions[byN[n]].title)}">${n}</a>)` : m);
return h;
}
const short = id => id.slice(S.id.length + 1);
function card(A){
const tags = A.tags.map(t => `<span class="tag${t.kind ? " rec" : ""}">${esc(t.t)}</span>`).join("")
+ (A.proposal ? `<span class="tag src" title="Avots: nozares priekšlikums">Nozares priekšlikums</span>` : "");
const refs = A.refs.map(r => {
const lab = {saistits: "Saistīts ar", avots: "Avots", piemers: "Piemērs"}[r.kind];
const links = r.href.map(u => ` <a href="${esc(u)}" target="_blank" rel="noopener">↗</a>`).join("");
return `<p class="ref">${lab ? `<b>${lab}:</b> ` : ""}${linkActs(r.t, r.actions)}${links}</p>`;
}).join("");
const resp = A.resp.length ? `<p class="ref"><b>Atbildīgais:</b> ${A.resp.map(x => esc(x.t) + (x.org ? ` <span class="chip">${esc(x.org)}</span>` : "")).join(", ")}</p>`
: `<p class="resp">Atbildīgā institūcija nav noteikta</p>`;
return `<article class="card" id="${esc(A.id)}">
<div class="top"><span class="num">Rīcība ${A.n}</span><span class="chip" title="${esc(A.id)}">${esc(short(A.id))}</span>${tags}
<span class="dl${A.dv ? "" : " open"}" title="Termiņš">${A.dv ? "⏱ " : ""}${esc(A.deadline)}</span></div>
<h4>${esc(A.title)}</h4>
<p>${esc(A.text)}</p>
<div class="measure"><b>Mērķa izpildes mērījums</b>${esc(A.measure)}</div>
${resp}${refs}
</article>`;
}
function renderPillar(P){
return `<section class="phead"><p class="kicker">${esc(P.n)} pīlārs · ${esc(P.name)} <span class="chip">${esc(short(P.id))}</span></p>
<h2>${esc(P.heading)}</h2><p>${esc(P.desc)}</p></section>`
+ P.goals.map(G => `<div class="goal"><span class="n">${esc(G.n)}</span><div><h3>${esc(G.title)} <span class="chip">${esc(short(G.id))}</span></h3><p>${esc(G.desc)}</p></div></div>
<div class="grid">${G.actions.map(card).join("")}</div>`).join("");
}
function renderAll(){
const pills = S.pillars.map(P => `<option value="${esc(P.id)}">${esc(P.n)} · ${esc(P.name)}</option>`).join("");
return `<div class="tools"><input type="search" id="q" placeholder="Meklēt rīcībās…" value="${esc(ctx.q || "")}">
<select id="fp"><option value="">Visi pīlāri</option>${pills}</select>
<select id="ft"><option value="">Visas rīcības</option><option value="prop">Nozares priekšlikumi</option><option value="rec">Pārvaldības rekomendācijas</option><option value="open">Bez termiņa</option></select>
<span class="meta" id="cnt"></span></div>
<div class="tw"><table><thead><tr><th data-s="n">Nr.</th><th data-s="title">Rīcība</th><th data-s="p">Pīlārs · mērķis</th><th data-s="dv">Termiņš</th></tr></thead><tbody id="rows"></tbody></table></div>`;
}
function fillRows(){
const q = ($("q").value || "").toLowerCase(), fp = $("fp").value, ft = $("ft").value, s = ctx.sort || "n";
ctx.q = $("q").value;
let list = Object.values(S.actions).filter(A =>
(!fp || A.pillar.id === fp) &&
(!ft || (ft === "prop" && A.proposal) || (ft === "rec" && A.tags.some(t => t.kind)) || (ft === "open" && !A.dv)) &&
(!q || [A.title, A.text, A.measure, ...A.refs.map(r => r.t)].join(" ").toLowerCase().includes(q)));
const key = {n: A => A.n, title: A => A.title, p: A => A.pillar.n + A.goal.n, dv: A => A.dv || "9999"}[s];
list.sort((a, b) => key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : a.n - b.n);
$("rows").innerHTML = list.map(A => `<tr data-act="${esc(A.id)}"><td><b>${A.n}</b></td>
<td>${esc(A.title)}${A.tags.map(t => ` <span class="tag${t.kind ? " rec" : ""}">${esc(t.t)}</span>`).join("")}${A.proposal ? ` <span class="tag src">Nozares priekšlikums</span>` : ""}</td>
<td>${esc(A.pillar.n)} · ${esc(A.pillar.name)}<br><span class="meta">${esc(A.goal.n)} ${esc(A.goal.title)}</span></td>
<td style="white-space:nowrap">${esc(A.deadline)}</td></tr>`).join("") || `<tr><td colspan="4" class="empty">Nekas nav atrasts</td></tr>`;
$("cnt").textContent = `${list.length} no ${Object.keys(S.actions).length}`;
}
function renderPrinciples(){
let h = "";
if (S.principles) h += `<div class="box"><h2>${esc(S.principles.heading)}</h2><p>${esc(S.principles.intro)}</p>
<div class="prin">${S.principles.items.map(x => `<div><b>${esc(x.label)}</b>${esc(x.text)}</div>`).join("")}</div></div>`;
if (S.proposals) h += `<div class="box"><h2>Saņemtie priekšlikumi</h2><p>${linkActs(S.proposals.text, S.proposals.actions)}</p></div>`;
if (S.resources.length) h += `<div class="box"><h2>Starptautiskā pieredze</h2><div class="prin">${S.resources.map(x =>
`<div><b>${esc(x.country)}</b><a href="${esc(x.href)}" target="_blank" rel="noopener">${esc(x.title)} ↗</a><br><span class="meta">${esc(x.text)}</span></div>`).join("")}</div></div>`;
return h;
}
function renderAbout(){
const acts = Object.values(S.actions), nores = acts.filter(a => !a.resp.length).length;
const open = acts.filter(a => !a.dv).length, prop = acts.filter(a => a.proposal).length;
const xsd = $("xsd").textContent.trim();
return `<div class="box"><h2>Par datukopu</h2>
<p>PPP Asociācijas (PPPA) piedāvātā Latvijas Nacionālā MI stratēģija mašīnlasāmā formā — daļa no iniciatīvas <i>Country as Code</i> (Valsts kā Kods). Teksts pārņemts burtiski, kā publicēts: <a href="${esc(S.url)}" target="_blank" rel="noopener">${esc(S.url)}</a>.</p>
<div class="tiles">
<div class="tile"><b>${S.pillars.length}</b><span>pīlāri</span></div>
<div class="tile"><b>${S.goals}</b><span>stratēģiskie mērķi</span></div>
<div class="tile"><b>${acts.length}</b><span>rīcības</span></div>
<div class="tile"><b>${prop}</b><span>nozares priekšlikumi</span></div>
<div class="tile"><b>${open}</b><span>bez noteikta termiņa</span></div>
<div class="tile"><b>${nores}</b><span>bez atbildīgās institūcijas</span></div>
</div>
${nores ? `<p><b>Atbildīgās institūcijas nav noteiktas</b> ${nores} no ${acts.length} rīcībām. Shēmā katrai rīcībai paredzēts elements <code>Responsible</code> ar CaC ID (<code>SS-IIII</code>) no Organizāciju reģistra, lai izpildi varētu sasaistīt ar konkrētu iestādi.</p>` : ""}
<h3>Identifikatori</h3>
<div class="tw"><table><thead><tr><th>Elements</th><th>ID</th><th>Piemērs</th></tr></thead><tbody>
<tr><td>Stratēģija</td><td><code>pppa-mis-GGGG-MM-DD</code> (atjaunošanas datums)</td><td><code>${esc(S.id)}</code></td></tr>
<tr><td>Pīlārs</td><td><code>.pN</code></td><td><code>${esc(S.id)}.p2</code></td></tr>
<tr><td>Mērķis</td><td><code>.mN</code> (vienota numerācija)</td><td><code>${esc(S.id)}.m3</code></td></tr>
<tr><td>Rīcība</td><td><code>.rNN</code> (= drukātais numurs)</td><td><code>${esc(S.id)}.r09</code></td></tr>
<tr><td>Princips / resurss</td><td><code>.prN</code> / <code>.aN</code></td><td><code>${esc(S.id)}.pr1</code></td></tr>
</tbody></table></div>
<p style="margin-top:8px;font-size:12.5px;color:var(--muted)">Datne = dokumenta ID (<code>${esc(S.id)}.xml</code>). Termiņi glabāti kā publicēti un atribūtā <code>@value</code> ISO formā. Atsauces uz citām rīcībām — atribūtā <code>@actions</code>.</p>
${xsd && !xsd.startsWith("__") ? `<h3>Shēma</h3><details><summary>mi-strategija-0.1.xsd</summary><pre>${esc(xsd)}</pre></details>` : ""}
</div>`;
}
const TABS = () => [...S.pillars.map(P => [P.id.slice(S.id.length + 1), `${P.n} · ${P.name}`]), ["all", "Visas rīcības"], ["prin", "Principi un resursi"], ["about", "Par"]];
function render(){
document.title = `${S.title} · ${S.version}`;
$("title").textContent = S.title;
$("slogan").textContent = S.slogan.join(" · ");
$("vision").textContent = S.vision;
$("meta").innerHTML = `<span>${esc(S.author)}</span><span>· ${esc(S.status)} ${esc(S.version)}</span><span>· atjaunots ${esc(S.updated)}</span><span class="chip">${esc(S.id)}</span>`
+ (ctx.pgv ? `<span class="pgvbar">· ${esc(ctx.pgv)}</span>` : "");
$("tabs").innerHTML = TABS().map(([k, l]) => `<button data-tab="${k}" class="${k === tab ? "on" : ""}">${esc(l)}</button>`).join("");
const P = S.pillars.find(p => short(p.id) === tab);
$("main").innerHTML = P ? renderPillar(P) : tab === "all" ? renderAll() : tab === "prin" ? renderPrinciples() : renderAbout();
if (tab === "all") { fillRows(); ["q", "fp", "ft"].forEach(i => $(i).addEventListener("input", fillRows)); if (ctx.fp) $("fp").value = ctx.fp; }
}
function setTab(t){ tab = t; render(); try { history.replaceState(null, "", "#" + t); } catch (e) {} scrollTo(0, 0); }
function goAction(id){
const A = S.actions[id]; if (!A) return;
tab = short(A.pillar.id); render();
const el = $(id); if (!el) return;
el.scrollIntoView({behavior: "smooth", block: "start"}); el.classList.add("flash"); setTimeout(() => el.classList.remove("flash"), 1400);
}
document.addEventListener("click", ev => {
const t = ev.target.closest("[data-tab]"); if (t) { setTab(t.dataset.tab); return; }
const a = ev.target.closest("[data-act]"); if (a) { ev.preventDefault(); goAction(a.dataset.act); return; }
const th = ev.target.closest("th[data-s]"); if (th) { ctx.sort = th.dataset.s; fillRows(); }
});
function load(text){
try { S = parse(text); } catch (e) { $("main").innerHTML = `<div class="empty">${esc(e.message)}</div>`; return; }
const h = location.hash.slice(1);
if (S.actions[h]) { goAction(h); return; }
tab = TABS().some(([k]) => k === h) ? h : short(S.pillars[0].id);
render();
}
/* ===== ProcessGit skatītājs (processgit.viewer.json): PGV_READY → PGV_INIT → PGV_FETCH ===== */
function pgvFetch(url){ return new Promise((res, rej) => {
const tm = setTimeout(() => { removeEventListener("message", hd); rej(new Error("PGV_FETCH timeout")); }, 15000);
function hd(ev){ const m = ev.data; if (!m || m.type !== "PGV_FETCH_RESULT" || m.url !== url) return; clearTimeout(tm); removeEventListener("message", hd); m.ok ? res(m.text) : rej(new Error(m.error || "fetch failed")); }
addEventListener("message", hd); parent.postMessage({type: "PGV_FETCH", url}, "*"); }); }
async function pgvInit(p){
const url = (p.targets && p.targets.xml) || `${p.repoLink}/raw/branch/${p.branch}/${p.path}`;
ctx.pgv = `ProcessGit · ${p.branch || ""} · ${p.path || ""}${p.lastCommit ? " · " + p.lastCommit.slice(0, 7) : ""}`;
try { load(await pgvFetch(url)); } catch (e) { $("main").innerHTML = `<div class="empty">Neizdevās ielādēt datni no repozitorija: ${esc(e.message)}</div>`; }
}
const inFrame = window.parent && window.parent !== window;
if (inFrame) {
addEventListener("message", ev => { const m = ev.data; if (m && typeof m === "object" && m.type === "PGV_INIT") pgvInit(m.payload || {}); });
try { parent.postMessage({type: "PGV_READY"}, "*"); } catch (e) {}
setTimeout(() => { if (!S) $("main").innerHTML = `<div class="empty">Gaida datni no ProcessGit…</div>`; }, 4000);
} else load($("embedded").textContent.trim());
</script>
</body>
</html>