@crvouga/mockingbird-service-bedrock 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 CHANGED
@@ -1,6 +1,14 @@
1
1
  # Changelog — @crvouga/mockingbird-service-bedrock
2
2
 
3
- ## 1.6.0 (2026-09-30)
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
 
@@ -2306,6 +2306,50 @@ function bootAdmin(config) {
2306
2306
  if (render)
2307
2307
  await render();
2308
2308
  }
2309
+ const delay = (ms) => new Promise((resolve) => {
2310
+ window.setTimeout(resolve, ms);
2311
+ });
2312
+ const confirmAction = async (button, pending, done, work) => {
2313
+ if (button.dataset.pending === "1")
2314
+ return;
2315
+ const label = button.textContent ?? "";
2316
+ const width = button.offsetWidth;
2317
+ button.dataset.pending = "1";
2318
+ button.classList.add("pressed");
2319
+ button.setAttribute("aria-busy", "true");
2320
+ button.style.minWidth = `${width}px`;
2321
+ button.textContent = pending;
2322
+ try {
2323
+ await work();
2324
+ button.textContent = done;
2325
+ await delay(1e3);
2326
+ } catch (error) {
2327
+ showError(error);
2328
+ } finally {
2329
+ button.textContent = label;
2330
+ button.style.minWidth = "";
2331
+ button.classList.remove("pressed");
2332
+ button.removeAttribute("aria-busy");
2333
+ delete button.dataset.pending;
2334
+ }
2335
+ };
2336
+ document.addEventListener("click", (event) => {
2337
+ const target = event.target;
2338
+ if (!(target instanceof Element))
2339
+ return;
2340
+ const button = target.closest("button");
2341
+ if (!(button instanceof HTMLButtonElement) || button.dataset.pending === "1")
2342
+ return;
2343
+ button.classList.add("pressed");
2344
+ const token = String(Date.now());
2345
+ button.dataset.press = token;
2346
+ window.setTimeout(() => {
2347
+ if (button.dataset.press !== token || button.dataset.pending === "1")
2348
+ return;
2349
+ button.classList.remove("pressed");
2350
+ delete button.dataset.press;
2351
+ }, 450);
2352
+ });
2309
2353
  el("namespace", HTMLSelectElement).addEventListener("change", () => {
2310
2354
  state.namespace = el("namespace", HTMLSelectElement).value;
2311
2355
  refresh().catch(showError);
@@ -2316,7 +2360,7 @@ function bootAdmin(config) {
2316
2360
  refresh().catch(showError);
2317
2361
  });
2318
2362
  el("refresh", HTMLButtonElement).addEventListener("click", () => {
2319
- refresh().catch(showError);
2363
+ confirmAction(el("refresh", HTMLButtonElement), "Refreshing...", "Updated", () => refresh()).catch(showError);
2320
2364
  });
2321
2365
  el("collections", HTMLElement).addEventListener("click", (event) => {
2322
2366
  const target = event.target;
@@ -2439,10 +2483,10 @@ function bootAdmin(config) {
2439
2483
  }).then(() => renderFaults()).catch(showError);
2440
2484
  });
2441
2485
  el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
2442
- api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
2486
+ confirmAction(el("fault-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/faults").then(() => renderFaults())).catch(showError);
2443
2487
  });
2444
2488
  el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
2445
- api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
2489
+ confirmAction(el("journal-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/requests").then(() => renderJournal())).catch(showError);
2446
2490
  });
2447
2491
  el("route-list", HTMLElement).addEventListener("click", (event) => {
2448
2492
  const target = event.target;
@@ -2557,9 +2601,13 @@ function bootAdmin(config) {
2557
2601
  var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
2558
2602
  var adminClientSource = (config) => {
2559
2603
  const payload = JSON.stringify(config).replace(/</g, "\\u003c");
2604
+ const entry = bootAdmin.name;
2605
+ if (!/^[A-Za-z_$][\w$]*$/.test(entry)) {
2606
+ throw new Error("admin client cannot call its boot function");
2607
+ }
2560
2608
  return `<script>
2561
2609
  ${clientPrelude}
2562
- bootAdmin(${payload});
2610
+ ${entry}(${payload});
2563
2611
  </script>`;
2564
2612
  };
2565
2613
 
@@ -2773,9 +2821,24 @@ var renderAdminDocument = (service) => {
2773
2821
  .controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
2774
2822
  label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
2775
2823
  label.field input, label.field select, .controls input[type="password"] {
2776
- background: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2777
- padding: 7px 8px; min-height: 36px;
2778
- }
2824
+ background-color: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2825
+ padding: 7px 8px; min-height: 36px; line-height: 1.2;
2826
+ }
2827
+ /* A native menu ignores min-height, so Preset rendered about half as tall as
2828
+ the text fields in the same form. Paint it like those fields. */
2829
+ label.field select {
2830
+ -webkit-appearance: none; appearance: none;
2831
+ padding-right: 28px;
2832
+ 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");
2833
+ background-repeat: no-repeat; background-position: right 10px center; background-size: 12px 12px;
2834
+ }
2835
+ .stack > .field > input,
2836
+ .stack > .field > select,
2837
+ .stack > .field > textarea,
2838
+ dialog .field > input,
2839
+ dialog .field > select,
2840
+ dialog .field > textarea { width: 100%; }
2841
+ .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger, nav.side button { cursor: pointer; }
2779
2842
  .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger {
2780
2843
  border-radius: 9px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line);
2781
2844
  background: var(--bg-elev);
@@ -2783,6 +2846,14 @@ var renderAdminDocument = (service) => {
2783
2846
  .btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
2784
2847
  .btn-quiet { background: transparent; }
2785
2848
  .btn-danger { color: var(--err); }
2849
+ /* Held after mouseup. Clearing an empty journal changes nothing on screen,
2850
+ so the press itself has to stay visible. */
2851
+ .icon-btn:active, .btn:active, .btn-quiet:active, .btn-danger:active, nav.side button:active,
2852
+ .icon-btn.pressed, .btn.pressed, .btn-quiet.pressed, .btn-danger.pressed, nav.side button.pressed {
2853
+ background: var(--bg-inset); border-color: var(--line-strong); color: var(--ink);
2854
+ }
2855
+ .btn-danger:active, .btn-danger.pressed { background: var(--err-soft); color: var(--err); }
2856
+ .btn-primary:active, .btn-primary.pressed { filter: brightness(0.88); color: var(--accent-ink); }
2786
2857
  /* minmax(0, 1fr): a 1fr column will not shrink below its content, so a long
2787
2858
  record or the full nav row would widen the page past the viewport. */
2788
2859
  .layout {
@@ -2871,8 +2942,16 @@ var renderAdminDocument = (service) => {
2871
2942
  input, select, textarea { font-size: 16px; }
2872
2943
  .brand, .vendor, .controls { width: 100%; }
2873
2944
  label.field, label.field select, label.field input { flex: 1; min-width: 0; }
2874
- .layout { grid-template-columns: minmax(0, 1fr); }
2945
+ /* The layout fills the screen. A single-column grid stretches every auto
2946
+ row, so the tab bar grew with the leftover height and the buttons floated
2947
+ in the middle of it. The bar stays one row; the page below takes the rest. */
2948
+ .layout {
2949
+ grid-template-columns: minmax(0, 1fr);
2950
+ grid-template-rows: auto minmax(0, 1fr);
2951
+ align-content: start;
2952
+ }
2875
2953
  nav.side {
2954
+ align-self: start; height: auto;
2876
2955
  flex-direction: row; align-items: center; overflow-x: auto; min-width: 0;
2877
2956
  border-right: 0; border-bottom: 1px solid var(--line);
2878
2957
  padding: 8px;
@@ -3740,7 +3819,7 @@ var stateAdminRoutes = (routes) => {
3740
3819
  };
3741
3820
 
3742
3821
  // ../core/dist/version.js
3743
- var PACKAGE_VERSION = true ? "1.6.0" : UNRELEASED_VERSION;
3822
+ var PACKAGE_VERSION = true ? "1.6.1" : UNRELEASED_VERSION;
3744
3823
 
3745
3824
  // ../core/dist/signing.js
3746
3825
  var encoder = new TextEncoder();
@@ -6904,4 +6983,4 @@ export {
6904
6983
  titanEmbedding,
6905
6984
  BedrockAPI
6906
6985
  };
6907
- //# sourceMappingURL=chunk-KLUDFWGJ.js.map
6986
+ //# sourceMappingURL=chunk-HQ6HZNDD.js.map