Skip to content

Commit 23ed407

Browse files
borisbatclaude
andcommitted
site: news nav link with "N NEW" chip + dasImgui announcement
- index.html: add id="news" to § 06 "Recent activity" so the nav link anchor-jumps there. Add `news` link to forge-nav__links before `docs`. Wire `files/news-counter.js`. - downloads.html, daspkg.html, playground/index.html, blog/template.html: same `news` nav link (anchors to index.html#news from non-landing pages). downloads + blog/template also pick up the counter script (matching the existing scope of blog-counter.js — daspkg and playground are skipped for both, preserving the prior pattern). - files/news-counter.js: new — mirrors blog-counter.js. Reuses `.forge-blog-chip` CSS class. Matches nav anchors by `link.hash === "#news"` so the same script works from any page. Marks seen via localStorage["daslang:news:lastSeen"] on landing-with-#news-hash AND on nav-link click; clears chips on click. - blog/build_blog.py: emit `files/news-meta.json` alongside `news.json` / `blog.json`. Same shape as blog.json (newest_date, baseline_date, items). - .gitignore: list `files/news-meta.json` as a build artifact. - _news/2026-05-27-0-6-3-is-just-around-the-corner-in-the.md: new entry announcing 0.6.3 + dasImgui's imgui_playwright addition. Links to https://borisbat.github.io/dasImgui/. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 999b8ea commit 23ed407

10 files changed

Lines changed: 149 additions & 3 deletions

File tree

site/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ blog/feed.xml
77
changelist.html
88
news/
99
files/news.json
10+
files/news-meta.json
1011
files/blog.json
1112

1213
# Playground: vendored from web/ui/src/ for local-dev preview only.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
date: 2026-05-27
3+
tag: dasImgui
4+
title: 0.6.3 is just around the corner. In the meantime, check out dasImgui — now with imgui_playwright.
5+
link: https://borisbat.github.io/dasImgui/
6+
---

site/blog/build_blog.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
blog/feed.xml — Atom feed of posts
1414
news/<slug>.html — one per news entry that has a body
1515
changelist.html — full long-form news list (all entries by date desc)
16-
files/news.json — top-N news for forge.js to render landing § 05
16+
files/news.json — top-N news for forge.js to render landing § 06
17+
files/news-meta.json — newest_date + baseline_date + all dates, drives
18+
the "N NEW" nav chip via files/news-counter.js
1719
1820
Hexo extensions translated:
1921
<!-- more --> → excerpt boundary (excerpt above used on index)
@@ -553,6 +555,20 @@ def main():
553555
(out / 'files' / 'blog.json').write_text(
554556
json.dumps(blog_data, indent=2), encoding='utf-8')
555557

558+
# 8. news-meta.json — parallel of blog.json for the news feed; same
559+
# metadata fields (newest_date, baseline_date), entries live under
560+
# `news` instead of `posts`. Drives the "N NEW" chip on the news
561+
# nav link via news-counter.js.
562+
newest_news = news[0].date if news else '1970-01-01'
563+
baseline_news = news[1].date if len(news) >= 2 else '1970-01-01'
564+
news_meta = {
565+
'newest_date': newest_news,
566+
'baseline_date': baseline_news,
567+
'news': [{'slug': n.slug, 'date': n.date} for n in news],
568+
}
569+
(out / 'files' / 'news-meta.json').write_text(
570+
json.dumps(news_meta, indent=2), encoding='utf-8')
571+
556572
print(f"built {len(posts)} posts, {len(news)} news entries -> {out}/")
557573

558574

site/blog/template.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
<script src="{{root}}files/cm/daslang-keywords.js" defer></script>
1616
<script src="{{root}}files/highlight.js" defer></script>
1717
<script src="{{root}}files/blog-counter.js" defer></script>
18+
<script src="{{root}}files/news-counter.js" defer></script>
1819

1920
<!-- Analytics -->
2021
<script data-goatcounter="https://borisbat.goatcounter.com/count"
@@ -31,6 +32,7 @@
3132
<span class="forge-mark__tld">.io</span>
3233
</a>
3334
<div class="forge-nav__links">
35+
<a href="{{root}}index.html#news">news</a>
3436
<a href="{{root}}doc/index.html">docs</a>
3537
<a href="{{root}}index.html#benchmarks">benchmarks</a>
3638
<a href="{{root}}downloads.html">downloads</a>

site/daspkg.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<span class="forge-mark__tld">.io</span>
3333
</a>
3434
<div class="forge-nav__links">
35+
<a href="index.html#news">news</a>
3536
<a href="doc/index.html">docs</a>
3637
<a href="index.html#benchmarks">benchmarks</a>
3738
<a href="downloads.html">downloads</a>

site/downloads.html

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<span class="forge-mark__tld">.io</span>
2727
</a>
2828
<div class="forge-nav__links">
29+
<a href="index.html#news">news</a>
2930
<a href="doc/index.html">docs</a>
3031
<a href="index.html#benchmarks">benchmarks</a>
3132
<a href="downloads.html">downloads</a>
@@ -206,5 +207,6 @@ <h2 class="forge-h2">Where to learn more.</h2>
206207

207208
</div>
208209
<script src="files/blog-counter.js" defer></script>
210+
<script src="files/news-counter.js" defer></script>
209211
</body>
210212
</html>

site/files/forge.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -494,7 +494,7 @@ def main() {
494494
`<a href="/playground/index.html?example=${slug}">try <span class="forge-bench__playground-test">${escapeHtml(benchBm)}</span> on the playground →</a>`;
495495
}
496496

497-
// ─── § 05 News feed (top 5 from news.json) ─────────────────────
497+
// ─── § 06 News feed (top 5 from news.json) ─────────────────────
498498
async function loadNews() {
499499
const rowsEl = document.getElementById('news-rows');
500500
if (!rowsEl) return;

site/files/news-counter.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/* Forge nav — "N NEW" news chip.
2+
*
3+
* Data source: files/news-meta.json (built by site/blog/build_blog.py).
4+
* Storage: localStorage["daslang:news:lastSeen"] = "YYYY-MM-DD".
5+
*
6+
* Semantics (mirrors blog-counter.js):
7+
* - First-time visitor (no key) → seeded from news-meta.json baseline_date,
8+
* which is the 2nd-newest entry's date.
9+
* So count = 1 (just the newest).
10+
* - Landing with hash "#news" → key set to newest_date; chip clears.
11+
* - Click on the news nav link → key set to newest_date.
12+
* - count > 9 → render "9+ NEW".
13+
* - count === 0 → chip not rendered.
14+
*
15+
* Nav anchors are matched by link.hash === "#news" so the same script works
16+
* from any page that links to "#news" / "index.html#news" / "../index.html#news".
17+
*/
18+
(function () {
19+
'use strict';
20+
21+
var STORAGE_KEY = 'daslang:news:lastSeen';
22+
23+
function readLastSeen() {
24+
try { return window.localStorage.getItem(STORAGE_KEY); }
25+
catch (_) { return null; }
26+
}
27+
28+
function writeLastSeen(date) {
29+
try { window.localStorage.setItem(STORAGE_KEY, date); }
30+
catch (_) { /* private mode / quota — silent */ }
31+
}
32+
33+
function newsJsonUrl() {
34+
// Locate our own <script> tag and derive news-meta.json next to it.
35+
var scripts = document.getElementsByTagName('script');
36+
for (var i = 0; i < scripts.length; i++) {
37+
var src = scripts[i].getAttribute('src') || '';
38+
if (src.indexOf('news-counter.js') !== -1) {
39+
return src.replace(/news-counter\.js(\?.*)?$/, 'news-meta.json');
40+
}
41+
}
42+
return 'files/news-meta.json';
43+
}
44+
45+
function findNewsLinks() {
46+
// Match any nav link whose URL hash is "#news" — works for
47+
// "#news", "index.html#news", "../index.html#news".
48+
var links = document.querySelectorAll('.forge-nav__links a[href]');
49+
var out = [];
50+
for (var i = 0; i < links.length; i++) {
51+
if (links[i].hash === '#news') out.push(links[i]);
52+
}
53+
return out;
54+
}
55+
56+
function renderChip(anchor, count) {
57+
if (anchor.querySelector('.forge-blog-chip')) return;
58+
var chip = document.createElement('span');
59+
chip.className = 'forge-blog-chip';
60+
chip.textContent = (count > 9 ? '9+' : count) + ' NEW';
61+
anchor.appendChild(chip);
62+
anchor.classList.add('forge-blog-link');
63+
}
64+
65+
function clearChips() {
66+
var chips = document.querySelectorAll('.forge-nav__links a .forge-blog-chip');
67+
for (var i = 0; i < chips.length; i++) {
68+
var parent = chips[i].parentNode;
69+
if (parent && parent.hash === '#news') {
70+
chips[i].remove();
71+
}
72+
}
73+
}
74+
75+
function update(data) {
76+
// Landing-with-#news: mark seen and render nothing. Early-return so
77+
// that a silently-failed writeLastSeen (private mode / quota) cannot
78+
// still cause the chip to render — we are already on the destination.
79+
if (window.location.hash === '#news') {
80+
writeLastSeen(data.newest_date);
81+
return;
82+
}
83+
var anchors = findNewsLinks();
84+
for (var k = 0; k < anchors.length; k++) {
85+
(function (a) {
86+
a.addEventListener('click', function () {
87+
writeLastSeen(data.newest_date);
88+
clearChips();
89+
});
90+
})(anchors[k]);
91+
}
92+
var lastSeen = readLastSeen() || data.baseline_date;
93+
var count = 0;
94+
for (var i = 0; i < data.news.length; i++) {
95+
if (data.news[i].date > lastSeen) count++;
96+
}
97+
if (count <= 0) return;
98+
for (var j = 0; j < anchors.length; j++) {
99+
renderChip(anchors[j], count);
100+
}
101+
}
102+
103+
function start() {
104+
fetch(newsJsonUrl(), { cache: 'no-cache' })
105+
.then(function (r) { return r.ok ? r.json() : null; })
106+
.then(function (data) { if (data) update(data); })
107+
.catch(function () { /* offline / missing — silent */ });
108+
}
109+
110+
if (document.readyState === 'loading') {
111+
document.addEventListener('DOMContentLoaded', start);
112+
} else {
113+
start();
114+
}
115+
})();

site/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
<span class="forge-mark__tld">.io</span>
4141
</a>
4242
<div class="forge-nav__links">
43+
<a href="#news">news</a>
4344
<a href="doc/index.html">docs</a>
4445
<a href="#benchmarks">benchmarks</a>
4546
<a href="downloads.html">downloads</a>
@@ -374,7 +375,7 @@ <h2>
374375
</section>
375376

376377
<!-- ───── § 06 Changelog / news ───── -->
377-
<section class="forge-section forge-section--alt">
378+
<section class="forge-section forge-section--alt" id="news">
378379
<div class="forge-container">
379380
<div class="forge-section-label">
380381
<span class="forge-section-label__num">§ 06</span>
@@ -452,5 +453,6 @@ <h2 class="forge-h2">Recent activity.</h2>
452453
<script src="files/runner.js" defer></script>
453454
<script src="files/forge.js" defer></script>
454455
<script src="files/blog-counter.js" defer></script>
456+
<script src="files/news-counter.js" defer></script>
455457
</body>
456458
</html>

site/playground/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<span class="forge-mark__tld">.io</span>
3434
</a>
3535
<div class="forge-nav__links">
36+
<a href="../index.html#news">news</a>
3637
<a href="../doc/index.html">docs</a>
3738
<a href="../index.html#benchmarks">benchmarks</a>
3839
<a href="../downloads.html">downloads</a>

0 commit comments

Comments
 (0)