feat(console): policies pilot + the dialog tier — confirm/token-created go native

Tool policies join the shelf: one pane-scoped editor for create+edit with a
priority-neighbor read-out computed from the loaded policy list ('evaluates
after deny-rm (900) · before default-ask (0)' — policies run highest-first),
so where a priority lands answers itself while typing. Live tool-pattern
match chips are deferred until a cluster tool-registry endpoint exists.

The reusable confirm and the show-once token dialog move onto the
document-modal hatch tier (native showModal): the confirm keeps its
showConfirmModal(title, message, actionLabel, callback) contract for all
14 call sites, gains the danger chrome (red LED/hairline/title + err-filled
action), and deliberately moves autofocus from the action button to Cancel
— Enter on a fresh destructive confirm no longer fires the action. The
token dialog gets the success chrome + show-once callout, with copy wired
to the primary. Nested confirm-over-shelf now stacks via the top layer;
the z-index 650 special case and four more overlay-ID/dispatch entries die.

Also fixes a real war the livepass caught: display rules on form chrome
(label.toggle-switch's inline-flex) defeat the hidden attribute — hatch
containers now enforce [hidden] with display:none !important.
This commit is contained in:
Patrick Buckley
2026-06-09 21:33:09 -07:00
parent da8506e44d
commit f609911b11
5 changed files with 310 additions and 323 deletions
+25 -50
View File
@@ -7,12 +7,9 @@ let _adminChannelUserId = "";
let _lastCreatedToken = "";
let _cuTrapHandler = null;
let _ctTrapHandler = null;
let _tcTrapHandler = null;
let _ccTrapHandler = null;
let _cfTrapHandler = null;
let _adminWatches = [];
let _confirmCallbackFn = null;
let _confirmTriggerEl = null;
// Settings whose choices are populated dynamically from the live model
// alias list, and whose empty-string option renders as "(server default)".
@@ -2554,16 +2551,19 @@ function submitCreateToken() {
function showTokenCreatedModal(token) {
document.getElementById("token-created-value").textContent = token;
document.getElementById("token-created-overlay").style.display = "flex";
_tcTrapHandler = _installTrap("token-created-overlay", "token-created-box");
window.TurnstoneHatch.openDialog(
document.getElementById("token-created-dialog"),
{
onClose: function () {
_lastCreatedToken = "";
},
},
);
}
function hideTokenCreatedModal() {
document.getElementById("token-created-overlay").style.display = "none";
_tcTrapHandler = _removeTrap(_tcTrapHandler);
_lastCreatedToken = "";
const trigger = document.querySelector("#admin-tokens .admin-action-btn");
if (trigger) trigger.focus();
const d = document.getElementById("token-created-dialog");
if (d.open) d.close();
}
function copyCreatedToken() {
@@ -2625,16 +2625,12 @@ function _installTrap(overlayId, boxId, trapRef) {
if (e.target === overlay) {
if (overlayId === "create-user-overlay") hideCreateUserModal();
else if (overlayId === "create-token-overlay") hideCreateTokenModal();
else if (overlayId === "token-created-overlay") hideTokenCreatedModal();
else if (overlayId === "create-channel-overlay")
hideCreateChannelModal();
else if (overlayId === "schedule-runs-overlay") hideScheduleRunsModal();
else if (overlayId === "confirm-overlay") hideConfirmModal();
else if (overlayId === "create-role-overlay") hideCreateRoleModal();
else if (overlayId === "edit-role-overlay") hideEditRoleModal();
else if (overlayId === "user-roles-overlay") hideUserRolesModal();
else if (overlayId === "create-policy-overlay") hideCreatePolicyModal();
else if (overlayId === "edit-policy-overlay") hideEditPolicyModal();
else if (overlayId === "create-template-overlay")
hideCreateTemplateModal();
else if (overlayId === "edit-template-overlay") hideEditTemplateModal();
@@ -2689,12 +2685,6 @@ document.addEventListener("keydown", function (e) {
hideCreateTokenModal();
return;
}
const tc = document.getElementById("token-created-overlay");
if (tc && tc.style.display !== "none") {
e.preventDefault();
hideTokenCreatedModal();
return;
}
const cc = document.getElementById("create-channel-overlay");
if (cc && cc.style.display !== "none") {
e.preventDefault();
@@ -2707,19 +2697,10 @@ document.addEventListener("keydown", function (e) {
hideScheduleRunsModal();
return;
}
const cf = document.getElementById("confirm-overlay");
if (cf && cf.style.display !== "none") {
e.preventDefault();
hideConfirmModal();
return;
}
// Governance modals
const govOverlays = [
["create-role-overlay", hideCreateRoleModal],
["edit-role-overlay", hideEditRoleModal],
["user-roles-overlay", hideUserRolesModal],
["create-policy-overlay", hideCreatePolicyModal],
["edit-policy-overlay", hideEditPolicyModal],
["create-template-overlay", hideCreateTemplateModal],
["edit-template-overlay", hideEditTemplateModal],
["memory-detail-overlay", hideMemoryDetailModal],
@@ -2751,41 +2732,31 @@ document.addEventListener("keydown", function (e) {
function showConfirmModal(title, message, actionLabel, callback) {
_confirmCallbackFn = callback;
_confirmTriggerEl = document.activeElement;
document.getElementById("confirm-title").textContent = title;
document.getElementById("confirm-message").textContent = message;
const btn = document.getElementById("confirm-submit");
btn.textContent = actionLabel;
btn.disabled = false;
const overlay = document.getElementById("confirm-overlay");
overlay.style.display = "flex";
_cfTrapHandler = _installTrap("confirm-overlay", "confirm-box");
setTimeout(function () {
btn.focus();
}, 50);
// Cancel carries the autofocus: Enter on a freshly-opened destructive
// confirm must not fire the action (deliberate change from the legacy
// action-focused behavior).
window.TurnstoneHatch.openDialog(document.getElementById("confirm-dialog"), {
onClose: function () {
_confirmCallbackFn = null;
},
});
}
function hideConfirmModal() {
document.getElementById("confirm-overlay").style.display = "none";
_cfTrapHandler = _removeTrap(_cfTrapHandler);
if (
_confirmTriggerEl &&
_confirmTriggerEl.focus &&
_confirmTriggerEl.isConnected
) {
_confirmTriggerEl.focus();
}
_confirmCallbackFn = null;
_confirmTriggerEl = null;
const d = document.getElementById("confirm-dialog");
if (d.open) d.close();
}
function _confirmCallback() {
const fn = _confirmCallbackFn;
_confirmCallbackFn = null;
const btn = document.getElementById("confirm-submit");
if (btn) btn.disabled = true;
if (fn) fn();
hideConfirmModal();
if (fn) fn();
}
// ---------------------------------------------------------------------------
@@ -6735,6 +6706,10 @@ function _refreshModelSuggestions() {
/* Register listeners once at page load */
(function () {
const confirmBtn = document.getElementById("confirm-submit");
if (confirmBtn) confirmBtn.addEventListener("click", _confirmCallback);
const tcCopy = document.getElementById("tc-copy");
if (tcCopy) tcCopy.addEventListener("click", copyCreatedToken);
const nameEl = document.getElementById("model-name");
const provEl = document.getElementById("model-provider");
const tmEl = document.getElementById("model-thinking-mode");
+146 -102
View File
@@ -31,8 +31,6 @@ let _etmTrapHandler = null; // edit template
let _crTriggerEl = null;
let _erTriggerEl = null;
let _urTriggerEl = null;
let _cpTriggerEl = null;
let _epTriggerEl = null;
let _ctmTriggerEl = null;
let _etmTriggerEl = null;
@@ -904,75 +902,107 @@ function _renderGovPolicies(items) {
});
}
// --- Tool-policy shelf (create + edit) ---
// One pane-scoped shelf; the priority field is annotated with its evaluation
// neighbors from the already-loaded _govPolicies (highest priority first), so
// "where does 200 land?" answers itself while typing. No backend involved.
let _polWired = false;
let _polShelfHandle = null;
function _polChip(text) {
const span = document.createElement("span");
span.className = "match-chip";
span.textContent = text;
return span;
}
function _polRenderNeighbors() {
const strip = document.getElementById("pol-neighbors");
while (strip.firstChild) strip.removeChild(strip.firstChild);
const selfId = document.getElementById("pol-id").value;
const pr = parseInt(document.getElementById("pol-priority").value, 10) || 0;
const others = _govPolicies.filter(function (q) {
return q.policy_id !== selfId;
});
if (!others.length) return;
// Highest priority evaluates first: the policy just BEFORE us is the
// smallest priority above ours; just AFTER us, the largest below.
let before = null;
let after = null;
others.forEach(function (q) {
if (q.priority > pr && (!before || q.priority < before.priority))
before = q;
if (q.priority <= pr && (!after || q.priority > after.priority)) after = q;
});
const count = document.createElement("span");
count.className = "match-count";
count.textContent = "evaluates";
strip.appendChild(count);
if (before) {
const lbl = document.createElement("span");
lbl.className = "match-count";
lbl.textContent = "after";
strip.appendChild(lbl);
strip.appendChild(_polChip(before.name + " (" + before.priority + ")"));
} else {
const first = document.createElement("span");
first.className = "match-count";
first.textContent = "first";
strip.appendChild(first);
}
if (after) {
const lbl2 = document.createElement("span");
lbl2.className = "match-count";
lbl2.textContent = "\u00b7 before";
strip.appendChild(lbl2);
strip.appendChild(_polChip(after.name + " (" + after.priority + ")"));
} else if (before) {
const last = document.createElement("span");
last.className = "match-count";
last.textContent = "\u00b7 last";
strip.appendChild(last);
}
}
function _polWire() {
if (_polWired) return;
_polWired = true;
document
.getElementById("pol-priority")
.addEventListener("input", _polRenderNeighbors);
document
.getElementById("pol-submit")
.addEventListener("click", _submitPolicyShelf);
}
function showCreatePolicyModal() {
_cpTriggerEl = document.activeElement;
const ov = document.getElementById("create-policy-overlay");
ov.style.display = "flex";
document.getElementById("cp-name").value = "";
document.getElementById("cp-pattern").value = "";
document.getElementById("cp-action").value = "ask";
document.getElementById("cp-priority").value = "0";
document.getElementById("create-policy-error").classList.remove("is-visible");
document.getElementById("cp-name").focus();
_cpTrapHandler = _installTrap("create-policy-overlay", "create-policy-box");
_polWire();
document.getElementById("policy-shelf-error").classList.remove("is-visible");
document.getElementById("pol-id").value = "";
document.getElementById("pol-name").value = "";
document.getElementById("pol-pattern").value = "";
document.getElementById("pol-action").value = "ask";
document.getElementById("pol-priority").value = "0";
document.getElementById("pol-enabled").checked = true;
document.getElementById("pol-enabled-row").hidden = true;
document.getElementById("policy-shelf-title").textContent = "New tool policy";
document.getElementById("policy-shelf-tag").textContent = "POL-NEW";
const shelf = document.getElementById("policy-shelf");
shelf.setAttribute("data-kind", "create");
document.getElementById("pol-submit").textContent = "Create";
_polRenderNeighbors();
_polShelfHandle = window.TurnstoneHatch.openShelf(shelf);
document.getElementById("pol-name").focus();
}
function hideCreatePolicyModal() {
document.getElementById("create-policy-overlay").style.display = "none";
_cpTrapHandler = _removeTrap(_cpTrapHandler);
if (_cpTriggerEl && _cpTriggerEl.focus) {
_cpTriggerEl.focus();
}
_cpTriggerEl = null;
}
function submitCreatePolicy() {
const name = document.getElementById("cp-name").value.trim();
const pattern = document.getElementById("cp-pattern").value.trim();
const action = document.getElementById("cp-action").value;
const priority =
parseInt(document.getElementById("cp-priority").value, 10) || 0;
if (!name || !pattern) {
const e = document.getElementById("create-policy-error");
e.textContent = "Name and pattern are required";
e.classList.add("is-visible");
return;
}
document.getElementById("cp-submit").disabled = true;
authFetch("/v1/api/admin/policies", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: name,
tool_pattern: pattern,
action: action,
priority: priority,
}),
})
.then(function (r) {
if (!r.ok)
return r.json().then(function (d) {
throw new Error(d.error || "Failed");
});
return r.json();
})
.then(function () {
hideCreatePolicyModal();
showToast("Policy created");
loadGovPolicies();
})
.catch(function (e) {
const el = document.getElementById("create-policy-error");
el.textContent = e.message;
el.classList.add("is-visible");
})
.finally(function () {
document.getElementById("cp-submit").disabled = false;
});
window.TurnstoneHatch.closeShelf(document.getElementById("policy-shelf"));
_polShelfHandle = null;
}
function showEditPolicyModal(policyId) {
_epTriggerEl = document.activeElement;
_polWire();
let policy = null;
for (let i = 0; i < _govPolicies.length; i++) {
if (_govPolicies[i].policy_id === policyId) {
@@ -981,41 +1011,57 @@ function showEditPolicyModal(policyId) {
}
}
if (!policy) return;
const ov = document.getElementById("edit-policy-overlay");
ov.style.display = "flex";
document.getElementById("ep-id").value = policyId;
document.getElementById("ep-name").value = policy.name;
document.getElementById("ep-pattern").value = policy.tool_pattern;
document.getElementById("ep-action").value = policy.action;
document.getElementById("ep-priority").value = policy.priority;
document.getElementById("ep-enabled").checked = policy.enabled;
document.getElementById("edit-policy-error").classList.remove("is-visible");
_epTrapHandler = _installTrap("edit-policy-overlay", "edit-policy-box");
document.getElementById("policy-shelf-error").classList.remove("is-visible");
document.getElementById("pol-id").value = policyId;
document.getElementById("pol-name").value = policy.name;
document.getElementById("pol-pattern").value = policy.tool_pattern;
document.getElementById("pol-action").value = policy.action;
document.getElementById("pol-priority").value = policy.priority;
document.getElementById("pol-enabled").checked = policy.enabled;
document.getElementById("pol-enabled-row").hidden = false;
document.getElementById("policy-shelf-title").textContent =
"Edit tool policy \u2014 " + policy.name;
document.getElementById("policy-shelf-tag").textContent = "POL-EDIT";
const shelf = document.getElementById("policy-shelf");
shelf.setAttribute("data-kind", "edit");
document.getElementById("pol-submit").textContent = "Save";
_polRenderNeighbors();
_polShelfHandle = window.TurnstoneHatch.openShelf(shelf);
document.getElementById("pol-name").focus();
}
function hideEditPolicyModal() {
document.getElementById("edit-policy-overlay").style.display = "none";
_epTrapHandler = _removeTrap(_epTrapHandler);
if (_epTriggerEl && _epTriggerEl.focus) {
_epTriggerEl.focus();
}
_epTriggerEl = null;
hideCreatePolicyModal();
}
function submitEditPolicy() {
const id = document.getElementById("ep-id").value;
document.getElementById("ep-submit").disabled = true;
authFetch("/v1/api/admin/policies/" + id, {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name: document.getElementById("ep-name").value.trim(),
tool_pattern: document.getElementById("ep-pattern").value.trim(),
action: document.getElementById("ep-action").value,
priority: parseInt(document.getElementById("ep-priority").value, 10) || 0,
enabled: document.getElementById("ep-enabled").checked,
}),
})
function _submitPolicyShelf() {
const shelf = document.getElementById("policy-shelf");
const errEl = document.getElementById("policy-shelf-error");
const policyId = document.getElementById("pol-id").value;
const name = document.getElementById("pol-name").value.trim();
const pattern = document.getElementById("pol-pattern").value.trim();
if (!name || !pattern) {
errEl.textContent = "Name and pattern are required";
errEl.classList.add("is-visible");
return;
}
const body = {
name: name,
tool_pattern: pattern,
action: document.getElementById("pol-action").value,
priority: parseInt(document.getElementById("pol-priority").value, 10) || 0,
};
if (policyId) body.enabled = document.getElementById("pol-enabled").checked;
errEl.classList.remove("is-visible");
window.TurnstoneHatch.setBusy(shelf, true);
authFetch(
policyId ? "/v1/api/admin/policies/" + policyId : "/v1/api/admin/policies",
{
method: policyId ? "PUT" : "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
},
)
.then(function (r) {
if (!r.ok)
return r.json().then(function (d) {
@@ -1024,17 +1070,15 @@ function submitEditPolicy() {
return r.json();
})
.then(function () {
hideEditPolicyModal();
showToast("Policy updated");
window.TurnstoneHatch.setBusy(shelf, false);
hideCreatePolicyModal();
showToast(policyId ? "Policy updated" : "Policy created");
loadGovPolicies();
})
.catch(function (e) {
const el = document.getElementById("edit-policy-error");
el.textContent = e.message;
el.classList.add("is-visible");
})
.finally(function () {
document.getElementById("ep-submit").disabled = false;
window.TurnstoneHatch.setBusy(shelf, false);
errEl.textContent = e.message;
errEl.classList.add("is-visible");
});
}
+134 -159
View File
@@ -2427,6 +2427,99 @@
</button>
</footer>
</dialog>
<!-- Tool-policy shelf (create + edit) — pane-scoped. The priority
field is annotated with its evaluation neighbors (computed from
the already-loaded policy list; policies run highest-first). -->
<dialog
class="hatch hatch--shelf"
id="policy-shelf"
data-kind="create"
aria-labelledby="policy-shelf-title"
>
<header class="sh-head">
<span class="sh-led" aria-hidden="true"></span>
<h2 class="sh-title" id="policy-shelf-title">New tool policy</h2>
<span class="sh-tag" aria-hidden="true" id="policy-shelf-tag"
>POL-NEW</span
>
<button class="sh-x" data-close aria-label="Close"></button>
</header>
<div class="sh-body">
<div
id="policy-shelf-error"
class="sh-alert"
role="alert"
aria-live="assertive"
aria-atomic="true"
></div>
<input id="pol-id" type="hidden" />
<label for="pol-name">Name</label>
<input
id="pol-name"
type="text"
placeholder="e.g. Block shell access"
autocomplete="off"
/>
<label for="pol-pattern"
>Tool pattern
<span class="label-hint"
>glob: bash*, file_write, *</span
></label
>
<input
id="pol-pattern"
type="text"
class="sh-mono"
placeholder="bash*"
autocomplete="off"
spellcheck="false"
/>
<div class="field-pair">
<div>
<label for="pol-action">Action</label>
<select id="pol-action">
<option value="ask">Ask (require approval)</option>
<option value="allow">Allow (auto-approve)</option>
<option value="deny">Deny (block)</option>
</select>
</div>
<div>
<label for="pol-priority"
>Priority
<span class="label-hint"
>higher = evaluated first</span
></label
>
<input
id="pol-priority"
type="number"
value="0"
min="0"
max="9999"
/>
</div>
</div>
<div
class="match-strip"
id="pol-neighbors"
role="status"
aria-live="polite"
></div>
<label class="toggle-switch" id="pol-enabled-row" hidden>
<input id="pol-enabled" type="checkbox" checked />
<span class="toggle-track" aria-hidden="true"></span>
<span class="toggle-label">Enabled</span>
</label>
</div>
<footer class="sh-foot">
<div class="sh-foot-meta"></div>
<button class="sh-btn" data-close>Cancel</button>
<button id="pol-submit" class="sh-btn sh-btn--primary">
Create
</button>
</footer>
</dialog>
</div>
<!-- /admin-layout -->
</div>
@@ -2669,60 +2762,58 @@
</div>
<!-- Token Created (show-once) Modal -->
<div
id="token-created-overlay"
style="display: none"
role="dialog"
aria-modal="true"
<dialog
class="hatch hatch--dialog"
id="token-created-dialog"
data-kind="success"
aria-labelledby="token-created-title"
>
<div id="token-created-box" class="admin-modal">
<h2 id="token-created-title">Token Created</h2>
<p class="token-created-warning">
Copy this token now. It will not be shown again.
<header class="sh-head">
<span class="sh-led" aria-hidden="true"></span>
<h2 class="sh-title" id="token-created-title">Token created</h2>
<span class="sh-tag" aria-hidden="true">TOK-ISSUE</span>
<button class="sh-x" data-close aria-label="Close"></button>
</header>
<div class="sh-body">
<div id="token-created-value" class="sh-artifact"></div>
<p class="sh-callout">
Shown once — store it now. Turnstone keeps only the hash.
</p>
<div id="token-created-value" class="token-display"></div>
<div class="modal-buttons">
<button class="modal-submit" onclick="copyCreatedToken()">
Copy to clipboard
</button>
<button class="modal-cancel" onclick="hideTokenCreatedModal()">
Done
</button>
</div>
</div>
</div>
<footer class="sh-foot">
<div class="sh-foot-meta"></div>
<button class="sh-btn" data-close>Done</button>
<button id="tc-copy" class="sh-btn sh-btn--primary" autofocus>
Copy token
</button>
</footer>
</dialog>
<!-- Confirm Action Modal (reusable) -->
<div
id="confirm-overlay"
style="display: none"
<dialog
class="hatch hatch--dialog"
id="confirm-dialog"
data-kind="danger"
role="alertdialog"
aria-modal="true"
aria-labelledby="confirm-title"
aria-describedby="confirm-message"
>
<div id="confirm-box" class="admin-modal">
<h2 id="confirm-title">Confirm</h2>
<p
id="confirm-message"
style="color: var(--fg-dim); margin: 12px 0 20px; line-height: 1.5"
></p>
<div class="modal-buttons">
<button class="modal-cancel" onclick="hideConfirmModal()">
Cancel
</button>
<button
id="confirm-submit"
class="modal-submit"
style="background: var(--red); border-color: var(--red)"
onclick="_confirmCallback()"
>
Confirm
</button>
</div>
<header class="sh-head">
<span class="sh-led" aria-hidden="true"></span>
<h2 class="sh-title" id="confirm-title">Confirm</h2>
<button class="sh-x" data-close aria-label="Close"></button>
</header>
<div class="sh-body">
<p class="sh-prose" id="confirm-message"></p>
</div>
</div>
<footer class="sh-foot">
<div class="sh-foot-meta"></div>
<button class="sh-btn" data-close autofocus>Cancel</button>
<button id="confirm-submit" class="sh-btn sh-btn--danger">
Confirm
</button>
</footer>
</dialog>
<!-- Create Channel Link Modal -->
<div
@@ -2907,122 +2998,6 @@
</div>
</div>
<!-- Create Policy Modal -->
<div
id="create-policy-overlay"
style="display: none"
role="dialog"
aria-modal="true"
aria-labelledby="create-policy-title"
>
<div id="create-policy-box" class="admin-modal">
<h2 id="create-policy-title">Create Tool Policy</h2>
<div
id="create-policy-error"
role="alert"
aria-live="assertive"
aria-atomic="true"
></div>
<label for="cp-name">Name</label>
<input
id="cp-name"
type="text"
placeholder="e.g. Block shell access"
autocomplete="off"
/>
<label for="cp-pattern"
>Tool pattern
<span class="label-hint"
>glob syntax: bash*, file_write, *</span
></label
>
<input
id="cp-pattern"
type="text"
placeholder="bash*"
autocomplete="off"
spellcheck="false"
/>
<label for="cp-action">Action</label>
<select id="cp-action">
<option value="ask">Ask (require approval)</option>
<option value="allow">Allow (auto-approve)</option>
<option value="deny">Deny (block)</option>
</select>
<label for="cp-priority"
>Priority
<span class="label-hint">higher = evaluated first</span></label
>
<input id="cp-priority" type="number" value="0" min="0" max="9999" />
<div class="modal-buttons">
<button class="modal-cancel" onclick="hideCreatePolicyModal()">
Cancel
</button>
<button
id="cp-submit"
class="modal-submit"
onclick="submitCreatePolicy()"
>
Create
</button>
</div>
</div>
</div>
<!-- Edit Policy Modal -->
<div
id="edit-policy-overlay"
style="display: none"
role="dialog"
aria-modal="true"
aria-labelledby="edit-policy-title"
>
<div id="edit-policy-box" class="admin-modal">
<h2 id="edit-policy-title">Edit Tool Policy</h2>
<div
id="edit-policy-error"
role="alert"
aria-live="assertive"
aria-atomic="true"
></div>
<input id="ep-id" type="hidden" />
<label for="ep-name">Name</label>
<input id="ep-name" type="text" autocomplete="off" />
<label for="ep-pattern">Tool pattern</label>
<input
id="ep-pattern"
type="text"
autocomplete="off"
spellcheck="false"
/>
<label for="ep-action">Action</label>
<select id="ep-action">
<option value="ask">Ask (require approval)</option>
<option value="allow">Allow (auto-approve)</option>
<option value="deny">Deny (block)</option>
</select>
<label for="ep-priority">Priority</label>
<input id="ep-priority" type="number" value="0" min="0" max="9999" />
<label class="toggle-switch">
<input id="ep-enabled" type="checkbox" checked />
<span class="toggle-track" aria-hidden="true"></span>
<span class="toggle-label">Enabled</span>
</label>
<div class="modal-buttons">
<button class="modal-cancel" onclick="hideEditPolicyModal()">
Cancel
</button>
<button
id="ep-submit"
class="modal-submit"
onclick="submitEditPolicy()"
>
Save
</button>
</div>
</div>
</div>
<!-- Create Prompt Policy Modal -->
<div
id="create-ppolicy-overlay"
-12
View File
@@ -1774,15 +1774,11 @@ textarea.skill-content-area {
#create-user-overlay,
#create-token-overlay,
#token-created-overlay,
#create-channel-overlay,
#confirm-overlay,
#schedule-runs-overlay,
#create-role-overlay,
#edit-role-overlay,
#user-roles-overlay,
#create-policy-overlay,
#edit-policy-overlay,
#create-ppolicy-overlay,
#edit-ppolicy-overlay,
#create-template-overlay,
@@ -1807,14 +1803,6 @@ textarea.skill-content-area {
justify-content: center;
z-index: 600;
}
/* Confirm dialogs are launched FROM other overlays (e.g. unlock-skill from
the edit-template modal). They share z-index 600, so DOM order picks the
winner — and confirm-overlay is earlier in the DOM, so it would render
underneath. Bump it above the per-feature overlays but keep it below
toasts (z-index 700). */
#confirm-overlay {
z-index: 650;
}
/* Token display (show-once) */
.token-created-warning {
+5
View File
@@ -81,6 +81,11 @@
.hatch:not([open]) {
display: none;
}
/* hidden must always win inside a hatch — display rules on form chrome
(e.g. label.toggle-switch's inline-flex) would otherwise defeat it. */
.hatch [hidden] {
display: none !important;
}
.hatch--shelf {
position: absolute;