@crvouga/mockingbird-service-google-maps 1.7.0 → 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 +10 -1
- package/dist/{chunk-KOTM4DOF.js → chunk-AIMXHIOX.js} +104 -13
- package/dist/chunk-AIMXHIOX.js.map +7 -0
- package/dist/{chunk-EC2N5KBB.js → chunk-VCAIWSHH.js} +2 -2
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-KOTM4DOF.js.map +0 -7
- /package/dist/{chunk-EC2N5KBB.js.map → chunk-VCAIWSHH.js.map} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Changelog — @crvouga/mockingbird-service-google-maps
|
|
2
2
|
|
|
3
|
-
## 1.7.
|
|
3
|
+
## 1.7.1 (2026-09-30)
|
|
4
|
+
|
|
5
|
+
### Fixes and improvements
|
|
6
|
+
|
|
7
|
+
- keep validated address lines within 80 characters ([2ba2574](https://github.com/crvouga/mockingbird/commit/2ba2574450c6900155e7b25a09891052faf7ba6f))
|
|
8
|
+
- keep admin buttons pressed until the action finishes ([0aabfcb](https://github.com/crvouga/mockingbird/commit/0aabfcb5f392aa51e193f9c3f817d5877d92bc6a))
|
|
9
|
+
- size admin select fields like the text inputs beside them ([0fb3ef2](https://github.com/crvouga/mockingbird/commit/0fb3ef2ddbf43376c93a2ae6d87c6c9bf890dafc))
|
|
10
|
+
- keep admin section tabs wired when boot is renamed ([4eda25e](https://github.com/crvouga/mockingbird/commit/4eda25eacd59da048d196f7cc8369d7a02b47bfc))
|
|
11
|
+
|
|
12
|
+
## 1.7.0 (2026-09-29)
|
|
4
13
|
|
|
5
14
|
### Features
|
|
6
15
|
|
|
@@ -2088,6 +2088,50 @@ function bootAdmin(config) {
|
|
|
2088
2088
|
if (render)
|
|
2089
2089
|
await render();
|
|
2090
2090
|
}
|
|
2091
|
+
const delay = (ms) => new Promise((resolve2) => {
|
|
2092
|
+
window.setTimeout(resolve2, ms);
|
|
2093
|
+
});
|
|
2094
|
+
const confirmAction = async (button, pending, done, work) => {
|
|
2095
|
+
if (button.dataset.pending === "1")
|
|
2096
|
+
return;
|
|
2097
|
+
const label = button.textContent ?? "";
|
|
2098
|
+
const width = button.offsetWidth;
|
|
2099
|
+
button.dataset.pending = "1";
|
|
2100
|
+
button.classList.add("pressed");
|
|
2101
|
+
button.setAttribute("aria-busy", "true");
|
|
2102
|
+
button.style.minWidth = `${width}px`;
|
|
2103
|
+
button.textContent = pending;
|
|
2104
|
+
try {
|
|
2105
|
+
await work();
|
|
2106
|
+
button.textContent = done;
|
|
2107
|
+
await delay(1e3);
|
|
2108
|
+
} catch (error) {
|
|
2109
|
+
showError(error);
|
|
2110
|
+
} finally {
|
|
2111
|
+
button.textContent = label;
|
|
2112
|
+
button.style.minWidth = "";
|
|
2113
|
+
button.classList.remove("pressed");
|
|
2114
|
+
button.removeAttribute("aria-busy");
|
|
2115
|
+
delete button.dataset.pending;
|
|
2116
|
+
}
|
|
2117
|
+
};
|
|
2118
|
+
document.addEventListener("click", (event) => {
|
|
2119
|
+
const target = event.target;
|
|
2120
|
+
if (!(target instanceof Element))
|
|
2121
|
+
return;
|
|
2122
|
+
const button = target.closest("button");
|
|
2123
|
+
if (!(button instanceof HTMLButtonElement) || button.dataset.pending === "1")
|
|
2124
|
+
return;
|
|
2125
|
+
button.classList.add("pressed");
|
|
2126
|
+
const token = String(Date.now());
|
|
2127
|
+
button.dataset.press = token;
|
|
2128
|
+
window.setTimeout(() => {
|
|
2129
|
+
if (button.dataset.press !== token || button.dataset.pending === "1")
|
|
2130
|
+
return;
|
|
2131
|
+
button.classList.remove("pressed");
|
|
2132
|
+
delete button.dataset.press;
|
|
2133
|
+
}, 450);
|
|
2134
|
+
});
|
|
2091
2135
|
el("namespace", HTMLSelectElement).addEventListener("change", () => {
|
|
2092
2136
|
state.namespace = el("namespace", HTMLSelectElement).value;
|
|
2093
2137
|
refresh().catch(showError);
|
|
@@ -2098,7 +2142,7 @@ function bootAdmin(config) {
|
|
|
2098
2142
|
refresh().catch(showError);
|
|
2099
2143
|
});
|
|
2100
2144
|
el("refresh", HTMLButtonElement).addEventListener("click", () => {
|
|
2101
|
-
refresh().catch(showError);
|
|
2145
|
+
confirmAction(el("refresh", HTMLButtonElement), "Refreshing...", "Updated", () => refresh()).catch(showError);
|
|
2102
2146
|
});
|
|
2103
2147
|
el("collections", HTMLElement).addEventListener("click", (event) => {
|
|
2104
2148
|
const target = event.target;
|
|
@@ -2221,10 +2265,10 @@ function bootAdmin(config) {
|
|
|
2221
2265
|
}).then(() => renderFaults()).catch(showError);
|
|
2222
2266
|
});
|
|
2223
2267
|
el("fault-clear", HTMLButtonElement).addEventListener("click", () => {
|
|
2224
|
-
api("DELETE", "/faults").then(() => renderFaults()).catch(showError);
|
|
2268
|
+
confirmAction(el("fault-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/faults").then(() => renderFaults())).catch(showError);
|
|
2225
2269
|
});
|
|
2226
2270
|
el("journal-clear", HTMLButtonElement).addEventListener("click", () => {
|
|
2227
|
-
api("DELETE", "/requests").then(() => renderJournal()).catch(showError);
|
|
2271
|
+
confirmAction(el("journal-clear", HTMLButtonElement), "Clearing...", "Cleared", () => api("DELETE", "/requests").then(() => renderJournal())).catch(showError);
|
|
2228
2272
|
});
|
|
2229
2273
|
el("route-list", HTMLElement).addEventListener("click", (event) => {
|
|
2230
2274
|
const target = event.target;
|
|
@@ -2339,9 +2383,13 @@ function bootAdmin(config) {
|
|
|
2339
2383
|
var clientPrelude = [isRecord2, mountAdminBrand, startAdminBrand, faultPresetList, bootAdmin].map((fn) => fn.toString()).join("\n");
|
|
2340
2384
|
var adminClientSource = (config) => {
|
|
2341
2385
|
const payload = JSON.stringify(config).replace(/</g, "\\u003c");
|
|
2386
|
+
const entry = bootAdmin.name;
|
|
2387
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(entry)) {
|
|
2388
|
+
throw new Error("admin client cannot call its boot function");
|
|
2389
|
+
}
|
|
2342
2390
|
return `<script>
|
|
2343
2391
|
${clientPrelude}
|
|
2344
|
-
|
|
2392
|
+
${entry}(${payload});
|
|
2345
2393
|
</script>`;
|
|
2346
2394
|
};
|
|
2347
2395
|
|
|
@@ -2555,9 +2603,24 @@ var renderAdminDocument = (service) => {
|
|
|
2555
2603
|
.controls { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
2556
2604
|
label.field { display: flex; flex-direction: column; gap: 2px; font-size: 11px; color: var(--faint); }
|
|
2557
2605
|
label.field input, label.field select, .controls input[type="password"] {
|
|
2558
|
-
background: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
|
|
2559
|
-
padding: 7px 8px; min-height: 36px;
|
|
2560
|
-
}
|
|
2606
|
+
background-color: var(--bg-inset); border: 1px solid var(--line); border-radius: 8px;
|
|
2607
|
+
padding: 7px 8px; min-height: 36px; line-height: 1.2;
|
|
2608
|
+
}
|
|
2609
|
+
/* A native menu ignores min-height, so Preset rendered about half as tall as
|
|
2610
|
+
the text fields in the same form. Paint it like those fields. */
|
|
2611
|
+
label.field select {
|
|
2612
|
+
-webkit-appearance: none; appearance: none;
|
|
2613
|
+
padding-right: 28px;
|
|
2614
|
+
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");
|
|
2615
|
+
background-repeat: no-repeat; background-position: right 10px center; background-size: 12px 12px;
|
|
2616
|
+
}
|
|
2617
|
+
.stack > .field > input,
|
|
2618
|
+
.stack > .field > select,
|
|
2619
|
+
.stack > .field > textarea,
|
|
2620
|
+
dialog .field > input,
|
|
2621
|
+
dialog .field > select,
|
|
2622
|
+
dialog .field > textarea { width: 100%; }
|
|
2623
|
+
.icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger, nav.side button { cursor: pointer; }
|
|
2561
2624
|
.icon-btn, .btn, .btn-primary, .btn-quiet, .btn-danger {
|
|
2562
2625
|
border-radius: 9px; min-height: 36px; padding: 0 12px; border: 1px solid var(--line);
|
|
2563
2626
|
background: var(--bg-elev);
|
|
@@ -2565,6 +2628,14 @@ var renderAdminDocument = (service) => {
|
|
|
2565
2628
|
.btn-primary { background: var(--accent); color: var(--accent-ink); border-color: transparent; }
|
|
2566
2629
|
.btn-quiet { background: transparent; }
|
|
2567
2630
|
.btn-danger { color: var(--err); }
|
|
2631
|
+
/* Held after mouseup. Clearing an empty journal changes nothing on screen,
|
|
2632
|
+
so the press itself has to stay visible. */
|
|
2633
|
+
.icon-btn:active, .btn:active, .btn-quiet:active, .btn-danger:active, nav.side button:active,
|
|
2634
|
+
.icon-btn.pressed, .btn.pressed, .btn-quiet.pressed, .btn-danger.pressed, nav.side button.pressed {
|
|
2635
|
+
background: var(--bg-inset); border-color: var(--line-strong); color: var(--ink);
|
|
2636
|
+
}
|
|
2637
|
+
.btn-danger:active, .btn-danger.pressed { background: var(--err-soft); color: var(--err); }
|
|
2638
|
+
.btn-primary:active, .btn-primary.pressed { filter: brightness(0.88); color: var(--accent-ink); }
|
|
2568
2639
|
/* minmax(0, 1fr): a 1fr column will not shrink below its content, so a long
|
|
2569
2640
|
record or the full nav row would widen the page past the viewport. */
|
|
2570
2641
|
.layout {
|
|
@@ -2653,8 +2724,16 @@ var renderAdminDocument = (service) => {
|
|
|
2653
2724
|
input, select, textarea { font-size: 16px; }
|
|
2654
2725
|
.brand, .vendor, .controls { width: 100%; }
|
|
2655
2726
|
label.field, label.field select, label.field input { flex: 1; min-width: 0; }
|
|
2656
|
-
|
|
2727
|
+
/* The layout fills the screen. A single-column grid stretches every auto
|
|
2728
|
+
row, so the tab bar grew with the leftover height and the buttons floated
|
|
2729
|
+
in the middle of it. The bar stays one row; the page below takes the rest. */
|
|
2730
|
+
.layout {
|
|
2731
|
+
grid-template-columns: minmax(0, 1fr);
|
|
2732
|
+
grid-template-rows: auto minmax(0, 1fr);
|
|
2733
|
+
align-content: start;
|
|
2734
|
+
}
|
|
2657
2735
|
nav.side {
|
|
2736
|
+
align-self: start; height: auto;
|
|
2658
2737
|
flex-direction: row; align-items: center; overflow-x: auto; min-width: 0;
|
|
2659
2738
|
border-right: 0; border-bottom: 1px solid var(--line);
|
|
2660
2739
|
padding: 8px;
|
|
@@ -3448,7 +3527,7 @@ var stateAdminRoutes = (routes) => {
|
|
|
3448
3527
|
};
|
|
3449
3528
|
|
|
3450
3529
|
// ../core/dist/version.js
|
|
3451
|
-
var PACKAGE_VERSION = true ? "1.7.
|
|
3530
|
+
var PACKAGE_VERSION = true ? "1.7.1" : UNRELEASED_VERSION;
|
|
3452
3531
|
|
|
3453
3532
|
// ../core/dist/signing.js
|
|
3454
3533
|
var encoder = new TextEncoder();
|
|
@@ -5266,6 +5345,14 @@ var POSTAL_ADDRESS = /* @__PURE__ */ new Set([
|
|
|
5266
5345
|
var MAX_INPUT_LENGTH = 280;
|
|
5267
5346
|
var MAX_ADDRESS_LINES = 3;
|
|
5268
5347
|
var MAX_ADDRESS_LINE_LENGTH = 80;
|
|
5348
|
+
var MAX_BARE_UNIT_LENGTH = 8;
|
|
5349
|
+
var postalLines = (parts) => {
|
|
5350
|
+
const pieces = parts.map((part) => part.trim()).filter(Boolean);
|
|
5351
|
+
if (pieces.length === 0) return [];
|
|
5352
|
+
const joined = pieces.join(" ");
|
|
5353
|
+
if (joined.length <= MAX_ADDRESS_LINE_LENGTH) return [joined];
|
|
5354
|
+
return pieces.map((part) => part.slice(0, MAX_ADDRESS_LINE_LENGTH)).slice(0, MAX_ADDRESS_LINES);
|
|
5355
|
+
};
|
|
5269
5356
|
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5270
5357
|
var optionalString = (value, field) => {
|
|
5271
5358
|
if (value === void 0 || value === null) return void 0;
|
|
@@ -5447,7 +5534,10 @@ var resolve = (input, rows) => {
|
|
|
5447
5534
|
const second = rest.join(" ").trim();
|
|
5448
5535
|
if (second) {
|
|
5449
5536
|
const only = UNIT_ONLY.exec(second);
|
|
5450
|
-
|
|
5537
|
+
const value = only?.[2];
|
|
5538
|
+
if (only && value && (only[1] !== void 0 || value.length <= MAX_BARE_UNIT_LENGTH)) {
|
|
5539
|
+
unit = parseUnit(only[1], value);
|
|
5540
|
+
}
|
|
5451
5541
|
}
|
|
5452
5542
|
} else {
|
|
5453
5543
|
const joined = input.addressLines.join(", ");
|
|
@@ -5610,13 +5700,14 @@ var validateAddress = (request, rows, options = {}) => {
|
|
|
5610
5700
|
hasInferred = input.regionCode === void 0;
|
|
5611
5701
|
unconfirmed = components.filter((c) => c.confirmationLevel !== "CONFIRMED").map((c) => c.componentType);
|
|
5612
5702
|
const line = [streetText, unit ? unitText(unit) : ""].filter(Boolean).join(" ");
|
|
5703
|
+
const lines = postalLines([streetText, unit ? unitText(unit) : ""]);
|
|
5613
5704
|
postalAddress = compact({
|
|
5614
5705
|
regionCode: region,
|
|
5615
5706
|
languageCode: "en",
|
|
5616
5707
|
postalCode: input.postalCode,
|
|
5617
5708
|
administrativeArea: state,
|
|
5618
5709
|
locality: input.locality,
|
|
5619
|
-
addressLines:
|
|
5710
|
+
addressLines: lines.length > 0 ? lines : void 0
|
|
5620
5711
|
});
|
|
5621
5712
|
formattedAddress2 = [
|
|
5622
5713
|
line,
|
|
@@ -5700,7 +5791,7 @@ var validateAddress = (request, rows, options = {}) => {
|
|
|
5700
5791
|
postalCode: zip,
|
|
5701
5792
|
administrativeArea: row2.state,
|
|
5702
5793
|
locality: row2.city,
|
|
5703
|
-
addressLines: [
|
|
5794
|
+
addressLines: postalLines([street, r.unit ? unitText(r.unit) : ""])
|
|
5704
5795
|
};
|
|
5705
5796
|
formattedAddress2 = `${line}, ${row2.city}, ${row2.state} ${zip}, USA`;
|
|
5706
5797
|
const place = r.inCorpus ? { placeId: corpusPlaceId(row2), lat: row2.lat, lng: row2.lng } : synthesize(street, row2);
|
|
@@ -6294,4 +6385,4 @@ export {
|
|
|
6294
6385
|
keyCredential,
|
|
6295
6386
|
GoogleMapsAPI
|
|
6296
6387
|
};
|
|
6297
|
-
//# sourceMappingURL=chunk-
|
|
6388
|
+
//# sourceMappingURL=chunk-AIMXHIOX.js.map
|