`;
+ // renderSortEditor replaces its mount wholesale, so the mount must be
+ // the canvas slot ALONE. Passing the .card (which holds the h2 with the
+ // save/reset/add buttons) used to wipe the toolbar on every open — the
+ // lane canvas survived, the buttons did not. The per-key modal never hit
+ // this because its mount only ever contained the canvas.
$("#tab-sort").innerHTML = `
-
${t("sortTitle")}${kindToggle}
+
+
${t("sortTitle")}${kindToggle}
-
+
+
`;
- renderSortEditor($("#tab-sort").querySelector(".card"), {
+ renderSortEditor($("#sort-mount"), {
canvasId: "scr-canvas",
});
}
diff --git a/internal/gateway/ui_key_auto_test.go b/internal/gateway/ui_key_auto_test.go
index 851a6b1..c49fea9 100644
--- a/internal/gateway/ui_key_auto_test.go
+++ b/internal/gateway/ui_key_auto_test.go
@@ -393,3 +393,46 @@ func TestAutoScopeConflictMessageComesFromCheckQuota(t *testing.T) {
t.Fatal("checkQuota's AUTO branch must name the scope as the cause")
}
}
+
+// renderSortEditor replaces its mount's innerHTML wholesale. The global
+// priority page used to hand it the .card that also holds the h2 toolbar, so
+// every open deleted the save/reset/add buttons: the lanes still rendered and
+// the drag worked, but there was no way to persist a change. The per-key modal
+// never showed this because its mount held only the canvas.
+//
+// The regression is invisible in the DOM when you only count lane elements, so
+// this asserts on the structure: the toolbar lives in a sibling of the mount,
+// not inside it.
+func TestGlobalSortToolbarSurvivesCanvasRender(t *testing.T) {
+ src := uiSource(t)
+
+ // The global editor must mount into a dedicated host, not the .card.
+ i := strings.Index(src, `$("#tab-sort").innerHTML = `)
+ if i < 0 {
+ t.Fatal("global sort markup not found")
+ }
+ chunk := src[i:min(len(src), i+1400)]
+ if !strings.Contains(chunk, `id="sort-mount"`) {
+ t.Fatal("global sort page must provide a dedicated #sort-mount for the canvas")
+ }
+ // The mount handed to renderSortEditor must be that host.
+ if !strings.Contains(src, `renderSortEditor($("#sort-mount")`) {
+ t.Fatal("renderSortEditor must be given #sort-mount; passing the .card " +
+ "wipes the h2 toolbar that holds the save button")
+ }
+ // The toolbar (save in particular) must be OUTSIDE #sort-mount.
+ close := strings.Index(chunk, "
`;")
+ if close < 0 {
+ t.Fatal("malformed #tab-sort template")
+ }
+ head := chunk[:close]
+ mountAt := strings.Index(head, `id="sort-mount"`)
+ saveAt := strings.Index(head, `onclick="saveSort()"`)
+ if saveAt < 0 {
+ t.Fatal("the global sort page has no save button at all")
+ }
+ if mountAt >= 0 && saveAt > mountAt {
+ t.Fatal("the save button sits inside the canvas mount and will be " +
+ "erased the moment the canvas renders")
+ }
+}