301 lines
10 KiB
HTML
301 lines
10 KiB
HTML
<!--
|
||
Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||
|
||
This program and the accompanying materials are made
|
||
available under the terms of the Eclipse Public License 2.0
|
||
which is available at https://www.eclipse.org/legal/epl-2.0/
|
||
|
||
SPDX-License-Identifier: EPL-2.0
|
||
-->
|
||
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>Eclipse hawkBit</title>
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||
<meta name="description" content="Description">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0">
|
||
<link rel="icon" href="images/hawkbit.svg" type="image/svg+xml">
|
||
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/docsify@4/lib/themes/vue.css">
|
||
</head>
|
||
|
||
<body>
|
||
<div id="app"></div>
|
||
<script>
|
||
window.$docsify = {
|
||
copyCode: {
|
||
buttonText: 'Copy',
|
||
errorText: 'Error',
|
||
successText: 'Copied',
|
||
},
|
||
name: ' ',
|
||
themeColor: '#0ea5ea',
|
||
coverpage: true,
|
||
auto2top: true,
|
||
loadSidebar: true,
|
||
subMaxLevel: 2,
|
||
maxLevel: 2,
|
||
sidebarDisplayLevel: 1,
|
||
|
||
search: {
|
||
placeholder: 'Type to search',
|
||
noData: 'No results!',
|
||
paths: 'auto',
|
||
depth: 3
|
||
}
|
||
}
|
||
</script>
|
||
<script>
|
||
(function () {
|
||
function logoPlugin(hook) {
|
||
hook.ready(function () {
|
||
fetch('images/hawkbit_logo.svg')
|
||
.then(function (r) { return r.text(); })
|
||
.then(function (svg) {
|
||
var el = document.querySelector('.app-name');
|
||
if (!el) return;
|
||
var div = document.createElement('div');
|
||
div.innerHTML = svg;
|
||
var svgEl = div.querySelector('svg');
|
||
svgEl.setAttribute('width', '200');
|
||
svgEl.removeAttribute('height');
|
||
svgEl.style.setProperty('--logo-color', '#F0EEEA');
|
||
el.innerHTML = '';
|
||
el.appendChild(svgEl);
|
||
});
|
||
});
|
||
}
|
||
window.$docsify.plugins = [].concat(logoPlugin, window.$docsify.plugins || []);
|
||
})();
|
||
</script>
|
||
<!-- Docsify v4 -->
|
||
<script src="//cdn.jsdelivr.net/npm/docsify@4"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/docsify/lib/plugins/search.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-clike.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-java.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-json.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-markdown.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-properties.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-nginx.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-yaml.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-ini.min.js"></script>
|
||
<script src="//cdn.jsdelivr.net/npm/prismjs@1/components/prism-http.min.js"></script>
|
||
<!-- Latest v2.x.x -->
|
||
<script src="https://unpkg.com/docsify-copy-code@2"></script>
|
||
<!-- Mermaid -->
|
||
<script src="//cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
||
<script src="//unpkg.com/docsify-mermaid@2.0.1/dist/docsify-mermaid.js"></script>
|
||
<script>mermaid.initialize({ startOnLoad: false, theme: 'default' });</script>
|
||
</body>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/dompurify@3.1.6/dist/purify.min.js"></script>
|
||
|
||
<style>
|
||
|
||
body, input {
|
||
background-color: #F0EEEA;
|
||
}
|
||
|
||
.sidebar-sep {
|
||
margin: 12px 0;
|
||
border: 0;
|
||
height: 1px;
|
||
}
|
||
|
||
.sidebar, .sidebar-toggle, .sidebar ul li a {
|
||
background-color: #43464A;
|
||
color: #f0eeea;
|
||
}
|
||
|
||
.sidebar ul li.active>a {
|
||
color: #027abe;
|
||
}
|
||
|
||
thead tr {
|
||
background-color: #015585;
|
||
color: #f0eeea;
|
||
}
|
||
tbody tr:nth-child(odd) {
|
||
background-color: #9abfdf;
|
||
}
|
||
|
||
tbody tr:nth-child(even) {
|
||
background-color: #ffffff;
|
||
}
|
||
|
||
.markdown-section code,.markdown-section pre {
|
||
background-color: #f8f8f8;
|
||
}
|
||
|
||
</style>
|
||
|
||
<style>
|
||
.rn-list { display:grid; gap:1rem; }
|
||
.rn-card {
|
||
border:1px solid rgba(0,0,0,.08);
|
||
border-radius:12px;
|
||
padding:16px;
|
||
background: var(--docsearch-modal-background, #fff);
|
||
}
|
||
.rn-header { display:flex; align-items:baseline; gap:.5rem; justify-content:space-between; }
|
||
.rn-title { margin:0; font-size:1.1rem; font-weight:600; } /* ← no real <h*> */
|
||
.rn-badge { font-size:.75rem; padding:.1rem .4rem; border-radius:4px; border:1px solid rgba(0,0,0,.15); }
|
||
.rn-date { font-size:1rem; opacity:.75; }
|
||
|
||
.rn-search {
|
||
display:block;
|
||
width:100%;
|
||
max-width:520px;
|
||
padding:.6rem .8rem;
|
||
margin:0 0 1rem 0;
|
||
border:1px solid rgba(0,0,0,.15);
|
||
border-radius:10px;
|
||
outline:none;
|
||
box-sizing:border-box;
|
||
}
|
||
.rn-search:focus {
|
||
border-color: rgba(64,128,255,.5);
|
||
box-shadow:0 0 0 3px rgba(64,128,255,.15);
|
||
}
|
||
|
||
.rn-body { font-size:.95rem; line-height:1.6; }
|
||
.rn-body p { margin:.45rem 0; }
|
||
.rn-body ul, .rn-body ol { margin:.4rem 0 .6rem 1.2rem; }
|
||
.rn-body a { text-decoration:none; border-bottom:1px dotted currentColor; }
|
||
.rn-body code {
|
||
background:rgba(0,0,0,.06);
|
||
padding:.1rem .3rem;
|
||
border-radius:.3rem;
|
||
font-size:.9em;
|
||
}
|
||
.rn-h { margin:.35rem 0 .15rem; font-weight:600; } /* “fake headings” inside cards */
|
||
.rn-h1 { font-size:1rem; }
|
||
.rn-h2 { font-size:1rem; }
|
||
.rn-h3 { font-size:.98rem; }
|
||
|
||
.rn-actions { margin-top:.6rem; }
|
||
.rn-btn {
|
||
display:inline-block;
|
||
padding:.5rem .8rem;
|
||
border-radius:8px;
|
||
border:1px solid rgba(0,0,0,.15);
|
||
text-decoration:none;
|
||
}
|
||
|
||
/* Scope details styling to release cards only */
|
||
.rn-card details summary { cursor:pointer; font-weight:600; margin:.25rem 0; }
|
||
</style>
|
||
|
||
<script>
|
||
(function () {
|
||
// --- settings ---
|
||
const owner = 'eclipse-hawkbit';
|
||
const repo = 'hawkbit';
|
||
const targetFile = 'markdown/release-notes.md';
|
||
const perPage = 100;
|
||
|
||
// Compile markdown BUT render headings as <p class="rn-h rn-h{level}"><strong>..</strong></p>
|
||
// so Docsify won't see <h1..h6> and won't add them to the sidebar.
|
||
function compileMd(md) {
|
||
const renderer = new marked.Renderer();
|
||
renderer.heading = (text, level) =>
|
||
`<p class="rn-h rn-h${level}"><strong>${text}</strong></p>`;
|
||
const html = marked.parse(md || '', { renderer });
|
||
return DOMPurify.sanitize(html, { USE_PROFILES: { html: true }});
|
||
}
|
||
|
||
// Follow GitHub API pagination via Link header
|
||
async function fetchAllReleases(page = 1, acc = []) {
|
||
const url = `https://api.github.com/repos/${owner}/${repo}/releases?per_page=${perPage}&page=${page}`;
|
||
const res = await fetch(url, { headers: { 'Accept': 'application/vnd.github+json' } });
|
||
if (!res.ok) throw new Error('GitHub API error: ' + res.status);
|
||
const list = await res.json();
|
||
const link = res.headers.get('Link') || '';
|
||
const next = /<([^>]+)>;\s*rel="next"/.exec(link);
|
||
const all = acc.concat(list);
|
||
return next ? fetchAllReleases(page + 1, all) : all;
|
||
}
|
||
|
||
function renderCard(r, prevTag) {
|
||
const name = r.name || r.tag_name;
|
||
const date = r.published_at ? new Date(r.published_at).toISOString().slice(0,10) : '';
|
||
const pre = r.prerelease ? '<span class="rn-badge">pre-release</span>' : '';
|
||
const bodyHtml = compileMd(r.body || '');
|
||
const compareBtn = prevTag
|
||
? `<a class="rn-btn" href="https://github.com/${owner}/${repo}/compare/${encodeURIComponent(prevTag)}...${encodeURIComponent(r.tag_name)}" target="_blank" rel="noopener">Compare</a>`
|
||
: '';
|
||
|
||
return `
|
||
<div class="rn-card">
|
||
<div class="rn-header">
|
||
<div class="rn-title">${name}</div>
|
||
<div>${pre} <span class="rn-date">${date}</span></div>
|
||
</div>
|
||
<details>
|
||
<summary>View release notes</summary>
|
||
<div class="rn-body">${bodyHtml || '<p>No summary provided.</p>'}</div>
|
||
</details>
|
||
<div class="rn-actions">
|
||
<a class="rn-btn" href="${r.html_url}" target="_blank" rel="noopener">Open on GitHub</a>
|
||
${compareBtn}
|
||
</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
function mountUI(root, releases) {
|
||
releases.sort((a,b) => new Date(b.published_at||b.created_at) - new Date(a.published_at||a.created_at));
|
||
root.innerHTML = `
|
||
<input class="rn-search" id="rn-search" placeholder="Filter releases (e.g. 0.4, prerelease, 2024)" />
|
||
<div class="rn-list">
|
||
${releases.map((r, i) => renderCard(r, i < releases.length - 1 ? releases[i + 1].tag_name : null)).join('')}
|
||
</div>
|
||
`;
|
||
const input = root.querySelector('#rn-search');
|
||
const cards = Array.from(root.querySelectorAll('.rn-card'));
|
||
input.addEventListener('input', () => {
|
||
const q = input.value.toLowerCase();
|
||
cards.forEach(c => { c.style.display = c.textContent.toLowerCase().includes(q) ? '' : 'none'; });
|
||
});
|
||
}
|
||
|
||
// ---- docsify plugin ----
|
||
function plugin(hook, vm) {
|
||
hook.afterEach(function (html, next) {
|
||
const isRN = vm.route.file === targetFile || vm.route.path.toLowerCase().includes('release-notes');
|
||
if (!isRN) return next(html);
|
||
next('<div id="release-notes-root"><p>Loading releases…</p></div>');
|
||
});
|
||
|
||
hook.doneEach(async function () {
|
||
const isRN =
|
||
(vm.route.file || '').toLowerCase() === targetFile.toLowerCase() ||
|
||
vm.route.path.toLowerCase().includes('release-notes');
|
||
if (!isRN) return;
|
||
|
||
const root = Docsify.dom.find('#release-notes-root');
|
||
if (!root) return;
|
||
|
||
try {
|
||
const releases = await fetchAllReleases();
|
||
mountUI(root, releases);
|
||
} catch (e) {
|
||
console.error(e);
|
||
root.innerHTML = `
|
||
<p>Couldn’t load releases from GitHub right now.</p>
|
||
<p><a class="rn-btn" href="https://github.com/${owner}/${repo}/releases" target="_blank" rel="noopener">
|
||
Open on GitHub ↗
|
||
</a></p>`;
|
||
}
|
||
});
|
||
}
|
||
|
||
window.$docsify = window.$docsify || {};
|
||
window.$docsify.plugins = [].concat(plugin, window.$docsify.plugins || []);
|
||
})();
|
||
</script>
|
||
|
||
</html> |