From cb5fa1bed1f4703ebea2cbd2c89707ffd43c7074 Mon Sep 17 00:00:00 2001 From: Patrick Buckley Date: Fri, 8 May 2026 14:26:08 -0700 Subject: [PATCH] =?UTF-8?q?fix(skills):=20unlock=20UX=20=E2=80=94=20lock?= =?UTF-8?q?=20icon=20top-right,=20save=20reset,=20confirm=20z-index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three issues from manual smoke-testing the unlock flow: 1. Confirm dialog rendered behind the edit-skill modal. Both overlays sat at z-index 600, and confirm-overlay is earlier in the DOM than edit-template-overlay — so DOM order put the parent modal on top of its own confirm. Bumped confirm-overlay to 650 (still below toasts at 700) since confirm dialogs are launched FROM other overlays and need to sit above them. 2. Save button stayed disabled (or non-functional) after unlock. submitEditTemplate disables etm-submit on click and re-enables in .finally, but a stale disabled=true survives the mutate-in-place re-render that runs after unlock. Always reset submitBtn.disabled = false in showEditTemplateModal so the re-render path can never inherit a stuck disabled state. 3. UX redesign — moved the unlock affordance from a "Customize…" button at the bottom of the footer to a 🔒 icon button at the top-right of the modal. The lock glyph is the universal "this is locked, click to unlock" affordance and reads more clearly than a footer button next to Cancel/Save. font-variant-emoji: text keeps it monochrome on browsers that support it (instrument-panel aesthetic) with graceful fallback to coloured emoji elsewhere. admin-modal-skill h2 reserves padding-right so a long title can never collide with the absolute-positioned button. Cleanup: removed the now-unused .modal-secondary and .modal-buttons-spacer rules; the bottom etm-unlock button + flex spacer are gone from the modal footer. --- turnstone/console/static/governance.js | 33 ++++++++------- turnstone/console/static/index.html | 20 +++++---- turnstone/console/static/style.css | 57 ++++++++++++++++++-------- 3 files changed, 69 insertions(+), 41 deletions(-) diff --git a/turnstone/console/static/governance.js b/turnstone/console/static/governance.js index ce68a12a..976a14cc 100644 --- a/turnstone/console/static/governance.js +++ b/turnstone/console/static/governance.js @@ -1446,16 +1446,23 @@ function showEditTemplateModal(tmplId) { originBadge.style.display = "none"; } } - var unlockBtn = document.getElementById("etm-unlock"); - if (unlockBtn) { - unlockBtn.style.display = isReadonly ? "" : "none"; - unlockBtn.dataset.skillId = tmplId; - unlockBtn.dataset.skillName = tmpl.name || ""; + var lockBtn = document.getElementById("etm-lock-btn"); + if (lockBtn) { + // Inline-flex (not "") so display: none doesn't bleed into a + // browser-default block reflow on re-show. + lockBtn.style.display = isReadonly ? "inline-flex" : "none"; + lockBtn.dataset.skillId = tmplId; + lockBtn.dataset.skillName = tmpl.name || ""; } var submitBtn = document.getElementById("etm-submit"); if (submitBtn) { submitBtn.style.display = ""; submitBtn.textContent = isReadonly ? "Save Config" : "Save"; + // Always reset to enabled — submitEditTemplate disables this on click + // and re-enables in .finally, but a stale disabled=true survives a + // mutate-in-place re-render (e.g. after unlock) and would leave the + // button non-functional otherwise. + submitBtn.disabled = false; } // Spec/content fields: locked for installed skills (preserve source fidelity). // Point screen readers at the origin badge so the "why is this disabled?" @@ -1538,7 +1545,7 @@ function hideEditTemplateModal() { } function unlockSkill() { - var btn = document.getElementById("etm-unlock"); + var btn = document.getElementById("etm-lock-btn"); if (!btn) return; var skillId = btn.dataset.skillId; var skillName = btn.dataset.skillName || "this skill"; @@ -1557,11 +1564,8 @@ function unlockSkill() { } function _performUnlockSkill(skillId) { - var btn = document.getElementById("etm-unlock"); - if (btn) { - btn.disabled = true; - btn.textContent = "Customizing…"; - } + var btn = document.getElementById("etm-lock-btn"); + if (btn) btn.disabled = true; authFetch("/v1/api/admin/skills/" + skillId + "/unlock", { method: "POST", }) @@ -1585,10 +1589,9 @@ function _performUnlockSkill(skillId) { showToast(e.message || "Unlock failed"); }) .finally(function () { - if (btn) { - btn.disabled = false; - btn.textContent = "Customize…"; - } + // Re-enable in case the re-render didn't happen (error path); the + // success path hides the button anyway via showEditTemplateModal. + if (btn) btn.disabled = false; }); } diff --git a/turnstone/console/static/index.html b/turnstone/console/static/index.html index 6717bf84..fcd67ee0 100644 --- a/turnstone/console/static/index.html +++ b/turnstone/console/static/index.html @@ -3298,6 +3298,17 @@ class="admin-modal admin-modal-wide admin-modal-skill" >

Edit Skill

+
Cancel - -