Replace astronomy/expedition-club content with generic placeholders (AI)

The demo tag registry, site name, and the year-category tag convention
(expa-YYYY) were all modeled on a specific Czech astronomy club. Swapped
in generic, English placeholders instead:

- blog-config/tags.json: 12 generic topic tags (guides, news,
  tutorials, ...) replacing the Czech astronomy-themed ones.
- The year/category tag convention is now `year-YYYY` instead of
  `expa-YYYY` (discovery.js's category regex, the derived field renamed
  expa -> categoryYear, and every views.js/README usage).
- siteName and yearLabel strings (en/cs/sk) and index.html's hardcoded
  title/header text no longer say "Expedition Blog(s)" / "Expa {year}".
- README examples updated to match (kometa -> guides, expa-2026 ->
  year-2026).

Also regenerated all test content (cmjs/articles/, gitignored) with the
new generic tags and year- prefix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXSVPS3UmmASiiprPfcnUd
This commit is contained in:
biosiasandClaude Sonnet 5 committed 2026-09-12 13:35:23 +02:00
1 parent 997768e2ae
commit e7f8939ced
9 files changed
+52 -129

No files matched your search

+10 -10
View File
@@ -98,8 +98,8 @@ not in frontmatter. The format is:
as permanent: retagging or correcting the date later just means renaming the file (see below),
but changing the slug changes the article's URL.
- `<DDMMYYYY>` — the publish date, 8 digits, no separators (e.g. `16062026` for 16 June 2026).
- `<tag1>+<tag2>+…` — zero or more tag ids, joined with `+`. A tag id matching `expa-YYYY` is
treated as the article's "year"/category, shown as a badge linking to `#category/expa-YYYY`;
- `<tag1>+<tag2>+…` — zero or more tag ids, joined with `+`. A tag id matching `year-YYYY` is
treated as the article's "year"/category, shown as a badge linking to `#category/year-YYYY`;
every other tag id is a normal topic tag, shown as a pill linking to `#tag/tag-id`. Omit this
whole segment (and its leading `~`) if the article has no tags.
@@ -120,7 +120,7 @@ Steps:
1. Create the author directory if it doesn't exist: `cmjs/articles/author/`
2. Create the Markdown file directly inside it, using the naming convention above, e.g.
`cmjs/articles/author/my-article~16062026~kometa+expa-2026.md`
`cmjs/articles/author/my-article~16062026~guides+year-2026.md`
3. Optionally add a cover image to that author's shared `cmjs/articles/author/media/` folder
(creating it if needed), and reference it from frontmatter with `image: media/photo.jpg`.
4. Add a frontmatter block at the top of the Markdown file
@@ -135,20 +135,20 @@ section below possible.
### Tag labels
A tag in the file name is just an id (e.g. `kometa`) — the human-readable label shown on
A tag in the file name is just an id (e.g. `guides`) — the human-readable label shown on
tag pills comes from `cmjs/blog-config/tags.json`:
```json
{
"schemaVersion": 1,
"tags": [
{ "id": "kometa", "label": "Comet", "description": "", "aliases": [], "active": true }
{ "id": "guides", "label": "Guides", "description": "", "aliases": [], "active": true }
]
}
```
Registering an id here is optional — an unregistered tag id still works, it just displays as
its raw id instead of a friendly label. `expa-YYYY` year ids don't need to be registered here;
its raw id instead of a friendly label. `year-YYYY` year ids don't need to be registered here;
they're recognised automatically by pattern and don't use this label registry.
### Frontmatter format
@@ -216,10 +216,10 @@ The body text below the frontmatter is rendered as markdown and shown as the bio
| `#search/linux/page/2` | Same, page 2 |
| `#author/alice` | All articles by alice, page 1 |
| `#author/alice/page/2` | All articles by alice, page 2 |
| `#category/expa-2026` | All articles carrying the "expa-2026" year tag, page 1 |
| `#category/expa-2026/page/2` | Same, page 2 |
| `#tag/kometa` | All articles tagged "kometa", page 1 |
| `#tag/kometa/page/2` | Same, page 2 |
| `#category/year-2026` | All articles carrying the "year-2026" year tag, page 1 |
| `#category/year-2026/page/2` | Same, page 2 |
| `#tag/guides` | All articles tagged "guides", page 1 |
| `#tag/guides/page/2` | Same, page 2 |
| `#post/alice/my-article` | Single article at `cmjs/articles/alice/my-article~DDMMYYYY~tags.md` |
Author names and category tags on each card and article are clickable links to their respective filter views. A search box in the header navigates to `#search/query`.
+24 -101
View File
@@ -2,162 +2,85 @@
"schemaVersion": 1,
"tags": [
{
"id": "analogova-astrofotografie",
"label": "Analogová astrofotografie",
"id": "guides",
"label": "Guides",
"description": "",
"aliases": [],
"active": true
},
{
"id": "astromakers",
"label": "Astromakers",
"id": "news",
"label": "News",
"description": "",
"aliases": [],
"active": true
},
{
"id": "ccd",
"label": "CCD",
"id": "tutorials",
"label": "Tutorials",
"description": "",
"aliases": [],
"active": true
},
{
"id": "deep-sky",
"label": "Deep-sky",
"id": "announcements",
"label": "Announcements",
"description": "",
"aliases": [],
"active": true
},
{
"id": "digitalni-astrofotografie",
"label": "Digitální astrofotografie",
"id": "interviews",
"label": "Interviews",
"description": "",
"aliases": [],
"active": true
},
{
"id": "existencialni-otazky",
"label": "Existenciální otázky",
"id": "opinion",
"label": "Opinion",
"description": "",
"aliases": [],
"active": true
},
{
"id": "expedicnici",
"label": "Expedičníci",
"id": "case-studies",
"label": "Case Studies",
"description": "",
"aliases": [],
"active": true
},
{
"id": "experimenty",
"label": "Experimenty",
"id": "community",
"label": "Community",
"description": "",
"aliases": [],
"active": true
},
{
"id": "katalog-og",
"label": "Katalog OG",
"id": "releases",
"label": "Releases",
"description": "",
"aliases": [],
"active": true
},
{
"id": "kometa",
"label": "Kometa",
"id": "behind-the-scenes",
"label": "Behind the Scenes",
"description": "",
"aliases": [],
"active": true
},
{
"id": "meziplanetarni-hmota",
"label": "Meziplanetární hmota",
"id": "faq",
"label": "FAQ",
"description": "",
"aliases": [],
"active": true
},
{
"id": "mesic",
"label": "Měsíc",
"description": "",
"aliases": [],
"active": true
},
{
"id": "nezarazene",
"label": "Nezařazené",
"description": "",
"aliases": [],
"active": true
},
{
"id": "nove-objevy",
"label": "Nové objevy",
"description": "",
"aliases": [],
"active": true
},
{
"id": "po-expedici",
"label": "Po expedici",
"description": "",
"aliases": [],
"active": true
},
{
"id": "projekty",
"label": "Projekty",
"description": "",
"aliases": [],
"active": true
},
{
"id": "radioastronomie",
"label": "Rádioastronomie",
"description": "",
"aliases": [],
"active": true
},
{
"id": "scigifoto",
"label": "Scigifoto",
"description": "",
"aliases": [],
"active": true
},
{
"id": "skupiny",
"label": "Skupiny",
"description": "",
"aliases": [],
"active": true
},
{
"id": "spolecenske-aktivity",
"label": "Společenské aktivity",
"description": "",
"aliases": [],
"active": true
},
{
"id": "vedouci",
"label": "Vedoucí",
"description": "",
"aliases": [],
"active": true
},
{
"id": "vysledky-pozorovani",
"label": "Výsledky pozorování",
"description": "",
"aliases": [],
"active": true
},
{
"id": "zivot-na-expedici",
"label": "Život na expedici",
"id": "resources",
"label": "Resources",
"description": "",
"aliases": [],
"active": true
+3 -3
View File
@@ -197,8 +197,8 @@ function buildStub(author, fileName, mtime, authorRoot) {
const { slug, date, tags, draft } = parsed;
const path = `${authorRoot}${encodeURIComponent(fileName)}`;
// "Category" isn't a separate concept anymore — it's just whichever tag
// id (if any) looks like an expedition year.
const category = tags.find(id => /^expa-20\d{2}$/i.test(id)) || null;
// id (if any) looks like a `year-YYYY` id.
const category = tags.find(id => /^year-20\d{2}$/i.test(id)) || null;
return {
author,
@@ -207,7 +207,7 @@ function buildStub(author, fileName, mtime, authorRoot) {
path,
mtime,
category,
expa: category ? Number(category.replace(/^expa-/i, '')) : null,
categoryYear: category ? Number(category.replace(/^year-/i, '')) : null,
date,
tags,
published: !draft,
+1 -1
View File
@@ -10,7 +10,7 @@
// 3 segments, first = "post" → single article (#post/alice/my-article)
// 4 segments, first = "author", third = "page" → author filter, page N (#author/alice/page/2)
// 4 segments, first = "category", third = "page" → category filter, page N (#category/linux/page/2)
// 4 segments, first = "tag", third = "page" → tag filter, page N (#tag/kometa/page/2)
// 4 segments, first = "tag", third = "page" → tag filter, page N (#tag/guides/page/2)
import { MARKED_URL } from './config.js';
import { loadTemplates } from './templates.js';
+6 -6
View File
@@ -47,7 +47,7 @@ function filterLabel(filter) {
if (!filter) return '';
if (filter.type === 'author') return t('filterAuthor', { value: filter.value });
if (filter.type === 'tag') return t('filterTag', { value: tagLabels.get(filter.value) || filter.value });
return t('filterYear', { value: filter.value.replace(/^expa-/i, 'Expa ') });
return t('filterYear', { value: filter.value.replace(/^year-/i, '') });
}
function pageHash(page, filter) {
@@ -116,7 +116,7 @@ function renderFilterNavigation(articles, activeFilter = null) {
years.innerHTML = '';
const yearCounts = new Map();
articles.forEach(article => {
const year = Number(article.expa);
const year = Number(article.categoryYear);
if (year) yearCounts.set(year, (yearCounts.get(year) || 0) + 1);
});
const yearList = document.createElement('div');
@@ -124,8 +124,8 @@ function renderFilterNavigation(articles, activeFilter = null) {
yearList.appendChild(allArticlesLink());
for (let year = 2026; year >= 2009; year -= 1) {
const count = yearCounts.get(year) || 0;
const active = activeFilter?.type === 'category' && activeFilter.value.toLowerCase() === `expa-${year}`;
yearList.appendChild(renderFilterLink(t('yearLabel', { year }), `#category/expa-${year}`, count, active));
const active = activeFilter?.type === 'category' && activeFilter.value.toLowerCase() === `year-${year}`;
yearList.appendChild(renderFilterLink(t('yearLabel', { year }), `#category/year-${year}`, count, active));
}
years.appendChild(yearList);
}
@@ -178,7 +178,7 @@ function renderCard(article) {
// A post only counts as a "category" when one of its directory-name
// tags happens to name an expedition year — not every article has one.
setField(field('category'), article.category, (category, value) => {
category.textContent = t('yearLabel', { year: article.expa });
category.textContent = t('yearLabel', { year: article.categoryYear });
category.href = `#category/${encodeURIComponent(value)}`;
});
renderTags(field('tags'), article.tags);
@@ -503,7 +503,7 @@ export async function loadArticle(hash) {
if (coverUrl) { image.src = coverUrl; image.alt = meta.title || article.slug; } else image.closest('figure')?.remove();
const author = field('author'); author.textContent = meta.authorName || article.author; author.href = `#author/${encodeURIComponent(article.author)}`;
setField(field('category'), article.category, (category, value) => {
category.textContent = t('yearLabel', { year: article.expa });
category.textContent = t('yearLabel', { year: article.categoryYear });
category.href = `#category/${encodeURIComponent(value)}`;
});
renderTags(field('tags'), article.tags);
+2 -2
View File
@@ -1,5 +1,5 @@
{
"siteName": "Expediční blogy",
"siteName": "Ukázkový blog",
"appStartError": "Blog se nepodařilo spustit: {message}",
"articleNotFound": "Článek nebyl nalezen",
@@ -7,7 +7,7 @@
"filterTag": "Štítek: {value}",
"filterYear": "Ročník: {value}",
"filterAllArticles": "Všechny články",
"yearLabel": "Expa {year}",
"yearLabel": "Ročník {year}",
"paginationLabel": "Stránkování",
"paginationPrevious": "Předchozí",
+2 -2
View File
@@ -1,5 +1,5 @@
{
"siteName": "Expedition Blogs",
"siteName": "Example Blog",
"appStartError": "Failed to start the blog: {message}",
"articleNotFound": "Article not found",
@@ -7,7 +7,7 @@
"filterTag": "Tag: {value}",
"filterYear": "Year: {value}",
"filterAllArticles": "All articles",
"yearLabel": "Expa {year}",
"yearLabel": "Year {year}",
"paginationLabel": "Pagination",
"paginationPrevious": "Previous",
+2 -2
View File
@@ -1,5 +1,5 @@
{
"siteName": "Expedičné blogy",
"siteName": "Ukážkový blog",
"appStartError": "Blog sa nepodarilo spustiť: {message}",
"articleNotFound": "Článok nebol nájdený",
@@ -7,7 +7,7 @@
"filterTag": "Štítok: {value}",
"filterYear": "Ročník: {value}",
"filterAllArticles": "Všetky články",
"yearLabel": "Expa {year}",
"yearLabel": "Ročník {year}",
"paginationLabel": "Stránkovanie",
"paginationPrevious": "Predchádzajúca",
+2 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1">
<title>Expedition Blogs</title>
<title>Example Blog</title>
<link rel="stylesheet" href="css/reset.css">
<link rel="stylesheet" href="css/header.css">
<link rel="stylesheet" href="css/layout.css">
@@ -17,7 +17,7 @@
<body class="blog-page">
<header>
<!-- Site title always links back to the article list -->
<a href="#" id="site-title">Expedition Blogs</a>
<a href="#" id="site-title">Example Blog</a>
<!-- Search form — submits to #search/query via router.js -->
<form id="search-form" role="search">