feat(releases): channel options in version dropdown, drop chip pills

Restructures the version picker on the releases page so channel
mirrors are first-class, selectable options:

  Channels (mutable URLs)
    stable — currently v0.0.8        ← default selection
    beta — tracks stable
    alpha — tracks stable
  Pinned versions (immutable URLs)
    v0.0.8
    v0.0.2
    v0.0.1

Picking "stable" now rewires every download link on the page to the
*_stable.html (HTML tools) or zddc-server_stable_<plat> (binary)
channel-mirror URL — the kind a user wants to copy + bookmark for a
"latest stable" reference. Same for beta and alpha. Picking a pinned
vX.Y.Z still rewires to immutable per-version URLs.

Removed the separate "Or pick a channel" chip-pill row that previously
sat under the picker. The dropdown is now the single control; chips
duplicated functionality and added visual noise. The .channel-chips
CSS rules in website/css/style.css come out with them.

Static defaults (without JS) now use the stable channel mirror URLs
too, so both copy-from-source and JS-rewire produce the same outcome
for users who want stable. The page works end-to-end with JS off.

Embedded snapshots refreshed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
ZDDC 2026-05-01 21:19:59 -05:00
parent 8c2e65e4a2
commit d688e20dad
10 changed files with 124 additions and 219 deletions

154
build.sh
View file

@ -331,52 +331,45 @@ build_releases_index() {
<select id="version-picker">
HEAD
# Stable versions, latest first
_first=1
printf '%s\n' "$_all_versions" | while read -r _v; do
[ -n "$_v" ] || continue
_label="v${_v}"
if [ "$_first" = "1" ]; then
_label="${_label} (current stable)"
_first=0
fi
printf ' <option value="v%s"%s>%s</option>\n' "$_v" "$( [ "$_v" = "$_latest" ] && printf ' selected' )" "$_label"
done
# Pre-release channels — always shown, so the alpha/beta URLs are
# discoverable without knowing the URL pattern. Label tells the
# truth about the current state: "active dev" when the channel has
# its own bytes, "tracks stable" when it's just a cascade symlink.
printf ' <optgroup label="Pre-release channels">\n'
# Channels — selectable directly so users can copy the channel-
# mirror URLs (e.g. archive_stable.html) for bookmarks. stable is
# the default. The label tells the truth about the channel's
# current state: when stable is set, show which version it points
# at; when alpha/beta is just a symlink to stable, mark as
# "tracks stable" so picking it isn't surprising.
printf ' <optgroup label="Channels (mutable URLs)">\n'
if [ -n "$_latest" ] && [ "$_latest" != "0.0.0" ]; then
printf ' <option value="stable" selected>stable — currently v%s</option>\n' "$_latest"
else
printf ' <option value="stable" selected>stable</option>\n'
fi
if [ "$_beta_active" = "1" ]; then
printf ' <option value="beta">beta — general testing</option>\n'
else
printf ' <option value="beta">beta — currently tracks stable</option>\n'
printf ' <option value="beta">beta — tracks stable</option>\n'
fi
if [ "$_alpha_active" = "1" ]; then
printf ' <option value="alpha">alpha — active dev</option>\n'
else
printf ' <option value="alpha">alpha — currently tracks stable</option>\n'
printf ' <option value="alpha">alpha — tracks stable</option>\n'
fi
printf ' </optgroup>\n'
# Pinned per-version, latest first. These are the immutable URLs
# for reproducibility. No "(current stable)" suffix because the
# stable channel above already covers that.
printf ' <optgroup label="Pinned versions (immutable URLs)">\n'
printf '%s\n' "$_all_versions" | while read -r _v; do
[ -n "$_v" ] || continue
printf ' <option value="v%s">v%s</option>\n' "$_v" "$_v"
done
printf ' </optgroup>\n'
cat <<'PICKER_END'
</select>
<span class="picker-hint">Changes every download link below.</span>
</div>
<!-- Channel quick-pick chips — visible at-a-glance entry points to
alpha and beta even when they cascade to stable. Clicking drives
the version picker (rewires the page) rather than navigating
away. The chip is always live; the cascade rule keeps the URLs
it represents permanently resolvable. -->
<div class="channel-chips" role="group" aria-label="Channel quick pick">
<span class="channel-chips-label">Or pick a channel:</span>
<button type="button" class="channel-chip is-current" data-channel="stable">stable</button>
<button type="button" class="channel-chip" data-channel="beta">beta</button>
<button type="button" class="channel-chip" data-channel="alpha">alpha</button>
</div>
<!-- ───────────── Path A — Self-host the server ───────────── -->
<section class="card" style="background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--spacing-lg) var(--spacing-xl); margin-top: var(--spacing-lg);">
<h2 style="margin-top:0;">Path A — Self-host the server</h2>
@ -392,15 +385,19 @@ PICKER_END
fi
if [ "$_zs_published" = "1" ]; then
# Default href is the channel-mirror URL (zddc-server_stable_<plat>)
# because "stable" is the dropdown's selected option. Picking a
# pinned version from the dropdown rewrites these to the
# immutable per-version URL via the IIFE.
printf ' <a class="dl-primary"\n'
printf ' data-tool="zddc-server"\n'
printf ' data-platform="linux-amd64"\n'
printf ' href="zddc-server_v%s_linux-amd64"\n' "$_latest"
printf ' href="zddc-server_stable_linux-amd64"\n'
printf ' id="dl-primary-binary">\n'
printf ' <span class="dl-icon">⬇</span>\n'
printf ' <span>Download <span id="dl-primary-platlabel">for Linux (x86_64)</span></span>\n'
printf ' </a>\n'
printf ' <span class="dl-primary-meta" id="dl-primary-meta">zddc-server_v%s_linux-amd64</span>\n' "$_latest"
printf ' <span class="dl-primary-meta" id="dl-primary-meta">zddc-server_stable_linux-amd64</span>\n'
printf ' <div class="dl-secondary-row" id="dl-others">\n'
printf ' <span>Other platforms:</span>\n'
@ -412,8 +409,8 @@ PICKER_END
_label="${_entry#*|}"
_suffix=""
case "$_plat" in *windows*) _suffix=".exe" ;; esac
printf ' <a data-tool="zddc-server" data-platform="%s" href="zddc-server_v%s_%s%s">%s</a>\n' \
"$_plat" "$_latest" "$_plat" "$_suffix" "$_label"
printf ' <a data-tool="zddc-server" data-platform="%s" href="zddc-server_stable_%s%s">%s</a>\n' \
"$_plat" "$_plat" "$_suffix" "$_label"
done
printf ' </div>\n'
@ -453,7 +450,9 @@ PATH_B_OPEN
_rest="${_entry#*|}"
_name="${_rest%%|*}"
_desc="${_rest#*|}"
printf ' <a class="tool-card" data-tool="%s" href="%s_v%s.html">\n' "$_t" "$_t" "$_latest"
# Default href is the stable-channel mirror; the dropdown
# rewires these per selection.
printf ' <a class="tool-card" data-tool="%s" href="%s_stable.html">\n' "$_t" "$_t"
printf ' <span class="tool-card__title">%s</span>\n' "$_name"
printf ' <span class="tool-card__desc">%s</span>\n' "$_desc"
printf ' <span class="tool-card__link">Download &rarr;</span>\n'
@ -546,90 +545,65 @@ PIN_MID
var primaryMeta = document.getElementById('dl-primary-meta');
var others = document.getElementById('dl-others');
function platBinaryName(version, plat) {
var suf = (plat.indexOf('windows') === 0) ? '.exe' : '';
return 'zddc-server_' + version + '_' + plat + suf;
function isChannel(v) {
return v === 'stable' || v === 'beta' || v === 'alpha';
}
function htmlAssetName(tool, version) {
return tool + '_' + version + '.html';
function platBinaryName(slug, plat) {
// slug is a channel name ("stable") or a pinned version ("v0.0.8").
// The on-disk name uses the slug as-is in both cases since the
// channel-mirror filenames are zddc-server_<channel>_<plat> and
// per-version are zddc-server_v<X.Y.Z>_<plat>.
var suf = (plat.indexOf('windows') === 0) ? '.exe' : '';
return 'zddc-server_' + slug + '_' + plat + suf;
}
function htmlAssetName(tool, slug) {
return tool + '_' + slug + '.html';
}
// Update the primary button to the detected platform (if different
// from default). Hide the matching link in the secondary row to
// avoid duplication.
// Promote the detected platform to the primary CTA. The secondary
// row keeps all four; the matching one is hidden to avoid showing
// the same download twice.
if (primary) {
var initialVer = primary.getAttribute('href').match(/_v[\d.]+_/);
initialVer = initialVer ? initialVer[0].slice(2, -1) : null;
primary.dataset.platform = detected;
if (initialVer) {
primary.href = platBinaryName('v' + initialVer, detected);
if (primaryMeta) primaryMeta.textContent = platBinaryName('v' + initialVer, detected);
}
if (primaryLabel) primaryLabel.textContent = 'for ' + platLabel;
}
// Hide the duplicate in "Other platforms" row.
if (others) {
others.querySelectorAll('a[data-platform="' + detected + '"]').forEach(function(a) {
a.style.display = 'none';
});
}
// Wire the version picker + channel chips. Both drive the same
// rewire function. Selecting a per-version stable from the picker
// also updates the chip set: stable becomes active, beta/alpha lose
// their is-current marker.
// Single source of truth: the dropdown's current value drives every
// download link's href. Static markup ships with the stable-channel
// mirror (`<tool>_stable.html`, `zddc-server_stable_<plat>`) so the
// page works fully without JS — the JS just keeps things in sync
// when the user picks a different channel or pins a version.
var picker = document.getElementById('version-picker');
if (!picker) return;
var chips = document.querySelectorAll('.channel-chip');
function rewire(v) {
// v is "vX.Y.Z" or "alpha" / "beta"
function rewire(slug) {
// slug ∈ {"stable", "beta", "alpha"} | "v<X.Y.Z>". Every link with
// a data-tool attribute is a download URL the dropdown owns.
document.querySelectorAll('[data-tool]').forEach(function(a) {
var tool = a.dataset.tool;
var plat = a.dataset.platform || '';
if (tool === 'zddc-server') {
if (plat) {
a.href = (v === 'alpha' || v === 'beta')
? 'zddc-server_' + v + '_' + plat + (plat.indexOf('windows') === 0 ? '.exe' : '')
: platBinaryName(v, plat);
} else {
a.href = 'zddc-server_' + v + '.html';
}
a.href = plat ? platBinaryName(slug, plat) : ('zddc-server_' + slug + '.html');
} else {
a.href = htmlAssetName(tool, v);
a.href = htmlAssetName(tool, slug);
}
});
if (primary && primaryMeta) {
primaryMeta.textContent = primary.getAttribute('href');
}
// Reflect channel-vs-version in the chip group. Per-version stable
// selections highlight the "stable" chip (since per-version files
// are stable releases).
var channel = (v === 'alpha' || v === 'beta') ? v : 'stable';
chips.forEach(function(c) {
c.classList.toggle('is-current', c.dataset.channel === channel);
});
}
picker.addEventListener('change', function() { rewire(picker.value); });
// Channel chips: clicking sets the picker's value to the channel
// (or to the latest stable when stable is clicked) and fires a
// change event so rewire() runs through the existing flow.
chips.forEach(function(c) {
c.addEventListener('click', function() {
var ch = c.dataset.channel;
if (ch === 'stable') {
// Latest stable is the first non-channel <option> in the picker.
var firstStable = picker.querySelector('option[value^="v"]');
if (firstStable) picker.value = firstStable.value;
} else {
picker.value = ch;
}
rewire(picker.value);
});
});
// Run rewire once on load to apply the platform-detection result
// (the static href for the primary button is for linux-amd64; on a
// non-linux client, that needs to flip to the detected platform).
rewire(picker.value);
})();
</script>
</body>

View file

@ -1774,7 +1774,7 @@ body.help-open .app-header {
</svg>
<div class="header-title-group">
<span class="app-header__title">ZDDC Markdown</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty</span></span>
</div>
<button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button>
</div>

View file

@ -1231,41 +1231,6 @@ html[data-theme="light"] {
font-size: 0.95rem;
}
/* Channel quick-pick chips next to the version picker. Visible at-a-
glance entry points to alpha and beta channels even when those
currently cascade to stable discoverability matters more than
minimalism here. Clicking drives the version picker. */
.channel-chips {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: var(--spacing-sm);
margin: 0 0 var(--spacing-lg);
font-size: 0.92rem;
}
.channel-chips-label { color: var(--color-text-muted); margin-right: var(--spacing-xs); }
.channel-chip {
font: inherit;
padding: 0.25rem 0.75rem;
background: var(--color-bg);
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: 999px;
cursor: pointer;
transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.channel-chip:hover { background: var(--color-accent-soft); border-color: var(--color-accent); color: var(--color-accent); }
.channel-chip.is-current {
background: var(--color-accent);
color: #fff;
border-color: var(--color-accent);
font-weight: 600;
}
.channel-chip[data-channel="alpha"]:not(.is-current),
.channel-chip[data-channel="beta"]:not(.is-current) {
color: var(--color-text-muted);
}
/* Channel explainer (bottom of the page) */
.channel-explainer { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--spacing-md); margin-top: var(--spacing-md); }
.channel-explainer > div {

View file

@ -44,29 +44,20 @@
<div class="version-picker-bar">
<label for="version-picker">Showing</label>
<select id="version-picker">
<option value="v0.0.8" selected>v0.0.8 (current stable)</option>
<option value="v0.0.2">v0.0.2</option>
<option value="v0.0.1">v0.0.1</option>
<optgroup label="Pre-release channels">
<option value="beta">beta — currently tracks stable</option>
<option value="alpha">alpha — currently tracks stable</option>
<optgroup label="Channels (mutable URLs)">
<option value="stable" selected>stable — currently v0.0.8</option>
<option value="beta">beta — tracks stable</option>
<option value="alpha">alpha — tracks stable</option>
</optgroup>
<optgroup label="Pinned versions (immutable URLs)">
<option value="v0.0.8">v0.0.8</option>
<option value="v0.0.2">v0.0.2</option>
<option value="v0.0.1">v0.0.1</option>
</optgroup>
</select>
<span class="picker-hint">Changes every download link below.</span>
</div>
<!-- Channel quick-pick chips — visible at-a-glance entry points to
alpha and beta even when they cascade to stable. Clicking drives
the version picker (rewires the page) rather than navigating
away. The chip is always live; the cascade rule keeps the URLs
it represents permanently resolvable. -->
<div class="channel-chips" role="group" aria-label="Channel quick pick">
<span class="channel-chips-label">Or pick a channel:</span>
<button type="button" class="channel-chip is-current" data-channel="stable">stable</button>
<button type="button" class="channel-chip" data-channel="beta">beta</button>
<button type="button" class="channel-chip" data-channel="alpha">alpha</button>
</div>
<!-- ───────────── Path A — Self-host the server ───────────── -->
<section class="card" style="background: var(--color-bg-subtle); border: 1px solid var(--color-border); border-radius: var(--radius-md); padding: var(--spacing-lg) var(--spacing-xl); margin-top: var(--spacing-lg);">
<h2 style="margin-top:0;">Path A — Self-host the server</h2>
@ -74,18 +65,18 @@
<a class="dl-primary"
data-tool="zddc-server"
data-platform="linux-amd64"
href="zddc-server_v0.0.8_linux-amd64"
href="zddc-server_stable_linux-amd64"
id="dl-primary-binary">
<span class="dl-icon"></span>
<span>Download <span id="dl-primary-platlabel">for Linux (x86_64)</span></span>
</a>
<span class="dl-primary-meta" id="dl-primary-meta">zddc-server_v0.0.8_linux-amd64</span>
<span class="dl-primary-meta" id="dl-primary-meta">zddc-server_stable_linux-amd64</span>
<div class="dl-secondary-row" id="dl-others">
<span>Other platforms:</span>
<a data-tool="zddc-server" data-platform="linux-amd64" href="zddc-server_v0.0.8_linux-amd64">Linux (x86_64)</a>
<a data-tool="zddc-server" data-platform="darwin-amd64" href="zddc-server_v0.0.8_darwin-amd64">macOS (Intel)</a>
<a data-tool="zddc-server" data-platform="darwin-arm64" href="zddc-server_v0.0.8_darwin-arm64">macOS (Apple Silicon)</a>
<a data-tool="zddc-server" data-platform="windows-amd64" href="zddc-server_v0.0.8_windows-amd64.exe">Windows (x86_64)</a>
<a data-tool="zddc-server" data-platform="linux-amd64" href="zddc-server_stable_linux-amd64">Linux (x86_64)</a>
<a data-tool="zddc-server" data-platform="darwin-amd64" href="zddc-server_stable_darwin-amd64">macOS (Intel)</a>
<a data-tool="zddc-server" data-platform="darwin-arm64" href="zddc-server_stable_darwin-arm64">macOS (Apple Silicon)</a>
<a data-tool="zddc-server" data-platform="windows-amd64" href="zddc-server_stable_windows-amd64.exe">Windows (x86_64)</a>
</div>
<p style="margin-top: var(--spacing-md); font-size: 0.92rem; color: var(--color-text-muted);">
After download: <code>chmod +x</code> the file, set <code>ZDDC_ROOT=/path/to/archive</code>, run.
@ -98,27 +89,27 @@
<h2 style="margin-top:0;">Path B — Standalone tools</h2>
<p>Every tool is a single self-contained HTML file. <strong>Open it locally and point it at a folder on your disk</strong> — no install, no server, no account. Same on-disk layout the server uses. Use one tool, use all five, mix and match — there is no orchestration to set up.</p>
<div class="grid-4" style="margin-top: var(--spacing-md);">
<a class="tool-card" data-tool="archive" href="archive_v0.0.8.html">
<a class="tool-card" data-tool="archive" href="archive_stable.html">
<span class="tool-card__title">Archive Browser</span>
<span class="tool-card__desc">Browse and download from a ZDDC archive.</span>
<span class="tool-card__link">Download &rarr;</span>
</a>
<a class="tool-card" data-tool="transmittal" href="transmittal_v0.0.8.html">
<a class="tool-card" data-tool="transmittal" href="transmittal_stable.html">
<span class="tool-card__title">Transmittal Creator</span>
<span class="tool-card__desc">Build, sign, and verify transmittal packages.</span>
<span class="tool-card__link">Download &rarr;</span>
</a>
<a class="tool-card" data-tool="classifier" href="classifier_v0.0.8.html">
<a class="tool-card" data-tool="classifier" href="classifier_stable.html">
<span class="tool-card__title">Classifier</span>
<span class="tool-card__desc">Rename loose files to ZDDC convention.</span>
<span class="tool-card__link">Download &rarr;</span>
</a>
<a class="tool-card" data-tool="mdedit" href="mdedit_v0.0.8.html">
<a class="tool-card" data-tool="mdedit" href="mdedit_stable.html">
<span class="tool-card__title">Markdown Editor</span>
<span class="tool-card__desc">Edit project markdown files in place.</span>
<span class="tool-card__link">Download &rarr;</span>
</a>
<a class="tool-card" data-tool="landing" href="landing_v0.0.8.html">
<a class="tool-card" data-tool="landing" href="landing_stable.html">
<span class="tool-card__title">Landing</span>
<span class="tool-card__desc">Project picker for multi-project servers.</span>
<span class="tool-card__link">Download &rarr;</span>
@ -203,90 +194,65 @@ apps:
var primaryMeta = document.getElementById('dl-primary-meta');
var others = document.getElementById('dl-others');
function platBinaryName(version, plat) {
var suf = (plat.indexOf('windows') === 0) ? '.exe' : '';
return 'zddc-server_' + version + '_' + plat + suf;
function isChannel(v) {
return v === 'stable' || v === 'beta' || v === 'alpha';
}
function htmlAssetName(tool, version) {
return tool + '_' + version + '.html';
function platBinaryName(slug, plat) {
// slug is a channel name ("stable") or a pinned version ("v0.0.8").
// The on-disk name uses the slug as-is in both cases since the
// channel-mirror filenames are zddc-server_<channel>_<plat> and
// per-version are zddc-server_v<X.Y.Z>_<plat>.
var suf = (plat.indexOf('windows') === 0) ? '.exe' : '';
return 'zddc-server_' + slug + '_' + plat + suf;
}
function htmlAssetName(tool, slug) {
return tool + '_' + slug + '.html';
}
// Update the primary button to the detected platform (if different
// from default). Hide the matching link in the secondary row to
// avoid duplication.
// Promote the detected platform to the primary CTA. The secondary
// row keeps all four; the matching one is hidden to avoid showing
// the same download twice.
if (primary) {
var initialVer = primary.getAttribute('href').match(/_v[\d.]+_/);
initialVer = initialVer ? initialVer[0].slice(2, -1) : null;
primary.dataset.platform = detected;
if (initialVer) {
primary.href = platBinaryName('v' + initialVer, detected);
if (primaryMeta) primaryMeta.textContent = platBinaryName('v' + initialVer, detected);
}
if (primaryLabel) primaryLabel.textContent = 'for ' + platLabel;
}
// Hide the duplicate in "Other platforms" row.
if (others) {
others.querySelectorAll('a[data-platform="' + detected + '"]').forEach(function(a) {
a.style.display = 'none';
});
}
// Wire the version picker + channel chips. Both drive the same
// rewire function. Selecting a per-version stable from the picker
// also updates the chip set: stable becomes active, beta/alpha lose
// their is-current marker.
// Single source of truth: the dropdown's current value drives every
// download link's href. Static markup ships with the stable-channel
// mirror (`<tool>_stable.html`, `zddc-server_stable_<plat>`) so the
// page works fully without JS — the JS just keeps things in sync
// when the user picks a different channel or pins a version.
var picker = document.getElementById('version-picker');
if (!picker) return;
var chips = document.querySelectorAll('.channel-chip');
function rewire(v) {
// v is "vX.Y.Z" or "alpha" / "beta"
function rewire(slug) {
// slug ∈ {"stable", "beta", "alpha"} | "v<X.Y.Z>". Every link with
// a data-tool attribute is a download URL the dropdown owns.
document.querySelectorAll('[data-tool]').forEach(function(a) {
var tool = a.dataset.tool;
var plat = a.dataset.platform || '';
if (tool === 'zddc-server') {
if (plat) {
a.href = (v === 'alpha' || v === 'beta')
? 'zddc-server_' + v + '_' + plat + (plat.indexOf('windows') === 0 ? '.exe' : '')
: platBinaryName(v, plat);
} else {
a.href = 'zddc-server_' + v + '.html';
}
a.href = plat ? platBinaryName(slug, plat) : ('zddc-server_' + slug + '.html');
} else {
a.href = htmlAssetName(tool, v);
a.href = htmlAssetName(tool, slug);
}
});
if (primary && primaryMeta) {
primaryMeta.textContent = primary.getAttribute('href');
}
// Reflect channel-vs-version in the chip group. Per-version stable
// selections highlight the "stable" chip (since per-version files
// are stable releases).
var channel = (v === 'alpha' || v === 'beta') ? v : 'stable';
chips.forEach(function(c) {
c.classList.toggle('is-current', c.dataset.channel === channel);
});
}
picker.addEventListener('change', function() { rewire(picker.value); });
// Channel chips: clicking sets the picker's value to the channel
// (or to the latest stable when stable is clicked) and fires a
// change event so rewire() runs through the existing flow.
chips.forEach(function(c) {
c.addEventListener('click', function() {
var ch = c.dataset.channel;
if (ch === 'stable') {
// Latest stable is the first non-channel <option> in the picker.
var firstStable = picker.querySelector('option[value^="v"]');
if (firstStable) picker.value = firstStable.value;
} else {
picker.value = ch;
}
rewire(picker.value);
});
});
// Run rewire once on load to apply the platform-detection result
// (the static href for the primary button is for linux-amd64; on a
// non-linux client, that needs to flip to the detected platform).
rewire(picker.value);
})();
</script>
</body>

View file

@ -2113,7 +2113,7 @@ td[data-field="trackingNumber"] {
</svg>
<div class="header-title-group">
<span class="app-header__title">ZDDC Archive</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty</span></span>
</div>
<button id="addDirectoryBtn" class="btn btn-primary">Add Local Directory</button>
<button id="refreshHeaderBtn" class="btn btn-secondary hidden" title="Refresh Data" style="font-size:1.1rem;"></button>

View file

@ -1376,7 +1376,7 @@ body.help-open .app-header {
</svg>
<div class="header-title-group">
<span class="app-header__title">ZDDC Classifier</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty</span></span>
</div>
<button id="selectDirectoryBtn" class="btn btn-primary">Select Directory</button>
<button id="refreshBtn" class="btn btn-secondary hidden" title="Refresh and rescan directory">Refresh</button>

View file

@ -866,7 +866,7 @@ body {
</g>
</svg>
<span class="app-header__title">ZDDC Archive</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty</span></span>
</div>
<div class="header-right">
<button id="theme-btn" class="btn btn-secondary" title="Theme: auto (follows OS)" aria-label="Theme: auto (follows OS)"></button>

View file

@ -1774,7 +1774,7 @@ body.help-open .app-header {
</svg>
<div class="header-title-group">
<span class="app-header__title">ZDDC Markdown</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty</span></span>
</div>
<button id="select-directory" class="btn btn-primary" title="Select a Directory">Select Directory</button>
</div>

View file

@ -2210,7 +2210,7 @@ dialog.modal--narrow {
</svg>
<div class="header-title-group">
<span class="app-header__title">ZDDC Transmittal</span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty</span></span>
<span class="build-timestamp"><span style="color:red;font-weight:bold">v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty</span></span>
</div>
<div class="app-header__spacer"></div>
<div class="app-header__icons">

View file

@ -1,6 +1,6 @@
# Generated by build.sh — do not edit. One <app>=<build label> per line.
archive=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
transmittal=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
classifier=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
mdedit=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
landing=v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty
archive=v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty
transmittal=v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty
classifier=v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty
mdedit=v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty
landing=v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty