@crvouga/mockingbird-service-flex 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -1
- package/dist/{chunk-O3RZRWN7.js → chunk-TA2IWDFT.js} +89 -10
- package/dist/chunk-TA2IWDFT.js.map +7 -0
- package/dist/{chunk-Q7BULM2B.js → chunk-YKLXWQXI.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-O3RZRWN7.js.map +0 -7
- /package/dist/{chunk-Q7BULM2B.js.map → chunk-YKLXWQXI.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-flex
|
|
2
2
|
|
|
3
|
-
## 1.6.
|
|
3
|
+
## 1.6.1 (2026-09-30)
|
|
4
|
+
|
|
5
|
+
### Fixes and improvements
|
|
6
|
+
|
|
7
|
+
- keep admin buttons pressed until the action finishes ([0aabfcb](https://github.com/crvouga/mockingbird/commit/0aabfcb5f392aa51e193f9c3f817d5877d92bc6a))
|
|
8
|
+
- size admin select fields like the text inputs beside them ([0fb3ef2](https://github.com/crvouga/mockingbird/commit/0fb3ef2ddbf43376c93a2ae6d87c6c9bf890dafc))
|
|
9
|
+
- keep admin section tabs wired when boot is renamed ([4eda25e](https://github.com/crvouga/mockingbird/commit/4eda25eacd59da048d196f7cc8369d7a02b47bfc))
|
|
10
|
+
|
|
11
|
+
## 1.6.0 (2026-09-29)
|
|
4
12
|
|
|
5
13
|
### Features
|
|
6
14
|
|
|
@@ -2365,6 +2365,50 @@ function bootAdmin(config) {
|
|
|
2365
2365
|
if (render)
|
|
2366
2366
|
await render();
|
|
2367
2367
|
}
|
|
2368
|
+
const delay = (ms) => new Promise((resolve) => {
|
|
2369
|
+
window.setTimeout(resolve, ms);
|
|
2370
|
+
});
|
|
2371
|
+
const confirmAction = async (button, pending, done, work) => {
|
|
2372
|
+
if (button.dataset.pending === "1")
|
|
2373
|
+
return;
|
|
2374
|
+
const label = button.textContent ?? "";
|
|
2375
|
+
const width = button.offsetWidth;
|
|
2376
|
+
button.dataset.pending = "1";
|
|
2377
|
+
button.classList.add("pressed");
|
|
2378
|
+
button.setAttribute("aria-busy", "true");
|
|
2379
|
+
button.style.minWidth = `${width}px`;
|
|
2380
|
+
button.textContent = pending;
|
|
2381
|
+
try {
|
|
2382
|
+
await work();
|
|
2383
|
+
button.textContent = done;
|
|
2384
|
+
await delay(1e3);
|
|
2385
|
+
} catch (error2) {
|
|
2386
|
+
showError(error2);
|
|
2387
|
+
} finally {
|
|
2388
|
+
button.textContent = label;
|
|
2389
|
+
button.style.minWidth = "";
|
|
2390
|
+
button.classList.remove("pressed");
|
|
2391
|
+
button.removeAttribute("aria-busy");
|
|
2392
|
+
delete button.dataset.pending;
|
|
2393
|
+
}
|
|
2394
|
+
};
|
|
2395
|
+
document.addEventListener("click", (event) => {
|
|
2396
|
+
const target = event.target;
|
|
2397
|
+
if (!(target instanceof Element))
|
|
2398
|
+
return;
|
|
2399
|
+
const button = target.closest("button");
|
|
2400
|
+
if (!(button instanceof HTMLButtonElement) || button.dataset.pending === "1")
|
|
2401
|
+
return;
|
|
2402
|
+
button.classList.add("pressed");
|
|
2403
|
+
const token = String(Date.now());
|
|
2404
|
+
button.dataset.press = token;
|
|
2405
|
+
window.setTimeout(() => {
|
|
2406
|
+
if (button.dataset.press !== token || button.dataset.pending === "1")
|
|
2407
|
+
return;
|
|
2408
|
+
button.classList.remove("pressed");
|
|
2409
|
+
delete button.dataset.press;
|
|
2410
|
+
}, 450);
|
|
2411
|
+
});
|
|
2368
2412
|
el("namespace", HTMLSelectElement).addEventListener("change", () => {
|
|
2369
2413
|
state.namespace = el("namespace", HTMLSelectElement).value;
|
|
2370
2414
|
refresh().catch(showError);
|
|
@@ -2375,7 +2419,7 @@ function bootAdmin(config) {
|
|
|
2375
2419
|
refresh().catch(showError);
|
|
2376
2420
|
});
|
|
2377
2421
|
el("refresh", HTMLButtonElement).addEventListener("click", () => {
|
|
2378
|
-
refresh().catch(showError);
|
|
2422
|
+
confirmAction(el("refresh", HTMLButtonElement), "Refreshing...", "Updated", () => refresh()).catch(showError);
|
|
2379
2423
|
});
|
|
2380
2424
|
el("collections", HTMLElement).addEventListener("click", (event) => {
|
|
2381
2425
|
const target = event.target;
|
|
@@ -2498,10 +2542,10 @@ function bootAdmin(config) {
|
|
|
2498
2542
|
}).then(() => renderFaults()).catch(showError);
|
|
2499
2543
|
});
|
|
2500
2544
|
el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
|
|
2501
|
-
api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
|
|
2545
|
+
confirmAction(el("fault-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/faults").then(() => renderFaults())).catch(showError);
|
|
2502
2546
|
});
|
|
2503
2547
|
el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
|
|
2504
|
-
api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
|
|
2548
|
+
confirmAction(el("journal-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/requests").then(() => renderJournal())).catch(showError);
|
|
2505
2549
|
});
|
|
2506
2550
|
el("route-list", HTMLElement).addEventListener("click", (event) => {
|
|
2507
2551
|
const target = event.target;
|
|
@@ -2616,9 +2660,13 @@ function bootAdmin(config) {
|
|
|
2616
2660
|
var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
|
|
2617
2661
|
var adminClientSource = (config) => {
|
|
2618
2662
|
const payload = JSON.stringify(config).replace(/</g, "\\u003c");
|
|
2663
|
+
const entry = bootAdmin.name;
|
|
2664
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(entry)) {
|
|
2665
|
+
throw new Error("admin client cannot call its boot function");
|
|
2666
|
+
}
|
|
2619
2667
|
return `<script>
|
|
2620
2668
|
${clientPrelude}
|
|
2621
|
-
|
|
2669
|
+
${entry}(${payload});
|
|
2622
2670
|
</script>`;
|
|
2623
2671
|
};
|
|
2624
2672
|
|
|
@@ -2832,9 +2880,24 @@ var renderAdminDocument = (service) => {
|
|
|
2832
2880
|
.controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
2833
2881
|
label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
|
|
2834
2882
|
label.field input, label.field select, .controls input[type="password"] {
|
|
2835
|
-
background: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
|
|
2836
|
-
padding: 7px 8px; min-height: 36px;
|
|
2837
|
-
}
|
|
2883
|
+
background-color: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
|
|
2884
|
+
padding: 7px 8px; min-height: 36px; line-height: 1.2;
|
|
2885
|
+
}
|
|
2886
|
+
/* A native menu ignores min-height, so Preset rendered about half as tall as
|
|
2887
|
+
the text fields in the same form. Paint it like those fields. */
|
|
2888
|
+
label.field select {
|
|
2889
|
+
-webkit-appearance: none; appearance: none;
|
|
2890
|
+
padding-right: 28px;
|
|
2891
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='none' stroke='%2371717a' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' d='M2.5 4.5 6 8l3.5-3.5'/%3E%3C/svg%3E");
|
|
2892
|
+
background-repeat: no-repeat; background-position: right 10px center; background-size: 12px 12px;
|
|
2893
|
+
}
|
|
2894
|
+
.stack > .field > input,
|
|
2895
|
+
.stack > .field > select,
|
|
2896
|
+
.stack > .field > textarea,
|
|
2897
|
+
dialog .field > input,
|
|
2898
|
+
dialog .field > select,
|
|
2899
|
+
dialog .field > textarea { width: 100%; }
|
|
2900
|
+
.icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger, nav.side button { cursor: pointer; }
|
|
2838
2901
|
.icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger {
|
|
2839
2902
|
border-radius: 9px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line);
|
|
2840
2903
|
background: var(--bg-elev);
|
|
@@ -2842,6 +2905,14 @@ var renderAdminDocument = (service) => {
|
|
|
2842
2905
|
.btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
|
|
2843
2906
|
.btn-quiet { background: transparent; }
|
|
2844
2907
|
.btn-danger { color: var(--err); }
|
|
2908
|
+
/* Held after mouseup. Clearing an empty journal changes nothing on screen,
|
|
2909
|
+
so the press itself has to stay visible. */
|
|
2910
|
+
.icon-btn:active, .btn:active, .btn-quiet:active, .btn-danger:active, nav.side button:active,
|
|
2911
|
+
.icon-btn.pressed, .btn.pressed, .btn-quiet.pressed, .btn-danger.pressed, nav.side button.pressed {
|
|
2912
|
+
background: var(--bg-inset); border-color: var(--line-strong); color: var(--ink);
|
|
2913
|
+
}
|
|
2914
|
+
.btn-danger:active, .btn-danger.pressed { background: var(--err-soft); color: var(--err); }
|
|
2915
|
+
.btn-primary:active, .btn-primary.pressed { filter: brightness(0.88); color: var(--accent-ink); }
|
|
2845
2916
|
/* minmax(0, 1fr): a 1fr column will not shrink below its content, so a long
|
|
2846
2917
|
record or the full nav row would widen the page past the viewport. */
|
|
2847
2918
|
.layout {
|
|
@@ -2930,8 +3001,16 @@ var renderAdminDocument = (service) => {
|
|
|
2930
3001
|
input, select, textarea { font-size: 16px; }
|
|
2931
3002
|
.brand, .vendor, .controls { width: 100%; }
|
|
2932
3003
|
label.field, label.field select, label.field input { flex: 1; min-width: 0; }
|
|
2933
|
-
|
|
3004
|
+
/* The layout fills the screen. A single-column grid stretches every auto
|
|
3005
|
+
row, so the tab bar grew with the leftover height and the buttons floated
|
|
3006
|
+
in the middle of it. The bar stays one row; the page below takes the rest. */
|
|
3007
|
+
.layout {
|
|
3008
|
+
grid-template-columns: minmax(0, 1fr);
|
|
3009
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
3010
|
+
align-content: start;
|
|
3011
|
+
}
|
|
2934
3012
|
nav.side {
|
|
3013
|
+
align-self: start; height: auto;
|
|
2935
3014
|
flex-direction: row; align-items: center; overflow-x: auto; min-width: 0;
|
|
2936
3015
|
border-right: 0; border-bottom: 1px solid var(--line);
|
|
2937
3016
|
padding: 8px;
|
|
@@ -3799,7 +3878,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
3799
3878
|
};
|
|
3800
3879
|
|
|
3801
3880
|
// ../core/dist/version.js
|
|
3802
|
-
var PACKAGE_VERSION = true ? "1.6.
|
|
3881
|
+
var PACKAGE_VERSION = true ? "1.6.1" : UNRELEASED_VERSION;
|
|
3803
3882
|
|
|
3804
3883
|
// ../core/dist/signing.js
|
|
3805
3884
|
var encoder = new TextEncoder();
|
|
@@ -6931,4 +7010,4 @@ export {
|
|
|
6931
7010
|
FlexAPI,
|
|
6932
7011
|
isNextActionType
|
|
6933
7012
|
};
|
|
6934
|
-
//# sourceMappingURL=chunk-
|
|
7013
|
+
//# sourceMappingURL=chunk-TA2IWDFT.js.map
|