@ksp-gonogo/ui-kit 1.0.0-rc.21 → 1.0.0-rc.27

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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  gridToPixels
3
- } from "./chunk-IZZF25H2.js";
3
+ } from "./chunk-SPBHQUHI.js";
4
4
 
5
5
  // src/tone.ts
6
6
  var TONE_TEXT = {
@@ -243,7 +243,7 @@ var TextButton = styled.button`
243
243
  cursor: not-allowed;
244
244
  }
245
245
  `;
246
- var IconButton = styled.button`
246
+ var IconButton__Body = styled.button`
247
247
  background: none;
248
248
  border: none;
249
249
  cursor: pointer;
@@ -275,6 +275,11 @@ var IconButton = styled.button`
275
275
  justify-content: center;
276
276
  }
277
277
  `;
278
+ var IconButton = forwardRef(
279
+ function IconButton2({ pressed, ...rest }, ref) {
280
+ return /* @__PURE__ */ jsx(IconButton__Body, { ref, "aria-pressed": pressed, ...rest });
281
+ }
282
+ );
278
283
 
279
284
  // src/Form.tsx
280
285
  import styled2, { css as css3 } from "styled-components";
@@ -1704,6 +1709,206 @@ function AugmentEntry({
1704
1709
  );
1705
1710
  }
1706
1711
 
1712
+ // src/Tooltip.tsx
1713
+ import {
1714
+ cloneElement,
1715
+ useCallback,
1716
+ useEffect,
1717
+ useRef as useRef2,
1718
+ useState
1719
+ } from "react";
1720
+ import { createPortal } from "react-dom";
1721
+ import styled7, { createGlobalStyle } from "styled-components";
1722
+ import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
1723
+ var ROOM_BELOW_PX = 120;
1724
+ var CROSSING_MS = 120;
1725
+ var words = (text) => text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
1726
+ function useTooltip(text) {
1727
+ const [at, setAt] = useState(null);
1728
+ const [hovered, setHovered] = useState(false);
1729
+ const [focused, setFocused] = useState(false);
1730
+ const [tipHovered, setTipHovered] = useState(false);
1731
+ const pressed = useRef2(false);
1732
+ const leaving = useRef2(null);
1733
+ const shown = text != null && text !== "" ? text : null;
1734
+ const open = at !== null && (hovered || focused || tipHovered);
1735
+ const cancelLeave = useCallback(() => {
1736
+ if (leaving.current !== null) clearTimeout(leaving.current);
1737
+ leaving.current = null;
1738
+ }, []);
1739
+ const close = useCallback(() => {
1740
+ cancelLeave();
1741
+ setAt(null);
1742
+ setHovered(false);
1743
+ setFocused(false);
1744
+ setTipHovered(false);
1745
+ }, [cancelLeave]);
1746
+ const leaveSoon = useCallback(() => {
1747
+ cancelLeave();
1748
+ leaving.current = setTimeout(() => {
1749
+ leaving.current = null;
1750
+ setHovered(false);
1751
+ setTipHovered(false);
1752
+ }, CROSSING_MS);
1753
+ }, [cancelLeave]);
1754
+ useEffect(() => cancelLeave, [cancelLeave]);
1755
+ useEffect(() => {
1756
+ if (at === null) return;
1757
+ window.addEventListener("scroll", close, true);
1758
+ window.addEventListener("resize", close);
1759
+ return () => {
1760
+ window.removeEventListener("scroll", close, true);
1761
+ window.removeEventListener("resize", close);
1762
+ };
1763
+ }, [at, close]);
1764
+ useEffect(() => {
1765
+ if (!open) return;
1766
+ const onKey = (event) => {
1767
+ if (event.key === "Escape") close();
1768
+ };
1769
+ window.addEventListener("keydown", onKey);
1770
+ return () => window.removeEventListener("keydown", onKey);
1771
+ }, [open, close]);
1772
+ if (shown === null) return { anchor: {}, tip: null };
1773
+ const place = (element) => {
1774
+ const box = element.getBoundingClientRect();
1775
+ setAt({
1776
+ top: box.bottom,
1777
+ left: box.left,
1778
+ above: window.innerHeight - box.bottom < ROOM_BELOW_PX,
1779
+ anchorTop: box.top
1780
+ });
1781
+ };
1782
+ return {
1783
+ anchor: {
1784
+ "data-tooltip": shown,
1785
+ onPointerEnter: (event) => {
1786
+ cancelLeave();
1787
+ place(event.currentTarget);
1788
+ setHovered(true);
1789
+ },
1790
+ onPointerLeave: leaveSoon,
1791
+ onPointerDown: () => {
1792
+ pressed.current = true;
1793
+ close();
1794
+ },
1795
+ onPointerUp: () => {
1796
+ pressed.current = false;
1797
+ },
1798
+ onFocus: (event) => {
1799
+ if (pressed.current) return;
1800
+ place(event.currentTarget);
1801
+ setFocused(true);
1802
+ },
1803
+ onBlur: () => {
1804
+ setFocused(false);
1805
+ if (!hovered && !tipHovered) setAt(null);
1806
+ }
1807
+ },
1808
+ tip: open ? createPortal(
1809
+ /* @__PURE__ */ jsx11(
1810
+ TipBody,
1811
+ {
1812
+ at,
1813
+ text: shown,
1814
+ onPointerEnter: () => {
1815
+ cancelLeave();
1816
+ setTipHovered(true);
1817
+ },
1818
+ onPointerLeave: leaveSoon
1819
+ }
1820
+ ),
1821
+ document.body
1822
+ ) : null
1823
+ };
1824
+ }
1825
+ function TipBody({
1826
+ at,
1827
+ text,
1828
+ onPointerEnter,
1829
+ onPointerLeave
1830
+ }) {
1831
+ const position = at.above ? { bottom: window.innerHeight - at.anchorTop } : { top: at.top };
1832
+ return /* @__PURE__ */ jsx11(
1833
+ Tooltip__Body,
1834
+ {
1835
+ "aria-hidden": "true",
1836
+ "data-tooltip-tip": "",
1837
+ "data-above": at.above ? "" : void 0,
1838
+ onPointerEnter,
1839
+ onPointerLeave,
1840
+ style: {
1841
+ ...position,
1842
+ left: at.left,
1843
+ "--tip-left": `${at.left}px`
1844
+ },
1845
+ children: text
1846
+ }
1847
+ );
1848
+ }
1849
+ function joined(own, ours) {
1850
+ return (...args) => {
1851
+ if (typeof own === "function") own(...args);
1852
+ ours?.(...args);
1853
+ };
1854
+ }
1855
+ function Tooltip({
1856
+ text,
1857
+ children,
1858
+ focusable = false,
1859
+ announce = true
1860
+ }) {
1861
+ const { anchor, tip } = useTooltip(text);
1862
+ if (text == null || text === "") return children;
1863
+ const own = children.props;
1864
+ const label = own["aria-label"];
1865
+ const named = !announce || typeof label === "string" && words(label).includes(words(text));
1866
+ return /* @__PURE__ */ jsxs3(Fragment3, { children: [
1867
+ cloneElement(children, {
1868
+ "data-tooltip": text,
1869
+ onPointerEnter: joined(own.onPointerEnter, anchor.onPointerEnter),
1870
+ onPointerLeave: joined(own.onPointerLeave, anchor.onPointerLeave),
1871
+ onPointerDown: joined(own.onPointerDown, anchor.onPointerDown),
1872
+ onPointerUp: joined(own.onPointerUp, anchor.onPointerUp),
1873
+ onFocus: joined(own.onFocus, anchor.onFocus),
1874
+ onBlur: joined(own.onBlur, anchor.onBlur),
1875
+ "aria-description": named ? own["aria-description"] : text,
1876
+ tabIndex: focusable ? own.tabIndex ?? 0 : own.tabIndex
1877
+ }),
1878
+ tip,
1879
+ focusable && /* @__PURE__ */ jsx11(TooltipFocusRing, {})
1880
+ ] });
1881
+ }
1882
+ var TooltipFocusRing = createGlobalStyle`
1883
+ [data-tooltip][tabindex]:not(button, a, input, select, textarea, summary):focus-visible {
1884
+ outline: 2px solid var(--color-focus);
1885
+ outline-offset: 2px;
1886
+ }
1887
+ `;
1888
+ var Tooltip__Body = styled7.div`
1889
+ position: fixed;
1890
+ z-index: var(--z-toast);
1891
+ margin-top: var(--offset-popover);
1892
+ max-width: min(18rem, calc(100vw - 2 * var(--offset-popover)));
1893
+ /* Pulled back inside the viewport when the anchor sits near its right edge. */
1894
+ translate: min(0px, calc(100vw - 100% - var(--offset-popover) - var(--tip-left, 0px)));
1895
+ padding: var(--inset-tooltip);
1896
+ background: var(--color-surface-raised);
1897
+ border: 1px solid var(--color-border-subtle);
1898
+ border-radius: var(--radius-floating);
1899
+ box-shadow: var(--shadow-popover-drop) rgba(0, 0, 0, 0.35);
1900
+ color: var(--color-text-primary);
1901
+ font-size: var(--font-size-caption);
1902
+ line-height: var(--line-height-body);
1903
+ letter-spacing: 0.02em;
1904
+ white-space: pre-line;
1905
+
1906
+ &[data-above] {
1907
+ margin-top: 0;
1908
+ margin-bottom: var(--offset-popover);
1909
+ }
1910
+ `;
1911
+
1707
1912
  // src/status/severity.ts
1708
1913
  var RANK = {
1709
1914
  go: 0,
@@ -1865,25 +2070,25 @@ var PanelStatusStoreProvider = StatusPanelStore.Provider;
1865
2070
  var usePanelStatusStore = StatusPanelStore.useStore;
1866
2071
 
1867
2072
  // src/status/useStatusContribution.ts
1868
- import { useEffect } from "react";
2073
+ import { useEffect as useEffect2 } from "react";
1869
2074
  function useStatusContribution(c) {
1870
2075
  const store = usePanelStatusStore();
1871
2076
  const id = c?.id;
1872
2077
  const severity = c?.severity;
1873
2078
  const label = c?.label;
1874
- useEffect(() => {
2079
+ useEffect2(() => {
1875
2080
  if (!store || id === void 0 || severity === void 0) return;
1876
2081
  const deregister = store.register({ id, severity, label: label ?? "" });
1877
2082
  return deregister;
1878
2083
  }, [store, id]);
1879
- useEffect(() => {
2084
+ useEffect2(() => {
1880
2085
  if (!store || id === void 0 || severity === void 0) return;
1881
2086
  store.update(id, { severity, label: label ?? "" });
1882
2087
  }, [store, id, severity, label]);
1883
2088
  }
1884
2089
 
1885
2090
  // src/Badge.tsx
1886
- import styled7, { css as css6 } from "styled-components";
2091
+ import styled8, { css as css6 } from "styled-components";
1887
2092
 
1888
2093
  // src/fitBox.ts
1889
2094
  import { css as css5 } from "styled-components";
@@ -1895,7 +2100,7 @@ var fitMask = (name) => css5`
1895
2100
  `;
1896
2101
 
1897
2102
  // src/Badge.tsx
1898
- import { jsx as jsx11 } from "react/jsx-runtime";
2103
+ import { jsx as jsx12 } from "react/jsx-runtime";
1899
2104
  function Badge({
1900
2105
  tone,
1901
2106
  size = "md",
@@ -1915,7 +2120,7 @@ function Badge({
1915
2120
  } : null
1916
2121
  );
1917
2122
  const liveAttrs = live ? { role: "status", "aria-live": "polite" } : {};
1918
- return /* @__PURE__ */ jsx11(Badge__Body, { $severity: severity, $size: size, ...liveAttrs, ...rest, children });
2123
+ return /* @__PURE__ */ jsx12(Badge__Body, { $severity: severity, $size: size, ...liveAttrs, ...rest, children });
1919
2124
  }
1920
2125
  var DECORATIVE_STYLE = css6`
1921
2126
  background: var(--color-surface-raised);
@@ -1969,7 +2174,7 @@ var SIZE_STYLES2 = {
1969
2174
  padding: var(--inset-chip-roomy);
1970
2175
  `
1971
2176
  };
1972
- var Badge__Body = styled7.span`
2177
+ var Badge__Body = styled8.span`
1973
2178
  display: inline-block;
1974
2179
  /* Sized by its own text, never stretched to a taller flex sibling. */
1975
2180
  align-self: center;
@@ -1994,11 +2199,6 @@ import {
1994
2199
  magnitudeOr
1995
2200
  } from "@ksp-gonogo/sitrep-sdk";
1996
2201
 
1997
- // src/bandClaim.ts
1998
- function bandClaim(_kind, claimed) {
1999
- return claimed;
2000
- }
2001
-
2002
2202
  // src/reckoningMarkSpec.ts
2003
2203
  var HOLLOW_MARK_STROKE = "max(0.075em, 1.25px)";
2004
2204
  var RECKONING_MARK = {
@@ -2059,206 +2259,6 @@ var RECKONING_MARK = {
2059
2259
  }
2060
2260
  };
2061
2261
 
2062
- // src/Tooltip.tsx
2063
- import {
2064
- cloneElement,
2065
- useCallback,
2066
- useEffect as useEffect2,
2067
- useRef as useRef2,
2068
- useState
2069
- } from "react";
2070
- import { createPortal } from "react-dom";
2071
- import styled8, { createGlobalStyle } from "styled-components";
2072
- import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs3 } from "react/jsx-runtime";
2073
- var ROOM_BELOW_PX = 120;
2074
- var CROSSING_MS = 120;
2075
- var words = (text) => text.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
2076
- function useTooltip(text) {
2077
- const [at, setAt] = useState(null);
2078
- const [hovered, setHovered] = useState(false);
2079
- const [focused, setFocused] = useState(false);
2080
- const [tipHovered, setTipHovered] = useState(false);
2081
- const pressed = useRef2(false);
2082
- const leaving = useRef2(null);
2083
- const shown = text != null && text !== "" ? text : null;
2084
- const open = at !== null && (hovered || focused || tipHovered);
2085
- const cancelLeave = useCallback(() => {
2086
- if (leaving.current !== null) clearTimeout(leaving.current);
2087
- leaving.current = null;
2088
- }, []);
2089
- const close = useCallback(() => {
2090
- cancelLeave();
2091
- setAt(null);
2092
- setHovered(false);
2093
- setFocused(false);
2094
- setTipHovered(false);
2095
- }, [cancelLeave]);
2096
- const leaveSoon = useCallback(() => {
2097
- cancelLeave();
2098
- leaving.current = setTimeout(() => {
2099
- leaving.current = null;
2100
- setHovered(false);
2101
- setTipHovered(false);
2102
- }, CROSSING_MS);
2103
- }, [cancelLeave]);
2104
- useEffect2(() => cancelLeave, [cancelLeave]);
2105
- useEffect2(() => {
2106
- if (at === null) return;
2107
- window.addEventListener("scroll", close, true);
2108
- window.addEventListener("resize", close);
2109
- return () => {
2110
- window.removeEventListener("scroll", close, true);
2111
- window.removeEventListener("resize", close);
2112
- };
2113
- }, [at, close]);
2114
- useEffect2(() => {
2115
- if (!open) return;
2116
- const onKey = (event) => {
2117
- if (event.key === "Escape") close();
2118
- };
2119
- window.addEventListener("keydown", onKey);
2120
- return () => window.removeEventListener("keydown", onKey);
2121
- }, [open, close]);
2122
- if (shown === null) return { anchor: {}, tip: null };
2123
- const place = (element) => {
2124
- const box = element.getBoundingClientRect();
2125
- setAt({
2126
- top: box.bottom,
2127
- left: box.left,
2128
- above: window.innerHeight - box.bottom < ROOM_BELOW_PX,
2129
- anchorTop: box.top
2130
- });
2131
- };
2132
- return {
2133
- anchor: {
2134
- "data-tooltip": shown,
2135
- onPointerEnter: (event) => {
2136
- cancelLeave();
2137
- place(event.currentTarget);
2138
- setHovered(true);
2139
- },
2140
- onPointerLeave: leaveSoon,
2141
- onPointerDown: () => {
2142
- pressed.current = true;
2143
- close();
2144
- },
2145
- onPointerUp: () => {
2146
- pressed.current = false;
2147
- },
2148
- onFocus: (event) => {
2149
- if (pressed.current) return;
2150
- place(event.currentTarget);
2151
- setFocused(true);
2152
- },
2153
- onBlur: () => {
2154
- setFocused(false);
2155
- if (!hovered && !tipHovered) setAt(null);
2156
- }
2157
- },
2158
- tip: open ? createPortal(
2159
- /* @__PURE__ */ jsx12(
2160
- TipBody,
2161
- {
2162
- at,
2163
- text: shown,
2164
- onPointerEnter: () => {
2165
- cancelLeave();
2166
- setTipHovered(true);
2167
- },
2168
- onPointerLeave: leaveSoon
2169
- }
2170
- ),
2171
- document.body
2172
- ) : null
2173
- };
2174
- }
2175
- function TipBody({
2176
- at,
2177
- text,
2178
- onPointerEnter,
2179
- onPointerLeave
2180
- }) {
2181
- const position = at.above ? { bottom: window.innerHeight - at.anchorTop } : { top: at.top };
2182
- return /* @__PURE__ */ jsx12(
2183
- Tooltip__Body,
2184
- {
2185
- "aria-hidden": "true",
2186
- "data-tooltip-tip": "",
2187
- "data-above": at.above ? "" : void 0,
2188
- onPointerEnter,
2189
- onPointerLeave,
2190
- style: {
2191
- ...position,
2192
- left: at.left,
2193
- "--tip-left": `${at.left}px`
2194
- },
2195
- children: text
2196
- }
2197
- );
2198
- }
2199
- function joined(own, ours) {
2200
- return (...args) => {
2201
- if (typeof own === "function") own(...args);
2202
- ours?.(...args);
2203
- };
2204
- }
2205
- function Tooltip({
2206
- text,
2207
- children,
2208
- focusable = false,
2209
- announce = true
2210
- }) {
2211
- const { anchor, tip } = useTooltip(text);
2212
- if (text == null || text === "") return children;
2213
- const own = children.props;
2214
- const label = own["aria-label"];
2215
- const named = !announce || typeof label === "string" && words(label).includes(words(text));
2216
- return /* @__PURE__ */ jsxs3(Fragment3, { children: [
2217
- cloneElement(children, {
2218
- "data-tooltip": text,
2219
- onPointerEnter: joined(own.onPointerEnter, anchor.onPointerEnter),
2220
- onPointerLeave: joined(own.onPointerLeave, anchor.onPointerLeave),
2221
- onPointerDown: joined(own.onPointerDown, anchor.onPointerDown),
2222
- onPointerUp: joined(own.onPointerUp, anchor.onPointerUp),
2223
- onFocus: joined(own.onFocus, anchor.onFocus),
2224
- onBlur: joined(own.onBlur, anchor.onBlur),
2225
- "aria-description": named ? own["aria-description"] : text,
2226
- tabIndex: focusable ? own.tabIndex ?? 0 : own.tabIndex
2227
- }),
2228
- tip,
2229
- focusable && /* @__PURE__ */ jsx12(TooltipFocusRing, {})
2230
- ] });
2231
- }
2232
- var TooltipFocusRing = createGlobalStyle`
2233
- [data-tooltip][tabindex]:not(button, a, input, select, textarea, summary):focus-visible {
2234
- outline: 2px solid var(--color-focus);
2235
- outline-offset: 2px;
2236
- }
2237
- `;
2238
- var Tooltip__Body = styled8.div`
2239
- position: fixed;
2240
- z-index: var(--z-toast);
2241
- margin-top: var(--offset-popover);
2242
- max-width: min(18rem, calc(100vw - 2 * var(--offset-popover)));
2243
- /* Pulled back inside the viewport when the anchor sits near its right edge. */
2244
- translate: min(0px, calc(100vw - 100% - var(--offset-popover) - var(--tip-left, 0px)));
2245
- padding: var(--inset-tooltip);
2246
- background: var(--color-surface-raised);
2247
- border: 1px solid var(--color-border-subtle);
2248
- border-radius: var(--radius-floating);
2249
- box-shadow: var(--shadow-popover-drop) rgba(0, 0, 0, 0.35);
2250
- color: var(--color-text-primary);
2251
- font-size: var(--font-size-caption);
2252
- line-height: var(--line-height-body);
2253
- letter-spacing: 0.02em;
2254
- white-space: pre-line;
2255
-
2256
- &[data-above] {
2257
- margin-top: 0;
2258
- margin-bottom: var(--offset-popover);
2259
- }
2260
- `;
2261
-
2262
2262
  // src/VisuallyHidden.tsx
2263
2263
  import styled9 from "styled-components";
2264
2264
  var VisuallyHidden = styled9.span`
@@ -3118,6 +3118,27 @@ function derivedMarking(reading, carried = false) {
3118
3118
  }
3119
3119
  return null;
3120
3120
  }
3121
+ function heldMarking(held) {
3122
+ if (held === void 0) return null;
3123
+ if (typeof held !== "string") return derivedMarking(held);
3124
+ return { kind: "held", caption: heldWord(held) };
3125
+ }
3126
+ function heldGradeOf(held) {
3127
+ if (held === void 0) return void 0;
3128
+ if (typeof held === "string") return held;
3129
+ return held.state === "held" ? held.grade ?? "held" : void 0;
3130
+ }
3131
+ function heldFigureOf(figure, held) {
3132
+ const grade = heldGradeOf(held);
3133
+ if (grade === void 0) return figure;
3134
+ return {
3135
+ state: "held",
3136
+ value: figure,
3137
+ grade,
3138
+ asOfUt: typeof held === "object" ? held.asOfUt : void 0,
3139
+ reckoning: { status: "none" }
3140
+ };
3141
+ }
3121
3142
 
3122
3143
  // src/standsApart.ts
3123
3144
  function writtenAs(write) {
@@ -3434,10 +3455,7 @@ function toInterval(shown, band, opts, rung) {
3434
3455
  };
3435
3456
  }
3436
3457
  function hover(caption, interval) {
3437
- const said2 = interval === null ? null : bandClaim(
3438
- interval.kind,
3439
- `with bands at ${interval.loSaid} and ${interval.hiSaid}`
3440
- );
3458
+ const said2 = interval === null ? null : `with bands at ${interval.loSaid} and ${interval.hiSaid}`;
3441
3459
  if (caption === null) return said2;
3442
3460
  return said2 === null ? caption : `${caption}, ${said2}`;
3443
3461
  }
@@ -3588,7 +3606,7 @@ var Cluster__Root = styled12.div`
3588
3606
 
3589
3607
  // src/badgeFace.ts
3590
3608
  function badgeFace(entry) {
3591
- const grade = heldGradeOf(entry);
3609
+ const grade = heldGradeOf(entry.held);
3592
3610
  if (grade === void 0) {
3593
3611
  return { label: entry.label, tone: entry.tone, title: entry.title };
3594
3612
  }
@@ -3599,11 +3617,6 @@ function badgeFace(entry) {
3599
3617
  title: entry.title ?? `${entry.label}: ${label}`
3600
3618
  };
3601
3619
  }
3602
- function heldGradeOf(entry) {
3603
- const held = entry.held;
3604
- if (held === void 0) return void 0;
3605
- return typeof held === "string" ? held : held.grade;
3606
- }
3607
3620
 
3608
3621
  // src/CommandDelay/commandRefusalSentence.ts
3609
3622
  import {
@@ -3792,7 +3805,7 @@ function InstrumentNoFigure({
3792
3805
  // src/CommandDelay/railTags.ts
3793
3806
  import { hasHost as hasHost2, logger as logger2 } from "@ksp-gonogo/sitrep-sdk";
3794
3807
  function railMark(tags) {
3795
- return tags.continuity === "continuous" ? "ribbon" : "dot";
3808
+ return tags.kind === "continuous" ? "ribbon" : "dot";
3796
3809
  }
3797
3810
  function railDrawsReturnLeg(tags) {
3798
3811
  return tags.delivery === "acked";
@@ -3804,12 +3817,14 @@ function railToneToken(tags) {
3804
3817
  return tags.direction === "command" ? "--color-accent-fg" : "--color-info-mark";
3805
3818
  }
3806
3819
  function railTagKey(tags) {
3807
- return `${tags.direction}/${tags.continuity}/${tags.delivery}`;
3820
+ return `${tags.direction}/${tags.kind}/${tags.delivery}`;
3808
3821
  }
3809
3822
  var RAIL_RENDERERS = {
3823
+ "command/event/acked": "in-flight-row",
3810
3824
  "command/discrete/acked": "in-flight-row",
3811
3825
  "command/continuous/acked": "continuous-strip",
3812
3826
  "telemetry/continuous/fire-and-forget": "continuous-strip",
3827
+ "telemetry/event/fire-and-forget": "in-flight-row",
3813
3828
  "telemetry/discrete/fire-and-forget": "in-flight-row"
3814
3829
  };
3815
3830
  function railRendererFor(tags) {
@@ -4165,7 +4180,7 @@ function RibbonMark({
4165
4180
  function ControlDelayStream({
4166
4181
  streams,
4167
4182
  ribbons = [],
4168
- ariaLabel = "Controls in flight",
4183
+ "aria-label": ariaLabel = "Controls in flight",
4169
4184
  variant = "inline",
4170
4185
  delayReading
4171
4186
  }) {
@@ -4671,14 +4686,14 @@ function InFlightList({
4671
4686
  mode,
4672
4687
  density = "auto",
4673
4688
  orientation = "column",
4674
- ariaLabel = "In-flight commands",
4689
+ "aria-label": ariaLabel = "In-flight commands",
4675
4690
  variant = "inline",
4676
4691
  onDismiss
4677
4692
  }) {
4678
4693
  const { ref, size } = useElementSize({ w: 320, h: 0 });
4679
4694
  if (variant === "rail") {
4680
4695
  if (items.length === 0) return null;
4681
- return /* @__PURE__ */ jsx23(InFlightRailStrip, { items, ariaLabel });
4696
+ return /* @__PURE__ */ jsx23(InFlightRailStrip, { items, "aria-label": ariaLabel });
4682
4697
  }
4683
4698
  if (variant === "expanded") {
4684
4699
  if (items.length === 0) return null;
@@ -4686,7 +4701,7 @@ function InFlightList({
4686
4701
  InFlightQueue,
4687
4702
  {
4688
4703
  items,
4689
- ariaLabel,
4704
+ "aria-label": ariaLabel,
4690
4705
  onDismiss
4691
4706
  }
4692
4707
  );
@@ -4700,7 +4715,7 @@ function InFlightList({
4700
4715
  ref,
4701
4716
  items,
4702
4717
  mode,
4703
- ariaLabel
4718
+ "aria-label": ariaLabel
4704
4719
  }
4705
4720
  );
4706
4721
  }
@@ -4731,7 +4746,7 @@ var GLOW_R = 9;
4731
4746
  var GLOW_PEAK_ALPHA = 0.22;
4732
4747
  function InFlightRailStrip({
4733
4748
  items,
4734
- ariaLabel
4749
+ "aria-label": ariaLabel
4735
4750
  }) {
4736
4751
  const gradBase = useId3();
4737
4752
  const summary = `${items.length} in flight`;
@@ -4794,7 +4809,7 @@ var InFlightBadge = function InFlightBadge2({
4794
4809
  ref,
4795
4810
  items,
4796
4811
  mode,
4797
- ariaLabel
4812
+ "aria-label": ariaLabel
4798
4813
  }) {
4799
4814
  const countdown = useCountdown(nearestEta(items));
4800
4815
  const worst = items.some((i) => ERROR_PHASES.has(i.phase));
@@ -4872,7 +4887,7 @@ var QUEUE_COLOUR = {
4872
4887
  };
4873
4888
  function InFlightQueue({
4874
4889
  items,
4875
- ariaLabel,
4890
+ "aria-label": ariaLabel,
4876
4891
  onDismiss
4877
4892
  }) {
4878
4893
  const { ref, size } = useElementSize({ w: 320, h: QUEUE_THICK });
@@ -5171,7 +5186,7 @@ import { jsx as jsx24 } from "react/jsx-runtime";
5171
5186
  function CommandDelay({
5172
5187
  handle,
5173
5188
  handles,
5174
- ariaLabel,
5189
+ "aria-label": ariaLabel,
5175
5190
  mode,
5176
5191
  density,
5177
5192
  orientation,
@@ -5181,7 +5196,7 @@ function CommandDelay({
5181
5196
  const lone = all.length === 1 ? all[0] : null;
5182
5197
  const loneRenderer = lone ? railRendererFor(lone.tags) : null;
5183
5198
  if (lone && loneRenderer === null) {
5184
- return /* @__PURE__ */ jsx24(UnrepresentedRailHandle, { tags: lone.tags, who: lone.ariaLabel });
5199
+ return /* @__PURE__ */ jsx24(UnrepresentedRailHandle, { tags: lone.tags, who: lone["aria-label"] });
5185
5200
  }
5186
5201
  if (lone && loneRenderer !== "in-flight-row") {
5187
5202
  const streamHandle = lone;
@@ -5193,7 +5208,7 @@ function CommandDelay({
5193
5208
  streams: streamHandle.streams ?? [],
5194
5209
  ribbons: streamHandle.ribbons ?? [],
5195
5210
  delayReading: streamHandle.delayReading,
5196
- ariaLabel: ariaLabel ?? streamHandle.ariaLabel,
5211
+ "aria-label": ariaLabel ?? streamHandle["aria-label"],
5197
5212
  variant
5198
5213
  }
5199
5214
  );
@@ -5205,7 +5220,7 @@ function CommandDelay({
5205
5220
  InFlightList,
5206
5221
  {
5207
5222
  items,
5208
- ariaLabel,
5223
+ "aria-label": ariaLabel,
5209
5224
  mode,
5210
5225
  density,
5211
5226
  orientation,
@@ -5287,7 +5302,7 @@ function boxesOf(props) {
5287
5302
  }
5288
5303
  function CommandList(props) {
5289
5304
  const { kind, onDismiss, live = true } = props;
5290
- const ariaLabel = props.ariaLabel ?? DEFAULT_LABEL[kind];
5305
+ const ariaLabel = props["aria-label"] ?? DEFAULT_LABEL[kind];
5291
5306
  const tone = kind === "found" ? "info" : "warn";
5292
5307
  if (!live && props.entries.length === 0) return null;
5293
5308
  const boxes = boxesOf(props).map((box) => /* @__PURE__ */ jsxs10(
@@ -5479,7 +5494,10 @@ function useContributions(slotOrSlots) {
5479
5494
  import {
5480
5495
  hasHost as hasHost3,
5481
5496
  logger as logger3,
5482
- PerfBudget
5497
+ observedAt,
5498
+ PerfBudget,
5499
+ topicReading,
5500
+ withoutReckoning
5483
5501
  } from "@ksp-gonogo/sitrep-sdk";
5484
5502
  import {
5485
5503
  getContributionsForSlot,
@@ -5514,6 +5532,10 @@ function getSlotPerfBudget(slot) {
5514
5532
  }
5515
5533
  return budget;
5516
5534
  }
5535
+ var NEVER_ARRIVED = topicReading({
5536
+ state: "pending",
5537
+ reckoning: { status: "none" }
5538
+ });
5517
5539
  var EMPTY_TOPIC_VALUES = Object.freeze({});
5518
5540
  var slotCache2 = /* @__PURE__ */ new Map();
5519
5541
  var cacheValid2 = false;
@@ -5562,6 +5584,18 @@ function shallowEqualValues(a, b) {
5562
5584
  if (keys.length !== Object.keys(b).length) return false;
5563
5585
  return keys.every((k) => Object.hasOwn(b, k) && Object.is(a[k], b[k]));
5564
5586
  }
5587
+ function contributionReading(previous, next) {
5588
+ if (previous === void 0 || previous === next) return next;
5589
+ const sameCurrency = previous.state === next.state && Object.is(
5590
+ "value" in previous ? previous.value : void 0,
5591
+ "value" in next ? next.value : void 0
5592
+ ) && sameInstant(observedAt(previous), observedAt(next)) && (previous.state === "held" ? previous.grade : void 0) === (next.state === "held" ? next.grade : void 0);
5593
+ return sameCurrency ? previous : next;
5594
+ }
5595
+ function sameInstant(a, b) {
5596
+ if (a === void 0 || b === void 0) return a === b;
5597
+ return a.equals(b);
5598
+ }
5565
5599
  function isModSettingDep(dep) {
5566
5600
  return "modSetting" in dep;
5567
5601
  }
@@ -5575,10 +5609,6 @@ function declaredTopicsOf(def) {
5575
5609
  topics.add(d);
5576
5610
  continue;
5577
5611
  }
5578
- if ("reading" in d) {
5579
- topics.add(d.reading);
5580
- continue;
5581
- }
5582
5612
  if (isModSettingDep(d)) {
5583
5613
  topics.add(modSettingKey(d.modSetting));
5584
5614
  }
@@ -5605,10 +5635,6 @@ function SlotAggregator({
5605
5635
  topics.add(d);
5606
5636
  continue;
5607
5637
  }
5608
- if ("reading" in d) {
5609
- topics.add(d.reading);
5610
- continue;
5611
- }
5612
5638
  if (isModSettingDep(d)) {
5613
5639
  settings.set(modSettingKey(d.modSetting), d.modSetting);
5614
5640
  continue;
@@ -5656,8 +5682,15 @@ function SlotAggregator({
5656
5682
  [client, telemetryStore, processorRuntime, unionDeps]
5657
5683
  );
5658
5684
  const topicCacheRef = useRef5(null);
5685
+ const readingsRef = useRef5(/* @__PURE__ */ new Map());
5686
+ const withoutStoreValues = useMemo3(() => {
5687
+ if (unionDeps.topics.length === 0) return EMPTY_TOPIC_VALUES;
5688
+ return Object.fromEntries(
5689
+ unionDeps.topics.map((topic) => [topic, NEVER_ARRIVED])
5690
+ );
5691
+ }, [unionDeps]);
5659
5692
  const getSnapshot = useCallback3(() => {
5660
- if (!telemetryStore) return EMPTY_TOPIC_VALUES;
5693
+ if (!telemetryStore) return withoutStoreValues;
5661
5694
  if (unionDeps.topics.length === 0 && unionDeps.processors.length === 0 && unionDeps.settings.length === 0) {
5662
5695
  return EMPTY_TOPIC_VALUES;
5663
5696
  }
@@ -5666,8 +5699,12 @@ function SlotAggregator({
5666
5699
  if (cached && cached.token === token) return cached.values;
5667
5700
  const values = {};
5668
5701
  for (const topic of unionDeps.topics) {
5669
- const point = telemetryStore.sample(topic, token);
5670
- values[topic] = point ? point.payload : void 0;
5702
+ const reading = contributionReading(
5703
+ readingsRef.current.get(topic),
5704
+ withoutReckoning(telemetryStore.sampleReading(topic, token))
5705
+ );
5706
+ readingsRef.current.set(topic, reading);
5707
+ values[topic] = reading;
5671
5708
  }
5672
5709
  for (const p of unionDeps.processors) {
5673
5710
  values[p.id] = processorRuntime?.value(p.id);
@@ -5682,7 +5719,7 @@ function SlotAggregator({
5682
5719
  const next = cached && shallowEqualValues(cached.values, values) ? cached.values : values;
5683
5720
  topicCacheRef.current = { token, values: next };
5684
5721
  return next;
5685
- }, [telemetryStore, processorRuntime, unionDeps]);
5722
+ }, [telemetryStore, processorRuntime, unionDeps, withoutStoreValues]);
5686
5723
  const topicValues = useSyncExternalStore6(subscribe, getSnapshot);
5687
5724
  const availability = useDomainAvailabilityStore();
5688
5725
  const presence = useSyncExternalStore6(
@@ -6486,7 +6523,7 @@ function PanelDelayRail({ tiny } = {}) {
6486
6523
  {
6487
6524
  handle: h,
6488
6525
  variant: grown ? "expanded" : "rail",
6489
- ariaLabel: h.ariaLabel ?? (grown ? "Delay detail" : void 0)
6526
+ "aria-label": h["aria-label"] ?? (grown ? "Delay detail" : void 0)
6490
6527
  },
6491
6528
  h.id
6492
6529
  )),
@@ -7999,7 +8036,7 @@ import { forwardRef as forwardRef7 } from "react";
7999
8036
  import { jsx as jsx38 } from "react/jsx-runtime";
8000
8037
  var ToggleButton = forwardRef7(
8001
8038
  function ToggleButton2({
8002
- active = false,
8039
+ pressed = false,
8003
8040
  tone = "go",
8004
8041
  size = "md",
8005
8042
  type = "button",
@@ -8011,10 +8048,10 @@ var ToggleButton = forwardRef7(
8011
8048
  {
8012
8049
  ref,
8013
8050
  type,
8014
- tone: active ? tone : void 0,
8051
+ tone: pressed ? tone : void 0,
8015
8052
  size,
8016
- pressed: active,
8017
- "aria-pressed": ariaPressed ?? active,
8053
+ pressed,
8054
+ "aria-pressed": ariaPressed ?? pressed,
8018
8055
  ...rest
8019
8056
  }
8020
8057
  );
@@ -8023,7 +8060,12 @@ var ToggleButton = forwardRef7(
8023
8060
 
8024
8061
  // src/widgetSize.ts
8025
8062
  import { TINY_SIZE } from "@ksp-gonogo/sitrep-sdk";
8026
- var TINY_BELOW = { w: 5, h: 4 };
8063
+ var TINY_BELOW = {
8064
+ /** Width in grid columns: a tile under 5 columns wide is `tiny`. */
8065
+ w: 5,
8066
+ /** Height in grid rows: a tile under 4 rows tall is `tiny`. */
8067
+ h: 4
8068
+ };
8027
8069
  var SMALL_W = 8;
8028
8070
  var SMALL_H = 7;
8029
8071
  function getSizeBucket(w, h) {
@@ -8078,23 +8120,29 @@ function TinyEssentials({ title, essentials }) {
8078
8120
  /* @__PURE__ */ jsx39(CompactGauge, { essential: hero, gauge: hero.gauge })
8079
8121
  ] })
8080
8122
  ] }),
8081
- rest.length > 0 && /* @__PURE__ */ jsx39(TinyEssentials__Rows, { ref: rowsRef, children: rest.slice(0, drawn).map((essential) => /* @__PURE__ */ jsxs18(
8082
- TinyEssentials__Row,
8083
- {
8084
- "data-tiny-essential": "",
8085
- children: [
8086
- /* @__PURE__ */ jsx39(TinyEssentials__Label, { children: essential.label }),
8087
- /* @__PURE__ */ jsx39(
8088
- TinyEssentials__RowFigure,
8089
- {
8090
- $tone: essential.tone ?? "neutral",
8091
- children: /* @__PURE__ */ jsx39(EssentialFigure, { essential })
8092
- }
8093
- )
8094
- ]
8095
- },
8096
- essential.label
8097
- )) })
8123
+ rest.length > 0 && /* @__PURE__ */ jsxs18(TinyEssentials__Rows, { ref: rowsRef, children: [
8124
+ rest.slice(0, drawn).map((essential) => /* @__PURE__ */ jsxs18(
8125
+ TinyEssentials__Row,
8126
+ {
8127
+ "data-tiny-essential": "",
8128
+ children: [
8129
+ /* @__PURE__ */ jsx39(TinyEssentials__Label, { children: essential.label }),
8130
+ /* @__PURE__ */ jsx39(
8131
+ TinyEssentials__RowFigure,
8132
+ {
8133
+ $tone: essential.tone ?? "neutral",
8134
+ children: /* @__PURE__ */ jsx39(EssentialFigure, { essential })
8135
+ }
8136
+ )
8137
+ ]
8138
+ },
8139
+ essential.label
8140
+ )),
8141
+ rest.slice(drawn).map((essential) => /* @__PURE__ */ jsxs18(VisuallyHidden, { as: "div", children: [
8142
+ /* @__PURE__ */ jsx39("dt", { children: essential.label }),
8143
+ /* @__PURE__ */ jsx39("dd", { children: essential.control === void 0 ? /* @__PURE__ */ jsx39(EssentialFigure, { essential }) : essential.control.label })
8144
+ ] }, essential.label))
8145
+ ] })
8098
8146
  ] })
8099
8147
  }
8100
8148
  );
@@ -8154,7 +8202,7 @@ function EssentialFigure({ essential }) {
8154
8202
  ToggleButton,
8155
8203
  {
8156
8204
  size: "sm",
8157
- active: control.active,
8205
+ pressed: control.active,
8158
8206
  disabled: control.disabled,
8159
8207
  onClick: control.onPress,
8160
8208
  "aria-label": control.title,
@@ -8426,6 +8474,8 @@ export {
8426
8474
  useSlotBound,
8427
8475
  useSlotLabel,
8428
8476
  useAugmentAvailable,
8477
+ useTooltip,
8478
+ Tooltip,
8429
8479
  worstSeverity,
8430
8480
  severityFromStreamStatus,
8431
8481
  PanelStatusStoreProvider,
@@ -8435,10 +8485,7 @@ export {
8435
8485
  asQuantityish,
8436
8486
  magnitudeOf,
8437
8487
  magnitudeOr,
8438
- bandClaim,
8439
8488
  RECKONING_MARK,
8440
- useTooltip,
8441
- Tooltip,
8442
8489
  VisuallyHidden,
8443
8490
  HeldMark,
8444
8491
  ReckoningMark,
@@ -8494,6 +8541,9 @@ export {
8494
8541
  resolveCurrency,
8495
8542
  figureAttributes,
8496
8543
  derivedMarking,
8544
+ heldMarking,
8545
+ heldGradeOf,
8546
+ heldFigureOf,
8497
8547
  writtenAs,
8498
8548
  writtenQuantity,
8499
8549
  placedOnScale,
@@ -8634,4 +8684,4 @@ lucide-react/dist/esm/lucide-react.mjs:
8634
8684
  * See the LICENSE file in the root directory of this source tree.
8635
8685
  *)
8636
8686
  */
8637
- //# sourceMappingURL=chunk-GGJES6XH.js.map
8687
+ //# sourceMappingURL=chunk-A3QYFQAV.js.map