@dennisrongo/dsh-weather 0.3.2 → 0.4.0
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/README.md +1 -1
- package/lib/client.js +222 -9
- package/lib/index.js +83 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Weather bar for the [DeepSeek Harness](https://github.com/deepseek-ai/dsh) web U
|
|
|
9
9
|
|
|
10
10
|
- **Data:** [Open-Meteo](https://open-meteo.com) (free, no API key) via the browser.
|
|
11
11
|
- **Location:** `localStorage["dsh-weather:location"] = "Your City"` if set, else a coarse IP-geolocation provider chain, else New York.
|
|
12
|
-
- **Placement:** centred on the space the shell has actually left it, not on the viewport
|
|
12
|
+
- **Placement:** drag the pill anywhere. Until you move it, it stays centred on the space the shell has actually left it, not on the viewport — it measures the shell frame's content box (which already excludes `dsh-mission-control`'s docked rail) and subtracts any overlay flying a `data-dsh-overlay-claim="right"` marker, today `dsh-plan-board`'s plan panel. After a drag the spot is remembered (cookie `dsh-weather-pos`, with `localStorage["dsh-weather:pos"]` as a fallback) so it survives a web UI or DSH Desktop restart. Click the temperature to toggle units; that does not start a drag.
|
|
13
13
|
- **Mount point:** additive `shell.overlay` slot — pure-consumer client plugin, empty host half.
|
|
14
14
|
- **Units:** Fahrenheit by default — click the temperature to toggle °F/°C. The choice is remembered in `localStorage["dsh-weather:unit"]`.
|
|
15
15
|
- **Refresh:** every 15 min, plus a manual ⟳ button.
|
package/lib/client.js
CHANGED
|
@@ -44,6 +44,81 @@ __export(client_exports, {
|
|
|
44
44
|
});
|
|
45
45
|
module.exports = __toCommonJS(client_exports);
|
|
46
46
|
var import_react = __toESM(require("react"), 1);
|
|
47
|
+
|
|
48
|
+
// src/position.ts
|
|
49
|
+
var POS_COOKIE = "dsh-weather-pos";
|
|
50
|
+
var POS_KEY = "dsh-weather:pos";
|
|
51
|
+
var MAX_AGE = 31536e4;
|
|
52
|
+
function formatPos(pos) {
|
|
53
|
+
return `${pos.x.toFixed(4)},${pos.y.toFixed(4)}`;
|
|
54
|
+
}
|
|
55
|
+
function parsePos(raw) {
|
|
56
|
+
if (raw == null || raw === "") return null;
|
|
57
|
+
const comma = raw.indexOf(",");
|
|
58
|
+
if (comma === -1) return null;
|
|
59
|
+
const x = Number(raw.slice(0, comma));
|
|
60
|
+
const y = Number(raw.slice(comma + 1));
|
|
61
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
62
|
+
if (x < 0 || x > 1 || y < 0 || y > 1) return null;
|
|
63
|
+
return { x, y };
|
|
64
|
+
}
|
|
65
|
+
function rangeOf(box) {
|
|
66
|
+
const minLeft = box.pad;
|
|
67
|
+
const maxLeft = Math.max(minLeft, box.viewW - box.width - box.pad);
|
|
68
|
+
const minTop = Math.max(box.pad, box.minTop);
|
|
69
|
+
const maxTop = Math.max(minTop, box.viewH - box.height - box.pad);
|
|
70
|
+
return { minLeft, maxLeft, minTop, maxTop };
|
|
71
|
+
}
|
|
72
|
+
function clampPx(left, top, box) {
|
|
73
|
+
const { minLeft, maxLeft, minTop, maxTop } = rangeOf(box);
|
|
74
|
+
return {
|
|
75
|
+
left: Math.min(maxLeft, Math.max(minLeft, left)),
|
|
76
|
+
top: Math.min(maxTop, Math.max(minTop, top))
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
function posToPx(pos, box) {
|
|
80
|
+
const { minLeft, maxLeft, minTop, maxTop } = rangeOf(box);
|
|
81
|
+
return {
|
|
82
|
+
left: minLeft + pos.x * (maxLeft - minLeft),
|
|
83
|
+
top: minTop + pos.y * (maxTop - minTop)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function pxToPos(left, top, box) {
|
|
87
|
+
const { minLeft, maxLeft, minTop, maxTop } = rangeOf(box);
|
|
88
|
+
const spanX = maxLeft - minLeft;
|
|
89
|
+
const spanY = maxTop - minTop;
|
|
90
|
+
return {
|
|
91
|
+
x: spanX <= 0 ? 0 : (left - minLeft) / spanX,
|
|
92
|
+
y: spanY <= 0 ? 0 : (top - minTop) / spanY
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function readCookie(jar, name) {
|
|
96
|
+
for (const part of jar.split(";")) {
|
|
97
|
+
const at = part.indexOf("=");
|
|
98
|
+
if (at === -1) continue;
|
|
99
|
+
if (part.slice(0, at).trim() !== name) continue;
|
|
100
|
+
return decodeURIComponent(part.slice(at + 1).trim());
|
|
101
|
+
}
|
|
102
|
+
return void 0;
|
|
103
|
+
}
|
|
104
|
+
function posCookieWrite(pos) {
|
|
105
|
+
return `${POS_COOKIE}=${encodeURIComponent(formatPos(pos))}; Path=/; Max-Age=${MAX_AGE}; SameSite=Lax`;
|
|
106
|
+
}
|
|
107
|
+
function loadPosFromStores(jar, storageGet) {
|
|
108
|
+
try {
|
|
109
|
+
const cookie = readCookie(jar, POS_COOKIE);
|
|
110
|
+
const fromCookie = parsePos(cookie);
|
|
111
|
+
if (fromCookie) return fromCookie;
|
|
112
|
+
} catch {
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
return parsePos(storageGet(POS_KEY));
|
|
116
|
+
} catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/client.tsx
|
|
47
122
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
48
123
|
var inject = ["slots"];
|
|
49
124
|
function describeCode(code, isDay = true) {
|
|
@@ -94,6 +169,24 @@ function saveUnit(unit) {
|
|
|
94
169
|
} catch {
|
|
95
170
|
}
|
|
96
171
|
}
|
|
172
|
+
function loadPos() {
|
|
173
|
+
try {
|
|
174
|
+
return loadPosFromStores(document.cookie, (key) => window.localStorage.getItem(key));
|
|
175
|
+
} catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function savePos(pos) {
|
|
180
|
+
try {
|
|
181
|
+
document.cookie = posCookieWrite(pos);
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
window.localStorage.setItem(POS_KEY, formatPos(pos));
|
|
186
|
+
} catch {
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
var DRAG_THRESHOLD_PX = 4;
|
|
97
190
|
var DEFAULT_LOCATION = { latitude: 40.7128, longitude: -74.006, label: "New York" };
|
|
98
191
|
var GEO_TIMEOUT_MS = 4e3;
|
|
99
192
|
var GEO_PROVIDERS = [
|
|
@@ -211,9 +304,10 @@ var BAR_STYLES = `
|
|
|
211
304
|
font: 400 13px/1.4 var(--dsw-font-family, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif);
|
|
212
305
|
font-variant-numeric: tabular-nums;
|
|
213
306
|
box-shadow: var(--dsw-shadow-lv3, 0 0 1px rgba(0,0,0,0.2), 0 8px 24px rgba(0,0,0,0.12));
|
|
214
|
-
cursor:
|
|
307
|
+
cursor: grab;
|
|
215
308
|
user-select: none;
|
|
216
309
|
white-space: nowrap;
|
|
310
|
+
touch-action: none;
|
|
217
311
|
/* DSH Desktop on Windows overlays a 36px window-drag strip at the top of the
|
|
218
312
|
viewport (#dsh-desktop-windows-drag-region: -webkit-app-region: drag,
|
|
219
313
|
z-index 2147483644, pointer-events: none) that the compositor resolves
|
|
@@ -229,9 +323,12 @@ var BAR_STYLES = `
|
|
|
229
323
|
body.dsh-desktop-windows-titlebar-layout .dshwx {
|
|
230
324
|
/* Clear the desktop drag strip: 36px strip + the usual 8px gap. The body
|
|
231
325
|
class is added by DSH Desktop's preload on Windows only, so the browser
|
|
232
|
-
and non-Windows builds keep top: 8px.
|
|
326
|
+
and non-Windows builds keep top: 8px. A user-placed inline top overrides
|
|
327
|
+
this; clampPx's minTop keeps a drag from parking back inside the strip. */
|
|
233
328
|
top: 44px;
|
|
234
329
|
}
|
|
330
|
+
.dshwx[data-placed] { transform: none; }
|
|
331
|
+
.dshwx[data-dragging] { cursor: grabbing; }
|
|
235
332
|
body[data-ds-dark-theme] .dshwx { box-shadow: 0 0 0 1px rgba(0,0,0,0.5), 0 8px 24px rgba(0,0,0,0.5); }
|
|
236
333
|
.dshwx[hidden] { display: none; }
|
|
237
334
|
.dshwx-icon { font-size: 16px; }
|
|
@@ -356,6 +453,20 @@ function zoomOf(el) {
|
|
|
356
453
|
const width = el.getBoundingClientRect().width;
|
|
357
454
|
return el.offsetWidth > 0 && width > 0 ? width / el.offsetWidth : 1;
|
|
358
455
|
}
|
|
456
|
+
function desktopMinTop() {
|
|
457
|
+
return document.body.classList.contains("dsh-desktop-windows-titlebar-layout") ? 44 : 8;
|
|
458
|
+
}
|
|
459
|
+
function clampBoxOf(el, zoom) {
|
|
460
|
+
const rect = el.getBoundingClientRect();
|
|
461
|
+
return {
|
|
462
|
+
width: rect.width / zoom,
|
|
463
|
+
height: rect.height / zoom,
|
|
464
|
+
viewW: window.innerWidth / zoom,
|
|
465
|
+
viewH: window.innerHeight / zoom,
|
|
466
|
+
minTop: desktopMinTop(),
|
|
467
|
+
pad: 8
|
|
468
|
+
};
|
|
469
|
+
}
|
|
359
470
|
function tierOf(width) {
|
|
360
471
|
if (width === void 0) return "full";
|
|
361
472
|
if (width < 200) return "none";
|
|
@@ -437,24 +548,126 @@ function WeatherBar() {
|
|
|
437
548
|
const [state, setState] = import_react.default.useState({ status: "loading" });
|
|
438
549
|
const [busy, setBusy] = import_react.default.useState(false);
|
|
439
550
|
const [unit, setUnit] = import_react.default.useState(loadUnit);
|
|
551
|
+
const [placed, setPlaced] = import_react.default.useState(loadPos);
|
|
552
|
+
const [livePx, setLivePx] = import_react.default.useState(null);
|
|
553
|
+
const [dragging, setDragging] = import_react.default.useState(false);
|
|
554
|
+
const drag = import_react.default.useRef(null);
|
|
440
555
|
const ref = import_react.default.useRef(null);
|
|
441
556
|
const fit = useBandFit(ref);
|
|
557
|
+
const parked = placed !== null || dragging;
|
|
558
|
+
const measured = tierOf(fit?.width);
|
|
559
|
+
const fitTier = parked && measured === "none" ? "tiny" : measured;
|
|
560
|
+
import_react.default.useLayoutEffect(() => {
|
|
561
|
+
if (placed === null || dragging) return;
|
|
562
|
+
const el = ref.current;
|
|
563
|
+
if (el === null) return;
|
|
564
|
+
const apply2 = () => {
|
|
565
|
+
const zoom = zoomOf(el);
|
|
566
|
+
if (el.getBoundingClientRect().height === 0) return;
|
|
567
|
+
const next = posToPx(placed, clampBoxOf(el, zoom));
|
|
568
|
+
setLivePx(
|
|
569
|
+
(prev) => prev !== null && Math.abs(prev.left - next.left) < 0.5 && Math.abs(prev.top - next.top) < 0.5 ? prev : next
|
|
570
|
+
);
|
|
571
|
+
};
|
|
572
|
+
apply2();
|
|
573
|
+
const resize = new ResizeObserver(apply2);
|
|
574
|
+
resize.observe(el);
|
|
575
|
+
const scaleWatch = new MutationObserver(apply2);
|
|
576
|
+
scaleWatch.observe(document.body, { attributes: true, attributeFilter: ["style", "class"] });
|
|
577
|
+
window.addEventListener("resize", apply2);
|
|
578
|
+
return () => {
|
|
579
|
+
resize.disconnect();
|
|
580
|
+
scaleWatch.disconnect();
|
|
581
|
+
window.removeEventListener("resize", apply2);
|
|
582
|
+
};
|
|
583
|
+
}, [placed, dragging]);
|
|
584
|
+
const onPointerDown = (event) => {
|
|
585
|
+
if (event.button !== 0) return;
|
|
586
|
+
const node = event.target;
|
|
587
|
+
if (node instanceof Element && node.closest("button")) return;
|
|
588
|
+
const el = ref.current;
|
|
589
|
+
if (el === null) return;
|
|
590
|
+
const zoom = zoomOf(el);
|
|
591
|
+
const rect = el.getBoundingClientRect();
|
|
592
|
+
el.setPointerCapture(event.pointerId);
|
|
593
|
+
drag.current = {
|
|
594
|
+
pointerId: event.pointerId,
|
|
595
|
+
startX: event.clientX,
|
|
596
|
+
startY: event.clientY,
|
|
597
|
+
origLeft: rect.left / zoom,
|
|
598
|
+
origTop: rect.top / zoom,
|
|
599
|
+
zoom,
|
|
600
|
+
moved: false
|
|
601
|
+
};
|
|
602
|
+
};
|
|
603
|
+
const onPointerMove = (event) => {
|
|
604
|
+
const session = drag.current;
|
|
605
|
+
if (session === null || event.pointerId !== session.pointerId) return;
|
|
606
|
+
const el = ref.current;
|
|
607
|
+
if (el === null) return;
|
|
608
|
+
const dx = event.clientX - session.startX;
|
|
609
|
+
const dy = event.clientY - session.startY;
|
|
610
|
+
if (!session.moved) {
|
|
611
|
+
if (dx * dx + dy * dy < DRAG_THRESHOLD_PX * DRAG_THRESHOLD_PX) return;
|
|
612
|
+
session.moved = true;
|
|
613
|
+
setDragging(true);
|
|
614
|
+
}
|
|
615
|
+
const next = clampPx(
|
|
616
|
+
session.origLeft + dx / session.zoom,
|
|
617
|
+
session.origTop + dy / session.zoom,
|
|
618
|
+
clampBoxOf(el, session.zoom)
|
|
619
|
+
);
|
|
620
|
+
setLivePx(next);
|
|
621
|
+
};
|
|
622
|
+
const endDrag = (event) => {
|
|
623
|
+
const session = drag.current;
|
|
624
|
+
if (session === null || event.pointerId !== session.pointerId) return;
|
|
625
|
+
drag.current = null;
|
|
626
|
+
const el = ref.current;
|
|
627
|
+
if (!session.moved || el === null) {
|
|
628
|
+
setDragging(false);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
const box = clampBoxOf(el, session.zoom);
|
|
632
|
+
const next = clampPx(
|
|
633
|
+
session.origLeft + (event.clientX - session.startX) / session.zoom,
|
|
634
|
+
session.origTop + (event.clientY - session.startY) / session.zoom,
|
|
635
|
+
box
|
|
636
|
+
);
|
|
637
|
+
const pos = pxToPos(next.left, next.top, box);
|
|
638
|
+
setPlaced(pos);
|
|
639
|
+
setLivePx(next);
|
|
640
|
+
setDragging(false);
|
|
641
|
+
savePos(pos);
|
|
642
|
+
};
|
|
643
|
+
const bandStyle = fit === null || fit.width <= 0 ? {} : {
|
|
644
|
+
left: `${fit.centre / fit.zoom}px`,
|
|
645
|
+
maxWidth: `${fit.width / fit.zoom}px`
|
|
646
|
+
};
|
|
647
|
+
const parkedStyle = livePx === null ? { transform: "none" } : {
|
|
648
|
+
left: `${livePx.left}px`,
|
|
649
|
+
top: `${livePx.top}px`,
|
|
650
|
+
transform: "none"
|
|
651
|
+
};
|
|
442
652
|
const shell = {
|
|
443
653
|
ref,
|
|
444
654
|
className: "dshwx",
|
|
445
|
-
|
|
655
|
+
// A parked bar must not vanish when a dock claims the top band — the user
|
|
656
|
+
// put it somewhere on purpose. Map the squeezed-out tier up to tiny so it
|
|
657
|
+
// still sheds rather than hiding.
|
|
658
|
+
"data-fit": fitTier,
|
|
446
659
|
// A non-positive width is the squeezed-out case: the `none` tier hides the
|
|
447
660
|
// bar, and writing a negative max-width would be an ignored declaration
|
|
448
661
|
// that left it at full size behind the overlay.
|
|
449
662
|
//
|
|
450
663
|
// Both lengths are divided by the zoom: `fit` is measured in viewport px
|
|
451
664
|
// and these are author px the zoom scales again (see zoomOf).
|
|
452
|
-
...fit === null || fit.width <= 0 ? {} : {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
665
|
+
...parked ? { "data-placed": "", style: parkedStyle } : fit === null || fit.width <= 0 ? {} : { style: bandStyle },
|
|
666
|
+
...dragging ? { "data-dragging": "" } : {},
|
|
667
|
+
onPointerDown,
|
|
668
|
+
onPointerMove,
|
|
669
|
+
onPointerUp: endDrag,
|
|
670
|
+
onPointerCancel: endDrag
|
|
458
671
|
};
|
|
459
672
|
const toggleUnit = () => {
|
|
460
673
|
setUnit((prev) => {
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,88 @@
|
|
|
1
|
+
// src/position.ts
|
|
2
|
+
var POS_COOKIE = "dsh-weather-pos";
|
|
3
|
+
var POS_KEY = "dsh-weather:pos";
|
|
4
|
+
var MAX_AGE = 31536e4;
|
|
5
|
+
function formatPos(pos) {
|
|
6
|
+
return `${pos.x.toFixed(4)},${pos.y.toFixed(4)}`;
|
|
7
|
+
}
|
|
8
|
+
function parsePos(raw) {
|
|
9
|
+
if (raw == null || raw === "") return null;
|
|
10
|
+
const comma = raw.indexOf(",");
|
|
11
|
+
if (comma === -1) return null;
|
|
12
|
+
const x = Number(raw.slice(0, comma));
|
|
13
|
+
const y = Number(raw.slice(comma + 1));
|
|
14
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return null;
|
|
15
|
+
if (x < 0 || x > 1 || y < 0 || y > 1) return null;
|
|
16
|
+
return { x, y };
|
|
17
|
+
}
|
|
18
|
+
function rangeOf(box) {
|
|
19
|
+
const minLeft = box.pad;
|
|
20
|
+
const maxLeft = Math.max(minLeft, box.viewW - box.width - box.pad);
|
|
21
|
+
const minTop = Math.max(box.pad, box.minTop);
|
|
22
|
+
const maxTop = Math.max(minTop, box.viewH - box.height - box.pad);
|
|
23
|
+
return { minLeft, maxLeft, minTop, maxTop };
|
|
24
|
+
}
|
|
25
|
+
function clampPx(left, top, box) {
|
|
26
|
+
const { minLeft, maxLeft, minTop, maxTop } = rangeOf(box);
|
|
27
|
+
return {
|
|
28
|
+
left: Math.min(maxLeft, Math.max(minLeft, left)),
|
|
29
|
+
top: Math.min(maxTop, Math.max(minTop, top))
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function posToPx(pos, box) {
|
|
33
|
+
const { minLeft, maxLeft, minTop, maxTop } = rangeOf(box);
|
|
34
|
+
return {
|
|
35
|
+
left: minLeft + pos.x * (maxLeft - minLeft),
|
|
36
|
+
top: minTop + pos.y * (maxTop - minTop)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function pxToPos(left, top, box) {
|
|
40
|
+
const { minLeft, maxLeft, minTop, maxTop } = rangeOf(box);
|
|
41
|
+
const spanX = maxLeft - minLeft;
|
|
42
|
+
const spanY = maxTop - minTop;
|
|
43
|
+
return {
|
|
44
|
+
x: spanX <= 0 ? 0 : (left - minLeft) / spanX,
|
|
45
|
+
y: spanY <= 0 ? 0 : (top - minTop) / spanY
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function readCookie(jar, name) {
|
|
49
|
+
for (const part of jar.split(";")) {
|
|
50
|
+
const at = part.indexOf("=");
|
|
51
|
+
if (at === -1) continue;
|
|
52
|
+
if (part.slice(0, at).trim() !== name) continue;
|
|
53
|
+
return decodeURIComponent(part.slice(at + 1).trim());
|
|
54
|
+
}
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
function posCookieWrite(pos) {
|
|
58
|
+
return `${POS_COOKIE}=${encodeURIComponent(formatPos(pos))}; Path=/; Max-Age=${MAX_AGE}; SameSite=Lax`;
|
|
59
|
+
}
|
|
60
|
+
function loadPosFromStores(jar, storageGet) {
|
|
61
|
+
try {
|
|
62
|
+
const cookie = readCookie(jar, POS_COOKIE);
|
|
63
|
+
const fromCookie = parsePos(cookie);
|
|
64
|
+
if (fromCookie) return fromCookie;
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
return parsePos(storageGet(POS_KEY));
|
|
69
|
+
} catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
1
74
|
// src/index.ts
|
|
2
75
|
function apply() {
|
|
3
76
|
}
|
|
4
77
|
export {
|
|
5
|
-
|
|
78
|
+
POS_COOKIE,
|
|
79
|
+
POS_KEY,
|
|
80
|
+
apply,
|
|
81
|
+
clampPx,
|
|
82
|
+
formatPos,
|
|
83
|
+
loadPosFromStores,
|
|
84
|
+
parsePos,
|
|
85
|
+
posCookieWrite,
|
|
86
|
+
posToPx,
|
|
87
|
+
pxToPos
|
|
6
88
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dennisrongo/dsh-weather",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Weather bar for DeepSeek Harness (dsh) — current conditions at the bottom of the web UI via Open-Meteo, rendered into shell.overlay",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"postbuild": "node build/postbuild.mjs",
|
|
30
30
|
"deploy": "node build/postbuild.mjs",
|
|
31
31
|
"typecheck": "tsc --noEmit",
|
|
32
|
-
"test": "node build/build.mjs && node test/smoke.mjs"
|
|
32
|
+
"test": "node build/build.mjs && node test/smoke.mjs && node test/position.mjs"
|
|
33
33
|
},
|
|
34
34
|
"dsh": {
|
|
35
35
|
"bundle": {
|