Your archive's tools are yours. The server is convenience; deletion of the server doesn't break your archive — every per-version download above is a real, immutable static file. Save what you trust.
+
+
+
Build your .zddc apps: block
+
Pick a channel or pinned version for each tool. The YAML on the right updates as you go — copy it into a .zddc file at the level of your archive where you want the config to apply. Closer-to-leaf wins, so a project subdirectory can override what its parent says.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Tip: leave a tool on stable if you don't have a reason to pin it. Pin specific versions when you depend on a behavior in that release. The server's resolution order is: a real file in your archive → .zddc apps: entry (closer-to-leaf wins) → embedded fallback.
+
+
Channels
@@ -695,6 +733,63 @@ PIN_MID
// non-linux client, that needs to flip to the detected platform).
rewire(picker.value);
})();
+
+ // ── Apps: composer ─────────────────────────────────────────────
+ // Picks per-app channels/versions from dropdowns, emits an equivalent
+ // .zddc apps: YAML block in a textarea, with a Copy button. Reuses
+ // the version-picker's option list as the source of truth for what
+ // values are valid (avoids duplicating the version data into JS).
+ (function() {
+ var picker = document.getElementById('version-picker');
+ var textarea = document.getElementById('composer-yaml');
+ var copyBtn = document.getElementById('composer-copy');
+ var status = document.getElementById('composer-status');
+ if (!picker || !textarea) return;
+ var selects = document.querySelectorAll('.composer-select');
+ if (selects.length === 0) return;
+
+ // Clone version-picker's option list (channels + pinned versions)
+ // into each per-app composer-select. innerHTML round-trips fine
+ // because every option is a static
+ // produced by the build script — no inline JS, no event handlers.
+ var optionsHTML = picker.innerHTML;
+ selects.forEach(function(sel) {
+ sel.innerHTML = optionsHTML;
+ sel.value = 'stable'; // default per-app
+ });
+
+ function rebuild() {
+ var lines = ['apps:'];
+ selects.forEach(function(sel) {
+ lines.push(' ' + sel.dataset.app + ': ' + sel.value);
+ });
+ textarea.value = lines.join('\n') + '\n';
+ }
+
+ selects.forEach(function(sel) {
+ sel.addEventListener('change', rebuild);
+ });
+ rebuild();
+
+ if (copyBtn) {
+ copyBtn.addEventListener('click', function() {
+ textarea.select();
+ try {
+ if (navigator.clipboard && navigator.clipboard.writeText) {
+ navigator.clipboard.writeText(textarea.value);
+ } else {
+ document.execCommand('copy');
+ }
+ if (status) {
+ status.textContent = 'Copied to clipboard';
+ setTimeout(function() { status.textContent = ''; }, 2000);
+ }
+ } catch (e) {
+ if (status) status.textContent = 'Copy failed — select the text and copy manually';
+ }
+ });
+ }
+ })();