diff --git a/build.sh b/build.sh index 1961b61..42affb2 100755 --- a/build.sh +++ b/build.sh @@ -331,52 +331,45 @@ build_releases_index() { Changes every download link below. - -
- Or pick a channel: - - - -
-

Path A — Self-host the server

@@ -392,15 +385,19 @@ PICKER_END fi if [ "$_zs_published" = "1" ]; then + # Default href is the channel-mirror URL (zddc-server_stable_) + # 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 ' \n' printf ' \n' printf ' Download for Linux (x86_64)\n' printf ' \n' - printf ' zddc-server_v%s_linux-amd64\n' "$_latest" + printf ' zddc-server_stable_linux-amd64\n' printf '
\n' printf ' Other platforms:\n' @@ -412,8 +409,8 @@ PICKER_END _label="${_entry#*|}" _suffix="" case "$_plat" in *windows*) _suffix=".exe" ;; esac - printf ' %s\n' \ - "$_plat" "$_latest" "$_plat" "$_suffix" "$_label" + printf ' %s\n' \ + "$_plat" "$_plat" "$_suffix" "$_label" done printf '
\n' @@ -453,7 +450,9 @@ PATH_B_OPEN _rest="${_entry#*|}" _name="${_rest%%|*}" _desc="${_rest#*|}" - printf ' \n' "$_t" "$_t" "$_latest" + # Default href is the stable-channel mirror; the dropdown + # rewires these per selection. + printf ' \n' "$_t" "$_t" printf ' %s\n' "$_name" printf ' %s\n' "$_desc" printf ' Download →\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__ and + // per-version are zddc-server_v_. + 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 (`_stable.html`, `zddc-server_stable_`) 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". 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

Path A — Self-host the server

@@ -74,18 +65,18 @@
Download for Linux (x86_64) - zddc-server_v0.0.8_linux-amd64 + zddc-server_stable_linux-amd64

After download: chmod +x the file, set ZDDC_ROOT=/path/to/archive, run. @@ -98,27 +89,27 @@

Path B — Standalone tools

Every tool is a single self-contained HTML file. Open it locally and point it at a folder on your disk — 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.

- + Archive Browser Browse and download from a ZDDC archive. Download → - + Transmittal Creator Build, sign, and verify transmittal packages. Download → - + Classifier Rename loose files to ZDDC convention. Download → - + Markdown Editor Edit project markdown files in place. Download → - + Landing Project picker for multi-project servers. Download → @@ -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__ and + // per-version are zddc-server_v_. + 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 (`_stable.html`, `zddc-server_stable_`) 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". 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
diff --git a/zddc/internal/apps/embedded/mdedit.html b/zddc/internal/apps/embedded/mdedit.html index 03432a0..7e18b00 100644 --- a/zddc/internal/apps/embedded/mdedit.html +++ b/zddc/internal/apps/embedded/mdedit.html @@ -1774,7 +1774,7 @@ body.help-open .app-header {
ZDDC Markdown - v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty + v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty
diff --git a/zddc/internal/apps/embedded/transmittal.html b/zddc/internal/apps/embedded/transmittal.html index 4118210..5c87421 100644 --- a/zddc/internal/apps/embedded/transmittal.html +++ b/zddc/internal/apps/embedded/transmittal.html @@ -2210,7 +2210,7 @@ dialog.modal--narrow {
ZDDC Transmittal - v0.0.9-alpha · 2026-05-02 02:07:03 · 17b0a4d-dirty + v0.0.9-alpha · 2026-05-02 02:15:56 · 8c2e65e-dirty
diff --git a/zddc/internal/apps/embedded/versions.txt b/zddc/internal/apps/embedded/versions.txt index 141e567..5a2a5c2 100644 --- a/zddc/internal/apps/embedded/versions.txt +++ b/zddc/internal/apps/embedded/versions.txt @@ -1,6 +1,6 @@ # Generated by build.sh — do not edit. One = 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