@crvouga/mockingbird-service-caretalk 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-caretalk
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
 
@@ -2286,6 +2286,50 @@ function bootAdmin(config) {
2286
2286
  if (render)
2287
2287
  await render();
2288
2288
  }
2289
+ const delay = (ms) => new Promise((resolve) => {
2290
+ window.setTimeout(resolve, ms);
2291
+ });
2292
+ const confirmAction = async (button, pending, done, work) => {
2293
+ if (button.dataset.pending === "1")
2294
+ return;
2295
+ const label = button.textContent ?? "";
2296
+ const width = button.offsetWidth;
2297
+ button.dataset.pending = "1";
2298
+ button.classList.add("pressed");
2299
+ button.setAttribute("aria-busy", "true");
2300
+ button.style.minWidth = `${width}px`;
2301
+ button.textContent = pending;
2302
+ try {
2303
+ await work();
2304
+ button.textContent = done;
2305
+ await delay(1e3);
2306
+ } catch (error) {
2307
+ showError(error);
2308
+ } finally {
2309
+ button.textContent = label;
2310
+ button.style.minWidth = "";
2311
+ button.classList.remove("pressed");
2312
+ button.removeAttribute("aria-busy");
2313
+ delete button.dataset.pending;
2314
+ }
2315
+ };
2316
+ document.addEventListener("click", (event) => {
2317
+ const target = event.target;
2318
+ if (!(target instanceof Element))
2319
+ return;
2320
+ const button = target.closest("button");
2321
+ if (!(button instanceof HTMLButtonElement) || button.dataset.pending === "1")
2322
+ return;
2323
+ button.classList.add("pressed");
2324
+ const token = String(Date.now());
2325
+ button.dataset.press = token;
2326
+ window.setTimeout(() => {
2327
+ if (button.dataset.press !== token || button.dataset.pending === "1")
2328
+ return;
2329
+ button.classList.remove("pressed");
2330
+ delete button.dataset.press;
2331
+ }, 450);
2332
+ });
2289
2333
  el("namespace", HTMLSelectElement).addEventListener("change", () => {
2290
2334
  state.namespace = el("namespace", HTMLSelectElement).value;
2291
2335
  refresh().catch(showError);
@@ -2296,7 +2340,7 @@ function bootAdmin(config) {
2296
2340
  refresh().catch(showError);
2297
2341
  });
2298
2342
  el("refresh", HTMLButtonElement).addEventListener("click", () => {
2299
- refresh().catch(showError);
2343
+ confirmAction(el("refresh", HTMLButtonElement), "Refreshing...", "Updated", () => refresh()).catch(showError);
2300
2344
  });
2301
2345
  el("collections", HTMLElement).addEventListener("click", (event) => {
2302
2346
  const target = event.target;
@@ -2419,10 +2463,10 @@ function bootAdmin(config) {
2419
2463
  }).then(() => renderFaults()).catch(showError);
2420
2464
  });
2421
2465
  el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
2422
- api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
2466
+ confirmAction(el("fault-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/faults").then(() => renderFaults())).catch(showError);
2423
2467
  });
2424
2468
  el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
2425
- api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
2469
+ confirmAction(el("journal-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/requests").then(() => renderJournal())).catch(showError);
2426
2470
  });
2427
2471
  el("route-list", HTMLElement).addEventListener("click", (event) => {
2428
2472
  const target = event.target;
@@ -2537,9 +2581,13 @@ function bootAdmin(config) {
2537
2581
  var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
2538
2582
  var adminClientSource = (config) => {
2539
2583
  const payload = JSON.stringify(config).replace(/</g, "\\u003c");
2584
+ const entry = bootAdmin.name;
2585
+ if (!/^[A-Za-z_$][\w$]*$/.test(entry)) {
2586
+ throw new Error("admin client cannot call its boot function");
2587
+ }
2540
2588
  return `<script>
2541
2589
  ${clientPrelude}
2542
- bootAdmin(${payload});
2590
+ ${entry}(${payload});
2543
2591
  </script>`;
2544
2592
  };
2545
2593
 
@@ -2753,9 +2801,24 @@ var renderAdminDocument = (service) => {
2753
2801
  .controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
2754
2802
  label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
2755
2803
  label.field input, label.field select, .controls input[type="password"] {
2756
- background: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2757
- padding: 7px 8px; min-height: 36px;
2758
- }
2804
+ background-color: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2805
+ padding: 7px 8px; min-height: 36px; line-height: 1.2;
2806
+ }
2807
+ /* A native menu ignores min-height, so Preset rendered about half as tall as
2808
+ the text fields in the same form. Paint it like those fields. */
2809
+ label.field select {
2810
+ -webkit-appearance: none; appearance: none;
2811
+ padding-right: 28px;
2812
+ 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");
2813
+ background-repeat: no-repeat; background-position: right 10px center; background-size: 12px 12px;
2814
+ }
2815
+ .stack > .field > input,
2816
+ .stack > .field > select,
2817
+ .stack > .field > textarea,
2818
+ dialog .field > input,
2819
+ dialog .field > select,
2820
+ dialog .field > textarea { width: 100%; }
2821
+ .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger, nav.side button { cursor: pointer; }
2759
2822
  .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger {
2760
2823
  border-radius: 9px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line);
2761
2824
  background: var(--bg-elev);
@@ -2763,6 +2826,14 @@ var renderAdminDocument = (service) => {
2763
2826
  .btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
2764
2827
  .btn-quiet { background: transparent; }
2765
2828
  .btn-danger { color: var(--err); }
2829
+ /* Held after mouseup. Clearing an empty journal changes nothing on screen,
2830
+ so the press itself has to stay visible. */
2831
+ .icon-btn:active, .btn:active, .btn-quiet:active, .btn-danger:active, nav.side button:active,
2832
+ .icon-btn.pressed, .btn.pressed, .btn-quiet.pressed, .btn-danger.pressed, nav.side button.pressed {
2833
+ background: var(--bg-inset); border-color: var(--line-strong); color: var(--ink);
2834
+ }
2835
+ .btn-danger:active, .btn-danger.pressed { background: var(--err-soft); color: var(--err); }
2836
+ .btn-primary:active, .btn-primary.pressed { filter: brightness(0.88); color: var(--accent-ink); }
2766
2837
  /* minmax(0, 1fr): a 1fr column will not shrink below its content, so a long
2767
2838
  record or the full nav row would widen the page past the viewport. */
2768
2839
  .layout {
@@ -2851,8 +2922,16 @@ var renderAdminDocument = (service) => {
2851
2922
  input, select, textarea { font-size: 16px; }
2852
2923
  .brand, .vendor, .controls { width: 100%; }
2853
2924
  label.field, label.field select, label.field input { flex: 1; min-width: 0; }
2854
- .layout { grid-template-columns: minmax(0, 1fr); }
2925
+ /* The layout fills the screen. A single-column grid stretches every auto
2926
+ row, so the tab bar grew with the leftover height and the buttons floated
2927
+ in the middle of it. The bar stays one row; the page below takes the rest. */
2928
+ .layout {
2929
+ grid-template-columns: minmax(0, 1fr);
2930
+ grid-template-rows: auto minmax(0, 1fr);
2931
+ align-content: start;
2932
+ }
2855
2933
  nav.side {
2934
+ align-self: start; height: auto;
2856
2935
  flex-direction: row; align-items: center; overflow-x: auto; min-width: 0;
2857
2936
  border-right: 0; border-bottom: 1px solid var(--line);
2858
2937
  padding: 8px;
@@ -3730,7 +3809,7 @@ var stateAdminRoutes = (routes) => {
3730
3809
  };
3731
3810
 
3732
3811
  // ../core/dist/version.js
3733
- var PACKAGE_VERSION = true ? "1.6.0" : UNRELEASED_VERSION;
3812
+ var PACKAGE_VERSION = true ? "1.6.1" : UNRELEASED_VERSION;
3734
3813
 
3735
3814
  // ../core/dist/signing.js
3736
3815
  var encoder = new TextEncoder();
@@ -5306,4 +5385,4 @@ export {
5306
5385
  normalizeDate,
5307
5386
  CareTalkAPI
5308
5387
  };
5309
- //# sourceMappingURL=chunk-VBOYORPS.js.map
5388
+ //# sourceMappingURL=chunk-3SMS2XHQ.js.map