Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Deploy GitHub Pages

on:
push:
branches: [main]
paths:
- "site/**"
- "CHANGELOG.md"
- ".github/workflows/pages.yml"
workflow_dispatch:

permissions:
contents: read
pages: write
id-token: write

concurrency:
group: pages
cancel-in-progress: false

jobs:
deploy:
name: deploy site/
runs-on: ubuntu-latest
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- uses: actions/checkout@v5

- name: Sync CHANGELOG.md into site/
run: python3 site/sync_changelog.py

- name: Configure Pages
uses: actions/configure-pages@v5

- name: Upload site artifact
uses: actions/upload-pages-artifact@v3
with:
path: site

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ sdkconfig
sdkconfig.old
sdkconfig.ci.*

# Site: generated from root CHANGELOG.md (python3 site/sync_changelog.py)
site/changelog.data.js

# Host unit-test binaries
tests/host/build/

Expand Down
Binary file modified brand/FlinT_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added site/.nojekyll
Empty file.
14 changes: 14 additions & 0 deletions site/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Product landing (GitHub Pages)

Static site for FlinT OS. Deployed by `.github/workflows/pages.yml`.

## Local preview

```bash
python3 site/sync_changelog.py # embed root CHANGELOG.md → changelog.data.js
# open site/index.html / site/changelog.html
```

Source of truth is the repo-root `CHANGELOG.md`. Pages only uploads `site/`,
so CI (and local sync) embed it into `changelog.data.js` — no duplicate
`site/CHANGELOG.md`, works under `file://`.
Binary file added site/assets/FlinT_logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added site/assets/FlinT_logo.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 91 additions & 0 deletions site/changelog.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Changelog — FlinT OS</title>
<meta
name="description"
content="FlinT OS release history from CHANGELOG.md (Keep a Changelog)."
/>
<meta name="theme-color" content="#0a0a0a" />
<link rel="icon" href="./assets/FlinT_logo.png" type="image/png" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=IBM+Plex+Mono:wght@400;500&display=swap"
rel="stylesheet"
/>
<link rel="stylesheet" href="./styles.css" />
</head>
<body class="page-changelog">
<div class="atmosphere" aria-hidden="true">
<canvas class="atmosphere__field" id="signal-field"></canvas>
<div class="atmosphere__grid"></div>
<div class="atmosphere__spark"></div>
</div>

<div class="scroll-progress" aria-hidden="true"><span></span></div>

<nav class="topnav">
<div class="shell topnav__inner">
<a class="topnav__brand" href="./index.html">
<svg
class="topnav__spark"
viewBox="0 0 24 24"
aria-hidden="true"
>
<path
fill="currentColor"
d="M12 1.2 13.15 8.4 19.8 6.2 15.1 11.1 22.8 12 15.1 12.9 19.8 17.8 13.15 15.6 12 22.8 10.85 15.6 4.2 17.8 8.9 12.9 1.2 12 8.9 11.1 4.2 6.2 10.85 8.4Z"
/>
</svg>
FlinT <span>OS</span>
</a>
<div class="topnav__links">
<a href="./index.html">Home</a>
<a href="./changelog.html" aria-current="page">Changelog</a>
<a href="https://github.com/DeeTech-Labs/FlinT-OS">GitHub</a>
</div>
</div>
</nav>

<header class="page-hero">
<div class="shell">
<p class="block__label">Release history</p>
<h1 class="page-hero__title">Changelog</h1>
<p class="page-hero__lede">
Sourced from <code>CHANGELOG.md</code> in the FlinT OS repository
(Keep a Changelog · SemVer).
</p>
</div>
</header>

<main class="shell changelog">
<p class="changelog__status" id="changelog-status" role="status">
Preparing changelog…
</p>
<div class="changelog__root" id="changelog-root" hidden></div>
</main>

<footer class="foot">
<div class="shell foot__inner">
<p class="foot__brand">FlinT · FlinT OS · DeeTech Labs</p>
<p class="foot__meta">
<a href="./index.html">Home</a>
<span aria-hidden="true">·</span>
<a
href="https://github.com/DeeTech-Labs/FlinT-OS/blob/main/CHANGELOG.md"
>CHANGELOG.md</a
>
<span aria-hidden="true">·</span>
<a href="https://github.com/DeeTech-Labs/FlinT-OS">GitHub</a>
</p>
</div>
</footer>

<script src="./changelog.data.js"></script>
<script src="./changelog.js" defer></script>
<script src="./site.js" defer></script>
</body>
</html>
180 changes: 180 additions & 0 deletions site/changelog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
(() => {
const statusEl = document.getElementById("changelog-status");
const rootEl = document.getElementById("changelog-root");
if (!statusEl || !rootEl) return;

const esc = (s) =>
s
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");

const inline = (text) => {
let t = esc(text);
t = t.replace(
/`([^`]+)`/g,
'<code class="cl-code">$1</code>'
);
t = t.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>");
t = t.replace(
/\[([^\]]+)\]\((https?:\/\/[^)]+)\)/g,
'<a href="$2" rel="noopener noreferrer">$1</a>'
);
return t;
};

const parseChangelog = (md) => {
const lines = md.replace(/\r\n/g, "\n").split("\n");
const releases = [];
const links = {};
let release = null;
let section = null;
let intro = [];
let inIntro = true;
let para = [];

const flushPara = () => {
if (!para.length || !release || section) {
para = [];
return;
}
release.notes.push(para.join(" "));
para = [];
};

for (const raw of lines) {
const line = raw.trimEnd();
const linkDef = line.match(/^\[([^\]]+)\]:\s+(\S+)\s*$/);
if (linkDef) {
links[linkDef[1]] = linkDef[2];
continue;
}

const h2 = line.match(/^##\s+\[([^\]]+)\](?:\s+-\s+(.+))?\s*$/);
if (h2) {
flushPara();
inIntro = false;
release = {
id: h2[1],
date: (h2[2] || "").trim(),
notes: [],
sections: [],
};
section = null;
releases.push(release);
continue;
}

if (inIntro) {
if (line.startsWith("# ")) continue;
if (!line.trim()) continue;
intro.push(line.trim());
continue;
}

if (!release) continue;

const h3 = line.match(/^###\s+(.+)\s*$/);
if (h3) {
flushPara();
section = { title: h3[1].trim(), items: [] };
release.sections.push(section);
continue;
}

const bullet = line.match(/^-\s+(.+)$/);
if (bullet) {
flushPara();
if (!section) {
section = { title: "Notes", items: [] };
release.sections.push(section);
}
section.items.push(bullet[1].trim());
continue;
}

const cont = line.match(/^\s{2,}(.+)$/);
if (cont && section && section.items.length) {
section.items[section.items.length - 1] += " " + cont[1].trim();
continue;
}

if (!line.trim()) {
flushPara();
continue;
}

if (!section) para.push(line.trim());
}
flushPara();

for (const r of releases) {
r.href = links[r.id] || null;
}
return { intro, releases };
};

const render = (data) => {
const parts = [];
if (data.intro.length) {
parts.push(
`<p class="changelog__intro">${data.intro.map(inline).join(" ")}</p>`
);
}

parts.push('<ol class="cl-timeline">');
data.releases.forEach((rel, idx) => {
const ver = rel.href
? `<a class="cl-ver" href="${esc(rel.href)}" rel="noopener noreferrer">${esc(rel.id)}</a>`
: `<span class="cl-ver">${esc(rel.id)}</span>`;
const date = rel.date
? `<time class="cl-date" datetime="${esc(rel.date)}">${esc(rel.date)}</time>`
: "";

parts.push(
`<li class="cl-release" data-reveal style="--i:${idx}">` +
`<div class="cl-release__head">${ver}${date}</div>`
);

for (const note of rel.notes) {
parts.push(`<p class="cl-note">${inline(note)}</p>`);
}

for (const sec of rel.sections) {
const kind = sec.title.toLowerCase().replace(/[^a-z]+/g, "-");
parts.push(
`<section class="cl-section cl-section--${esc(kind)}">` +
`<h3 class="cl-section__title">${esc(sec.title)}</h3><ul class="cl-list">`
);
for (const item of sec.items) {
parts.push(`<li>${inline(item)}</li>`);
}
parts.push("</ul></section>");
}
parts.push("</li>");
});
parts.push("</ol>");

rootEl.innerHTML = parts.join("");
rootEl.hidden = false;
statusEl.hidden = true;

// Re-run reveal for injected nodes
document.dispatchEvent(new CustomEvent("flint:content"));
};

const fail = (msg) => {
statusEl.textContent = msg;
statusEl.classList.add("is-error");
};

const md = window.__FLINT_CHANGELOG_MD__;
if (typeof md === "string" && md.trim()) {
render(parseChangelog(md));
} else {
fail(
"Changelog data missing. Run: python3 site/sync_changelog.py"
);
}
})();
Loading