@crvouga/mockingbird-service-twilio 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-twilio
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
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createRuntime
3
- } from "./chunk-VV4XROZJ.js";
3
+ } from "./chunk-KBYCDOZD.js";
4
4
 
5
5
  // ../../adapters/node/dist/cli.js
6
6
  import { readFile } from "node:fs/promises";
@@ -389,4 +389,4 @@ export {
389
389
  createServer2 as createServer,
390
390
  serveTarget
391
391
  };
392
- //# sourceMappingURL=chunk-5CN3JESR.js.map
392
+ //# sourceMappingURL=chunk-JIDPDWLD.js.map
@@ -2192,6 +2192,50 @@ function bootAdmin(config) {
2192
2192
  if (render)
2193
2193
  await render();
2194
2194
  }
2195
+ const delay = (ms) => new Promise((resolve) => {
2196
+ window.setTimeout(resolve, ms);
2197
+ });
2198
+ const confirmAction = async (button, pending, done, work) => {
2199
+ if (button.dataset.pending === "1")
2200
+ return;
2201
+ const label = button.textContent ?? "";
2202
+ const width = button.offsetWidth;
2203
+ button.dataset.pending = "1";
2204
+ button.classList.add("pressed");
2205
+ button.setAttribute("aria-busy", "true");
2206
+ button.style.minWidth = `${width}px`;
2207
+ button.textContent = pending;
2208
+ try {
2209
+ await work();
2210
+ button.textContent = done;
2211
+ await delay(1e3);
2212
+ } catch (error) {
2213
+ showError(error);
2214
+ } finally {
2215
+ button.textContent = label;
2216
+ button.style.minWidth = "";
2217
+ button.classList.remove("pressed");
2218
+ button.removeAttribute("aria-busy");
2219
+ delete button.dataset.pending;
2220
+ }
2221
+ };
2222
+ document.addEventListener("click", (event) => {
2223
+ const target = event.target;
2224
+ if (!(target instanceof Element))
2225
+ return;
2226
+ const button = target.closest("button");
2227
+ if (!(button instanceof HTMLButtonElement) || button.dataset.pending === "1")
2228
+ return;
2229
+ button.classList.add("pressed");
2230
+ const token = String(Date.now());
2231
+ button.dataset.press = token;
2232
+ window.setTimeout(() => {
2233
+ if (button.dataset.press !== token || button.dataset.pending === "1")
2234
+ return;
2235
+ button.classList.remove("pressed");
2236
+ delete button.dataset.press;
2237
+ }, 450);
2238
+ });
2195
2239
  el("namespace", HTMLSelectElement).addEventListener("change", () => {
2196
2240
  state.namespace = el("namespace", HTMLSelectElement).value;
2197
2241
  refresh().catch(showError);
@@ -2202,7 +2246,7 @@ function bootAdmin(config) {
2202
2246
  refresh().catch(showError);
2203
2247
  });
2204
2248
  el("refresh", HTMLButtonElement).addEventListener("click", () => {
2205
- refresh().catch(showError);
2249
+ confirmAction(el("refresh", HTMLButtonElement), "Refreshing...", "Updated", () => refresh()).catch(showError);
2206
2250
  });
2207
2251
  el("collections", HTMLElement).addEventListener("click", (event) => {
2208
2252
  const target = event.target;
@@ -2325,10 +2369,10 @@ function bootAdmin(config) {
2325
2369
  }).then(() => renderFaults()).catch(showError);
2326
2370
  });
2327
2371
  el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
2328
- api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
2372
+ confirmAction(el("fault-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/faults").then(() => renderFaults())).catch(showError);
2329
2373
  });
2330
2374
  el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
2331
- api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
2375
+ confirmAction(el("journal-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/requests").then(() => renderJournal())).catch(showError);
2332
2376
  });
2333
2377
  el("route-list", HTMLElement).addEventListener("click", (event) => {
2334
2378
  const target = event.target;
@@ -2443,9 +2487,13 @@ function bootAdmin(config) {
2443
2487
  var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
2444
2488
  var adminClientSource = (config) => {
2445
2489
  const payload = JSON.stringify(config).replace(/</g, "\\u003c");
2490
+ const entry = bootAdmin.name;
2491
+ if (!/^[A-Za-z_$][\w$]*$/.test(entry)) {
2492
+ throw new Error("admin client cannot call its boot function");
2493
+ }
2446
2494
  return `<script>
2447
2495
  ${clientPrelude}
2448
- bootAdmin(${payload});
2496
+ ${entry}(${payload});
2449
2497
  </script>`;
2450
2498
  };
2451
2499
 
@@ -2659,9 +2707,24 @@ var renderAdminDocument = (service) => {
2659
2707
  .controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
2660
2708
  label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
2661
2709
  label.field input, label.field select, .controls input[type="password"] {
2662
- background: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2663
- padding: 7px 8px; min-height: 36px;
2664
- }
2710
+ background-color: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2711
+ padding: 7px 8px; min-height: 36px; line-height: 1.2;
2712
+ }
2713
+ /* A native menu ignores min-height, so Preset rendered about half as tall as
2714
+ the text fields in the same form. Paint it like those fields. */
2715
+ label.field select {
2716
+ -webkit-appearance: none; appearance: none;
2717
+ padding-right: 28px;
2718
+ 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");
2719
+ background-repeat: no-repeat; background-position: right 10px center; background-size: 12px 12px;
2720
+ }
2721
+ .stack > .field > input,
2722
+ .stack > .field > select,
2723
+ .stack > .field > textarea,
2724
+ dialog .field > input,
2725
+ dialog .field > select,
2726
+ dialog .field > textarea { width: 100%; }
2727
+ .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger, nav.side button { cursor: pointer; }
2665
2728
  .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger {
2666
2729
  border-radius: 9px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line);
2667
2730
  background: var(--bg-elev);
@@ -2669,6 +2732,14 @@ var renderAdminDocument = (service) => {
2669
2732
  .btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
2670
2733
  .btn-quiet { background: transparent; }
2671
2734
  .btn-danger { color: var(--err); }
2735
+ /* Held after mouseup. Clearing an empty journal changes nothing on screen,
2736
+ so the press itself has to stay visible. */
2737
+ .icon-btn:active, .btn:active, .btn-quiet:active, .btn-danger:active, nav.side button:active,
2738
+ .icon-btn.pressed, .btn.pressed, .btn-quiet.pressed, .btn-danger.pressed, nav.side button.pressed {
2739
+ background: var(--bg-inset); border-color: var(--line-strong); color: var(--ink);
2740
+ }
2741
+ .btn-danger:active, .btn-danger.pressed { background: var(--err-soft); color: var(--err); }
2742
+ .btn-primary:active, .btn-primary.pressed { filter: brightness(0.88); color: var(--accent-ink); }
2672
2743
  /* minmax(0, 1fr): a 1fr column will not shrink below its content, so a long
2673
2744
  record or the full nav row would widen the page past the viewport. */
2674
2745
  .layout {
@@ -2757,8 +2828,16 @@ var renderAdminDocument = (service) => {
2757
2828
  input, select, textarea { font-size: 16px; }
2758
2829
  .brand, .vendor, .controls { width: 100%; }
2759
2830
  label.field, label.field select, label.field input { flex: 1; min-width: 0; }
2760
- .layout { grid-template-columns: minmax(0, 1fr); }
2831
+ /* The layout fills the screen. A single-column grid stretches every auto
2832
+ row, so the tab bar grew with the leftover height and the buttons floated
2833
+ in the middle of it. The bar stays one row; the page below takes the rest. */
2834
+ .layout {
2835
+ grid-template-columns: minmax(0, 1fr);
2836
+ grid-template-rows: auto minmax(0, 1fr);
2837
+ align-content: start;
2838
+ }
2761
2839
  nav.side {
2840
+ align-self: start; height: auto;
2762
2841
  flex-direction: row; align-items: center; overflow-x: auto; min-width: 0;
2763
2842
  border-right: 0; border-bottom: 1px solid var(--line);
2764
2843
  padding: 8px;
@@ -3552,7 +3631,7 @@ var stateAdminRoutes = (routes) => {
3552
3631
  };
3553
3632
 
3554
3633
  // ../core/dist/version.js
3555
- var PACKAGE_VERSION = true ? "1.6.0" : UNRELEASED_VERSION;
3634
+ var PACKAGE_VERSION = true ? "1.6.1" : UNRELEASED_VERSION;
3556
3635
 
3557
3636
  // ../core/dist/signing.js
3558
3637
  var encoder = new TextEncoder();
@@ -5906,4 +5985,4 @@ export {
5906
5985
  twilioError,
5907
5986
  TwilioAPI
5908
5987
  };
5909
- //# sourceMappingURL=chunk-VV4XROZJ.js.map
5988
+ //# sourceMappingURL=chunk-KBYCDOZD.js.map