@crvouga/mockingbird-service-slack 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-slack
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
 
@@ -2162,6 +2162,50 @@ function bootAdmin(config) {
2162
2162
  if (render)
2163
2163
  await render();
2164
2164
  }
2165
+ const delay = (ms) => new Promise((resolve) => {
2166
+ window.setTimeout(resolve, ms);
2167
+ });
2168
+ const confirmAction = async (button, pending, done, work) => {
2169
+ if (button.dataset.pending === "1")
2170
+ return;
2171
+ const label = button.textContent ?? "";
2172
+ const width = button.offsetWidth;
2173
+ button.dataset.pending = "1";
2174
+ button.classList.add("pressed");
2175
+ button.setAttribute("aria-busy", "true");
2176
+ button.style.minWidth = `${width}px`;
2177
+ button.textContent = pending;
2178
+ try {
2179
+ await work();
2180
+ button.textContent = done;
2181
+ await delay(1e3);
2182
+ } catch (error) {
2183
+ showError(error);
2184
+ } finally {
2185
+ button.textContent = label;
2186
+ button.style.minWidth = "";
2187
+ button.classList.remove("pressed");
2188
+ button.removeAttribute("aria-busy");
2189
+ delete button.dataset.pending;
2190
+ }
2191
+ };
2192
+ document.addEventListener("click", (event) => {
2193
+ const target = event.target;
2194
+ if (!(target instanceof Element))
2195
+ return;
2196
+ const button = target.closest("button");
2197
+ if (!(button instanceof HTMLButtonElement) || button.dataset.pending === "1")
2198
+ return;
2199
+ button.classList.add("pressed");
2200
+ const token = String(Date.now());
2201
+ button.dataset.press = token;
2202
+ window.setTimeout(() => {
2203
+ if (button.dataset.press !== token || button.dataset.pending === "1")
2204
+ return;
2205
+ button.classList.remove("pressed");
2206
+ delete button.dataset.press;
2207
+ }, 450);
2208
+ });
2165
2209
  el("namespace", HTMLSelectElement).addEventListener("change", () => {
2166
2210
  state.namespace = el("namespace", HTMLSelectElement).value;
2167
2211
  refresh().catch(showError);
@@ -2172,7 +2216,7 @@ function bootAdmin(config) {
2172
2216
  refresh().catch(showError);
2173
2217
  });
2174
2218
  el("refresh", HTMLButtonElement).addEventListener("click", () => {
2175
- refresh().catch(showError);
2219
+ confirmAction(el("refresh", HTMLButtonElement), "Refreshing...", "Updated", () => refresh()).catch(showError);
2176
2220
  });
2177
2221
  el("collections", HTMLElement).addEventListener("click", (event) => {
2178
2222
  const target = event.target;
@@ -2295,10 +2339,10 @@ function bootAdmin(config) {
2295
2339
  }).then(() => renderFaults()).catch(showError);
2296
2340
  });
2297
2341
  el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
2298
- api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
2342
+ confirmAction(el("fault-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/faults").then(() => renderFaults())).catch(showError);
2299
2343
  });
2300
2344
  el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
2301
- api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
2345
+ confirmAction(el("journal-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/requests").then(() => renderJournal())).catch(showError);
2302
2346
  });
2303
2347
  el("route-list", HTMLElement).addEventListener("click", (event) => {
2304
2348
  const target = event.target;
@@ -2413,9 +2457,13 @@ function bootAdmin(config) {
2413
2457
  var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
2414
2458
  var adminClientSource = (config) => {
2415
2459
  const payload = JSON.stringify(config).replace(/</g, "\\u003c");
2460
+ const entry = bootAdmin.name;
2461
+ if (!/^[A-Za-z_$][\w$]*$/.test(entry)) {
2462
+ throw new Error("admin client cannot call its boot function");
2463
+ }
2416
2464
  return `<script>
2417
2465
  ${clientPrelude}
2418
- bootAdmin(${payload});
2466
+ ${entry}(${payload});
2419
2467
  </script>`;
2420
2468
  };
2421
2469
 
@@ -2629,9 +2677,24 @@ var renderAdminDocument = (service) => {
2629
2677
  .controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
2630
2678
  label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
2631
2679
  label.field input, label.field select, .controls input[type="password"] {
2632
- background: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2633
- padding: 7px 8px; min-height: 36px;
2634
- }
2680
+ background-color: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
2681
+ padding: 7px 8px; min-height: 36px; line-height: 1.2;
2682
+ }
2683
+ /* A native menu ignores min-height, so Preset rendered about half as tall as
2684
+ the text fields in the same form. Paint it like those fields. */
2685
+ label.field select {
2686
+ -webkit-appearance: none; appearance: none;
2687
+ padding-right: 28px;
2688
+ 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");
2689
+ background-repeat: no-repeat; background-position: right 10px center; background-size: 12px 12px;
2690
+ }
2691
+ .stack > .field > input,
2692
+ .stack > .field > select,
2693
+ .stack > .field > textarea,
2694
+ dialog .field > input,
2695
+ dialog .field > select,
2696
+ dialog .field > textarea { width: 100%; }
2697
+ .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger, nav.side button { cursor: pointer; }
2635
2698
  .icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger {
2636
2699
  border-radius: 9px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line);
2637
2700
  background: var(--bg-elev);
@@ -2639,6 +2702,14 @@ var renderAdminDocument = (service) => {
2639
2702
  .btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
2640
2703
  .btn-quiet { background: transparent; }
2641
2704
  .btn-danger { color: var(--err); }
2705
+ /* Held after mouseup. Clearing an empty journal changes nothing on screen,
2706
+ so the press itself has to stay visible. */
2707
+ .icon-btn:active, .btn:active, .btn-quiet:active, .btn-danger:active, nav.side button:active,
2708
+ .icon-btn.pressed, .btn.pressed, .btn-quiet.pressed, .btn-danger.pressed, nav.side button.pressed {
2709
+ background: var(--bg-inset); border-color: var(--line-strong); color: var(--ink);
2710
+ }
2711
+ .btn-danger:active, .btn-danger.pressed { background: var(--err-soft); color: var(--err); }
2712
+ .btn-primary:active, .btn-primary.pressed { filter: brightness(0.88); color: var(--accent-ink); }
2642
2713
  /* minmax(0, 1fr): a 1fr column will not shrink below its content, so a long
2643
2714
  record or the full nav row would widen the page past the viewport. */
2644
2715
  .layout {
@@ -2727,8 +2798,16 @@ var renderAdminDocument = (service) => {
2727
2798
  input, select, textarea { font-size: 16px; }
2728
2799
  .brand, .vendor, .controls { width: 100%; }
2729
2800
  label.field, label.field select, label.field input { flex: 1; min-width: 0; }
2730
- .layout { grid-template-columns: minmax(0, 1fr); }
2801
+ /* The layout fills the screen. A single-column grid stretches every auto
2802
+ row, so the tab bar grew with the leftover height and the buttons floated
2803
+ in the middle of it. The bar stays one row; the page below takes the rest. */
2804
+ .layout {
2805
+ grid-template-columns: minmax(0, 1fr);
2806
+ grid-template-rows: auto minmax(0, 1fr);
2807
+ align-content: start;
2808
+ }
2731
2809
  nav.side {
2810
+ align-self: start; height: auto;
2732
2811
  flex-direction: row; align-items: center; overflow-x: auto; min-width: 0;
2733
2812
  border-right: 0; border-bottom: 1px solid var(--line);
2734
2813
  padding: 8px;
@@ -3522,7 +3601,7 @@ var stateAdminRoutes = (routes) => {
3522
3601
  };
3523
3602
 
3524
3603
  // ../core/dist/version.js
3525
- var PACKAGE_VERSION = true ? "1.6.0" : UNRELEASED_VERSION;
3604
+ var PACKAGE_VERSION = true ? "1.6.1" : UNRELEASED_VERSION;
3526
3605
 
3527
3606
  // ../core/dist/webhooks.js
3528
3607
  var json5 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
@@ -5352,4 +5431,4 @@ export {
5352
5431
  slackCredential,
5353
5432
  SlackAPI
5354
5433
  };
5355
- //# sourceMappingURL=chunk-XNPGYYVE.js.map
5434
+ //# sourceMappingURL=chunk-6NKJ75J3.js.map