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