@ksp-gonogo/ui-kit 0.2.0-rc.12 → 0.2.0-rc.8
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/dist/{chunk-PG6WD6AT.js → chunk-UBNGQZWK.js} +1 -1
- package/dist/chunk-UBNGQZWK.js.map +1 -0
- package/dist/guards.d.ts +3 -3
- package/dist/guards.js.map +1 -1
- package/dist/index.d.ts +179 -304
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +2 -3
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-PG6WD6AT.js.map +0 -1
- package/dist/compat.json +0 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tone.ts","../src/Button.tsx","../src/focusRing.ts","../src/Form.tsx","../src/Stack.tsx","../src/scales.ts","../src/AugmentSettings.tsx","../src/augments.ts","../src/domainAvailability.tsx","../src/store/createPanelStore.tsx","../src/kspTime.ts","../src/Text.tsx","../src/NullValue.tsx","../src/units.ts","../src/__generated__/unit-kinds.ts","../src/formatKspDate.ts","../src/formatDuration.ts","../src/LockScope.tsx","../src/InactiveNotice.tsx","../src/lockQuantity.ts","../src/Section.tsx","../src/WidgetMetaContext.ts","../src/AugmentSlot.tsx","../src/status/severity.ts","../src/store/createStore.ts","../src/status/PanelStatusStore.tsx","../src/status/useStatusContribution.ts","../src/Badge.tsx","../src/fitBox.ts","../src/magnitude.ts","../src/bandClaim.ts","../src/reckoningMarkSpec.ts","../src/Tooltip.tsx","../src/VisuallyHidden.tsx","../src/HeldMark.tsx","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/shared/src/utils/mergeClasses.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/shared/src/utils/toKebabCase.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/shared/src/utils/toCamelCase.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/shared/src/utils/toPascalCase.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/defaultAttributes.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/shared/src/utils/hasA11yProp.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/context.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/Icon.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/createLucideIcon.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/arrow-down.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/arrow-left.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/arrow-right.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/arrow-up-to-line.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/arrow-up.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/bell.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/check.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/chevron-down.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/chevron-left.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/chevron-right.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/chevron-up.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/columns-2.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/computer.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/corner-down-left.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/database.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/file-text.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/heart.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/history.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/house.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/info.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/joystick.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/layers.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/lock.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/maximize.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/microscope.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/minimize.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/pause.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/pencil.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/play.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/plus.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/radio.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/rectangle-horizontal.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/rectangle-vertical.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/rows-2.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/satellite.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/settings.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/square.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/star.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/undo-2.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/volume-2.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/volume-x.ts","../../../node_modules/.pnpm/lucide-react@1.14.0_react@18.3.1/node_modules/lucide-react/src/icons/x.ts","../src/Icons.tsx","../src/status/streamStatusWord.ts","../src/readingCurrency.ts","../src/standsApart.ts","../src/UnitSharedFormat.tsx","../src/Unit.tsx","../src/Cluster.tsx","../src/badgeFace.ts","../src/CommandDelay/commandRefusalSentence.ts","../src/CommandDelay/commandFoundSentence.ts","../src/CommandDelay/commandLossSentence.ts","../src/LiveRegion.tsx","../src/instrumentCurrency.tsx","../src/CommandDelay/railTags.ts","../src/CommandDelay/waveformPath.ts","../src/CommandDelay/ControlDelayStream.tsx","../src/CommandDelay/delayCurrency.ts","../src/ModelledAlongside.tsx","../src/Countdown.tsx","../src/useElementSize.ts","../src/CommandDelay/toInFlightListItems.ts","../src/CommandDelay/InFlightList.tsx","../src/CommandDelay/CommandDelay.tsx","../src/CommandDelay/commandFailedSentence.ts","../src/CommandDelay/commandUndeliveredSentence.ts","../src/CommandDelay/CommandList.tsx","../src/CommandDelay/DelayRailContext.tsx","../src/contributionsRead.tsx","../src/contributionsRuntime.tsx","../src/FilterChip.tsx","../src/SearchBox.tsx","../src/useRowFilter.tsx","../src/FramedDisplay.tsx","../src/GraphNotice.tsx","../src/LevelBars.tsx","../src/PanelBadges.tsx","../src/status/useStatusBreakdown.ts","../src/status/useStatusSummary.ts","../src/usePanelAsideSize.ts","../src/Panel.tsx","../src/CommandDelay/PanelDelayRail.tsx","../src/status/PanelStatusDot.tsx","../src/titleText.ts","../src/useFittedTitle.ts","../src/useScrollerMetric.ts","../src/ToggleButton.tsx","../src/widgetSize.ts","../src/TinyEssentials.tsx","../src/fillQuantity.ts","../src/useWidgetBadges.ts"],"sourcesContent":["import type { Tone } from \"@ksp-gonogo/sitrep-sdk\";\n\n/*\n * What colour a tone is depends on the job the colour does, so there is one\n * map per job and a caller picks the map by what it is drawing. Each value is\n * a role token from the theme, which the contrast gate holds to its grounds.\n */\n\n/**\n * A tone's text colour on a surface, or on its own muted ground. Each value is\n * a CSS variable string, for use in a style or a styled-components template.\n *\n * @category Theme\n */\nexport const TONE_TEXT: Readonly<Record<Tone, string>> = {\n neutral: \"var(--color-neutral-text)\",\n info: \"var(--color-info-text)\",\n go: \"var(--color-go-text)\",\n caution: \"var(--color-caution-text)\",\n warn: \"var(--color-warn-text)\",\n nogo: \"var(--color-nogo-text)\",\n offline: \"var(--color-offline-text)\",\n};\n\n/**\n * A tone standing alone: the colour of a dot, fill, edge or stroke with no\n * text of its own.\n *\n * @category Theme\n */\nexport const TONE_MARK: Readonly<Record<Tone, string>> = {\n neutral: \"var(--color-neutral-mark)\",\n info: \"var(--color-info-mark)\",\n go: \"var(--color-go-mark)\",\n caution: \"var(--color-caution-mark)\",\n warn: \"var(--color-warn-mark)\",\n nogo: \"var(--color-nogo-mark)\",\n offline: \"var(--color-offline-mark)\",\n};\n\n/**\n * A tone as a fill that carries its own text, drawn in {@link TONE_ON_STATUS}.\n * Each value is a CSS variable string.\n *\n * @category Theme\n */\nexport const TONE_STATUS: Readonly<Record<Tone, string>> = {\n neutral: \"var(--color-neutral-status)\",\n info: \"var(--color-info-status)\",\n go: \"var(--color-go-status)\",\n caution: \"var(--color-caution-status)\",\n warn: \"var(--color-warn-status)\",\n nogo: \"var(--color-nogo-status)\",\n offline: \"var(--color-offline-status)\",\n};\n\n/**\n * The text colour on a {@link TONE_STATUS} fill.\n *\n * @category Theme\n */\nexport const TONE_ON_STATUS: Readonly<Record<Tone, string>> = {\n neutral: \"var(--color-neutral-on-status)\",\n info: \"var(--color-info-on-status)\",\n go: \"var(--color-go-on-status)\",\n caution: \"var(--color-caution-on-status)\",\n warn: \"var(--color-warn-on-status)\",\n nogo: \"var(--color-nogo-on-status)\",\n offline: \"var(--color-offline-on-status)\",\n};\n\n/**\n * The word a screen reader hears for a tone that is announced rather than seen.\n *\n * @category Theme\n */\nexport const TONE_LABEL: Readonly<Record<Tone, string>> = {\n neutral: \"neutral\",\n info: \"info\",\n go: \"nominal\",\n caution: \"caution\",\n warn: \"warning\",\n nogo: \"critical\",\n offline: \"offline\",\n};\n\n/**\n * A box's edge in a tone: the tone's mark, except `neutral`, which has no state to edge with and takes the decorative border.\n *\n * @category Theme\n */\nexport function toneEdge(tone: Tone): string {\n return tone === \"neutral\" ? \"var(--color-border-subtle)\" : TONE_MARK[tone];\n}\n\n/**\n * A box filled with a tone's status colour: the fill, its on-status text, and\n * the neutral text and every text level inside it redeclared to that on-status\n * text, so a word a child draws reads on the fill rather than in a grey made\n * for the panel.\n */\nexport function statusFill(tone: Tone): string {\n const on = TONE_ON_STATUS[tone];\n return `\n background: ${TONE_STATUS[tone]};\n color: ${on};\n --color-neutral-text: ${on};\n --color-text-primary: ${on};\n --color-text-muted: ${on};\n --color-text-dim: ${on};\n --color-text-faint: ${on};\n`;\n}\n","import type { Tone } from \"@ksp-gonogo/sitrep-sdk\";\nimport { type ButtonHTMLAttributes, forwardRef } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { focusRing } from \"./focusRing\";\nimport { TONE_MARK, TONE_ON_STATUS, TONE_STATUS, TONE_TEXT } from \"./tone\";\n\n/**\n * How much a button asks to be pressed: `default` is the ordinary raised\n * control, `primary` the one commit action of a group, filled in its tone,\n * `ghost` the quiet secondary one beside it, and `text` no chrome at all, for\n * words that are themselves the control (a name that renames on click): it\n * takes its colour and type from the text around it.\n *\n * @category Button\n */\nexport type ButtonVariant = \"default\" | \"primary\" | \"ghost\" | \"text\";\n\n/**\n * The state a button's action puts things in. `nogo` is the destructive tone,\n * `warn` an action that needs attention; `neutral` has no state.\n *\n * @category Button\n */\nexport type ButtonTone = Extract<Tone, \"neutral\" | \"go\" | \"warn\" | \"nogo\">;\n\n/**\n * `sm` for dense rows, `md` otherwise; both sizes keep the kit's one control\n * height, and only the type and padding change.\n *\n * @category Button\n */\nexport type ButtonSize = \"sm\" | \"md\";\n\n/**\n * A native button's attributes, plus how prominent it is, what its action\n * does, how large it is and, for a toggle, whether it is pressed.\n *\n * @category Button\n */\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** How prominent the button is. Defaults to `default`. */\n variant?: ButtonVariant;\n /** Defaults to `go` for a primary button and `neutral` otherwise. */\n tone?: ButtonTone;\n /** Defaults to `md`. */\n size?: ButtonSize;\n /**\n * Makes the button a toggle: sets `aria-pressed` and, while true, draws the\n * one pressed look, filled in the tone's status colour. A pressed button\n * with no state of its own reads as on, so a neutral one fills in go.\n */\n pressed?: boolean;\n}\n\n/**\n * The kit's button: one family whose `variant` says how prominent it is and\n * whose `tone` says what its action does. Write the label in sentence case;\n * uppercase belongs to headings and state tokens. It takes no alignment of its\n * own: where it sits in a row is the caller's layout. The ref reaches the\n * `button` element.\n *\n * Its content is a centred flex row, so an icon beside the word centres on it\n * rather than sitting on the text baseline.\n *\n * @example\n * ```tsx\n * <Cluster justify=\"end\">\n * <Button variant=\"ghost\" onClick={onCancel}>Cancel</Button>\n * <Button variant=\"primary\" onClick={onSave}>\n * <CheckIcon />\n * Save\n * </Button>\n * </Cluster>\n * ```\n *\n * @category Button\n */\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(\n function Button(\n { variant = \"default\", tone, size = \"md\", pressed, ...rest },\n ref,\n ) {\n return (\n <Button__Body\n ref={ref}\n $variant={variant}\n $tone={tone ?? (variant === \"primary\" ? \"go\" : \"neutral\")}\n $size={size}\n $pressed={pressed === true}\n aria-pressed={pressed}\n {...rest}\n />\n );\n },\n);\n\n/** The fill a pressed button takes: its tone's status fill, and go for a neutral one. */\nfunction pressedTone(tone: ButtonTone): Exclude<ButtonTone, \"neutral\"> {\n return tone === \"neutral\" ? \"go\" : tone;\n}\n\n/** Filled in a tone's status colour under its own text: a primary button, and every pressed one. */\nexport const filledLook = (tone: ButtonTone) => css`\n background: ${TONE_STATUS[tone]};\n border-color: ${TONE_STATUS[tone]};\n color: ${TONE_ON_STATUS[tone]};\n`;\n\n/** A toned outline: the tone's words on the button's own ground, edged in its mark. */\nconst tonedOutline = (tone: Exclude<ButtonTone, \"neutral\">) => css`\n border-color: ${TONE_MARK[tone]};\n color: ${TONE_TEXT[tone]};\n`;\n\nconst VARIANT_LOOK = {\n default: css`\n background: var(--color-surface-raised);\n border-color: var(--color-border-strong);\n color: var(--color-text-primary);\n `,\n ghost: css`\n background: none;\n border-color: var(--color-border-strong);\n /* Clears 4.5:1 on the app background. */\n color: var(--color-text-muted);\n `,\n} as const;\n\n/* Words that are the control: the surrounding colour and type, with no box, so nothing but the focus ring says it is a button. */\nconst TEXT_LOOK = css`\n background: none;\n border: none;\n padding: 0;\n min-height: 0;\n font: inherit;\n color: inherit;\n text-align: inherit;\n justify-content: flex-start;\n\n /* No box to brighten, so the words underline under the pointer. */\n @media (hover: hover) {\n &:hover:not(:disabled) {\n text-decoration: underline;\n }\n }\n`;\n\n/* Words keep their place under a coarse pointer: the touch target grows, but no inset pushes the text off its line. */\nconst TEXT_TOUCH = css`\n @media (pointer: coarse) {\n padding: 0;\n }\n`;\n\nfunction variantLook(variant: ButtonVariant, tone: ButtonTone) {\n if (variant === \"text\") return TEXT_LOOK;\n if (variant === \"primary\") return filledLook(tone);\n if (tone === \"neutral\") return VARIANT_LOOK[variant];\n return css`\n ${VARIANT_LOOK[variant]}\n ${tonedOutline(tone)}\n `;\n}\n\n/* A filled button keeps its fill under the pointer; only an outlined one brightens its edge and words. */\nconst OUTLINE_HOVER = css`\n @media (hover: hover) {\n &:hover:not(:disabled) {\n border-color: var(--color-text-faint);\n color: var(--color-text-primary);\n }\n }\n`;\n\nconst SIZE_LOOK = {\n sm: css`\n font-size: var(--font-size-caption);\n padding: var(--inset-control-small);\n `,\n md: css`\n font-size: var(--font-size-compact);\n padding: var(--inset-control);\n `,\n} as const;\n\n/**\n * The button's body, shared by every kit control that is a button:\n * `ToggleButton` and `CommandButton` draw with it and add only their own\n * behaviour's states.\n */\nexport const Button__Body = styled.button<{\n $variant: ButtonVariant;\n $tone: ButtonTone;\n $size: ButtonSize;\n $pressed: boolean;\n}>`\n display: inline-flex;\n align-items: center;\n justify-content: center;\n gap: var(--gap-glyph-control);\n border: 1px solid;\n border-radius: var(--radius-regular);\n font-family: inherit;\n font-weight: 600;\n /* The kit's one control height, with a flush line height so type size cannot change the box height. */\n min-height: var(--control-height);\n line-height: var(--line-height-flush);\n cursor: pointer;\n transition: background var(--duration-fast), border-color var(--duration-fast), color var(--duration-fast);\n\n ${({ $size }) => SIZE_LOOK[$size]}\n ${({ $variant, $tone }) => variantLook($variant, $tone)}\n ${({ $pressed, $tone }) => ($pressed ? filledLook(pressedTone($tone)) : \"\")}\n\n ${({ $variant, $pressed }) =>\n $variant === \"primary\" || $variant === \"text\" || $pressed\n ? \"\"\n : OUTLINE_HOVER}\n ${focusRing}\n &:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n\n /* An unconfirmed command may still run and a failed one never left; both read in the warning tone. */\n &[data-unconfirmed=\"true\"],\n &[data-failed=\"true\"] {\n border-color: var(--color-warn-mark);\n color: var(--color-warn-text);\n background: color-mix(\n in srgb,\n var(--color-warn-mark) 18%,\n var(--color-surface-raised)\n );\n }\n\n @media (pointer: coarse) {\n min-height: 44px;\n /* Wider on both axes: min-height only covers the vertical target. */\n padding: ${({ $size }) =>\n $size === \"sm\"\n ? \"var(--inset-control-small-touch)\"\n : \"var(--inset-control-touch)\"};\n }\n\n ${({ $variant }) => ($variant === \"text\" ? TEXT_TOUCH : \"\")}\n`;\n\n/**\n * A subtle, underlined link-style `button` for a tertiary action inside copy\n * (such as \"Clear all\" in a list row). For a paired Cancel and Confirm row,\n * use a ghost {@link Button} beside a primary one.\n *\n * @category Button\n */\nexport const TextButton = styled.button`\n background: none;\n border: none;\n color: var(--color-text-muted);\n font-size: var(--font-size-compact);\n font-family: inherit;\n cursor: pointer;\n padding: 0;\n text-decoration: underline;\n transition: color var(--duration-fast);\n\n @media (hover: hover) {\n &:hover {\n color: var(--color-text-primary);\n }\n }\n ${focusRing}\n &:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n`;\n\n/**\n * An icon-only `button` with no chrome, just the glyph. Give it an\n * `aria-label`, since the icon inside is decorative. As a toggle, with\n * `aria-pressed`, it takes the filled pressed look while pressed. Under a\n * coarse pointer it grows to a 44px touch target.\n *\n * @example\n * ```tsx\n * <IconButton aria-label=\"Close\" onClick={onClose}>\n * <CloseIcon />\n * </IconButton>\n * ```\n *\n * @category Button\n */\nexport const IconButton = styled.button`\n background: none;\n border: none;\n cursor: pointer;\n color: var(--color-text-faint);\n font-size: var(--font-size-base);\n line-height: var(--line-height-flush);\n padding: var(--inset-glyph-button);\n transition: color var(--duration-fast);\n\n @media (hover: hover) {\n &:hover {\n color: var(--color-text-primary);\n }\n }\n &[aria-pressed=\"true\"] {\n border-radius: var(--radius-regular);\n ${filledLook(\"go\")}\n }\n ${focusRing}\n &:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n }\n @media (pointer: coarse) {\n min-width: 44px;\n min-height: 44px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n }\n`;\n","import { css } from \"styled-components\";\n\n/**\n * The keyboard focus ring, defined once for the kit.\n *\n * The colour is `--color-focus`, not the same-valued `--color-accent-fg`, so\n * focus stays retunable on its own. Kit-internal, not on the barrel.\n */\nexport const focusRing = css`\n &:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n }\n`;\n\n/**\n * The same ring drawn inside the element's own edge, for a full-bleed control\n * whose parent clips it: a row, a tab, a rail segment, where a ring outside the\n * box would be clipped away.\n */\nexport const focusRingInset = css`\n &:focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: -2px;\n }\n`;\n","import styled, { css } from \"styled-components\";\nimport { focusRing } from \"./focusRing\";\n\n/**\n * A vertical form body, the root of a widget's config form. Each child is\n * usually a {@link Field} (label above control) or a {@link FieldRow} (label\n * beside control).\n *\n * `$boxed` puts it on a panel surface with a border and its own padding, and\n * tightens the gap, since a boxed form is denser than a settings section.\n *\n * @example\n * ```tsx\n * import {\n * ConfigForm,\n * Field,\n * FieldHint,\n * FieldLabel,\n * Input,\n * Select,\n * } from \"@ksp-gonogo/ui-kit\";\n *\n * <ConfigForm>\n * <Field>\n * <FieldLabel htmlFor=\"ag-select\">Action Group</FieldLabel>\n * <Select\n * id=\"ag-select\"\n * value={groupId}\n * onChange={(e) => setGroupId(e.target.value)}\n * >\n * {groups.map((g) => (\n * <option key={g.id} value={g.id}>{g.name}</option>\n * ))}\n * </Select>\n * </Field>\n * <Field>\n * <FieldLabel htmlFor=\"ag-label\">Custom Label</FieldLabel>\n * <Input\n * id=\"ag-label\"\n * type=\"text\"\n * value={label}\n * onChange={(e) => setLabel(e.target.value)}\n * />\n * <FieldHint>Leave blank to use the action group name.</FieldHint>\n * </Field>\n * </ConfigForm>\n * ```\n *\n * @category Form\n */\nexport const ConfigForm = styled.div<{ $boxed?: boolean }>`\n display: flex;\n flex-direction: column;\n gap: ${({ $boxed }) => ($boxed ? \"var(--gap-form-field-boxed)\" : \"var(--gap-form-field)\")};\n ${({ $boxed }) =>\n $boxed\n ? `\n background: var(--color-surface-panel);\n border: 1px solid var(--color-border-subtle);\n border-radius: var(--radius-regular);\n padding: var(--inset-form-box);\n`\n : \"\"}\n`;\n\n/**\n * One labelled control in a {@link ConfigForm}, stacked: {@link FieldLabel} on\n * top, the control below, an optional {@link FieldHint} under it. Tie the label\n * to the control with `htmlFor` and `id`.\n *\n * @example\n * ```tsx\n * <Field>\n * <FieldLabel htmlFor=\"gain\">Gain</FieldLabel>\n * <Input id=\"gain\" value={gain} onChange={(e) => setGain(e.target.value)} />\n * <FieldHint>Applied on the next frame.</FieldHint>\n * </Field>\n * ```\n *\n * @category Form\n */\nexport const Field = styled.div`\n display: flex;\n flex-direction: column;\n gap: var(--gap-field-label);\n`;\n\n/**\n * A labelled control laid out horizontally: label on the left, control on the right, vertically centred.\n *\n * @category Form\n */\nexport const FieldRow = styled.div`\n display: flex;\n align-items: center;\n gap: var(--gap-field-label-inline);\n`;\n\n/**\n * The label of a {@link Field} or {@link FieldRow}: a native `<label>` in small uppercase caption type. Pass `htmlFor` to tie it to its control.\n *\n * @category Form\n */\nexport const FieldLabel = styled.label`\n font-size: var(--font-size-caption);\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--color-text-dim);\n`;\n\n/**\n * Small, faint help text under a control in a {@link Field}. Give it an `id` and point the control's `aria-describedby` at it when it should be read with the control.\n *\n * @category Form\n */\nexport const FieldHint = styled.span`\n font-size: var(--font-size-compact);\n color: var(--color-text-faint);\n`;\n\n/**\n * A horizontal row of buttons at the foot of a form, vertically centred.\n *\n * @category Form\n */\nexport const FormActions = styled.div`\n display: flex;\n gap: var(--gap-control-row);\n align-items: center;\n`;\n\nconst inputBase = css`\n background: var(--color-surface-raised);\n border: 1px solid var(--color-border-strong);\n border-radius: var(--radius-regular);\n color: var(--color-text-primary);\n font-size: var(--font-size-compact);\n padding: var(--inset-field);\n\n &:focus {\n /* The accent border on the raised surface clears WCAG 1.4.11's 3:1. */\n border-color: var(--color-accent-fg);\n outline: none;\n }\n\n ${focusRing}\n\n /* The browser's own clear control ignores the theme; SearchBox draws the kit's. */\n &::-webkit-search-cancel-button {\n appearance: none;\n }\n\n @media (pointer: coarse) {\n min-height: 44px;\n padding: var(--inset-field-touch);\n /* 16px stops iOS Safari zooming on focus; a literal, because the threshold is absolute. */\n font-size: 16px;\n }\n`;\n\n/**\n * A native `<input>` in the kit's field style, full width, with a visible focus ring and a 44px touch target on coarse pointers.\n *\n * @category Form\n */\nexport const Input = styled.input`\n ${inputBase}\n width: 100%;\n`;\n\n/**\n * A native `<select>` in the kit's field style, full width. For a long or searchable list, use {@link ComboboxListbox} with an {@link Input}.\n *\n * @category Form\n */\nexport const Select = styled.select`\n ${inputBase}\n width: 100%;\n appearance: none;\n /* The native arrow hugs the field's edge and cannot be inset, so the chevron is two gradient strokes drawn clear of it. */\n background-image:\n linear-gradient(45deg, transparent 50%, var(--color-text-muted) 50%),\n linear-gradient(135deg, var(--color-text-muted) 50%, transparent 50%);\n background-position:\n calc(100% - var(--offset-select-arrow-far)) 50%,\n calc(100% - var(--offset-select-arrow-near)) 50%;\n background-size:\n var(--size-select-arrow) var(--size-select-arrow),\n var(--size-select-arrow) var(--size-select-arrow);\n background-repeat: no-repeat;\n padding-inline-end: var(--inset-field-select-end);\n\n @media (pointer: coarse) {\n padding-inline-end: var(--inset-field-select-end);\n }\n`;\n\n/**\n * A native `<textarea>` in the kit's field style, full width, resizable vertically only.\n *\n * @category Form\n */\nexport const Textarea = styled.textarea`\n ${inputBase}\n width: 100%;\n resize: vertical;\n`;\n","import type { HTMLAttributes, ReactNode } from \"react\";\nimport styled from \"styled-components\";\nimport { GAP_VAR, type GapToken } from \"./scales\";\nimport type { StaticElement } from \"./staticElement\";\n\n/**\n * Props for {@link Stack}. Any other `div` attribute passes through.\n *\n * @category Layout\n */\nexport interface StackProps extends HTMLAttributes<HTMLDivElement> {\n /**\n * Gap between children, one of the {@link GapToken} names. Omitted, the\n * stack takes the `related` gap of whatever container it sits in, so the\n * same stack is 8px on a panel and 6px inside a card. Set it only where this\n * stack decides the spacing of what it holds.\n */\n gap?: GapToken;\n /** Rendered tag. Defaults to `div`. */\n as?: StaticElement;\n /**\n * Take the remaining space in a flex parent and allow shrinking below the\n * content's natural height, so a scrolling region inside the stack scrolls\n * rather than growing the whole column.\n */\n fill?: boolean;\n children?: ReactNode;\n}\n\n/**\n * A vertical flex column, the most common container shape in a widget body.\n * Children sit one above the other with the container's related gap between\n * them unless `gap` names another.\n *\n * The layout primitives compose: a {@link Stack} of sections, a\n * {@link Cluster} for a label beside its controls, a {@link Grid} for a\n * label/value table, and a {@link Box} for an inset well.\n *\n * @example\n * ```tsx\n * <Stack gap=\"section\">\n * <Cluster>\n * <Text>Target</Text>\n * <Inline>\n * <Button onClick={onSet}>Set</Button>\n * <Button onClick={onClear}>Clear</Button>\n * </Inline>\n * </Cluster>\n * <Grid cols=\"auto 1fr\" gap=\"label-value\" rowGap=\"readout-row\" align=\"baseline\">\n * <Text level=\"muted\">Apoapsis</Text>\n * <Text>{apoapsis}</Text>\n * <Text level=\"muted\">Periapsis</Text>\n * <Text>{periapsis}</Text>\n * </Grid>\n * <Box surface=\"sunken\" pad=\"surface\" radius=\"regular\">\n * {notes}\n * </Box>\n * </Stack>\n * ```\n *\n * @category Layout\n */\nexport function Stack({ gap, fill = false, children, ...rest }: StackProps) {\n return (\n <Stack__Root $gap={gap} $fill={fill} {...rest}>\n {children}\n </Stack__Root>\n );\n}\n\nconst Stack__Root = styled.div<{ $gap?: GapToken; $fill: boolean }>`\n display: flex;\n flex-direction: column;\n gap: ${({ $gap }) => ($gap ? GAP_VAR[$gap] : \"var(--gap-related)\")};\n ${({ $fill }) => ($fill ? \"flex: 1; min-height: 0;\" : \"\")}\n\n /* Rendered as a list it is still a layout box: the browser's own list indent, margin and bullets would shift and squeeze its rows. Zero specificity, so a caller's own rule still wins. */\n &:where(ul, ol) {\n margin: 0;\n padding: 0;\n list-style: none;\n }\n`;\n","/**\n * The spacing and corner names the layout primitives resolve a prop against.\n *\n * Each is a handle onto a semantic token in `tokens.css`, not a value of its\n * own, so a host restyles the kit by mounting a sheet rather than by passing a\n * scale in. That keeps geometry out of the theme contract: a theme supplies\n * colours, type and borders, and the kit brings its own spacing.\n */\n\n/**\n * The spacing names a layout primitive's `gap`, `rowGap` or `space` prop\n * accepts. Each names what the space is for, and resolves to the `--gap-*`\n * CSS variable of the same name.\n *\n * - `related` and `section`: between related items, and between sections.\n * Both are smaller inside a {@link Card} than on a panel\n * - `related-comfortable`, `related-compact`, `related-dense`,\n * `related-packed`, `section-comfortable` and `section-compact`: the same\n * two spacings at one fixed density, whatever the container\n * - `rows`: between stacked rows that carry their own padding\n * - `caption`: under the line a caption belongs to\n * - `readout-row` and `label-value`: between the rows and between the columns\n * of a label/value grid\n *\n * @category Layout\n */\nexport type GapToken =\n | \"related\"\n | \"section\"\n | \"related-comfortable\"\n | \"related-compact\"\n | \"related-dense\"\n | \"related-packed\"\n | \"section-comfortable\"\n | \"section-compact\"\n | \"rows\"\n | \"caption\"\n | \"readout-row\"\n | \"label-value\";\n\nexport const GAP_VAR = {\n related: \"var(--gap-related)\",\n section: \"var(--gap-section)\",\n \"related-comfortable\": \"var(--gap-related-comfortable)\",\n \"related-compact\": \"var(--gap-related-compact)\",\n \"related-dense\": \"var(--gap-related-dense)\",\n \"related-packed\": \"var(--gap-related-packed)\",\n \"section-comfortable\": \"var(--gap-section-comfortable)\",\n \"section-compact\": \"var(--gap-section-compact)\",\n rows: \"var(--gap-rows)\",\n caption: \"var(--gap-caption)\",\n \"readout-row\": \"var(--gap-readout-row)\",\n \"label-value\": \"var(--gap-label-value)\",\n} as const satisfies Record<GapToken, string>;\n\n/**\n * The padding names {@link Box}'s `pad` prop accepts, each resolving to the\n * `--inset-*` CSS variable of the same name: `chip`, `chip-roomy` and\n * `chip-readout` for small pills, `pill` for a stadium, `surface` and\n * `surface-standalone` for a block's own padding, `popover` for a floating\n * card.\n *\n * @category Layout\n */\nexport type InsetToken =\n | \"chip\"\n | \"chip-roomy\"\n | \"chip-readout\"\n | \"pill\"\n | \"surface\"\n | \"surface-standalone\"\n | \"popover\";\n\nexport const INSET_NAME = {\n chip: \"--inset-chip\",\n \"chip-roomy\": \"--inset-chip-roomy\",\n \"chip-readout\": \"--inset-chip-readout\",\n pill: \"--inset-pill\",\n surface: \"--inset-surface\",\n \"surface-standalone\": \"--inset-surface-standalone\",\n popover: \"--inset-popover\",\n} as const satisfies Record<InsetToken, `--inset-${string}`>;\n\n/**\n * The corner handles a surface primitive accepts.\n *\n * Three, and they name a role rather than a size: an ordinary corner, a corner\n * on something floating above the app, and a stadium. The display-frame\n * corner is not here because it belongs to `FramedDisplay` alone, and `circle` is\n * not a corner at all.\n */\nexport type RadiusToken = \"regular\" | \"floating\" | \"pill\";\n\nexport const RADIUS_VAR = {\n /** Every ordinary corner: controls, chips, rows, cells, cards, menus. */\n regular: \"var(--radius-regular)\",\n /** A box that sits above the app: a modal, a dialog, the FAB, the landing surface. */\n floating: \"var(--radius-floating)\",\n /** Fully rounded: chips, avatars, toggle knobs. */\n pill: \"var(--radius-pill)\",\n} as const satisfies Record<RadiusToken, string>;\n","import { createContext, type ReactNode, useContext, useMemo } from \"react\";\n\n/*\n * Augment settings are a framework capability: any augment may declare them,\n * the values live in the HOST WIDGET INSTANCE's config under\n * `augmentSettings[<augmentId>]`, the host provides once and any augment reads\n * with a hook, so segment slots stay propless.\n */\n\n/**\n * What {@link AugmentSettingsProvider} publishes: the widget instance's saved\n * augment settings and a writer for them.\n *\n * @category Extensions\n */\nexport interface AugmentSettingsContextValue {\n /**\n * The host widget instance's saved per-augment settings, keyed\n * `[augmentId][fieldKey]`: the same namespacing {@link getAugmentSettings} and\n * {@link AugmentSettingsPanel} use. `undefined` for a field nothing has saved yet,\n * so an augment falls back to its own declared `default`.\n */\n settings: Record<string, Record<string, unknown>> | undefined;\n /**\n * Persists one field of one augment's settings into the host widget\n * instance's own config, the same place the settings panel writes, so a\n * quick toggle in an `actions` augment and the settings panel always agree.\n */\n setAugmentSetting: (augmentId: string, key: string, value: unknown) => void;\n}\n\nconst AugmentSettingsContext =\n createContext<AugmentSettingsContextValue | null>(null);\n\n/**\n * Publishes a widget instance's augment settings, stored in its config under\n * `augmentSettings[<augmentId>]`, to the augments rendered inside it. The\n * dashboard mounts one for every widget; outside the dashboard there is none,\n * and {@link useAugmentSettings} reads empty values.\n *\n * @category Extensions\n */\nexport function AugmentSettingsProvider({\n settings,\n setAugmentSetting,\n children,\n}: AugmentSettingsContextValue & { children?: ReactNode }) {\n const value = useMemo(\n () => ({ settings, setAugmentSetting }),\n [settings, setAugmentSetting],\n );\n return (\n <AugmentSettingsContext.Provider value={value}>\n {children}\n </AugmentSettingsContext.Provider>\n );\n}\n\n/**\n * One augment's own settings for the widget instance it is rendered in, plus a\n * writer scoped to that augment. Pass the `id` the augment was registered\n * with. A field nothing has saved is missing from `values`, so fall back to\n * the field's declared `default`.\n *\n * Outside an {@link AugmentSettingsProvider} the values read empty and `set`\n * does nothing, the same as an operator having saved nothing.\n *\n * @example\n * ```tsx\n * function CompactToggle() {\n * const { values, set } = useAugmentSettings(\"example-cadence-section\");\n * const compact = values.compact === true;\n * return <Switch checked={compact} onChange={(v) => set(\"compact\", v)} label=\"Compact\" />;\n * }\n * ```\n *\n * @category Extensions\n */\nexport function useAugmentSettings(augmentId: string): {\n /** This augment's saved fields; empty when nothing has been saved. */\n values: Record<string, unknown>;\n /** Persists one of this augment's fields into the host widget's config. */\n set: (key: string, value: unknown) => void;\n} {\n const ctx = useContext(AugmentSettingsContext);\n const settings = ctx?.settings;\n const setAugmentSetting = ctx?.setAugmentSetting;\n return useMemo(\n () => ({\n values: settings?.[augmentId] ?? EMPTY_VALUES,\n set: (key: string, value: unknown) =>\n setAugmentSetting?.(augmentId, key, value),\n }),\n [settings, setAugmentSetting, augmentId],\n );\n}\n\n/**\n * The whole per-augment settings map for the current widget instance, keyed\n * `[augmentId][fieldKey]`, or `undefined` outside a provider or before\n * anything is saved. For a widget reasoning across all its augments, such as\n * drawing its settings panel or compositing only the layers switched on.\n *\n * @category Extensions\n */\nexport function useAllAugmentSettings():\n | Record<string, Record<string, unknown>>\n | undefined {\n return useContext(AugmentSettingsContext)?.settings;\n}\n\nconst EMPTY_VALUES: Record<string, unknown> = Object.freeze({});\n","import type {\n AugmentDefinition,\n NamespacedAugmentSettings,\n SlotProps,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport { hasHost, logger } from \"@ksp-gonogo/sitrep-sdk\";\nimport { assertSizeDelta } from \"@ksp-gonogo/sitrep-sdk/spine\";\nimport type { ComponentType } from \"react\";\n\n/*\n * Widgets expose named augment slots, any Uplink contributes a component into a\n * slot using only its own Topics, and the host composes. Two mutually unaware\n * mods binding the same slot both render, ordered by priority.\n *\n * Slot ids are typed by declaration merging into `SlotRegistry`, which maps\n * a slot id to the props it passes down. An undeclared slot id still compiles.\n * The seam is the sdk's, re-exported, so a merge into either package lands on\n * the same interface; declaring it in both would split slot ids across two\n * interfaces with nothing failing.\n */\nexport type {\n SlotId,\n SlotProps,\n SlotRegistry,\n} from \"@ksp-gonogo/sitrep-sdk\";\n\n/**\n * The standard augment segments every widget's {@link Panel} carries, each\n * mapped to the props its augments receive. A segment is bound as\n * `\"<widget-id>.<segment>\"`. Both pass no props (`Record<string, never>`): an\n * augment reads its own Topics, and reads what the widget is focused on\n * through {@link useWidgetScope}. Where each renders is under Standard slots\n * on {@link Panel}.\n *\n * @category Panel\n */\nexport interface AugmentSegmentRegistry {\n /** Body content after everything the widget draws, bound as `\"<widget-id>.sections\"`. */\n sections: Record<string, never>;\n /** Controls in the widget's panel header, bound as `\"<widget-id>.actions\"`. */\n actions: Record<string, never>;\n}\n\n/**\n * The props an augment bound to a standard segment receives, looked up in\n * {@link AugmentSegmentRegistry}: `Record<string, never>` for `sections` and\n * `actions`, and `never` for a name that is not a standard segment.\n *\n * @category Panel\n */\nexport type AugmentSegmentProps<Segment extends string> =\n Segment extends keyof AugmentSegmentRegistry\n ? AugmentSegmentRegistry[Segment]\n : never;\n\n/*\n * `AugmentSettingField` is one per-instance setting an augment contributes to\n * the host widget's settings panel, namespaced by augment id;\n * `NamespacedAugmentSettings` is one augment's block of them.\n */\n/**\n * Registration descriptor for an augment: a component bound into another\n * widget's slot. `Slot` is inferred from `augments`, so `component` is typed\n * against that slot's {@link SlotProps}.\n */\nexport type {\n AugmentDefinition,\n AugmentSettingField,\n NamespacedAugmentSettings,\n} from \"@ksp-gonogo/sitrep-sdk\";\n\n// `SlotProps<string>` would resolve to `never` for an undeclared slot, so the props are erased here and `Slot` is checked at the `registerAugment` call site instead.\n/**\n * An augment as the registry holds it, whatever slot it binds: an\n * `AugmentDefinition` whose component takes a loose props object. The lookup\n * functions ({@link getAugmentsForSlot}, {@link getAugments}) return this.\n *\n * @category Extensions\n */\nexport type AnyAugment = Omit<AugmentDefinition<string>, \"component\"> & {\n component: ComponentType<Record<string, unknown>>;\n};\n\n/**\n * The registry lives on `globalThis` under a string key so every loaded copy of\n * this package (an Uplink that inlines ui-kit loads a second) shares one state.\n */\nconst AUGMENT_REGISTRY_KEY = \"__GONOGO_AUGMENT_REGISTRY__\" as const;\n\ninterface AugmentRegistry {\n augments: Map<string, { def: AnyAugment; order: number }>;\n /** Registration order, so ties in `priority` sort deterministically. */\n counter: number;\n listeners: Set<() => void>;\n}\n\nfunction registry(): AugmentRegistry {\n const slot = globalThis as typeof globalThis & {\n [AUGMENT_REGISTRY_KEY]?: AugmentRegistry;\n };\n slot[AUGMENT_REGISTRY_KEY] ??= {\n augments: new Map(),\n counter: 0,\n listeners: new Set(),\n };\n return slot[AUGMENT_REGISTRY_KEY];\n}\n\n/**\n * Slot ids that no widget mounts any more, each mapped to the slot that\n * replaced it. {@link registerAugment} logs an error naming the replacement\n * when an augment binds one of these; the augment is stored but never renders.\n * A misspelled slot id is not detected at runtime.\n *\n * @category Extensions\n */\nexport const RETIRED_SLOT_IDS: Readonly<Record<string, string>> = {\n \"distance-to-target.camera\": \"targeting.camera\",\n \"distance-to-target.overlay\": \"targeting.overlay\",\n \"astronaut-complex.training\": \"astronaut-complex.tab\",\n};\n\n/** Reports a retired slot id. Falls back to `console.error` without a host, because the sdk's `logger` throws when none is installed. */\nfunction reportIfSlotRetired(\n def: Pick<AugmentDefinition<string>, \"id\" | \"augments\" | \"owner\">,\n): void {\n const replacement = RETIRED_SLOT_IDS[def.augments];\n if (!replacement) return;\n const owner = def.owner ? `${def.owner.name} (${def.owner.id})` : \"unknown\";\n const message =\n `Augment \"${def.id}\" binds retired slot \"${def.augments}\", ` +\n `renamed to \"${replacement}\"; it will render nothing until updated. ` +\n `Registered by Uplink: ${owner}`;\n if (hasHost()) logger.error(message);\n else console.error(message);\n}\n\nfunction notifyAugmentChange(): void {\n for (const cb of registry().listeners) cb();\n}\n\n/**\n * Calls `cb` whenever an augment is registered or the registry is cleared.\n * Returns a function that unsubscribes.\n *\n * @category Extensions\n */\nexport function onAugmentsChange(cb: () => void): () => void {\n registry().listeners.add(cb);\n return () => {\n registry().listeners.delete(cb);\n };\n}\n\n/**\n * Registers an augment into a widget's slot. Call it at module load, like\n * `registerComponent`. Several augments may bind one slot; they all render,\n * ordered by `priority`. Registering an id again replaces the earlier augment.\n * `component` is typed against the slot's props in {@link SlotRegistry}.\n *\n * An Uplink calls the `registerAugment` exported by `@ksp-gonogo/sitrep-sdk`,\n * which reaches this registry through the host. For binding a widget's\n * standard `sections` or `actions` slot, see {@link Panel}.\n *\n * @category Extensions\n */\nexport function registerAugment<Slot extends string>(\n def: AugmentDefinition<Slot>,\n): void {\n reportIfSlotRetired(def);\n assertSizeDelta(def.id, def.sizeDelta);\n const state = registry();\n state.augments.set(def.id, {\n // Erased through `unknown`: a merged `SlotProps<Slot>` is not bivariantly comparable to the erased form.\n def: def as unknown as AnyAugment,\n order: state.counter++,\n });\n notifyAugmentChange();\n}\n\n/**\n * Every augment bound to `slotName`, in render order: ascending `priority`\n * (default 0), ties in registration order. It includes augments whose\n * `requires` Domain is absent; {@link AugmentSlot} applies that gate when it\n * renders.\n *\n * @category Extensions\n */\nexport function getAugmentsForSlot(slotName: string): AnyAugment[] {\n return Array.from(registry().augments.values())\n .filter((entry) => entry.def.augments === slotName)\n .sort((a, b) => {\n const pa = a.def.priority ?? 0;\n const pb = b.def.priority ?? 0;\n if (pa !== pb) return pa - pb;\n return a.order - b.order;\n })\n .map((entry) => entry.def);\n}\n\n/**\n * Every registered augment, in no particular order.\n *\n * @category Extensions\n */\nexport function getAugments(): AnyAugment[] {\n return Array.from(registry().augments.values()).map((entry) => entry.def);\n}\n\n/**\n * One settings block for each augment bound to `slotName` that declares\n * `settings`, in render order. The host widget's settings panel shows these\n * after its own settings; each block's `namespace` (the augment id) scopes its\n * fields in the widget instance's config. An Uplink that is not loaded adds no\n * block.\n *\n * @category Extensions\n */\nexport function getAugmentSettings(\n slotName: string,\n): NamespacedAugmentSettings[] {\n return getAugmentsForSlot(slotName)\n .filter((def) => def.settings && def.settings.length > 0)\n .map((def) => ({\n augmentId: def.id,\n namespace: def.id,\n fields: def.settings ?? [],\n }));\n}\n\n/**\n * Empties the augment registry and notifies subscribers. For tests only.\n *\n * @category Extensions\n */\nexport function clearAugments(): void {\n const state = registry();\n state.augments.clear();\n state.counter = 0;\n notifyAugmentChange();\n}\n","import { useSyncExternalStore } from \"react\";\nimport { createPanelStore } from \"./store/createPanelStore\";\n\n/*\n * The augment presence gate asks whether an augment's Domain is live. It reads\n * a ui-kit-owned store rather than telemetry, so ui-kit takes no spine\n * dependency; the host feeds `<domain>.available` into it. With no provider\n * mounted, a Domain nothing has announced is not available.\n */\n\n/**\n * A store of which Domains are present, keyed by Domain id (the value an\n * augment or contribution names in `requires`). Augment slots read it to\n * decide what to render, and re-render only when it changes.\n *\n * @category Extensions\n */\nexport interface DomainAvailabilityStore {\n /** Marks a Domain available or unavailable. Does nothing, and notifies no one, when the value is unchanged. */\n setAvailable(domain: string, available: boolean): void;\n /** Whether `domain` has announced availability. `false` for an unknown Domain. */\n isAvailable(domain: string): boolean;\n /** Calls `onChange` on any availability change. Returns a function that unsubscribes. */\n subscribe(onChange: () => void): () => void;\n}\n\n/**\n * Creates an empty {@link DomainAvailabilityStore}, in which every Domain reads\n * as unavailable until marked otherwise. Useful for seeding presence in a test\n * through {@link DomainAvailabilityContext}.\n *\n * @category Extensions\n */\nexport function createDomainAvailabilityStore(): DomainAvailabilityStore {\n const availability = new Map<string, boolean>();\n const listeners = new Set<() => void>();\n return {\n setAvailable(domain, available) {\n // A feeder re-emitting the same presence wakes no reader.\n if (availability.get(domain) === available) return;\n availability.set(domain, available);\n for (const listener of listeners) listener();\n },\n isAvailable(domain) {\n return availability.get(domain) ?? false;\n },\n subscribe(onChange) {\n listeners.add(onChange);\n return () => {\n listeners.delete(onChange);\n };\n },\n };\n}\n\nconst DomainAvailabilityPanelStore = createPanelStore(\n createDomainAvailabilityStore,\n);\n\n/**\n * The React context holding the nearest {@link DomainAvailabilityStore}, or\n * `null`, in which case every Domain reads as unavailable. Provide a store\n * from {@link createDomainAvailabilityStore} through it to seed presence in a\n * test; otherwise use {@link DomainAvailabilityProvider}.\n *\n * @category Extensions\n */\nexport const DomainAvailabilityContext = DomainAvailabilityPanelStore.Context;\n\n/**\n * Creates one {@link DomainAvailabilityStore} and provides it for as long as it\n * is mounted. The app mounts one near the root and feeds it from each Domain's\n * `<domain>.available` Topic.\n *\n * @category Extensions\n */\nexport const DomainAvailabilityProvider = DomainAvailabilityPanelStore.Provider;\n\n/**\n * The nearest {@link DomainAvailabilityStore}, or `null` outside a provider.\n *\n * @category Extensions\n */\nexport const useDomainAvailabilityStore = DomainAvailabilityPanelStore.useStore;\n\n// Stable fallbacks for `useSyncExternalStore` with no provider in the tree.\nconst NO_SUBSCRIBE = (): (() => void) => () => {};\n\n/**\n * Whether `domain`'s Domain is currently available, read reactively from the\n * nearest {@link DomainAvailabilityStore}. `undefined`, a Domain nothing has\n * announced, and a tree with no store all read `false`.\n */\nexport function useDomainAvailable(domain: string | undefined): boolean {\n const store = useDomainAvailabilityStore();\n const snapshot = (): boolean =>\n store && domain ? store.isAvailable(domain) : false;\n return useSyncExternalStore(\n store ? store.subscribe : NO_SUBSCRIBE,\n snapshot,\n snapshot,\n );\n}\n","import { createContext, type ReactNode, useContext, useRef } from \"react\";\n\n/**\n * A per-panel React context so both a widget body and the panel chrome reach\n * the same off-tree store, scoped to the nearest provider.\n *\n * The provider creates one store and holds it for its whole life, so its\n * context value never changes identity; live data flows through the store's\n * own `subscribe`. `useStore` is `null` with no provider, which every reader\n * treats as a no-op. `Context` is returned raw for a caller that owns the\n * store's lifetime itself (a test injecting a pre-seeded store).\n */\nexport interface PanelStore<Store> {\n Context: React.Context<Store | null>;\n Provider: (props: { children?: ReactNode }) => ReactNode;\n useStore: () => Store | null;\n}\n\nexport function createPanelStore<Store>(\n create: () => Store,\n): PanelStore<Store> {\n const Context = createContext<Store | null>(null);\n\n function Provider({ children }: { children?: ReactNode }) {\n const storeRef = useRef<Store | null>(null);\n if (storeRef.current === null) storeRef.current = create();\n return (\n <Context.Provider value={storeRef.current}>{children}</Context.Provider>\n );\n }\n\n function useStore(): Store | null {\n return useContext(Context);\n }\n\n return { Context, Provider, useStore };\n}\n","/**\n * The calendar the game is running, re-exported from the unit model. It lives\n * in `@ksp-gonogo/sitrep-sdk` because it decides the ratio of `d` to `s`, so\n * the formatters and `Value` arithmetic must both follow it.\n */\n\nexport {\n type KspCalendar,\n kspCalendar,\n kspYearDays,\n STOCK_KERBIN_CALENDAR,\n setKspCalendar,\n} from \"@ksp-gonogo/sitrep-sdk\";\n","import type { Tone } from \"@ksp-gonogo/sitrep-sdk\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { TONE_TEXT } from \"./tone\";\n\n/**\n * How far plain text recedes: `muted` for secondary text, `faint` the quietest\n * tier below it, for a readout that should recede rather than compete, like\n * the numeric echo beside an analog stick.\n *\n * @category Typography\n */\nexport type TextLevel = \"muted\" | \"faint\";\n\n/**\n * The type-scale steps {@link Text}'s `size` accepts, smallest to largest:\n * `xs` for dense rows, `sm` for values, `base` for prose, `lg` for a headline\n * figure.\n *\n * @category Typography\n */\nexport type TextSize = \"xs\" | \"sm\" | \"base\" | \"lg\";\n\n/**\n * The font weights {@link Text}'s `weight` accepts: `regular` (400) and\n * `semibold` (600).\n *\n * @category Typography\n */\nexport type TextWeight = \"regular\" | \"semibold\";\n\n/**\n * Props for {@link Text}. Any other `span` attribute passes through.\n *\n * @category Typography\n */\nexport interface TextProps extends HTMLAttributes<HTMLSpanElement> {\n /** The state the text shows. Defaults to `neutral`, the primary text colour. */\n tone?: Tone;\n /**\n * Recedes neutral text to a quieter tier. A text with a state keeps its\n * tone's colour, since a state is never dimmed away, so\n * `tone={alarm ? \"nogo\" : undefined} level=\"faint\"` reads faint until the\n * alarm.\n */\n level?: TextLevel;\n /** Adds `margin-left: 2px` so the value sits apart from a preceding label. */\n spaced?: boolean;\n /**\n * Font size, snapped to the type scale. Omit to inherit the ambient\n * font-size; set it for dense list or grid rows that need to stay off the\n * body-text size.\n */\n size?: TextSize;\n /** Font weight. Omit to inherit the ambient weight; set `semibold` to emphasise a key figure. */\n weight?: TextWeight;\n children?: ReactNode;\n}\n\nconst LEVEL_COLOR: Record<TextLevel, string> = {\n muted: \"var(--color-text-muted)\",\n faint: \"var(--color-text-faint)\",\n};\n\n/**\n * Inline style declarations that recede every neutral word inside a box to the\n * faint text level, which still meets WCAG AA contrast on a panel. Use it to\n * quiet a block of words; opacity would drop them below contrast, so keep\n * opacity for marks that carry no text. A word in a tone keeps its tone. The\n * same declarations as {@link faintText}, for a `style` prop.\n *\n * @category Typography\n */\nexport const FAINT_TEXT_STYLE: Readonly<Record<string, string>> = {\n color: LEVEL_COLOR.faint,\n \"--color-neutral-text\": LEVEL_COLOR.faint,\n \"--color-text-primary\": LEVEL_COLOR.faint,\n \"--color-text-muted\": LEVEL_COLOR.faint,\n \"--color-text-dim\": LEVEL_COLOR.faint,\n};\n\n/**\n * {@link FAINT_TEXT_STYLE} as a CSS declaration string, for a styled-components\n * rule.\n *\n * @category Typography\n */\nexport function faintText(): string {\n return Object.entries(FAINT_TEXT_STYLE)\n .map(([property, value]) => `${property}: ${value};`)\n .join(\"\\n\");\n}\n\nconst WEIGHT_STYLES = {\n regular: css`\n font-weight: 400;\n `,\n semibold: css`\n font-weight: 600;\n `,\n} as const;\n\nconst SIZE_STYLES = {\n xs: css`\n font-size: var(--font-size-compact);\n `,\n sm: css`\n font-size: var(--font-size-value);\n `,\n base: css`\n font-size: var(--font-size-prose);\n `,\n lg: css`\n font-size: var(--font-size-figure);\n `,\n} as const;\n\n/**\n * An inline `span` with a tone, level, size and weight. Digits are always\n * tabular (`font-variant-numeric: tabular-nums`), so a changing number does\n * not jitter. It renders whatever it is given, with no magnitude, unit or\n * formatting of its own.\n *\n * {@link Unit} renders a quantity and its symbol; `Text` colours and sizes the\n * span around it. The two compose, and that is the normal case.\n *\n * @example\n * ```tsx\n * <Cluster>\n * <Text level=\"muted\" size=\"xs\">Altitude</Text>\n * <Text tone=\"go\" weight=\"semibold\">\n * <Unit value={altitude} />\n * </Text>\n * </Cluster>\n * ```\n *\n * @category Typography\n */\nexport function Text({\n tone = \"neutral\",\n level,\n spaced = false,\n size,\n weight,\n children,\n ...rest\n}: TextProps) {\n return (\n <Text__Root\n $color={\n tone === \"neutral\" && level !== undefined\n ? LEVEL_COLOR[level]\n : TONE_TEXT[tone]\n }\n $spaced={spaced}\n $size={size}\n $weight={weight}\n {...rest}\n >\n {children}\n </Text__Root>\n );\n}\n\nconst Text__Root = styled.span<{\n $color: string;\n $spaced: boolean;\n $size?: TextSize;\n $weight?: TextWeight;\n}>`\n font-variant-numeric: tabular-nums;\n color: ${({ $color }) => $color};\n ${({ $size }) => $size && SIZE_STYLES[$size]}\n ${({ $weight }) => $weight && WEIGHT_STYLES[$weight]}\n ${({ $spaced }) => $spaced && `margin-left: var(--gap-lead-figure);`}\n`;\n","import type { ReactNode } from \"react\";\nimport { Text } from \"./Text\";\n\n// styleguide-emdash.test.ts allows the em dash here and nowhere else.\n/**\n * The null token, the dash that means \"no data yet\". Use it anywhere only a\n * string fits: a formatter's return value, a template fallback, an attribute\n * value. For a bare JSX node use {@link NullValue}. {@link Unit} already draws\n * it for an absent value.\n *\n * @category Unit\n */\nexport const NULL_DISPLAY = \"—\";\n\n/**\n * Renders {@link NULL_DISPLAY} as muted text, so a bare placeholder reads as\n * intentionally empty rather than as ordinary body text. Where a wrapper\n * already carries a placeholder look, interpolate {@link NULL_DISPLAY} into it\n * instead.\n *\n * @category Unit\n */\nexport function NullValue(): ReactNode {\n return <Text level=\"muted\">{NULL_DISPLAY}</Text>;\n}\n","import {\n calendarRatio,\n type DeclaredUnit,\n onUnitRegistered,\n type RegisteredUnit,\n type UnitDeclarations,\n type UnitRung,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport { GENERATED_UNIT_KINDS } from \"./__generated__/unit-kinds\";\nimport { formatKspDate } from \"./formatKspDate\";\n\n/** A symbol the generated first-party table carries, for the runtime lookups. */\ntype GeneratedUnit = keyof typeof GENERATED_UNIT_KINDS;\n\n/**\n * What the declaration says `Unit` measures, or `never` for a symbol nothing\n * declares.\n *\n * Every type in this module reads `UnitDeclarations` and nothing else, which is\n * what makes a unit an Uplink merges into it indistinguishable here from a\n * first-party one.\n */\ntype KindOfSymbol<Unit extends string> = Unit extends DeclaredUnit\n ? UnitDeclarations[Unit] extends { kind: infer Kind }\n ? Kind\n : never\n : never;\n\n/** Every declared symbol that measures `Kind`. */\ntype SymbolsOfKind<Kind> = {\n [Unit in DeclaredUnit]: KindOfSymbol<Unit> extends Kind ? Unit : never;\n}[DeclaredUnit];\n\n/** The ladder `Unit` is declared on, or `never` for a unit that climbs none. */\ntype LadderOfSymbol<Unit extends string> = Unit extends DeclaredUnit\n ? UnitDeclarations[Unit] extends { ladder: infer Ladder extends string }\n ? Ladder\n : never\n : never;\n\n/**\n * Every symbol of kind `Kind` that exists only as a conversion TARGET (`°C`),\n * which the declarations do not name and {@link kindOfConversion} still\n * resolves. A pair whose source is itself undeclared is skipped, or `°C→K`\n * would make every kind accept `K`.\n */\ntype ConversionTargetsOfKind<Kind> = {\n [Conversion in keyof typeof CONVERSIONS]: Conversion extends `${infer Source}→${infer Target}`\n ? [KindOfSymbol<Source>] extends [never]\n ? never\n : [KindOfSymbol<Source>] extends [Kind]\n ? Target\n : never\n : never;\n}[keyof typeof CONVERSIONS];\n\n/**\n * Every unit that measures the same thing as `Unit`, which is exactly the set a\n * value in `Unit` may be re-expressed in.\n *\n * This is what makes `format=\"km/h\"` check on a speed and fail on a length. A\n * unit an Uplink declares in `UnitDeclarations` widens the accepted set.\n *\n * A literal symbol nothing declares accepts no format at all, since there is no\n * kind to check the request against. Only a wide `string` accepts any string.\n *\n * @category Unit\n */\nexport type FormatsFor<Unit extends string> = string extends Unit\n ? string\n : FormatsForKind<KindOfSymbol<Unit>>;\n\n/**\n * {@link FormatsFor}, asked of the KIND rather than of one of its units, for a\n * pin addressed to a whole group that has no single unit to check against.\n *\n * @category Unit\n */\nexport type FormatsForKind<Kind> = SymbolsOfKind<Kind>;\n\n/**\n * Every unit a value in `Unit` may be SHOWN as, which is every unit of its kind\n * plus the presentation-only ones the conversion table reaches.\n *\n * Wider than {@link FormatsFor} by exactly the symbols that are not on the\n * wire: `format` pins a rung, `as` re-expresses, so `°C` belongs only to `as`.\n * Both refuse a cross-kind request: `as=\"kg\"` on a length is an error.\n *\n * @category Unit\n */\nexport type PresentableAs<Unit extends string> = string extends Unit\n ? string\n : PresentableAsKind<KindOfSymbol<Unit>>;\n\n/**\n * {@link PresentableAs}, asked of the KIND. See {@link FormatsForKind}.\n *\n * @category Unit\n */\nexport type PresentableAsKind<Kind> =\n | SymbolsOfKind<Kind>\n | ConversionTargetsOfKind<Kind>;\n\n/**\n * How many of the kind's BASE unit one of `symbol` is worth.\n *\n * The LIVE CALENDAR first, because `d`, `h`, `min` and `science/day` are sized\n * by the running game and codegen bakes stock Kerbin figures for all four.\n * Then the unit's declared ratio, then the rung's own divisor. The calendar\n * table is the SDK's, so a formatted duration and a computed one agree.\n */\nfunction ratioOf(symbol: string): number | undefined {\n const fromCalendar = calendarRatio(symbol);\n if (fromCalendar !== undefined) return fromCalendar;\n const declared = declarationOf(symbol);\n if (declared) return declared.ratio;\n const ladder = rungLadderOf(symbol);\n return ladder === undefined\n ? undefined\n : laddersByName[ladder]?.find((r) => r.symbol === symbol)?.per;\n}\n\nimport { formatDuration, formatIrlDuration } from \"./formatDuration\";\nimport { NULL_DISPLAY } from \"./NullValue\";\n\n/*\n * Unit-aware value formatting. The contract declares what a field IS (metres,\n * m/s, kelvin) and this decides how to SHOW it. The wire is canonical SI and\n * never pre-scaled, because a pre-scaled value cannot be graphed, diffed or\n * re-scaled by a consumer; scaling is a presentation decision.\n */\n\n/**\n * What a unit measures. The kind is what makes the system checkable: it says\n * `m` and `km` are interchangeable and `m` and `m/s` are not, and it selects\n * the ladder and the precision rule.\n *\n * @category Unit\n */\nexport type KnownQuantityKind =\n | \"length\"\n | \"speed\"\n | \"acceleration\"\n | \"mass\"\n | \"force\"\n | \"pressure\"\n | \"temperature\"\n | \"time\"\n // An INSTANT on the game's clock, not a length of it: render it with `<MissionDate>`, not `<Unit>`.\n | \"universalTime\"\n // Seconds by dimension but not a duration: a 320 s engine must not read \"5min 20s\".\n | \"specificImpulse\"\n // Wall-clock time, where a day is 24 hours rather than the game's 6.\n | \"irlTime\"\n | \"planeAngle\"\n | \"power\"\n // Data at rest (a file, a drive), bit-based like `dataRate` but not interchangeable with it.\n | \"data\"\n | \"dataRate\"\n | \"doseRate\"\n | \"irradiance\"\n | \"level\"\n | \"density\"\n // Kilograms per second, unlike `resourceFlow`, which counts in whatever the tank counts in.\n | \"massFlow\"\n | \"gravParameter\"\n | \"dimensionless\"\n | \"percent\"\n | \"scienceData\"\n | \"scienceRate\"\n | \"ratio\"\n /*\n * Non-dimensional kinds: \"this has no physical dimension\", unlike\n * `dimensionless` (a real measurement with no unit, Mach and TWR) and unlike\n * an absent unit (nobody said). They let the contract DECLARE non-quantities.\n */\n | \"area\"\n | \"volume\"\n | \"angularSpeed\"\n | \"funds\"\n | \"science\"\n | \"reputation\"\n // Currencies that FLOW: a rate wants a decimal where a balance does not.\n | \"fundsRate\"\n | \"reputationRate\"\n | \"count\"\n | \"id\"\n | \"resourceUnits\"\n | \"resourceFlow\"\n | \"text\"\n | \"flag\"\n | \"enum\"\n | \"n/a\";\n\n/**\n * A quantity kind, OPEN to kinds this package has never heard of.\n *\n * The `string & {}` member lets a third-party Uplink introduce its own dimension\n * while the known kinds still autocomplete.\n *\n * @category Unit\n */\nexport type QuantityKind = KnownQuantityKind | (string & {});\n\n/**\n * One step of a unit's scale: a symbol (`m`, `km`, `Mm`) and the size, in the\n * base unit, from which a value is written in it. A kind's steps in order are\n * its ladder, and choosing a unit for a value is choosing its rung.\n *\n * @category Unit\n */\nexport type Rung = UnitRung;\n\n/**\n * Ladders, ascending, each in its kind's base unit. A dimension with no entry\n * never scales.\n *\n * Deliberately absent:\n * - `speed`, because delta-v and surface speed are read in m/s universally\n * - `temperature`, because kelvin has no prefix convention and Celsius is an\n * OFFSET conversion (see `as`)\n * - `time`, which has its own composite formatter\n * - `gravParameter`, which uses scientific notation (see `SCIENTIFIC`)\n * - `planeAngle`, `ratio`, `dimensionless`, which have no magnitudes to climb\n */\nexport const LADDERS = {\n length: [\n { from: 0, symbol: \"m\", per: 1 },\n { from: 1e3, symbol: \"km\", per: 1e3 },\n { from: 1e6, symbol: \"Mm\", per: 1e6 },\n { from: 1e9, symbol: \"Gm\", per: 1e9 },\n // Out of reach in the stock system, kept so an outer-planets install does not stop at \"1500.0 Gm\".\n { from: 1e12, symbol: \"Tm\", per: 1e12 },\n ],\n // Every threshold and divisor is in KILOGRAMS, including the gram-based astronomical rungs (1 Yg is 1e21 kg).\n mass: [\n { from: 0, symbol: \"kg\", per: 1 },\n { from: 1e3, symbol: \"t\", per: 1e3 },\n { from: 1e6, symbol: \"kt\", per: 1e6 },\n { from: 1e9, symbol: \"Tg\", per: 1e9 },\n { from: 1e12, symbol: \"Pg\", per: 1e12 },\n { from: 1e15, symbol: \"Eg\", per: 1e15 },\n { from: 1e18, symbol: \"Zg\", per: 1e18 },\n { from: 1e21, symbol: \"Yg\", per: 1e21 },\n ],\n force: [\n { from: 0, symbol: \"N\", per: 1 },\n { from: 1e3, symbol: \"kN\", per: 1e3 },\n { from: 1e6, symbol: \"MN\", per: 1e6 },\n ],\n // Descends below the base unit: the upper atmosphere runs to fractions of a pascal.\n pressure: [\n { from: 0, symbol: \"mPa\", per: 1e-3 },\n { from: 1, symbol: \"Pa\", per: 1 },\n { from: 1e3, symbol: \"kPa\", per: 1e3 },\n { from: 1e6, symbol: \"MPa\", per: 1e6 },\n ],\n /*\n * Byte rungs on a BIT base, so a drive in MB and a link budget in bit/s are\n * commensurable. Decimal throughout (1 KB is 8e3 bit), matching `dataRate`;\n * the binary KiB family is deliberately absent. The `bit` rung keeps a\n * sub-byte reading from rendering as a fraction of a byte.\n */\n data: [\n { from: 0, symbol: \"bit\", per: 1 },\n { from: 8, symbol: \"B\", per: 8 },\n { from: 8e3, symbol: \"KB\", per: 8e3 },\n { from: 8e6, symbol: \"MB\", per: 8e6 },\n { from: 8e9, symbol: \"GB\", per: 8e9 },\n ],\n /**\n * The bit-rate rungs, decimal throughout and based in bit/s, matching `data`.\n */\n dataRate: [\n { from: 0, symbol: \"bit/s\", per: 1 },\n { from: 1e3, symbol: \"kbit/s\", per: 1e3 },\n { from: 1e6, symbol: \"Mbit/s\", per: 1e6 },\n { from: 1e9, symbol: \"Gbit/s\", per: 1e9 },\n ],\n // Based in watts although the wire declares kW; the MW rung covers reentry heat flux.\n power: [\n { from: 0, symbol: \"W\", per: 1 },\n { from: 1e3, symbol: \"kW\", per: 1e3 },\n { from: 1e6, symbol: \"MW\", per: 1e6 },\n { from: 1e9, symbol: \"GW\", per: 1e9 },\n ],\n // One always-on rung: dose rates arrive in rad/s and are conventionally read in rad/h.\n doseRate: [{ from: 0, symbol: \"rad/h\", per: 1 / 3600 }],\n // Based in kg/m³ with a gram rung beneath it: sea-level air is about one kilogram per cubic metre, and the upper atmosphere is read in grams.\n density: [\n { from: 0, symbol: \"g/m³\", per: 1e-3 },\n { from: 1, symbol: \"kg/m³\", per: 1 },\n ],\n} satisfies Record<string, readonly Rung[]>;\n\n/**\n * Every ladder by name: the first-party ones above and every one a\n * registration has supplied rungs for since.\n */\nconst laddersByName: Record<string, readonly Rung[]> = { ...LADDERS };\n\n/**\n * Every ladder (a kind's {@link Rung}s in order) a declared unit names, and\n * therefore every group that settles one unit together. A unit on none of them is a group of its own: `m`, `km`\n * and `Mm` are one group and `s` and `min` are two.\n *\n * @category Unit\n */\nexport type LadderName = {\n [Unit in DeclaredUnit]: LadderOfSymbol<Unit>;\n}[DeclaredUnit];\n\n/**\n * What a pin may be addressed to: one GROUP, named the way the group is formed.\n *\n * A unit on a ladder is named by the ladder, because every unit of it settles\n * together and `m`, `km` and `Mm` are one thing to pin. Everything else is named\n * by the unit, because nothing interconverts it with its siblings and a pin on\n * `s` says nothing about `min`. Keyed this way, a group is pinned at most once\n * by construction.\n *\n * @category Unit\n */\nexport type UnitGroupKey =\n | LadderName\n | {\n [Unit in DeclaredUnit]: [LadderOfSymbol<Unit>] extends [never]\n ? Unit\n : never;\n }[DeclaredUnit];\n\n/**\n * What the group named by `Group` measures, so a pin addressed to it can be checked.\n *\n * A ladder key measures whatever its units do; a unit key resolves through the\n * declarations the same way a lone `<Unit>` does.\n *\n * @category Unit\n */\nexport type KindOfGroup<Group extends UnitGroupKey> = Group extends LadderName\n ? {\n [Unit in DeclaredUnit]: [LadderOfSymbol<Unit>] extends [Group]\n ? [LadderOfSymbol<Unit>] extends [never]\n ? never\n : KindOfSymbol<Unit>\n : never;\n }[DeclaredUnit]\n : KindOfSymbol<Group>;\n\n/**\n * Kinds that render in scientific notation when nothing says otherwise: their\n * exponent is huge and has no conventional prefix.\n */\nconst SCIENTIFIC = new Set<string>([\"gravParameter\"]);\n\n/** Significant figures in a scientific mantissa, unless the caller overrides. */\nconst SCIENTIFIC_SIGNIFICANT = 4;\n\nconst SUPERSCRIPT: Record<string, string> = {\n \"0\": \"⁰\",\n \"1\": \"¹\",\n \"2\": \"²\",\n \"3\": \"³\",\n \"4\": \"⁴\",\n \"5\": \"⁵\",\n \"6\": \"⁶\",\n \"7\": \"⁷\",\n \"8\": \"⁸\",\n \"9\": \"⁹\",\n \"-\": \"⁻\",\n};\n\n/** `3.532×10¹²`, not `3.532e12`, as a plain string usable in an axis label or `aria-label`. */\nfunction toScientific(value: number, significant: number): string {\n if (value === 0) return \"0\";\n const exponent = Math.floor(Math.log10(Math.abs(value)));\n const mantissa = value / 10 ** exponent;\n // `significant` counts digits, and the mantissa always has exactly one before the point, so the rest are decimals.\n const digits = String(exponent)\n .split(\"\")\n .map((c) => SUPERSCRIPT[c] ?? c)\n .join(\"\");\n return `${mantissa.toFixed(Math.max(0, significant - 1))}×10${digits}`;\n}\n\n/** Standard gravity, the SI definition, which is also KSP's single global constant and Kerbin's surface gravity. */\nexport { STANDARD_GRAVITY } from \"@ksp-gonogo/sitrep-sdk\";\n\nimport { STANDARD_GRAVITY } from \"@ksp-gonogo/sitrep-sdk\";\n\n/**\n * Presentation conversions: what a value may be SHOWN as, given what it IS.\n *\n * Keyed source→target, `target = value / per + offset`, which covers the\n * offset conversions a ladder cannot. Only within a kind: `formatQuantity`\n * refuses a cross-kind conversion. `as const` is load-bearing, since\n * {@link PresentableAs} reads the pair KEYS.\n */\nconst CONVERSIONS = {\n \"K→°C\": { per: 1, offset: -273.15 },\n \"°C→K\": { per: 1, offset: 273.15 },\n \"m/s²→g\": { per: STANDARD_GRAVITY, offset: 0 },\n \"g→m/s²\": { per: 1 / STANDARD_GRAVITY, offset: 0 },\n \"rad→°\": { per: Math.PI / 180, offset: 0 },\n \"°→rad\": { per: 180 / Math.PI, offset: 0 },\n} as const satisfies Record<string, { per: number; offset: number }>;\n\n/** The same table, indexable by a key built at runtime. */\nconst CONVERSION_BY_PAIR: Record<string, { per: number; offset: number }> =\n CONVERSIONS;\n\n/**\n * How much precision a kind is read at, as decimal places on the SCALED value.\n *\n * Per kind rather than per magnitude, so a readout holds a stable digit count\n * as the value moves.\n */\nconst DECIMALS: Record<string, number> = {\n length: 1,\n speed: 1,\n acceleration: 2,\n mass: 2,\n force: 1,\n pressure: 2,\n temperature: 0,\n time: 0,\n irlTime: 0,\n specificImpulse: 0,\n planeAngle: 2,\n density: 4,\n // A small RCS burn runs at a fraction of a kilogram a second, which must not round to zero.\n massFlow: 2,\n data: 1,\n dataRate: 1,\n power: 1,\n doseRate: 4,\n irradiance: 1,\n level: 1,\n dimensionless: 2,\n percent: 1,\n scienceData: 1,\n scienceRate: 1,\n ratio: 0,\n // A count is integral: \"3.00 crew\" is wrong in a way \"3.00 Mach\" is not.\n count: 0,\n id: 0,\n resourceUnits: 1,\n resourceFlow: 2,\n area: 1,\n volume: 1,\n angularSpeed: 0,\n funds: 0,\n science: 1,\n reputation: 1,\n // A reputation decaying by 0.11 a day rounds to nothing without decimals.\n fundsRate: 1,\n reputationRate: 2,\n};\n\n/**\n * What to SHOW beside the number, when it differs from the token the wire\n * carries. The non-dimensional tokens name a CATEGORY, not a symbol (\"12\n * count\" is not a readout), so they render with no symbol at all. `ratio` and\n * `time` carry extra behaviour and keep their own branches below.\n */\nconst DISPLAY_BY_KIND: Record<string, string> = {\n // Text fallbacks for the currency icons.\n funds: \"f\",\n science: \"sci\",\n reputation: \"rep\",\n // The wire token is `Mit`; the game writes \"mits\".\n scienceData: \"mits\",\n specificImpulse: \"s\",\n count: \"\",\n id: \"\",\n text: \"\",\n flag: \"\",\n enum: \"\",\n \"n/a\": \"\",\n};\n\n/**\n * What `registerUnit` taught the kit about each Uplink unit, layered over the\n * generated first-party table. Empty until an Uplink registers something.\n */\ninterface UnitFacts {\n readonly kind: QuantityKind;\n readonly ratio: number;\n readonly ladder?: string;\n}\n\nconst REGISTERED_UNITS: Record<string, UnitFacts> = {};\n\n/** The kind every ladder scales, by ladder name. */\nconst LADDER_KINDS: Record<string, QuantityKind> = Object.fromEntries(\n Object.keys(LADDERS).map((name) => [name, name]),\n);\n\n/** The declaration a symbol has, registered or first-party, if it has one. */\nfunction declarationOf(symbol: string): UnitFacts | undefined {\n return (\n REGISTERED_UNITS[symbol] ?? GENERATED_UNIT_KINDS[symbol as GeneratedUnit]\n );\n}\n\n/**\n * The ladder a symbol climbs within, by name.\n *\n * A declared unit climbs exactly the ladder its declaration names, and none if\n * it names none, matching `UnitGroupKey`. The ladder is declared rather than\n * taken from the kind because bits and bytes are both `data` and must never\n * interleave. An undeclared symbol is a rung and climbs the ladder listing it.\n */\nfunction ladderNameOf(symbol: string | undefined): string | undefined {\n if (symbol === undefined) return undefined;\n const declared = declarationOf(symbol);\n if (declared) return declared.ladder;\n return rungLadderOf(symbol);\n}\n\n/** The rungs a value climbs. A unit on no ladder never scales. */\nfunction ladderForUnit(unit: string | undefined): readonly Rung[] | undefined {\n const name = ladderNameOf(unit);\n return name === undefined ? undefined : laddersByName[name];\n}\n\n/**\n * The ladder each rung symbol belongs to, since a rung such as `Mbit/s` is not\n * a declared unit. Built lazily and rebuilt whenever a registration supplies a\n * ladder.\n */\nlet rungLadders: Record<string, string> | undefined;\n\nfunction rungLadderOf(symbol: string): string | undefined {\n if (rungLadders === undefined) {\n rungLadders = {};\n for (const [name, rungs] of Object.entries(laddersByName)) {\n for (const rung of rungs) {\n // First ladder wins, so a base symbol shared with the model keeps the model's answer rather than a ladder's.\n rungLadders[rung.symbol] ??= name;\n }\n }\n }\n return rungLadders[symbol];\n}\n\nfunction kindOfRung(symbol: string): QuantityKind | undefined {\n const name = rungLadderOf(symbol);\n return name === undefined ? undefined : LADDER_KINDS[name];\n}\n\n/** The kind of a presentation-only unit (`°C`), named only by the conversion table. */\nfunction kindOfConversion(symbol: string): QuantityKind | undefined {\n for (const pair of Object.keys(CONVERSIONS)) {\n const [from, to] = pair.split(\"\\u2192\");\n if (to === symbol) {\n return declarationOf(from)?.kind ?? kindOfRung(from);\n }\n }\n return undefined;\n}\n\n/**\n * What each displayed symbol is CALLED, for the accessibility tree: a screen\n * reader announces `km` as \"kay em\" and `°` as nothing at all.\n *\n * Keyed on the DISPLAYED symbol rather than the wire token, so every rung and\n * every currency abbreviation has its own entry.\n */\nconst WORD_BY_SYMBOL: Record<string, string> = {\n m: \"metres\",\n km: \"kilometres\",\n Mm: \"megametres\",\n Gm: \"gigametres\",\n Tm: \"terametres\",\n kg: \"kilograms\",\n t: \"tonnes\",\n kt: \"kilotonnes\",\n Tg: \"teragrams\",\n Pg: \"petagrams\",\n Eg: \"exagrams\",\n Zg: \"zettagrams\",\n Yg: \"yottagrams\",\n N: \"newtons\",\n kN: \"kilonewtons\",\n MN: \"meganewtons\",\n mPa: \"millipascals\",\n Pa: \"pascals\",\n kPa: \"kilopascals\",\n MPa: \"megapascals\",\n W: \"watts\",\n kW: \"kilowatts\",\n MW: \"megawatts\",\n GW: \"gigawatts\",\n \"bit/s\": \"bits per second\",\n \"kbit/s\": \"kilobits per second\",\n \"Mbit/s\": \"megabits per second\",\n \"Gbit/s\": \"gigabits per second\",\n bit: \"bits\",\n B: \"bytes\",\n KB: \"kilobytes\",\n MB: \"megabytes\",\n GB: \"gigabytes\",\n \"m/s\": \"metres per second\",\n \"m/s\\u00B2\": \"metres per second squared\",\n g: \"gee\",\n rpm: \"revolutions per minute\",\n \"\\u00B0\": \"degrees\",\n \"\\u2032\": \"arcminutes\",\n \"\\u2033\": \"arcseconds\",\n rad: \"radians\",\n K: \"kelvin\",\n \"\\u00B0C\": \"degrees celsius\",\n s: \"seconds\",\n min: \"minutes\",\n h: \"hours\",\n dB: \"decibels\",\n \"rad/s\": \"radians per second\",\n \"W/m\\u00B2\": \"watts per square metre\",\n \"g/m\\u00B3\": \"grams per cubic metre\",\n \"kg/m\\u00B3\": \"kilograms per cubic metre\",\n \"m\\u00B3/s\\u00B2\": \"cubic metres per second squared\",\n \"m\\u00B2\": \"square metres\",\n \"m\\u00B3\": \"cubic metres\",\n \"%\": \"percent\",\n Mit: \"mits\",\n \"science/day\": \"science per day\",\n \"f/day\": \"funds per day\",\n \"rep/day\": \"reputation per day\",\n units: \"units\",\n \"units/s\": \"units per second\",\n f: \"funds\",\n sci: \"science\",\n rep: \"reputation\",\n};\n\n/**\n * What a displayed symbol is called, or undefined for one with no word (the\n * category kinds display as an empty string and have nothing to announce).\n */\nexport function wordForSymbol(symbol: string): string | undefined {\n return WORD_BY_SYMBOL[symbol];\n}\n\nfunction formatWhole(\n quantity: { magnitude: number; unit: string } | null | undefined,\n opts: FormatQuantityOptions,\n): FormattedQuantity {\n return formatQuantity(quantity?.magnitude, quantity?.unit, opts);\n}\n\n/**\n * A quantity as a screen reader should hear it: the value followed by the\n * unit's WORD rather than its symbol.\n *\n * For the places that cannot take a node (`aria-label`, `title`, an SVG\n * `aria-valuetext`), so an accessible name reads \"250.0 kilometres\" rather\n * than \"kay em\". If the result is going to be rendered, use {@link Unit} or\n * {@link writeQuantity}. Falls back to the symbol for a unit with no word.\n *\n * @category Unit\n */\nexport function speakQuantity(\n quantity: { magnitude: number; unit: string } | null | undefined,\n opts: FormatQuantityOptions = {},\n): string {\n const { value: text, symbol } = formatWhole(quantity, opts);\n if (symbol === \"\") return text;\n return `${text} ${wordForSymbol(symbol) ?? symbol}`;\n}\n\n/**\n * The symbols written hard against the number: plane angle, because SI says so\n * (`22°`, but `22 °C`), and the currency marks, which are typography rather\n * than unit symbols (`42,500f`). Shared by `<Unit>` and `writeQuantity` so\n * both agree.\n */\nexport const ATTACHED_SYMBOLS: ReadonlySet<string> = new Set([\n \"°\",\n \"′\",\n \"″\",\n \"f\",\n \"sci\",\n \"rep\",\n]);\n\n/**\n * A quantity as it is WRITTEN: the value and the unit's symbol, `250.0 km`.\n *\n * For visible text that cannot take a node: an SVG `<text>`, a canvas label, a\n * chart annotation measured before it is drawn. Anywhere a node fits, use\n * {@link Unit}; for an accessible name, {@link speakQuantity}.\n *\n * An ordinary space, not the thin space `<Unit>` sets, since a U+2009 is at\n * the mercy of the SVG or canvas renderer.\n *\n * @category Unit\n */\nexport function writeQuantity(\n quantity: { magnitude: number; unit: string } | null | undefined,\n opts: FormatQuantityOptions = {},\n): string {\n const { value: text, symbol } = formatWhole(quantity, opts);\n if (symbol === \"\") return text;\n return ATTACHED_SYMBOLS.has(symbol)\n ? `${text}${symbol}`\n : `${text} ${symbol}`;\n}\n\n/**\n * The symbol shown beside a value: the kind's display override if it has one,\n * otherwise the token itself.\n */\nexport function displaySymbol(\n unit: string,\n kind: QuantityKind | undefined,\n): string {\n return kind === undefined ? unit : (DISPLAY_BY_KIND[kind] ?? unit);\n}\n\n/**\n * What a unit symbol measures, or undefined for one we do not know: from its\n * declaration, then the ladder a rung belongs to, then the conversion that\n * names it.\n */\nexport function kindOfUnit(\n symbol: string | undefined,\n): QuantityKind | undefined {\n if (symbol === undefined) return undefined;\n return (\n declarationOf(symbol)?.kind ??\n kindOfRung(symbol) ??\n kindOfConversion(symbol)\n );\n}\n\nfunction sameRungs(a: readonly Rung[], b: readonly Rung[]): boolean {\n return (\n a.length === b.length &&\n a.every(\n (rung, i) =>\n rung.from === b[i].from &&\n rung.symbol === b[i].symbol &&\n rung.per === b[i].per,\n )\n );\n}\n\n/**\n * Applies what one `registerUnit` call says about how a unit READS, including\n * registrations made before this module loaded.\n *\n * Two registrations of one symbol must mean the same thing by it, and two of\n * one ladder must climb the same rungs, so a disagreement throws rather than\n * rendering by load order. Identical registrations are a no-op, so two Uplinks\n * can share a ladder.\n */\nfunction applyRegisteredUnit(unit: RegisteredUnit): void {\n const { symbol, kind, ratio, ladder, rungs } = unit;\n const prior = declarationOf(symbol);\n if (prior && (prior.kind !== kind || prior.ladder !== ladder)) {\n throw new Error(\n `Unit \"${symbol}\" is already declared as ${prior.kind}` +\n `${prior.ladder ? ` (ladder ${prior.ladder})` : \"\"} and cannot be ` +\n `registered as ${kind}${ladder ? ` (ladder ${ladder})` : \"\"}. ` +\n \"Two mods declaring the same symbol must mean the same thing by it: \" +\n \"a value would otherwise render differently depending on which loaded last.\",\n );\n }\n if (ladder !== undefined) {\n const ladderKind = LADDER_KINDS[ladder];\n if (ladderKind !== undefined && ladderKind !== kind) {\n throw new Error(\n `Unit \"${symbol}\" names the ${ladder} ladder, which scales ${ladderKind}, ` +\n `not ${kind}. Every unit on one ladder measures one kind.`,\n );\n }\n const existing = laddersByName[ladder];\n if (rungs !== undefined && existing && !sameRungs(existing, rungs)) {\n throw new Error(\n `The ${ladder} ladder already has rungs, and \"${symbol}\" registers ` +\n \"different ones. Two registrations of one ladder must climb the same rungs.\",\n );\n }\n LADDER_KINDS[ladder] = kind;\n if (rungs !== undefined && !existing) {\n laddersByName[ladder] = rungs;\n // A new ladder brings rung symbols the cached index does not hold.\n rungLadders = undefined;\n }\n }\n REGISTERED_UNITS[symbol] =\n ladder === undefined ? { kind, ratio } : { kind, ratio, ladder };\n if (unit.decimals !== undefined) DECIMALS[kind] = unit.decimals;\n if (unit.scientific) SCIENTIFIC.add(kind);\n if (unit.display !== undefined) DISPLAY_BY_KIND[kind] = unit.display;\n if (unit.word !== undefined) WORD_BY_SYMBOL[symbol] = unit.word;\n}\n\nexport interface FormatQuantityOptions {\n /**\n * Pin the unit rather than letting the ladder choose, for where convention\n * beats magnitude (orbital velocity in km/s, a broadcast in km/h).\n *\n * Typed by KIND, so `format=\"km/h\"` checks on a speed and is an error on a\n * length. Any unit of the same kind is accepted, including ones the ladder\n * would never pick.\n */\n format?: string;\n /**\n * `\"auto\"` climbs the kind's ladder (or uses the kind's default presentation,\n * such as scientific notation or a composite duration), `\"never\"` holds the\n * base unit and formats it as a plain number, `\"scientific\"` forces\n * scientific notation. `\"compact\"` is auto, except that a kind counted like\n * money writes ten thousand and over to three significant figures with a\n * magnitude suffix (`1.29Mf`), for a tile with no room for every digit.\n * Defaults to auto.\n */\n scale?: \"auto\" | \"never\" | \"scientific\" | \"compact\";\n /**\n * Show the value in a different unit OF THE SAME KIND: `as: \"°C\"` on a kelvin\n * field, `as: \"g\"` on an m/s² one. A cross-kind request is refused and the\n * value renders in its true unit.\n */\n as?: string;\n /** Override the kind's decimal places. */\n decimals?: number;\n /** The rung the value was last shown at, so a value hovering on a boundary does not flicker. */\n heldSymbol?: string;\n}\n\nexport interface FormattedQuantity {\n /** The scaled number, formatted. `NULL_DISPLAY` when there is no value. */\n value: string;\n /** The symbol to show beside it. Empty when the unit is unknown or bare. */\n symbol: string;\n /** The rung actually used, to feed back as `heldSymbol` on the next render. */\n rung: string;\n}\n\n/**\n * How far a value must fall BELOW a rung's threshold before dropping back down,\n * so 999.6 m does not flicker between \"1.0 km\" and \"1000 m\".\n */\nconst HYSTERESIS = 0.05;\n\n/** The kinds counted like money: they group from a thousand, and their symbols attach (see ATTACHED_SYMBOLS). */\nconst COUNTED_LIKE_MONEY: ReadonlySet<string> = new Set([\n \"funds\",\n \"science\",\n \"reputation\",\n]);\n\n/** `useGrouping`'s string form is ES2023 and the published package targets ES2022, so it is typed here and cast once. */\ninterface GroupingOptions\n extends Omit<Intl.NumberFormatOptions, \"useGrouping\"> {\n useGrouping?: \"always\" | \"auto\" | \"min2\" | boolean;\n}\n\n/**\n * The locale every quantity in the app is written in.\n *\n * `undefined` means the reader's own. Only a test pins it (to `en-GB`), so a\n * render is reproducible across machines.\n */\nlet locale: string | undefined;\n\n/**\n * Pin the locale every quantity is written in, or pass `undefined` to go back\n * to the reader's own. One call changes every readout at once.\n *\n * @category Unit\n */\nexport function setQuantityLocale(next: string | undefined): void {\n locale = next;\n formatters.clear();\n}\n\n/** Built `Intl.NumberFormat`s, cached because construction is expensive and this runs per readout per frame. */\nconst formatters = new Map<string, Intl.NumberFormat>();\n\nfunction numberFormat(decimals: number, money: boolean): Intl.NumberFormat {\n const key = `${locale ?? \"\"}|${decimals}|${money}`;\n let existing = formatters.get(key);\n if (existing === undefined) {\n const options: GroupingOptions = {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n // `min2` is SI's rule (`3200 K`, `12,400 m`); money always groups (`2,340f`).\n useGrouping: money ? \"always\" : \"min2\",\n };\n existing = new Intl.NumberFormat(\n locale,\n options as Intl.NumberFormatOptions,\n );\n formatters.set(key, existing);\n }\n return existing;\n}\n\n/** The smallest magnitude `scale: \"compact\"` shortens; below it every digit fits. */\nconst COMPACT_FROM = 1e4;\n\n/**\n * The magnitude suffixes `scale: \"compact\"` writes, ascending. Spelled out\n * rather than taken from `Intl`'s compact notation, whose suffixes vary by\n * locale and include a lowercase `m` that reads as metres beside a unit.\n */\nconst COMPACT_SUFFIXES = [\"\", \"k\", \"M\", \"G\", \"T\"] as const;\n\n/** A money-like count to three significant figures with its magnitude suffix: `1.29M`, `12.5k`. */\nfunction compact(value: number): string {\n let tier = Math.min(\n Math.floor(Math.log10(Math.abs(value)) / 3),\n COMPACT_SUFFIXES.length - 1,\n );\n const format = significantFormat();\n let digits = format.format(value / 1000 ** tier);\n // Three significant figures round 999,500 up to a fourth digit, so it is \"1M\", never \"1,000k\".\n if (\n tier < COMPACT_SUFFIXES.length - 1 &&\n Math.abs(value / 1000 ** tier) >= 999.5\n ) {\n tier += 1;\n digits = format.format(value / 1000 ** tier);\n }\n return `${digits}${COMPACT_SUFFIXES[tier]}`;\n}\n\nfunction significantFormat(): Intl.NumberFormat {\n const key = `${locale ?? \"\"}|significant3`;\n let existing = formatters.get(key);\n if (existing === undefined) {\n existing = new Intl.NumberFormat(locale, {\n maximumSignificantDigits: 3,\n });\n formatters.set(key, existing);\n }\n return existing;\n}\n\n/**\n * A fixed-precision number, grouped by the locale's separator rather than\n * SI's thin space, since `<Unit>` already puts a thin space before the symbol.\n */\nfunction fixed(value: number, decimals: number, kind?: string): string {\n return numberFormat(\n decimals,\n kind !== undefined && COUNTED_LIKE_MONEY.has(kind),\n ).format(value);\n}\n\n/**\n * Format a value for display, given the unit the contract declared for it.\n *\n * Returns the parts separately rather than a joined string, because callers\n * need them apart: a readout renders the number large and the symbol small, and\n * an axis wants the number alone with the symbol in the axis label.\n */\nexport function formatQuantity(\n value: number | null | undefined,\n unit: string | undefined,\n opts: FormatQuantityOptions = {},\n): FormattedQuantity {\n if (value === null || value === undefined || !Number.isFinite(value)) {\n return { value: NULL_DISPLAY, symbol: \"\", rung: unit ?? \"\" };\n }\n\n // The presentation unit applies first so ladder, precision and notation describe the unit shown; a cross-kind request is refused.\n if (opts.as !== undefined && opts.as !== unit && unit !== undefined) {\n const conversion = CONVERSION_BY_PAIR[`${unit}→${opts.as}`];\n if (conversion && kindOfUnit(opts.as) === kindOfUnit(unit)) {\n const { as: _as, ...rest } = opts;\n return formatQuantity(\n value / conversion.per + conversion.offset,\n opts.as,\n rest,\n );\n }\n }\n\n const kind = kindOfUnit(unit);\n\n /*\n * A pin is refused when the kinds differ or either ratio is unknown, rather\n * than inventing a conversion factor. Not gated on the format differing\n * from the unit: `format=\"m\"` on 12,400 m must still defeat the ladder.\n */\n if (opts.format !== undefined && unit !== undefined) {\n const from = ratioOf(unit);\n const to = ratioOf(opts.format);\n if (\n from !== undefined &&\n to !== undefined &&\n kindOfUnit(opts.format) === kind\n ) {\n const { format: _format, ...rest } = opts;\n return formatQuantity((value * from) / to, opts.format, {\n ...rest,\n scale: \"never\",\n });\n }\n }\n\n // A ratio is 0-1 and shown as a percentage; a percent token is already 0-100 and must not be multiplied again.\n if (kind === \"ratio\") {\n const decimals = opts.decimals ?? DECIMALS.ratio ?? 0;\n return { value: fixed(value * 100, decimals), symbol: \"%\", rung: \"%\" };\n }\n\n // An undeclared unit renders bare rather than guessed at, as does \"1\" (explicitly dimensionless).\n if (unit === undefined || unit === \"1\" || kind === undefined) {\n const decimals = opts.decimals ?? (kind ? DECIMALS[kind] : undefined);\n return {\n value: decimals === undefined ? String(value) : fixed(value, decimals),\n symbol: unit === undefined || unit === \"1\" ? \"\" : unit,\n rung: unit ?? \"\",\n };\n }\n\n // `\"never\"` opts out of the composite and scientific presentations as well as the ladder; the result is still grouped, so never parse it.\n if (opts.scale !== \"never\") {\n if (opts.scale === \"scientific\" || SCIENTIFIC.has(kind)) {\n return {\n value: toScientific(\n value,\n opts.decimals === undefined\n ? SCIENTIFIC_SIGNIFICANT\n : opts.decimals + 1,\n ),\n symbol: displaySymbol(unit, kind),\n rung: unit,\n };\n }\n\n /*\n * Durations interleave their parts with the number (\"2h 15m\"), so the\n * symbol is empty. The formatters take SECONDS, and `ratioOf` sizes `d`,\n * `h` and `min` by the calendar the game reported.\n */\n if (kind === \"time\") {\n return {\n value: formatDuration(value * (ratioOf(unit) ?? 1)),\n symbol: \"\",\n rung: \"s\",\n };\n }\n\n if (kind === \"irlTime\") {\n return {\n value: formatIrlDuration(value * (ratioOf(unit) ?? 1)),\n symbol: \"\",\n rung: \"irl:s\",\n };\n }\n\n // A universal time is an INSTANT: a date on the game's calendar, never a duration.\n if (kind === \"universalTime\") {\n return { value: formatKspDate(value), symbol: \"\", rung: \"ut\" };\n }\n }\n\n const ladder = opts.scale === \"never\" ? undefined : ladderForUnit(unit);\n const decimals = opts.decimals ?? DECIMALS[kind] ?? 2;\n\n if (\n !ladder &&\n opts.scale === \"compact\" &&\n COUNTED_LIKE_MONEY.has(kind) &&\n Math.abs(value) >= COMPACT_FROM\n ) {\n return {\n value: compact(value),\n symbol: displaySymbol(unit, kind),\n rung: unit,\n };\n }\n\n if (!ladder) {\n return {\n value: fixed(value, decimals, kind),\n symbol: displaySymbol(unit, kind),\n rung: unit,\n };\n }\n\n const base = toBase(value, ladder, unit);\n\n const magnitude = Math.abs(base);\n let chosen = ladder[0];\n for (const rung of ladder) {\n if (magnitude >= rung.from) chosen = rung;\n }\n\n // Only ever holds a HIGHER rung, so a value climbing past a boundary scales up promptly.\n if (opts.heldSymbol && opts.heldSymbol !== chosen.symbol) {\n const held = ladder.find((r) => r.symbol === opts.heldSymbol);\n if (\n held &&\n held.from > chosen.from &&\n magnitude >= held.from * (1 - HYSTERESIS)\n ) {\n chosen = held;\n }\n }\n\n const scaled = base / chosen.per;\n\n // A nonzero reading too small for the lowest rung's decimals goes scientific rather than rendering as zero.\n if (\n scaled !== 0 &&\n chosen === ladder[0] &&\n Math.abs(scaled) * 10 ** decimals < 0.5\n ) {\n return {\n value: toScientific(\n scaled,\n opts.decimals === undefined\n ? SCIENTIFIC_SIGNIFICANT\n : opts.decimals + 1,\n ),\n symbol: chosen.symbol,\n rung: chosen.symbol,\n };\n }\n\n return {\n value: fixed(scaled, decimals),\n symbol: chosen.symbol,\n rung: chosen.symbol,\n };\n}\n\n/**\n * A scale: one rung, settled once, and every mark printed against it.\n *\n * For a moving-scale instrument (an altimeter strip, a chart axis): the marks\n * carry NO symbol, the symbol is shown once at the head, and every mark sits\n * on the rung settled from the value that defines the scale, so a strip never\n * reads \"500 m\" three marks below \"1.0 km\".\n *\n * Not a general string formatter ({@link writeQuantity}, {@link Unit}), and not\n * for aligning a group of readouts with no known bound ({@link UnitSharedFormat}).\n *\n * @category Unit\n */\nexport interface QuantityScale {\n /** The unit every mark is printed in. */\n readonly rung: string;\n /**\n * The symbol for that rung, to show ONCE beside the scale. Empty for a kind\n * that displays none, in which case the scale shows no header at all.\n */\n readonly symbol: string;\n /** One mark, as the bare number: {@link symbol} carries the unit for the whole scale. */\n mark(magnitude: number | null | undefined): string;\n}\n\n/**\n * Build a {@link QuantityScale} whose rung is taken from `reference`.\n *\n * `reference` is the value that DEFINES the scale rather than a value on it:\n * the top of an altimeter strip, an axis maximum. `opts.format` pins the rung\n * outright.\n *\n * @category Unit\n */\nexport function quantityScale(\n reference: { magnitude: number; unit: string } | null | undefined,\n opts: FormatQuantityOptions = {},\n): QuantityScale {\n const head = formatWhole(reference, opts);\n const unit = reference?.unit;\n return {\n rung: head.rung,\n symbol: head.symbol,\n mark: (magnitude) =>\n formatQuantity(magnitude, unit, { ...opts, format: head.rung }).value,\n };\n}\n\n/**\n * `value`, in the base unit of the ladder it climbs. KSP sends tonnes and kN,\n * so a field can arrive partway up its own ladder and must be normalised\n * before any magnitude comparison.\n */\nfunction toBase(\n value: number,\n ladder: readonly Rung[] | undefined,\n unit: string | undefined,\n): number {\n const declared = ladder?.find((r) => r.symbol === unit);\n return declared ? value * declared.per : value;\n}\n\n/** Where one reading sits on the ladder it climbs. */\nexport interface LadderPosition {\n /** How big it is in the ladder's BASE unit, absolute: the only figure comparable across rungs. */\n readonly base: number;\n /** The rung this reading ALONE would climb to, which is what a group votes with. */\n readonly rung: string;\n}\n\n/**\n * Where a reading sits on its ladder, for a caller settling one rung across\n * several readings: how they compare, and what each would choose alone.\n */\nexport function ladderPosition(\n magnitude: number,\n unit: string | undefined,\n): LadderPosition {\n const ladder = ladderForUnit(unit);\n return {\n base: Math.abs(toBase(magnitude, ladder, unit)),\n rung: formatQuantity(magnitude, unit).rung,\n };\n}\n\n/**\n * The set of rungs a unit may climb within, as a key: two readings can settle\n * on one rung exactly when they share this.\n *\n * Undefined for a unit that never climbs, deliberately: a duration, a mission\n * date and a scientific value report a `rung` nothing may be pinned to.\n *\n * Keyed by the LADDER a unit declares, never by its kind, since bits and bytes\n * are both `data` and must never share rungs.\n */\nexport function unitScaleKey(unit: string | undefined): string | undefined {\n if (unit === undefined) return undefined;\n const kind = kindOfUnit(unit);\n if (kind === undefined || SCIENTIFIC.has(kind)) return undefined;\n if (ladderForUnit(unit) === undefined) return undefined;\n const name = ladderNameOf(unit);\n return name === undefined ? undefined : ladderGroupKey(name);\n}\n\n/** The key every unit on one ladder reports under. */\nfunction ladderGroupKey(ladder: string): string {\n return `ladder:${ladder}`;\n}\n\n/**\n * The group a reading settles its whole FORMAT with, as a key.\n *\n * Wider than {@link unitScaleKey}: a rung can only be settled among readings\n * that share a ladder, but a digit count can be settled among any readings\n * written alike. A unit on a ladder groups by the ladder (even one with no\n * rungs registered yet), and a unit on none groups by ITSELF, never by kind.\n */\nexport function formatGroupKey(unit: string): string;\nexport function formatGroupKey(unit: string | undefined): string | undefined;\nexport function formatGroupKey(unit: string | undefined): string | undefined {\n if (unit === undefined) return undefined;\n const scale = unitScaleKey(unit);\n if (scale !== undefined) return scale;\n const declared = declarationOf(unit);\n return declared?.ladder === undefined\n ? `unit:${unit}`\n : ladderGroupKey(declared.ladder);\n}\n\n/**\n * The group a PIN is addressed to, from the token a caller keyed it by.\n *\n * The mirror of {@link formatGroupKey}: a pin names its group outright, so a\n * ladder name is a key here and never there. An unknown token resolves as an\n * unladdered unit rather than throwing.\n */\nexport function pinGroupKey(token: string): string {\n return LADDER_KINDS[token] === undefined\n ? formatGroupKey(token)\n : ladderGroupKey(token);\n}\n\n/** How many digits it takes for two distinct readings to READ as distinct. */\nconst MAX_SEPARATING_DECIMALS = 6;\n\n/** One reading of a group, as the rules below compare them. */\nexport interface FormatMember {\n /** The reading, in the unit it arrived in. */\n readonly reading: number;\n /** The unit it arrived in, which need not be the one it is written at. */\n readonly unit: string;\n}\n\n/**\n * Where a reading sits on its own ladder in the ladder's base unit, signed for\n * ordering, unlike {@link ladderPosition}.\n */\nfunction baseMeasure(reading: number, unit: string): number {\n return toBase(reading, ladderForUnit(unit), unit);\n}\n\n/**\n * The digit count at which the readings of a group stop printing the same\n * thing as each other, or undefined to leave the kind's own default alone.\n *\n * A band of 6 700 km to 6 710 km lands on the megametre rung, where the\n * default prints both ends as `6.7 Mm`; this widens the digits until they\n * differ.\n *\n * - Only adjacent readings (sorted by base measure) are compared, since\n * rounding never reorders\n * - Equal readings are left alone\n * - It only ever widens: a coarser count can round two ends apart across a\n * boundary the interval never reaches (`47.471` to `47.529` as `47 - 48`)\n */\nexport function separatingDecimals(\n members: readonly FormatMember[],\n opts: { format?: string; as?: string } = {},\n): number | undefined {\n if (members.length < 2) {\n return undefined;\n }\n const ordered = [...members].sort(\n (a, b) => baseMeasure(a.reading, a.unit) - baseMeasure(b.reading, b.unit),\n );\n const show = (member: FormatMember, decimals?: number) =>\n formatQuantity(\n member.reading,\n member.unit,\n decimals === undefined ? opts : { ...opts, decimals },\n );\n const separatesAt = (decimals?: number): boolean =>\n ordered.every((member, index) => {\n if (index === 0) return true;\n const previous = ordered[index - 1];\n if (\n baseMeasure(previous.reading, previous.unit) ===\n baseMeasure(member.reading, member.unit)\n ) {\n return true;\n }\n const a = show(previous, decimals);\n const b = show(member, decimals);\n return a.value !== b.value || a.rung !== b.rung;\n });\n if (separatesAt()) {\n return undefined;\n }\n\n /*\n * How many decimals the default prints, found by reproducing its string,\n * since counting digits cannot tell a decimal point from a grouping mark\n * under an arbitrary locale. Zero when nothing reproduces it.\n */\n const lowest = ordered[0];\n const printedByDefault = show(lowest).value;\n let from = 0;\n for (let decimals = 0; decimals <= MAX_SEPARATING_DECIMALS; decimals++) {\n if (show(lowest, decimals).value === printedByDefault) {\n from = decimals;\n break;\n }\n }\n for (let decimals = from; decimals <= MAX_SEPARATING_DECIMALS; decimals++) {\n if (separatesAt(decimals)) {\n return decimals;\n }\n }\n // Readings no precision here can separate get the kind's default, as genuinely equal readings do.\n return undefined;\n}\n\n/**\n * Whether a group reads as ONE figure: every member comes out as the same text\n * at the format the group has settled on.\n *\n * A band asks this before drawing two ends, so it never prints\n * `65.3 km - 65.3 km`. It compares the rendered text rather than magnitudes,\n * so there is no tolerance to tune. Ask with the SETTLED options, so the\n * figures compared are the ones that will be drawn.\n */\nexport function readsAsOneFigure(\n members: readonly (FormatMember | { magnitude: number; unit: string })[],\n opts: { format?: string; as?: string; decimals?: number } = {},\n): boolean {\n if (members.length < 2) {\n return false;\n }\n const shown = members.map((member) =>\n \"reading\" in member\n ? formatQuantity(member.reading, member.unit, opts)\n : formatWhole(member, opts),\n );\n const first = shown[0];\n return shown.every(\n (other) => other.value === first.value && other.rung === first.rung,\n );\n}\n\n// Last, so every table the registrations write to exists before the replay of the ones an Uplink made before this module loaded.\nonUnitRegistered(applyRegisteredUnit);\n","// This code was generated by scripts/gen-unit-kinds.mjs.\n// Changes to this file may cause incorrect behavior and will be lost if\n// the code is regenerated.\n//\n// A unit's KIND is a model fact, owned by mod/sitrep-sdk's UNIT_DEFINITIONS;\n// ui-kit reads it from here rather than keeping a copy of its own.\n//\n// Generated rather than imported so that packages/ui-kit keeps its zero\n// runtime dependencies. A token the contract declares but the model has no\n// unit for is a non-quantity, and its kind is the token itself.\n//\n// The ratio converts to the kind's BASE unit, which is what lets the format\n// prop re-express a value in any unit of the same kind without ui-kit\n// keeping conversion factors of its own.\n\n// The RUNTIME copy of what the SDK's UnitDeclarations states for first-party\n// units. The types read the declarations, never this table, so an Uplink's\n// declared unit is checked exactly as one of these is; unit-kinds.test-d.ts\n// holds the two to one another. `as const` keeps that comparison exact.\nexport const GENERATED_UNIT_KINDS = {\n \"%\": { kind: \"percent\", ratio: 0.01 },\n \"1\": { kind: \"dimensionless\", ratio: 1 },\n \"Eg\": { kind: \"mass\", ratio: 1000000000000000, ladder: \"mass\" },\n \"Gm\": { kind: \"length\", ratio: 1000000000, ladder: \"length\" },\n \"J\": { kind: \"energy\", ratio: 1 },\n \"J/s\": { kind: \"power\", ratio: 1, ladder: \"power\" },\n \"K\": { kind: \"temperature\", ratio: 1 },\n \"MN\": { kind: \"force\", ratio: 1000000, ladder: \"force\" },\n \"Mit\": { kind: \"scienceData\", ratio: 1 },\n \"Mm\": { kind: \"length\", ratio: 1000000, ladder: \"length\" },\n \"N\": { kind: \"force\", ratio: 1, ladder: \"force\" },\n \"N·m\": { kind: \"torque\", ratio: 1 },\n \"Pa\": { kind: \"pressure\", ratio: 1, ladder: \"pressure\" },\n \"Pg\": { kind: \"mass\", ratio: 1000000000000, ladder: \"mass\" },\n \"Tg\": { kind: \"mass\", ratio: 1000000000, ladder: \"mass\" },\n \"Tm\": { kind: \"length\", ratio: 1000000000000, ladder: \"length\" },\n \"W\": { kind: \"power\", ratio: 1, ladder: \"power\" },\n \"W/m²\": { kind: \"irradiance\", ratio: 1 },\n \"Yg\": { kind: \"mass\", ratio: 1e+21, ladder: \"mass\" },\n \"Zg\": { kind: \"mass\", ratio: 1000000000000000000, ladder: \"mass\" },\n \"bit\": { kind: \"data\", ratio: 1, ladder: \"data\" },\n \"bit/s\": { kind: \"dataRate\", ratio: 1, ladder: \"dataRate\" },\n \"count\": { kind: \"count\", ratio: 1 },\n \"d\": { kind: \"time\", ratio: 21600 },\n \"dB\": { kind: \"level\", ratio: 1 },\n \"enum\": { kind: \"enum\", ratio: 1 },\n \"f/day\": { kind: \"fundsRate\", ratio: 0.000046296296296296294 },\n \"flag\": { kind: \"flag\", ratio: 1 },\n \"funds\": { kind: \"funds\", ratio: 1 },\n \"g\": { kind: \"acceleration\", ratio: 9.80665 },\n \"g/m³\": { kind: \"density\", ratio: 0.001, ladder: \"density\" },\n \"h\": { kind: \"time\", ratio: 3600 },\n \"id\": { kind: \"id\", ratio: 1 },\n \"irl:d\": { kind: \"irlTime\", ratio: 86400 },\n \"irl:h\": { kind: \"irlTime\", ratio: 3600 },\n \"irl:min\": { kind: \"irlTime\", ratio: 60 },\n \"irl:s\": { kind: \"irlTime\", ratio: 1 },\n \"isp\": { kind: \"specificImpulse\", ratio: 1 },\n \"kN\": { kind: \"force\", ratio: 1000, ladder: \"force\" },\n \"kPa\": { kind: \"pressure\", ratio: 1000, ladder: \"pressure\" },\n \"kW\": { kind: \"power\", ratio: 1000, ladder: \"power\" },\n \"kg\": { kind: \"mass\", ratio: 1, ladder: \"mass\" },\n \"kg/m³\": { kind: \"density\", ratio: 1, ladder: \"density\" },\n \"kg/s\": { kind: \"massFlow\", ratio: 1 },\n \"km\": { kind: \"length\", ratio: 1000, ladder: \"length\" },\n \"km/h\": { kind: \"speed\", ratio: 0.2777777777777778 },\n \"km/s\": { kind: \"speed\", ratio: 1000 },\n \"kt\": { kind: \"mass\", ratio: 1000000, ladder: \"mass\" },\n \"m\": { kind: \"length\", ratio: 1, ladder: \"length\" },\n \"m/s\": { kind: \"speed\", ratio: 1 },\n \"m/s²\": { kind: \"acceleration\", ratio: 1 },\n \"mPa\": { kind: \"pressure\", ratio: 0.001, ladder: \"pressure\" },\n \"min\": { kind: \"time\", ratio: 60 },\n \"m²\": { kind: \"area\", ratio: 1 },\n \"m³\": { kind: \"volume\", ratio: 1 },\n \"m³/s²\": { kind: \"gravParameter\", ratio: 1 },\n \"n/a\": { kind: \"n/a\", ratio: 1 },\n \"rad\": { kind: \"planeAngle\", ratio: 1 },\n \"rad/h\": { kind: \"doseRate\", ratio: 0.0002777777777777778, ladder: \"doseRate\" },\n \"rad/s\": { kind: \"doseRate\", ratio: 1, ladder: \"doseRate\" },\n \"rad·s⁻¹\": { kind: \"angularSpeed\", ratio: 1 },\n \"ratio\": { kind: \"ratio\", ratio: 1 },\n \"rep\": { kind: \"reputation\", ratio: 1 },\n \"rep/day\": { kind: \"reputationRate\", ratio: 0.000046296296296296294 },\n \"rpm\": { kind: \"angularSpeed\", ratio: 0.10471975511965977 },\n \"s\": { kind: \"time\", ratio: 1 },\n \"science\": { kind: \"science\", ratio: 1 },\n \"science/day\": { kind: \"scienceRate\", ratio: 0.000046296296296296294 },\n \"t\": { kind: \"mass\", ratio: 1000, ladder: \"mass\" },\n \"text\": { kind: \"text\", ratio: 1 },\n \"units\": { kind: \"resourceUnits\", ratio: 1 },\n \"units/s\": { kind: \"resourceFlow\", ratio: 1 },\n \"ut\": { kind: \"universalTime\", ratio: 1 },\n \"y\": { kind: \"time\", ratio: 9201600 },\n \"°\": { kind: \"planeAngle\", ratio: 0.017453292519943295 },\n} as const;\n","import { kspCalendar } from \"./kspTime\";\nimport { NULL_DISPLAY } from \"./NullValue\";\n\nconst pad = (n: number) => String(n).padStart(2, \"0\");\n\n/**\n * Formats a KSP universal time (UT, seconds) as a date, in whichever of the\n * two calendars the running game actually has.\n *\n * - No epoch (stock): `Y<year> D<day> HH:MM:SS`, 1-based, so UT 0 is\n * `Y1 D1 00:00:00`, as KSP's own UI prints it\n * - An epoch: `14 Mar 1957 03:22:37`, the real instant that many seconds after\n * the anchor, which the mod reports only for a game with a real calendar.\n * The Gregorian calendar then governs, as the game's own formatter does\n *\n * The lengths and the anchor come from the calendar the game reported. A\n * negative UT is clamped to the epoch; a non-finite one, or one too large for\n * any real date, renders as `NULL_DISPLAY`.\n */\nexport function formatKspDate(ut: number): string {\n if (!Number.isFinite(ut)) return NULL_DISPLAY;\n\n // Read per call: the calendar arrives from the game after this module is imported.\n const {\n day: DAY,\n year: YEAR,\n hour: HOUR,\n minute: MINUTE,\n epochMs,\n } = kspCalendar();\n\n const clamped = Math.max(0, ut);\n\n if (epochMs !== undefined) {\n return formatRealDate(epochMs + clamped * 1000);\n }\n\n const year = Math.floor(clamped / YEAR) + 1;\n const yearRemainder = clamped % YEAR;\n\n const day = Math.floor(yearRemainder / DAY) + 1;\n const dayRemainder = yearRemainder % DAY;\n\n const hours = Math.floor(dayRemainder / HOUR);\n const minutes = Math.floor((dayRemainder % HOUR) / MINUTE);\n const seconds = Math.floor(dayRemainder % MINUTE);\n\n return `Y${year} D${day} ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;\n}\n\n/**\n * Month names in English rather than from `toLocaleDateString`, so dates do not\n * change shape by machine.\n */\nconst MONTHS = [\n \"Jan\",\n \"Feb\",\n \"Mar\",\n \"Apr\",\n \"May\",\n \"Jun\",\n \"Jul\",\n \"Aug\",\n \"Sep\",\n \"Oct\",\n \"Nov\",\n \"Dec\",\n];\n\n/**\n * A real instant as `14 Mar 1957 03:22:37`, read in UTC.\n *\n * UTC, not the viewer's zone, so two stations on one mission never read a day\n * apart.\n */\nfunction formatRealDate(ms: number): string {\n const at = new Date(ms);\n if (Number.isNaN(at.getTime())) return NULL_DISPLAY;\n\n const day = at.getUTCDate();\n const month = MONTHS[at.getUTCMonth()];\n const year = at.getUTCFullYear();\n const time = `${pad(at.getUTCHours())}:${pad(at.getUTCMinutes())}:${pad(\n at.getUTCSeconds(),\n )}`;\n\n return `${day} ${month} ${year} ${time}`;\n}\n","import { kspCalendar } from \"./kspTime\";\nimport { NULL_DISPLAY } from \"./NullValue\";\n\nconst SECOND = 1;\nconst MINUTE = 60 * SECOND;\nconst HOUR = 60 * MINUTE;\n\ninterface Tier {\n symbol: string;\n size: number;\n}\n\n/**\n * The game-time ladder, built per call rather than at module load, since the\n * calendar is whatever the running game reports and arrives after import.\n */\nfunction tiers(): readonly Tier[] {\n const calendar = kspCalendar();\n return [\n { symbol: \"y\", size: calendar.year },\n { symbol: \"d\", size: calendar.day },\n { symbol: \"h\", size: calendar.hour },\n // \"min\", not \"m\", which is the metre and reads as \"4M\" under an uppercasing badge.\n { symbol: \"min\", size: calendar.minute },\n { symbol: \"s\", size: SECOND },\n ];\n}\n\n/**\n * The game-time ladder's own symbols, kind `\"time\"`, for the symbol collision\n * guard to read rather than copy.\n */\nexport function durationTierSymbols(): readonly string[] {\n return tiers().map((tier) => tier.symbol);\n}\n\n/**\n * The same ladder on a real day, for durations measured by the clock on the\n * desk (how long ago a reading was seen, how long a recorder ran) rather than\n * by the game. Fixed, since wall-clock time is 24h regardless. No year rung.\n */\nconst IRL_TIERS: readonly Tier[] = [\n { symbol: \"d\", size: 24 * HOUR },\n { symbol: \"h\", size: HOUR },\n { symbol: \"min\", size: MINUTE },\n { symbol: \"s\", size: SECOND },\n];\n\n/** `IRL_TIERS`' own symbols, kind `\"irlTime\"`; see `durationTierSymbols`. */\nexport function irlDurationTierSymbols(): readonly string[] {\n return IRL_TIERS.map((tier) => tier.symbol);\n}\n\nexport interface FormatDurationOptions {\n /** Below 1s, render milliseconds (`820 ms`, `0 ms`) instead of `0s`. Default false. */\n ms?: boolean;\n /**\n * Prefix a launch-clock sign: `T+` for a negative (already-elapsed/past)\n * value, `T−` for a positive-or-zero (future) value. Off by default.\n */\n sign?: boolean;\n}\n\n/**\n * Formats a duration in seconds as the largest two significant KSP-time\n * units, space-separated and suffixed (`45s`, `1min 20s`, `2h 15min`, `3d 4h`,\n * `1y 200d`). The smaller unit is only shown when non-zero at that scale\n * (exactly 2h renders as `2h`, not `2h 0min`).\n *\n * The smaller unit is truncated, not rounded, so a countdown never shows\n * progress that has not happened: `89.9` -> `1min 29s`. Non-finite values\n * render as `NULL_DISPLAY`.\n */\nexport function formatDuration(\n seconds: number,\n opts: FormatDurationOptions = {},\n): string {\n return format(seconds, tiers(), opts);\n}\n\n/**\n * The wall-clock twin of {@link formatDuration}: same shape, real days.\n *\n * For seconds measured by a clock on the desk rather than by the game (the\n * `irlTime` kind). Collapsing the two is a silent factor-of-four error.\n */\nexport function formatIrlDuration(\n seconds: number,\n opts: FormatDurationOptions = {},\n): string {\n return format(seconds, IRL_TIERS, opts);\n}\n\nfunction format(\n seconds: number,\n tiers: readonly Tier[],\n opts: FormatDurationOptions,\n): string {\n if (!Number.isFinite(seconds)) return NULL_DISPLAY;\n\n const { ms = false, sign = false } = opts;\n const signPrefix = sign ? (seconds < 0 ? \"T+\" : \"T−\") : \"\";\n const abs = Math.abs(seconds);\n\n if (abs < 1) {\n if (ms) {\n return `${signPrefix}${Math.round(abs * 1000)} ms`;\n }\n return `${signPrefix}0s`;\n }\n\n // Never a unit finer than seconds outside the `ms` path.\n const totalSeconds = Math.floor(abs);\n\n // Below the finest tier, the smallest rung is still the answer.\n const found = tiers.findIndex((tier) => totalSeconds >= tier.size);\n const majorIndex = found === -1 ? tiers.length - 1 : found;\n const major = tiers[majorIndex];\n const majorValue = Math.floor(totalSeconds / major.size);\n\n if (majorIndex === tiers.length - 1) {\n // Already at the finest tier (seconds): nothing smaller to pair with.\n return `${signPrefix}${majorValue}${major.symbol}`;\n }\n\n const minor = tiers[majorIndex + 1];\n const remainder = totalSeconds - majorValue * major.size;\n const minorValue = Math.floor(remainder / minor.size);\n\n if (minorValue === 0) {\n return `${signPrefix}${majorValue}${major.symbol}`;\n }\n return `${signPrefix}${majorValue}${major.symbol} ${minorValue}${minor.symbol}`;\n}\n\n/**\n * Countdown convenience for an in-transit / time-remaining strip: never\n * negative, never sub-second noise, no sign prefix (a countdown is always\n * \"time remaining\", not a launch-clock T+/T− reading).\n */\nexport function formatCountdown(seconds: number): string {\n return formatDuration(Math.max(0, seconds));\n}\n","import {\n type CapabilityLock,\n LockScopeContext,\n lockSentence,\n useLockScope,\n} from \"@ksp-gonogo/sitrep-sdk/spine\";\nimport type { ReactElement, ReactNode } from \"react\";\nimport { InactiveNotice } from \"./InactiveNotice\";\nimport { writeLockQuantity } from \"./lockQuantity\";\n\n/**\n * What a locked {@link LockScope} hands its `fallback`: the sentence every lock\n * notice reads, and the locks behind it.\n *\n * @category Panel\n */\nexport interface LockSummary {\n /** \"Missing tech: Flight Control\", or a building's name such as \"Mission Control\". */\n reason: string;\n /** \"45 science to research\", or \"Needs Building level 2\". */\n hint?: string;\n locks: readonly CapabilityLock[];\n}\n\n/**\n * Props for {@link LockScope}.\n *\n * @category Panel\n */\nexport interface LockScopeProps {\n children?: ReactNode;\n /**\n * What draws in place of `children` while something used inside the scope\n * is not unlocked in this save. Given a function, it is called with the\n * {@link LockSummary}. Defaults to a centred notice of the reason; `null`\n * draws nothing, for a scope that is an overlay or a badge.\n */\n fallback?: ReactNode | ((lock: LockSummary) => ReactNode);\n}\n\n/**\n * Marks a part of the tree that stands or falls together. When anything\n * inside it reads a Topic (with `useTelemetry` or `useStream`) or uses a\n * command (with `useCommand`) that this save has not unlocked, the whole scope\n * is replaced by its `fallback`. The innermost scope wins, so a scope inside a\n * section is replaced while the section stays.\n *\n * It draws no element of its own. A {@link Section}, each augment an\n * {@link AugmentSlot} renders, and every widget as a whole are lock scopes\n * already; wrap anything else that should be replaced on its own.\n *\n * @category Panel\n */\nexport function LockScope({\n children,\n fallback,\n}: LockScopeProps): ReactElement {\n const { scope, locks } = useLockScope();\n if (locks.length > 0) {\n const summary: LockSummary = {\n ...lockSentence(locks, writeLockQuantity),\n locks,\n };\n const drawn =\n fallback === undefined ? (\n <InactiveNotice reason={summary} />\n ) : typeof fallback === \"function\" ? (\n fallback(summary)\n ) : (\n fallback\n );\n return <>{drawn}</>;\n }\n return (\n <LockScopeContext.Provider value={scope}>\n {children}\n </LockScopeContext.Provider>\n );\n}\n","import styled from \"styled-components\";\n\n/**\n * What an inactive panel or section takes: the reason as one line, or the\n * reason plus a hint at what would bring it back.\n *\n * @category Panel\n */\nexport type PanelInactiveReason = string | { reason: string; hint?: string };\n\n/**\n * The one body an inactive panel or section draws in place of its content:\n * the reason, centred, announced politely.\n *\n * @category Panel\n */\nexport function InactiveNotice({ reason }: { reason: PanelInactiveReason }) {\n const { reason: message, hint } =\n typeof reason === \"string\" ? { reason, hint: undefined } : reason;\n return (\n <InactiveNotice__Body role=\"status\" aria-live=\"polite\">\n <InactiveNotice__Reason>{message}</InactiveNotice__Reason>\n {hint !== undefined && (\n <InactiveNotice__Hint>{hint}</InactiveNotice__Hint>\n )}\n </InactiveNotice__Body>\n );\n}\n\nconst InactiveNotice__Body = styled.div`\n flex: 0 1 auto;\n min-height: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: var(--gap-caption);\n text-align: center;\n padding: var(--inset-refusal);\n`;\n\nconst InactiveNotice__Reason = styled.div`\n color: var(--color-text-muted);\n font-size: var(--font-size-caption);\n font-weight: 600;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n`;\n\nconst InactiveNotice__Hint = styled.div`\n color: var(--color-text-faint);\n font-size: var(--font-size-caption);\n letter-spacing: 0.04em;\n`;\n","import { writeQuantity } from \"./units\";\n\n/** A quantity in a lock sentence, written the way the kit writes every other one. */\nexport function writeLockQuantity(\n quantity: { magnitude: number; unit: string } | number,\n unit: string,\n): string {\n return writeQuantity(\n typeof quantity === \"number\" ? { magnitude: quantity, unit } : quantity,\n );\n}\n","import type { HTMLAttributes, ReactNode } from \"react\";\nimport styled from \"styled-components\";\nimport { InactiveNotice } from \"./InactiveNotice\";\nimport { LockScope } from \"./LockScope\";\nimport { Stack } from \"./Stack\";\nimport type { GapToken } from \"./scales\";\nimport type { StaticElement } from \"./staticElement\";\n\n/** Marks a section that spans every column of a panel's section grid; the rule acting on it lives on the grid (see `Panel`). */\nexport const SECTION_FULL_ATTR = \"data-section-full\";\n\n/** Marks a section that takes the panel body's leftover height; the rule acting on it lives on the body (see `Panel`). */\nexport const SECTION_FILL_ATTR = \"data-section-fill\";\n\n/**\n * Props for {@link Section}. Any other `div` attribute passes through, except\n * the HTML `title`, whose name is taken by the heading.\n *\n * @category Layout\n */\nexport interface SectionProps\n extends Omit<HTMLAttributes<HTMLDivElement>, \"title\"> {\n /** Rendered tag. Defaults to `div`. */\n as?: StaticElement;\n /** Gap between the section's children. Defaults to `rows`; a section of groups wants a wider one. */\n gap?: GapToken;\n /** The section's heading, rendered as a `SectionTitle` above its children. */\n title?: ReactNode;\n /** Tag for `title`. Defaults to `h4`, the level under a panel's own `h3`. */\n titleAs?: StaticElement;\n /**\n * Span every column of the panel's section grid rather than taking one, for a\n * summary strip or a table whose columns are already its own. Inert outside a\n * grid parent.\n */\n full?: boolean;\n /**\n * Take the panel body's leftover height rather than the section's natural\n * one, for a section that is a drawing (a map, a plot, a dial).\n *\n * A filling section is lifted out of the section grid, so it always spans the\n * panel's full width; ordinary sections around it still columnise. Two filling\n * sections each keep their content height and share the leftover equally.\n * Ignored under `fitToSize`, and inert outside a panel body.\n */\n fill?: boolean;\n children?: ReactNode;\n}\n\n/**\n * A named group of rows inside a panel: a {@link Stack} at the `rows` gap,\n * headed by a {@link SectionTitle} when `title` is set. Inside a panel body,\n * `full` spans every column of the section grid and `fill` takes the body's\n * leftover height.\n *\n * A section is also a {@link LockScope}: when anything inside it reads a Topic\n * or uses a command this save has not unlocked, the section keeps its title\n * and shows what is missing in place of its content.\n *\n * @category Layout\n */\nexport function Section({\n children,\n gap = \"rows\",\n title,\n titleAs = \"h4\",\n full = false,\n fill = false,\n ...rest\n}: SectionProps) {\n const heading =\n title == null ? null : <SectionTitle as={titleAs}>{title}</SectionTitle>;\n const stackProps = {\n gap,\n [SECTION_FULL_ATTR]: full ? \"\" : undefined,\n [SECTION_FILL_ATTR]: fill ? \"\" : undefined,\n ...rest,\n };\n /* The scope wraps the Stack rather than sitting inside it, so the caller's\n children reach the Stack unwrapped: an element in between changes which DOM\n nodes React reuses as conditional children come and go. Not `Stack`'s own\n `fill` either: that is a zero basis, splitting the body evenly however\n little is in either section, where the body's rule divides only what is\n spare. */\n return (\n <LockScope\n fallback={(lock) => (\n <Stack {...stackProps}>\n {heading}\n <InactiveNotice reason={lock} />\n </Stack>\n )}\n >\n <Stack {...stackProps}>\n {heading}\n {children}\n </Stack>\n </LockScope>\n );\n}\n\n/**\n * The uppercase, tracked-out heading {@link Section} draws for its `title`,\n * usable on its own. Use `as=\"h3\"` (or another heading tag) for a real heading;\n * `$rule` draws a hairline under it.\n *\n * @category Layout\n */\nexport const SectionTitle = styled.div<{ $rule?: boolean }>`\n margin: 0;\n font-size: var(--font-size-value);\n font-weight: 700;\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--color-text-muted);\n ${({ $rule }) =>\n $rule\n ? `\n padding-bottom: var(--gap-title-rule);\n border-bottom: 1px solid var(--color-border-subtle);\n`\n : \"\"}\n`;\n","import { createContext, useContext } from \"react\";\nimport type { ContributionSlotId } from \"./contributions\";\n\n/**\n * The identity and declared contribution slots of the widget the caller is\n * rendered inside. {@link ContributionsProvider} reads it to know which slots\n * to serve, and segment slots complete to `${componentId}.<segment>` from it.\n *\n * @category Extensions\n */\nexport interface WidgetMetaContextValue {\n /** The widget's registered component id. */\n componentId: string;\n /** The contribution slots the widget declares in its `contributionSlots`, or an empty list. */\n contributionSlots: readonly ContributionSlotId[];\n}\n\n/**\n * The context carrying {@link WidgetMetaContextValue} for the current widget,\n * `null` outside one. The dashboard provides it for every widget; a test\n * rendering a widget bare provides it to get the widget's slots working.\n *\n * @category Extensions\n */\nexport const WidgetMetaContext = createContext<WidgetMetaContextValue | null>(\n null,\n);\n\n/**\n * The current widget's identity and declared contribution slots, or `null`\n * outside a {@link WidgetMetaContext} provider (a widget rendered bare, a panel\n * outside the dashboard).\n *\n * @category Extensions\n */\nexport function useWidgetMeta(): WidgetMetaContextValue | null {\n return useContext(WidgetMetaContext);\n}\n","import { type ReactElement, useSyncExternalStore } from \"react\";\nimport {\n type AnyAugment,\n type AugmentSegmentProps,\n getAugmentsForSlot,\n onAugmentsChange,\n type SlotProps,\n} from \"./augments\";\nimport {\n useDomainAvailabilityStore,\n useDomainAvailable,\n} from \"./domainAvailability\";\nimport { InactiveNotice } from \"./InactiveNotice\";\nimport { LockScope } from \"./LockScope\";\nimport { Section } from \"./Section\";\nimport { useWidgetMeta } from \"./WidgetMetaContext\";\n\n/**\n * Renders every augment bound to a slot, in priority order, as siblings in a\n * fragment. A widget places an `<AugmentSlot>` where Uplinks may add content\n * and never names the Uplinks that fill it. Augments registered after the slot\n * mounts appear when they register.\n *\n * `props` is required and passed to every augment, typed against the slot's\n * {@link SlotProps} entry: an overlay slot passes its parent's projection, for\n * example. Pass `{}` for a slot with no props.\n *\n * An augment declaring `requires: \"<domain>\"` renders only while the host\n * reports that Domain present; a held `<domain>.available` counts as present.\n *\n * Two forms, of which a call uses one:\n * - `name`: the full slot id (`\"power-systems.sections\"`), props typed\n * through {@link SlotRegistry}\n * - `segment`: only the segment (`\"overlay\"`), completed to\n * `${componentId}.<segment>` for the widget it renders in, props typed\n * through {@link AugmentSegmentProps}. Outside a widget it renders nothing\n *\n * @example\n * ```tsx\n * <AugmentSlot name=\"crew-status.row-badges\" props={{ crewName: kerbal.name, crewIndex: kerbal.index }} />\n * ```\n *\n * @category Extensions\n */\nexport function AugmentSlot<Slot extends string>(\n args:\n | { name: Slot; props: SlotProps<Slot>; segment?: never }\n | { segment: Slot; props: AugmentSegmentProps<Slot>; name?: never },\n): ReactElement {\n // Read unconditionally for a stable hook order; only the `segment` form uses it.\n const meta = useWidgetMeta();\n const slotName =\n args.name ?? (meta ? `${meta.componentId}.${args.segment}` : undefined);\n\n const augments = useAugmentsFor(slotName);\n\n return (\n <>\n {augments.map((augment) => (\n <AugmentEntry\n key={augment.id}\n augment={augment}\n slotProps={args.props as Record<string, unknown>}\n asSection={slotName?.endsWith(\".sections\") === true}\n />\n ))}\n </>\n );\n}\n\nconst EMPTY_AUGMENTS: AnyAugment[] = [];\n\n/** Re-read on every registry change, so a slot mounted before an augment's module loads still picks it up. */\nfunction useAugmentsFor(slotName: string | undefined): AnyAugment[] {\n return useSyncExternalStore(\n onAugmentsChange,\n () => (slotName ? getAugmentsForSlotCached(slotName) : EMPTY_AUGMENTS),\n () => (slotName ? getAugmentsForSlotCached(slotName) : EMPTY_AUGMENTS),\n );\n}\n\n/**\n * Whether anything would render in the current widget's\n * `${componentId}.<segment>` slot, for a widget deciding whether to draw\n * chrome around it. Counts only augments whose `requires` Domain is present,\n * so an Uplink whose mod is not running changes no layout. False outside a\n * widget.\n *\n * @category Extensions\n */\nexport function useWidgetSegmentBound(segment: string): boolean {\n const meta = useWidgetMeta();\n const slotName = meta ? `${meta.componentId}.${segment}` : undefined;\n return useAnyAvailable(useAugmentsFor(slotName));\n}\n\n/**\n * Whether anything would render in the slot with the full id `name`, for a\n * widget affordance (a tab, a reserved cell) that should exist only when\n * something can fill it. Counts only augments whose `requires` Domain is\n * present, so an Uplink whose mod is not running changes no layout. Unlike\n * {@link useWidgetSegmentBound} it works outside a widget.\n *\n * @category Extensions\n */\nexport function useSlotBound(name: string): boolean {\n return useAnyAvailable(useAugmentsFor(name));\n}\n\nconst NO_SUBSCRIBE = (): (() => void) => () => {};\n\n/** Whether any of `augments` passes the same presence gate {@link AugmentEntry} applies, re-read as Domains announce. */\nfunction useAnyAvailable(augments: AnyAugment[]): boolean {\n const store = useDomainAvailabilityStore();\n const snapshot = (): boolean =>\n augments.some(\n (augment) =>\n !augment.requires || store?.isAvailable(augment.requires) === true,\n );\n return useSyncExternalStore(\n store ? store.subscribe : NO_SUBSCRIBE,\n snapshot,\n snapshot,\n );\n}\n\n/**\n * The `label` of the first available augment bound to the slot `name`, in\n * render order, for a widget that needs a caption before it draws the slot,\n * such as a tab's label. `undefined` while nothing is bound, nothing bound has\n * its `requires` Domain present, or the first available augment declares no\n * label. With several augments bound, only the first one's label is used,\n * while {@link AugmentSlot} renders them all together.\n *\n * @category Extensions\n */\nexport function useSlotLabel(name: string): string | undefined {\n const augments = useAugmentsFor(name);\n const store = useDomainAvailabilityStore();\n const snapshot = (): string | undefined =>\n augments.find(\n (augment) =>\n !augment.requires || store?.isAvailable(augment.requires) === true,\n )?.label;\n return useSyncExternalStore(\n store ? store.subscribe : NO_SUBSCRIBE,\n snapshot,\n snapshot,\n );\n}\n\n// useSyncExternalStore loops without a referentially stable snapshot, so each slot's list is memoised until the registry notifies.\nconst slotCache = new Map<string, AnyAugment[]>();\nlet cacheValid = false;\nonAugmentsChange(() => {\n cacheValid = false;\n slotCache.clear();\n});\nfunction getAugmentsForSlotCached(name: string): AnyAugment[] {\n if (!cacheValid) {\n slotCache.clear();\n cacheValid = true;\n }\n let cached = slotCache.get(name);\n if (cached === undefined) {\n cached = getAugmentsForSlot(name);\n slotCache.set(name, cached);\n }\n return cached;\n}\n\n/**\n * True when `augment` declares no `requires`, or the host reports its Domain\n * present: the same check {@link AugmentSlot} makes before rendering it. Use\n * it to ask that question without rendering the augment, since an Uplink\n * client registers its augments whether or not its mod is running.\n *\n * @category Extensions\n */\nexport function useAugmentAvailable(augment: AnyAugment): boolean {\n // Called unconditionally for a stable hook order.\n const available = useDomainAvailable(augment.requires);\n return !augment.requires || available;\n}\n\n/**\n * One augment behind its gate, isolated so the gate hook's position is stable\n * as the registered set changes.\n *\n * Each augment is its own lock scope. An Uplink's section reads and commands\n * through hooks in its own body, which would otherwise claim against the host\n * widget's scope and take the whole host down for a capability only the\n * augment uses. A locked augment in a sections slot draws the missing unlock\n * under its label; anywhere else (an overlay, a badge) it draws nothing.\n */\nfunction AugmentEntry({\n augment,\n slotProps,\n asSection,\n}: {\n augment: AnyAugment;\n slotProps: Record<string, unknown>;\n asSection: boolean;\n}): ReactElement | null {\n if (!useAugmentAvailable(augment)) {\n return null;\n }\n\n const Component = augment.component;\n return (\n <LockScope\n fallback={\n asSection\n ? (lock) => (\n <Section title={augment.label}>\n <InactiveNotice reason={lock} />\n </Section>\n )\n : null\n }\n >\n <Component {...slotProps} />\n </LockScope>\n );\n}\n","import type { StreamStatusValue, Tone } from \"@ksp-gonogo/sitrep-sdk\";\n\n/**\n * The tones that take part in a panel's summary: every `Tone` but `neutral`,\n * which carries no state. From best to worst: `go`, `info`, `caution`, `warn`,\n * `nogo`, `offline`.\n *\n * @category Panel\n */\nexport type Severity = Exclude<Tone, \"neutral\">;\n\n/**\n * Total order for the max-merge, best to worst.\n *\n * `info` sits above `go`, so an info contributor lights an otherwise quiet\n * panel. `offline` sits at the top: a nogo alarm cannot be trusted once the\n * data feeding it is gone.\n */\nconst RANK: Record<Severity, number> = {\n go: 0,\n info: 1,\n caution: 2,\n warn: 3,\n nogo: 4,\n offline: 5,\n};\n\nexport function severityRank(s: Severity): number {\n return RANK[s];\n}\n\n/**\n * The worst severity among `severities`, in the order `go`, `info`, `caution`,\n * `warn`, `nogo`, `offline` (worst). `go` for an empty list.\n *\n * @category Panel\n */\nexport function worstSeverity(severities: readonly Severity[]): Severity {\n let worst: Severity = \"go\";\n for (const s of severities) {\n if (RANK[s] > RANK[worst]) worst = s;\n }\n return worst;\n}\n\n/**\n * The {@link Severity} a stream status reads as: `live` is `go`, `resyncing`\n * is `caution`, `recorded` is `info` (the reading is exact, for an earlier\n * instant), `held`, `last-before-blackout`, `loading` and `no-game` are `warn`, and `disconnected`\n * and `absent` are `offline`.\n *\n * @category Panel\n */\nexport function severityFromStreamStatus(status: StreamStatusValue): Severity {\n switch (status) {\n case \"live\":\n return \"go\";\n case \"resyncing\":\n return \"caution\";\n case \"recorded\":\n return \"info\";\n case \"held\":\n case \"last-before-blackout\":\n case \"loading\":\n case \"no-game\":\n return \"warn\";\n case \"disconnected\":\n case \"absent\":\n return \"offline\";\n }\n}\n","/**\n * A generic, off-tree external store: an id-keyed set of entries with\n * `subscribe`/`getSnapshot`/`register`/`update`, a referentially stable\n * snapshot while unchanged, and insertion order.\n *\n * The live data lives in the store, never in a React context value, so a\n * change re-renders only the snapshot's subscribers.\n */\nexport interface Store<Item extends { id: string }> {\n /** Add or replace an entry (keyed on `entry.id`). Returns its deregister function. */\n register(entry: Item): () => void;\n /**\n * Change an already-registered entry's non-id fields in place. A no-op when\n * the id is unknown, and a no-op (no notify, snapshot identity preserved)\n * when every field is shallow-equal to the current entry, so a value that\n * did not actually move does not hand `useSyncExternalStore` a fresh\n * snapshot.\n */\n update(id: string, next: Omit<Item, \"id\">): void;\n /** Subscribe to any change to the entry set. Returns unsubscribe. */\n subscribe(onChange: () => void): () => void;\n /** The entries, insertion-ordered, referentially stable while unchanged. */\n getSnapshot(): readonly Item[];\n}\n\n// Shared frozen empty snapshot so an empty store returns one stable identity (a fresh `[]` per call would loop `useSyncExternalStore`).\nconst EMPTY: readonly never[] = Object.freeze([]);\n\n/** Shallow-equal over `next`'s own keys against the current entry (id aside). */\nfunction unchanged<Item extends { id: string }>(\n current: Item,\n next: Omit<Item, \"id\">,\n): boolean {\n for (const key in next) {\n if (\n (current as Record<string, unknown>)[key] !==\n (next as Record<string, unknown>)[key]\n ) {\n return false;\n }\n }\n return true;\n}\n\nexport function createStore<Item extends { id: string }>(): Store<Item> {\n // Insertion order keeps a derived view's top-rank tie-break deterministic.\n const entries = new Map<string, Item>();\n const listeners = new Set<() => void>();\n\n let cached: readonly Item[] = EMPTY;\n let dirty = true;\n\n function emit() {\n dirty = true;\n for (const listener of listeners) listener();\n }\n\n // Which registration currently owns each id, so a registrant that has been replaced cannot deregister its replacement.\n const owners = new Map<string, object>();\n\n return {\n register(entry) {\n const owner = {};\n entries.set(entry.id, entry);\n owners.set(entry.id, owner);\n emit();\n return () => {\n if (owners.get(entry.id) !== owner) return;\n owners.delete(entry.id);\n if (entries.delete(entry.id)) emit();\n };\n },\n update(id, next) {\n const current = entries.get(id);\n if (!current || unchanged(current, next)) return;\n entries.set(id, { id, ...next } as Item);\n emit();\n },\n subscribe(onChange) {\n listeners.add(onChange);\n return () => {\n listeners.delete(onChange);\n };\n },\n getSnapshot() {\n if (dirty) {\n cached = entries.size === 0 ? EMPTY : Array.from(entries.values());\n dirty = false;\n }\n return cached;\n },\n };\n}\n","import { createPanelStore } from \"../store/createPanelStore\";\nimport { createStore } from \"../store/createStore\";\nimport { type Severity, severityRank, worstSeverity } from \"./severity\";\n\n/**\n * One statement about a panel's state, as published to its\n * {@link PanelStatusStore}. A {@link Badge} with `report`, the panel's stream\n * status and the dashboard's alarms all publish this shape, and the panel\n * summarises them together.\n *\n * @category Panel\n */\nexport interface StatusContribution {\n /** Stable for the contributor's lifetime; a second contribution with the same id replaces the first. */\n id: string;\n /** How bad the state is. The worst across the panel wins its summary. */\n severity: Severity;\n /** Shown when this contribution wins the summary. */\n label: string;\n}\n\n/**\n * The contribution that wins a panel's summary: the worst severity, and the\n * earliest registered among equals. `id` is the winner's own contribution id,\n * so a caller already drawing that contribution (as a badge pill, say) can\n * skip drawing it twice.\n *\n * @category Panel\n */\nexport interface StatusSummary {\n id: string;\n severity: Severity;\n label: string;\n}\n\n/**\n * One row of a panel's per-severity breakdown: how many contributions sit at\n * one severity. Two cautions are one `{ severity: \"caution\", count: 2 }` row;\n * severities are never folded into a worse one.\n *\n * @category Panel\n */\nexport interface StatusBreakdownEntry {\n severity: Severity;\n count: number;\n}\n\n/**\n * The store of {@link StatusContribution}s for one dashboard tile, from which\n * the panel draws its summary badge and the collapsed header's status dots.\n * Components publish with {@link useStatusContribution} and read with\n * {@link useStatusSummary} and {@link useStatusBreakdown}; a change re-renders\n * only those subscribers.\n *\n * @category Panel\n */\nexport interface PanelStatusStore {\n /** Adds a contribution. Returns a function that removes it. */\n register(c: StatusContribution): () => void;\n /** Changes a registered contribution's severity and label in place. */\n update(id: string, next: Omit<StatusContribution, \"id\">): void;\n /** Calls `onChange` on any change to the contributions. Returns a function that unsubscribes. */\n subscribe(onChange: () => void): () => void;\n /** The winning contribution (see {@link StatusSummary}), or `null` when there are none. The same object is returned while the result is unchanged. */\n getSummary(): StatusSummary | null;\n /**\n * Per-severity counts, worst first, one row per severity present. The same\n * array is returned while the counts are unchanged.\n */\n getBreakdown(): readonly StatusBreakdownEntry[];\n}\n\n/** The worst contribution's OWN label, tie-broken by insertion order (the\n * earliest contribution at the worst rank wins, so the winning label cannot\n * flicker between two equal-severity contributors frame to frame). */\nfunction summarise(\n contributions: readonly StatusContribution[],\n): StatusSummary | null {\n if (contributions.length === 0) return null;\n const worst = worstSeverity(contributions.map((c) => c.severity));\n for (const c of contributions) {\n if (c.severity === worst) {\n return { id: c.id, severity: c.severity, label: c.label };\n }\n }\n return null; // unreachable: worst is drawn from the set\n}\n\n// Shared frozen empty so an empty store returns one stable identity (a fresh `[]` per call would loop useSyncExternalStore).\nconst EMPTY_BREAKDOWN: readonly StatusBreakdownEntry[] = Object.freeze([]);\n\n/** Per-severity counts, worst-first. Each distinct severity is its own row with\n * its own count; nothing is folded across tiers. */\nfunction breakdownOf(\n contributions: readonly StatusContribution[],\n): readonly StatusBreakdownEntry[] {\n if (contributions.length === 0) return EMPTY_BREAKDOWN;\n const counts = new Map<Severity, number>();\n for (const c of contributions) {\n counts.set(c.severity, (counts.get(c.severity) ?? 0) + 1);\n }\n return Array.from(counts, ([severity, count]) => ({ severity, count })).sort(\n (a, b) => severityRank(b.severity) - severityRank(a.severity),\n );\n}\n\n/** Structural equality over the worst-first entries (same length, same\n * severity + count at each index), so a change that leaves the breakdown\n * identical (a label-only update) preserves the array's identity. */\nfunction breakdownEqual(\n a: readonly StatusBreakdownEntry[],\n b: readonly StatusBreakdownEntry[],\n): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].severity !== b[i].severity || a[i].count !== b[i].count) {\n return false;\n }\n }\n return true;\n}\n\nexport function createPanelStatusStore(): PanelStatusStore {\n const base = createStore<StatusContribution>();\n\n /*\n * Derived views are cached on the base snapshot's identity, and an equal\n * recomputed result keeps the previous object, so a losing contributor\n * moving never hands useSyncExternalStore a fresh summary.\n */\n let cachedSummary: StatusSummary | null = null;\n let cachedFrom: readonly StatusContribution[] | null = null;\n let cachedBreakdown: readonly StatusBreakdownEntry[] = EMPTY_BREAKDOWN;\n let cachedBreakdownFrom: readonly StatusContribution[] | null = null;\n\n return {\n register: base.register,\n update: base.update,\n subscribe: base.subscribe,\n getSummary() {\n const snapshot = base.getSnapshot();\n if (snapshot === cachedFrom) return cachedSummary;\n const next = summarise(snapshot);\n cachedFrom = snapshot;\n if (\n cachedSummary !== null &&\n next !== null &&\n cachedSummary.id === next.id &&\n cachedSummary.severity === next.severity &&\n cachedSummary.label === next.label\n ) {\n return cachedSummary; // identity preserved: same merged result\n }\n cachedSummary = next;\n return cachedSummary;\n },\n getBreakdown() {\n const snapshot = base.getSnapshot();\n if (snapshot === cachedBreakdownFrom) return cachedBreakdown;\n const next = breakdownOf(snapshot);\n cachedBreakdownFrom = snapshot;\n if (breakdownEqual(cachedBreakdown, next)) return cachedBreakdown;\n cachedBreakdown = next;\n return cachedBreakdown;\n },\n };\n}\n\n/** Shared, stable empty breakdown for the no-store hook fallback (same identity\n * an empty store returns), so `useSyncExternalStore` has a referentially stable\n * snapshot with no provider in the tree. */\nexport const NO_STATUS_BREAKDOWN = EMPTY_BREAKDOWN;\n\nconst StatusPanelStore = createPanelStore(createPanelStatusStore);\n\n/**\n * Creates one {@link PanelStatusStore} and provides it for as long as it is\n * mounted, so the widget body and the tile's chrome share it. The dashboard\n * mounts one per tile.\n *\n * @category Panel\n */\nexport const PanelStatusStoreProvider = StatusPanelStore.Provider;\n\n/**\n * The nearest {@link PanelStatusStore}, or `null` outside a dashboard tile.\n *\n * @category Panel\n */\nexport const usePanelStatusStore = StatusPanelStore.useStore;\n","import { useEffect } from \"react\";\nimport {\n type StatusContribution,\n usePanelStatusStore,\n} from \"./PanelStatusStore\";\n\n/**\n * Publishes a contribution into the nearest {@link PanelStatusStore} for as\n * long as the calling component is mounted, so it counts toward the panel's\n * summary badge and status dots.\n *\n * - does nothing when there is no store above (a widget outside the dashboard)\n * - keyed on `c.id`: a changed severity or label updates the contribution in\n * place\n * - `null` contributes nothing, so the contribution can be passed\n * conditionally\n *\n * @example\n * ```tsx\n * function ThermalWatch({ overheating }: { overheating: boolean }) {\n * useStatusContribution(\n * overheating ? { id: \"thermal\", severity: \"warn\", label: \"Overheating\" } : null,\n * );\n * return null;\n * }\n * ```\n *\n * @category Panel\n */\nexport function useStatusContribution(c: StatusContribution | null): void {\n const store = usePanelStatusStore();\n const id = c?.id;\n const severity = c?.severity;\n const label = c?.label;\n\n // Keyed on identity only: the update effect below applies value changes in place.\n // biome-ignore lint/correctness/useExhaustiveDependencies: value changes are applied by the update effect, not by re-registering\n useEffect(() => {\n if (!store || id === undefined || severity === undefined) return;\n const deregister = store.register({ id, severity, label: label ?? \"\" });\n return deregister;\n }, [store, id]);\n\n useEffect(() => {\n if (!store || id === undefined || severity === undefined) return;\n store.update(id, { severity, label: label ?? \"\" });\n }, [store, id, severity, label]);\n}\n","import type { Tone } from \"@ksp-gonogo/sitrep-sdk\";\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { fitBox } from \"./fitBox\";\nimport type { Severity } from \"./status/severity\";\nimport { useStatusContribution } from \"./status/useStatusContribution\";\nimport { TONE_MARK, TONE_TEXT } from \"./tone\";\n\n/**\n * Badge text size: `sm` for dense rows and table cells, `md` (the default) elsewhere.\n *\n * @category Badge\n */\nexport type BadgeSize = \"sm\" | \"md\";\n\n/**\n * Props for {@link Badge}. Any other `span` attribute (`title`, `aria-label`) passes through to the pill.\n *\n * @category Badge\n */\nexport interface BadgeProps extends HTMLAttributes<HTMLSpanElement> {\n /**\n * The state the badge shows. Sets its colour and, with `report`, where it\n * ranks in the panel's summary. Omit it, or pass `neutral`, for a purely\n * decorative badge (a kind tag, a count), which renders a grey chip and never\n * moves a panel summary.\n */\n tone?: Tone;\n size?: BadgeSize;\n /**\n * Announce this badge as a screen-reader live region (`role=\"status\"`). Use\n * for state that changes and the operator benefits from being told (a stream\n * going held, an alarm firing). Decorative badges leave it off so they do not\n * flood the accessibility tree.\n */\n live?: boolean;\n /**\n * When set, this badge registers itself into the nearest\n * {@link PanelStatusStore} as a contribution `{ id, severity, label }`, so the panel\n * can summarise it. `id` must be stable for the badge's lifetime. `label`\n * defaults to the badge's text content when `children` is a plain string; pass\n * it explicitly otherwise. A `go` badge with `report` still registers, but\n * any worse state in the panel wins the summary over it.\n */\n report?: { id: string; label?: string };\n children: ReactNode;\n}\n\n/**\n * A compact uppercase pill that shows a label in one `Tone`. A stateful tone\n * (`go` through `nogo`, and `offline`) draws an outlined pill in that tone's\n * colour, with a glow that grows with severity; no tone, or `neutral`, draws\n * a grey rounded chip for a kind tag or a count. The panel's summary badge and\n * {@link StreamStatusBadge} are both drawn with it.\n *\n * @example\n * ```tsx\n * <Badge tone=\"warn\" live report={{ id: \"fuel-low\" }}>\n * Fuel low\n * </Badge>\n * ```\n *\n * @category Badge\n */\nexport function Badge({\n tone,\n size = \"md\",\n live = false,\n report,\n children,\n ...rest\n}: BadgeProps) {\n const severity: Severity | undefined =\n tone === undefined || tone === \"neutral\" ? undefined : tone;\n const reportLabel =\n report?.label ?? (typeof children === \"string\" ? children : \"\");\n useStatusContribution(\n report\n ? {\n id: report.id,\n // A reporting badge with no state sits at the floor.\n severity: severity ?? \"go\",\n label: reportLabel,\n }\n : null,\n );\n\n const liveAttrs = live\n ? ({ role: \"status\", \"aria-live\": \"polite\" } as const)\n : {};\n\n return (\n <Badge__Body $severity={severity} $size={size} {...liveAttrs} {...rest}>\n {children}\n </Badge__Body>\n );\n}\n\n/** Decorative grey, solid-filled: a kind-chip or count with no state. Distinct from `offline`, which is a real \"data absent\" reading. */\nconst DECORATIVE_STYLE = css`\n background: var(--color-surface-raised);\n border-color: var(--color-border-subtle);\n color: var(--color-text-muted);\n`;\n\n/**\n * A transparent pill with the tone's mark as its outline and its text as the\n * label, so a `go` badge reads as status rather than as a solid \"go\" button.\n * The glow scales with severity: none for `go` and `offline`, growing from\n * `info` to `nogo`.\n */\nconst SEVERITY_STYLES: Record<Severity, ReturnType<typeof css>> = {\n go: css`\n background: transparent;\n border-color: ${TONE_MARK.go};\n color: ${TONE_TEXT.go};\n `,\n info: css`\n background: transparent;\n border-color: ${TONE_MARK.info};\n color: ${TONE_TEXT.info};\n box-shadow: 0 0 4px 0 color-mix(in srgb, ${TONE_MARK.info} 40%, transparent);\n `,\n caution: css`\n background: transparent;\n border-color: ${TONE_MARK.caution};\n color: ${TONE_TEXT.caution};\n box-shadow: 0 0 5px 0 color-mix(in srgb, ${TONE_MARK.caution} 45%, transparent);\n `,\n warn: css`\n background: transparent;\n border-color: ${TONE_MARK.warn};\n color: ${TONE_TEXT.warn};\n box-shadow: 0 0 6px 1px color-mix(in srgb, ${TONE_MARK.warn} 55%, transparent);\n `,\n nogo: css`\n background: transparent;\n border-color: ${TONE_MARK.nogo};\n color: ${TONE_TEXT.nogo};\n box-shadow: 0 0 8px 2px color-mix(in srgb, ${TONE_MARK.nogo} 65%, transparent);\n `,\n // Data gone reads as faded, dimmer than a decorative chip, rather than alarming.\n offline: css`\n background: transparent;\n border-color: ${TONE_MARK.offline};\n color: ${TONE_TEXT.offline};\n `,\n};\n\nconst SIZE_STYLES = {\n sm: css`\n font-size: var(--font-size-caption);\n padding: var(--inset-chip);\n `,\n md: css`\n font-size: var(--font-size-compact);\n padding: var(--inset-chip-roomy);\n `,\n} as const;\n\nconst Badge__Body = styled.span<{\n $severity: Severity | undefined;\n $size: BadgeSize;\n}>`\n display: inline-block;\n /* Sized by its own text, never stretched to a taller flex sibling. */\n align-self: center;\n ${fitBox(\"badge\")}\n border: 1px solid;\n font-weight: 600;\n letter-spacing: 0.08em;\n text-transform: uppercase;\n white-space: nowrap;\n\n /* Pill-shaped only for a real severity; a decorative kind chip keeps the rounded rect. */\n border-radius: ${({ $severity }) =>\n $severity === undefined ? \"var(--radius-regular)\" : \"var(--radius-pill)\"};\n\n ${({ $size }) => SIZE_STYLES[$size]}\n ${({ $severity }) =>\n $severity === undefined ? DECORATIVE_STYLE : SEVERITY_STYLES[$severity]}\n`;\n","import { css } from \"styled-components\";\n\n/**\n * Declare that this box's own EDGES are content, not decoration.\n *\n * The min-fit audit (`auditMinFit` in `@ksp-gonogo/uplink-tools`) reports text a\n * tile cuts off but not boxes, since many are drawn oversized on purpose. A box\n * carrying this is one whose shape is the affordance, so a clipped edge is\n * reported under `box-clipped` / `box-escapes-tile`.\n *\n * `name` is what a finding calls the box, so pick the primitive's own name. A\n * custom property rather than an attribute, so DOM snapshots are unaffected.\n */\nexport const fitBox = (name: string) => css`\n --fit-box: ${name};\n`;\n\n/**\n * Declare that this box PAINTS OVER the scrolling content behind it.\n *\n * A scroll glow is drawn at a fixed height, so on a short body it can mask more\n * than the overflow it advertises. `auditMinFit` reports that under\n * `masked-by-glow`, reading the depth from the element's computed gradient.\n *\n * `name` is what a finding calls the mask.\n */\nexport const fitMask = (name: string) => css`\n --fit-mask: ${name};\n`;\n","/**\n * The magnitude unwrap, re-exported from `@ksp-gonogo/sitrep-sdk`, where it\n * lives beside `Value` because ui-kit depends on the sdk and not the reverse.\n */\nexport {\n asQuantityish,\n magnitudeOf,\n magnitudeOr,\n type Quantityish,\n} from \"@ksp-gonogo/sitrep-sdk\";\n","import type { BandKind } from \"@ksp-gonogo/sitrep-sdk\";\n\n/**\n * What a band claims, in plain language, for a surface that speaks it rather\n * than draws it. A hard bound and a one-sigma interval are both said as plain\n * containment, with no statistics vocabulary, so `claimed` is returned\n * unchanged for every `kind`.\n *\n * `claimed` is the containment statement in the calling surface's own grammar:\n * an interval for a meter (`\"with bands at 30 percent and 44 percent\"`), a\n * whole clause for a chart whose shaded region varies per sample.\n *\n * @category Unit\n */\nexport function bandClaim(_kind: BandKind, claimed: string): string {\n return claimed;\n}\n","/**\n * The one definition of how a figure that is not a reading of now is marked.\n * The DOM marks, the SVG tspans and the canvas painters all read it, so a\n * change of hue, shape or size happens here and nowhere else.\n */\n\n/**\n * Which mark: `held` is the last observation kept past its time, `modelled` is\n * a figure a model carried to now.\n *\n * @category Unit\n */\nexport type ReckoningKind = \"held\" | \"modelled\";\n\n/**\n * What a figure is, for the purpose of marking it: `current` is a reading of\n * now, and the other two are the {@link ReckoningKind}s.\n *\n * @category Unit\n */\nexport type MarkKind = \"current\" | ReckoningKind;\n\n/**\n * The stroke of a hollow mark, the form a mark takes for a figure that is of\n * something other than what its label names (`elsewhere`).\n *\n * @category Unit\n */\nexport const HOLLOW_MARK_STROKE = \"max(0.075em, 1.25px)\";\n\n/**\n * The spec for each kind. `held` is a square in the warning hue, a stop sign\n * for data that has stopped; `modelled` is a\n * triangle, point up, in the modelled hue and about a pixel larger, so the two\n * weigh alike at small sizes.\n *\n * Each also has a hollow form, drawn where the figure is `elsewhere`: of\n * something other than what its label names. Hollow says that and nothing\n * else; the shape and the hue go on saying how current the figure is. A hollow\n * mark is larger than its filled twin (`hollowDomSize`), because at the filled\n * size the hole closes up. `current` has only the hollow form, a ring: a\n * current figure of the thing its label names carries no mark at all.\n *\n * @category Unit\n */\nexport const RECKONING_MARK = {\n current: {\n shape: \"ring\",\n glyph: \"○\",\n color: \"var(--color-elsewhere-mark)\",\n cssVar: \"--color-elsewhere-mark\",\n fallback: \"rgb(170 156 255)\",\n hollowDomSize: \"max(calc(0.3em + 2px), 6px)\",\n canvasRadius: 5,\n },\n held: {\n shape: \"square\",\n glyph: \"■\",\n color: \"var(--color-warn-mark)\",\n cssVar: \"--color-warn-mark\",\n fallback: \"rgb(217 161 59)\",\n domSize: \"max(0.3em, 4px)\",\n hollowDomSize: \"max(calc(0.3em + 2px), 6px)\",\n canvasRadius: 4,\n },\n modelled: {\n shape: \"triangle\",\n glyph: \"▲\",\n color: \"var(--color-modelled-mark)\",\n cssVar: \"--color-modelled-mark\",\n fallback: \"rgb(138 180 248)\",\n domSize: \"max(calc(0.3em + 1px), 5px)\",\n hollowDomSize: \"max(calc(0.3em + 3px), 7px)\",\n canvasRadius: 5,\n },\n} as const;\n","import {\n type CSSProperties,\n cloneElement,\n type FocusEvent,\n type ReactElement,\n type ReactNode,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { createPortal } from \"react-dom\";\nimport styled, { createGlobalStyle } from \"styled-components\";\n\n/** Where the tip sits, in viewport pixels: below its anchor, or above it where the anchor is low on the screen. */\ninterface Placement {\n top: number;\n left: number;\n above: boolean;\n anchorTop: number;\n}\n\n/** Anchors with less than this much viewport below them put their tip above. */\nconst ROOM_BELOW_PX = 120;\n\n/** How long the pointer may spend crossing from an anchor to its tip before the tip closes. */\nconst CROSSING_MS = 120;\n\n/** A name or description reduced to its words, so punctuation cannot make two phrasings of one thing differ. */\nconst words = (text: string): string =>\n text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \" \")\n .trim();\n\n/**\n * The handlers and data attribute to spread onto the element the tip describes.\n *\n * @category Floating\n */\nexport interface TooltipAnchorProps {\n onPointerEnter?: (event: { currentTarget: Element }) => void;\n onPointerLeave?: () => void;\n onPointerDown?: () => void;\n onPointerUp?: () => void;\n onFocus?: (event: FocusEvent<Element>) => void;\n onBlur?: () => void;\n \"data-tooltip\"?: string;\n}\n\n/**\n * What {@link useTooltip} returns: props for the anchor, and the tip to render beside it.\n *\n * @category Floating\n */\nexport interface UseTooltipResult {\n anchor: TooltipAnchorProps;\n /** The tip itself, portalled to the document body; render it anywhere beside the anchor. */\n tip: ReactNode;\n}\n\n/**\n * The kit's tip for `text`: padded, rounded, drawn over everything, and\n * placed below its anchor inside the viewport, so a clipping or scrolling\n * ancestor never cuts it off. It opens while the pointer is over the anchor or\n * the anchor has keyboard focus, and closes on a press of the anchor, on Escape (from anywhere on the page), scroll or resize. The pointer can move onto the tip, which stays open while it is hovered.\n *\n * The tip is hidden from the accessibility tree, so the anchor must already\n * say the same thing to a screen reader (in its text, an `aria-label` or a\n * visually hidden span); {@link Tooltip} does this for any element. The\n * anchor must be focusable for the keyboard to open the tip. `null` or empty\n * text gives an inert anchor and no tip.\n *\n * @example\n * ```tsx\n * function Figure({ caption, children }: { caption: string | null; children: ReactNode }) {\n * const { anchor, tip } = useTooltip(caption);\n * return (\n * <span {...anchor}>\n * {children}\n * {caption && <VisuallyHidden>, {caption}</VisuallyHidden>}\n * {tip}\n * </span>\n * );\n * }\n * ```\n *\n * @category Floating\n */\nexport function useTooltip(text: string | null | undefined): UseTooltipResult {\n const [at, setAt] = useState<Placement | null>(null);\n const [hovered, setHovered] = useState(false);\n const [focused, setFocused] = useState(false);\n const [tipHovered, setTipHovered] = useState(false);\n const pressed = useRef(false);\n const leaving = useRef<ReturnType<typeof setTimeout> | null>(null);\n const shown = text != null && text !== \"\" ? text : null;\n const open = at !== null && (hovered || focused || tipHovered);\n const cancelLeave = useCallback(() => {\n if (leaving.current !== null) clearTimeout(leaving.current);\n leaving.current = null;\n }, []);\n const close = useCallback(() => {\n cancelLeave();\n setAt(null);\n setHovered(false);\n setFocused(false);\n setTipHovered(false);\n }, [cancelLeave]);\n\n // The pointer gets a moment to cross onto the tip before the anchor's leave closes it.\n const leaveSoon = useCallback(() => {\n cancelLeave();\n leaving.current = setTimeout(() => {\n leaving.current = null;\n setHovered(false);\n setTipHovered(false);\n }, CROSSING_MS);\n }, [cancelLeave]);\n\n useEffect(() => cancelLeave, [cancelLeave]);\n\n // A tip placed once goes stale when the page under it moves.\n useEffect(() => {\n if (at === null) return;\n window.addEventListener(\"scroll\", close, true);\n window.addEventListener(\"resize\", close);\n return () => {\n window.removeEventListener(\"scroll\", close, true);\n window.removeEventListener(\"resize\", close);\n };\n }, [at, close]);\n\n // WCAG 1.4.13: Escape dismisses the tip wherever focus or the pointer is.\n useEffect(() => {\n if (!open) return;\n const onKey = (event: globalThis.KeyboardEvent) => {\n if (event.key === \"Escape\") close();\n };\n window.addEventListener(\"keydown\", onKey);\n return () => window.removeEventListener(\"keydown\", onKey);\n }, [open, close]);\n\n if (shown === null) return { anchor: {}, tip: null };\n\n const place = (element: Element) => {\n const box = element.getBoundingClientRect();\n setAt({\n top: box.bottom,\n left: box.left,\n above: window.innerHeight - box.bottom < ROOM_BELOW_PX,\n anchorTop: box.top,\n });\n };\n\n return {\n anchor: {\n \"data-tooltip\": shown,\n onPointerEnter: (event) => {\n cancelLeave();\n place(event.currentTarget);\n setHovered(true);\n },\n onPointerLeave: leaveSoon,\n onPointerDown: () => {\n pressed.current = true;\n close();\n },\n onPointerUp: () => {\n pressed.current = false;\n },\n onFocus: (event) => {\n // A pointer press focuses its target too; only keyboard focus opens the tip on its own.\n if (pressed.current) return;\n place(event.currentTarget);\n setFocused(true);\n },\n onBlur: () => {\n setFocused(false);\n if (!hovered && !tipHovered) setAt(null);\n },\n },\n tip: open\n ? createPortal(\n <TipBody\n at={at}\n text={shown}\n onPointerEnter={() => {\n cancelLeave();\n setTipHovered(true);\n }}\n onPointerLeave={leaveSoon}\n />,\n document.body,\n )\n : null,\n };\n}\n\nfunction TipBody({\n at,\n text,\n onPointerEnter,\n onPointerLeave,\n}: {\n at: Placement;\n text: string;\n onPointerEnter: () => void;\n onPointerLeave: () => void;\n}) {\n const position: CSSProperties = at.above\n ? { bottom: window.innerHeight - at.anchorTop }\n : { top: at.top };\n return (\n <Tooltip__Body\n aria-hidden=\"true\"\n data-tooltip-tip=\"\"\n data-above={at.above ? \"\" : undefined}\n onPointerEnter={onPointerEnter}\n onPointerLeave={onPointerLeave}\n style={\n {\n ...position,\n left: at.left,\n \"--tip-left\": `${at.left}px`,\n } as CSSProperties\n }\n >\n {text}\n </Tooltip__Body>\n );\n}\n\n/**\n * The props of {@link Tooltip}.\n *\n * @category Floating\n */\nexport interface TooltipProps {\n /** What the tip says. `null` or empty leaves the child untouched. */\n text: string | null | undefined;\n /** The one element the tip describes. It must forward its props to a DOM element. */\n children: ReactElement<Record<string, unknown>>;\n /**\n * Makes the child a tab stop, so the keyboard can reach a tip on an element\n * that is not a control (a readout, a badge). Leave it off on a button,\n * link or input, which is focusable already.\n */\n focusable?: boolean;\n /**\n * Whether the text is also given to assistive technology as the child's\n * `aria-description`. Defaults to `true`. Set `false` where the child already\n * says the same words in its own text, as a tab does whose tip only shows a\n * label that truncates, or where the child is hidden from assistive\n * technology.\n */\n announce?: boolean;\n}\n\n/** The child's own handler, if it has one, then the tip's. */\nfunction joined<Args extends unknown[]>(\n own: unknown,\n ours: ((...args: Args) => void) | undefined,\n): (...args: Args) => void {\n return (...args) => {\n if (typeof own === \"function\") own(...args);\n ours?.(...args);\n };\n}\n\n/**\n * A tip for any element, in place of the browser's `title` attribute: themed,\n * opened by pointer or keyboard focus, dismissed by Escape, and announced as\n * the element's description. The text is also set as the child's\n * `aria-description`, so a screen reader hears it without the tip being open\n * and no hidden text is added to the page. Where the child already carries the\n * same words in its `aria-label`, nothing is added.\n *\n * It adds no wrapper: the handlers are merged onto the child itself.\n *\n * @example\n * ```tsx\n * <Tooltip text=\"Saves the game, then flies the vessel you pick\">\n * <Button onClick={open}>Fly vessel</Button>\n * </Tooltip>\n * ```\n *\n * @category Floating\n */\nexport function Tooltip({\n text,\n children,\n focusable = false,\n announce = true,\n}: TooltipProps) {\n const { anchor, tip } = useTooltip(text);\n if (text == null || text === \"\") return children;\n\n const own = children.props;\n const label = own[\"aria-label\"];\n const named =\n !announce ||\n (typeof label === \"string\" && words(label).includes(words(text)));\n\n return (\n <>\n {cloneElement(children, {\n \"data-tooltip\": text,\n onPointerEnter: joined(own.onPointerEnter, anchor.onPointerEnter),\n onPointerLeave: joined(own.onPointerLeave, anchor.onPointerLeave),\n onPointerDown: joined(own.onPointerDown, anchor.onPointerDown),\n onPointerUp: joined(own.onPointerUp, anchor.onPointerUp),\n onFocus: joined(own.onFocus, anchor.onFocus),\n onBlur: joined(own.onBlur, anchor.onBlur),\n \"aria-description\": named ? own[\"aria-description\"] : text,\n tabIndex: focusable ? (own.tabIndex ?? 0) : own.tabIndex,\n })}\n {tip}\n {focusable && <TooltipFocusRing />}\n </>\n );\n}\n\n/** The kit focus ring for a non-control made focusable by {@link Tooltip}, in place of the browser default. */\nconst TooltipFocusRing = createGlobalStyle`\n [data-tooltip][tabindex]:not(button, a, input, select, textarea, summary):focus-visible {\n outline: 2px solid var(--color-focus);\n outline-offset: 2px;\n }\n`;\n\nconst Tooltip__Body = styled.div`\n position: fixed;\n z-index: var(--z-toast);\n margin-top: var(--offset-popover);\n max-width: min(18rem, calc(100vw - 2 * var(--offset-popover)));\n /* Pulled back inside the viewport when the anchor sits near its right edge. */\n translate: min(0px, calc(100vw - 100% - var(--offset-popover) - var(--tip-left, 0px)));\n padding: var(--inset-tooltip);\n background: var(--color-surface-raised);\n border: 1px solid var(--color-border-subtle);\n border-radius: var(--radius-floating);\n box-shadow: var(--shadow-popover-drop) rgba(0, 0, 0, 0.35);\n color: var(--color-text-primary);\n font-size: var(--font-size-caption);\n line-height: var(--line-height-body);\n letter-spacing: 0.02em;\n white-space: pre-line;\n\n &[data-above] {\n margin-top: 0;\n margin-bottom: var(--offset-popover);\n }\n`;\n","import styled from \"styled-components\";\n\n/**\n * Screen-reader-only content: visually removed but kept in the accessibility\n * tree. Use it for what a sighted user reads from another channel (colour, a\n * ticking number) that an assistive-technology user would otherwise miss, such\n * as a discrete power-state word beside a colour-coded net-rate readout, or the\n * word a currency icon stands for.\n *\n * Pair it with `role=\"status\" aria-live=\"polite\"` to announce a discrete state\n * change; keep a streaming value out of the live region so it does not flood\n * the screen reader every tick.\n *\n * @example\n * ```tsx\n * <span role=\"status\" aria-live=\"polite\">\n * <VisuallyHidden>{netRate < 0 ? \"Draining\" : \"Charging\"}</VisuallyHidden>\n * </span>\n * ```\n *\n * @category Typography\n */\nexport const VisuallyHidden = styled.span`\n position: absolute;\n width: 1px;\n height: 1px;\n padding: 0;\n margin: -1px;\n overflow: hidden;\n clip: rect(0, 0, 0, 0);\n white-space: nowrap;\n border: 0;\n`;\n","/**\n * The mark a figure carries when it is no longer a reading of now. A held\n * figure takes a square at superscript height in the warning hue the panel badge is\n * painted from; a figure a model carried takes a blue triangle of the same\n * footprint, about a pixel larger so the two weigh alike. One spec, in\n * {@link RECKONING_MARK}, drives these DOM marks and the SVG and canvas faces.\n */\nimport type { HTMLAttributes, ReactNode } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport {\n HOLLOW_MARK_STROKE,\n type MarkKind,\n RECKONING_MARK,\n} from \"./reckoningMarkSpec\";\nimport { useTooltip } from \"./Tooltip\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\nconst MARK_SIZE = RECKONING_MARK.held.domSize;\nconst MODELLED_MARK_SIZE = RECKONING_MARK.modelled.domSize;\n\n/**\n * The room a host holds at the end of its figure for the square and the gap\n * before it, so a held figure keeps its mark inside its own box whatever clips\n * or wraps around it. It is a generated box with no content: it adds nothing\n * to the host's accessible name and nothing to the clipboard.\n */\nexport const reservesHeldMark = css`\n &::after {\n content: \"\";\n display: inline-block;\n width: calc(${MARK_SIZE} + 0.14em);\n }\n`;\n\n/** {@link reservesHeldMark} for the modelled mark's slightly larger box. */\nexport const reservesModelledTriangle = css`\n &::after {\n content: \"\";\n display: inline-block;\n width: calc(${MODELLED_MARK_SIZE} + 0.14em);\n }\n`;\n\n/** The room a hollow mark of a kind needs: it is drawn larger than the filled one. */\nfunction reservesHollow(kind: MarkKind) {\n return css`\n &::after {\n content: \"\";\n display: inline-block;\n width: calc(${RECKONING_MARK[kind].hollowDomSize} + 0.14em);\n }\n `;\n}\n\n/** The room to reserve for a kind of mark, hollow where the figure is `elsewhere`. A current figure that is not `elsewhere` carries no mark and reserves nothing. */\nexport function reservesMark(kind: MarkKind, elsewhere = false) {\n if (elsewhere) return reservesHollow(kind);\n if (kind === \"current\") return null;\n return kind === \"modelled\" ? reservesModelledTriangle : reservesHeldMark;\n}\n\n/**\n * The square itself. Absolutely positioned in the room its host reserves at the\n * end of the figure (see {@link reservesHeldMark}), at superscript height, so\n * the square is inside the host's box and the line never grows. Sized in `em`\n * with a minimum size in pixels, so it stays a square in small text.\n *\n * It needs a positioned container that reserves that room: use\n * {@link HeldHost}, or give a container of your own `position: relative`,\n * `nowrap` and `reservesHeldMark`.\n *\n * @category Unit\n */\nexport const HeldMark = styled.span`\n position: absolute;\n right: 0;\n top: 0;\n width: ${MARK_SIZE};\n height: ${MARK_SIZE};\n background: ${RECKONING_MARK.held.color};\n`;\n\n/**\n * The modelled mark: a triangle, point up, in the modelled hue, hung off its\n * host exactly as {@link HeldMark} is.\n *\n * @category Unit\n */\nexport const ModelledTriangle = styled.span`\n position: absolute;\n right: 0;\n top: 0;\n width: ${MODELLED_MARK_SIZE};\n height: ${MODELLED_MARK_SIZE};\n clip-path: polygon(50% 0, 100% 100%, 0 100%);\n background: ${RECKONING_MARK.modelled.color};\n`;\n\nconst hollowBox = (kind: MarkKind) => css`\n position: absolute;\n right: 0;\n top: 0;\n width: ${RECKONING_MARK[kind].hollowDomSize};\n height: ${RECKONING_MARK[kind].hollowDomSize};\n`;\n\n/** The ring: a figure measured now, of something other than its label names. */\nconst ElsewhereRing = styled.span`\n ${hollowBox(\"current\")}\n border: ${HOLLOW_MARK_STROKE} solid ${RECKONING_MARK.current.color};\n border-radius: var(--radius-circle);\n`;\n\n/** The held square emptied: a held figure of something other than its label names. */\nconst HollowSquare = styled.span`\n ${hollowBox(\"held\")}\n border: ${HOLLOW_MARK_STROKE} solid ${RECKONING_MARK.held.color};\n`;\n\n/** The modelled triangle emptied. The hole is cut from the fill, since a clipped box has no border to draw. */\nconst HollowTriangle = styled.span`\n ${hollowBox(\"modelled\")}\n --stroke: ${HOLLOW_MARK_STROKE};\n background: ${RECKONING_MARK.modelled.color};\n clip-path: polygon(\n 50% 0,\n 100% 100%,\n 0 100%,\n 50% 0,\n 50% calc(var(--stroke) * 2.4),\n calc(var(--stroke) * 1.9) calc(100% - var(--stroke)),\n calc(100% - var(--stroke) * 1.9) calc(100% - var(--stroke)),\n 50% calc(var(--stroke) * 2.4)\n );\n`;\n\nconst HOLLOW = {\n current: ElsewhereRing,\n held: HollowSquare,\n modelled: HollowTriangle,\n} as const;\n\n/**\n * The DOM mark for a kind, silent to a screen reader: the caption beside it is\n * what is spoken.\n *\n * `elsewhere` draws the kind's hollow form, for a figure that is of something\n * other than what its label names: a ring for a current figure, the square or\n * the triangle emptied for a held or modelled one. A current figure that is\n * not `elsewhere` has no mark, and this draws nothing.\n *\n * @category Unit\n */\nexport function ReckoningMark({\n kind,\n elsewhere = false,\n}: {\n kind: MarkKind;\n elsewhere?: boolean;\n}) {\n if (kind === \"current\" && !elsewhere) return null;\n const Mark = elsewhere\n ? HOLLOW[kind]\n : kind === \"modelled\"\n ? ModelledTriangle\n : HeldMark;\n return (\n <Mark\n aria-hidden=\"true\"\n data-held-mark=\"\"\n data-reckoning-mark={kind}\n data-elsewhere={elsewhere ? \"\" : undefined}\n />\n );\n}\n\n/**\n * Something for the mark to hang off, for a component that renders bare text\n * and so has no box of its own.\n *\n * `nowrap`, since a phrase broken across two lines has two right edges.\n *\n * @category Unit\n */\nexport const HeldHost = styled.span<{\n $kind?: MarkKind;\n $elsewhere?: boolean;\n}>`\n position: relative;\n white-space: nowrap;\n ${({ $kind, $elsewhere }) => reservesMark($kind ?? \"held\", $elsewhere)}\n`;\n\n/**\n * The props of {@link HeldFigure}.\n *\n * @category Unit\n */\nexport interface HeldFigureProps extends HTMLAttributes<HTMLSpanElement> {\n /** What the mark means in words: spoken after the figure, and shown on hover. */\n caption: string | null;\n /** Which mark: a held figure takes the square, a figure a model carried the triangle. `current` is a reading of now, which is marked only when it is `elsewhere`. Defaults to `held`. */\n kind?: MarkKind;\n /** The figure is of something other than what its label names, so its mark is drawn hollow: a ring for a current figure, the square or triangle emptied otherwise. */\n elsewhere?: boolean;\n children: ReactNode;\n}\n\n/**\n * A figure marked held or modelled, or as being of something other than its\n * label names: the mark for its `kind` (hollow where `elsewhere`), the caption\n * spoken after the figure, and the same caption in the kit's hover tip.\n *\n * @category Unit\n */\nexport function HeldFigure({\n caption,\n kind = \"held\",\n elsewhere = false,\n children,\n ...rest\n}: HeldFigureProps) {\n const { anchor, tip } = useTooltip(caption);\n return (\n <HeldHost $kind={kind} $elsewhere={elsewhere} {...rest} {...anchor}>\n {children}\n <ReckoningMark kind={kind} elsewhere={elsewhere} />\n {caption !== null && (\n <VisuallyHidden data-unit-currency=\"\">, {caption}</VisuallyHidden>\n )}\n {tip}\n </HeldHost>\n );\n}\n","/**\n * Merges classes into a single string\n *\n * @param {array} classes\n * @returns {string} A string of classes\n */\nexport const mergeClasses = <ClassType = string | undefined | null>(...classes: ClassType[]) =>\n classes\n .filter((className, index, array) => {\n return (\n Boolean(className) &&\n (className as string).trim() !== '' &&\n array.indexOf(className) === index\n );\n })\n .join(' ')\n .trim();\n","/**\n * Converts string to kebab case\n *\n * @param {string} string\n * @returns {string} A kebabized string\n */\nexport const toKebabCase = (string: string) =>\n string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();\n","/**\n * Converts string to camel case\n *\n * @param {string} string\n * @returns {string} A camelized string\n */\nexport const toCamelCase = <T extends string>(string: T) =>\n string.replace(/^([A-Z])|[\\s-_]+(\\w)/g, (match, p1, p2) =>\n p2 ? p2.toUpperCase() : p1.toLowerCase(),\n );\n","import { CamelToPascal } from '../utility-types';\nimport { toCamelCase } from './toCamelCase';\n\n/**\n * Converts string to pascal case\n *\n * @param {string} string\n * @returns {string} A pascalized string\n */\nexport const toPascalCase = <T extends string>(string: T): CamelToPascal<T> => {\n const camelCase = toCamelCase(string);\n\n return (camelCase.charAt(0).toUpperCase() + camelCase.slice(1)) as CamelToPascal<T>;\n};\n","export default {\n xmlns: 'http://www.w3.org/2000/svg',\n width: 24,\n height: 24,\n viewBox: '0 0 24 24',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n};\n","/**\n * Check if a component has an accessibility prop\n *\n * @param {object} props\n * @returns {boolean} Whether the component has an accessibility prop\n */\nexport const hasA11yProp = (props: Record<string, any>) => {\n for (const prop in props) {\n if (prop.startsWith('aria-') || prop === 'role' || prop === 'title') {\n return true;\n }\n }\n\n return false;\n};\n","'use client';\n\nimport { createContext, createElement, type ReactNode, useContext, useMemo } from 'react';\nimport { LucideProps } from './types';\n\ntype LucideConfig = {\n size: number;\n color: string;\n strokeWidth: number;\n absoluteStrokeWidth: boolean;\n className: string;\n};\n\nconst LucideContext = createContext<LucideProps>({});\n\ntype LucideProviderProps = {\n children: ReactNode;\n} & Partial<LucideConfig>;\n\nexport function LucideProvider({\n children,\n size,\n color,\n strokeWidth,\n absoluteStrokeWidth,\n className,\n}: LucideProviderProps) {\n const value = useMemo(\n () => ({\n size,\n color,\n strokeWidth,\n absoluteStrokeWidth,\n className,\n }),\n [size, color, strokeWidth, absoluteStrokeWidth, className],\n );\n\n return createElement(LucideContext.Provider, { value }, children);\n}\n\nexport const useLucideContext = () => useContext(LucideContext);\n","'use client';\n\nimport { createElement, forwardRef } from 'react';\nimport defaultAttributes from './defaultAttributes';\nimport { IconNode, LucideProps } from './types';\nimport { mergeClasses, hasA11yProp } from '@lucide/shared';\nimport { useLucideContext } from './context';\n\ninterface IconComponentProps extends LucideProps {\n iconNode: IconNode;\n}\n\n/**\n * Lucide icon component\n *\n * @component Icon\n * @param {object} props\n * @param {string} props.color - The color of the icon\n * @param {number} props.size - The size of the icon\n * @param {number} props.strokeWidth - The stroke width of the icon\n * @param {boolean} props.absoluteStrokeWidth - Whether to use absolute stroke width\n * @param {string} props.className - The class name of the icon\n * @param {IconNode} props.children - The children of the icon\n * @param {IconNode} props.iconNode - The icon node of the icon\n *\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst Icon = forwardRef<SVGSVGElement, IconComponentProps>(\n (\n { color, size, strokeWidth, absoluteStrokeWidth, className = '', children, iconNode, ...rest },\n ref,\n ) => {\n const {\n size: contextSize = 24,\n strokeWidth: contextStrokeWidth = 2,\n absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,\n color: contextColor = 'currentColor',\n className: contextClass = '',\n } = useLucideContext() ?? {};\n\n const calculatedStrokeWidth =\n absoluteStrokeWidth ?? contextAbsoluteStrokeWidth\n ? (Number(strokeWidth ?? contextStrokeWidth) * 24) / Number(size ?? contextSize)\n : strokeWidth ?? contextStrokeWidth;\n\n return createElement(\n 'svg',\n {\n ref,\n ...defaultAttributes,\n width: size ?? contextSize ?? defaultAttributes.width,\n height: size ?? contextSize ?? defaultAttributes.height,\n stroke: color ?? contextColor,\n strokeWidth: calculatedStrokeWidth,\n className: mergeClasses('lucide', contextClass, className),\n ...(!children && !hasA11yProp(rest) && { 'aria-hidden': 'true' }),\n ...rest,\n },\n [\n ...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),\n ...(Array.isArray(children) ? children : [children]),\n ],\n );\n },\n);\n\nexport default Icon;\n","import { createElement, forwardRef } from 'react';\nimport { mergeClasses, toKebabCase, toPascalCase } from '@lucide/shared';\nimport { IconNode, LucideProps } from './types';\nimport Icon from './Icon';\n\n/**\n * Create a Lucide icon component\n * @param {string} iconName\n * @param {array} iconNode\n * @returns {ForwardRefExoticComponent} LucideIcon\n */\nconst createLucideIcon = (iconName: string, iconNode: IconNode) => {\n const Component = forwardRef<SVGSVGElement, LucideProps>(({ className, ...props }, ref) =>\n createElement(Icon, {\n ref,\n iconNode,\n className: mergeClasses(\n `lucide-${toKebabCase(toPascalCase(iconName))}`,\n `lucide-${iconName}`,\n className,\n ),\n ...props,\n }),\n );\n\n Component.displayName = toPascalCase(iconName);\n\n return Component;\n};\n\nexport default createLucideIcon;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M12 5v14', key: 's699le' }],\n ['path', { d: 'm19 12-7 7-7-7', key: '1idqje' }],\n];\n\n/**\n * @component @name ArrowDown\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-down\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowDown = createLucideIcon('arrow-down', __iconNode);\n\nexport default ArrowDown;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'm12 19-7-7 7-7', key: '1l729n' }],\n ['path', { d: 'M19 12H5', key: 'x3x0zl' }],\n];\n\n/**\n * @component @name ArrowLeft\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-left\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowLeft = createLucideIcon('arrow-left', __iconNode);\n\nexport default ArrowLeft;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M5 12h14', key: '1ays0h' }],\n ['path', { d: 'm12 5 7 7-7 7', key: 'xquz4c' }],\n];\n\n/**\n * @component @name ArrowRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowRight = createLucideIcon('arrow-right', __iconNode);\n\nexport default ArrowRight;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M5 3h14', key: '7usisc' }],\n ['path', { d: 'm18 13-6-6-6 6', key: '1kf1n9' }],\n ['path', { d: 'M12 7v14', key: '1akyts' }],\n];\n\n/**\n * @component @name ArrowUpToLine\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up-to-line\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUpToLine = createLucideIcon('arrow-up-to-line', __iconNode);\n\nexport default ArrowUpToLine;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'm5 12 7-7 7 7', key: 'hav0vg' }],\n ['path', { d: 'M12 19V5', key: 'x0mq9r' }],\n];\n\n/**\n * @component @name ArrowUp\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/arrow-up\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ArrowUp = createLucideIcon('arrow-up', __iconNode);\n\nexport default ArrowUp;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M10.268 21a2 2 0 0 0 3.464 0', key: 'vwvbt9' }],\n [\n 'path',\n {\n d: 'M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326',\n key: '11g9vi',\n },\n ],\n];\n\n/**\n * @component @name Bell\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/bell\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Bell = createLucideIcon('bell', __iconNode);\n\nexport default Bell;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [['path', { d: 'M20 6 9 17l-5-5', key: '1gmf2c' }]];\n\n/**\n * @component @name Check\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/check\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Check = createLucideIcon('check', __iconNode);\n\nexport default Check;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [['path', { d: 'm6 9 6 6 6-6', key: 'qrunsl' }]];\n\n/**\n * @component @name ChevronDown\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-down\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronDown = createLucideIcon('chevron-down', __iconNode);\n\nexport default ChevronDown;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [['path', { d: 'm15 18-6-6 6-6', key: '1wnfg3' }]];\n\n/**\n * @component @name ChevronLeft\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-left\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronLeft = createLucideIcon('chevron-left', __iconNode);\n\nexport default ChevronLeft;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [['path', { d: 'm9 18 6-6-6-6', key: 'mthhwq' }]];\n\n/**\n * @component @name ChevronRight\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-right\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronRight = createLucideIcon('chevron-right', __iconNode);\n\nexport default ChevronRight;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [['path', { d: 'm18 15-6-6-6 6', key: '153udz' }]];\n\n/**\n * @component @name ChevronUp\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/chevron-up\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst ChevronUp = createLucideIcon('chevron-up', __iconNode);\n\nexport default ChevronUp;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n ['path', { d: 'M12 3v18', key: '108xh3' }],\n];\n\n/**\n * @component @name Columns2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/columns-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Columns2 = createLucideIcon('columns-2', __iconNode);\n\nexport default Columns2;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { width: '14', height: '8', x: '5', y: '2', rx: '2', key: 'wc9tft' }],\n ['rect', { width: '20', height: '8', x: '2', y: '14', rx: '2', key: 'w68u3i' }],\n ['path', { d: 'M6 18h2', key: 'rwmk9e' }],\n ['path', { d: 'M12 18h6', key: 'aqd8w3' }],\n];\n\n/**\n * @component @name Computer\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/computer\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Computer = createLucideIcon('computer', __iconNode);\n\nexport default Computer;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M20 4v7a4 4 0 0 1-4 4H4', key: '6o5b7l' }],\n ['path', { d: 'm9 10-5 5 5 5', key: '1kshq7' }],\n];\n\n/**\n * @component @name CornerDownLeft\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/corner-down-left\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst CornerDownLeft = createLucideIcon('corner-down-left', __iconNode);\n\nexport default CornerDownLeft;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['ellipse', { cx: '12', cy: '5', rx: '9', ry: '3', key: 'msslwz' }],\n ['path', { d: 'M3 5V19A9 3 0 0 0 21 19V5', key: '1wlel7' }],\n ['path', { d: 'M3 12A9 3 0 0 0 21 12', key: 'mv7ke4' }],\n];\n\n/**\n * @component @name Database\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/database\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Database = createLucideIcon('database', __iconNode);\n\nexport default Database;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z',\n key: '1oefj6',\n },\n ],\n ['path', { d: 'M14 2v5a1 1 0 0 0 1 1h5', key: 'wfsgrz' }],\n ['path', { d: 'M10 9H8', key: 'b1mrlr' }],\n ['path', { d: 'M16 13H8', key: 't4e002' }],\n ['path', { d: 'M16 17H8', key: 'z1uh3a' }],\n];\n\n/**\n * @component @name FileText\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/file-text\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst FileText = createLucideIcon('file-text', __iconNode);\n\nexport default FileText;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5',\n key: 'mvr1a0',\n },\n ],\n];\n\n/**\n * @component @name Heart\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/heart\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Heart = createLucideIcon('heart', __iconNode);\n\nexport default Heart;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8', key: '1357e3' }],\n ['path', { d: 'M3 3v5h5', key: '1xhq8a' }],\n ['path', { d: 'M12 7v5l4 2', key: '1fdv2h' }],\n];\n\n/**\n * @component @name History\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/history\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst History = createLucideIcon('history', __iconNode);\n\nexport default History;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M15 21v-8a1 1 0 0 0-1-1h-4a1 1 0 0 0-1 1v8', key: '5wwlr5' }],\n [\n 'path',\n {\n d: 'M3 10a2 2 0 0 1 .709-1.528l7-6a2 2 0 0 1 2.582 0l7 6A2 2 0 0 1 21 10v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z',\n key: 'r6nss1',\n },\n ],\n];\n\n/**\n * @component @name House\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/house\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst House = createLucideIcon('house', __iconNode);\n\nexport default House;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['circle', { cx: '12', cy: '12', r: '10', key: '1mglay' }],\n ['path', { d: 'M12 16v-4', key: '1dtifu' }],\n ['path', { d: 'M12 8h.01', key: 'e9boi3' }],\n];\n\n/**\n * @component @name Info\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/info\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Info = createLucideIcon('info', __iconNode);\n\nexport default Info;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M21 17a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v2a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-2Z',\n key: 'jg2n2t',\n },\n ],\n ['path', { d: 'M6 15v-2', key: 'gd6mvg' }],\n ['path', { d: 'M12 15V9', key: '8c7uyn' }],\n ['circle', { cx: '12', cy: '6', r: '3', key: '1gm2ql' }],\n];\n\n/**\n * @component @name Joystick\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/joystick\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Joystick = createLucideIcon('joystick', __iconNode);\n\nexport default Joystick;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z',\n key: 'zw3jo',\n },\n ],\n [\n 'path',\n {\n d: 'M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12',\n key: '1wduqc',\n },\n ],\n [\n 'path',\n {\n d: 'M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17',\n key: 'kqbvx6',\n },\n ],\n];\n\n/**\n * @component @name Layers\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/layers\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Layers = createLucideIcon('layers', __iconNode);\n\nexport default Layers;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { width: '18', height: '11', x: '3', y: '11', rx: '2', ry: '2', key: '1w4ew1' }],\n ['path', { d: 'M7 11V7a5 5 0 0 1 10 0v4', key: 'fwvmzm' }],\n];\n\n/**\n * @component @name Lock\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/lock\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Lock = createLucideIcon('lock', __iconNode);\n\nexport default Lock;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M8 3H5a2 2 0 0 0-2 2v3', key: '1dcmit' }],\n ['path', { d: 'M21 8V5a2 2 0 0 0-2-2h-3', key: '1e4gt3' }],\n ['path', { d: 'M3 16v3a2 2 0 0 0 2 2h3', key: 'wsl5sc' }],\n ['path', { d: 'M16 21h3a2 2 0 0 0 2-2v-3', key: '18trek' }],\n];\n\n/**\n * @component @name Maximize\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/maximize\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Maximize = createLucideIcon('maximize', __iconNode);\n\nexport default Maximize;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M6 18h8', key: '1borvv' }],\n ['path', { d: 'M3 22h18', key: '8prr45' }],\n ['path', { d: 'M14 22a7 7 0 1 0 0-14h-1', key: '1jwaiy' }],\n ['path', { d: 'M9 14h2', key: '197e7h' }],\n ['path', { d: 'M9 12a2 2 0 0 1-2-2V6h6v4a2 2 0 0 1-2 2Z', key: '1bmzmy' }],\n ['path', { d: 'M12 6V3a1 1 0 0 0-1-1H9a1 1 0 0 0-1 1v3', key: '1drr47' }],\n];\n\n/**\n * @component @name Microscope\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/microscope\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Microscope = createLucideIcon('microscope', __iconNode);\n\nexport default Microscope;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M8 3v3a2 2 0 0 1-2 2H3', key: 'hohbtr' }],\n ['path', { d: 'M21 8h-3a2 2 0 0 1-2-2V3', key: '5jw1f3' }],\n ['path', { d: 'M3 16h3a2 2 0 0 1 2 2v3', key: '198tvr' }],\n ['path', { d: 'M16 21v-3a2 2 0 0 1 2-2h3', key: 'ph8mxp' }],\n];\n\n/**\n * @component @name Minimize\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/minimize\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Minimize = createLucideIcon('minimize', __iconNode);\n\nexport default Minimize;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { x: '14', y: '3', width: '5', height: '18', rx: '1', key: 'kaeet6' }],\n ['rect', { x: '5', y: '3', width: '5', height: '18', rx: '1', key: '1wsw3u' }],\n];\n\n/**\n * @component @name Pause\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/pause\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Pause = createLucideIcon('pause', __iconNode);\n\nexport default Pause;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z',\n key: '1a8usu',\n },\n ],\n ['path', { d: 'm15 5 4 4', key: '1mk7zo' }],\n];\n\n/**\n * @component @name Pencil\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/pencil\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Pencil = createLucideIcon('pencil', __iconNode);\n\nexport default Pencil;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z',\n key: '10ikf1',\n },\n ],\n];\n\n/**\n * @component @name Play\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/play\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Play = createLucideIcon('play', __iconNode);\n\nexport default Play;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M5 12h14', key: '1ays0h' }],\n ['path', { d: 'M12 5v14', key: 's699le' }],\n];\n\n/**\n * @component @name Plus\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/plus\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Plus = createLucideIcon('plus', __iconNode);\n\nexport default Plus;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M16.247 7.761a6 6 0 0 1 0 8.478', key: '1fwjs5' }],\n ['path', { d: 'M19.075 4.933a10 10 0 0 1 0 14.134', key: 'ehdyv1' }],\n ['path', { d: 'M4.925 19.067a10 10 0 0 1 0-14.134', key: '1q22gi' }],\n ['path', { d: 'M7.753 16.239a6 6 0 0 1 0-8.478', key: 'r2q7qm' }],\n ['circle', { cx: '12', cy: '12', r: '2', key: '1c9p78' }],\n];\n\n/**\n * @component @name Radio\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/radio\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Radio = createLucideIcon('radio', __iconNode);\n\nexport default Radio;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { width: '20', height: '12', x: '2', y: '6', rx: '2', key: '9lu3g6' }],\n];\n\n/**\n * @component @name RectangleHorizontal\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/rectangle-horizontal\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst RectangleHorizontal = createLucideIcon('rectangle-horizontal', __iconNode);\n\nexport default RectangleHorizontal;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { width: '12', height: '20', x: '6', y: '2', rx: '2', key: '1oxtiu' }],\n];\n\n/**\n * @component @name RectangleVertical\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/rectangle-vertical\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst RectangleVertical = createLucideIcon('rectangle-vertical', __iconNode);\n\nexport default RectangleVertical;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n ['path', { d: 'M3 12h18', key: '1i2n21' }],\n];\n\n/**\n * @component @name Rows2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/rows-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Rows2 = createLucideIcon('rows-2', __iconNode);\n\nexport default Rows2;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'm13.5 6.5-3.148-3.148a1.205 1.205 0 0 0-1.704 0L6.352 5.648a1.205 1.205 0 0 0 0 1.704L9.5 10.5',\n key: 'dzhfyz',\n },\n ],\n ['path', { d: 'M16.5 7.5 19 5', key: '1ltcjm' }],\n [\n 'path',\n {\n d: 'm17.5 10.5 3.148 3.148a1.205 1.205 0 0 1 0 1.704l-2.296 2.296a1.205 1.205 0 0 1-1.704 0L13.5 14.5',\n key: 'nfoymv',\n },\n ],\n ['path', { d: 'M9 21a6 6 0 0 0-6-6', key: '1iajcf' }],\n [\n 'path',\n {\n d: 'M9.352 10.648a1.205 1.205 0 0 0 0 1.704l2.296 2.296a1.205 1.205 0 0 0 1.704 0l4.296-4.296a1.205 1.205 0 0 0 0-1.704l-2.296-2.296a1.205 1.205 0 0 0-1.704 0z',\n key: 'nv9zqy',\n },\n ],\n];\n\n/**\n * @component @name Satellite\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/satellite\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Satellite = createLucideIcon('satellite', __iconNode);\n\nexport default Satellite;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915',\n key: '1i5ecw',\n },\n ],\n ['circle', { cx: '12', cy: '12', r: '3', key: '1v7zrd' }],\n];\n\n/**\n * @component @name Settings\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/settings\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Settings = createLucideIcon('settings', __iconNode);\n\nexport default Settings;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['rect', { width: '18', height: '18', x: '3', y: '3', rx: '2', key: 'afitv7' }],\n];\n\n/**\n * @component @name Square\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/square\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Square = createLucideIcon('square', __iconNode);\n\nexport default Square;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z',\n key: 'r04s7s',\n },\n ],\n];\n\n/**\n * @component @name Star\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/star\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Star = createLucideIcon('star', __iconNode);\n\nexport default Star;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M9 14 4 9l5-5', key: '102s5s' }],\n ['path', { d: 'M4 9h10.5a5.5 5.5 0 0 1 5.5 5.5a5.5 5.5 0 0 1-5.5 5.5H11', key: 'f3b9sd' }],\n];\n\n/**\n * @component @name Undo2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/undo-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Undo2 = createLucideIcon('undo-2', __iconNode);\n\nexport default Undo2;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z',\n key: 'uqj9uw',\n },\n ],\n ['path', { d: 'M16 9a5 5 0 0 1 0 6', key: '1q6k2b' }],\n ['path', { d: 'M19.364 18.364a9 9 0 0 0 0-12.728', key: 'ijwkga' }],\n];\n\n/**\n * @component @name Volume2\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/volume-2\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst Volume2 = createLucideIcon('volume-2', __iconNode);\n\nexport default Volume2;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n [\n 'path',\n {\n d: 'M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z',\n key: 'uqj9uw',\n },\n ],\n ['line', { x1: '22', x2: '16', y1: '9', y2: '15', key: '1ewh16' }],\n ['line', { x1: '16', x2: '22', y1: '9', y2: '15', key: '5ykzw1' }],\n];\n\n/**\n * @component @name VolumeX\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/volume-x\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst VolumeX = createLucideIcon('volume-x', __iconNode);\n\nexport default VolumeX;\n","import createLucideIcon from '../createLucideIcon';\nimport { IconNode } from '../types';\n\nexport const __iconNode: IconNode = [\n ['path', { d: 'M18 6 6 18', key: '1bl5f8' }],\n ['path', { d: 'm6 6 12 12', key: 'd8bk6v' }],\n];\n\n/**\n * @component @name X\n * @description Lucide SVG icon component, renders SVG Element with children.\n *\n * @preview  - https://lucide.dev/icons/x\n * @see https://lucide.dev/guide/packages/lucide-react - Documentation\n *\n * @param {Object} props - Lucide icons props and any valid SVG attribute\n * @returns {JSX.Element} JSX Element\n *\n */\nconst X = createLucideIcon('x', __iconNode);\n\nexport default X;\n","import {\n ArrowDown,\n ArrowLeft,\n ArrowRight,\n ArrowUp,\n ArrowUpToLine,\n Bell,\n Check,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n ChevronUp,\n Columns2,\n Computer,\n CornerDownLeft,\n Database,\n FileText,\n Heart,\n History,\n Home,\n Info,\n Joystick,\n Layers,\n Lock,\n type LucideProps,\n Maximize,\n Microscope,\n Minimize,\n Pause,\n Pencil,\n Play,\n Plus,\n Radio,\n RectangleHorizontal,\n RectangleVertical,\n Rows2,\n Satellite,\n Settings,\n Square,\n Star,\n Undo2,\n Volume2,\n VolumeX,\n X,\n} from \"lucide-react\";\nimport { forwardRef } from \"react\";\n\n/**\n * Props for every icon in the kit: the lucide-react SVG props plus `label`.\n *\n * `size` defaults to the standalone icon size token, which grows a step on a\n * coarse pointer; pass a number (pixels) or any CSS length to override it.\n * `strokeWidth` defaults to 1.8 (2.4 on {@link PlusIcon}). `color` sets the\n * stroke and defaults to `currentColor`, so an icon takes its text colour;\n * `fill` fills the shape. Any other SVG attribute passes through, and the ref\n * reaches the `svg` element.\n *\n * @category Icons\n */\nexport interface IconProps extends LucideProps {\n /**\n * The icon's accessible name. With one, the icon renders as `role=\"img\"`\n * named by it; without one it is decorative and hidden from assistive\n * technology, so the control it sits in must carry the name.\n */\n label?: string;\n}\n\n/**\n * A `var()` resolves inside the SVG `width`/`height` presentation attribute\n * lucide writes `size` out as, so the size token picks up the coarse-pointer\n * step a bare number cannot. The default is `control`, the glyph inside a\n * control the operator presses; a glyph that is its own control (a Fab) is\n * sized by that control.\n */\nconst ICON_DEFAULTS: LucideProps = {\n size: \"var(--icon-size-control)\",\n strokeWidth: 1.8,\n \"aria-hidden\": true,\n};\n\nfunction makeIcon(\n Component: React.ComponentType<LucideProps>,\n extraDefaults?: LucideProps,\n) {\n const Wrapped = forwardRef<SVGSVGElement, IconProps>(\n ({ label, ...props }, ref) => {\n const naming = label\n ? { role: \"img\", \"aria-label\": label, \"aria-hidden\": false }\n : {};\n return (\n <Component\n {...ICON_DEFAULTS}\n {...extraDefaults}\n {...naming}\n {...props}\n ref={ref}\n />\n );\n },\n );\n Wrapped.displayName = Component.displayName ?? Component.name;\n return Wrapped;\n}\n\n/**\n * A joystick on a base, used for serial and controller input.\n *\n * @category Icons\n */\nexport const JoystickIcon = makeIcon(Joystick);\n/**\n * A clock face with a counter-clockwise arrow, for history or past flights.\n *\n * @category Icons\n */\nexport const HistoryIcon = makeIcon(History);\n/**\n * A house, marking the home command centre. It carries no name of its own, so give it a `label` or name the control it sits in.\n *\n * @category Icons\n */\nexport const HomeIcon = makeIcon(Home);\n/**\n * A lowercase \"i\" in a circle, for information.\n *\n * @category Icons\n */\nexport const InfoIcon = makeIcon(Info);\n/**\n * A closed padlock, marking something this save has not unlocked yet.\n *\n * @category Icons\n */\nexport const LockIcon = makeIcon(Lock);\n/**\n * A point with radio waves spreading on either side, for broadcasting or a live link.\n *\n * @category Icons\n */\nexport const BroadcastIcon = makeIcon(Radio);\n/**\n * A speaker with sound waves, for audible. {@link MutedIcon} is its muted pair.\n *\n * @category Icons\n */\nexport const SpeakerIcon = makeIcon(Volume2);\n/**\n * A speaker crossed out, for muted. {@link SpeakerIcon} is its audible pair.\n *\n * @category Icons\n */\nexport const MutedIcon = makeIcon(VolumeX);\n/**\n * A bell, for notifications or alerts.\n *\n * @category Icons\n */\nexport const BellIcon = makeIcon(Bell);\n/**\n * Stacked layers, for profiles, presets or layered views.\n *\n * @category Icons\n */\nexport const LayersIcon = makeIcon(Layers);\n/**\n * A cog, for settings.\n *\n * @category Icons\n */\nexport const SettingsIcon = makeIcon(Settings);\n/**\n * A satellite with solar panels.\n *\n * @category Icons\n */\nexport const SatelliteIcon = makeIcon(Satellite);\n/**\n * Four corners pointing outward, for entering fullscreen.\n *\n * @category Icons\n */\nexport const FullscreenEnterIcon = makeIcon(Maximize);\n/**\n * Four corners pointing inward, for leaving fullscreen.\n *\n * @category Icons\n */\nexport const FullscreenExitIcon = makeIcon(Minimize);\n/**\n * A database cylinder, for stored data.\n *\n * @category Icons\n */\nexport const DatabaseIcon = makeIcon(Database);\n/**\n * A desktop computer, for a device or processor.\n *\n * @category Icons\n */\nexport const ComputerIcon = makeIcon(Computer);\n/**\n * A page of text, for diagnostics and logs.\n *\n * @category Icons\n */\nexport const DiagnosticsIcon = makeIcon(FileText);\n/**\n * A plus sign, for add. Drawn with a heavier 2.4 stroke than the other icons.\n *\n * @category Icons\n */\nexport const PlusIcon = makeIcon(Plus, { strokeWidth: 2.4 });\n\n/**\n * An X, for close or dismiss.\n *\n * @category Icons\n */\nexport const CloseIcon = makeIcon(X);\n/**\n * A pencil, for edit.\n *\n * @category Icons\n */\nexport const PencilIcon = makeIcon(Pencil);\n/**\n * A check mark, for confirm or done.\n *\n * @category Icons\n */\nexport const CheckIcon = makeIcon(Check);\n/**\n * A five-pointed star; {@link Unit} draws it as the reputation symbol.\n *\n * @category Icons\n */\nexport const StarIcon = makeIcon(Star);\n/**\n * A heart outline; pass `fill=\"currentColor\"` for a solid heart.\n *\n * @category Icons\n */\nexport const HeartIcon = makeIcon(Heart);\n/**\n * A microscope; {@link Unit} draws it as the science symbol.\n *\n * @category Icons\n */\nexport const MicroscopeIcon = makeIcon(Microscope);\n/**\n * A right-pointing triangle, for play or run.\n *\n * @category Icons\n */\nexport const PlayIcon = makeIcon(Play);\n/**\n * Two vertical bars, for pause.\n *\n * @category Icons\n */\nexport const PauseIcon = makeIcon(Pause);\n/**\n * A square, for stop.\n *\n * @category Icons\n */\nexport const StopIcon = makeIcon(Square);\n/**\n * An upward chevron, for collapse or step up.\n *\n * @category Icons\n */\nexport const ChevronUpIcon = makeIcon(ChevronUp);\n/**\n * A downward chevron, for expand or step down.\n *\n * @category Icons\n */\nexport const ChevronDownIcon = makeIcon(ChevronDown);\n/**\n * A left-pointing chevron, for back or previous.\n *\n * @category Icons\n */\nexport const ChevronLeftIcon = makeIcon(ChevronLeft);\n/**\n * A right-pointing chevron, for forward or next.\n *\n * @category Icons\n */\nexport const ChevronRightIcon = makeIcon(ChevronRight);\n/**\n * A left-pointing arrow.\n *\n * @category Icons\n */\nexport const ArrowLeftIcon = makeIcon(ArrowLeft);\n/**\n * An upward arrow.\n *\n * @category Icons\n */\nexport const ArrowUpIcon = makeIcon(ArrowUp);\n/**\n * A right-pointing arrow.\n *\n * @category Icons\n */\nexport const ArrowRightIcon = makeIcon(ArrowRight);\n/**\n * A downward arrow.\n *\n * @category Icons\n */\nexport const ArrowDownIcon = makeIcon(ArrowDown);\n/**\n * An upward arrow meeting a line, for pushing something up to another screen.\n *\n * @category Icons\n */\nexport const PushUpIcon = makeIcon(ArrowUpToLine);\n/**\n * A curved arrow turning back, for recalling something that was pushed away or undoing it.\n *\n * @category Icons\n */\nexport const RecallIcon = makeIcon(Undo2);\n/**\n * A rectangle split into two columns, for a half-width layout.\n *\n * @category Icons\n */\nexport const HalfWidthIcon = makeIcon(Columns2);\n/**\n * A wide rectangle, for a full-width layout.\n *\n * @category Icons\n */\nexport const FullWidthIcon = makeIcon(RectangleHorizontal);\n/**\n * A rectangle split into two rows, for a half-height layout.\n *\n * @category Icons\n */\nexport const HalfHeightIcon = makeIcon(Rows2);\n/**\n * A tall rectangle, for a full-height layout.\n *\n * @category Icons\n */\nexport const FullHeightIcon = makeIcon(RectangleVertical);\n/**\n * A return arrow (the symbol on the Enter key), for committing what the\n * operator composed locally, such as a console line or a staged command. Use\n * a different icon for anything that fires on its own.\n *\n * Unnamed, it is decorative, so name the action on the control (for example\n * {@link ComposerBar}'s `sendLabel`).\n *\n * @category Icons\n */\nexport const SendIcon = makeIcon(CornerDownLeft);\n","import type { HeldGrade, StreamStatusValue } from \"@ksp-gonogo/sitrep-sdk\";\n\n/**\n * The operator's word for each reason a held reading stopped updating.\n *\n * Loading and no game say the game itself is the reason, which no craft or link can answer for. Blackout and recorded keep words of their own because they ask something\n * different of the operator: a blackout is waited out, and a recorded value is\n * exact for the instant it names. Only a Topic that simply went quiet is HELD.\n */\nconst HELD_GRADE_WORD: Readonly<Record<HeldGrade, string>> = {\n held: \"HELD\",\n disconnected: \"OFFLINE\",\n \"last-before-blackout\": \"BLACKOUT\",\n recorded: \"RECORDED\",\n loading: \"LOADING\",\n \"no-game\": \"NO GAME\",\n};\n\n/** Every word a held mark's caption can open with, one per grade. */\nexport const HELD_WORDS: readonly string[] = Object.values(HELD_GRADE_WORD);\n\nconst STREAM_STATUS_WORD: Readonly<Record<StreamStatusValue, string | null>> = {\n ...HELD_GRADE_WORD,\n live: null,\n resyncing: \"SYNCING\",\n absent: \"NO DATA\",\n};\n\n/**\n * The short uppercase word a badge or caption prints for a `StreamStatusValue`\n * (HELD, OFFLINE, BLACKOUT, RECORDED, LOADING, NO GAME, SYNCING, NO DATA), or `null` for `\"live\"`,\n * which shows nothing.\n *\n * @category Badge\n */\nexport function formatStreamStatus(status: StreamStatusValue): string | null {\n return STREAM_STATUS_WORD[status];\n}\n\n/**\n * The word for a held reading's grade (HELD, OFFLINE, BLACKOUT, RECORDED, LOADING or NO GAME), or\n * HELD when no grade is given, as for a value combined from several readings.\n *\n * @category Badge\n */\nexport function heldWord(grade: HeldGrade | undefined): string {\n return HELD_GRADE_WORD[grade ?? \"held\"];\n}\n","/**\n * The number to draw, and the statement the reading makes about it.\n *\n * Shared by every primitive that draws a figure, so a `Reading` cannot mean one\n * thing on a readout and another on the instrument beside it.\n */\nimport {\n isDeterministicValue,\n isStaticValue,\n type Reading,\n type UncertaintyBand,\n type Value,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport { NULL_DISPLAY } from \"./NullValue\";\nimport type { ReckoningKind } from \"./reckoningMarkSpec\";\nimport { heldWord } from \"./status/streamStatusWord\";\nimport { formatQuantity } from \"./units\";\n\n/**\n * What a figure prop accepts: the quantity on its own, or the whole `Reading`\n * it arrived in.\n *\n * One type across every primitive that draws a figure, so a call site holding\n * a reading can fill a readout, a bar and an instrument with the same thing.\n *\n * @category Unit\n */\nexport type UnitValue<Unit extends string = string> =\n | Value<Unit>\n | Reading<Value<Unit>>;\n\n/**\n * What {@link resolveCurrency} returns: the number to draw, and whether it is current.\n *\n * @category Unit\n */\nexport interface Resolved<Unit extends string> {\n /** The number to draw; null or undefined draws the null token. */\n shown: Value<Unit> | null | undefined;\n /** True when the number on screen is not a reading of now, so it takes a mark: `mark` says which. */\n held: boolean;\n /**\n * Which mark the figure takes: `held` for the last observation kept, `modelled`\n * for a figure a model carried. Null exactly when `held` is false.\n */\n mark: ReckoningKind | null;\n /**\n * Whether the number is a fact the contract declares static, which is never\n * old and so is never marked held, whatever the reading's state.\n */\n isStatic: boolean;\n /**\n * Whether the number is exact at any instant: computed from fixed inputs and\n * the clock, as a figure between two bodies on fixed conics is. It moves, so\n * it is not static, and it is never a guess, so it takes no held or modelled\n * mark whatever the reading's state.\n */\n isDeterministic: boolean;\n /**\n * What the mark means in words: the grade where the reading names one, a\n * grade-neutral word where it does not, and the instant the number was last\n * a reading of now where that is readable. Said rather than shown, and null\n * only where nothing was marked.\n */\n caption: string | null;\n /**\n * The uncertainty band the reading's model publishes, as it arrived and not\n * yet narrowed to a unit; only a band in the shown value's unit applies.\n */\n band: UncertaintyBand | null;\n}\n\n/**\n * When the observation was made, on the game's own calendar, or null when the\n * reading carries no readable instant.\n *\n * Through `formatQuantity`, so a held number and a `<MissionDate>` beside it\n * print one spelling of a UT. A malformed instant answers null, since \"as of\"\n * followed by the null token is worse than the grade on its own.\n */\nfunction lastValidAt(asOfUt: Value<\"ut\"> | undefined): string | null {\n if (asOfUt === undefined) return null;\n const { value } = formatQuantity(asOfUt.magnitude, asOfUt.unit);\n return value === NULL_DISPLAY ? null : value;\n}\n\n/**\n * What the mark says in words: the grade, and how far back the number is from.\n * The dot answers at a glance; this answers the follow-up in the hover and the\n * accessibility tree.\n */\nfunction sayCurrency(caption: string, asOfUt: Value<\"ut\"> | undefined): string {\n const at = lastValidAt(asOfUt);\n return at === null ? caption : `${caption}, as of ${at}`;\n}\n\n/**\n * How a primitive reads a {@link Reading}.\n *\n * @category Unit\n */\nexport interface CurrencyOptions {\n /**\n * The consumer draws the reading's reckoning, where one is on offer, rather\n * than its observation: a countdown, or a figure a widget solves forward.\n * The modelled figure is then marked wherever it is not a reading of now,\n * which under signal delay includes a current reading carried forward to the\n * craft's own present (SCET, spacecraft event time).\n */\n readonly drawsReckoning?: boolean;\n}\n\n/**\n * The mark's words for a current reading whose figure the model carried\n * forward by the signal delay, to the craft's own present: SCET, spacecraft\n * event time.\n *\n * @category Unit\n */\nexport const MODELLED_TO_SCET = \"modelled to SCET\";\n\n/**\n * What the model says a current reading's figure is at the instant it was\n * carried to, where that is later than the newest data received: the figure a\n * widget draws beside the observation with {@link ModelledAlongside}.\n * `undefined` everywhere else.\n *\n * @category Unit\n */\nexport function modelledBeyondReceived<Payload>(\n reading: Reading<Payload> | null | undefined,\n): Payload | undefined {\n if (reading == null || reading.state !== \"observed\") return undefined;\n if (reading.value === undefined) return undefined;\n if (reading.reckoning.status !== \"available\") return undefined;\n if (!reading.reckoning.beyondReceived) return undefined;\n return reading.reckoning.modelled;\n}\n\n/** The mark and its words for a held reading's observation. */\nfunction heldCurrency<Unit extends string>(\n input: Reading<Value<Unit>>,\n): Omit<Resolved<Unit>, \"band\"> {\n /*\n * The mark follows the number, not the state: a held reading with no value\n * gets no dot. Whatever is marked gets words, including the ordinary\n * gradeless held reading a derived value produces.\n */\n if (isStaticValue(input.value) || isDeterministicValue(input.value)) {\n return {\n shown: input.value,\n held: false,\n mark: null,\n isStatic: isStaticValue(input.value),\n isDeterministic: isDeterministicValue(input.value),\n caption: null,\n };\n }\n return {\n shown: input.value,\n held: input.value !== undefined,\n mark: input.value !== undefined ? \"held\" : null,\n isStatic: false,\n isDeterministic: false,\n caption:\n input.value === undefined\n ? null\n : sayCurrency(heldWord(input.grade), input.asOfUt),\n };\n}\n\n/**\n * Split what was handed in into the number and the statement about it.\n *\n * A bare `Value` (and `null`, and nothing at all) is current by construction.\n * A `Reading` in `observed` state is current, one in `held` state with a value\n * is marked held, and any other state yields `shown: null`. With\n * `drawsReckoning`, the reading's modelled figure is shown in place of the\n * observation where the model has one.\n *\n * @category Unit\n */\nexport function resolveCurrency<Unit extends string>(\n input: UnitValue<Unit> | null | undefined,\n options: CurrencyOptions = {},\n): Resolved<Unit> {\n // `in` throws on a primitive, and some callers hand over a raw magnitude.\n if (typeof input !== \"object\" || input === null || !(\"state\" in input)) {\n return {\n shown: input,\n held: false,\n mark: null,\n isStatic: isStaticValue(input),\n isDeterministic: isDeterministicValue(input),\n caption: null,\n band: null,\n };\n }\n // The band is orthogonal to the state: a live reading and a held one may each carry a model.\n const band =\n input.reckoning.status === \"available\"\n ? (input.reckoning.band ?? null)\n : null;\n if (\n options.drawsReckoning &&\n input.reckoning.status === \"available\" &&\n (input.state === \"observed\" || input.state === \"held\") &&\n // An exact figure needs no model to carry it, so it is never drawn as one.\n !isDeterministicValue(input.value)\n ) {\n const shown = input.reckoning.modelled;\n if (input.state === \"held\") {\n const held = heldCurrency(input);\n // The figure drawn is the model's, so it is marked as modelled, from the instant the observation stopped.\n return {\n ...held,\n shown,\n mark: held.held ? \"modelled\" : null,\n caption: held.caption === null ? null : `${held.caption}, modelled`,\n band,\n };\n }\n const carried = input.reckoning.beyondReceived;\n return {\n shown,\n held: carried,\n mark: carried ? \"modelled\" : null,\n isStatic: false,\n isDeterministic: false,\n caption: carried ? MODELLED_TO_SCET : null,\n band,\n };\n }\n if (input.state === \"observed\") {\n return {\n shown: input.value,\n held: false,\n mark: null,\n isStatic: isStaticValue(input.value),\n isDeterministic: isDeterministicValue(input.value),\n caption: null,\n band,\n };\n }\n if (input.state === \"held\") return { ...heldCurrency(input), band };\n // `null` rather than `undefined`, so the caller renders the null token rather than the symbol form.\n return {\n shown: null,\n held: false,\n mark: null,\n isStatic: false,\n isDeterministic: false,\n caption: null,\n band: null,\n };\n}\n\n/**\n * The attributes every figure-drawing primitive stamps on the element that\n * carries its figure: `data-figure` names it as a figure, `\"static\"` for a fact\n * the contract declares static and `\"deterministic\"` for one exact at any\n * instant, and `data-held` marks one that is not a reading of now. A render sweep reads the pair to find a held figure drawn as current.\n * No `data-figure` where there is no number to draw.\n */\nexport function figureAttributes(resolved: {\n readonly shown: unknown;\n readonly held: boolean;\n readonly mark?: ReckoningKind | null;\n readonly isStatic: boolean;\n readonly isDeterministic?: boolean;\n}): {\n \"data-figure\": string | undefined;\n \"data-held\": \"\" | undefined;\n \"data-reckoned\": ReckoningKind | undefined;\n} {\n return {\n \"data-figure\":\n resolved.shown == null\n ? undefined\n : resolved.isStatic\n ? \"static\"\n : resolved.isDeterministic\n ? \"deterministic\"\n : \"\",\n \"data-held\": resolved.held ? \"\" : undefined,\n \"data-reckoned\": resolved.held ? (resolved.mark ?? \"held\") : undefined,\n };\n}\n\n/**\n * The mark a derived figure takes, and its words: what a widget hands `<Unit marked>` for a number it computed from a reading's\n * payload (an apoapsis solved from an orbit) rather than read off a field.\n *\n * @category Unit\n */\nexport interface ReckoningMarking {\n kind: ReckoningKind;\n caption: string;\n}\n\n/**\n * How a figure derived from this reading should be marked, or null where the reading is a current observation of now.\n *\n * A current reading a model carried past the newest data received marks `modelled`; a held one marks `held`, or `modelled` where a model\n * carries it. Use it for every displayed figure computed from a reading's payload, a plan's included.\n *\n * @category Unit\n */\nexport function derivedMarking(\n reading: Reading<unknown> | null | undefined,\n /** The figure is carried by a model that the reading itself does not name, such as a position the map models from the orbit. */\n carried = false,\n): ReckoningMarking | null {\n if (reading == null) return null;\n if (reading.state === \"observed\") {\n return carried ||\n (reading.reckoning.status === \"available\" &&\n reading.reckoning.beyondReceived)\n ? { kind: \"modelled\", caption: MODELLED_TO_SCET }\n : null;\n }\n if (reading.state === \"held\") {\n const said = sayCurrency(heldWord(reading.grade), reading.asOfUt);\n return carried || reading.reckoning.status === \"available\"\n ? { kind: \"modelled\", caption: `${said}, modelled` }\n : { kind: \"held\", caption: said };\n }\n return null;\n}\n","import type { Value } from \"@ksp-gonogo/sitrep-sdk\";\nimport { type FormatQuantityOptions, writeQuantity } from \"./units\";\n\n/**\n * The precision a figure is drawn at, reduced to one test: whether two figures\n * come out as the same drawing.\n *\n * @category Unit\n */\nexport interface DrawnPrecision<Figure> {\n readsAsOne(a: Figure, b: Figure): boolean;\n}\n\n/**\n * Drawn as text: two figures are one where `write` gives them the same text.\n *\n * @category Unit\n */\nexport function writtenAs<Figure>(\n write: (figure: Figure) => string,\n): DrawnPrecision<Figure> {\n return { readsAsOne: (a, b) => write(a) === write(b) };\n}\n\n/**\n * A quantity drawn as {@link writeQuantity} writes it, which is also how {@link Unit} draws it with the same options.\n *\n * @category Unit\n */\nexport function writtenQuantity<Unit extends string>(\n opts: FormatQuantityOptions = {},\n): DrawnPrecision<Value<Unit>> {\n return writtenAs((quantity) => writeQuantity(quantity, opts));\n}\n\n/**\n * How far apart two positions on an instrument's scale sit, as a fraction of\n * the whole scale, before they read as two.\n *\n * One percent, the finest step a percentage readout writes.\n *\n * @category Unit\n */\nexport const SCALE_TOLERANCE = 0.01;\n\n/**\n * Placed on an instrument's scale: each figure is a fraction of it, clamped to\n * it first, and two read as one within one percent of the scale. `wraps`\n * measures the short way round a scale whose ends meet.\n *\n * @category Unit\n */\nexport function placedOnScale({\n wraps = false,\n}: {\n wraps?: boolean;\n} = {}): DrawnPrecision<number> {\n const onScale = (at: number): number =>\n wraps ? at - Math.floor(at) : Math.min(1, Math.max(0, at));\n return {\n readsAsOne(a, b) {\n const apart = Math.abs(onScale(a) - onScale(b));\n return (wraps ? Math.min(apart, 1 - apart) : apart) <= SCALE_TOLERANCE;\n },\n };\n}\n\n/**\n * Whether a model's figure stands apart from the observation at the precision\n * both are drawn, so drawing it says something the observation beside it does\n * not. Several figures, such as a band's two ends, stand apart where any one\n * does. Beside no observation at all, every figure stands apart.\n *\n * @category Unit\n */\nexport function standsApart<Figure>(\n observed: Figure | null | undefined,\n modelled: Figure | readonly Figure[],\n precision: DrawnPrecision<Figure>,\n): boolean {\n const figures: readonly Figure[] = Array.isArray(modelled)\n ? modelled\n : [modelled as Figure];\n if (observed === null || observed === undefined) return figures.length > 0;\n return figures.some((figure) => !precision.readsAsOne(observed, figure));\n}\n","import type { Value } from \"@ksp-gonogo/sitrep-sdk\";\nimport {\n createContext,\n type ReactElement,\n type ReactNode,\n useContext,\n useId,\n useLayoutEffect,\n useState,\n useSyncExternalStore,\n} from \"react\";\nimport { magnitudeOf } from \"./magnitude\";\nimport {\n type FormatQuantityOptions,\n type FormatsFor,\n type FormatsForKind,\n formatGroupKey,\n type KindOfGroup,\n type LadderPosition,\n ladderPosition,\n type PresentableAs,\n type PresentableAsKind,\n pinGroupKey,\n readsAsOneFigure,\n separatingDecimals,\n type UnitGroupKey,\n unitScaleKey,\n} from \"./units\";\n\n/**\n * What a group settles, and what every member inside it is written at. The fields are the formatter's own, so applying one is a spread.\n *\n * An absent field is a group with no opinion: the member's own props fill it.\n *\n * @category Unit\n */\nexport interface SharedFormat {\n /** The rung every member is written at. */\n readonly format?: string;\n /** The unit every member is converted to, when the scope asked for one. */\n readonly as?: string;\n /** The digit count every member is written at. */\n readonly decimals?: number;\n}\n\n/** What one member hands over, and the whole of it. */\ninterface Report {\n /** Its reading, in the unit it arrived in. */\n readonly reading: number;\n readonly unit: string;\n /** Where it sits on the shared ladder, or undefined when its unit climbs nothing; such a member still reports for the digit count. */\n readonly position?: LadderPosition;\n}\n\n/**\n * What a scope pins for one group, typed by the unit it is addressed to: `of=\"m\" as=\"kg\"` is a compile error.\n *\n * @category Unit\n */\nexport interface UnitPins<Unit extends string = string> {\n /** The rung every member of the group is written at. */\n format?: FormatsFor<Unit>;\n /** The unit every member of the group is shown in. */\n as?: PresentableAs<Unit>;\n /** The digit count every member of the group is written at. */\n decimals?: number;\n}\n\n/**\n * {@link UnitPins} addressed to a whole group, checked against what that group measures.\n *\n * @category Unit\n */\nexport interface UnitGroupPins<Group extends UnitGroupKey> {\n /** The rung every member of the group is written at. */\n format?: FormatsForKind<KindOfGroup<Group>>;\n /** The unit every member of the group is shown in. */\n as?: PresentableAsKind<KindOfGroup<Group>>;\n /** The digit count every member of the group is written at. */\n decimals?: number;\n}\n\n/**\n * What each named group of a mixed scope is pinned to. Every entry is optional and a key that is not a group is a compile error. An Uplink's units reach this type by merging into `UnitDeclarations`.\n *\n * @category Unit\n */\nexport type UnitPinsByGroup = {\n [Group in UnitGroupKey]?: UnitGroupPins<Group>;\n};\n\nconst NO_PINS: UnitPins = {};\n\n/** What a scope was ASKED for, as against what it settles. */\ninterface Policy {\n /** What each named group was pinned to, by its {@link formatGroupKey}. A missing key settles for itself. */\n readonly byKey: ReadonlyMap<string, UnitPins>;\n /** A pin that named no unit, which reaches every group in the scope. */\n readonly unaddressed: UnitPins | undefined;\n /** Widen digits until the members read apart. A property of the whole scope. */\n readonly separate: boolean;\n}\n\nconst NO_POLICY: Policy = {\n byKey: new Map(),\n unaddressed: undefined,\n separate: false,\n};\n\n/** Where every scope of one tree keeps what the whole tree shares. */\ninterface Root {\n /** Every scope of this tree, so a report in one can re-settle the others, sibling subtrees included. */\n readonly family: Set<Scope>;\n /** Subscribe to every settle in the tree. A bound function, never a method. */\n readonly subscribe: (listener: () => void) => () => void;\n /** Re-settle every scope for `key`, and notify once if anything moved. */\n readonly sweep: (key: string) => void;\n}\n\ninterface Scope {\n readonly root: Root;\n /** How far inside the tree this scope is: parents settle before children. */\n readonly depth: number;\n /** Put or drop one member's report under `key`. `undefined` drops it. */\n hold(key: string, id: string, report: Report | undefined): void;\n /** What `key` settled on, or undefined while the group has no opinion. */\n settled(key: string): SharedFormat | undefined;\n /** Whether `key`'s members all print as the same text at what it settled on. Kept apart from the format so the formatter is never handed a non-format field. */\n readsAsOneFigure(key: string): boolean;\n /** State what this scope was asked for. Its own props, never an answer. */\n setPolicy(next: Policy): void;\n /** Recompute this scope's answer for `key`. True when it moved. */\n resettle(key: string): boolean;\n /** Join the tree, or leave it once unmounted. Both halves, because strict mode runs a layout effect's cleanup and then its body again on one mount. */\n attach(): void;\n detach(): void;\n}\n\nfunction samePins(a: UnitPins | undefined, b: UnitPins | undefined): boolean {\n if (a === undefined || b === undefined) return a === b;\n return a.format === b.format && a.as === b.as && a.decimals === b.decimals;\n}\n\nfunction samePolicy(a: Policy, b: Policy): boolean {\n if (a.separate !== b.separate) return false;\n if (!samePins(a.unaddressed, b.unaddressed)) return false;\n if (a.byKey.size !== b.byKey.size) return false;\n for (const [key, pins] of a.byKey) {\n if (!samePins(pins, b.byKey.get(key))) return false;\n }\n return true;\n}\n\n/** The policy a scope's props amount to. {@link samePolicy} compares by field, so a fresh literal every render settles nothing extra. */\nfunction policyOf(\n of: string | undefined,\n pins: UnitPinsByToken | undefined,\n own: UnitPins,\n separate: boolean,\n): Policy {\n const byKey = new Map<string, UnitPins>();\n if (of !== undefined) byKey.set(formatGroupKey(of), own);\n for (const [token, pin] of Object.entries(pins ?? {})) {\n if (pin !== undefined) byKey.set(pinGroupKey(token), pin);\n }\n return {\n byKey,\n unaddressed: of === undefined && pins === undefined ? own : undefined,\n separate,\n };\n}\n\nfunction sameFormat(\n a: SharedFormat | undefined,\n b: SharedFormat | undefined,\n): boolean {\n if (a === undefined || b === undefined) return a === b;\n return a.format === b.format && a.as === b.as && a.decimals === b.decimals;\n}\n\nfunction sameReport(a: Report | undefined, b: Report): boolean {\n return (\n a !== undefined &&\n a.reading === b.reading &&\n a.unit === b.unit &&\n a.position?.base === b.position?.base &&\n a.position?.rung === b.position?.rung\n );\n}\n\nfunction createRoot(): Root {\n const family = new Set<Scope>();\n const listeners = new Set<() => void>();\n return {\n family,\n subscribe(listener) {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n },\n sweep(key) {\n // Outermost first: a child's answer is built on its parent's.\n const ordered = [...family].sort((a, b) => a.depth - b.depth);\n let moved = false;\n for (const member of ordered) {\n if (member.resettle(key)) moved = true;\n }\n if (!moved) return;\n for (const listener of listeners) listener();\n },\n };\n}\n\nfunction createScope(parent: Scope | undefined): Scope {\n const held = new Map<string, Map<string, Report>>();\n const settled = new Map<string, SharedFormat>();\n const oneFigure = new Set<string>();\n const root = parent?.root ?? createRoot();\n let policy = NO_POLICY;\n\n /** The rung of the LARGEST member, the one the group is a group about. */\n const ownRung = (members: Iterable<Report>): string | undefined => {\n let winner: LadderPosition | undefined;\n for (const member of members) {\n const position = member.position;\n if (position === undefined) continue;\n if (winner === undefined || position.base > winner.base)\n winner = position;\n }\n return winner?.rung;\n };\n\n const scope: Scope = {\n root,\n depth: parent === undefined ? 0 : parent.depth + 1,\n\n hold(key, id, report) {\n const forKey = held.get(key);\n if (report === undefined) {\n if (forKey !== undefined) {\n forKey.delete(id);\n if (forKey.size === 0) held.delete(key);\n }\n } else {\n if (sameReport(forKey?.get(id), report)) return;\n if (forKey === undefined) held.set(key, new Map([[id, report]]));\n else forKey.set(id, report);\n }\n // Up first, so the outermost scope holds every reading in the tree; only it sweeps, once, for the whole family.\n if (parent === undefined) root.sweep(key);\n else parent.hold(key, id, report);\n },\n\n settled: (key) => settled.get(key),\n\n readsAsOneFigure: (key) => oneFigure.has(key),\n\n setPolicy(next) {\n if (samePolicy(policy, next)) return;\n policy = next;\n // Every key, not only pinned ones: `separate` is scope-wide, and a pin moving off a key lets that key settle for itself again.\n for (const key of held.keys()) root.sweep(key);\n },\n\n resettle(key) {\n const members = [...(held.get(key)?.values() ?? [])];\n const inherited = parent?.settled(key);\n // An unaddressed pin applies only where no group was named.\n const pins = policy.byKey.get(key) ?? policy.unaddressed ?? NO_PINS;\n const format = pins.format ?? inherited?.format ?? ownRung(members);\n const as = pins.as ?? inherited?.as;\n const decimals =\n pins.decimals ??\n (policy.separate\n ? separatingDecimals(members, { format, as })\n : inherited?.decimals);\n const next =\n format === undefined && as === undefined && decimals === undefined\n ? undefined\n : {\n ...(format !== undefined && { format }),\n ...(as !== undefined && { as }),\n ...(decimals !== undefined && { decimals }),\n };\n // Asked after the ladder, about the figures that will actually be drawn, and only by a scope asked to `separate`.\n const oneFigureNow =\n policy.separate && readsAsOneFigure(members, { format, as, decimals });\n const oneFigureMoved = oneFigureNow !== oneFigure.has(key);\n if (oneFigureNow) oneFigure.add(key);\n else oneFigure.delete(key);\n if (sameFormat(settled.get(key), next)) return oneFigureMoved;\n if (next === undefined) settled.delete(key);\n else settled.set(key, next);\n return true;\n },\n\n attach() {\n root.family.add(scope);\n },\n\n detach() {\n root.family.delete(scope);\n },\n };\n\n root.family.add(scope);\n return scope;\n}\n\n/** The inert default: a `<Unit>` with no scope above it reports into this and hears nothing back. */\nconst NO_SCOPE: Scope = {\n root: { family: new Set(), subscribe: () => () => {}, sweep: () => {} },\n depth: 0,\n hold: () => {},\n settled: () => undefined,\n readsAsOneFigure: () => false,\n setPolicy: () => {},\n resettle: () => false,\n attach: () => {},\n detach: () => {},\n};\n\n// The scope's identity never changes, and answers reach members through `useSyncExternalStore`, so a settle never re-runs a member's report effect.\nconst ScopeContext = createContext<Scope>(NO_SCOPE);\n\n/**\n * Whether there is a real `<UnitSharedFormat>` above this point.\n *\n * Membership cannot be read off {@link useSharedFormat}: a group over a unit with no ladder (`rpm`) legitimately settles an empty format.\n */\nexport function useInSharedFormat(): boolean {\n return useContext(ScopeContext) !== NO_SCOPE;\n}\n\n/** What every spelling of the props below shares. */\ninterface UnitSharedFormatBaseProps {\n children?: ReactNode;\n /**\n * Widen the digit count until the members stop printing the same figure as each other. What an interval wants (6 700 km and 6 710 km both print `6.7 Mm` otherwise) and a column does not. Applies to the whole scope.\n */\n separate?: boolean;\n}\n\n/**\n * A scope that pins one kind. `of` names the unit the pins are addressed to and types them: `of=\"m\"` makes `as` a length and `format` a rung on the length ladder, and `of={v.unit}` needs no annotation.\n *\n * Without `of` the pins are unaddressed: they reach every group and nothing checks them.\n *\n * @category Unit\n */\nexport interface UnitSharedFormatProps<Unit extends string = string>\n extends UnitSharedFormatBaseProps,\n UnitPins<Unit> {\n /** The unit the pins below are addressed to. */\n of?: Unit;\n}\n\n/**\n * A scope that pins several groups, each keyed by name, so a group is pinned at most once and one that needs nothing is simply absent: `pins={{ length: { as: \"km\" }, mass: { as: \"t\" } }}`.\n *\n * Each value is typed by its own key, so `{ length: { as: \"kg\" } }` is a compile error at that entry. A laddered kind is keyed by its kind; a unit that climbs nothing keys itself, so `s` and `min` stay separate groups. See {@link UnitGroupKey}.\n *\n * @category Unit\n */\nexport interface UnitSharedFormatMixedProps extends UnitSharedFormatBaseProps {\n /** What each named group is pinned to, checked against what it measures. */\n pins: UnitPinsByGroup;\n}\n\n/** The pin map with its keys erased, as the runtime reads it. */\ntype UnitPinsByToken = Readonly<Record<string, UnitPins | undefined>>;\n\n/** Both spellings at once, as the component body actually reads them. */\ninterface UnitSharedFormatAnyProps extends UnitSharedFormatBaseProps, UnitPins {\n of?: string;\n pins?: UnitPinsByToken;\n}\n\n/**\n * A group of quantities that settles one format per kind, so the whole group reads as one instrument. It renders nothing of its own, so it can wrap a row, a cell, a table or a widget body.\n *\n * Every `<Unit>` inside reports its value and is given back the settled format; outside a scope each value formats itself. The caller names no unit or digit count.\n *\n * - The unit is the one the largest member would choose (its {@link Rung}), since a group reads at the size of the thing it describes: `999 m` beside `1000 m` reads `1.0 km`, and a group that must tell its members apart says `separate`\n * - Grouping is per kind (per ladder family, else per unit), so metres settle with metres and kilograms with kilograms in one scope\n * - `format`, `as` and `decimals` pin what a group would otherwise settle. `of` addresses them to one group (`of=\"m\" as=\"km\"`), `pins` to several (`pins={{ length: { as: \"km\" }, mass: { as: \"t\" } }}`), and a pin that names no unit reaches every group unchecked\n * - Nested scopes compose: the unit comes from the outermost, the digit count from the nearest scope that asked for one. Two scopes side by side are two groups\n *\n * The settled format is applied before the first paint, so a reader never sees the members drawn apart.\n *\n * @example\n * ```tsx\n * // Current over limit, one rung, symbol drawn once: \"1234/2000 rpm\".\n * <UnitSharedFormat>\n * <Unit value={currentRpm} decimals={0} hideUnitInGroup />\n * /\n * <Unit value={rpmLimit} decimals={0} />\n * </UnitSharedFormat>\n *\n * // An interval whose ends must read apart.\n * <UnitSharedFormat separate>\n * <Unit value={low} /> to <Unit value={high} />\n * </UnitSharedFormat>\n *\n * // Every length in a table shown in km.\n * <UnitSharedFormat of=\"m\" as=\"km\">\n * {rows}\n * </UnitSharedFormat>\n * ```\n *\n * @category Unit\n */\nexport function UnitSharedFormat<Unit extends string = string>(\n props: UnitSharedFormatProps<Unit> | UnitSharedFormatMixedProps,\n): ReactElement;\nexport function UnitSharedFormat({\n children,\n of,\n pins,\n format,\n as,\n decimals,\n separate = false,\n}: UnitSharedFormatAnyProps): ReactElement {\n const enclosing = useContext(ScopeContext);\n // The inert default is not a parent: its root never sweeps.\n const [scope] = useState(() =>\n createScope(enclosing === NO_SCOPE ? undefined : enclosing),\n );\n useLayoutEffect(() => {\n scope.attach();\n return () => scope.detach();\n }, [scope]);\n // After the members' own effects, which React runs child-first, so the reports are in before the policy that reads them.\n useLayoutEffect(() => {\n scope.setPolicy(policyOf(of, pins, { format, as, decimals }, separate));\n }, [scope, of, pins, format, as, decimals, separate]);\n return (\n <ScopeContext.Provider value={scope}>{children}</ScopeContext.Provider>\n );\n}\n\n/**\n * Report a quantity to the enclosing {@link UnitSharedFormat} and hear back the format its group settled on, or `undefined` when there is none.\n *\n * `<Unit>` calls this itself, and so do the kit's own readouts that cannot draw a `<Unit>` (SVG axis text, an `aria-valuetext`) but must be written at the same format as the `<Unit>`s beside them. It is internal to the kit and not exported from the package. A quantity whose caller already decided its presentation (`format`, `as`, a non-auto `scale`) neither reports nor hears back.\n */\nexport function useSharedFormat<Unit extends string = string>(\n value: Value<Unit> | null | undefined,\n opts: FormatQuantityOptions = {},\n): SharedFormat | undefined {\n const scope = useContext(ScopeContext);\n const id = useId();\n const reading = magnitudeOf(value);\n const unit: string | undefined = value?.unit;\n const decided =\n opts.format !== undefined ||\n opts.as !== undefined ||\n (opts.scale !== undefined && opts.scale !== \"auto\");\n const key = decided || reading === null ? undefined : formatGroupKey(unit);\n // Two numbers rather than the value, so the effect depends on the reading and not on an object that is fresh every render.\n const position =\n key === undefined ||\n reading === null ||\n unit === undefined ||\n unitScaleKey(unit) === undefined\n ? undefined\n : ladderPosition(reading, unit);\n const base = position?.base;\n const rung = position?.rung;\n\n useLayoutEffect(() => {\n if (key === undefined || reading === null || unit === undefined) return;\n scope.hold(key, id, {\n reading,\n unit,\n ...(base !== undefined &&\n rung !== undefined && { position: { base, rung } }),\n });\n return () => scope.hold(key, id, undefined);\n // The format this hook returns is deliberately absent from these deps: a report that depended on the answer would loop.\n }, [scope, id, key, reading, unit, base, rung]);\n\n return useSyncExternalStore(\n scope.root.subscribe,\n () => (key === undefined ? undefined : scope.settled(key)),\n () => undefined,\n );\n}\n\n/**\n * Whether the group this value belongs to reads as one figure at the format it settled on. Only a scope asked to `separate` answers true.\n *\n * Read-only, unlike {@link useSharedFormat}: a caller that decides what to draw from this must keep reporting as before, or membership becomes a function of the verdict.\n */\nexport function useReadsAsOneFigure<Unit extends string = string>(\n value: Value<Unit> | null | undefined,\n opts: FormatQuantityOptions = {},\n): boolean {\n const scope = useContext(ScopeContext);\n const decided =\n opts.format !== undefined ||\n opts.as !== undefined ||\n (opts.scale !== undefined && opts.scale !== \"auto\");\n const key =\n decided ||\n magnitudeOf(value) === null ||\n value === null ||\n value === undefined\n ? undefined\n : formatGroupKey(value.unit);\n return useSyncExternalStore(\n scope.root.subscribe,\n () => (key === undefined ? false : scope.readsAsOneFigure(key)),\n () => false,\n );\n}\n","import {\n type BandKind,\n bandIn,\n type Reading,\n type UncertaintyBand,\n type Value,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport type { ReactNode } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { bandClaim } from \"./bandClaim\";\nimport { ReckoningMark, reservesMark } from \"./HeldMark\";\nimport { MicroscopeIcon, StarIcon } from \"./Icons\";\nimport type { ReckoningMarking } from \"./readingCurrency\";\nimport {\n figureAttributes,\n resolveCurrency,\n type UnitValue,\n} from \"./readingCurrency\";\nimport type { MarkKind } from \"./reckoningMarkSpec\";\nimport { standsApart, writtenAs } from \"./standsApart\";\nimport { Tooltip, useTooltip } from \"./Tooltip\";\nimport { useInSharedFormat, useSharedFormat } from \"./UnitSharedFormat\";\nimport {\n ATTACHED_SYMBOLS,\n displaySymbol,\n type FormatQuantityOptions,\n type FormatsFor,\n formatQuantity,\n kindOfUnit,\n type PresentableAs,\n readsAsOneFigure,\n speakQuantity,\n wordForSymbol,\n} from \"./units\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\nconst ATTACHED = ATTACHED_SYMBOLS;\n\n/** Kinds shown as a glyph rather than text, keyed on the displayed symbol. Funds keeps its `f`, the game's own convention. */\nconst ICON_BY_SYMBOL = {\n sci: MicroscopeIcon,\n rep: StarIcon,\n} as const;\n\nconst Unit__Span = styled.span<{ $attached: boolean; $icon: boolean }>`\n /* Relative to the parent's font size with a floor; attached symbols keep full size, and icons take 0.9em so a thin stroke stays legible. */\n font-size: ${({ $attached, $icon }) =>\n $attached ? \"1em\" : $icon ? \"0.9em\" : \"max(0.72em, 10px)\"};\n /* No margin: the number-unit gap is a thin space character in the markup, so it survives copying. */\n /* \"m/s\" and \"kg/m³\" must never wrap mid-symbol. */\n white-space: nowrap;\n /* A unit must survive an uppercasing parent: m and M are metre and mega. */\n text-transform: none;\n /* A glyph centres on the digits beside it rather than sitting on their baseline; the offset is in em so it holds at every size. */\n ${({ $icon }) =>\n $icon\n ? \"display: inline-flex; align-items: center; vertical-align: -0.12em;\"\n : \"\"}\n`;\n\n// A held figure is relatively positioned and reserves the mark's room at its end, so the mark sits inside the box that a clipping edge cuts and a line never lands the mark alone.\nconst Unit__Quantity = styled.span<{\n $mark: MarkKind | null;\n $elsewhere?: boolean;\n}>`\n white-space: nowrap;\n ${({ $mark, $elsewhere }) =>\n $mark !== null\n ? css`\n position: relative;\n ${reservesMark($mark, $elsewhere)}\n `\n : \"\"}\n`;\n\n// Dimmer than the value but never smaller: a qualifier must recede without becoming unreadable.\nconst Unit__Interval = styled.span`\n color: var(--color-text-muted);\n white-space: nowrap;\n`;\n\n// The spoken staleness caption, excluded from selection so copying a readout does not pick it up.\nconst Unit__Currency = styled(VisuallyHidden)`\n user-select: none;\n`;\n\n// The spoken unit word, excluded from selection so copying \"12.4 km\" does not also yield \"kilometres\".\nconst Unit__Word = styled(VisuallyHidden)`\n user-select: none;\n`;\n\nexport type { UnitValue } from \"./readingCurrency\";\n\n/** U+2009 THIN SPACE. SI puts a space between a number and its unit. */\nconst THIN_SPACE = \"\\u2009\";\n\n/**\n * The model's interval, written against the figure on screen at the value's own rung, so it never prints as `1 km ± 25 m`.\n *\n * `±` is used only where the two half-widths agree to the precision shown and the band's value is the figure being drawn, since `±` reads as an offset from the number beside it. Otherwise the range form prints the model's own two ends.\n */\nfunction toInterval<UnitSymbol extends string>(\n shown: Value<UnitSymbol>,\n band: UncertaintyBand<UnitSymbol> | undefined,\n opts: FormatQuantityOptions,\n rung: string,\n): Interval | null {\n if (band === undefined) return null;\n const write = <OtherUnit extends string>(\n quantity: Value<OtherUnit>,\n ): string =>\n formatQuantity(quantity.magnitude, quantity.unit, {\n ...opts,\n format: rung,\n }).value;\n const width = (from: Value<UnitSymbol>, to: Value<UnitSymbol>): string =>\n write(to.minus(from));\n // Ends that print as the same text draw as one approximate figure, by the same test `<Band>` uses; an interval that stands nowhere apart from the figure on screen adds nothing.\n const oneFigure = readsAsOneFigure([band.lo, band.hi], {\n ...opts,\n format: rung,\n });\n if (!standsApart(shown, [band.lo, band.hi], writtenAs(write))) return null;\n const below = width(band.lo, band.value);\n const above = width(band.value, band.hi);\n // Half-widths that both write as zero, from ends that straddle a rounding boundary, claim nothing to read.\n const writesZero = (text: string): boolean =>\n /^[-+\\u2212]?[0.,\\s]*$/.test(text);\n if (!oneFigure && writesZero(below) && writesZero(above)) return null;\n const anchored = write(band.value) === write(shown);\n // The spoken ends carry the unit's word; the written ends carry no symbol because one `<UnitSymbol>` follows the pair.\n const spoken = { ...opts, format: rung };\n return {\n oneFigure,\n plusMinus: !oneFigure && anchored && below === above ? above : null,\n lo: write(band.lo),\n hi: write(band.hi),\n loSaid: speakQuantity(band.lo, spoken),\n hiSaid: speakQuantity(band.hi, spoken),\n kind: band.kind,\n };\n}\n\n/** The interval as it is written and as it is qualified. See {@link toInterval}. */\ninterface Interval {\n /** Whether both ends print as the same text, so only `lo` is drawn. */\n oneFigure: boolean;\n /** The half-width, where the short form is honest, and null where it is not. */\n plusMinus: string | null;\n /** The low end for the eye: no symbol, since the pair shares one. */\n lo: string;\n hi: string;\n /** The low end for the ear, with the unit's WORD rather than its symbol. */\n loSaid: string;\n hiSaid: string;\n kind: BandKind;\n}\n\n/** The hover text: the staleness sentence, the band's claim (worded by `bandClaim`, as a meter's is), or both. */\nfunction hover(\n caption: string | null,\n interval: Interval | null,\n): string | null {\n const said =\n interval === null\n ? null\n : bandClaim(\n interval.kind,\n `with bands at ${interval.loSaid} and ${interval.hiSaid}`,\n );\n if (caption === null) return said;\n return said === null ? caption : `${caption}, ${said}`;\n}\n\n/**\n * Props for {@link Unit}. The formatter options it extends (`decimals`, `scale` and the rest) apply to the number drawn.\n *\n * @category Unit\n */\nexport interface UnitProps<UnitSymbol extends string = string>\n extends Omit<FormatQuantityOptions, \"format\" | \"as\"> {\n /**\n * The quantity to show; it carries its own unit. Absent or null renders the null token, so a read can be handed straight over without a gate; zero renders as zero.\n *\n * A whole {@link Reading} also draws whether the number is current.\n */\n value?: UnitValue<UnitSymbol> | null;\n /**\n * Pin the unit rather than letting the value's size choose it, where\n * convention calls for one: km/h on a launch broadcast, km/s in a technical\n * readout.\n *\n * Validated against the value's kind: it checks on a speed and is a type error on a length.\n */\n format?: FormatsFor<UnitSymbol>;\n /**\n * Show the value in a different unit of the same kind: `as=\"°C\"` on a kelvin\n * field, `as=\"g\"` on an m/s² one. The contract says what the field IS, this\n * says what the operator wants to READ.\n *\n * Validated against the value's kind, as `format` is. A cross-kind request at runtime renders the value in its own unit.\n */\n as?: PresentableAs<UnitSymbol>;\n /**\n * Draw the number without its symbol, for a member of a group that prints the\n * symbol once at the end: `1234/2000 rpm` rather than `1234 rpm/2000 rpm`.\n *\n * Honoured only inside a `<UnitSharedFormat>`; on a lone `<Unit>` it does nothing, since a number with no unit near it is not a readout. The value still counts toward the unit the group settles on. The symbol is hidden from the accessibility tree as well as the screen.\n */\n hideUnitInGroup?: boolean;\n /**\n * Draw the reading's model rather than its observation, where the model has\n * one, and mark the figure as modelled wherever it is not a reading of now:\n * a blue triangle, where a held figure takes the warning dot. For a figure a\n * person reads as the truth about now (a position, an altitude, a clock);\n * leave it off where the number is an input to a computation.\n */\n reckoned?: boolean;\n /**\n * The mark and words for a figure derived from a reading rather than read\n * off one (see {@link derivedMarking}), where `value` is a bare quantity that\n * carries none of its own. Ignored when `value` is a whole `Reading`.\n */\n marked?: ReckoningMarking | null;\n /**\n * Says the figure is of something other than what its label names (a\n * strength measured on another route), in the words given. The figure's mark\n * is then drawn hollow, a ring where the figure is a reading of now, and the\n * words follow whatever the figure already says about how current it is.\n */\n elsewhere?: string | null;\n /** A bare unit token, rendered as a symbol with no number. Used only when `value` is not passed; prefer passing a value. */\n children?: ReactNode;\n className?: string;\n}\n\n/** The symbol half: the glyph or icon, plus the word that replaces it in the accessibility tree. */\nfunction UnitSymbol({\n token,\n className,\n spaced,\n}: {\n token: string;\n className?: string;\n spaced: boolean;\n}) {\n const symbol = displaySymbol(token, kindOfUnit(token));\n\n // Category kinds (count, id, text, flag, enum, n/a) display as an empty string: they name what a field is, not a unit.\n if (symbol === \"\") return null;\n\n const word = wordForSymbol(symbol);\n const Glyph = ICON_BY_SYMBOL[symbol as keyof typeof ICON_BY_SYMBOL];\n const attached = ATTACHED.has(symbol);\n\n // The word replaces the symbol in the accessibility tree; a symbol with no word stays announced rather than the unit vanishing.\n const spoken = word !== undefined;\n\n return (\n <>\n {spaced && !attached ? THIN_SPACE : null}\n <Tooltip text={word} announce={false}>\n <Unit__Span\n $attached={attached}\n $icon={Glyph !== undefined}\n className={className}\n data-unit={token}\n >\n {Glyph ? (\n <Glyph size=\"1em\" />\n ) : spoken ? (\n <span aria-hidden=\"true\">{symbol}</span>\n ) : (\n symbol\n )}\n {spoken && <Unit__Word data-unit-word=\"\"> {word}</Unit__Word>}\n </Unit__Span>\n </Tooltip>\n </>\n );\n}\n\n/**\n * A quantity, rendered whole: `<Unit value={altitude} />`. The value carries its own unit, so the call site names neither the unit nor the format.\n *\n * An absent or null value renders `NULL_DISPLAY`, never blank space, and a magnitude of zero keeps its zero.\n *\n * Handed a `Reading<Value<UnitSymbol>>`, it also draws whether the number is current, in three treatments:\n * - current (`observed`): drawn as a bare `Value` is, with no mark\n * - no number (`pending`, `unowned`, `absent`): the null token\n * - held (`held`, any grade): the last observation in full, marked by a square at superscript height in the warning hue, with the grade and the `asOfUt` instant on hover and in the spoken caption\n *\n * With `reckoned`, a reading with a model draws the model's figure and marks it modelled (a blue triangle) wherever it is not a reading of now.\n *\n * The mark is out of flow, so a column never reflows when a channel goes quiet, and it is not a live region: a widget that wants the change announced wraps its readout in `role=\"status\"`. Without `reckoned` it never draws a reckoned figure.\n *\n * A held reading whose model publishes an {@link UncertaintyBand} also shows the interval (`1 km ± 0.025 km`, `1 km (0.97 to 1.03 km)`, `1 km (~1.2 km)`). A current reading shows none, and a band in another unit draws nothing.\n *\n * The symbol is sized relative to the text around it (never below 10px) and takes its colour, so it needs no size or tone prop and keeps the value's tone at the value's contrast. Plane angles attach to the number and keep full size. Every symbol is replaced in the accessibility tree by its spoken word.\n *\n * `<Unit>` is the one way to put a quantity on screen. Hand it the value as it arrived (SI, never pre-scaled) and let it choose the unit; pin one with `format` or convert with `as` only where convention calls for it.\n *\n * @example\n * ```tsx\n * import { value } from \"@ksp-gonogo/sitrep-sdk\";\n * import { Unit } from \"@ksp-gonogo/ui-kit\";\n *\n * // A whole Reading: draws the held mark when the channel goes quiet.\n * <Unit value={rangeReading} />\n * // A bare number off a payload, wrapped with its SI unit.\n * <Unit value={value(\"m\", body.radius)} />\n * <Unit value={value(\"m/s\", closingSpeed)} decimals={1} />\n * <Unit value={value(\"m/s\", surfaceSpeed)} format=\"km/h\" />\n * ```\n *\n * @category Unit\n */\nexport function Unit<UnitSymbol extends string = string>({\n value,\n children,\n className,\n // Kept out of `opts`, which goes to the formatter: this is about drawing, not the number.\n hideUnitInGroup,\n reckoned,\n marked,\n elsewhere,\n ...opts\n}: UnitProps<UnitSymbol>) {\n const resolvedCurrency = resolveCurrency(value, { drawsReckoning: reckoned });\n // A bare quantity is current by construction, so a derived figure's mark comes from the caller.\n const currency =\n marked != null && !resolvedCurrency.held\n ? {\n ...resolvedCurrency,\n held: true,\n mark: marked.kind,\n caption: marked.caption,\n }\n : resolvedCurrency;\n const { shown, held, mark: ownMark, band } = currency;\n const hollow = elsewhere != null && elsewhere !== \"\" && shown != null;\n // A reading of now carries no mark of its own, and takes the ring where it is of something else.\n const mark: MarkKind | null = ownMark ?? (hollow ? \"current\" : null);\n const caption = hollow\n ? [currency.caption, elsewhere].filter(Boolean).join(\"; \")\n : currency.caption;\n /*\n * Reports to an enclosing `<UnitSharedFormat>` and returns the format the group settled on; inert with no scope above it.\n * A held member reports as a live one does, so a column's rung does not move when one cell stops updating.\n */\n const shared = useSharedFormat(shown, opts);\n\n // Membership, not `shared !== undefined`: a group over a unit with no ladder (`rpm`) settles nothing yet still hides the symbol.\n const inGroup = useInSharedFormat();\n const hideSymbol = hideUnitInGroup === true && inGroup;\n\n /*\n * The group's answer goes under this call site's own props, so a caller's pin wins.\n * Render `formatted.symbol`, never `formatted.rung`: a duration interleaves its parts into the value (\"2h 14m\") with an empty symbol while its rung is still \"s\".\n */\n const resolved = shared === undefined ? opts : { ...shared, ...opts };\n const formatted = formatQuantity(shown?.magnitude, shown?.unit, resolved);\n // Narrowed to the shown value's unit, never assumed: a band arrives keyed by a runtime path, and `bandIn` answers nothing rather than converting.\n const interval =\n shown == null || band === null || !held\n ? null\n : toInterval(shown, bandIn(band, shown.unit), resolved, formatted.rung);\n // The hover says in words what the mark says in shape, plus the instant and the band's claim.\n const { anchor, tip } = useTooltip(hover(caption, interval));\n\n if (value !== undefined || children === undefined) {\n return (\n <Unit__Quantity\n className={className}\n $mark={mark}\n $elsewhere={hollow}\n {...figureAttributes(currency)}\n {...anchor}\n >\n {formatted.value}\n {!hideSymbol && <UnitSymbol token={formatted.symbol} spaced />}\n {interval !== null && (\n <Unit__Interval data-unit-band=\"\">\n {interval.oneFigure ? (\n <>\n {\" (\"}\n {/* The approximate mark is silent; the word beside it is spoken. */}\n <span aria-hidden=\"true\">~</span>\n <Unit__Word data-unit-word=\"\">approximately </Unit__Word>\n {interval.lo}\n </>\n ) : interval.plusMinus === null ? (\n ` (${interval.lo} to ${interval.hi}`\n ) : (\n ` ± ${interval.plusMinus}`\n )}\n {/* A band is in the figure's unit, so its symbol hides with the main one. */}\n {!hideSymbol && <UnitSymbol token={formatted.symbol} spaced />}\n {interval.plusMinus === null ? \")\" : null}\n </Unit__Interval>\n )}\n {/* Silent: the caption below is what gets spoken, and the shape alone carries the meaning (WCAG 1.4.1). */}\n {mark !== null && <ReckoningMark kind={mark} elsewhere={hollow} />}\n {caption !== null && (\n <Unit__Currency data-unit-currency=\"\">, {caption}</Unit__Currency>\n )}\n {tip}\n </Unit__Quantity>\n );\n }\n\n // A non-string child cannot be looked up, so it renders as given.\n if (typeof children !== \"string\") {\n return (\n <Unit__Span $attached={false} $icon={false} className={className}>\n {children}\n </Unit__Span>\n );\n }\n\n return <UnitSymbol token={children} className={className} spaced={false} />;\n}\n","import { forwardRef, type HTMLAttributes, type ReactNode } from \"react\";\nimport styled from \"styled-components\";\nimport { GAP_VAR, type GapToken } from \"./scales\";\n\n/**\n * Horizontal distribution for {@link Cluster}: `between` pushes the first and\n * last children to the edges, the others pack children to one side or the\n * middle.\n *\n * @category Layout\n */\nexport type ClusterJustify = \"between\" | \"start\" | \"center\" | \"end\";\n\n/**\n * Vertical alignment of {@link Cluster}'s children.\n *\n * @category Layout\n */\nexport type ClusterAlign = \"center\" | \"start\" | \"baseline\";\n\n/**\n * Props for {@link Cluster}. Any other `div` attribute passes through, and the\n * ref reaches the root `div`.\n *\n * @category Layout\n */\nexport interface ClusterProps extends HTMLAttributes<HTMLDivElement> {\n /** `justify-content` shorthand. Defaults to `between`. */\n justify?: ClusterJustify;\n /**\n * `align-items` shorthand. Defaults to `center`; use `start` when one side\n * can be taller than the other, such as a label beside a wrapping control.\n */\n align?: ClusterAlign;\n /**\n * Gap between children, one of the {@link GapToken} names. Omitted, the row\n * takes the `related` gap of whatever container it sits in, so the same row\n * is 8px on a panel and 6px inside a card. Set it only where this row\n * decides the spacing of what it holds.\n */\n gap?: GapToken;\n /**\n * Let children wrap onto further lines. Off by default, so a row of controls\n * never silently turns into a ragged block; chip strips and tag lists opt in.\n */\n wrap?: boolean;\n children?: ReactNode;\n}\n\nconst JUSTIFY_CONTENT: Record<ClusterJustify, string> = {\n between: \"space-between\",\n start: \"flex-start\",\n center: \"center\",\n end: \"flex-end\",\n};\n\nconst ALIGN_ITEMS: Record<ClusterAlign, string> = {\n center: \"center\",\n start: \"flex-start\",\n baseline: \"baseline\",\n};\n\n/**\n * A horizontal flex row. By default its children are vertically centred and\n * spread to both edges (`space-between`), and it carries `min-width: 0` so a\n * truncating child truncates instead of overflowing. Children stay on one\n * line unless `wrap` is set.\n *\n * @category Layout\n */\nexport const Cluster = forwardRef<HTMLDivElement, ClusterProps>(\n function Cluster(\n {\n justify = \"between\",\n align = \"center\",\n gap,\n wrap = false,\n children,\n ...rest\n },\n ref,\n ) {\n return (\n <Cluster__Root\n ref={ref}\n $justify={justify}\n $align={align}\n $gap={gap}\n $wrap={wrap}\n {...rest}\n >\n {children}\n </Cluster__Root>\n );\n },\n);\n\nconst Cluster__Root = styled.div<{\n $justify: ClusterJustify;\n $align: ClusterAlign;\n $gap?: GapToken;\n $wrap: boolean;\n}>`\n display: flex;\n align-items: ${({ $align }) => ALIGN_ITEMS[$align]};\n justify-content: ${({ $justify }) => JUSTIFY_CONTENT[$justify]};\n gap: ${({ $gap }) => ($gap ? GAP_VAR[$gap] : \"var(--gap-related)\")};\n ${({ $wrap }) => ($wrap ? \"flex-wrap: wrap;\" : \"\")}\n min-width: 0;\n`;\n","import type { BadgeEntry, HeldGrade, Tone } from \"@ksp-gonogo/sitrep-sdk\";\nimport { severityFromStreamStatus } from \"./status/severity\";\nimport { heldWord } from \"./status/streamStatusWord\";\n\n/**\n * What a badge entry draws: its words, its tone and its tooltip.\n *\n * @category Badge\n */\nexport interface BadgeFace {\n label: string;\n tone: Tone | undefined;\n title: string | undefined;\n}\n\n/**\n * How a `BadgeEntry` reads once its `held` grade is taken into account. With\n * no held grade it returns the entry's own `label`, `tone` and `title`. With\n * one, the grade's word and severity replace the label and tone, and the\n * title keeps the replaced label (`\"<label>: <word>\"`) unless the entry set\n * its own. Use it to draw a `BadgeEntry` the same way the panel header does.\n *\n * @category Badge\n */\nexport function badgeFace(entry: BadgeEntry & { title?: string }): BadgeFace {\n const grade = heldGradeOf(entry);\n if (grade === undefined) {\n return { label: entry.label, tone: entry.tone, title: entry.title };\n }\n const label = heldWord(grade);\n return {\n label,\n tone: severityFromStreamStatus(grade),\n title: entry.title ?? `${entry.label}: ${label}`,\n };\n}\n\n/**\n * A badge's own grade, whether it names one directly or carries the whole\n * `Reading` it was derived from. `Reading.grade` is only ever set once the\n * reading itself is `\"held\"`, so a live reading passed through `held` yields\n * `undefined` here the same as no `held` at all.\n */\nfunction heldGradeOf(entry: BadgeEntry): HeldGrade | undefined {\n const held = entry.held;\n if (held === undefined) return undefined;\n return typeof held === \"string\" ? held : held.grade;\n}\n","import {\n CommandErrorCode,\n commandRefusalSubject,\n describeErrorCode,\n type LimitBreach,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport { writeQuantity } from \"../units\";\nimport type { RailTags } from \"./railTags\";\n\n/**\n * One refused dispatch, as much of it as the sentence needs. The entries in `useCommand`'s `refusals` satisfy it, and so does one built by hand.\n *\n * @category CommandDelay\n */\nexport interface CommandRefusalLike {\n errorCode: CommandErrorCode;\n /** The refinement's id, when the refusal was more specific than its root `errorCode`. */\n reason?: string;\n /** The command id that was dispatched, e.g. `career.facility.upgrade`. */\n command?: string;\n /** The args it was dispatched with. */\n args?: unknown;\n /** The dispatch's own operator-facing description, when it carried one. */\n label?: string;\n /** The limit and the actual behind the reason, when the mod sent them. */\n breach?: LimitBreach;\n /**\n * The refusal in the game's own words, when the game gave any: a\n * strategy's own reason, a pre-flight check's warning title. Used in place\n * of the kit's own clause, since it arrives localised and follows the game.\n */\n detail?: string;\n}\n\n/**\n * A refusal a surface can render: the text's inputs plus the dispatch's own\n * `requestId`, which keys the box and is what `dismiss` takes.\n *\n * @category CommandDelay\n */\nexport interface CommandRefusalEntry extends CommandRefusalLike {\n id: string;\n}\n\n/**\n * One refused dispatch as the rail renders it: the refusal itself, plus the two\n * things only the registering handle knows, its stable id and the command's\n * rail axes.\n *\n * @category CommandDelay\n */\nexport interface RailRefusal extends CommandRefusalEntry {\n /**\n * The command's three axes. Only the MARK is read: a discrete command gets\n * its in-flight glyph tile, a continuous one its text label.\n */\n tags: RailTags;\n}\n\n/** A number as it is written beside its unit (`253,000f`, `16`), through `units.ts` so funds read the same everywhere. */\nfunction quantity(value: number, unit: string): string {\n return writeQuantity({ magnitude: value, unit });\n}\n\n/** The clause after the colon, or `null` when this arm needs numbers it did not get; never an invented \"0 of 0\". */\nfunction comparison(\n errorCode: CommandErrorCode,\n breach: LimitBreach,\n): string | null {\n const { limit, actual, unit } = breach;\n // `== null`: the wire writes an explicit JSON null for a breach with no numbers.\n if (limit == null || actual == null) return null;\n\n switch (errorCode) {\n case CommandErrorCode.LimitReached: {\n // Names the facility, since the operator's next move is at that building.\n const what = readableQuantity(breach.quantity);\n const counts = `${quantity(actual, unit)} of ${quantity(limit, unit)}`;\n return breach.facilityName\n ? `the ${breach.facilityName} holds ${counts} ${what}`\n : `it holds ${counts} ${what}`;\n }\n case CommandErrorCode.AlreadyAtMaximum:\n return `it is already at ${breach.quantity || \"level\"} ${quantity(actual, unit)} of ${quantity(limit, unit)}`;\n case CommandErrorCode.InsufficientFunds:\n // Actual is the price and Limit is the balance.\n return `it costs ${quantity(actual, unit)} and funds are ${quantity(limit, unit)}`;\n case CommandErrorCode.InsufficientScience:\n return `it costs ${quantity(actual, unit)} and science is ${quantity(limit, unit)}`;\n default:\n return null;\n }\n}\n\n/** `activeCrew` -> `active crew`: a camelCase id inside a sentence reads as a leaked variable. */\nfunction readableQuantity(quantityId: string): string {\n return quantityId.replace(/([a-z0-9])([A-Z])/g, \"$1 $2\").toLowerCase();\n}\n\n/** The sentence a code was declared with, or `null` when nothing this client knows declares it. */\nfunction declaredSentence(id: string | undefined): string | null {\n return (id && describeErrorCode(id)?.sentence) || null;\n}\n\n/**\n * The game's own words as a mid-sentence clause: trimmed, trailing full stop\n * dropped. Case is never folded, which would damage KSP's proper nouns.\n */\nfunction said(detail: string | undefined): string | null {\n const trimmed = detail?.trim().replace(/\\.$/, \"\");\n return trimmed ? trimmed : null;\n}\n\n/**\n * What the operator reads when the game refuses a command:\n *\n * Hire Valentina Kerman refused: the Astronaut Complex holds 16 of 16 active crew.\n * Upgrade Launch Pad refused: it is already at tier 3 of 3.\n * Upgrade Launch Pad refused: it costs 253,000f and funds are 189,412f.\n *\n * The command and args name the SUBJECT, the code picks the clause (the\n * refinement's own sentence ahead of its root's), and the `LimitBreach`\n * supplies the NUMBERS, written as {@link Unit} writes them.\n *\n * Names are user-supplied and unbounded, so whatever renders this must WRAP:\n * truncation eats the numbers off the end.\n *\n * @category CommandDelay\n */\nexport function commandRefusalSentence(refusal: CommandRefusalLike): string {\n return sentence(refusal, \"refused\");\n}\n\n/**\n * What the operator reads when the game will refuse a command that has not been\n * pressed yet:\n *\n * Hire Valentina Kerman unavailable: the Astronaut Complex holds 16 of 16 active crew.\n * Recover unavailable: the craft is throttled up.\n *\n * The same clause as {@link commandRefusalSentence}, so a control never says\n * one thing before the press and another after. \"Unavailable\" because nothing\n * has been asked yet: a gate is a condition that holds.\n *\n * @category CommandDelay\n */\nexport function commandGateSentence(gate: CommandRefusalLike): string {\n return sentence(gate, \"unavailable\");\n}\n\nfunction sentence(refusal: CommandRefusalLike, verb: string): string {\n const subject = commandRefusalSubject(refusal);\n const clause =\n (refusal.breach ? comparison(refusal.errorCode, refusal.breach) : null) ??\n // What the game itself said, ahead of anything declared.\n said(refusal.detail) ??\n declaredSentence(refusal.reason) ??\n declaredSentence(refusal.errorCode) ??\n // An id nothing declares is not prose, but it beats a bare \"refused.\" with nothing after it.\n refusal.reason ??\n refusal.errorCode;\n return subject\n ? `${subject} ${verb}: ${clause}.`\n : `${verb.charAt(0).toUpperCase()}${verb.slice(1)}: ${clause}.`;\n}\n","import {\n type CommandErrorCode,\n commandRefusalSubject,\n type LimitBreach,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport { commandRefusalSentence } from \"./commandRefusalSentence\";\nimport type { RailTags } from \"./railTags\";\n\n/**\n * One dispatch that was called lost and then replied after all, as much of it\n * as the sentence needs. The entries in `useCommand`'s `founds` satisfy it,\n * and so does one built by hand. `outcome` is required: every sentence turns on it.\n *\n * @category CommandDelay\n */\nexport interface CommandFoundLike {\n /**\n * What the late reply turned out to say: the command executed, the game\n * refused it, or the machinery broke on the far side.\n */\n outcome: \"ran\" | \"refused\" | \"errored\";\n /** The command id that was dispatched, e.g. `vessel.control.setSas`. */\n command?: string;\n /** The args it was dispatched with. */\n args?: unknown;\n /** The dispatch's own operator-facing description, when it carried one. */\n label?: string;\n /** `outcome: \"refused\"` only: the game's typed reason. */\n errorCode?: CommandErrorCode;\n /** `outcome: \"refused\"` only: the refinement's id, when the refusal was more specific than `errorCode`. */\n reason?: string;\n /** `outcome: \"refused\"` only: the limit and the actual behind the reason. */\n breach?: LimitBreach;\n /** `outcome: \"refused\"` only: the refusal in the game's own words. */\n detail?: string;\n /** `outcome: \"errored\"` only: what broke, in the machinery's own words. */\n error?: { code: string; message: string };\n}\n\n/**\n * A found a surface can render: the text's inputs plus the dispatch's `requestId`, which keys the box and is what `dismiss` takes.\n *\n * @category CommandDelay\n */\nexport interface CommandFoundEntry extends CommandFoundLike {\n id: string;\n}\n\n/**\n * One found dispatch as the rail renders it, plus its command's rail axes.\n *\n * @category CommandDelay\n */\nexport interface RailFound extends CommandFoundEntry {\n tags: RailTags;\n}\n\n/**\n * What the operator is told about a command they were told was lost, which then\n * replied: the action, then the outcome, then the reason. The row's own state\n * already says the reply was lost, so the sentence does not repeat it.\n *\n * - `ran`: `Hire Valentina Kerman ran.` If they re-sent it, it ran twice\n * - `refused`: `Hire Valentina Kerman was refused: the Astronaut Complex holds\n * 16 of 16 active crew.` The reason is the refusal composer's own words\n * - `errored`: `Hire Valentina Kerman failed: <reason>.` The machinery broke\n * over there; a retry may work\n *\n * Never \"confirmed\": the operator may already have re-sent it. No imperative:\n * the rail says what happened and lets the operator decide.\n *\n * @category CommandDelay\n */\nexport function commandFoundSentence(found: CommandFoundLike): string {\n const subject = commandRefusalSubject(found);\n const what = subject || found.command || \"The command\";\n if (found.outcome === \"refused\") {\n // The refusal composer, not a second table of reasons.\n const reason =\n found.errorCode === undefined\n ? \"\"\n : afterSubject(\n commandRefusalSentence({\n errorCode: found.errorCode,\n reason: found.reason,\n command: found.command,\n args: found.args,\n label: found.label,\n breach: found.breach,\n detail: found.detail,\n }),\n );\n return reason ? `${what} was refused: ${reason}` : `${what} was refused.`;\n }\n if (found.outcome === \"errored\") {\n const said = found.error?.message?.trim().replace(/\\.$/, \"\");\n return said ? `${what} failed: ${said}.` : `${what} failed.`;\n }\n return `${what} ran.`;\n}\n\n/**\n * `Hire Valentina Kerman refused: the Astronaut Complex holds 16 of 16 active\n * crew.` -> `the Astronaut Complex holds 16 of 16 active crew.`, since the found\n * sentence supplies its own subject and verb.\n */\nfunction afterSubject(sentence: string): string {\n const at = sentence.indexOf(\": \");\n return at === -1 ? sentence : sentence.slice(at + 2);\n}\n","import { commandRefusalSubject } from \"@ksp-gonogo/sitrep-sdk\";\nimport type { RailTags } from \"./railTags\";\n\n/**\n * One dispatch that got no reply, as much of it as the sentence needs. The entries in `useCommand`'s `losses` satisfy it.\n *\n * @category CommandDelay\n */\nexport interface CommandLossLike {\n /** The command id that was dispatched, e.g. `vessel.control.setSas`. */\n command?: string;\n /** The args it was dispatched with. */\n args?: unknown;\n /** The dispatch's own operator-facing description, when it carried one. */\n label?: string;\n}\n\n/**\n * A loss a surface can render: the text's inputs plus the dispatch's `requestId`, which keys the box and is what `dismiss` takes.\n *\n * @category CommandDelay\n */\nexport interface CommandLossEntry extends CommandLossLike {\n id: string;\n}\n\n/**\n * One lost dispatch as the rail renders it, plus its command's rail axes.\n *\n * @category CommandDelay\n */\nexport interface RailLoss extends CommandLossEntry {\n tags: RailTags;\n}\n\n/**\n * What the operator is told about a command that went quiet. \"May have run\":\n * a dropped command and a reply lost on the way home look the same from here,\n * so pressing again may do the thing twice. Never \"refused\", which is the\n * game's verdict.\n *\n * @category CommandDelay\n */\nexport function commandLossSentence(loss: CommandLossLike): string {\n const subject = commandRefusalSubject(loss);\n const what = subject || loss.command || \"The command\";\n return `${what}: no reply. May have run.`;\n}\n","import { createElement, type ReactNode } from \"react\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\n/**\n * Props of {@link LiveRegion}.\n *\n * @category Typography\n */\nexport interface LiveRegionProps {\n /** The words to announce; leave empty until there is something to say. */\n children?: ReactNode;\n /** Names the region, for a region that also holds visible content. */\n \"aria-label\"?: string;\n /**\n * Keep the region off screen: an announcer, a bare `aria-live` region rather\n * than a `status`, for words whose visible form is drawn somewhere else.\n */\n visuallyHidden?: boolean;\n /**\n * Announce each addition on its own rather than re-reading the whole region.\n * For a region that collects entries (one per outcome), where a new entry\n * should not repeat every entry before it.\n */\n additionsOnly?: boolean;\n /**\n * Interrupt rather than announce: `aria-live=\"assertive\"`, and `role=\"alert\"`\n * where the region is on screen. For ABORT-class states and nothing softer.\n */\n assertive?: boolean;\n /** The element the region renders as. */\n as?: \"div\" | \"span\";\n className?: string;\n}\n\n/**\n * A live region that is in the document before anything is said in it.\n *\n * Assistive tech announces changes to a region, so one inserted already holding\n * its message is often never announced. Mount this for as long as the thing it\n * reports on is on screen, and put the words in when there is something to say.\n *\n * Polite unless `assertive`, which interrupts and is kept for ABORT-class states.\n *\n * @example\n * ```tsx\n * <LiveRegion>{outcome}</LiveRegion>\n * ```\n *\n * @category Typography\n */\nexport function LiveRegion({\n children,\n \"aria-label\": ariaLabel,\n visuallyHidden = false,\n additionsOnly = false,\n assertive = false,\n as = \"span\",\n className,\n}: LiveRegionProps) {\n const visibleRole = assertive ? \"alert\" : \"status\";\n const props = {\n role: visuallyHidden ? undefined : visibleRole,\n \"aria-live\": assertive ? \"assertive\" : \"polite\",\n \"aria-atomic\": additionsOnly ? \"false\" : \"true\",\n \"aria-label\": ariaLabel,\n className,\n \"data-live-region\": \"\",\n } as const;\n return visuallyHidden ? (\n <VisuallyHidden as={as} {...props}>\n {children}\n </VisuallyHidden>\n ) : (\n createElement(as, props, children)\n );\n}\n","/**\n * What an instrument draws when its figure is a reading rather than a bare\n * quantity: the held mark, the model's two bounds, and the sentence a\n * screen reader hears.\n *\n * The vocabulary is `<Unit>`'s and `<Meter>`'s, in SVG: the held square is a tspan and\n * the bound is a line, with the same hue, shape and wording.\n */\nimport type { ReactNode } from \"react\";\nimport { RECKONING_MARK, type ReckoningKind } from \"./reckoningMarkSpec\";\n\n/** How tall a lining digit stands above its baseline, and how far the mark's glyph does, each as a share of its own font size. */\nconst DIGIT_HEIGHT_EM = 0.72;\nconst GLYPH_TOP_EM = 0.76;\n\n/**\n * The held mark, as an instrument can draw it.\n *\n * A superscript tspan in the readout's warning hue; a shape that is present or\n * absent, so nothing rests on telling amber from grey. Silent to a screen\n * reader: the instrument says it through {@link sayHeld}.\n *\n * Give `figureSize`, the font size of the figure it follows, wherever the\n * figure is much larger than the mark: the mark is then raised until its top\n * meets the top of the figure's digits. Without it the mark is raised by half\n * its own height, which is superscript only beside text of about its own size.\n *\n * @category Unit\n */\nexport function InstrumentHeldMark({\n size,\n kind = \"held\",\n figureSize,\n}: {\n size: number;\n kind?: ReckoningKind;\n figureSize?: number;\n}) {\n return (\n <tspan\n // Raised, so it reads as a mark on the figure rather than another figure.\n dy={\n figureSize === undefined\n ? -size * 0.55\n : -(figureSize * DIGIT_HEIGHT_EM - size * GLYPH_TOP_EM)\n }\n fontSize={size}\n fill={RECKONING_MARK[kind].color}\n data-held-mark=\"\"\n data-reckoning-mark={kind}\n >\n {RECKONING_MARK[kind].glyph}\n </tspan>\n );\n}\n\n/**\n * One end of the model's interval, drawn across the instrument's own track.\n *\n * Two marks, never a shaded interval, as on `<Meter>`. The caller owns the\n * geometry; what is shared is how it looks.\n *\n * @category Unit\n */\nexport function InstrumentBound({\n end,\n x1,\n y1,\n x2,\n y2,\n}: {\n end: \"lo\" | \"hi\";\n x1: number;\n y1: number;\n x2: number;\n y2: number;\n}) {\n return (\n <line\n data-bound={end}\n x1={x1}\n y1={y1}\n x2={x2}\n y2={y2}\n stroke=\"var(--color-text-primary)\"\n strokeWidth={2}\n opacity={0.62}\n pointerEvents=\"none\"\n />\n );\n}\n\n/**\n * The accessible name with the held or modelled mark's words after it.\n * Nothing is appended when the figure is current.\n *\n * @category Unit\n */\nexport function sayHeld(name: string, caption: string | null): string {\n return caption === null ? name : `${name}, ${caption}`;\n}\n\n/**\n * The attribute that marks an element whose accessible name ends with a\n * {@link sayHeld} caption. Spread it on the element that carries that name.\n *\n * @category Unit\n */\nexport function heldNameMarker(caption: string | null): {\n \"data-currency-in-name\"?: \"\";\n} {\n return caption === null ? {} : { \"data-currency-in-name\": \"\" };\n}\n\n/**\n * What an instrument shows in place of a needle when the reading carries no\n * number at all.\n *\n * A needle parked at the bottom of the scale would be a reading, so an\n * instrument with nothing to point at draws no pointer.\n *\n * @category Unit\n */\nexport function InstrumentNoFigure({\n x,\n y,\n size,\n children,\n}: {\n x: number;\n y: number;\n size: number;\n children: ReactNode;\n}) {\n return (\n <text\n x={x}\n y={y}\n textAnchor=\"middle\"\n fontSize={size}\n fill=\"var(--color-text-muted)\"\n >\n {children}\n </text>\n );\n}\n","/**\n * How the rail DRAWS an entry, given what the entry says it is.\n *\n * The three axes and the derivations that fill them in live in the SDK\n * (`@ksp-gonogo/sitrep-sdk`'s `rail-tags.ts`), because they are read off what\n * the mod declares. This file is the other half: one accessor per axis, and a\n * TABLE saying which combination each renderer draws.\n *\n * | direction | continuity | delivery | a real example | drawn by |\n * |-----------|------------|------------------|---------------------------------|--------------------|\n * | command | discrete | acked | staging, an action group | `in-flight-row` |\n * | command | continuous | acked | fly-by-wire, ack = the readback | `continuous-strip` |\n * | telemetry | continuous | fire-and-forget | radio voice | `continuous-strip` |\n * | telemetry | discrete | fire-and-forget | a science result sent home | `in-flight-row` |\n *\n * The table names a RENDERER (a component). WHICH MARK it draws follows from\n * the data: both continuous rows share one strip, lines for a value against a\n * readback, a trace for an amplitude history.\n *\n * A combination with no renderer reads as `null`, `unrepresentedRailTags()`\n * names it, and an entry carrying it is reported rather than silently omitted\n * or drawn wrongly. A new row costs an entry here plus the renderer it names.\n *\n * Each axis drives exactly ONE visual property.\n */\n\nimport type {\n RailContinuity,\n RailDelivery,\n RailDirection,\n RailTags,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport { hasHost, logger } from \"@ksp-gonogo/sitrep-sdk\";\n\nexport type {\n RailContinuity,\n RailDelivery,\n RailDirection,\n RailTags,\n} from \"@ksp-gonogo/sitrep-sdk\";\n\n/** The CONTINUITY axis, and only it: a point travelling, or a span lying along. */\nexport function railMark(tags: RailTags): \"dot\" | \"ribbon\" {\n return tags.continuity === \"continuous\" ? \"ribbon\" : \"dot\";\n}\n\n/** The DELIVERY axis, and only it: whether anything is drawn coming back. */\nexport function railDrawsReturnLeg(tags: RailTags): boolean {\n return tags.delivery === \"acked\";\n}\n\n/** The DIRECTION axis, and only it: which way along the rail the entry runs. */\nexport function railFlow(tags: RailTags): \"outbound\" | \"inbound\" {\n return tags.direction === \"command\" ? \"outbound\" : \"inbound\";\n}\n\n/**\n * The DIRECTION axis, and only it, as a theme token NAME (no `var()` wrapper):\n * accent for orders leaving, info for news arriving.\n */\nexport function railToneToken(tags: RailTags): string {\n return tags.direction === \"command\"\n ? \"--color-accent-fg\"\n : \"--color-info-mark\";\n}\n\n/** Every value of each axis, in the order the table above reads. */\nconst DIRECTIONS: readonly RailDirection[] = [\"command\", \"telemetry\"];\nconst CONTINUITIES: readonly RailContinuity[] = [\"discrete\", \"continuous\"];\nconst DELIVERIES: readonly RailDelivery[] = [\"acked\", \"fire-and-forget\"];\n\n/** One combination, spelled as the table's key; a template-literal type, so the table cannot hold a typo. */\nexport type RailTagKey = `${RailDirection}/${RailContinuity}/${RailDelivery}`;\n\n/**\n * A handle's three rail axes as one `direction/continuity/delivery` string, e.g. `command/discrete/acked`.\n *\n * @category CommandDelay\n */\nexport function railTagKey(tags: RailTags): RailTagKey {\n return `${tags.direction}/${tags.continuity}/${tags.delivery}`;\n}\n\n/**\n * What actually draws an entry. Two components, and there are only two:\n *\n * - `in-flight-row`: a row in `InFlightList`, the discrete queue\n * - `continuous-strip`: `ControlDelayStream`, the three-zone graph, whichever\n * mark the entry's data calls for inside it\n */\nexport type RailRenderer = \"in-flight-row\" | \"continuous-strip\";\n\n/** Which renderer draws which combination. `Partial`: an absent combination has NO renderer, never a fallback. */\nconst RAIL_RENDERERS: Partial<Record<RailTagKey, RailRenderer>> = {\n \"command/discrete/acked\": \"in-flight-row\",\n \"command/continuous/acked\": \"continuous-strip\",\n \"telemetry/continuous/fire-and-forget\": \"continuous-strip\",\n \"telemetry/discrete/fire-and-forget\": \"in-flight-row\",\n};\n\n/**\n * The renderer for an entry, or `null` when nothing draws that combination. A\n * caller handed `null` reports it ({@link reportUnrepresentedRail}) rather than\n * rendering nothing quietly.\n */\nexport function railRendererFor(tags: RailTags): RailRenderer | null {\n return RAIL_RENDERERS[railTagKey(tags)] ?? null;\n}\n\n/** Every combination of the three axes: the whole product, eight of them. */\nexport function allRailTags(): RailTags[] {\n const out: RailTags[] = [];\n for (const direction of DIRECTIONS)\n for (const continuity of CONTINUITIES)\n for (const delivery of DELIVERIES)\n out.push({ direction, continuity, delivery });\n return out;\n}\n\n/** The combinations nothing can draw, as a value the tree can assert on. */\nexport function unrepresentedRailTags(): RailTags[] {\n return allRailTags().filter((tags) => railRendererFor(tags) === null);\n}\n\n/** Combinations already reported, keyed by combination rather than entry, so a frame-rate re-render says it once. */\nconst reportedUnrepresented = new Set<RailTagKey>();\n\n/**\n * Reports that an entry declared something the rail cannot draw, naming the\n * combination and who declared it. Never throws, so the widget survives. Falls\n * back to `console.error` without a host, because the sdk's `logger` throws\n * when none is installed.\n */\nexport function reportUnrepresentedRail(tags: RailTags, who: string): void {\n const key = railTagKey(tags);\n if (reportedUnrepresented.has(key)) return;\n reportedUnrepresented.add(key);\n const message =\n `Delay rail entry \"${who}\" is tagged ${key}, and no renderer draws that ` +\n `combination, so it will not appear on the rail. Declare a renderer for it ` +\n `in ui-kit's railTags.ts, or correct the entry's tags.`;\n if (hasHost()) logger.error(message);\n else console.error(message);\n}\n\n/** Test-only: forget what has been reported. Not on the published barrel. */\nexport function resetUnrepresentedRailReports(): void {\n reportedUnrepresented.clear();\n}\n","/**\n * The RIBBON mark's geometry: a continuous entry's amplitude history as an open\n * waveform trace, in its own 16-unit-tall box. A pure module, so the drawing can\n * be asserted directly.\n */\n\n/** The mark's own coordinate box: 100 wide by 16 tall, scaled by the caller into whatever band it has. */\nexport const WAVE_VB_H = 16;\n/**\n * The centre line of the ribbon mark's box, in box units.\n *\n * @category CommandDelay\n */\nexport const WAVE_MID_Y = WAVE_VB_H / 2;\n/**\n * How far a full-scale sample reaches either side of the centre line, in box units. Short: the rail is a band, not a meter.\n *\n * @category CommandDelay\n */\nexport const WAVE_HALF_H = 5.5;\n/**\n * The NOMINAL distance between turning points (half a period), in box units.\n * Fixed in the DRAWING rather than taken from the capture rate, so the trace\n * reads as a wave at any capture density rather than a solid hatch or a flat\n * line.\n */\nconst WAVE_STEP = 2;\n\nconst clamp01 = (v: number): number =>\n !Number.isFinite(v) ? 0 : v < 0 ? 0 : v > 1 ? 1 : v;\n\n/**\n * How far along the rail the drawn trace reaches: one light-time out at most,\n * and only as far as there is history. A MEASUREMENT, not a drawing choice:\n * samples older than the span have arrived and are dropped.\n */\nexport function waveformExtentX(\n amplitudes: readonly number[],\n spanSamples: number,\n boundaryX: number,\n): number {\n if (!(spanSamples > 0)) return 0;\n return (\n (Math.min(amplitudes.length - 1, spanSamples) / spanSamples) * boundaryX\n );\n}\n\n/**\n * How many turning points the trace is entitled to: the tighter of the drawing\n * pitch (`WAVE_STEP`) and the samples the gap holds. A trace may be coarse; it\n * may not invent turning points it has no samples for. The resolution limit is\n * floored at two segments, since one cannot cross the centre line.\n */\nfunction waveformSteps(\n amplitudes: readonly number[],\n spanSamples: number,\n extentX: number,\n): number {\n const resolvable = Math.min(amplitudes.length, spanSamples);\n return Math.min(\n Math.max(1, Math.ceil(extentX / WAVE_STEP)),\n Math.max(2, Math.round(resolvable)),\n );\n}\n\n/**\n * Turn a newest-last amplitude ring into an open waveform TRACE crossing the\n * band's centre, each turning point's height a sample's amplitude, at a fixed\n * pitch so the period stays visible. Silence is a flat line down the middle:\n * the key is open and nobody is speaking.\n *\n * Each sample is drawn at its age, where that audio is on its way across the\n * gap, so a short history draws a trace that stops short of the boundary.\n * Keep a longer history to fill it.\n *\n * @category CommandDelay\n */\nexport function waveformPath(\n amplitudes: readonly number[],\n spanSamples: number,\n boundaryX: number,\n): string {\n if (amplitudes.length === 0) return \"\";\n const extentX = waveformExtentX(amplitudes, spanSamples, boundaryX);\n // A trace of no length is a single vertex, not empty: a key that has just opened IS a crossing.\n if (!(extentX > 0)) return `M0.00,${WAVE_MID_Y.toFixed(2)}`;\n const steps = waveformSteps(amplitudes, spanSamples, extentX);\n const points: string[] = [];\n for (let k = 0; k <= steps; k++) {\n const x = Math.min((k / steps) * extentX, extentX);\n // Newest at this end: age 0 is `now`, and x walks back through the ring.\n const age = Math.min(\n Math.round((x / boundaryX) * spanSamples),\n amplitudes.length - 1,\n );\n const a = clamp01(amplitudes[amplitudes.length - 1 - age]);\n const dy = (k % 2 === 0 ? -1 : 1) * a * WAVE_HALF_H;\n points.push(`${x.toFixed(2)},${(WAVE_MID_Y + dy).toFixed(2)}`);\n }\n return `M${points.join(\" L\")}`;\n}\n","import { type Reading, type Value, value } from \"@ksp-gonogo/sitrep-sdk\";\nimport { useId } from \"react\";\nimport styled from \"styled-components\";\nimport {\n heldNameMarker,\n InstrumentHeldMark,\n sayHeld,\n} from \"../instrumentCurrency\";\nimport { resolveCurrency } from \"../readingCurrency\";\nimport { useTooltip } from \"../Tooltip\";\nimport { Unit } from \"../Unit\";\nimport { writeQuantity } from \"../units\";\nimport { withDelayCurrency } from \"./delayCurrency\";\nimport {\n type RailTags,\n railDrawsReturnLeg,\n railFlow,\n railRendererFor,\n railTagKey,\n railToneToken,\n reportUnrepresentedRail,\n} from \"./railTags\";\nimport { WAVE_VB_H, waveformExtentX, waveformPath } from \"./waveformPath\";\n\n/**\n * One sample on a control axis's trace in {@link ControlDelayStream}.\n *\n * @category CommandDelay\n */\nexport interface ControlStreamSample {\n /** Seconds since issue: 0 = now (left), increasing rightward. */\n age: number;\n /** Value in the shared 0..1 band. */\n value: number;\n}\n\n/**\n * One control axis drawn by {@link ControlDelayStream}: what is still crossing\n * to the craft, what has come back, and the value commanded now.\n *\n * @category CommandDelay\n */\nexport interface ControlStreamDatum {\n id: string;\n /** The axis's name, e.g. \"Throttle\". */\n label: string;\n /** One-way delay in seconds; the strip spans three times this. Below {@link STREAM_MIN_DELAY_SECONDS}, or `null`, nothing is drawn. */\n oneWaySeconds: number | null;\n /** Commanded values still on their way to the craft. */\n inTransit: ControlStreamSample[];\n /** Values the craft has confirmed back. */\n echo: ControlStreamSample[];\n /** `null` while nothing has been commanded on the axis. */\n current: number | null;\n /**\n * What this entry IS on the three axes, in full, and NOT optional. A control\n * axis gets them from `railTagsForControlAxis(writeCommand)`. A\n * `fire-and-forget` entry gets the outgoing zone only, since nothing comes\n * back to draw.\n */\n tags: RailTags;\n}\n\n/**\n * A continuous entry with amplitude history and no readback: the operator's\n * voice crossing the gap, drawn as the RIBBON mark in the outgoing zone. It\n * differs from {@link ControlStreamDatum} in its data: loudness per chunk, no\n * value and no echo.\n *\n * @category CommandDelay\n */\nexport interface ControlRibbonDatum {\n id: string;\n /** What the ribbon IS, in a sentence, e.g. \"Your transmission crossing to Odyssey\". */\n label: string;\n /** One-way delay seconds, read the same way a stream datum's is. */\n oneWaySeconds: number | null;\n /**\n * The amplitude history, one value per sample in 0..1, newest last. Each\n * sample is drawn at its age, so a history shorter than the trip draws a\n * trace that stops short; keep a longer history to fill it.\n */\n amplitudes: readonly number[];\n /**\n * How many samples span the trip to one light-time. Defaults to the whole\n * array. FRACTIONAL below one and never floored: a low-orbit light-time can be\n * a fraction of a single sample.\n */\n spanSamples?: number;\n /**\n * What this entry IS on the three axes, in full, and NOT optional. A\n * producer builds them with `railTagsForTelemetry(continuity)`.\n */\n tags: RailTags;\n}\n\n/**\n * The three sizes of {@link ControlDelayStream}. See {@link ControlDelayStreamProps.variant}.\n *\n * @category CommandDelay\n */\nexport type ControlDelayStreamVariant = \"inline\" | \"rail\" | \"expanded\";\n\n/**\n * Props for {@link ControlDelayStream}.\n *\n * @category CommandDelay\n */\nexport interface ControlDelayStreamProps {\n /** All of a widget's local control axes on ONE graph. */\n streams: ControlStreamDatum[];\n /**\n * Continuous entries with no readback, drawn as ribbons in the outgoing zone\n * of the SAME graph, with nothing past the first boundary.\n */\n ribbons?: ControlRibbonDatum[];\n /** Accessible label for the graph. Defaults to \"Controls in flight\". */\n ariaLabel?: string;\n /**\n * `\"inline\"` (default, 40px) is the in-widget rendering; `\"rail\"` (16px, no\n * labels) is the collapsed drag-bar strip; `\"expanded\"` is the full-bleed,\n * taller graph with zone labels, a legend and a readout.\n */\n variant?: ControlDelayStreamVariant;\n /**\n * The one-way delay as the reading it arrived in, so the T and 2T figures\n * draw held while `comms.delay` is quiet. Omitted, they draw as current.\n */\n delayReading?: Reading<Value<\"s\">> | null;\n}\n\n/**\n * The one-way delay, in seconds, under which {@link ControlDelayStream} draws no\n * command streams. Ribbons draw at any positive delay.\n *\n * @category CommandDelay\n */\nexport const STREAM_MIN_DELAY_SECONDS = 0.05;\nconst DEVIATION_EPSILON = 0.02;\n\n/** Soft distinct hues, in axis order (throttle, pitch, yaw, roll, ...). Wraps past four. */\nconst STREAM_TOKENS = [\n \"--color-data-1\",\n \"--color-data-2\",\n \"--color-data-3\",\n \"--color-data-4\",\n] as const;\n\n// viewBox units. Short height by design. Padding keeps strokes off the edges.\nconst VB_W = 100;\nconst VB_H = 30;\nconst PAD_X = 1.5;\nconst PAD_T = 2;\nconst PAD_B = 4;\nconst PLOT_H = VB_H - PAD_T - PAD_B;\n\nconst xAt = (age: number, span: number, padX: number): number =>\n padX + (span <= 0 ? 0 : Math.min(1, age / span)) * (VB_W - padX * 2);\nconst yAt = (value: number): number =>\n PAD_T + (1 - Math.max(0, Math.min(1, value))) * PLOT_H;\n\n/** The stroke inset, which only the in-widget inline variant keeps. */\nconst padXFor = (variant: ControlDelayStreamVariant): number =>\n variant === \"inline\" ? PAD_X : 0;\n\n/**\n * Where one light-time sits for a ribbon, in the graph's viewBox x units (100\n * wide): the T divider, a third of the plot. It never moves with delivery;\n * delivery only decides whether anything is drawn past it.\n *\n * @category CommandDelay\n */\nexport function ribbonBoundaryX(\n variant: ControlDelayStreamVariant = \"inline\",\n): number {\n const padX = padXFor(variant);\n return (VB_W - padX * 2) / 3;\n}\n\n/**\n * An entry whose declared combination NOTHING draws: zero ink, an addressable\n * marker (`data-rail-unrepresented`), and one report per combination through\n * {@link reportUnrepresentedRail}. Reported in render, not an effect, because a\n * static render runs no effects; the reporter is idempotent.\n */\nfunction UnrepresentedRailEntry({\n tags,\n who,\n}: {\n tags: RailTags;\n who: string;\n}) {\n reportUnrepresentedRail(tags, who);\n return <g data-rail-unrepresented={railTagKey(tags)} data-rail-entry={who} />;\n}\n\nfunction polyline(points: { x: number; y: number }[]): string {\n return points\n .map((p, i) => `${i === 0 ? \"M\" : \"L\"}${p.x.toFixed(2)},${p.y.toFixed(2)}`)\n .join(\" \");\n}\n\n/**\n * Clip the confirmed-echo line so it BEGINS exactly at the 2T divider: nothing\n * is confirmed before it arrives, so earlier samples drop and an interpolated\n * vertex lands on the divider.\n */\nfunction clipToConfirmed(\n echo: ControlStreamSample[],\n boundaryAge: number,\n): ControlStreamSample[] {\n if (echo.length === 0 || echo[0].age >= boundaryAge) return echo;\n const after = echo.findIndex((s) => s.age >= boundaryAge);\n if (after === -1) return []; // nothing confirmed yet\n const before = echo[after - 1];\n const first = echo[after];\n const span = first.age - before.age || 1;\n const t = (boundaryAge - before.age) / span;\n const value = before.value + t * (first.value - before.value);\n return [{ age: boundaryAge, value }, ...echo.slice(after)];\n}\n\n/**\n * Clip a line so it ENDS exactly at the outgoing-stage boundary (`oneWay`, the\n * same value the T divider is drawn from), the mirror of `clipToConfirmed`.\n *\n * What a `fire-and-forget` entry gets for its SOLID leg. An interpolated vertex\n * is placed on the boundary itself so the line's end lands ON the divider rather\n * than short of or past it; what happens immediately past the divider is\n * {@link OUTGOING_TAIL}'s business, and it is not this line.\n */\nfunction clipToOutgoing(\n samples: ControlStreamSample[],\n boundaryAge: number,\n): ControlStreamSample[] {\n if (samples.length === 0) return samples;\n const last = samples[samples.length - 1];\n if (last.age <= boundaryAge) return samples;\n const after = samples.findIndex((s) => s.age > boundaryAge);\n const kept = samples.slice(0, after);\n const first = samples[after];\n const before = kept[kept.length - 1];\n if (!before) return [{ age: boundaryAge, value: first.value }];\n const span = first.age - before.age || 1;\n const t = (boundaryAge - before.age) / span;\n return [\n ...kept,\n {\n age: boundaryAge,\n value: before.value + t * (first.value - before.value),\n },\n ];\n}\n\n/**\n * How far past the T divider a fire-and-forget leg trails off, as a fraction of\n * ONE light-time. A sample past the boundary has ARRIVED, so the trace fades\n * rather than stopping dead; short enough to read as an ending, not a second\n * leg.\n */\nconst OUTGOING_TAIL = 0.25;\n\n/** Commanded value interpolated at `age` (local copy; ui-kit imports no model). */\nfunction commandedAt(\n inTransit: ControlStreamSample[],\n age: number,\n): number | null {\n if (inTransit.length === 0) return null;\n const first = inTransit[0];\n const last = inTransit[inTransit.length - 1];\n if (age <= first.age) return first.value;\n if (age >= last.age) return last.value;\n for (let i = 1; i < inTransit.length; i++) {\n const b = inTransit[i];\n if (age <= b.age) {\n const a = inTransit[i - 1];\n const t = (age - a.age) / (b.age - a.age || 1);\n return a.value + t * (b.value - a.value);\n }\n }\n return last.value;\n}\n\nfunction StreamPaths({\n stream,\n span,\n index,\n padX,\n oneT,\n twoT,\n}: {\n stream: ControlStreamDatum;\n span: number;\n index: number;\n padX: number;\n /** The T stage boundary AGE, where a fire-and-forget entry's leg out ends. */\n oneT: number;\n /** The 2T stage boundary AGE, the same value the 2T divider is drawn from. */\n twoT: number;\n}) {\n const token = STREAM_TOKENS[index % STREAM_TOKENS.length];\n const colour = `var(${token})`;\n // Per-instance ids: `url(#id)` resolves to the first match in the document, so a shared literal lets one instance's gradient win for both.\n const uid = useId();\n const gradId = `cds-ramp-${uid}-${index}`;\n const fillId = `cds-fill-${uid}-${index}`;\n const tailId = `cds-tail-${uid}-${index}`;\n /*\n * Whether this strip draws the entry at all comes from the renderer table. A\n * combination nothing draws is REPORTED, ahead of the geometry so an empty\n * buffer is reported too.\n */\n const renderer = railRendererFor(stream.tags);\n if (renderer !== \"continuous-strip\") {\n return renderer === null ? (\n <UnrepresentedRailEntry tags={stream.tags} who={stream.id} />\n ) : null;\n }\n /*\n * DELIVERY alone decides this. Acked, the line runs the whole strip against\n * its confirmed echo; fire-and-forget, it gets the leg out plus the short\n * dissolving tail, and no return leg. The dividers never move.\n */\n const returnLeg = railDrawsReturnLeg(stream.tags);\n const outgoing = returnLeg\n ? stream.inTransit\n : clipToOutgoing(stream.inTransit, oneT);\n const cmd = outgoing.map((s) => ({\n x: xAt(s.age, span, padX),\n y: yAt(s.value),\n }));\n if (cmd.length === 0) return null;\n /*\n * The fire-and-forget tail: the segment between T and the tail's end, built\n * from the two existing clips so it starts on the vertex the solid leg ends\n * on. No sample moves; every vertex keeps the x its own age gives it.\n */\n const tail = returnLeg\n ? []\n : clipToOutgoing(\n clipToConfirmed(stream.inTransit, oneT),\n oneT * (1 + OUTGOING_TAIL),\n );\n const tailPts = tail.map((s) => ({\n x: xAt(s.age, span, padX),\n y: yAt(s.value),\n }));\n // A gradient with no extent paints one flat colour, so a zero-width tail is not drawn.\n const drawsTail =\n tailPts.length > 1 && tailPts[tailPts.length - 1].x > tailPts[0].x;\n\n // Low-alpha glow under the commanded line, fading downward.\n const areaPath = `${polyline(cmd)} L${cmd[cmd.length - 1].x.toFixed(2)},${(PAD_T + PLOT_H).toFixed(2)} L${cmd[0].x.toFixed(2)},${(PAD_T + PLOT_H).toFixed(2)} Z`;\n\n const confirmedEcho = returnLeg ? clipToConfirmed(stream.echo, twoT) : [];\n const echoPts = confirmedEcho.map((s) => ({\n x: xAt(s.age, span, padX),\n y: yAt(s.value),\n }));\n const expectedPts = confirmedEcho.map((s) => ({\n x: xAt(s.age, span, padX),\n y: yAt(commandedAt(stream.inTransit, s.age) ?? s.value),\n }));\n // The deviation treatment starts at the first confirmed sample that diverges from the commanded path.\n const divergeIndex = confirmedEcho.findIndex((s) => {\n const c = commandedAt(stream.inTransit, s.age);\n return c !== null && Math.abs(s.value - c) > DEVIATION_EPSILON;\n });\n const diverged = divergeIndex !== -1;\n // The two segments share the diverging vertex, so the path reads as one line.\n const confirmedPts = diverged ? echoPts.slice(0, divergeIndex + 1) : echoPts;\n const deviationPts = diverged ? echoPts.slice(divergeIndex) : [];\n const deviationExpectedPts = diverged ? expectedPts.slice(divergeIndex) : [];\n // A divergence at the very first sample leaves no confirmed prefix to draw.\n const hasConfirmedPrefix = !diverged || divergeIndex > 0;\n\n return (\n <g data-stream-group={stream.id} data-return-leg={returnLeg}>\n <defs>\n {/* Confidence ramp: muted left (least known) to clear right, kept faint so the rail reads as texture. */}\n <linearGradient id={gradId} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">\n <stop offset=\"0\" stopColor={colour} stopOpacity=\"0.10\" />\n <stop offset=\"1\" stopColor={colour} stopOpacity=\"0.40\" />\n </linearGradient>\n <linearGradient id={fillId} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0\" stopColor={colour} stopOpacity=\"0.22\" />\n <stop offset=\"1\" stopColor={colour} stopOpacity=\"0\" />\n </linearGradient>\n {/*\n The tail's dissolve starts at the ramp's right-hand alpha. In user\n space, because a flat line's bounding box has no height and an\n objectBoundingBox ramp would not paint it.\n */}\n {drawsTail && (\n <linearGradient\n id={tailId}\n gradientUnits=\"userSpaceOnUse\"\n x1={tailPts[0].x}\n y1=\"0\"\n x2={tailPts[tailPts.length - 1].x}\n y2=\"0\"\n >\n <stop offset=\"0\" stopColor={colour} stopOpacity=\"0.40\" />\n <stop offset=\"1\" stopColor={colour} stopOpacity=\"0\" />\n </linearGradient>\n )}\n </defs>\n <path\n data-role=\"area\"\n d={areaPath}\n fill={`url(#${fillId})`}\n stroke=\"none\"\n />\n <path\n data-role=\"commanded\"\n data-stream={stream.id}\n d={polyline(cmd)}\n fill=\"none\"\n stroke={`url(#${gradId})`}\n strokeWidth=\"0.8\"\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n {drawsTail && (\n <path\n data-role=\"commanded-tail\"\n data-stream={stream.id}\n d={polyline(tailPts)}\n fill=\"none\"\n stroke={`url(#${tailId})`}\n strokeWidth=\"0.8\"\n strokeLinejoin=\"round\"\n strokeLinecap=\"round\"\n />\n )}\n {confirmedPts.length > 0 && hasConfirmedPrefix && (\n <path\n data-role=\"echo\"\n data-stream={stream.id}\n d={polyline(confirmedPts)}\n fill=\"none\"\n stroke={colour}\n strokeWidth=\"0.8\"\n strokeLinecap=\"round\"\n />\n )}\n {diverged && (\n <path\n data-role=\"deviation-actual\"\n data-stream={stream.id}\n data-deviation=\"true\"\n d={polyline(deviationPts)}\n fill=\"none\"\n stroke=\"var(--color-warn-mark)\"\n strokeWidth=\"1\"\n strokeLinecap=\"round\"\n />\n )}\n {diverged && (\n <path\n data-role=\"deviation-expected\"\n data-stream={stream.id}\n d={polyline(deviationExpectedPts)}\n fill=\"none\"\n stroke={colour}\n strokeWidth=\"0.8\"\n strokeDasharray=\"2 1.5\"\n />\n )}\n </g>\n );\n}\n\n/**\n * The RIBBON mark: one continuous entry's amplitude history, lying along the\n * OUTGOING zone and fading there. Each axis lands on one property:\n *\n * - CONTINUITY puts it on this strip at all; a discrete datum draws nothing\n * - DELIVERY keeps it in the outgoing zone, ending at the T divider\n * - DIRECTION picks the tone and the fade direction: a command dissolves\n * toward the target, telemetry is clearest where it lands\n *\n * Drawn in the waveform's own 16-unit box and scaled into the plot band.\n */\nfunction RibbonMark({\n ribbon,\n padX,\n}: {\n ribbon: ControlRibbonDatum;\n padX: number;\n}) {\n // Per-instance, since `url(#id)` resolves to the first match in the document.\n const fadeId = `cds-ribbon-fade-${useId()}`;\n const tags = ribbon.tags;\n // The same renderer table `StreamPaths` asks.\n const renderer = railRendererFor(tags);\n if (renderer !== \"continuous-strip\") {\n return renderer === null ? (\n <UnrepresentedRailEntry tags={tags} who={ribbon.id} />\n ) : null;\n }\n\n const boundaryX = (VB_W - padX * 2) / 3;\n const samples = ribbon.amplitudes;\n const span = ribbon.spanSamples ?? samples.length;\n const path = waveformPath(samples, span, boundaryX);\n // A ribbon with nothing in it is not a crossing.\n if (path === \"\") return null;\n\n // The fade spans the trace's own length, at least a unit so the gradient has extent.\n const fadeX = Math.max(waveformExtentX(samples, span, boundaryX), 1);\n const tone = `var(${railToneToken(tags)})`;\n const outbound = railFlow(tags) === \"outbound\";\n\n return (\n <g\n data-ribbon-group={ribbon.id}\n // The waveform's 16-unit box mapped onto the plot band.\n transform={`translate(${padX} ${PAD_T}) scale(1 ${PLOT_H / WAVE_VB_H})`}\n >\n <defs>\n {/* The fade runs the way the entry does, in user space so a flat passage still paints. */}\n <linearGradient\n id={fadeId}\n gradientUnits=\"userSpaceOnUse\"\n x1={outbound ? 0 : fadeX}\n y1=\"0\"\n x2={outbound ? fadeX : 0}\n y2=\"0\"\n >\n {/* Nearer full strength than a filled ribbon would carry: a hairline\n trace at 0.55 is most of the way to invisible where a broad fill at\n the same value still read. */}\n <stop offset=\"0\" stopColor={tone} stopOpacity=\"0.9\" />\n <stop offset=\"1\" stopColor={tone} stopOpacity=\"0.1\" />\n </linearGradient>\n </defs>\n {/* Stroked in SCREEN units: the box is stretched to the widget's width\n AND scaled vertically into the band, so a scaled stroke would come out\n several times thicker across than it is tall and the near-vertical\n parts of the trace would fatten into a blob. */}\n <path\n data-role=\"ribbon\"\n data-ribbon={ribbon.id}\n d={path}\n fill=\"none\"\n stroke={`url(#${fadeId})`}\n strokeWidth=\"1\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n vectorEffect=\"non-scaling-stroke\"\n />\n </g>\n );\n}\n\n/**\n * The continuous sibling of {@link InFlightList}: one three-zone sparkline for\n * ALL of a widget's control axes. Now on the left, age to the right; outgoing, echo\n * and confirmed zones split by dividers at T and 2T; a muted-to-clear\n * confidence ramp; deviation in the confirmed zone as the expected path dashed\n * plus the actual path in the warning token; zone and delay labels on hover.\n * Renders `null` when the one-way delay is zero or unknown, and draws no command\n * streams under {@link STREAM_MIN_DELAY_SECONDS}; a ribbon draws at any\n * positive delay. It reads nothing itself: everything comes in through props.\n *\n * A continuous entry with no readback (the operator's voice) is drawn as a\n * ribbon in the outgoing zone of the same graph. Delivery only decides whether anything is drawn past the first\n * boundary; it never moves a boundary or picks a different picture.\n *\n * @category CommandDelay\n */\nexport function ControlDelayStream({\n streams,\n ribbons = [],\n ariaLabel = \"Controls in flight\",\n variant = \"inline\",\n delayReading,\n}: ControlDelayStreamProps) {\n // Before the early return, so the hook runs unconditionally.\n const dividerFadeId = `cds-divfade-${useId()}`;\n const delayHeld = delayReading?.state === \"held\";\n const delayCaption = delayHeld\n ? resolveCurrency(delayReading ?? null).caption\n : null;\n const { anchor, tip } = useTooltip(delayCaption);\n // Every entry crosses the same gap, so the first one's light-time serves the strip.\n const first = streams[0] ?? ribbons[0];\n const oneWay = first?.oneWaySeconds ?? null;\n if (!first || oneWay === null || oneWay <= 0) return null;\n const drawStreams = oneWay >= STREAM_MIN_DELAY_SECONDS;\n if (!drawStreams && ribbons.length === 0) return null;\n\n const span = 3 * oneWay;\n // Computed once and shared by the dividers and the line clips, so a line changes appearance only on a divider.\n const oneT = oneWay;\n const twoT = 2 * oneWay;\n const padX = padXFor(variant);\n const divX1 = xAt(oneT, span, padX);\n const divX2 = xAt(twoT, span, padX);\n /*\n * Nothing commanded yet: the zone labels show without hover, so an empty\n * strip does not read as missing data. The box itself never hides, so the\n * surface does not move under the operator's hands.\n */\n const quiet =\n streams.every((s) => s.inTransit.length === 0 && s.echo.length === 0) &&\n ribbons.every((r) => r.amplitudes.length === 0);\n\n return (\n <ControlDelayStream__Root\n data-oneway={oneWay}\n data-variant={variant}\n $variant={variant}\n {...anchor}\n >\n <ControlDelayStream__Svg\n $variant={variant}\n $quiet={quiet}\n role=\"img\"\n aria-label={sayHeld(ariaLabel, delayCaption)}\n {...heldNameMarker(delayCaption)}\n viewBox={`0 0 ${VB_W} ${VB_H}`}\n preserveAspectRatio=\"none\"\n >\n <defs>\n {/* The dividers fade downward, matching the under-line shading. */}\n <linearGradient\n id={dividerFadeId}\n gradientUnits=\"userSpaceOnUse\"\n x1=\"0\"\n y1={PAD_T}\n x2=\"0\"\n y2={PAD_T + PLOT_H}\n >\n <stop\n offset=\"0\"\n stopColor=\"var(--color-border-subtle)\"\n stopOpacity=\"0.9\"\n />\n <stop\n offset=\"1\"\n stopColor=\"var(--color-border-subtle)\"\n stopOpacity=\"0\"\n />\n </linearGradient>\n </defs>\n {(drawStreams ? streams : []).map((s, i) => (\n <StreamPaths\n key={s.id}\n stream={s}\n span={span}\n index={i}\n padX={padX}\n oneT={oneT}\n twoT={twoT}\n />\n ))}\n {ribbons.map((r) => (\n <RibbonMark key={r.id} ribbon={r} padX={padX} />\n ))}\n <line\n data-divider=\"t\"\n x1={divX1}\n x2={divX1}\n y1={PAD_T}\n y2={PAD_T + PLOT_H}\n stroke={`url(#${dividerFadeId})`}\n strokeWidth=\"0.4\"\n />\n <line\n data-divider=\"2t\"\n x1={divX2}\n x2={divX2}\n y1={PAD_T}\n y2={PAD_T + PLOT_H}\n stroke={`url(#${dividerFadeId})`}\n strokeWidth=\"0.4\"\n />\n {/* Hover-only labels, inline variant only; the svg's own aria-label carries the accessible name. */}\n {variant === \"inline\" && (\n <g data-role=\"hover-labels\">\n <text x={divX1} y={PAD_T - 0.4} textAnchor=\"middle\" fontSize=\"2\">\n {writeQuantity(value(\"s\", oneWay), { decimals: 1 })}\n {delayHeld && <InstrumentHeldMark size={1.2} />}\n </text>\n <text x={divX2} y={PAD_T - 0.4} textAnchor=\"middle\" fontSize=\"2\">\n {writeQuantity(value(\"s\", 2 * oneWay), { decimals: 1 })}\n {delayHeld && <InstrumentHeldMark size={1.2} />}\n </text>\n <text\n x={xAt(oneWay / 2, span, padX)}\n y={VB_H - 0.6}\n textAnchor=\"middle\"\n fontSize=\"2\"\n >\n outgoing\n </text>\n <text\n x={xAt(1.5 * oneWay, span, padX)}\n y={VB_H - 0.6}\n textAnchor=\"middle\"\n fontSize=\"2\"\n >\n echo\n </text>\n <text\n x={xAt(2.5 * oneWay, span, padX)}\n y={VB_H - 0.6}\n textAnchor=\"middle\"\n fontSize=\"2\"\n >\n confirmed\n </text>\n </g>\n )}\n </ControlDelayStream__Svg>\n {variant === \"expanded\" && (\n <>\n {/* HTML zone labels under the graph, with the T and 2T boundary times. */}\n <ControlDelayStream__Zones aria-hidden=\"true\">\n <span>\n outgoing <b>0</b>\n </span>\n <span>\n echo{\" \"}\n <b>\n <Unit\n value={withDelayCurrency(value(\"s\", oneWay), delayReading)}\n decimals={1}\n />\n </b>\n </span>\n <span>\n confirmed{\" \"}\n <b>\n <Unit\n value={withDelayCurrency(\n value(\"s\", 2 * oneWay),\n delayReading,\n )}\n decimals={1}\n />\n </b>\n </span>\n </ControlDelayStream__Zones>\n <ControlDelayStream__Legend aria-hidden=\"true\">\n {streams.map((s, i) => (\n <span key={s.id}>\n <i\n style={{\n background: `var(${STREAM_TOKENS[i % STREAM_TOKENS.length]})`,\n }}\n />\n {s.label}\n </span>\n ))}\n {ribbons.map((r) => (\n <span key={r.id} data-role=\"legend-ribbon\">\n <i\n style={{\n background: `var(${railToneToken(r.tags)})`,\n }}\n />\n {r.label}\n </span>\n ))}\n {/* Ribbons have no commanded path to deviate from. */}\n {streams.length > 0 && (\n <span data-role=\"legend-deviation\">\n <i style={{ background: \"var(--color-warn-mark)\" }} />\n off-command\n </span>\n )}\n </ControlDelayStream__Legend>\n </>\n )}\n {tip}\n </ControlDelayStream__Root>\n );\n}\n\nconst ControlDelayStream__Root = styled.div<{\n $variant: \"inline\" | \"rail\" | \"expanded\";\n}>`\n flex: 0 0 auto;\n width: 100%;\n ${({ $variant }) =>\n $variant === \"expanded\" &&\n \"display: flex; flex-direction: column; gap: var(--gap-delay-stream);\"}\n`;\n\nconst ControlDelayStream__Svg = styled.svg<{\n $variant: \"inline\" | \"rail\" | \"expanded\";\n $quiet?: boolean;\n}>`\n display: block;\n width: 100%;\n /* The rail height is the band the Panel reserves, not a size the graph chose. */\n height: ${({ $variant }) =>\n $variant === \"rail\"\n ? \"var(--panel-rail-band)\"\n : $variant === \"expanded\"\n ? \"86px\"\n : \"40px\"};\n\n /* Strokes are scaled by 30/16 against the stretched 30-unit viewBox, so the trace is a real pixel wide in the band. */\n ${({ $variant }) =>\n $variant === \"rail\"\n ? `[data-role=\"commanded\"],\n [data-role=\"echo\"],\n [data-role=\"deviation-expected\"] { stroke-width: 1.5; }\n [data-role=\"deviation-actual\"] { stroke-width: 1.88; }`\n : \"\"}\n\n\n [data-role=\"hover-labels\"] {\n opacity: ${({ $quiet }) => ($quiet ? 1 : 0)};\n fill: var(--color-text-muted);\n font-family: var(--font-family-mono);\n }\n &:hover [data-role=\"hover-labels\"] {\n opacity: 1;\n }\n`;\n\nconst ControlDelayStream__Zones = styled.div`\n display: flex;\n justify-content: space-between;\n /* The graph stays full-bleed; the zone labels inset to the standard content margin. */\n margin: 0 var(--gutter-panel);\n font-size: var(--font-size-caption);\n color: var(--color-text-muted);\n letter-spacing: 0.06em;\n\n b {\n color: var(--color-text-dim);\n font-weight: 600;\n font-variant-numeric: tabular-nums;\n }\n`;\n\nconst ControlDelayStream__Legend = styled.div`\n display: flex;\n flex-wrap: wrap;\n gap: var(--gap-legend);\n /* The legend sits at the standard content margin, with bottom room since the pinned rail has no padding. */\n margin: var(--outset-delay-legend);\n font-size: var(--font-size-caption);\n color: var(--color-text-muted);\n letter-spacing: 0.06em;\n\n span {\n display: inline-flex;\n align-items: center;\n gap: var(--gap-glyph);\n }\n i {\n display: inline-block;\n width: 10px;\n height: 2px;\n }\n`;\n","import type { Reading, Value } from \"@ksp-gonogo/sitrep-sdk\";\nimport type { UnitValue } from \"../readingCurrency\";\n\n/**\n * A figure worked out from the one-way delay (the delay itself, twice it),\n * carrying the delay reading's currency, so a `comms.delay` that has gone\n * quiet draws every figure derived from it held.\n */\nexport function withDelayCurrency(\n figure: Value<\"s\">,\n delay: Reading<Value<\"s\">> | null | undefined,\n): UnitValue<\"s\"> {\n if (delay?.state !== \"held\") return figure;\n return { ...delay, value: figure };\n}\n","import type { Reading, Value } from \"@ksp-gonogo/sitrep-sdk\";\nimport type { ReactNode } from \"react\";\nimport styled from \"styled-components\";\nimport { HeldFigure } from \"./HeldMark\";\nimport { MODELLED_TO_SCET, modelledBeyondReceived } from \"./readingCurrency\";\nimport { standsApart, writtenAs, writtenQuantity } from \"./standsApart\";\nimport { Unit } from \"./Unit\";\n\nconst ModelledAlongside__Figure = styled.span`\n margin-left: 0.6em;\n color: var(--color-text-muted);\n`;\n\n/**\n * Props for {@link ModelledAlongside} with a `Value` pair: a modelled quantity beside the quantity observed, drawn through {@link Unit}.\n *\n * @category Unit\n */\nexport interface ModelledQuantityAlongsideProps<UnitSymbol extends string> {\n /** The observation the caller has drawn just before this. */\n observed: Value<UnitSymbol> | null | undefined;\n /** The model's figure; nothing is drawn without one. */\n modelled: Value<UnitSymbol> | null | undefined;\n write?: undefined;\n}\n\n/**\n * Props for {@link ModelledAlongside} with any other figure: a modelled figure beside the figure observed, both written by `write`.\n *\n * @category Unit\n */\nexport interface ModelledFigureAlongsideProps<Figure> {\n /** The observation the caller has drawn just before this, written by `write`. */\n observed: Figure | null | undefined;\n /** The model's figure; nothing is drawn without one. */\n modelled: Figure | null | undefined;\n /** How the caller writes the observation, which is how the modelled figure is written and compared. */\n write: (figure: Figure) => string;\n}\n\n/**\n * A modelled figure drawn beside the observation it was carried from, never in\n * its place, with the modelled mark. The caller draws the observation first.\n *\n * Drawn only where the figure stands apart from the observation at the\n * precision both are drawn (see {@link standsApart}), so a model that agrees\n * with what was received to the last place drawn is not repeated. A `Value`\n * pair is drawn through {@link Unit}; anything else goes through `write`.\n *\n * @category Unit\n */\nexport function ModelledAlongside<UnitSymbol extends string>(\n props: ModelledQuantityAlongsideProps<UnitSymbol>,\n): ReactNode;\nexport function ModelledAlongside<Figure>(\n props: ModelledFigureAlongsideProps<Figure>,\n): ReactNode;\nexport function ModelledAlongside({\n observed,\n modelled,\n write,\n}:\n | ModelledFigureAlongsideProps<unknown>\n | ModelledQuantityAlongsideProps<string>) {\n if (modelled === null || modelled === undefined) return null;\n if (write === undefined) {\n const quantity = modelled as Value<string>;\n if (!standsApart(observed as Value<string>, quantity, writtenQuantity()))\n return null;\n return (\n <ModelledMark>\n <Unit value={quantity} />\n </ModelledMark>\n );\n }\n if (!standsApart(observed, modelled, writtenAs(write))) return null;\n return <ModelledMark>{write(modelled)}</ModelledMark>;\n}\n\nfunction ModelledMark({ children }: { children: ReactNode }) {\n return (\n <ModelledAlongside__Figure data-modelled-alongside=\"\">\n <HeldFigure data-held=\"\" kind=\"modelled\" caption={MODELLED_TO_SCET}>\n {children}\n </HeldFigure>\n </ModelledAlongside__Figure>\n );\n}\n\n/**\n * A reading's observation through {@link Unit}, followed by the model's figure\n * with the modelled mark where the model reaches past the newest data\n * received to a figure that reads apart from it. Shorthand for a {@link Unit} followed by a\n * {@link ModelledAlongside} fed from {@link modelledBeyondReceived}.\n *\n * @category Unit\n */\nexport function ReckonedUnit<UnitSymbol extends string>({\n value,\n}: {\n value: Reading<Value<UnitSymbol>>;\n}) {\n return (\n <>\n <Unit value={value} />\n <ModelledAlongside\n observed={value.value}\n modelled={modelledBeyondReceived(value)}\n />\n </>\n );\n}\n","import type { Reading, Value } from \"@ksp-gonogo/sitrep-sdk\";\nimport { formatDuration } from \"./formatDuration\";\nimport { HeldFigure } from \"./HeldMark\";\nimport { ModelledAlongside } from \"./ModelledAlongside\";\nimport { NULL_DISPLAY } from \"./NullValue\";\nimport {\n figureAttributes,\n modelledBeyondReceived,\n resolveCurrency,\n} from \"./readingCurrency\";\n\n/**\n * Props for {@link Countdown}.\n *\n * @category MissionDate\n */\nexport interface CountdownProps {\n /**\n * A DURATION in seconds: how long until, or how long since. A bare number is\n * accepted for client-computed durations.\n *\n * NOT `Value<\"ut\">`: an instant is a type error here. Subtract the instant\n * the screen is showing, from `useViewUt`, to turn an instant into a\n * duration.\n */\n value: Value<\"s\"> | Reading<Value<\"s\">> | number | null | undefined;\n /**\n * Prefix the launch-clock sign: `T−` counting down to the event, `T+` once\n * it has passed. Off by default: a \"how long until\" readout is not a clock.\n */\n clock?: boolean;\n /**\n * Count the last second in milliseconds rather than showing `0s`, for a cue\n * the operator acts ON, where `0s` for a whole second reads as stopped. Off by\n * default, where it would be false precision.\n */\n precise?: boolean;\n}\n\n/**\n * A duration read as a CLOCK: `1m 20s`, or `T−1m 20s` on a launch clock.\n * Which component renders a `Value<\"s\">` depends on what it MEANS:\n *\n * - {@link Unit} for a magnitude: a burn lasting 90 seconds\n * - `Countdown` for a clock, with the opt-in `T−` / `T+` prefix and\n * sub-second precision for a cue that would otherwise sit at `0s`\n * - {@link MissionDate} for an instant: a UT is not a length of time\n *\n * Handed a `Reading`, a countdown draws the model's reckoning where there is\n * one, so it keeps advancing while a model carries it, and freezes on the last\n * observation, with the held mark, otherwise. {@link Unit} does this only when\n * given `reckoned`. A current reading whose model reaches past the newest data\n * received draws its observation, and the model's figure beside it with the\n * modelled mark. An absent value renders\n * {@link NULL_DISPLAY}.\n *\n * @category MissionDate\n */\nexport function Countdown({\n value,\n clock = false,\n precise = false,\n}: CountdownProps) {\n // A bare number never carries currency, so it is split off before the resolver.\n const carried = typeof value === \"number\" ? undefined : value;\n const format = (duration: Value<\"s\"> | number) =>\n formatDuration(\n typeof duration === \"number\" ? duration : duration.magnitude,\n {\n ms: precise,\n sign: clock,\n },\n );\n const reading = carried != null && \"state\" in carried ? carried : undefined;\n const modelled = modelledBeyondReceived(reading);\n if (reading?.value !== undefined && modelled !== undefined) {\n return (\n <>\n {format(reading.value)}\n <ModelledAlongside\n observed={reading.value}\n modelled={modelled}\n write={format}\n />\n </>\n );\n }\n const currency = resolveCurrency(carried, { drawsReckoning: true });\n const { shown, held, mark, caption } = currency;\n const drawn = typeof value === \"number\" ? value : shown;\n if (drawn === undefined || drawn === null) return NULL_DISPLAY;\n const text = format(drawn);\n const figure = figureAttributes({ ...currency, shown: drawn });\n if (!held) return <span data-figure={figure[\"data-figure\"]}>{text}</span>;\n return (\n <HeldFigure {...figure} kind={mark ?? \"held\"} caption={caption}>\n {text}\n </HeldFigure>\n );\n}\n","import { type RefObject, useEffect, useRef, useState } from \"react\";\n\n/**\n * Pixel dimensions of an observed element, rounded down to whole pixels.\n *\n * @category Layout\n */\nexport interface ElementSize {\n w: number;\n h: number;\n}\n\n/**\n * Observe an element's content box and track its `{ w, h }` size.\n *\n * Attach `ref` to the element to measure. Zero-size measurements are ignored\n * and the result is rounded down to whole pixels. The element may come and go,\n * or be swapped for another: whichever one `ref` holds after a render is the\n * one observed. Where there is no `ResizeObserver` (jsdom) the size stays at\n * `initial`.\n *\n * @param initial - Seed size used until the first non-zero measurement\n * @returns `{ ref, size }`: attach `ref` to the element to measure\n *\n * @example\n * ```tsx\n * function Chart() {\n * const { ref, size } = useElementSize<HTMLDivElement>({ w: 200, h: 120 });\n * return <div ref={ref}>{size.w > 300 ? <WideChart /> : <NarrowChart />}</div>;\n * }\n * ```\n *\n * @category Layout\n */\nexport function useElementSize<\n MeasuredElement extends HTMLElement = HTMLDivElement,\n>(\n initial: ElementSize,\n): { ref: RefObject<MeasuredElement>; size: ElementSize } {\n const ref = useRef<MeasuredElement>(null);\n const [size, setSize] = useState<ElementSize>(initial);\n const [observed, setObserved] = useState<MeasuredElement | null>(null);\n\n // After every render, so an element that mounts late or is replaced is picked up; only a different element re-renders.\n useEffect(() => {\n if (ref.current !== observed) setObserved(ref.current);\n });\n\n useEffect(() => {\n if (!observed || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver((entries) => {\n for (const e of entries) {\n if (e.contentRect.width > 0 && e.contentRect.height > 0) {\n setSize({\n w: Math.floor(e.contentRect.width),\n h: Math.floor(e.contentRect.height),\n });\n }\n }\n });\n ro.observe(observed);\n return () => ro.disconnect();\n }, [observed]);\n\n return { ref, size };\n}\n","import type { RailDirection } from \"@ksp-gonogo/sitrep-sdk\";\nimport type { InFlightListItem } from \"./InFlightList\";\n\n/** The structural subset of an in-flight command that this mapping reads. */\nexport interface InFlightCommandLike {\n id: string;\n label: string;\n command: string;\n reachEtaSeconds: number | null;\n replyEtaSeconds: number | null;\n predictedPhase: InFlightListItem[\"phase\"];\n /** The issuing button's own terse glyph (\"PRO\", \"RET\"). Defaults to an abbreviation of `label`. */\n glyph?: string;\n /** Which way the entry crosses the link. Absent reads as a command going up. */\n direction?: RailDirection;\n /** The one-way delay the entry was sent under, which places an entry with no reply leg. Absent or `null` for one sent with no route known. */\n oneWaySeconds?: number | null;\n}\n\n/**\n * Fallback queue glyph: the significant word of the label, upper-cased and cut\n * to 4 chars (\"SAS Prograde\" -> \"PROG\"). An identity, never a status word.\n */\nexport function deriveGlyph(label: string): string {\n const words = label.trim().split(/\\s+/).filter(Boolean);\n const pick = words.length > 1 ? words[words.length - 1] : (words[0] ?? \"\");\n const letters = pick.replace(/[^a-zA-Z0-9]/g, \"\");\n return (letters || label).slice(0, 4).toUpperCase();\n}\n\n/** Progress anchored by phase, for when the eta geometry is missing (overdue, lost, zero delay, or a hand-built item). */\nexport const PHASE_PROGRESS: Record<InFlightListItem[\"phase\"], number> = {\n \"in-transit\": 0.18,\n \"awaiting-reply\": 0.5,\n due: 0.62,\n overdue: 0.82,\n lost: 0.95,\n};\n\n/**\n * An entry's TRUE progress along the 3-stage delay axis (0 just sent, 1 the\n * end of the 3T span). `T = replyEtaSeconds - reachEtaSeconds`, which holds even once\n * `reachEtaSeconds` goes negative, or the sent-under delay for an entry with no reply\n * leg; elapsed is `T - reachEtaSeconds`. Falls back to a phase anchor when `T` or the\n * reach eta is absent.\n */\nexport function journeyProgress(item: InFlightCommandLike): number {\n const reach = item.reachEtaSeconds;\n const t = oneWayOf(item);\n if (reach === null || t === null) return PHASE_PROGRESS[item.predictedPhase];\n const elapsed = t - reach;\n return Math.max(0, Math.min(1, elapsed / (3 * t)));\n}\n\n/** `T` from the reply leg when there is one, else from the delay the entry was sent under. */\nfunction oneWayOf(item: InFlightCommandLike): number | null {\n const reach = item.reachEtaSeconds;\n const reply = item.replyEtaSeconds;\n if (reach !== null && reply !== null && reply > reach) return reply - reach;\n if (reply === null && item.oneWaySeconds != null && item.oneWaySeconds > 0) {\n return item.oneWaySeconds;\n }\n return null;\n}\n\n/**\n * Maps a handle's in-flight commands (`useCommand().inFlight`) to\n * {@link InFlightListItem}s for {@link InFlightList}. While `in-transit` a row\n * counts down to the command REACHING the craft; after that, to the\n * acknowledgement coming back. `label` falls back to the command id.\n *\n * @category CommandDelay\n */\nexport function toInFlightListItems(\n items: readonly InFlightCommandLike[],\n): InFlightListItem[] {\n return items.map((item) => ({\n id: item.id,\n label: item.label || item.command,\n etaSeconds:\n item.predictedPhase === \"in-transit\"\n ? item.reachEtaSeconds\n : item.replyEtaSeconds,\n phase: item.predictedPhase,\n progress: journeyProgress(item),\n glyph: item.glyph ?? deriveGlyph(item.label || item.command),\n ...(item.direction === \"telemetry\" ? { flow: \"inbound\" as const } : {}),\n }));\n}\n","import { currentMode, value } from \"@ksp-gonogo/sitrep-sdk\";\nimport {\n type CSSProperties,\n type RefObject,\n useEffect,\n useId,\n useRef,\n useState,\n} from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { Countdown } from \"../Countdown\";\nimport { Tooltip } from \"../Tooltip\";\nimport { TONE_MARK, TONE_TEXT } from \"../tone\";\nimport { writeQuantity } from \"../units\";\nimport { useElementSize } from \"../useElementSize\";\nimport { deriveGlyph, PHASE_PROGRESS } from \"./toInFlightListItems\";\n\n/** Which ONE of the two delay readings a console draws. */\nexport type SignalDelayPresentation = \"badge\" | \"strip\" | \"none\";\n\nexport interface SignalDelayPresentationInput {\n /**\n * One-way separation in seconds, `null` when there is no measurable path.\n * Neither `null` nor a measured zero gets a badge.\n */\n oneWaySeconds: number | null;\n /**\n * Whether this console can put something in the strip. A read-only viewer\n * gets NEITHER reading at a long delay: no queue, and no cost it ever pays.\n */\n canQueue: boolean;\n /**\n * Force the badge whatever the magnitude, for a terminal in CHARACTER mode:\n * every keystroke goes on its own, so the strip has nothing to list.\n */\n alwaysBadge?: boolean;\n}\n\n/**\n * Which ONE of the two delay readings a console shows, given how far away the\n * other end is:\n *\n * - a BADGE is a standing readout of the separation, useful before anything\n * has been sent\n * - a STRIP (`InFlightList`) is one row per thing actually crossing, useful\n * once something is out\n *\n * They are MUTUALLY EXCLUSIVE: drawn together they say the same number twice\n * in two shapes. The boundary is `currentMode`'s, the same one the engine uses\n * to stage a dispatch rather than send it live.\n */\nexport function signalDelayPresentation({\n oneWaySeconds,\n canQueue,\n alwaysBadge = false,\n}: SignalDelayPresentationInput): SignalDelayPresentation {\n if (oneWaySeconds === null || oneWaySeconds <= 0) return \"none\";\n if (alwaysBadge) return \"badge\";\n if (currentMode({ oneWaySeconds: value(\"s\", oneWaySeconds) }) === \"live\") {\n return \"badge\";\n }\n return canQueue ? \"strip\" : \"none\";\n}\n\n/**\n * Display shape for one delayed command. `etaSeconds` is the\n * caller's choice of clock (reach or reply); `null` renders as \"no ETA\" (an\n * `overdue` or `lost` entry, or `no-path` mode).\n *\n * @category CommandDelay\n */\nexport interface InFlightListItem {\n id: string;\n label: string;\n etaSeconds: number | null;\n phase: \"in-transit\" | \"awaiting-reply\" | \"due\" | \"overdue\" | \"lost\";\n /**\n * True position along the 3-stage delay axis, 0 (just sent) to 1 (end of the\n * 3T span). Only the `variant=\"rail\"` glow reads it; without it the glow\n * anchors by phase.\n */\n progress?: number;\n /**\n * The command's OWN terse glyph (\"PRO\", \"RET\", \"WARP\"), shown in the\n * `variant=\"expanded\"` queue square. Phase is conveyed by colour, never by\n * the glyph. Defaults to an abbreviation of `label`.\n */\n glyph?: string;\n /** Which way the entry crosses: `outbound` to the craft (the default), `inbound` home from it. */\n flow?: \"outbound\" | \"inbound\";\n}\n\n/**\n * What the link is doing, as `useCommand`'s `delayMode` reports it: `live`\n * under the staged threshold, `staged` above it, `no-path` when nothing is\n * measurable.\n * {@link InFlightList} writes it to its root's `data-mode` attribute, for\n * styling, and draws nothing different for it: each command already shows its\n * own phase and countdown, and under `no-path` it shows \"no ETA\".\n *\n * @category CommandDelay\n */\nexport type InFlightListMode = \"live\" | \"staged\" | \"no-path\";\n\n/**\n * How much room the list gets to say what it knows. Orthogonal to `mode`,\n * which is about WHAT is being counted; this is about how much space there is\n * to count it in.\n *\n * - `full` arrow, label and countdown per command, one per line\n * - `compact` arrow and countdown only; the label moves to the accessible\n * name and tooltip\n * - `badge` one chip for the whole set: count plus the nearest arrival\n *\n * `auto` (the default) measures the rendered width and picks.\n *\n * @category CommandDelay\n */\nexport type InFlightListDensity = \"auto\" | \"full\" | \"compact\" | \"badge\";\n\n/**\n * Props for {@link InFlightList}.\n *\n * @category CommandDelay\n */\nexport interface InFlightListProps {\n /** The commands in flight; `toInFlightListItems(handle.inFlight)` builds them. */\n items: InFlightListItem[];\n /** The link's mode, written to `data-mode`. */\n mode?: InFlightListMode;\n /** Defaults to `auto`. */\n density?: InFlightListDensity;\n /**\n * `column` stacks the entries, `row` flows them. A queue under a button\n * group wants a column; one beside an action row wants a row. Row wraps, so\n * it degrades to several short lines rather than overflowing.\n */\n orientation?: \"column\" | \"row\";\n /** Accessible label for the list region. Defaults to \"In-flight commands\". */\n ariaLabel?: string;\n /**\n * `\"inline\"` (default) is the monospace row/badge list. `\"rail\"` is the\n * Panel rail's 16px strip: each command is a soft glow grazing the top edge,\n * sweeping left to right by journey progress. `\"expanded\"` is the pinned\n * detail: a queue square per command. `mode`, `density` and `orientation`\n * apply to `\"inline\"` only.\n */\n variant?: \"inline\" | \"rail\" | \"expanded\";\n /**\n * Clear an unanswered command (`overdue`/`lost`) from the shared delay queue.\n * When given, the `\"expanded\"` queue's unanswered squares become real clear buttons.\n * Applies to `\"expanded\"` only.\n */\n onDismiss?: (id: string) => void;\n}\n\nconst PHASE_ARROW: Record<InFlightListItem[\"phase\"], string> = {\n \"in-transit\": \"↑\",\n \"awaiting-reply\": \"↓\",\n due: \"↓\",\n overdue: \"!\",\n lost: \"?\",\n};\n\n/**\n * The word an entry with no countdown shows and speaks. A `lost` command got no\n * reply and may still have run, so it reads as unconfirmed, never as failed.\n */\nconst PHASE_WORD: Record<InFlightListItem[\"phase\"], string> = {\n \"in-transit\": \"in transit\",\n \"awaiting-reply\": \"awaiting reply\",\n due: \"due\",\n overdue: \"overdue\",\n lost: \"unconfirmed\",\n};\n\n/** An inbound entry in transit is coming down, whatever the phase table says a command does. */\nfunction arrowOf(item: InFlightListItem): string {\n if (item.flow === \"inbound\" && item.phase === \"in-transit\") return \"↓\";\n return PHASE_ARROW[item.phase];\n}\n\nconst ERROR_PHASES = new Set<InFlightListItem[\"phase\"]>([\"overdue\", \"lost\"]);\n\n/**\n * Width thresholds for `auto`, in px, set by what the content needs. `full`\n * needs arrow, countdown, gaps and padding plus about 100px of label room;\n * `compact` needs arrow plus countdown. Below that the set collapses to the\n * badge.\n */\nconst FULL_MIN_WIDTH = 180;\nconst COMPACT_MIN_WIDTH = 96;\n\n/**\n * The countdown a strip SPEAKS, for the accessible name and a row's `title`.\n * Clamped at zero: a strip shows time REMAINING, never a negative duration.\n */\nfunction clampedCountdown(seconds: number): string {\n return writeQuantity(value(\"s\", Math.max(0, seconds)));\n}\n\nfunction nearestEta(items: InFlightListItem[]): number | null {\n let best: number | null = null;\n for (const item of items) {\n if (item.etaSeconds === null) continue;\n if (best === null || item.etaSeconds < best) best = item.etaSeconds;\n }\n return best;\n}\n\n/** Re-seed the local countdown only on a jump this large (seconds): the caller's `etaSeconds` drifts by fractions on every re-render. */\nconst RESYNC_THRESHOLD_SECONDS = 1;\n\n/**\n * A local-ticking countdown: seeds from `etaSeconds`, resyncs only on a real\n * jump, and otherwise decrements once per second on its own interval, so the\n * number stays smooth whatever the caller's cadence. Reads only the value\n * passed in.\n */\nexport function useCountdown(etaSeconds: number | null): number | null {\n const [value, setValue] = useState(etaSeconds);\n const lastSeedRef = useRef(etaSeconds);\n\n useEffect(() => {\n const last = lastSeedRef.current;\n const jumped =\n (last === null) !== (etaSeconds === null) ||\n (last !== null &&\n etaSeconds !== null &&\n Math.abs(etaSeconds - last) >= RESYNC_THRESHOLD_SECONDS);\n if (jumped) {\n lastSeedRef.current = etaSeconds;\n setValue(etaSeconds);\n }\n }, [etaSeconds]);\n\n // Mount-once and not keyed on `etaSeconds`, which drifts every render and would never let a full second elapse.\n useEffect(() => {\n const id = setInterval(() => {\n setValue((prev) => (prev === null ? null : Math.max(0, prev - 1)));\n }, 1000);\n return () => clearInterval(id);\n }, []);\n\n return value;\n}\n\n/**\n * A list of in-flight commands: rows with per-entry countdowns and phase\n * styling. Renders nothing for an empty set. It has no data hooks; build its\n * items from `useCommand().inFlight` with {@link toInFlightListItems}.\n *\n * @category CommandDelay\n */\nexport function InFlightList({\n items,\n mode,\n density = \"auto\",\n orientation = \"column\",\n ariaLabel = \"In-flight commands\",\n variant = \"inline\",\n onDismiss,\n}: InFlightListProps) {\n // Seeded wide so `auto` only ever shrinks from the full form, never flashing a badge on mount.\n const { ref, size } = useElementSize({ w: 320, h: 0 });\n\n // The rail and expanded renderings bypass density entirely.\n if (variant === \"rail\") {\n if (items.length === 0) return null;\n return <InFlightRailStrip items={items} ariaLabel={ariaLabel} />;\n }\n if (variant === \"expanded\") {\n if (items.length === 0) return null;\n return (\n <InFlightQueue\n items={items}\n ariaLabel={ariaLabel}\n onDismiss={onDismiss}\n />\n );\n }\n\n const resolved: Exclude<InFlightListDensity, \"auto\"> =\n density !== \"auto\"\n ? density\n : size.w >= FULL_MIN_WIDTH\n ? \"full\"\n : size.w >= COMPACT_MIN_WIDTH\n ? \"compact\"\n : \"badge\";\n\n // The size hook keeps its last measurement across an empty render.\n if (items.length === 0) return null;\n\n if (resolved === \"badge\") {\n return (\n <InFlightBadge\n ref={ref}\n items={items}\n mode={mode}\n ariaLabel={ariaLabel}\n />\n );\n }\n\n return (\n <InFlightList__Root\n ref={ref}\n role=\"list\"\n aria-label={ariaLabel}\n data-mode={mode}\n data-density={resolved}\n $row={orientation === \"row\"}\n >\n {items.map((item) => (\n <InFlightRow\n key={item.id}\n item={item}\n $compact={resolved === \"compact\"}\n />\n ))}\n </InFlightList__Root>\n );\n}\n\n/*\n * Rail strip geometry: each command is a glow centred ABOVE the top edge, so\n * only its blur grazes the strip, with x tracking true journey progress. No\n * baseline or dividers.\n */\nconst RAIL_VB_W = 100;\nconst RAIL_VB_H = 16;\nconst GLOW_CY = -4;\nconst GLOW_R = 9;\nconst GLOW_PEAK_ALPHA = 0.22;\n\nfunction InFlightRailStrip({\n items,\n ariaLabel,\n}: {\n items: InFlightListItem[];\n ariaLabel: string;\n}) {\n const gradBase = useId();\n const summary = `${items.length} in flight`;\n return (\n <InFlightRailStrip__Svg\n role=\"img\"\n aria-label={`${ariaLabel}: ${summary}`}\n viewBox={`0 0 ${RAIL_VB_W} ${RAIL_VB_H}`}\n preserveAspectRatio=\"none\"\n >\n <defs>\n {items.map((item, i) => {\n // An unanswered command's glow goes to the warning tone, so it is never invisible here.\n const colour = ERROR_PHASES.has(item.phase)\n ? TONE_MARK.warn\n : \"var(--color-accent-fg)\";\n const progress = Math.max(\n 0,\n Math.min(1, item.progress ?? PHASE_PROGRESS[item.phase]),\n );\n const cx = progress * RAIL_VB_W;\n return (\n <radialGradient\n key={item.id}\n id={`${gradBase}-${i}`}\n gradientUnits=\"userSpaceOnUse\"\n cx={cx}\n cy={GLOW_CY}\n r={GLOW_R}\n >\n <stop\n offset=\"0\"\n stopColor={colour}\n stopOpacity={GLOW_PEAK_ALPHA}\n />\n <stop offset=\"1\" stopColor={colour} stopOpacity=\"0\" />\n </radialGradient>\n );\n })}\n </defs>\n {items.map((item, i) => (\n <rect\n key={item.id}\n data-role=\"glow\"\n data-phase={item.phase}\n x=\"0\"\n y=\"0\"\n width={RAIL_VB_W}\n height={RAIL_VB_H}\n fill={`url(#${gradBase}-${i})`}\n />\n ))}\n </InFlightRailStrip__Svg>\n );\n}\n\n/** The whole queue as one chip: how many are out, and when the next one arrives. */\nconst InFlightBadge = function InFlightBadge({\n ref,\n items,\n mode,\n ariaLabel,\n}: {\n ref: RefObject<HTMLDivElement>;\n items: InFlightListItem[];\n mode?: InFlightListMode;\n ariaLabel: string;\n}) {\n const countdown = useCountdown(nearestEta(items));\n const worst = items.some((i) => ERROR_PHASES.has(i.phase));\n const summary =\n countdown === null\n ? `${items.length} in flight`\n : `${items.length} in flight, next in ${clampedCountdown(countdown)}`;\n return (\n <Tooltip text={items.map((i) => i.label).join(\"\\n\")} focusable>\n <InFlightList__Root\n ref={ref}\n role=\"group\"\n aria-label={`${ariaLabel}: ${summary}`}\n data-mode={mode}\n data-density=\"badge\"\n $row={false}\n >\n <InFlightList__Row $phase={worst ? \"overdue\" : \"in-transit\"}>\n <InFlightList__Arrow aria-hidden=\"true\" $pulse={!worst}>\n ↑\n </InFlightList__Arrow>\n <InFlightList__Phase>\n {items.length}\n {countdown !== null && (\n <>\n {\" · \"}\n <Countdown value={Math.max(0, countdown)} />\n </>\n )}\n </InFlightList__Phase>\n </InFlightList__Row>\n </InFlightList__Root>\n </Tooltip>\n );\n};\n\nfunction InFlightRow({\n item,\n $compact,\n}: {\n item: InFlightListItem;\n $compact: boolean;\n}) {\n const countdown = useCountdown(item.etaSeconds);\n const isError = ERROR_PHASES.has(item.phase);\n const spoken =\n countdown === null\n ? `${item.label}, ${PHASE_WORD[item.phase]}`\n : `${item.label}, ${clampedCountdown(countdown)}`;\n return (\n // Compact drops the visible label, so the row carries it as its accessible name; `title` is only a convenience.\n <InFlightList__Row\n $phase={item.phase}\n role=\"listitem\"\n {...($compact ? { \"aria-label\": spoken, title: spoken } : {})}\n >\n <InFlightList__Arrow\n aria-hidden=\"true\"\n $pulse={!isError}\n $inherit={isError}\n >\n {arrowOf(item)}\n </InFlightList__Arrow>\n {!$compact && <InFlightList__Label>{item.label}</InFlightList__Label>}\n <InFlightList__Phase>\n {countdown === null ? (\n PHASE_WORD[item.phase]\n ) : (\n <Countdown value={Math.max(0, countdown)} />\n )}\n </InFlightList__Phase>\n </InFlightList__Row>\n );\n}\n\nconst InFlightRailStrip__Svg = styled.svg`\n display: block;\n width: 100%;\n height: 16px;\n`;\n\n/*\n * The expanded command queue: a SQUARE per command showing its own glyph,\n * never a status icon. Phase is colour, progress a thin bar. A fixed box that\n * never grows the widget: overflow becomes a `+N` count, never a scroll. A row\n * in a wide box, a column in a narrow one. An unanswered (lost or overdue)\n * square is a clear button.\n */\nconst QUEUE_THICK = 54; // px\nconst QUEUE_BAR = 5;\nconst QUEUE_GAP = 3;\nconst QUEUE_SQUARE = QUEUE_THICK - 8 - QUEUE_BAR; // less border+padding and bar\nconst QUEUE_ROW_MIN = 60; // container width at/above which the queue runs as a row\n\nconst QUEUE_COLOUR: Record<InFlightListItem[\"phase\"], string> = {\n \"in-transit\": \"var(--color-accent-fg)\",\n \"awaiting-reply\": \"var(--color-accent-fg)\",\n due: \"var(--color-accent-fg)\",\n overdue: TONE_MARK.warn,\n lost: TONE_MARK.warn,\n};\n\nfunction InFlightQueue({\n items,\n ariaLabel,\n onDismiss,\n}: {\n items: InFlightListItem[];\n ariaLabel: string;\n onDismiss?: (id: string) => void;\n}) {\n const { ref, size } = useElementSize({ w: 320, h: QUEUE_THICK });\n // Capacity is measured on the main axis, so a full queue caps at `+N` rather than scrolling.\n const row = size.w >= QUEUE_ROW_MIN;\n const main = row ? size.w : size.h;\n const capacity = Math.max(\n 1,\n Math.floor((main - 6) / (QUEUE_SQUARE + QUEUE_GAP)),\n );\n const willOverflow = items.length > capacity;\n const shown = willOverflow ? items.slice(0, capacity - 1) : items;\n const hidden = items.length - shown.length;\n return (\n <InFlightQueue__Box ref={ref}>\n <InFlightQueue__Inner role=\"list\" aria-label={ariaLabel}>\n {shown.map((item) => (\n <InFlightQueueCmd key={item.id} item={item} onDismiss={onDismiss} />\n ))}\n {hidden > 0 && (\n <InFlightQueue__Overflow\n role=\"listitem\"\n aria-label={`${hidden} more in flight`}\n >\n +{hidden}\n </InFlightQueue__Overflow>\n )}\n </InFlightQueue__Inner>\n </InFlightQueue__Box>\n );\n}\n\nfunction InFlightQueueCmd({\n item,\n onDismiss,\n}: {\n item: InFlightListItem;\n onDismiss?: (id: string) => void;\n}) {\n const glyph = item.glyph ?? deriveGlyph(item.label);\n const colour = QUEUE_COLOUR[item.phase];\n const progress = Math.max(\n 0,\n Math.min(1, item.progress ?? PHASE_PROGRESS[item.phase]),\n );\n const dismissable = ERROR_PHASES.has(item.phase) && !!onDismiss;\n const spoken = `${item.label}, ${PHASE_WORD[item.phase]}`;\n return (\n <InFlightQueue__Cmd\n role=\"listitem\"\n aria-label={spoken}\n data-phase={item.phase}\n style={{ color: colour }}\n >\n <Tooltip\n text={dismissable ? `Dismiss ${item.label}` : spoken}\n announce={false}\n >\n <InFlightQueue__Sq\n as={dismissable ? \"button\" : \"div\"}\n {...(dismissable\n ? {\n type: \"button\" as const,\n onClick: () => onDismiss?.(item.id),\n \"aria-label\": `Dismiss ${item.label}`,\n }\n : { \"aria-hidden\": true })}\n >\n <span className=\"glyph\" aria-hidden=\"true\">\n {glyph}\n </span>\n {dismissable && (\n <span className=\"dismiss\" aria-hidden=\"true\">\n ✕\n </span>\n )}\n </InFlightQueue__Sq>\n </Tooltip>\n <InFlightQueue__Bar>\n <span\n className=\"fill\"\n style={{ \"--fill-ratio\": progress } as CSSProperties}\n />\n </InFlightQueue__Bar>\n </InFlightQueue__Cmd>\n );\n}\n\nconst InFlightQueue__Box = styled.div.attrs({ role: \"group\" })`\n container-type: inline-size;\n /* Never shrinks in the rail's flex column, so a combined pinned rail shows the whole tile row. */\n flex: 0 0 auto;\n /* The row container the tiles sit in; its horizontal inset matches the standard content margin. */\n margin: var(--outset-command-strip);\n /* The inset equals (panel radius - tile radius), so the first tile's corner nests concentrically in the panel's. */\n --queue-panel-radius: calc(var(--radius-floating) * 2);\n --queue-tile-inset: calc(var(--queue-panel-radius) - var(--radius-regular));\n padding: var(--queue-tile-inset);\n height: calc(\n ${QUEUE_SQUARE}px + ${QUEUE_BAR}px + (var(--queue-tile-inset) * 2)\n );\n border-radius: var(--queue-panel-radius);\n background: color-mix(in srgb, var(--color-surface-raised) 68%, transparent);\n backdrop-filter: blur(6px);\n overflow: hidden;\n`;\n\nconst InFlightQueue__Inner = styled.div`\n display: flex;\n flex-direction: column;\n /* Centred on the cross axis so both margins match; the main axis fills from the start, so a short queue reads as started. */\n align-items: center;\n justify-content: flex-start;\n gap: ${QUEUE_GAP}px;\n width: 100%;\n height: 100%;\n\n @container (min-width: ${QUEUE_ROW_MIN}px) {\n flex-direction: row;\n }\n`;\n\nconst InFlightQueue__Cmd = styled.div`\n display: flex;\n flex: 0 0 auto;\n flex-direction: row;\n min-width: 0;\n min-height: 0;\n\n @container (min-width: ${QUEUE_ROW_MIN}px) {\n flex-direction: column;\n }\n`;\n\nconst InFlightQueue__Sq = styled.div`\n --s: ${QUEUE_SQUARE}px;\n flex: 0 0 var(--s);\n width: var(--s);\n height: var(--s);\n align-self: center;\n position: relative;\n display: grid;\n place-items: center;\n margin: 0;\n padding: 0;\n appearance: none;\n font: inherit;\n font-size: var(--font-size-compact);\n font-weight: 700;\n color: currentColor;\n background: color-mix(in srgb, currentColor 10%, var(--color-surface-raised));\n border: 1px solid currentColor;\n border-right: 0;\n border-radius: var(--radius-regular) 0 0 var(--radius-regular);\n overflow: hidden;\n\n @container (min-width: ${QUEUE_ROW_MIN}px) {\n border-right: 1px solid currentColor;\n border-bottom: 0;\n border-radius: var(--radius-regular) var(--radius-regular) 0 0;\n }\n\n .glyph {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n color: var(--color-text-primary);\n }\n .dismiss {\n position: absolute;\n inset: 0;\n display: grid;\n place-items: center;\n opacity: 0;\n background: color-mix(in srgb, currentColor 22%, var(--color-surface-sunken));\n }\n\n &:is(button) {\n cursor: pointer;\n }\n &:is(button):hover .dismiss,\n &:is(button):focus-visible .dismiss {\n opacity: 1;\n }\n &:is(button):hover .glyph,\n &:is(button):focus-visible .glyph {\n opacity: 0;\n }\n /* Not the kit's shared ring: every other part of the tile follows the phase colour through currentColor. */\n &:focus-visible {\n outline: 2px solid currentColor;\n outline-offset: 1px;\n }\n`;\n\nconst InFlightQueue__Bar = styled.div`\n --s: ${QUEUE_SQUARE}px;\n position: relative;\n flex: 0 0 ${QUEUE_BAR}px;\n width: ${QUEUE_BAR}px;\n height: var(--s);\n overflow: hidden;\n background: var(--color-surface-panel);\n border: 1px solid currentColor;\n border-radius: 0 var(--radius-regular) var(--radius-regular) 0;\n\n @container (min-width: ${QUEUE_ROW_MIN}px) {\n width: var(--s);\n height: ${QUEUE_BAR}px;\n border-radius: 0 0 var(--radius-regular) var(--radius-regular);\n }\n\n .fill {\n position: absolute;\n left: 0;\n right: 0;\n bottom: 0;\n height: calc(var(--fill-ratio, 0) * 100%);\n background: currentColor;\n }\n @container (min-width: ${QUEUE_ROW_MIN}px) {\n .fill {\n top: 0;\n bottom: 0;\n left: 0;\n right: auto;\n width: calc(var(--fill-ratio, 0) * 100%);\n height: auto;\n }\n }\n`;\n\nconst InFlightQueue__Overflow = styled.span`\n align-self: center;\n flex: 0 0 auto;\n padding: 0 ${QUEUE_GAP}px;\n font-size: var(--font-size-compact);\n font-variant-numeric: tabular-nums;\n color: var(--color-text-muted);\n`;\n\nconst InFlightList__Root = styled.div<{ $row: boolean }>`\n flex: 0 0 auto;\n display: flex;\n flex-direction: ${({ $row }) => ($row ? \"row\" : \"column\")};\n flex-wrap: ${({ $row }) => ($row ? \"wrap\" : \"nowrap\")};\n column-gap: var(--gap-command-list-column);\n gap: var(--gap-command-list-row);\n padding: var(--inset-command-list);\n font-family: var(--font-family-mono);\n font-size: var(--font-size-compact);\n background: var(--color-surface-panel);\n border: 1px solid var(--color-border-subtle);\n border-radius: var(--radius-regular);\n`;\n\nconst PHASE_ROW_STYLES: Record<\n InFlightListItem[\"phase\"],\n ReturnType<typeof css>\n> = {\n \"in-transit\": css`\n color: var(--color-text-primary);\n `,\n \"awaiting-reply\": css`\n color: var(--color-text-muted);\n `,\n due: css`\n color: var(--color-text-muted);\n `,\n overdue: css`\n color: ${TONE_TEXT.warn};\n `,\n lost: css`\n color: ${TONE_TEXT.warn};\n `,\n};\n\nconst InFlightList__Row = styled.div<{ $phase: InFlightListItem[\"phase\"] }>`\n display: flex;\n align-items: baseline;\n gap: var(--gap-command-row);\n\n ${({ $phase }) => PHASE_ROW_STYLES[$phase]}\n`;\n\n/** `$inherit` draws the glyph in its row's colour, so an unanswered entry's mark carries its tone. */\nconst InFlightList__Arrow = styled.span<{\n $pulse: boolean;\n $inherit?: boolean;\n}>`\n flex: 0 0 auto;\n color: ${({ $inherit }) => ($inherit ? \"inherit\" : \"var(--color-accent-fg)\")};\n\n ${({ $pulse }) =>\n $pulse &&\n css`\n @media (prefers-reduced-motion: no-preference) {\n animation: in-flight-list-pulse 1.6s var(--ease-emphasis) infinite;\n }\n `}\n\n @keyframes in-flight-list-pulse {\n 50% {\n opacity: 0.35;\n }\n }\n`;\n\nconst InFlightList__Label = styled.span`\n flex: 1 1 auto;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n`;\n\nconst InFlightList__Phase = styled.span`\n flex: 0 0 auto;\n`;\n","import type { Reading, Value } from \"@ksp-gonogo/sitrep-sdk\";\nimport {\n ControlDelayStream,\n type ControlRibbonDatum,\n type ControlStreamDatum,\n} from \"./ControlDelayStream\";\nimport type { CommandFailedEntry } from \"./commandFailedSentence\";\nimport type { CommandFoundEntry } from \"./commandFoundSentence\";\nimport type { CommandLossEntry } from \"./commandLossSentence\";\nimport type { CommandRefusalEntry } from \"./commandRefusalSentence\";\nimport type { CommandUndeliveredEntry } from \"./commandUndeliveredSentence\";\nimport {\n InFlightList,\n type InFlightListDensity,\n type InFlightListMode,\n} from \"./InFlightList\";\nimport {\n type RailTags,\n railRendererFor,\n railTagKey,\n reportUnrepresentedRail,\n} from \"./railTags\";\nimport {\n type InFlightCommandLike,\n toInFlightListItems,\n} from \"./toInFlightListItems\";\n\n/**\n * What {@link CommandDelay} and the Panel delay rail read from a command: its\n * in-flight rows, its delay, and what became of its dispatches. Declared\n * structurally; `useCommand`'s return value satisfies it.\n *\n * @category CommandDelay\n */\nexport interface CommandDelayHandle {\n /**\n * Discrete in-flight rows. Empty for a pure stream handle, and always empty\n * once `effectiveDelaySeconds` is 0 (nothing is ever in flight instantly).\n */\n inFlight: InFlightCommandLike[];\n /**\n * What this entry IS on the rail's three axes, in full. The renderer follows\n * from these, so no consumer branches on what kind of command this is. A\n * command handle gets them from `useCommand`; a producer that is not a\n * command (an open microphone) states them with `railTagsForTelemetry`.\n */\n tags: RailTags;\n /**\n * The command's effective one-way delay under its selected vantage. `0`\n * means nothing to visualise, so `<CommandDelay>` draws null (an instant\n * command still mounts it). `null` means the delay is unknown, which also\n * draws nothing but is not a claim of instant arrival.\n */\n effectiveDelaySeconds: number | null;\n /**\n * The one-way delay as the reading it arrived in (`useCommand`'s\n * `delayReading`), so the delay figures the rail draws are held while\n * `comms.delay` is quiet. Omitted, they draw as current.\n */\n delayReading?: Reading<Value<\"s\">> | null;\n /**\n * Stream buffers for a CONTINUOUS handle (the in-transit + confirmed-echo\n * samples `ControlDelayStream` draws). Ignored by the discrete queue.\n */\n streams?: ControlStreamDatum[];\n /**\n * Ribbon buffers: a continuous entry with amplitude history and no readback,\n * drawn in the rail's outgoing zone. Carried alongside `streams`, so a widget\n * with both a control axis and an open microphone gets one graph.\n */\n ribbons?: ControlRibbonDatum[];\n /**\n * What the rail should call this handle's graph, when \"Delay detail\" is not\n * what it is. A voice ribbon names the transmission it is drawing; a control\n * axis is happy with the default.\n */\n ariaLabel?: string;\n /**\n * Dispatches from this command the GAME REFUSED, until dismissed. Rendered by\n * the Panel rail under both queues, never by `<CommandDelay>`: a refusal is\n * terminal and has nothing to do with delay.\n */\n refusals?: CommandRefusalEntry[];\n /**\n * Dispatches from this command that got NO ANSWER, until dismissed. Rendered\n * by the Panel rail, since a loss can have no in-flight entry at all. Apart\n * from `refusals` because a loss carries no verdict: the command may have\n * executed.\n */\n losses?: CommandLossEntry[];\n /**\n * Dispatches from this command that were called LOST and then replied after\n * all, until dismissed. An entry here has left `losses`: the same dispatch at\n * a later moment, never two boxes at once.\n */\n founds?: CommandFoundEntry[];\n /**\n * Dispatches from this command that NEVER LEFT this machine, until dismissed:\n * the command did not run. An entry here has left `losses`, as with `founds`.\n */\n undelivered?: CommandUndeliveredEntry[];\n /**\n * Dispatches from this command the mod answered with a FAULT in place of a\n * reply, until dismissed: it expired or was cancelled on its way, or a game\n * load undid it. Each carries the fault's code, which says which.\n */\n failures?: CommandFailedEntry[];\n /**\n * Clear a dead command (`overdue`/`lost`) from this handle's shared delay\n * queue, both in the widget-top queue and on the issuing control. When\n * present, the expanded queue's failed squares become clear buttons.\n */\n dismiss?: (id: string) => void;\n}\n\n/**\n * Props for {@link CommandDelay}. Pass `handle` or `handles`.\n *\n * @category CommandDelay\n */\nexport interface CommandDelayProps {\n /** A single command handle. Sugar for `handles={[handle]}`. */\n handle?: CommandDelayHandle;\n /**\n * Several command handles drawn as one merged list, for a widget whose\n * controls send more than one command (e.g. a maneuver planner's\n * add/update/remove). Their discrete in-flight rows are concatenated into a\n * single `InFlightList`.\n */\n handles?: CommandDelayHandle[];\n /**\n * Accessible label for the rendered region. Left undefined, each branch\n * keeps its own default (\"In-flight commands\" / \"Controls in flight\").\n */\n ariaLabel?: string;\n /** Discrete-branch passthrough to `InFlightList`. Ignored for stream. */\n mode?: InFlightListMode;\n /** Discrete-branch passthrough to `InFlightList`. Ignored for stream. */\n density?: InFlightListDensity;\n /** Discrete-branch passthrough to `InFlightList`. Ignored for stream. */\n orientation?: \"column\" | \"row\";\n /**\n * `\"inline\"` (default) is the in-body rendering. `\"rail\"` (collapsed strip)\n * and `\"expanded\"` (grown detail) are what the Panel-owned rail passes,\n * forwarded to whichever child branch renders.\n */\n variant?: \"inline\" | \"rail\" | \"expanded\";\n}\n\n/**\n * Draws what a delayed command is doing on its way across the link. It draws\n * nothing at zero or unknown delay, the continuous {@link ControlDelayStream}\n * for a single stream command, or the discrete {@link InFlightList} (merged\n * across handles) otherwise, so a widget gets the right delay display with no\n * branching of its own.\n *\n * On the dashboard every `useCommand` handle already reaches its `Panel`'s\n * delay rail; render this for the same display inside the widget body.\n *\n * @example\n * ```tsx\n * const setTarget = useCommand(\"vessel.target.set\");\n * const clearTarget = useCommand(\"vessel.target.clear\");\n *\n * <CommandDelay handles={[setTarget, clearTarget]} orientation=\"row\" />\n * ```\n *\n * @category CommandDelay\n */\nexport function CommandDelay({\n handle,\n handles,\n ariaLabel,\n mode,\n density,\n orientation,\n variant = \"inline\",\n}: Readonly<CommandDelayProps>) {\n const all = handles ?? (handle ? [handle] : []);\n\n /*\n * Which child draws a lone handle comes from the renderer table. Both\n * continuous forms land on the ONE graph. Never branch on delivery here:\n * whether a return leg is drawn is a decision inside the graph.\n */\n const lone = all.length === 1 ? all[0] : null;\n const loneRenderer = lone ? railRendererFor(lone.tags) : null;\n if (lone && loneRenderer === null) {\n return <UnrepresentedRailHandle tags={lone.tags} who={lone.ariaLabel} />;\n }\n if (lone && loneRenderer !== \"in-flight-row\") {\n const streamHandle = lone;\n // An unknown delay draws nothing, as an instant one does.\n const delay = streamHandle.effectiveDelaySeconds;\n if (delay === null || delay <= 0) return null;\n return (\n <ControlDelayStream\n streams={streamHandle.streams ?? []}\n ribbons={streamHandle.ribbons ?? []}\n delayReading={streamHandle.delayReading}\n ariaLabel={ariaLabel ?? streamHandle.ariaLabel}\n variant={variant}\n />\n );\n }\n\n /*\n * Discrete: merge every handle's in-flight rows. Not gated on\n * `effectiveDelaySeconds`, which a widget may not carry; an instant command\n * simply has no `inFlight` rows and `InFlightList` self-blanks.\n */\n const items = toInFlightListItems(all.flatMap((h) => h.inFlight));\n // A dismiss routes to the handle that owns the command.\n const canDismiss = all.some((h) => h.dismiss);\n const onDismiss = canDismiss\n ? (id: string) =>\n all.find((h) => h.inFlight.some((c) => c.id === id))?.dismiss?.(id)\n : undefined;\n return (\n <InFlightList\n items={items}\n ariaLabel={ariaLabel}\n mode={mode}\n density={density}\n orientation={orientation}\n variant={variant}\n onDismiss={onDismiss}\n />\n );\n}\n\n/**\n * A handle whose declared combination nothing draws, reported and left as a\n * `hidden` marker (`data-rail-unrepresented`) a probe can find. Never a\n * fallback to the discrete list, which would look right and be wrong.\n */\nfunction UnrepresentedRailHandle({\n tags,\n who,\n}: {\n tags: RailTags;\n who?: string;\n}) {\n const named = who ?? \"unnamed handle\";\n reportUnrepresentedRail(tags, named);\n return <span hidden data-rail-unrepresented={railTagKey(tags)} />;\n}\n","import {\n commandRefusalSubject,\n describeErrorCode,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport type { CommandLossLike } from \"./commandLossSentence\";\nimport type { RailTags } from \"./railTags\";\n\n/**\n * One dispatch that failed with an error rather than an answer: the fields of\n * {@link CommandLossLike}, plus the fault it was answered with.\n *\n * @category CommandDelay\n */\nexport interface CommandFailedLike extends CommandLossLike {\n /** The fault the dispatch was answered with, a `FaultCode` id, when one came back. */\n code?: string;\n}\n\n/**\n * A failed dispatch a surface can render: the text's inputs plus the dispatch's `requestId`, which keys the box and is what `dismiss` takes.\n *\n * @category CommandDelay\n */\nexport interface CommandFailedEntry extends CommandFailedLike {\n id: string;\n}\n\n/**\n * One failed dispatch as the rail renders it, plus its command's rail axes.\n *\n * @category CommandDelay\n */\nexport interface RailFailed extends CommandFailedEntry {\n tags: RailTags;\n}\n\n/**\n * What the operator is told about a command that came back with a fault in\n * place of an answer. A fault the code table declares is said in its own\n * sentence, since the mod has said what became of the command: it expired, was\n * cancelled, or a game load undid it. Anything else, the machinery breaking\n * or the client going away while it waited, gets no verdict. Never \"refused\",\n * which is the game's verdict, and it does not quote the error's own message.\n *\n * @category CommandDelay\n */\nexport function commandFailedSentence(failed: CommandFailedLike): string {\n const subject = commandRefusalSubject(failed);\n const what = subject || failed.command || \"The command\";\n const said =\n failed.code === undefined ? undefined : describeErrorCode(failed.code);\n return said?.kind === \"fault\"\n ? `${what}: failed, ${said.sentence}.`\n : `${what}: failed, with no verdict from the game.`;\n}\n","import { commandRefusalSubject } from \"@ksp-gonogo/sitrep-sdk\";\nimport type {\n CommandLossEntry,\n CommandLossLike,\n RailLoss,\n} from \"./commandLossSentence\";\n\n/**\n * One dispatch that never left this machine. It has the same fields as {@link CommandLossLike}.\n *\n * @category CommandDelay\n */\nexport type CommandUndeliveredLike = CommandLossLike;\n\n/**\n * An undelivered dispatch a surface can render: the text's inputs plus the dispatch's `requestId`, which keys the box and is what `dismiss` takes.\n *\n * @category CommandDelay\n */\nexport type CommandUndeliveredEntry = CommandLossEntry;\n\n/**\n * One undelivered dispatch as the rail renders it, plus its command's rail axes.\n *\n * @category CommandDelay\n */\nexport type RailUndelivered = RailLoss;\n\n/**\n * What the operator is told about a command that never went out: it did not\n * run, so it is safe to re-send. It never says \"lost\", which means the\n * opposite (\"may have run\"), and does not quote the connection's own error.\n *\n * @category CommandDelay\n */\nexport function commandUndeliveredSentence(\n undelivered: CommandUndeliveredLike,\n): string {\n const subject = commandRefusalSubject(undelivered);\n const what = subject || undelivered.command || \"The command\";\n return `${what}: never sent. Safe to re-send.`;\n}\n","import { commandRefusalSubject, type Tone } from \"@ksp-gonogo/sitrep-sdk\";\nimport styled, { css } from \"styled-components\";\nimport { focusRing } from \"../focusRing\";\nimport { LiveRegion } from \"../LiveRegion\";\nimport { TONE_MARK, TONE_TEXT } from \"../tone\";\nimport {\n commandFailedSentence,\n type RailFailed,\n} from \"./commandFailedSentence\";\nimport { commandFoundSentence, type RailFound } from \"./commandFoundSentence\";\nimport { commandLossSentence, type RailLoss } from \"./commandLossSentence\";\nimport {\n commandRefusalSentence,\n type RailRefusal,\n} from \"./commandRefusalSentence\";\nimport {\n commandUndeliveredSentence,\n type RailUndelivered,\n} from \"./commandUndeliveredSentence\";\nimport { railMark } from \"./railTags\";\nimport { deriveGlyph } from \"./toInFlightListItems\";\n\n/**\n * What became of the dispatches a {@link CommandList} draws:\n *\n * - `refused`: the game said no, and why\n * - `lost`: no reply came, so the command may have run\n * - `undelivered`: it never left this machine, so a re-send repeats nothing\n * - `found`: a lost dispatch replied after all\n * - `failed`: the machinery broke, so there is no verdict either way\n *\n * @category CommandDelay\n */\nexport type CommandListKind =\n | \"refused\"\n | \"lost\"\n | \"undelivered\"\n | \"found\"\n | \"failed\";\n\ninterface CommandListCommonProps {\n /** Clear one entry by its `id`. Omit it and the boxes carry no clear control rather than an inert one. */\n onDismiss?: (id: string) => void;\n /**\n * Announce each entry as it arrives (the default), as a polite live region\n * that stays mounted while empty. Off only where something else already\n * announces these outcomes, as the delay rail does; the list is then a plain\n * `role=\"list\"` that draws nothing when empty.\n */\n live?: boolean;\n /** Names the list for assistive tech. Each kind has its own default. */\n ariaLabel?: string;\n}\n\n/**\n * Props for {@link CommandList}: the entries it draws, keyed by what became of them. Each entry\n * carries its dispatch's `requestId` as `id` and its command's rail axes as\n * `tags`; only the axes' mark is read, so a discrete command gets its\n * in-flight glyph tile and a continuous one its name in words.\n *\n * @category CommandDelay\n */\nexport type CommandListProps = CommandListCommonProps &\n (\n | { kind: \"refused\"; entries: readonly RailRefusal[] }\n | { kind: \"lost\"; entries: readonly RailLoss[] }\n | { kind: \"undelivered\"; entries: readonly RailUndelivered[] }\n | { kind: \"found\"; entries: readonly RailFound[] }\n | { kind: \"failed\"; entries: readonly RailFailed[] }\n );\n\n/** One box as drawn: the kind's sentence and gesture already composed. */\ninterface CommandListBox {\n id: string;\n subject: string;\n sentence: string;\n dismissLabel: string;\n tags: RailRefusal[\"tags\"];\n}\n\nconst DEFAULT_LABEL: Record<CommandListKind, string> = {\n refused: \"Refused commands\",\n lost: \"Commands with no reply\",\n undelivered: \"Commands that were never sent\",\n found: \"Unconfirmed commands that answered\",\n failed: \"Commands that failed\",\n};\n\n/** What the clear control names when a dispatch has no subject to name. */\nconst FALLBACK_DISMISS: Record<CommandListKind, string> = {\n refused: \"refusal\",\n lost: \"unconfirmed command\",\n undelivered: \"unsent command\",\n found: \"found command\",\n failed: \"failed command\",\n};\n\nfunction sentenceOf(props: CommandListProps, index: number): string {\n if (props.kind === \"refused\")\n return commandRefusalSentence(props.entries[index]);\n if (props.kind === \"found\") return commandFoundSentence(props.entries[index]);\n if (props.kind === \"failed\") {\n return commandFailedSentence(props.entries[index]);\n }\n if (props.kind === \"undelivered\") {\n return commandUndeliveredSentence(props.entries[index]);\n }\n return commandLossSentence(props.entries[index]);\n}\n\nfunction boxesOf(props: CommandListProps): CommandListBox[] {\n return props.entries.map((entry, index) => {\n const subject = commandRefusalSubject(entry);\n return {\n id: entry.id,\n subject: subject || entry.command || \"\",\n sentence: sentenceOf(props, index),\n dismissLabel: `Dismiss ${subject || FALLBACK_DISMISS[props.kind]}`,\n tags: entry.tags,\n };\n });\n}\n\n/**\n * Outcome boxes for dispatches of one {@link CommandListKind}: one box per\n * dispatch, with the command's identity and the whole sentence of what became\n * of it. The Panel delay rail draws these under its queues; a widget draws one\n * wherever it reports the same outcomes itself. The sentence wraps and is never\n * truncated, since names are unbounded and the numbers sit at the end. A\n * `found` is drawn in the notice tone, every other kind in the warning tone.\n *\n * The sentences are {@link commandRefusalSentence},\n * {@link commandLossSentence}, {@link commandUndeliveredSentence},\n * {@link commandFoundSentence} and {@link commandFailedSentence}, so a surface\n * that states the same outcome elsewhere can say it in the same words.\n *\n * @example\n * ```tsx\n * <CommandList\n * kind=\"lost\"\n * live={false}\n * entries={losses.map((loss) => ({\n * ...loss,\n * tags: railTagsForCommand(loss.command ?? \"\"),\n * }))}\n * onDismiss={(id) => setLosses((all) => all.filter((l) => l.id !== id))}\n * />\n * ```\n *\n * @category CommandDelay\n */\nexport function CommandList(props: Readonly<CommandListProps>) {\n const { kind, onDismiss, live = true } = props;\n const ariaLabel = props.ariaLabel ?? DEFAULT_LABEL[kind];\n const tone: OutcomeTone = kind === \"found\" ? \"info\" : \"warn\";\n if (!live && props.entries.length === 0) return null;\n const boxes = boxesOf(props).map((box) => (\n <CommandList__Box\n key={box.id}\n role={live ? undefined : \"listitem\"}\n $tone={tone}\n >\n {railMark(box.tags) === \"ribbon\" ? (\n <CommandList__Label $tone={tone}>{box.subject}</CommandList__Label>\n ) : (\n <CommandList__Glyph aria-hidden=\"true\" $tone={tone}>\n {deriveGlyph(box.subject)}\n </CommandList__Glyph>\n )}\n <CommandList__Text>{box.sentence}</CommandList__Text>\n {onDismiss && (\n <CommandList__Dismiss\n type=\"button\"\n onClick={() => onDismiss(box.id)}\n aria-label={box.dismissLabel}\n >\n ✕\n </CommandList__Dismiss>\n )}\n </CommandList__Box>\n ));\n // A live list stays mounted while empty so assistive tech is already watching when the first outcome arrives.\n if (live) {\n return (\n <CommandList__Region\n forwardedAs=\"div\"\n aria-label={ariaLabel}\n additionsOnly\n >\n {boxes}\n </CommandList__Region>\n );\n }\n return (\n <CommandList__Root role=\"list\" aria-label={ariaLabel}>\n {boxes}\n </CommandList__Root>\n );\n}\n\nconst outcomeListLayout = css`\n display: flex;\n flex: 0 0 auto;\n flex-direction: column;\n gap: var(--gap-message-stack);\n /* Matches the queue container's inset, so the boxes line up with the tiles above. */\n margin: var(--outset-command-strip);\n`;\n\nconst CommandList__Root = styled.div`\n ${outcomeListLayout}\n`;\n\nconst CommandList__Region = styled(LiveRegion)`\n ${outcomeListLayout}\n &:empty {\n margin: 0;\n }\n`;\n\n/** `warn` for an outcome that did NOT happen (a refusal, a loss), `info` for one that did after all (a found). */\ntype OutcomeTone = Extract<Tone, \"warn\" | \"info\">;\n\nconst CommandList__Box = styled.div<{ $tone: OutcomeTone }>`\n display: flex;\n align-items: flex-start;\n gap: var(--gap-glyph-box);\n padding: var(--inset-surface);\n border: 1px solid ${({ $tone }) => TONE_MARK[$tone]};\n border-radius: var(--radius-regular);\n background: ${({ $tone }) =>\n `color-mix(in srgb, ${TONE_MARK[$tone]} 18%, var(--color-surface-raised))`};\n color: var(--color-text-primary);\n text-align: left;\n`;\n\n/** The same glyph the command's tile carries in the in-flight queue, so a dead command is recognisable. */\nconst CommandList__Glyph = styled.span<{ $tone: OutcomeTone }>`\n flex: 0 0 auto;\n display: grid;\n place-items: center;\n min-width: 34px;\n padding: var(--inset-glyph);\n align-self: stretch;\n font-size: var(--font-size-xs);\n font-weight: 700;\n color: ${({ $tone }) => TONE_TEXT[$tone]};\n border: 1px solid ${({ $tone }) => TONE_MARK[$tone]};\n border-radius: var(--radius-regular);\n background: ${({ $tone }) =>\n `color-mix(in srgb, ${TONE_MARK[$tone]} 14%, var(--color-surface-raised))`};\n`;\n\n/** A stream command's name in words, since it has no queue tile to echo. */\nconst CommandList__Label = styled.span<{ $tone: OutcomeTone }>`\n flex: 0 0 auto;\n align-self: center;\n font-size: var(--font-size-compact);\n font-weight: 700;\n color: ${({ $tone }) => TONE_TEXT[$tone]};\n`;\n\nconst CommandList__Text = styled.span`\n flex: 1 1 auto;\n min-width: 0;\n font-size: var(--font-size-compact);\n line-height: var(--line-height-body);\n /* Wraps, never truncates: the numbers are at the end of the sentence. */\n overflow-wrap: anywhere;\n`;\n\nconst CommandList__Dismiss = styled.button`\n flex: 0 0 auto;\n appearance: none;\n padding: var(--inset-glyph);\n border: 0;\n background: transparent;\n color: var(--color-text-muted);\n font: inherit;\n font-size: var(--font-size-xs);\n cursor: pointer;\n\n &:hover,\n &:focus-visible {\n color: var(--color-text-primary);\n }\n ${focusRing}\n`;\n","import { RailRegistryContext } from \"@ksp-gonogo/sitrep-sdk/spine\";\nimport { type ReactNode, useSyncExternalStore } from \"react\";\nimport { createPanelStore } from \"../store/createPanelStore\";\nimport { createStore } from \"../store/createStore\";\nimport type { CommandDelayHandle } from \"./CommandDelay\";\n\n/**\n * One entry registered on a panel's delay rail: the handle plus an `id` that\n * stays the same for as long as the registering component is mounted.\n *\n * @category CommandDelay\n */\nexport interface CommandHandle extends CommandDelayHandle {\n /** Stable for the registering hook's whole mounted life. */\n id: string;\n}\n\n/**\n * A per-panel, off-tree registry of active command handles. `update` keeps a\n * registered handle current in place. The live data lives in the store, never\n * in a context value, so a registration change re-renders only the rail's\n * subscribers.\n */\nexport interface DelayRailStore {\n /** Add or replace a handle (keyed on `handle.id`). Returns its deregister function. */\n register(handle: CommandHandle): () => void;\n /** Update a registered handle's non-id fields in place (keyed on `id`). */\n update(id: string, next: Omit<CommandHandle, \"id\">): void;\n /** Subscribe to any change in the active handle set. Returns unsubscribe. */\n subscribe(onChange: () => void): () => void;\n /** The current active handles, insertion-ordered, referentially stable while unchanged. */\n getActiveHandles(): readonly CommandHandle[];\n}\n\n/**\n * Create an empty delay-rail store, for a caller that owns its lifetime and provides it through {@link DelayRailContext}.\n *\n * @category CommandDelay\n */\nexport function createDelayRailStore(): DelayRailStore {\n const base = createStore<CommandHandle>();\n return {\n register: base.register,\n update: base.update,\n subscribe: base.subscribe,\n getActiveHandles: base.getSnapshot,\n };\n}\n\nconst DelayPanelStore = createPanelStore(createDelayRailStore);\n\n/**\n * Carries only the store HANDLE, never the live registrations. `null` outside\n * a `Panel`, where `useRailEntry` and `useActiveHandles` degrade to no-ops.\n * Exported raw for a caller that owns the store's lifetime.\n *\n * @category CommandDelay\n */\nexport const DelayRailContext = DelayPanelStore.Context;\n\n/**\n * Mints one delay-rail store and holds it for its whole life. It is also the\n * rail registry every `useCommand` beneath it registers with, so each command's\n * outcome reaches the rail with nothing wired in the widget. The dashboard\n * mounts one around every widget; a dev build throws on a dispatch with none\n * mounted.\n *\n * @category CommandDelay\n */\nexport function DelayRailProvider({ children }: { children?: ReactNode }) {\n return (\n <DelayPanelStore.Provider>\n <CommandRailBridge>{children}</CommandRailBridge>\n </DelayPanelStore.Provider>\n );\n}\n\nfunction CommandRailBridge({ children }: { children?: ReactNode }) {\n const store = DelayPanelStore.useStore();\n return (\n <RailRegistryContext.Provider value={store}>\n {children}\n </RailRegistryContext.Provider>\n );\n}\n\n/**\n * The nearest store, or `null` outside a `Panel`. Does not subscribe, so a registering widget does not re-render when a sibling registers.\n *\n * @intent Published so a third party can build a representation of the rail of its own, as Console does.\n * @category CommandDelay\n */\nexport const useDelayRailStore = DelayPanelStore.useStore;\n\n// Stable no-store fallbacks: a fresh `[]` would make `useSyncExternalStore` loop.\nconst NO_SUBSCRIBE = (): (() => void) => () => {};\nconst NO_HANDLES: readonly CommandHandle[] = [];\nconst NO_HANDLES_SNAPSHOT = (): readonly CommandHandle[] => NO_HANDLES;\n\n/**\n * The panel's active command handles, or `[]` when the store is empty or there\n * is no store in the tree. A subscriber (`Panel.Delay`) re-renders only when\n * the active handle set changes.\n *\n * @category CommandDelay\n */\nexport function useActiveHandles(): readonly CommandHandle[] {\n const store = useDelayRailStore();\n return useSyncExternalStore(\n store ? store.subscribe : NO_SUBSCRIBE,\n store ? store.getActiveHandles : NO_HANDLES_SNAPSHOT,\n store ? store.getActiveHandles : NO_HANDLES_SNAPSHOT,\n );\n}\n","import { useCallback, useSyncExternalStore } from \"react\";\nimport type {\n ComponentSlotRegistry,\n ComponentSlotSegment,\n Contributed,\n ContributionEntry,\n ContributionSlotId,\n} from \"./contributions\";\nimport { createPanelStore } from \"./store/createPanelStore\";\nimport { createStore, type Store } from \"./store/createStore\";\nimport { useWidgetMeta } from \"./WidgetMetaContext\";\n\n/*\n * The contribution READ seam: the per-widget store plus the hooks that read\n * it, spine-free. The WRITE half (the per-frame aggregation that pulls\n * telemetry) mounts this same `ContributionsPanelStore` and writes into it.\n */\n\n/**\n * One slot's row in a widget's contribution store: the full slot id and the\n * entries its contributions produced, in render order.\n *\n * @category Extensions\n */\nexport interface ContributionSlotEntry {\n /** The full slot id, such as `\"crew-status.badges\"`. */\n id: string;\n // A primitive contribution cannot carry a provenance stamp and is stored verbatim, hence the loose type.\n /** The entries, each stamped with its contribution id and owner when it is an object. */\n entries: readonly unknown[];\n}\n\nconst EMPTY_ENTRIES: readonly unknown[] = Object.freeze([]);\n\n/**\n * The host-invariant segments at RUNTIME, so both halves of the seam agree on\n * which bare names complete to `${componentId}.<segment>`. Checked against\n * `ComponentSlotSegment` in both directions, so the two lists cannot drift.\n */\nexport const COMPONENT_SLOT_SEGMENTS = [\n \"badges\",\n \"filters\",\n \"meters\",\n] as const satisfies readonly ComponentSlotSegment[];\n\ntype _EverySegmentListed =\n ComponentSlotSegment extends (typeof COMPONENT_SLOT_SEGMENTS)[number]\n ? true\n : never;\nconst _everySegmentListed: _EverySegmentListed = true;\nvoid _everySegmentListed;\n\n/**\n * The standard contribution segments every widget carries: `badges`. An\n * Uplink fills any widget's header badges by contributing to\n * `\"<widget-id>.badges\"` with its handle's `registerContribution`, without the\n * widget declaring the slot. `compute` returns {@link BadgeEntry} items. How\n * the panel draws them is under Standard slots on {@link Panel}.\n *\n * @example\n * ```ts\n * EXAMPLE.registerContribution({\n * id: \"cadence-badge\",\n * contributes: \"space-center-status.badges\",\n * deps: [\"example.heartbeat\"],\n * requires: \"example\",\n * compute: (topics) =>\n * topics[\"example.heartbeat\"]\n * ? [{ id: \"cadence\", label: \"Publishing\", tone: \"info\" as const }]\n * : null,\n * });\n * ```\n *\n * @category Panel\n */\nexport const FRAMEWORK_CONTRIBUTION_SEGMENTS = [\n \"badges\",\n] as const satisfies readonly ComponentSlotSegment[];\n\nconst EMPTY_SLOT_ENTRIES: readonly ContributionSlotEntry[] = Object.freeze([]);\n\n/**\n * The per-widget store of contribution entries, one {@link ContributionSlotEntry}\n * per slot. {@link ContributionsProvider} mounts and fills it; the read hooks\n * ({@link useContributions}, {@link useContributionsBySlotId}) read it. A\n * widget reads through those hooks rather than the store.\n *\n * @category Extensions\n */\nexport const ContributionsPanelStore = createPanelStore<\n Store<ContributionSlotEntry>\n>(() => createStore<ContributionSlotEntry>());\n\n/** The single subscription point: one `useSyncExternalStore` for the whole store, however many slots are read. */\nfunction useAllContributionSlots(): readonly ContributionSlotEntry[] {\n const store = ContributionsPanelStore.useStore();\n const subscribe = useCallback(\n (onChange: () => void) => (store ? store.subscribe(onChange) : () => {}),\n [store],\n );\n const getSnapshot = useCallback(\n (): readonly ContributionSlotEntry[] =>\n store ? store.getSnapshot() : EMPTY_SLOT_ENTRIES,\n [store],\n );\n return useSyncExternalStore(subscribe, getSnapshot);\n}\n\n/**\n * The entries contributed to the slot with the full id `slot`, untyped. Empty\n * when nothing contributes or no contribution store is mounted. Prefer\n * {@link useContributions}, which types the entries from the slot's\n * declaration; use this for a slot id built at runtime.\n *\n * @category Extensions\n */\nexport function useContributionsBySlotId(slot: string): readonly unknown[] {\n const snapshot = useAllContributionSlots();\n return snapshot.find((e) => e.id === slot)?.entries ?? EMPTY_ENTRIES;\n}\n\n/**\n * The entries contributed to a slot, for a widget to draw. Three call shapes:\n *\n * - a full slot id declared in `ContributionRegistry` (`\"ship-map.part-meters\"`):\n * its entries, typed and stamped with `contributionId` and `owner`\n * - a component segment (`\"badges\"`, `\"filters\"`, `\"meters\"`): completed to\n * `${componentId}.<segment>` for the widget it is called in, typed from\n * `ComponentSlotRegistry`\n * - an array of full slot ids: an object keyed by slot id\n *\n * Only the highest `priority` band of contributions to a slot is present.\n * Without a {@link WidgetMetaContext} and a {@link ContributionsProvider}\n * above the caller it returns empty lists. The dashboard and the test render\n * helpers mount both; a test rendering a widget bare must mount them too.\n *\n * @example\n * ```tsx\n * function ContributedBadges() {\n * const badges = useContributions(\"badges\");\n * return badges.map((b) => (\n * <Badge key={b.id} tone={b.tone}>\n * {b.label}\n * </Badge>\n * ));\n * }\n * ```\n *\n * @category Extensions\n */\nexport function useContributions<Slot extends ContributionSlotId>(\n slot: Slot,\n): readonly Contributed<ContributionEntry<Slot>>[];\n// A reusable component writes only the segment (\"filters\"), completed from `useWidgetMeta()` and typed via `ComponentSlotRegistry`.\nexport function useContributions<Segment extends ComponentSlotSegment>(\n segment: Segment,\n): readonly ComponentSlotRegistry[Segment][];\n// An array of full slot ids.\nexport function useContributions<\n const Slots extends readonly ContributionSlotId[],\n>(\n slots: Slots,\n): { [Slot in Slots[number]]: readonly Contributed<ContributionEntry<Slot>>[] };\nexport function useContributions(\n slotOrSlots: string | readonly string[],\n): unknown {\n const meta = useWidgetMeta();\n const snapshot = useAllContributionSlots();\n /*\n * A declared SEGMENT is completed to `${componentId}.${segment}`; anything\n * else is used as-is. The test is membership of `COMPONENT_SLOT_SEGMENTS`,\n * not the absence of a dot, since an undotted slot id can be app-wide.\n */\n const isSegment = (slot: string): boolean =>\n (COMPONENT_SLOT_SEGMENTS as readonly string[]).includes(slot);\n const complete = (slot: string): string =>\n meta && isSegment(slot) ? `${meta.componentId}.${slot}` : slot;\n const read = (slot: string): readonly unknown[] =>\n snapshot.find((e) => e.id === complete(slot))?.entries ?? EMPTY_ENTRIES;\n\n if (typeof slotOrSlots === \"string\") return read(slotOrSlots);\n return Object.fromEntries(slotOrSlots.map((slot) => [slot, read(slot)]));\n}\n","import {\n type AnyContribution,\n hasHost,\n logger,\n type ModSettingsModel,\n PerfBudget,\n type TopicId,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport {\n type FrameToken,\n getContributionsForSlot,\n modSettingsTopic,\n onContributionsChange,\n type ProcessorHandle,\n processorRuntimeFor,\n readModSetting,\n runContributionCompute,\n subscribeTopicRead,\n useTelemetryClientOptional,\n useTelemetryStoreOptional,\n} from \"@ksp-gonogo/sitrep-sdk/spine\";\nimport {\n type ReactElement,\n type ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useSyncExternalStore,\n} from \"react\";\nimport {\n COMPONENT_SLOT_SEGMENTS,\n type ContributionSlotEntry,\n ContributionsPanelStore,\n useContributions,\n useContributionsBySlotId,\n} from \"./contributionsRead\";\nimport {\n type DomainAvailabilityStore,\n useDomainAvailabilityStore,\n} from \"./domainAvailability\";\nimport type { Store } from \"./store/createStore\";\nimport { useWidgetMeta } from \"./WidgetMetaContext\";\n\n/*\n * The contribution WRITE seam: the per-frame aggregation that pulls each\n * contribution's telemetry deps and fans the computed entries into the\n * per-widget `ContributionsPanelStore` the read hooks share.\n */\n\n/*\n * One PerfBudget per slot id, created lazily since slot ids are open-ended. It\n * counts entry sets that actually MOVED, not aggregation passes, which run on\n * every animation frame; 30 is about 3x the wire sample rate.\n */\nconst slotBudgets = new Map<string, PerfBudget>();\nfunction getSlotPerfBudget(slot: string): PerfBudget {\n let budget = slotBudgets.get(slot);\n if (!budget) {\n budget = new PerfBudget({\n name: `Contributions \"${slot}\" entries recomputed/sec`,\n threshold: 30,\n windowMs: 1000,\n unit: \"recomputes\",\n });\n slotBudgets.set(slot, budget);\n }\n return budget;\n}\n\n// Stable empty snapshot with no `TelemetryProvider`: a fresh `{}` would make `useSyncExternalStore` see a change every render.\nconst EMPTY_TOPIC_VALUES: Readonly<Record<string, unknown>> = Object.freeze({});\n\n// Memoised per slot, since useSyncExternalStore needs a referentially stable snapshot between changes.\nconst slotCache = new Map<string, AnyContribution[]>();\nlet cacheValid = false;\nonContributionsChange(() => {\n cacheValid = false;\n slotCache.clear();\n});\nfunction getContributionsForSlotCached(slot: string): AnyContribution[] {\n if (!cacheValid) {\n slotCache.clear();\n cacheValid = true;\n }\n let cached = slotCache.get(slot);\n if (cached === undefined) {\n cached = getContributionsForSlot(slot);\n slotCache.set(slot, cached);\n }\n return cached;\n}\n\n/**\n * True when the two entries carry the same value: the same reference, or two\n * objects shallow-equal over their own keys. Reference equality alone would\n * never hold, since the aggregation stamps provenance onto a fresh row every\n * frame. Shallow, because the rows are flat.\n */\nfunction entryUnchanged(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) return true;\n if (typeof a !== \"object\" || typeof b !== \"object\") return false;\n if (a === null || b === null) return false;\n if (Array.isArray(a) !== Array.isArray(b)) return false;\n const aKeys = Object.keys(a);\n if (aKeys.length !== Object.keys(b).length) return false;\n for (const key of aKeys) {\n if (\n !Object.is(\n (a as Record<string, unknown>)[key],\n (b as Record<string, unknown>)[key],\n )\n ) {\n return false;\n }\n }\n return true;\n}\n\n/** Element-wise: true when every entry holds the same value as before. */\nfunction entriesUnchanged(\n a: readonly unknown[],\n b: readonly unknown[],\n): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (!entryUnchanged(a[i], b[i])) return false;\n }\n return true;\n}\n\nfunction shallowEqualValues(\n a: Record<string, unknown>,\n b: Record<string, unknown>,\n): boolean {\n const keys = Object.keys(a);\n if (keys.length !== Object.keys(b).length) return false;\n return keys.every((k) => Object.hasOwn(b, k) && Object.is(a[k], b[k]));\n}\n\ninterface ModSettingRef {\n readonly uplink: string;\n readonly key: string;\n}\n\nfunction isModSettingDep(dep: object): dep is { modSetting: ModSettingRef } {\n return \"modSetting\" in dep;\n}\n\n/** The key a setting dep's value arrives under in `compute`'s argument. */\nfunction modSettingKey(ref: ModSettingRef): string {\n return `settings.${ref.uplink}.${ref.key}`;\n}\n\n/** The topics a contribution declared, which it may read through any seam unreported. */\nfunction declaredTopicsOf(def: AnyContribution): ReadonlySet<string> {\n const topics = new Set<string>();\n for (const d of def.deps ?? []) {\n if (typeof d === \"string\") {\n topics.add(d);\n continue;\n }\n if (\"reading\" in d) {\n topics.add(d.reading);\n continue;\n }\n if (isModSettingDep(d)) {\n topics.add(modSettingKey(d.modSetting));\n }\n }\n if (def.requires) topics.add(`${def.requires}.available`);\n return topics;\n}\n\n/**\n * One slot's aggregation pipeline: bulk-reads the union of every gated-in\n * contribution's `deps` once per frame, calls each `compute()` in a plain loop\n * (no hooks, so the registered set may change freely), isolates a throwing\n * contribution, and writes the result under this slot's key. Its own\n * component, so each slot's hooks have a stable position.\n */\nfunction SlotAggregator({\n slot,\n store,\n}: {\n slot: string;\n store: Store<ContributionSlotEntry>;\n}) {\n const contribs = useSyncExternalStore(\n onContributionsChange,\n () => getContributionsForSlotCached(slot),\n () => getContributionsForSlotCached(slot),\n );\n\n const unionDeps = useMemo(() => {\n const topics = new Set<TopicId>();\n const processors = new Map<string, ProcessorHandle<unknown>>();\n const settings = new Map<string, ModSettingRef>();\n for (const c of contribs) {\n for (const d of c.deps ?? []) {\n if (typeof d === \"string\") {\n topics.add(d as TopicId);\n continue;\n }\n // A reading dep subscribes to the same wire topic a bare id does.\n if (\"reading\" in d) {\n topics.add(d.reading as TopicId);\n continue;\n }\n if (isModSettingDep(d)) {\n settings.set(modSettingKey(d.modSetting), d.modSetting);\n continue;\n }\n processors.set(d.id, d);\n }\n }\n return {\n topics: Array.from(topics),\n processors: Array.from(processors.values()),\n settings: Array.from(settings.entries()),\n };\n }, [contribs]);\n\n const client = useTelemetryClientOptional();\n const telemetryStore = useTelemetryStoreOptional();\n const processorRuntime = telemetryStore\n ? processorRuntimeFor(telemetryStore)\n : undefined;\n\n // Processor freshness rides the same frame boundary as the Topic reads, so no per-processor subscription is needed.\n useEffect(() => {\n if (!processorRuntime) return;\n const deactivates = unionDeps.processors.map((p) =>\n processorRuntime.activate(p.id),\n );\n return () => {\n for (const d of deactivates) d();\n };\n }, [processorRuntime, unionDeps.processors]);\n\n const subscribe = useCallback(\n (onChange: () => void) => {\n if (!client || !telemetryStore) return () => {};\n // Through the shared read seam, which also holds up each dep's reckoner inputs.\n const settingsTopics = new Set(\n unionDeps.settings.map(([, ref]) => modSettingsTopic(ref.uplink)),\n );\n const unsubscribeInputs = [...unionDeps.topics, ...settingsTopics].map(\n (topic) => subscribeTopicRead(client, telemetryStore, topic),\n );\n const unsubscribeFrame = telemetryStore.subscribeFrame(() => {\n // Evaluate Processors BEFORE notifying React: this listener can fire before the evaluator's shared one. Idempotent.\n if (unionDeps.processors.length > 0) processorRuntime?.evaluateActive();\n onChange();\n });\n return () => {\n unsubscribeFrame();\n for (const u of unsubscribeInputs) u();\n };\n },\n [client, telemetryStore, processorRuntime, unionDeps],\n );\n\n // Keyed on the `FrameToken`, which is stable for a whole frame, so re-reads within one frame return the identical object.\n const topicCacheRef = useRef<{\n token: FrameToken;\n values: Record<string, unknown>;\n } | null>(null);\n\n const getSnapshot = useCallback((): Record<string, unknown> => {\n if (!telemetryStore) return EMPTY_TOPIC_VALUES;\n if (\n unionDeps.topics.length === 0 &&\n unionDeps.processors.length === 0 &&\n unionDeps.settings.length === 0\n ) {\n return EMPTY_TOPIC_VALUES;\n }\n const token = telemetryStore.currentFrame();\n const cached = topicCacheRef.current;\n if (cached && cached.token === token) return cached.values;\n const values: Record<string, unknown> = {};\n for (const topic of unionDeps.topics) {\n // `undefined` is no point yet, and a tombstone's `null` passes through as the mod's confirmed absence.\n const point = telemetryStore.sample(topic, token);\n values[topic] = point ? point.payload : undefined;\n }\n for (const p of unionDeps.processors) {\n values[p.id] = processorRuntime?.value(p.id);\n }\n for (const [key, ref] of unionDeps.settings) {\n const point = telemetryStore.sample<ModSettingsModel | null>(\n modSettingsTopic(ref.uplink),\n token,\n );\n values[key] = point ? readModSetting(point.payload, ref.key) : undefined;\n }\n /*\n * A frame arrives every animation tick whether or not anything moved, so\n * the previous object is kept while its contents are unchanged. Work that\n * must advance with the clock belongs in a processor.\n */\n const next =\n cached && shallowEqualValues(cached.values, values)\n ? cached.values\n : values;\n topicCacheRef.current = { token, values: next };\n return next;\n }, [telemetryStore, processorRuntime, unionDeps]);\n\n const topicValues = useSyncExternalStore(subscribe, getSnapshot);\n // The host's presence store, the same one an augment's `requires` reads, so both gates follow one rule.\n const availability = useDomainAvailabilityStore();\n const presence = useSyncExternalStore(\n availability ? availability.subscribe : NO_AVAILABILITY_SUBSCRIBE,\n () => presenceKey(contribs, availability),\n () => presenceKey(contribs, availability),\n );\n const budget = useMemo(() => getSlotPerfBudget(slot), [slot]);\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: `presence` is an intentional trigger, since the store keeps its identity when a Domain comes or goes.\n useEffect(() => {\n const collected: unknown[] = [];\n for (const def of contribs) {\n if (def.requires && !availability?.isAvailable(def.requires)) continue;\n try {\n const result = runContributionCompute(\n def.id,\n declaredTopicsOf(def),\n () => def.compute(topicValues as never),\n );\n if (result) {\n for (const entry of result) {\n if (entry !== null && typeof entry === \"object\") {\n collected.push({\n ...entry,\n contributionId: def.id,\n owner: def.owner,\n });\n } else {\n // A primitive contribution cannot carry the provenance stamp, so it is stored verbatim.\n collected.push(entry);\n }\n }\n }\n } catch (err) {\n reportContributionThrew(def.id, err);\n }\n }\n const current = store.getSnapshot().find((e) => e.id === slot);\n if (current && entriesUnchanged(current.entries, collected)) return;\n // After the guard, so one record is one entry set that genuinely moved.\n budget.record();\n store.update(slot, { entries: collected });\n // update() returns early on an unknown id, so the first write registers.\n if (!current) store.register({ id: slot, entries: collected });\n }, [contribs, topicValues, slot, store, budget, availability, presence]);\n\n return null;\n}\n\nconst NO_AVAILABILITY_SUBSCRIBE = (): (() => void) => () => {};\n\n/** Which of these contributions' Domains are present, as a string, so a presence change re-runs them and nothing else does. */\nfunction presenceKey(\n contribs: readonly AnyContribution[],\n availability: DomainAvailabilityStore | null,\n): string {\n return contribs\n .map((c) =>\n c.requires && availability?.isAvailable(c.requires) ? c.requires : \"\",\n )\n .join(\"|\");\n}\n\n/**\n * A contribution threw, which is an Uplink author's bug and must never be\n * silent. Falls back to `console.error` without a host, because the sdk's\n * `logger` throws when none is installed.\n */\nfunction reportContributionThrew(id: string, err: unknown): void {\n const error = err instanceof Error ? err : new Error(String(err));\n const message = `Contribution \"${id}\" threw; skipped`;\n if (hasHost()) logger.error(message, error);\n else console.error(message, error);\n}\n\n/**\n * Runs the registered contributions for the tree below it and stores their\n * entries for {@link useContributions} to read. Inside a widget it serves the\n * widget's declared `contributionSlots` plus the component segments\n * (`badges`, `filters`, `meters`) completed to `${componentId}.<segment>`.\n * `slots` names the full slot ids to serve instead, for a surface that is not\n * a widget, such as the screen header's `app.header-badges`. The dashboard\n * mounts one per widget.\n *\n * @category Extensions\n */\nexport function ContributionsProvider({\n slots,\n children,\n}: {\n slots?: readonly string[];\n children?: ReactNode;\n}): ReactElement {\n return (\n <ContributionsPanelStore.Provider>\n <ContributionsAggregation slots={slots}>\n {children}\n </ContributionsAggregation>\n </ContributionsPanelStore.Provider>\n );\n}\n\n/*\n * The framework-universal segments (`COMPONENT_SLOT_SEGMENTS`, shared with the\n * read half) are aggregated for EVERY widget on top of what it declared, so a\n * component owning one gets its contributions with nothing written by the\n * host. Only something every widget has belongs here; any other slot is\n * declared by the widget that hosts it.\n */\nfunction ContributionsAggregation({\n slots: named,\n children,\n}: {\n slots?: readonly string[];\n children?: ReactNode;\n}) {\n const meta = useWidgetMeta();\n const store = ContributionsPanelStore.useStore();\n const slots = useMemo(() => {\n if (named) return named;\n const declared = meta?.contributionSlots ?? [];\n if (!meta) return declared;\n const merged = [...declared];\n for (const segment of COMPONENT_SLOT_SEGMENTS) {\n const slot = `${meta.componentId}.${segment}`;\n if (!merged.includes(slot as never)) merged.push(slot as never);\n }\n return merged;\n }, [meta, named]);\n\n if (!store) return <>{children}</>;\n\n return (\n <>\n {slots.map((slot) => (\n <SlotAggregator key={slot} slot={slot} store={store} />\n ))}\n {children}\n </>\n );\n}\n\nexport { useContributions, useContributionsBySlotId };\n","import styled from \"styled-components\";\nimport { fitBox } from \"./fitBox\";\nimport { focusRing } from \"./focusRing\";\n\n/**\n * Props for {@link FilterChip}.\n *\n * @category FilterList\n */\nexport interface FilterChipProps {\n /** The chip's text, which is also its accessible name. */\n label: string;\n /** Whether the filter is on. Drives `aria-pressed` and the filled look. */\n selected: boolean;\n /** An optional count drawn after the label, such as how many rows the filter matches. */\n count?: number;\n /** Called on press; flip `selected` here. */\n onToggle: () => void;\n}\n\n/**\n * A pill-shaped toggle `button` for one filter term, with `aria-pressed`\n * following `selected` and an optional count after the label. It is what\n * {@link useRowFilter} draws for each contributed term; use it directly for a\n * filter row of your own.\n *\n * @example\n * ```tsx\n * <Cluster justify=\"start\" wrap role=\"group\" aria-label=\"Filters\">\n * {BODIES.map((body) => (\n * <FilterChip\n * key={body}\n * label={body}\n * selected={shown.has(body)}\n * count={countFor(body)}\n * onToggle={() => toggle(body)}\n * />\n * ))}\n * </Cluster>\n * ```\n *\n * @category FilterList\n */\nexport function FilterChip({\n label,\n selected,\n count,\n onToggle,\n}: FilterChipProps) {\n return (\n <ChipButton\n type=\"button\"\n $selected={selected}\n onClick={onToggle}\n aria-pressed={selected}\n >\n <span>{label}</span>\n {count !== undefined && <Count>{count}</Count>}\n </ChipButton>\n );\n}\n\nconst ChipButton = styled.button<{ $selected: boolean }>`\n ${fitBox(\"chip\")}\n display: inline-flex;\n align-items: center;\n gap: var(--gap-glyph-control);\n padding: var(--inset-control-small);\n /* --radius-pill keeps the stadium shape through a padding change. */\n border-radius: var(--radius-pill);\n font-size: var(--font-size-compact);\n font-weight: 600;\n /* Sentence case: a real toggle button rather than a Badge, and its label is not the kit's to shout. */\n cursor: pointer;\n transition:\n background var(--duration-fast),\n border-color var(--duration-fast),\n color var(--duration-fast);\n\n background: ${({ $selected }) =>\n $selected ? \"var(--color-accent-fg)\" : \"transparent\"};\n color: ${({ $selected }) =>\n $selected ? \"var(--color-text-inverse)\" : \"var(--color-text-dim)\"};\n border: 1px solid\n ${({ $selected }) =>\n $selected ? \"var(--color-accent-fg)\" : \"var(--color-border-subtle)\"};\n\n &:hover {\n border-color: var(--color-accent-fg);\n color: ${({ $selected }) =>\n $selected ? \"var(--color-text-inverse)\" : \"var(--color-text-primary)\"};\n }\n\n ${focusRing}\n`;\n\nconst Count = styled.span`\n font-variant-numeric: tabular-nums;\n font-size: var(--font-size-caption);\n opacity: 0.75;\n`;\n","import { type InputHTMLAttributes, useRef } from \"react\";\nimport styled from \"styled-components\";\nimport { IconButton } from \"./Button\";\nimport { Input } from \"./Form\";\nimport { CloseIcon } from \"./Icons\";\n\n/**\n * The props of {@link SearchBox}.\n *\n * @category Form\n */\nexport interface SearchBoxProps\n extends Omit<\n InputHTMLAttributes<HTMLInputElement>,\n \"type\" | \"value\" | \"onChange\"\n > {\n value: string;\n onChange: (next: string) => void;\n /** The clear control's accessible name. Defaults to \"Clear search\". */\n clearLabel?: string;\n}\n\n/**\n * A search box with the kit's own clear control, shown while there is text to\n * clear. Clearing hands focus back to the field.\n *\n * @category Form\n */\nexport function SearchBox({\n value,\n onChange,\n clearLabel = \"Clear search\",\n className,\n ...rest\n}: Readonly<SearchBoxProps>) {\n const field = useRef<HTMLInputElement>(null);\n return (\n <SearchBox__Root className={className}>\n <SearchBox__Field\n {...rest}\n ref={field}\n type=\"search\"\n value={value}\n onChange={(event) => onChange(event.target.value)}\n />\n {value !== \"\" && (\n <SearchBox__Clear\n type=\"button\"\n aria-label={clearLabel}\n onClick={() => {\n onChange(\"\");\n field.current?.focus();\n }}\n >\n <CloseIcon size=\"var(--icon-size-control)\" />\n </SearchBox__Clear>\n )}\n </SearchBox__Root>\n );\n}\n\nconst SearchBox__Root = styled.div`\n position: relative;\n display: flex;\n align-items: center;\n`;\n\nconst SearchBox__Field = styled(Input)`\n && {\n padding-right: var(--inset-field-clearable-end);\n }\n`;\n\nconst SearchBox__Clear = styled(IconButton)`\n position: absolute;\n right: 0;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n`;\n","import { type ReactNode, useId, useState } from \"react\";\nimport { Cluster } from \"./Cluster\";\nimport type {\n ComponentSlotRegistry,\n ComponentSlotSegment,\n} from \"./contributions\";\nimport { useContributions } from \"./contributionsRead\";\nimport { FilterChip } from \"./FilterChip\";\nimport { Field, FieldLabel } from \"./Form\";\nimport { SearchBox } from \"./SearchBox\";\nimport { Stack } from \"./Stack\";\n\n/**\n * A filter made by {@link useRowFilter}. Test each row with `matches`. Its\n * control (contributed toggles, then a search box) is not part of this object:\n * it renders above the list it narrows when the filter is passed to\n * {@link FilterRegion} or to `Panel`'s `panelFilter`.\n *\n * @category FilterList\n */\nexport interface RowFilter {\n /** True when the row's search text contains every selected term and the typed text. */\n matches: (searchText: string) => boolean;\n /** Whether anything is narrowing the list right now, for empty-state copy. */\n active: boolean;\n}\n\n/**\n * A declared segment whose contributions are plain search terms. `badges`, for\n * one, contributes a `BadgeEntry`, which cannot be substring-matched.\n */\nexport type TermSegment = {\n [Segment in ComponentSlotSegment]: ComponentSlotRegistry[Segment] extends string\n ? Segment\n : never;\n}[ComponentSlotSegment];\n\n/**\n * Options for {@link useRowFilter}.\n *\n * @category FilterList\n */\nexport interface UseRowFilterOptions {\n /**\n * The contribution segment to pull toggle terms from. Defaults to the\n * universal `filters`; override it only for a declared segment of your own\n * whose entries are search terms.\n */\n segment?: TermSegment;\n /** Accessible name for the search box. Defaults to \"Search\". */\n label?: string;\n /** Placeholder in the search box. Defaults to \"Filter...\". */\n placeholder?: string;\n}\n\n/**\n * The filter behind {@link FilterList}, for a host that renders its own rows,\n * such as a table that must keep its columns.\n *\n * Selected toggle terms and the typed text all have to match: each is a\n * case-insensitive substring of the row's search text. Nothing selected and\n * nothing typed matches everything, and `active` is then false. The toggle\n * terms come from the widget's contributions, each term listed once. Place the\n * filter's control by passing the result to {@link FilterRegion} around the\n * list, or to `Panel`'s `panelFilter` for a filter over the whole body.\n *\n * @example\n * ```tsx\n * const filter = useRowFilter({ placeholder: \"Filter subjects...\" });\n * const shown = subjects.filter((s) => filter.matches(`${s.title} ${s.biome}`));\n *\n * return (\n * <FilterRegion filter={filter} fill>\n * {shown.length > 0 ? (\n * <SubjectTable rows={shown} />\n * ) : (\n * <EmptyState>\n * {filter.active ? \"Nothing matches the filter\" : \"No subjects yet\"}\n * </EmptyState>\n * )}\n * </FilterRegion>\n * );\n * ```\n *\n * @category FilterList\n */\nexport function useRowFilter({\n segment = \"filters\",\n label = \"Search\",\n placeholder = \"Filter...\",\n}: UseRowFilterOptions = {}): RowFilter {\n const terms = useContributions(segment);\n // Distinct terms, in contribution order: two providers can land the same word, and a doubled toggle is just noise.\n const uniqueTerms = [...new Set(terms)];\n\n const [selected, setSelected] = useState<ReadonlySet<string>>(\n () => new Set(),\n );\n const [typed, setTyped] = useState(\"\");\n const searchId = useId();\n\n const toggle = (term: string) => {\n setSelected((prev) => {\n const next = new Set(prev);\n if (next.has(term)) next.delete(term);\n else next.add(term);\n return next;\n });\n };\n\n const needles = [...selected, typed]\n .filter((s) => s.length > 0)\n .map((s) => s.toLowerCase());\n\n const control = (\n <Stack gap=\"related-packed\">\n {uniqueTerms.length > 0 && (\n <Cluster\n justify=\"start\"\n gap=\"related-packed\"\n wrap\n role=\"group\"\n aria-label=\"Filters\"\n >\n {uniqueTerms.map((term) => (\n <FilterChip\n key={term}\n label={term}\n selected={selected.has(term)}\n onToggle={() => toggle(term)}\n />\n ))}\n </Cluster>\n )}\n <Field>\n <FieldLabel htmlFor={searchId}>{label}</FieldLabel>\n <SearchBox\n id={searchId}\n value={typed}\n placeholder={placeholder}\n onChange={setTyped}\n />\n </Field>\n </Stack>\n );\n\n const filter: RowFilter = {\n matches: (searchText: string) => {\n const haystack = searchText.toLowerCase();\n return needles.every((needle) => haystack.includes(needle));\n },\n active: needles.length > 0,\n };\n CONTROLS.set(filter, control);\n return filter;\n}\n\nconst CONTROLS = new WeakMap<RowFilter, ReactNode>();\n\n/** The control of a filter made by `useRowFilter`. Kit-internal: hosts place it through `FilterRegion` or `panelFilter`. */\nexport function filterControlOf(filter: RowFilter): ReactNode {\n return CONTROLS.get(filter) ?? null;\n}\n\n/**\n * The props of {@link FilterRegion}.\n *\n * @category FilterList\n */\nexport interface FilterRegionProps {\n /** The filter from {@link useRowFilter} whose control is drawn. */\n filter: RowFilter;\n /** The list the filter narrows, drawn under its control. */\n children?: ReactNode;\n /** Take the remaining height, for a region whose list scrolls. */\n fill?: boolean;\n}\n\n/**\n * A filtered list with its filter control above it: the way to place a\n * {@link useRowFilter} control inside a widget body. It does not filter the\n * children itself; filter them with the same `filter.matches`.\n *\n * @category FilterList\n */\nexport function FilterRegion({\n filter,\n children,\n fill = false,\n}: FilterRegionProps) {\n return (\n <Stack gap=\"related-dense\" fill={fill}>\n {filterControlOf(filter)}\n {children}\n </Stack>\n );\n}\n","import type { ComponentPropsWithoutRef, ReactNode } from \"react\";\nimport styled from \"styled-components\";\n\n/**\n * Props for {@link FramedDisplay}. Any other `div` attribute passes through.\n *\n * @category Layout\n */\nexport interface FramedDisplayProps extends ComponentPropsWithoutRef<\"div\"> {\n children?: ReactNode;\n /**\n * Adds an inner gutter between the frame and its contents, for content that\n * carries no margin of its own.\n */\n padded?: boolean;\n /**\n * A small label inlaid over the frame's top-left corner, naming the visual\n * (which frame a drawing is in, what a gauge reads) on the drawing itself.\n * Prefer it to a caption element beside the frame: a panel whose body holds\n * only this frame gives it a thinner inset when narrow, and a caption beside\n * it would cost that. The caption sets its own type (caption size, muted\n * colour), so pass bare text rather than a sized `Text`. Omitted, nothing is\n * drawn.\n */\n caption?: ReactNode;\n /**\n * A single line inlaid over the frame's bottom-left corner: a note or\n * status about what is drawn (an axis warning, a data age). Set on the same\n * opaque backing as `caption`; its colour is the caller's, so a warning can\n * pass a toned `Text`. Omitted, nothing is drawn.\n */\n footer?: ReactNode;\n /**\n * A band along the frame's bottom edge, inside the same border, for a trend\n * of the figure drawn above it. It is never padded: its content runs to the\n * frame's inner edges, while `padded` insets only the drawing above. Size\n * the content to the band (it takes three tenths of the frame's height,\n * within a minimum and a maximum) rather than giving the band a height. Omitted, the\n * frame is the one drawing.\n */\n strip?: ReactNode;\n}\n\nconst STRIP_MIN_PX = 24;\nconst STRIP_MAX_PX = 96;\n\n/**\n * A bordered, sunken box for visual content (SVG diagrams, canvases, maps,\n * gauges) inside an ordinary padded panel body, so readouts beside it keep the\n * standard inset and the diagram has a visible edge. A direct `svg` or\n * `canvas` child fills the frame.\n *\n * It does not scroll or size itself: the caller decides how much room the\n * visual gets. It grows with `flex: 1`, so a tall flex sibling squeezes it;\n * overlay a control on the frame rather than placing it beside. The visual\n * fills to the rounded edge unless `padded`. A drawing and its trend share one\n * frame through `strip` rather than two frames stacked.\n *\n * @category Layout\n */\nexport function FramedDisplay({\n children,\n padded,\n caption,\n footer,\n strip,\n ...rest\n}: FramedDisplayProps) {\n const hasStrip = strip !== undefined && strip !== null && strip !== false;\n return (\n <FramedDisplay__Box\n $padded={padded && !hasStrip}\n $stacked={hasStrip}\n {...rest}\n >\n {hasStrip ? (\n <>\n <FramedDisplay__Main $padded={padded}>{children}</FramedDisplay__Main>\n <FramedDisplay__Strip data-framed-display-strip=\"\">\n {strip}\n </FramedDisplay__Strip>\n </>\n ) : (\n children\n )}\n {caption !== undefined && caption !== null && (\n <FramedDisplay__Caption>{caption}</FramedDisplay__Caption>\n )}\n {footer !== undefined && footer !== null && (\n <FramedDisplay__Footer>{footer}</FramedDisplay__Footer>\n )}\n </FramedDisplay__Box>\n );\n}\n\nconst FramedDisplay__Box = styled.div<{\n $padded?: boolean;\n $stacked?: boolean;\n}>`\n position: relative;\n display: flex;\n flex-direction: ${({ $stacked }) => ($stacked ? \"column\" : \"row\")};\n min-height: 0;\n min-width: 0;\n /* Sunken: the visual sits inside the panel surface rather than floating on it. */\n background: var(--color-surface-sunken);\n border: 1px solid var(--color-border-subtle);\n /* The container answers with a corner proportioned to the box, so a frame never needs to know its own size. */\n border-radius: var(--radius-display-frame);\n overflow: hidden;\n padding: ${({ $padded }) => ($padded ? \"var(--inset-framed-display)\" : \"0\")};\n\n /* A child SVG or canvas fills the frame instead of its intrinsic 300x150. */\n & > svg,\n & > canvas {\n display: block;\n width: 100%;\n height: 100%;\n }\n`;\n\n/** The drawing above a strip: it takes the height the strip leaves, and carries the frame's gutter itself so the strip stays flush. */\nconst FramedDisplay__Main = styled.div<{ $padded?: boolean }>`\n flex: 7 1 0;\n display: flex;\n min-height: 0;\n min-width: 0;\n padding: ${({ $padded }) => ($padded ? \"var(--inset-framed-display)\" : \"0\")};\n\n & > svg,\n & > canvas {\n display: block;\n width: 100%;\n height: 100%;\n }\n`;\n\n/**\n * Three parts in ten of the frame's height, floored so a trend stays legible\n * in a short frame and capped so it never outgrows the drawing it sits under.\n * A grow share, not a percentage height: a percentage does not resolve inside\n * a frame that is itself a stretched flex item, and the band then sizes to its\n * content.\n */\nconst FramedDisplay__Strip = styled.div`\n flex: 3 1 0;\n display: flex;\n min-height: ${STRIP_MIN_PX}px;\n max-height: ${STRIP_MAX_PX}px;\n min-width: 0;\n overflow: hidden;\n`;\n\n/*\n * An opaque backing in the frame's own surface, not a translucent scrim:\n * muted text over a scrim that lets a pale drawing through falls under 4.5:1.\n * Padded like GraphNotice's overlay pill, but pinned to the\n * frame's top-left rather than its bottom-left.\n *\n * Local sibling ordering inside this frame's own stacking context (the\n * caption over the drawing it labels), not app-global chrome, so the literal\n * z-index stays off the app's named rungs.\n */\nconst FramedDisplay__Caption = styled.div`\n position: absolute;\n top: var(--offset-frame-caption-top);\n left: var(--offset-frame-caption-left);\n z-index: 1;\n padding: var(--inset-notice-pill);\n border-radius: var(--radius-regular);\n /* The smallest rung, flush, so the label covers as little of the drawing as it can. */\n font-size: var(--font-size-caption);\n line-height: var(--line-height-flush);\n color: var(--color-text-muted);\n background: var(--color-surface-sunken);\n pointer-events: none;\n`;\n\n/** The caption's twin on the bottom edge: same backing and type, mirrored offsets. */\nconst FramedDisplay__Footer = styled(FramedDisplay__Caption)`\n top: auto;\n bottom: var(--offset-frame-footer-bottom);\n left: var(--offset-frame-footer-left);\n`;\n","import type { HTMLAttributes, ReactNode } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { RADIUS_VAR } from \"./scales\";\n\n/**\n * Where a {@link GraphNotice} sits: `\"overlay\"` over the graph's bottom-left\n * corner, `\"center\"` over its middle, `\"inline\"` as a row below it, `\"beside\"` as a column to its right.\n *\n * @category LineGraph\n */\nexport type GraphNoticePlacement = \"overlay\" | \"center\" | \"inline\" | \"beside\";\n\n/**\n * What {@link placeGraphNotice} reads: the space the graph and its notice\n * share, as measured, and whether the plot has anything a notice could cover.\n *\n * @category LineGraph\n */\nexport interface GraphNoticeSpace {\n /** Width of the area the graph and its notice share, in CSS pixels. */\n width: number;\n /** Height of that area, in CSS pixels. */\n height: number;\n /** The plot draws a trace, a curve or a layer, so an overlay would sit on data. */\n plotHasData: boolean;\n}\n\n/** Smallest empty plot that still has room for a pill laid over it. */\nconst OVERLAY_MIN = { width: 160, height: 72 } as const;\n\n/** A shared area at least this many times wider than tall puts the notice at the side, where a row below would take a large share of its height. */\nconst BESIDE_ASPECT = 2.5;\n\n/** Narrowest shared area where a side column leaves the plot a usable width. */\nconst BESIDE_MIN_WIDTH = 420;\n\n/**\n * Where the kit puts a graph's notice, decided from measured space.\n *\n * An empty plot with room takes it over its middle, since nothing is underneath\n * to cover. Otherwise it goes beside a wide, short graph, where a row below\n * would cost the plot a large share of its height, and below everything else.\n *\n * @category LineGraph\n */\nexport function placeGraphNotice({\n width,\n height,\n plotHasData,\n}: GraphNoticeSpace): GraphNoticePlacement {\n if (\n !plotHasData &&\n width >= OVERLAY_MIN.width &&\n height >= OVERLAY_MIN.height\n ) {\n return \"center\";\n }\n if (width >= BESIDE_MIN_WIDTH && width >= height * BESIDE_ASPECT) {\n return \"beside\";\n }\n return \"inline\";\n}\n\n/**\n * Props for {@link GraphNotice}.\n *\n * @category LineGraph\n */\nexport interface GraphNoticeProps extends HTMLAttributes<HTMLDivElement> {\n children?: ReactNode;\n /**\n * `overlay` pins the pill to the bottom-left corner over the graph (pair with\n * a `position: relative` ancestor such as `Fill`); `center` pins it to the\n * middle of that ancestor instead. `inline` sits as a flow\n * row below the graph, where an overlay would cover the x-axis labels.\n * `beside` sits as a flex column to its right. {@link placeGraphNotice}\n * picks one from measured space.\n */\n placement: GraphNoticePlacement;\n}\n\nconst PLACEMENT_STYLES = {\n overlay: css`\n position: absolute;\n bottom: var(--offset-graph-notice-bottom);\n left: var(--offset-graph-notice-left);\n `,\n center: css`\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n max-width: 90%;\n text-align: center;\n `,\n inline: css`\n flex: 0 0 auto;\n align-self: flex-start;\n max-width: 100%;\n margin-top: var(--gap-sub-readout);\n `,\n beside: css`\n flex: 0 1 30%;\n align-self: flex-start;\n max-width: 40%;\n `,\n} as const;\n\n/**\n * Faint degraded-state pill for a graph widget (\"no reference data\",\n * \"unknown body\"). `pointer-events: none`, so it never intercepts the graph's\n * clicks. `role=\"status\"` by default; override via `role`.\n *\n * @example Over a graph in a {@link Fill}\n * ```tsx\n * <Fill>\n * <LineGraph series={series} ariaLabel=\"Altitude trend\" />\n * {series.length === 0 && (\n * <GraphNotice placement=\"overlay\">no reference data</GraphNotice>\n * )}\n * </Fill>\n * ```\n *\n * @category LineGraph\n */\nexport function GraphNotice({\n placement,\n role = \"status\",\n children,\n ...rest\n}: GraphNoticeProps) {\n return (\n <GraphNotice__Root $placement={placement} role={role} {...rest}>\n {children}\n </GraphNotice__Root>\n );\n}\n\nconst GraphNotice__Root = styled.div<{ $placement: GraphNoticePlacement }>`\n font-size: var(--font-size-compact);\n color: var(--color-text-faint);\n /* No surface token is translucent, so this scrim stays a raw value. */\n background: rgba(0, 0, 0, 0.7);\n padding: var(--inset-notice-pill);\n border-radius: ${RADIUS_VAR.regular};\n pointer-events: none;\n\n ${({ $placement }) => PLACEMENT_STYLES[$placement]}\n`;\n","import type { Tone } from \"@ksp-gonogo/sitrep-sdk\";\nimport styled from \"styled-components\";\nimport { TONE_MARK } from \"./tone\";\n\n/**\n * Props for {@link LevelBars}.\n *\n * @category Meter\n */\nexport interface LevelBarsProps {\n /** How many bars are lit, or null when there is nothing to judge by. Zero is a reading, and draws every bar unlit. */\n lit: number | null;\n /** How many bars the glyph has. */\n of: number;\n /**\n * Colour of the lit bars.\n *\n * @defaultValue `\"neutral\"`\n */\n tone?: Tone;\n /** What the glyph measures, spoken before the count: \"Signal\" reads \"Signal 3 of 4\". */\n label?: string;\n}\n\n/**\n * A stepped level glyph, bars rising left to right with `lit` of them filled:\n * a signal-strength indicator. Sized in em, so it scales with the figure it\n * stands beside.\n *\n * @category Meter\n */\nexport function LevelBars({\n lit,\n of,\n tone = \"neutral\",\n label,\n}: LevelBarsProps) {\n const count = lit === null ? \"unknown\" : `${lit} of ${of}`;\n return (\n <LevelBars__Root\n role=\"img\"\n aria-label={label === undefined ? count : `${label} ${count}`}\n data-level-bars=\"\"\n >\n {Array.from({ length: of }, (_, index) => (\n <LevelBars__Bar\n // biome-ignore lint/suspicious/noArrayIndexKey: a bar is its position\n key={index}\n $fill={lit !== null && index < lit ? TONE_MARK[tone] : null}\n $height={(index + 1) / of}\n />\n ))}\n </LevelBars__Root>\n );\n}\n\nconst LevelBars__Root = styled.span`\n display: inline-flex;\n align-items: flex-end;\n gap: var(--gap-signal-bars);\n height: 0.8em;\n flex: none;\n`;\n\nconst LevelBars__Bar = styled.span<{ $fill: string | null; $height: number }>`\n width: 0.22em;\n height: ${({ $height }) => `${Math.round($height * 100)}%`};\n background: ${({ $fill }) => $fill ?? \"transparent\"};\n border: 1px solid ${({ $fill }) => $fill ?? \"var(--color-border-subtle)\"};\n`;\n","import type { BadgeEntry } from \"@ksp-gonogo/sitrep-sdk\";\nimport { createContext, type ReactNode, useContext } from \"react\";\n\n// `BadgeEntry`, the entry type of every widget's `${componentId}.badges` slot, is declared in the sdk.\nexport type { BadgeEntry };\n\nconst PanelBadgesCtx = createContext<readonly BadgeEntry[] | null>(null);\n\n/**\n * Supplies the badges contributed to the current widget's\n * `${componentId}.badges` slot to the {@link Panel} inside it, which draws\n * them after its own `panelBadges`. The dashboard mounts it for every widget,\n * fed by {@link useWidgetBadges}; a widget never mounts it itself.\n *\n * @category Panel\n */\nexport function PanelBadgesProvider({\n badges,\n children,\n}: {\n badges: readonly BadgeEntry[];\n children?: ReactNode;\n}) {\n return (\n <PanelBadgesCtx.Provider value={badges}>{children}</PanelBadgesCtx.Provider>\n );\n}\n\n/** Host-derived badges for the current widget, or null outside a dashboard. Internal: Panel.tsx is the only caller. */\nexport function usePanelBadgesContext(): readonly BadgeEntry[] | null {\n return useContext(PanelBadgesCtx);\n}\n","import { useSyncExternalStore } from \"react\";\nimport {\n NO_STATUS_BREAKDOWN,\n type StatusBreakdownEntry,\n usePanelStatusStore,\n} from \"./PanelStatusStore\";\n\n// `getSnapshot` must return a referentially stable value or useSyncExternalStore loops.\nconst NO_SUBSCRIBE = (): (() => void) => () => {};\nconst EMPTY_BREAKDOWN_SNAPSHOT = (): readonly StatusBreakdownEntry[] =>\n NO_STATUS_BREAKDOWN;\n\n/**\n * The panel's per-severity status counts, worst first, one row per severity\n * present, or an empty list when nothing has contributed or there is no\n * {@link PanelStatusStore} above. The collapsed header draws one dot per row.\n *\n * @category Panel\n */\nexport function useStatusBreakdown(): readonly StatusBreakdownEntry[] {\n const store = usePanelStatusStore();\n return useSyncExternalStore(\n store ? store.subscribe : NO_SUBSCRIBE,\n store ? store.getBreakdown : EMPTY_BREAKDOWN_SNAPSHOT,\n store ? store.getBreakdown : EMPTY_BREAKDOWN_SNAPSHOT,\n );\n}\n","import { useSyncExternalStore } from \"react\";\nimport { type StatusSummary, usePanelStatusStore } from \"./PanelStatusStore\";\n\n// Module-level so the no-store subscribe and snapshot functions keep one identity.\nconst NO_SUBSCRIBE = (): (() => void) => () => {};\nconst NULL_SUMMARY = (): StatusSummary | null => null;\n\n/**\n * The panel's winning status contribution (see {@link StatusSummary}), or\n * `null` when nothing has contributed or there is no {@link PanelStatusStore}\n * above. Re-renders only when the winner changes.\n *\n * @category Panel\n */\nexport function useStatusSummary(): StatusSummary | null {\n const store = usePanelStatusStore();\n return useSyncExternalStore(\n store ? store.subscribe : NO_SUBSCRIBE,\n store ? store.getSummary : NULL_SUMMARY,\n store ? store.getSummary : NULL_SUMMARY,\n );\n}\n","import {\n createContext,\n type RefObject,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\n/**\n * How a panel header's aside is currently drawn: `full`, inline beside the\n * title, or `collapsed`, behind the status dots and the toggle that opens it.\n *\n * @category Panel\n */\nexport type PanelAsideSize = \"full\" | \"collapsed\";\n\n/**\n * How much spare room the full aside needs, once collapsed, before it\n * re-expands. Collapsing reacts the instant content stops fitting; without this\n * margin a panel sitting exactly at the boundary flips on every measurement.\n */\nconst REEXPAND_MARGIN_PX = 24;\n\n/**\n * Whether the aside should be collapsed, given the row's available width and\n * the width the title and aside need side by side. The two directions have\n * different thresholds (see `REEXPAND_MARGIN_PX`).\n *\n * A `0` (unmeasured) width holds the previous state rather than deciding\n * anything.\n *\n * `previousNeededWidth` is what the content needed on the last measurement. The\n * dead band guards against the room moving under unchanged content; once the\n * content itself changes width it is a new question, answered with no margin,\n * so an aside that collapsed for a badge that has since gone re-expands.\n *\n * `roomHasNarrowed` says whether the row has been narrower than it was at any\n * point since the aside collapsed. A boundary can only be flipped across by\n * room that moves both ways, so a row that has only grown is also answered\n * with no margin: a tile still easing out to its width as its aside arrives\n * collapses a hair short of a fit, and would otherwise stay collapsed at rest\n * with room for it.\n */\nexport function nextAsideCollapsed(\n prevCollapsed: boolean,\n availableWidth: number,\n neededWidth: number,\n previousNeededWidth?: number,\n roomHasNarrowed = true,\n): boolean {\n if (availableWidth <= 0 || neededWidth <= 0) return prevCollapsed;\n const contentChanged =\n previousNeededWidth !== undefined &&\n previousNeededWidth > 0 &&\n previousNeededWidth !== neededWidth;\n return prevCollapsed && !contentChanged && roomHasNarrowed\n ? !(availableWidth > neededWidth + REEXPAND_MARGIN_PX)\n : neededWidth > availableWidth;\n}\n\n/**\n * An element's natural (unconstrained) rendered width, measured off a clone.\n *\n * The live title and aside sit in a flex chain whose shrink-to-fit can resolve\n * far short of the content's real size exactly when the row runs out of room,\n * and a collapsed aside is laid out as the floating box, not the row. The clone\n * goes in beside the live element so it inherits the same font and contextual\n * selectors; `restyle` restates the aside's inline layout.\n *\n * `text`, when given, replaces the clone's content before measuring. The title\n * element's own live text can already be `useFittedTitle`'s compacted form\n * (the two hooks share no state), and measuring that would ask \"does the short\n * form fit\" instead of \"does the aside need to give the FULL form room\",\n * so a title that had already been shortened would report itself small enough\n * that the aside never collapsed to let it grow back.\n *\n * Inserted and removed in one synchronous call so nothing observes the clone.\n * Returns `0`, which `nextAsideCollapsed` treats as unmeasured, where nothing\n * is laid out (jsdom).\n */\nfunction measureNaturalElementWidth(\n el: HTMLElement | null,\n restyle: Partial<CSSStyleDeclaration> = {},\n text?: string,\n): number {\n const parent = el?.parentNode;\n if (!el || !parent) return 0;\n const clone = el.cloneNode(true) as HTMLElement;\n if (text !== undefined) clone.textContent = text;\n Object.assign(clone.style, {\n position: \"absolute\",\n visibility: \"hidden\",\n pointerEvents: \"none\",\n top: \"0\",\n left: \"0\",\n width: \"max-content\",\n minWidth: \"0\",\n maxWidth: \"none\",\n ...restyle,\n });\n clone.setAttribute(\"aria-hidden\", \"true\");\n parent.insertBefore(clone, el.nextSibling);\n const width = clone.getBoundingClientRect().width;\n parent.removeChild(clone);\n return width;\n}\n\n/**\n * The aside's inline layout, restated on its clone: while collapsed and open,\n * the live element is a padded, bordered column floating under the summary.\n */\nconst ASIDE_INLINE: Partial<CSSStyleDeclaration> = {\n flexDirection: \"row\",\n flexWrap: \"nowrap\",\n padding: \"0\",\n border: \"0\",\n};\n\n/** A computed length, or 0 for one that is not a plain number (jsdom). */\nfunction px(value: string): number {\n return Number.parseFloat(value) || 0;\n}\n\n/**\n * The measured-fit collapse `Panel.Header` runs to decide whether its aside\n * belongs inline or behind the dots and expand box. Measured rather than a\n * width breakpoint, which is content-blind and would collapse a short title's\n * aside that has room.\n *\n * `rowRef` is the header row: its content width is the room available.\n * `titleRef` is the rendered `Panel.Title`, and `asideRef` the\n * `[data-panel-aside-full]` box. What the two need side by side is the title's\n * natural width, the row's gap, the aside box's horizontal padding, and the\n * aside's natural width.\n *\n * `fullTitle`, when given, is measured in place of whatever text is currently\n * live in `titleRef`. `Panel.Title` runs its own, separate fit (`useFittedTitle`)\n * and may already be showing a compacted form; measuring that would ask\n * whether the SHORT form fits rather than whether the aside must give way for\n * the full one, and a title that had already shortened would then report\n * itself small enough that the aside never collapsed, locking the short form\n * in for good.\n *\n * Content changes are read from a `MutationObserver`'s pending records after\n * each render rather than by comparing the `title` and `aside` nodes, which are\n * new on every render and would force a layout each time.\n */\nexport function useHeaderAsideFit(\n rowRef: RefObject<HTMLElement | null>,\n titleRef: RefObject<HTMLElement | null>,\n asideRef: RefObject<HTMLElement | null>,\n fullTitle?: string,\n): boolean {\n const [collapsed, setCollapsed] = useState(false);\n const collapsedRef = useRef(collapsed);\n collapsedRef.current = collapsed;\n const neededRef = useRef<number | undefined>(undefined);\n const availableRef = useRef<number | undefined>(undefined);\n const narrowedRef = useRef(false);\n const fullTitleRef = useRef(fullTitle);\n fullTitleRef.current = fullTitle;\n\n const recompute = useCallback(() => {\n const row = rowRef.current;\n if (!row) return;\n const rowStyle = getComputedStyle(row);\n const available =\n row.getBoundingClientRect().width -\n px(rowStyle.paddingLeft) -\n px(rowStyle.paddingRight);\n const title = measureNaturalElementWidth(\n titleRef.current,\n {},\n fullTitleRef.current,\n );\n const aside = measureNaturalElementWidth(asideRef.current, ASIDE_INLINE);\n const asideBox = asideRef.current?.closest<HTMLElement>(\n \"[data-panel-aside-expand]\",\n )?.parentElement;\n const asideBoxStyle = asideBox ? getComputedStyle(asideBox) : null;\n const needed =\n title === 0 || aside === 0\n ? title + aside\n : title +\n px(rowStyle.columnGap) +\n px(asideBoxStyle?.paddingLeft ?? \"\") +\n px(asideBoxStyle?.paddingRight ?? \"\") +\n aside;\n const was = collapsedRef.current;\n // An unmeasured row (a tile not laid out) is not a narrower one.\n const narrowed =\n available > 0 &&\n availableRef.current !== undefined &&\n available < availableRef.current;\n if (was && narrowed) narrowedRef.current = true;\n const next = nextAsideCollapsed(\n was,\n available,\n needed,\n neededRef.current,\n narrowedRef.current,\n );\n // A collapse the narrowing row caused starts out with the margin, and any other starts without it.\n if (next !== was) narrowedRef.current = next && narrowed;\n if (available > 0) availableRef.current = available;\n if (needed > 0) neededRef.current = needed;\n if (next !== collapsedRef.current) {\n collapsedRef.current = next;\n setCollapsed(next);\n }\n }, [rowRef, titleRef, asideRef]);\n\n const watched = useRef<{\n title: HTMLElement | null;\n aside: HTMLElement | null;\n observer: MutationObserver | null;\n }>({ title: null, aside: null, observer: null });\n\n // Runs after every render, and measures only when the elements were swapped or something inside them changed.\n useLayoutEffect(() => {\n const title = titleRef.current;\n const aside = asideRef.current;\n const w = watched.current;\n if (w.observer === null || w.title !== title || w.aside !== aside) {\n w.observer?.disconnect();\n w.title = title;\n w.aside = aside;\n w.observer =\n typeof MutationObserver === \"undefined\"\n ? null\n : new MutationObserver(() => recompute());\n for (const el of [title, aside]) {\n if (el) {\n w.observer?.observe(el, {\n subtree: true,\n childList: true,\n characterData: true,\n attributes: true,\n });\n }\n }\n recompute();\n return;\n }\n if (w.observer.takeRecords().length > 0) recompute();\n });\n\n useEffect(() => {\n const w = watched.current;\n return () => {\n w.observer?.disconnect();\n w.observer = null;\n };\n }, []);\n\n useEffect(() => {\n const row = rowRef.current;\n if (!row || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(() => recompute());\n ro.observe(row);\n if (asideRef.current) ro.observe(asideRef.current);\n return () => ro.disconnect();\n }, [rowRef, asideRef, recompute]);\n\n return collapsed;\n}\n\nconst PanelAsideSizeContext = createContext<PanelAsideSize>(\"full\");\n\n/**\n * Carries `Panel.Header`'s collapse decision down to content inside\n * `panelAside`. Content never measures independently, so it cannot disagree\n * with the chrome about which state the aside is in.\n */\nexport const PanelAsideSizeProvider = PanelAsideSizeContext.Provider;\n\n/**\n * Whether the panel header's aside is drawn `full` or `collapsed`, for content\n * inside `panelAside` that draws differently in each. The header decides and\n * this reads that decision, so the content always agrees with it. `full`\n * outside a `Panel.Header`.\n *\n * @category Panel\n */\nexport function usePanelAsideSize(): PanelAsideSize {\n return useContext(PanelAsideSizeContext);\n}\n","import type { StreamStatusValue } from \"@ksp-gonogo/sitrep-sdk\";\nimport {\n Children,\n type ComponentPropsWithoutRef,\n createContext,\n Fragment,\n forwardRef,\n isValidElement,\n type ReactElement,\n type ReactNode,\n useCallback,\n useContext,\n useEffect,\n useId,\n useImperativeHandle,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { AugmentSlot, useWidgetSegmentBound } from \"./AugmentSlot\";\nimport { Badge } from \"./Badge\";\nimport { badgeFace } from \"./badgeFace\";\nimport { PanelDelayRail } from \"./CommandDelay/PanelDelayRail\";\nimport { FramedDisplay } from \"./FramedDisplay\";\nimport { fitBox, fitMask } from \"./fitBox\";\nimport { focusRing, focusRingInset } from \"./focusRing\";\nimport { GraphNotice } from \"./GraphNotice\";\nimport { gridToPixels } from \"./gridUnits\";\nimport { InactiveNotice, type PanelInactiveReason } from \"./InactiveNotice\";\nimport { LiveRegion } from \"./LiveRegion\";\nimport { type BadgeEntry, usePanelBadgesContext } from \"./PanelBadges\";\nimport { SECTION_FILL_ATTR, SECTION_FULL_ATTR, Section } from \"./Section\";\nimport { PanelStatusDot } from \"./status/PanelStatusDot\";\nimport type { StatusSummary } from \"./status/PanelStatusStore\";\nimport {\n type Severity,\n severityFromStreamStatus,\n severityRank,\n} from \"./status/severity\";\nimport { formatStreamStatus } from \"./status/streamStatusWord\";\nimport { useStatusBreakdown } from \"./status/useStatusBreakdown\";\nimport { useStatusContribution } from \"./status/useStatusContribution\";\nimport { useStatusSummary } from \"./status/useStatusSummary\";\nimport { Tooltip } from \"./Tooltip\";\nimport { titleText } from \"./titleText\";\nimport { TONE_LABEL } from \"./tone\";\nimport { useElementSize } from \"./useElementSize\";\nimport { useFittedTitle } from \"./useFittedTitle\";\nimport { PanelAsideSizeProvider, useHeaderAsideFit } from \"./usePanelAsideSize\";\nimport { filterControlOf, type RowFilter } from \"./useRowFilter\";\nimport { useKeyboardScrollable, useScrollerMetric } from \"./useScrollerMetric\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\n\ninterface PanelContextValue {\n scroller: HTMLElement | null;\n registerScroller: (el: HTMLElement | null) => void;\n}\n\nconst PanelCtx = createContext<PanelContextValue | null>(null);\n\n/**\n * Links a panel's parts: `Panel.Body` registers the element that scrolls and\n * `Panel.Glow` draws its edge glow from it, whatever their nesting order. A\n * hand-composed panel mounts it around both.\n */\nexport function PanelContextProvider({ children }: { children?: ReactNode }) {\n const [scroller, setScroller] = useState<HTMLElement | null>(null);\n const value = useMemo(\n () => ({ scroller, registerScroller: setScroller }),\n [scroller],\n );\n return <PanelCtx.Provider value={value}>{children}</PanelCtx.Provider>;\n}\n\n/**\n * The per-panel providers a `Panel` mounts. `Panel.Root` renders this; a\n * hand-composed panel can too. The delay-rail store is not here: a widget's\n * commands register above the `<Panel>` it returns, so that store is provided\n * above the widget.\n */\nexport function PanelProviders({ children }: { children?: ReactNode }) {\n return <PanelContextProvider>{children}</PanelContextProvider>;\n}\n\n/**\n * What a `Section fill` resolves to inside the box that owns the leftover\n * height. A content basis (not `flex: 1`) lets two filling sections keep their\n * own heights and split only the spare room. It still shrinks, so a drawing\n * sized from a ResizeObserver can give room back, but only down to a floor: a\n * zero minimum let the sections around it squeeze its scroller to nothing, and\n * the body scrolls past the floor instead.\n */\nconst SECTION_FILL_RULE = `\n & > [${SECTION_FILL_ATTR}] {\n flex: 1 1 auto;\n min-height: var(--size-section-fill-floor);\n }\n`;\n\nexport const PanelContainer = styled.div<{\n $railTravels?: boolean;\n $hoverTitle?: boolean;\n}>`\n /* Chrome only: border, surface and clip. The inset is Panel.Body's and the glow is Panel.Glow's. */\n background: var(--color-surface-panel);\n /* A size container, so the popped-open aside sizes itself in cqw against the panel's width. */\n container-type: inline-size;\n border: 1px solid var(--color-border-subtle);\n border-radius: var(--radius-regular);\n /* Top only: the delay rail's band, reserved on every widget so a command in flight never pushes the title down. Sides and bottom stay zero so visual content can reach the chrome. */\n padding: var(--panel-rail-band) 0 0;\n /* When the rail travels with the header, the band is the sticky unit's first row instead. */\n ${({ $railTravels }) => ($railTravels ? \"padding-top: 0;\" : \"\")}\n width: 100%;\n height: 100%;\n display: flex;\n flex-direction: column;\n gap: 0;\n overflow: hidden;\n /* A hoverTitle panel is itself the tab stop, and the grid cell clips an outset ring, so the ring is drawn inside the edge. */\n ${({ $hoverTitle }) => ($hoverTitle ? \"position: relative;\" : \"\")}\n ${({ $hoverTitle }) => ($hoverTitle ? focusRingInset : \"\")}\n /* A hand-composed panel can put sections straight in here, so this box owns the leftover height. */\n ${SECTION_FILL_RULE}\n`;\n\nconst PanelTitle__Box = styled.h3`\n margin: 0;\n /* No top inset: PanelHeader__Row carries it, so the header pays for it once. */\n padding: var(--inset-panel-header);\n ${titleText}\n /* Flush, so the all-caps glyphs centre on the box the row aligns the aside against. */\n line-height: var(--line-height-flush);\n /* One line always: a wrapped title would push the aside down. */\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n`;\n\n/**\n * Props for `Panel.Title`, the panel heading. Other `h3` attributes pass through.\n *\n * @category Panel\n */\nexport interface PanelTitleProps\n extends Omit<ComponentPropsWithoutRef<\"h3\">, \"title\"> {\n /**\n * Shorter forms of this title, longest first, for tiles the full one will not\n * fit in.\n *\n * The widest form that fits the box is drawn. When a shorter form is\n * showing, the full title stays the accessible name and the hover tooltip.\n */\n compact?: string | readonly string[];\n}\n\n/** A panel's title, in the longest form that fits. */\nexport const PanelTitle = forwardRef<HTMLHeadingElement, PanelTitleProps>(\n function PanelTitle({ compact, children, ...rest }, forwarded) {\n const own = useRef<HTMLHeadingElement | null>(null);\n // Candidates are measured by substituting their text into a clone, so only a string title can be compacted.\n const full = typeof children === \"string\" ? children : \"\";\n const forms =\n compact === undefined || full === \"\"\n ? EMPTY_COMPACT\n : typeof compact === \"string\"\n ? [compact]\n : compact;\n const { index, compacted } = useFittedTitle(own, full, forms);\n return (\n <Tooltip text={compacted ? full : undefined}>\n <PanelTitle__Box\n {...rest}\n ref={(node: HTMLHeadingElement | null) => {\n own.current = node;\n if (typeof forwarded === \"function\") {\n forwarded(node);\n return;\n }\n if (forwarded) forwarded.current = node;\n }}\n aria-label={compacted ? full : undefined}\n >\n {index === 0 ? children : forms[index - 1]}\n </PanelTitle__Box>\n </Tooltip>\n );\n },\n);\n\n/** Stable, so a title with no compact forms does not hand the fit hook a fresh array each render. */\nconst EMPTY_COMPACT: readonly string[] = [];\n\nconst PanelHeader__Row = styled.div`\n padding-top: var(--inset-panel-header-top);\n display: flex;\n /* Centred, which levels the collapsed dots and chevron on the single-line title. */\n align-items: center;\n justify-content: space-between;\n gap: var(--gap-panel-header);\n min-width: 0;\n /* Wraps so PanelToolbar's full basis starts its own line; the aside never wraps, because the title column absorbs the pressure first. */\n flex-wrap: wrap;\n /* Never shrink, or a short tile's body would overprint the title. */\n flex-shrink: 0;\n`;\n\nconst PanelHeader__Titles = styled.div`\n min-width: 0;\n /* A zero basis, so a long title shrinks beside the aside instead of wrapping it onto a second line; useFittedTitle measures the room this leaves. */\n flex-grow: 1;\n flex-shrink: 1;\n flex-basis: 0;\n display: flex;\n align-items: center;\n & > h3 {\n flex: 1 1 auto;\n min-width: 0;\n }\n`;\n\n/**\n * An invisible, zero-width badge beside the title, so the row is always as\n * tall as a badge and one arriving in the aside fills height that was already\n * there. Its line is a generated space, so the panel's text content stays the\n * widget's own.\n */\nconst PanelHeader__Strut = styled.span`\n display: flex;\n flex: 0 0 0;\n width: 0;\n overflow: hidden;\n visibility: hidden;\n & > *::before {\n content: \"\\\\00a0\";\n }\n`;\n\nconst PanelHeader__Aside = styled.div`\n display: flex;\n align-items: center;\n gap: var(--gap-panel-aside);\n /* Never grows or wraps: an aside that stops fitting collapses to the dots. */\n justify-content: flex-end;\n flex-shrink: 0;\n /* Mirrors PanelTitle's inset so the badges line up with the title. */\n padding: var(--inset-panel-header);\n`;\n\n/**\n * The aside's collapse box. While the aside fits it shows inline; once\n * `$collapsed` is true it swaps to the per-severity status dots plus a chevron,\n * and the full aside (badges and controls) floats open on toggle. `$collapsed`\n * is a measured fit from `useHeaderAsideFit`, not a width breakpoint, so a\n * short title keeps its aside on a narrow panel.\n */\nconst PanelAsideExpand = styled.details<{ $collapsed?: boolean }>`\n position: relative;\n margin: 0;\n display: flex;\n align-items: center;\n /* Drops the ::details-content box, which otherwise breaks this element's shrink-to-fit sizing. */\n &::details-content {\n display: contents;\n }\n /* Exactly its content wide: the inline aside, or the dots summary once collapsed. */\n flex: 0 0 auto;\n\n & > summary {\n /* Wide default: no collapsed affordance, the aside just shows inline. */\n display: none;\n align-items: center;\n gap: var(--gap-panel-aside);\n list-style: none;\n cursor: pointer;\n /* Lands the dots on the title's cap band rather than its line box centre. */\n transform: translateY(-1px);\n /* Panel sets no foreground, so the currentColor chevron needs one. */\n color: var(--color-text-dim);\n }\n & > summary {\n ${focusRing}\n }\n & > summary::-webkit-details-marker {\n display: none;\n }\n & > summary [data-panel-aside-chevron] {\n /* A CSS caret, not an icon, so the header stays out of every widget's SVG queries. Points down closed. */\n flex: 0 0 auto;\n width: 6px;\n height: 6px;\n /* Extra space before the chevron only: the rotated box's corner eats into the gap. */\n margin-left: var(--gap-panel-aside);\n border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor;\n /* The rotation leaves the ink centroid 1.4px low; the lift centres it on the dots. */\n transform: translateY(-1.4px) rotate(45deg);\n transition: transform var(--duration-base) var(--ease-standard);\n }\n &[open] > summary [data-panel-aside-chevron] {\n /* Points up when open; the centroid moves above centre, so the lift flips sign. */\n transform: translateY(1.4px) rotate(225deg);\n }\n\n & > [data-panel-aside-full] {\n /* Wide default: the full aside shows inline whatever the [open] state. */\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: var(--gap-panel-aside);\n flex-wrap: wrap;\n min-width: 0;\n }\n\n ${({ $collapsed }) =>\n $collapsed &&\n css`\n & > summary {\n display: inline-flex;\n }\n /* Hidden rather than display: none, so useHeaderAsideFit's clone measurement sees it as it sees the inline state. */\n &:not([open]) > [data-panel-aside-full] {\n position: absolute;\n top: 0;\n right: 0;\n visibility: hidden;\n pointer-events: none;\n }\n /* Collapsed and open: floats over the body so its controls do not reflow the panel. */\n &[open] > [data-panel-aside-full] {\n position: absolute;\n top: calc(100% + var(--offset-popover));\n right: 0;\n /* Widget-internal stacking above the header, off the app-global z ladder. */\n z-index: 3;\n flex-direction: column;\n align-items: stretch;\n justify-content: flex-start;\n flex-wrap: nowrap;\n /* A real floor so a control has room, capped at the panel width. */\n min-width: min(14rem, 90cqw);\n max-width: 90cqw;\n padding: var(--inset-popover);\n background: var(--color-surface-panel);\n border: 1px solid var(--color-border-subtle);\n border-radius: var(--radius-regular);\n box-shadow: var(--shadow-popover-drop)\n rgba(0, 0, 0, 0.35);\n }\n `}\n`;\n\n/**\n * Title and an optional right-hand aside on one row.\n *\n * When the title and aside no longer fit side by side, the aside collapses to\n * the panel's per-severity status dots (worst first) plus a chevron, and the\n * full aside floats open on toggle. A hand-composed header gets this too.\n */\nexport function PanelHeader({\n title,\n compactTitle,\n aside,\n toolbar,\n ...rest\n}: Omit<ComponentPropsWithoutRef<\"div\">, \"title\"> & {\n title?: ReactNode;\n /** Shorter forms of `title`, longest first. See {@link PanelTitleProps}. */\n compactTitle?: string | readonly string[];\n aside?: ReactNode;\n /**\n * A row of controls on its own line below the title. See `Panel.Toolbar`.\n */\n toolbar?: ReactNode;\n}) {\n const breakdown = useStatusBreakdown();\n\n // `rowRef` is the room available to title and aside together; `titleRef` and `asideFullRef` are what they need. jsdom never measures, so it always sees the wide default.\n const rowRef = useRef<HTMLDivElement>(null);\n const titleRef = useRef<HTMLHeadingElement>(null);\n const asideFullRef = useRef<HTMLDivElement>(null);\n const collapsed = useHeaderAsideFit(\n rowRef,\n titleRef,\n asideFullRef,\n typeof title === \"string\" ? title : undefined,\n );\n\n /**\n * The `<details>` is a disclosure only while collapsed. Inline it is forced\n * open, so assistive tech is never told that badges on screen are hidden\n * behind a trigger that does not exist. Leaving the collapsed state drops the\n * operator's open choice, so a panel that narrows again starts closed.\n */\n const [openWhileCollapsed, setOpenWhileCollapsed] = useState(false);\n useEffect(() => {\n if (!collapsed) setOpenWhileCollapsed(false);\n }, [collapsed]);\n\n return (\n /* `data-panel-header` is a stable targeting hook. */\n <PanelHeader__Row ref={rowRef} data-panel-header=\"\" {...rest}>\n <PanelHeader__Titles>\n {title !== undefined && (\n <PanelTitle ref={titleRef} compact={compactTitle}>\n {title}\n </PanelTitle>\n )}\n <PanelHeader__Strut aria-hidden=\"true\">\n <Badge>{null}</Badge>\n </PanelHeader__Strut>\n </PanelHeader__Titles>\n {aside !== undefined && (\n <PanelHeader__Aside>\n {/* A native `<details>` carries an implicit `role=\"group\"`, so an aside's own `getByRole(\"group\")` query must scope to its subtree. */}\n <PanelAsideExpand\n data-panel-aside-expand=\"\"\n $collapsed={collapsed}\n open={collapsed ? openWhileCollapsed : true}\n onToggle={(e) => {\n // The forced-open inline state is not an operator choice.\n if (collapsed) setOpenWhileCollapsed(e.currentTarget.open);\n }}\n >\n <summary\n aria-label={\n breakdown.length === 0\n ? \"Panel status and controls\"\n : `${breakdown\n .map((e) => `${e.count} ${TONE_LABEL[e.severity]}`)\n .join(\", \")}. Panel status and controls`\n }\n >\n {breakdown.map((e) => (\n <PanelStatusDot\n key={e.severity}\n severity={e.severity}\n count={e.count}\n />\n ))}\n <span data-panel-aside-chevron=\"\" aria-hidden=\"true\" />\n </summary>\n <PanelAsideSizeProvider value={collapsed ? \"collapsed\" : \"full\"}>\n <div data-panel-aside-full=\"\" ref={asideFullRef}>\n {aside}\n </div>\n </PanelAsideSizeProvider>\n </PanelAsideExpand>\n </PanelHeader__Aside>\n )}\n {toolbar !== undefined && <PanelToolbar>{toolbar}</PanelToolbar>}\n </PanelHeader__Row>\n );\n}\n\n/**\n * A full-width row of controls under the header, pinned like the header and\n * outside the scrolling body.\n *\n * `panelAside` is the small slot beside the title (a chip, a badge, one\n * select); a toolbar is for controls that are a row in their own right, such as\n * a map's layer pickers or a graph's series toggles. It wraps rather than\n * scrolls, so a narrow tile gets a taller toolbar.\n */\nexport const PanelToolbar = styled.div`\n ${fitBox(\"panel-toolbar\")}\n display: flex;\n align-items: center;\n flex-wrap: wrap;\n gap: var(--gap-control-row);\n padding: var(--inset-panel-toolbar);\n min-width: 0;\n /* Never shrink, or a short tile squeezes the controls away. */\n flex-shrink: 0;\n /* A full basis, so the toolbar always takes its own line in the wrapping header row. */\n flex-basis: 100%;\n width: 100%;\n /* Opaque: the sticky header's scroll glow only masks its top strip, so a tall toolbar would let scrolled rows draw through it. */\n background: var(--color-surface-panel);\n`;\n\n/**\n * A panel body is the default density tier, and re-declares both steppable gap\n * names so a panel nested inside a compact `Card` does not inherit the card's\n * density.\n */\nconst PanelBody__Box = styled.div<{\n $fitToSize?: boolean;\n $loneFrame?: boolean;\n $hoverTitle?: boolean;\n}>`\n --gap-related: var(--gap-related-comfortable);\n --gap-section: var(--gap-section-comfortable);\n --bleed-inline: var(--gutter-panel);\n /* The body's side and bottom inset; a lone framed drawing steps this down under a narrow container. */\n --panel-body-gutter: var(--gutter-panel);\n --panel-body-bottom: var(--inset-panel-bottom);\n\n flex: 1;\n min-height: 0;\n display: flex;\n flex-direction: column;\n gap: var(--gap-related);\n /* Longhands, because jsdom drops a shorthand made of var() calls. */\n padding-top: var(--inset-panel-top);\n padding-right: var(--panel-body-gutter);\n padding-bottom: var(--panel-body-bottom);\n padding-left: var(--panel-body-gutter);\n /* Body is the scroller, so the inset sits inside the scrolling box and never clips overflow. */\n overflow: auto;\n ${focusRingInset}\n /* The glow shows scroll state, so the native bar is hidden. */\n scrollbar-width: none;\n -ms-overflow-style: none;\n &::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n }\n /* Fit-to-size never scrolls, and centres only once measurement says the content fits: Firefox clips safe center while reporting support for it. */\n ${({ $fitToSize }) => ($fitToSize ? \"flex: 1; overflow: hidden;\" : \"\")}\n ${({ $loneFrame }) =>\n $loneFrame\n ? `@container (width < ${LONE_FRAME_BREAKPOINT}) {\n --panel-body-gutter: var(--inset-tiny);\n --panel-body-bottom: var(--inset-tiny);\n }`\n : \"\"}\n /* The title row is out of flow entirely (see PanelHoverTop), so the body\n takes the same thin inset a lone framed drawing gets under a narrow\n container, unconditionally: a hoverTitle panel is always that narrow. */\n ${({ $hoverTitle }) =>\n $hoverTitle\n ? `--panel-body-gutter: var(--inset-tiny);\n --panel-body-bottom: var(--inset-tiny);`\n : \"\"}\n ${SECTION_FILL_RULE}\n /* A lone drawing is the whole body, so nothing squeezes it and it keeps giving room back. */\n ${({ $loneFrame }) =>\n $loneFrame\n ? `& > [${SECTION_FILL_ATTR}] {\n min-height: 0;\n }`\n : \"\"}\n`;\n\n/**\n * The panel width below which a lone framed drawing takes the thin inset.\n * Under it the standard gutters cost such a drawing 15 to 35 percent of its\n * area; above it about 10.\n */\nconst LONE_FRAME_BREAKPOINT = \"25rem\";\n\n/**\n * The content box, the inset, and the scrolling. Registers itself with the\n * panel context so `Panel.Glow` can observe it without reaching into the tree.\n */\nexport function PanelBody({\n children,\n fitToSize,\n loneFrame,\n hoverTitle,\n ...rest\n}: ComponentPropsWithoutRef<\"div\"> & {\n fitToSize?: boolean;\n /** The body is one framed drawing and nothing else. See `Panel`'s `sections`. */\n loneFrame?: boolean;\n /** The title row is out of flow; see `Panel`'s `hoverTitle`. */\n hoverTitle?: boolean;\n}) {\n const ctx = useContext(PanelCtx);\n const register = ctx?.registerScroller;\n const [scroller, setScroller] = useState<HTMLDivElement | null>(null);\n const ref = useCallback(\n (el: HTMLDivElement | null) => {\n register?.(el);\n setScroller(el);\n },\n [register],\n );\n const tabIndex = useKeyboardScrollable(scroller);\n return (\n /* `data-panel-body` is a stable targeting hook for the scroller's visible height. */\n <PanelBody__Box\n ref={ref}\n tabIndex={tabIndex}\n data-panel-body=\"\"\n $fitToSize={fitToSize}\n $loneFrame={loneFrame}\n data-panel-lone-frame={loneFrame ? \"\" : undefined}\n $hoverTitle={hoverTitle}\n {...rest}\n >\n {children}\n </PanelBody__Box>\n );\n}\n\n/**\n * The default narrowest a section column may be before the panel stops offering\n * a second one: two columns in a panel about 470px wide. A string so a widget\n * can pass `100%` to opt out of columns entirely.\n */\nconst DEFAULT_SECTION_MIN_WIDTH = \"13rem\";\n\n/**\n * Where the wide-layout decision lives: `auto-fit` + `minmax` takes as many\n * columns of at least `$min` as the panel's width allows, so sections flow\n * across a landscape tile and stack in a portrait one.\n *\n * `min($min, 100%)` keeps a narrow tile stacking instead of scrolling sideways.\n * The `max(..., 1/N of the panel)` floor makes more than N tracks impossible,\n * because a track a full-width section spans is not collapsed by `auto-fit`.\n */\nconst PanelSections__Grid = styled.div<{ $min: string; $columns: number }>`\n display: grid;\n grid-template-columns: repeat(\n auto-fit,\n minmax(\n max(\n min(${({ $min }) => $min}, 100%),\n ${({ $columns }) =>\n `calc((100% - ${$columns - 1} * var(--gap-panel-columns)) / ${$columns})`}\n ),\n 1fr\n )\n );\n /* Wider between columns than rows: a column gap has no section title doing the separating. */\n gap: var(--gap-panel-sections) var(--gap-panel-columns);\n /* Natural heights, so a short section beside a tall one does not read as an empty box. */\n align-items: start;\n & > [${SECTION_FULL_ATTR}] {\n grid-column: 1 / -1;\n }\n`;\n\n/**\n * The tiny-tile layout: fills every pixel between the header's foot and the\n * panel's clip edge and centres the widget's content in it, but only while\n * measurement says the content fits. Wraps the children alone, so the header\n * is neither centred nor measured.\n */\nfunction PanelFitBody({\n children,\n hoverTitle,\n}: {\n children?: ReactNode;\n hoverTitle?: boolean;\n}) {\n const outerRef = useRef<HTMLDivElement | null>(null);\n const innerRef = useRef<HTMLDivElement | null>(null);\n const fits = useContentFits(outerRef, innerRef);\n return (\n <PanelBody__FitOuter ref={outerRef} $fits={fits} data-panel-fit-body=\"\">\n <PanelBody__FitContent\n ref={innerRef}\n $fits={fits}\n $hoverTitle={hoverTitle}\n >\n {children}\n </PanelBody__FitContent>\n </PanelBody__FitOuter>\n );\n}\n\nconst PanelBody__FitOuter = styled.div<{ $fits?: boolean }>`\n flex: 1;\n min-height: 0;\n /* Takes back the body's gap above and its bottom inset, so the box is all the room a tiny tile has and centring is measured against it. Reads the body's own custom property rather than the raw token, so a stepped-down bottom inset (loneFrame, hoverTitle) is taken back by exactly as much. */\n margin-top: calc(-1 * var(--gap-related));\n margin-bottom: calc(-1 * var(--panel-body-bottom));\n display: flex;\n flex-direction: column;\n /* Never clips: the body owns the real boundary. */\n ${({ $fits }) =>\n $fits ? \"justify-content: center;\" : \"justify-content: flex-start;\"}\n`;\n\nconst PanelBody__FitContent = styled.div<{\n $fits?: boolean;\n $hoverTitle?: boolean;\n}>`\n display: flex;\n flex-direction: column;\n align-items: center;\n /* Aligned like its box: centring here would push overflowing content up under the header. */\n ${({ $fits }) =>\n $fits ? \"justify-content: center;\" : \"justify-content: flex-start;\"}\n /* The delay rail band above still reserves its own strip on a hoverTitle\n panel (see PanelProps.hoverTitle), so the box this centres within is\n shorter at the top than the panel actually is, by the band's own height.\n Centred content would read as sitting low by half of it, the same way it\n would if the box below a real header were centred on the whole panel\n instead of the room under that header. The band has no visible header to\n read as the reason, so the shift here is what keeps the figure reading as\n centred on the panel while the box itself stays exactly what fitToSize's\n own docs promise: never clipped, and unaffected while the content does\n not fit.\n\n The flex centring above absorbs half of any margin change into its own\n free-space split (a flex item's centred position moves by m/2 for a\n margin-top of m, not m), so the full band's height is what actually\n buys the half-band visual shift this needs. */\n ${({ $fits, $hoverTitle }) =>\n $fits && $hoverTitle\n ? \"margin-top: calc(-1 * var(--panel-rail-band));\"\n : \"\"}\n gap: var(--gap-tiny-content);\n min-height: 0;\n /* A query container, so a tiny presentation sizes its headline in cqw against the tile rather than the viewport. */\n container-type: inline-size;\n`;\n\n/**\n * Whether the content currently fits its box, so a tiny tile centres only\n * when centring cannot push the first line up under the header. Measured\n * because Firefox clips `safe center` in practice. True when there is nothing\n * to measure.\n */\nfunction useContentFits(\n boxRef: { current: HTMLElement | null },\n contentRef: { current: HTMLElement | null },\n): boolean {\n const [fits, setFits] = useState(true);\n useLayoutEffect(() => {\n const box = boxRef.current;\n const content = contentRef.current;\n if (!box || !content) return;\n const measure = () => {\n setFits(contentFits(contentExtent(content), box.clientHeight));\n };\n measure();\n if (typeof ResizeObserver === \"undefined\") return;\n /* The content box is squeezed to the box, so it stops resizing once its\n children outgrow it. Each child is observed as well, and a child added\n later is picked up as it arrives. */\n const observer = new ResizeObserver(measure);\n const observeAll = () => {\n observer.observe(box);\n observer.observe(content);\n for (const child of Array.from(content.children)) observer.observe(child);\n };\n observeAll();\n const children = new MutationObserver(() => {\n observeAll();\n measure();\n });\n children.observe(content, { childList: true });\n return () => {\n observer.disconnect();\n children.disconnect();\n };\n }, [boxRef, contentRef]);\n return fits;\n}\n\n/**\n * How tall the content is, whatever its box does: from the top of its first\n * child to the bottom of its last. The box itself is squeezed to the room left\n * under the header and its children overflow it, so its own height says\n * nothing once they no longer fit, and its scroll height counts only the half\n * of a centred overflow that went downwards.\n */\nfunction contentExtent(content: HTMLElement): number {\n const rects = Array.from(content.children, (child) =>\n child.getBoundingClientRect(),\n );\n if (rects.length === 0) return content.scrollHeight;\n return (\n Math.max(...rects.map((r) => r.bottom)) -\n Math.min(...rects.map((r) => r.top))\n );\n}\n\n/**\n * Whether content of `content` height, centred in a box `box` tall, stays\n * inside it. The box already runs from the header's foot to the clip edge, so\n * there is no room past either end to overflow into.\n */\nexport function contentFits(content: number, box: number): boolean {\n return content <= box;\n}\n\nconst ScrollAreaRoot = styled.div`\n position: relative;\n display: flex;\n flex-direction: column;\n /* Fills the flex-column parent so the inner scroller engages rather than spilling past the panel edge. */\n flex: 1;\n min-height: 0;\n /* Out to the panel's edges, its content back on the column, so a strip inside that bleeds is not clipped by this scroller. */\n margin-inline: calc(-1 * var(--bleed-inline));\n`;\n\n/**\n * Inner scroll element. Carries a stable `data-scroll-area-inner` attribute so\n * a `styled(ScrollArea)` can target it to lay out the scrolling children.\n */\nconst ScrollAreaInner = styled.div`\n flex: 1;\n min-height: 0;\n overflow: auto;\n padding-inline: var(--bleed-inline);\n ${focusRingInset}\n /* The glow indicators show scroll state, so the native bar is hidden. */\n scrollbar-width: none;\n -ms-overflow-style: none;\n &::-webkit-scrollbar {\n width: 0;\n height: 0;\n display: none;\n }\n`;\n\nconst ScrollOverflowGlow = styled.div<{\n $position: \"top\" | \"bottom\";\n $visible: boolean;\n /** How far below the scroller's top edge the top glow starts: the rail band's height when the rail travels inside the scroller. */\n $topOffset?: string;\n}>`\n position: absolute;\n /* Flush with the scroller's edges: the body's inset is inside the scroller, so nothing pads this. */\n left: 0;\n right: 0;\n ${({ $position, $topOffset }) =>\n $position === \"top\" ? `top: ${$topOffset ?? \"0\"};` : \"bottom: 0;\"}\n /* The box both layers fade within; each sets its own reach in its gradient stops. */\n height: 44px;\n /* The audit reads the mask depth from the gradient, so the literal height costs it nothing. */\n ${fitMask(\"panel-scroll-glow\")}\n pointer-events: none;\n opacity: ${({ $visible }) => ($visible ? 1 : 0)};\n transition: opacity var(--duration-base) var(--ease-standard);\n /* A light affordance tint over a fully opaque panel-colour mask; only an opaque mask keeps scrolled content from ghosting through the sticky title. */\n background:\n linear-gradient(\n ${({ $position }) => ($position === \"top\" ? \"to bottom\" : \"to top\")},\n color-mix(\n in srgb,\n color-mix(in srgb, var(--color-surface-panel), white 20%) 55%,\n transparent\n ),\n transparent 40%\n ),\n linear-gradient(\n ${({ $position }) => ($position === \"top\" ? \"to bottom\" : \"to top\")},\n var(--color-surface-panel) 0%,\n var(--color-surface-panel) 27%,\n transparent 50%\n );\n /* Widget-internal stacking, off the app-global z ladder so it never lifts over dashboard chrome. */\n z-index: 1;\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n`;\n\n/**\n * A scrolling region with a glow at the top and bottom edges while there is\n * content to scroll to. A panel body already scrolls and glows; use this for a\n * second scrolling region inside a widget (a sidebar list, a terminal log).\n * Its native scrollbar is hidden, and it takes keyboard focus when its content\n * overflows so it can be scrolled from the keyboard.\n *\n * The ref is forwarded to the inner scroll element; `className` and other\n * `div` attributes go to the outer root. A `styled(ScrollArea)` can reach the\n * inner element through its `data-scroll-area-inner` attribute.\n *\n * @category Panel\n */\nexport const ScrollArea = forwardRef<\n HTMLDivElement,\n ComponentPropsWithoutRef<\"div\">\n>(function ScrollArea({ children, ...rest }, ref) {\n const innerRef = useRef<HTMLDivElement | null>(null);\n const [inner, setInner] = useState<HTMLDivElement | null>(null);\n const attachInner = useCallback((el: HTMLDivElement | null) => {\n innerRef.current = el;\n setInner(el);\n }, []);\n\n useImperativeHandle(ref, () => innerRef.current as HTMLDivElement);\n\n const overflow = useScrollerMetric(\n inner,\n scrollOverflow,\n sameOverflow,\n NO_OVERFLOW,\n );\n const innerTabIndex = useKeyboardScrollable(inner);\n\n return (\n <ScrollAreaRoot {...rest}>\n <ScrollAreaInner\n ref={attachInner}\n tabIndex={innerTabIndex}\n data-scroll-area-inner=\"\"\n >\n {children}\n </ScrollAreaInner>\n <ScrollOverflowGlow $position=\"top\" $visible={overflow.top} />\n <ScrollOverflowGlow $position=\"bottom\" $visible={overflow.bottom} />\n </ScrollAreaRoot>\n );\n});\n\n/**\n * Where a panel's sidebar sits relative to the body, in logical terms: `end` is\n * the right edge in LTR and the left in RTL, and the bottom when the sidebar\n * sits under the body. {@link Panel} defaults to `end`. The sidebar always\n * follows the body in the DOM, whichever side it is drawn on.\n *\n * @category Panel\n */\nexport type PanelSidebarSide = \"start\" | \"end\";\n\n/** Sidebar beside the body (inline) or under it (block). Derived, never passed. */\ntype PanelSidebarAxis = \"inline\" | \"block\";\n\n/** Resolve a `px` or `rem` length to pixels; undefined for any other unit. */\nfunction resolveCssLength(value: string): number | undefined {\n const n = Number.parseFloat(value);\n if (!Number.isFinite(n)) return undefined;\n if (value.endsWith(\"px\")) return n;\n if (value.endsWith(\"rem\")) {\n const root =\n typeof document === \"undefined\"\n ? 16\n : Number.parseFloat(\n getComputedStyle(document.documentElement).fontSize || \"16\",\n );\n return n * (Number.isFinite(root) ? root : 16);\n }\n return undefined;\n}\n\n// A column beside the body wants an absolute width; a strip under it competes for the tile's height, so it takes a share.\nconst SIDEBAR_INLINE_SIZE = \"14rem\";\n/** Grid rows a sidebar under the body takes, whatever it holds, so it is the same height in every widget and its content can never grow it. */\nconst SIDEBAR_BLOCK_ROWS = 5;\n/** The cap that keeps a very short tile's body the larger share. */\nconst SIDEBAR_BLOCK_MAX_SHARE = \"45%\";\nconst SIDEBAR_BLOCK_SIZE = `min(${gridToPixels(1, SIDEBAR_BLOCK_ROWS).pxH}px, ${SIDEBAR_BLOCK_MAX_SHARE})`;\n\nfunction sidebarTracks(side: \"start\" | \"end\", size: string): string {\n return side === \"start\"\n ? `minmax(0, ${size}) minmax(0, 1fr)`\n : `minmax(0, 1fr) minmax(0, ${size})`;\n}\n\nconst PanelSplit__Box = styled.div<{\n $axis: PanelSidebarAxis;\n $side: \"start\" | \"end\";\n $size: string;\n $railBand?: boolean;\n}>`\n flex: 1;\n min-height: 0;\n min-width: 0;\n display: grid;\n gap: 0;\n /* Every track is minmax(0, ...), or a sidebar with its own ScrollArea floors at its content and is clipped instead of scrolling. */\n ${({ $axis, $side, $size }) =>\n $axis === \"inline\"\n ? `grid-template-columns: ${sidebarTracks($side, $size)};\n grid-template-rows: minmax(0, 1fr);`\n : `grid-template-columns: minmax(0, 1fr);\n grid-template-rows: ${sidebarTracks($side, $size)};`}\n\n /* Visual placement only: the sidebar always follows the body in the DOM, so reading and tab order never depend on its side. */\n ${({ $side }) =>\n $side === \"start\" ? \"& > [data-panel-sidebar] { order: -1; }\" : \"\"}\n\n /* The sidebar gets the rail band when the rail travels with the header, since only the body's scroller holds it. Under the body it is nowhere near the header, so it takes none. */\n ${({ $railBand, $axis, $side }) =>\n $railBand && !($axis === \"block\" && $side === \"end\")\n ? `& > [data-panel-sidebar] {\n padding-block-start: var(--panel-rail-band);\n }`\n : \"\"}\n\n /* Under the body the sidebar follows the body's own bottom inset, so it takes a small top inset rather than the panel's. */\n ${({ $axis, $side }) =>\n $axis === \"block\" && $side === \"end\"\n ? `& > [data-panel-sidebar] > * > [data-scroll-area-inner] {\n padding-top: var(--inset-panel-sidebar-under);\n }`\n : \"\"}\n\n /* On the inline axis the sidebar gives back its inset on the edge facing the body, which already pays one. */\n ${({ $axis, $side }) =>\n $axis === \"inline\"\n ? /* Child combinators the whole way down, so a ScrollArea the sidebar's\n own CONTENT carries keeps its own padding. */\n `& > [data-panel-sidebar] > * > [data-scroll-area-inner] {\n padding-inline-${$side === \"start\" ? \"end\" : \"start\"}: 0;\n }`\n : \"\"}\n`;\n\n/**\n * Props for the grid that holds a panel's body and sidebar. {@link Panel}\n * draws this grid itself when `panelSidebar` is set, taking `sidebarSide` and\n * `sidebarSize` as `side` and `size`.\n *\n * @category Panel\n */\nexport interface PanelSplitProps extends ComponentPropsWithoutRef<\"div\"> {\n /** Which edge the sidebar sits against. See {@link PanelSidebarSide}. Defaults to `end`. */\n side?: PanelSidebarSide;\n /**\n * Size of the sidebar track: a width on the inline axis, a height on the\n * block axis. Defaults to `14rem`, and to a fixed five grid rows under the\n * body (capped at 45% of a very short tile), so the body takes the rest and\n * content never grows the sidebar. Overflow scrolls inside it.\n */\n size?: string;\n /** Gives the sidebar a top inset matching the delay rail's band, which the body track holds inside its scroller. */\n railBand?: boolean;\n}\n\n/**\n * The grid that holds `Panel.Body` and `Panel.Sidebar`.\n *\n * The axis is measured on this box, whose size does not change when the grid\n * flips axes; measuring the body instead would oscillate. A container query\n * would impose size containment on a box that must hand its height down.\n */\nexport function PanelSplit({\n side = \"end\",\n size,\n railBand,\n children,\n ...rest\n}: PanelSplitProps) {\n // Seeded square, so an unmeasured panel gets the side-by-side arrangement.\n const { ref, size: measured } = useElementSize<HTMLDivElement>({\n w: 1,\n h: 1,\n });\n /*\n * The inline axis also needs absolute room: the body must keep at least as\n * much width as the sidebar takes, or the sidebar goes under. Applied only\n * once measured, so the 1x1 seed does not read as \"no room\".\n */\n const sidebarInline = resolveCssLength(size ?? SIDEBAR_INLINE_SIZE);\n const roomBeside =\n measured.w <= 1 ||\n sidebarInline === undefined ||\n measured.w >= sidebarInline * 2;\n const axis: PanelSidebarAxis =\n measured.w >= measured.h && roomBeside ? \"inline\" : \"block\";\n\n const resolvedSize =\n size ?? (axis === \"inline\" ? SIDEBAR_INLINE_SIZE : SIDEBAR_BLOCK_SIZE);\n return (\n /* `data-panel-split` is a stable targeting hook carrying the resolved axis. */\n <PanelSplit__Box\n ref={ref}\n data-panel-split={axis}\n $axis={axis}\n $side={side}\n $size={resolvedSize}\n $railBand={railBand}\n {...rest}\n >\n {children}\n </PanelSplit__Box>\n );\n}\n\n/** The sidebar's scroller. The inset goes inside it, since padding outside a scroller clips what scrolls under it. */\nconst PanelSidebar__Scroll = styled(ScrollArea)`\n /* Longhands, because jsdom drops a shorthand made of var() calls. */\n & > [data-scroll-area-inner] {\n padding-top: var(--inset-panel-top);\n padding-right: var(--gutter-panel);\n padding-bottom: var(--inset-panel-bottom);\n padding-left: var(--gutter-panel);\n }\n`;\n\nconst PanelSidebar__Box = styled.div`\n display: flex;\n flex-direction: column;\n /* Grid items floor at min-content, which would let the ScrollArea grow the track instead of scrolling. */\n min-width: 0;\n min-height: 0;\n`;\n\n/**\n * Secondary content beside or below the body: an almanac for the diagram, a\n * legend for the plot, a detail pane for the selected row.\n *\n * It has its own {@link ScrollArea} and sits outside `Panel.Body`, so\n * scrolling it does not scroll the drawing it annotates. It is padded like the\n * body, except on the edge it shares with the body.\n */\nexport function PanelSidebar({\n children,\n ...rest\n}: ComponentPropsWithoutRef<\"div\">) {\n return (\n <PanelSidebar__Box data-panel-sidebar=\"\" {...rest}>\n <PanelSidebar__Scroll>{children}</PanelSidebar__Scroll>\n </PanelSidebar__Box>\n );\n}\n\ninterface ScrollOverflow {\n top: boolean;\n bottom: boolean;\n}\n\nconst NO_OVERFLOW: ScrollOverflow = { top: false, bottom: false };\n\n/** Whether there is more to scroll to above and below the current position. */\nfunction scrollOverflow(el: HTMLElement): ScrollOverflow {\n return {\n top: el.scrollTop > 1,\n bottom: el.scrollTop + el.clientHeight < el.scrollHeight - 1,\n };\n}\n\nfunction sameOverflow(a: ScrollOverflow, b: ScrollOverflow): boolean {\n return a.top === b.top && a.bottom === b.bottom;\n}\n\nconst PanelGlow__Root = styled.div`\n position: relative;\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n`;\n\n/**\n * Owns the overflow glow and nothing else. It does not scroll; it decorates\n * whatever does, found through the panel context, so it can wrap the body or\n * sit beside it.\n */\nexport function PanelGlow({\n children,\n railBandAbove,\n ...rest\n}: ComponentPropsWithoutRef<\"div\"> & {\n /** The scroller's first row is the delay rail's opaque band, so start the top glow below it. */\n railBandAbove?: boolean;\n}) {\n const ctx = useContext(PanelCtx);\n const el = ctx?.scroller ?? null;\n\n useEffect(() => {\n if (!ctx && process.env.NODE_ENV !== \"production\") {\n // Without a context this renders correctly and does nothing, so say so.\n console.warn(\n \"Panel.Glow rendered outside a Panel.Context, so it has no scroller \" +\n \"to observe and will never show. Wrap it in Panel.Context (or use \" +\n \"Panel, which does).\",\n );\n }\n }, [ctx]);\n\n const overflow = useScrollerMetric(\n el,\n scrollOverflow,\n sameOverflow,\n NO_OVERFLOW,\n );\n\n return (\n <PanelGlow__Root {...rest}>\n {children}\n <ScrollOverflowGlow\n $position=\"top\"\n $visible={overflow.top}\n $topOffset={railBandAbove ? \"var(--panel-rail-band)\" : undefined}\n />\n <ScrollOverflowGlow $position=\"bottom\" $visible={overflow.bottom} />\n </PanelGlow__Root>\n );\n}\n\nexport type { PanelInactiveReason } from \"./InactiveNotice\";\n\n/**\n * Props for {@link Panel}. Any other `div` attribute passes through to the\n * panel's container.\n *\n * @category Panel\n */\nexport interface PanelProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * The panel's heading. With it set, the panel draws its own header and pads\n * its body. Named `panelTitle` so it does not clash with the HTML `title`\n * tooltip attribute.\n */\n panelTitle?: ReactNode;\n /**\n * Why this panel has nothing to show and is off entirely: the header stays,\n * the body, sidebar, footer and trend give way to the reason. A string, or\n * `{ reason, hint }` for a second line saying what would bring it back.\n * Reserve it for a widget that has decided there is nothing to present at\n * all; any other emptiness (an empty list, a section awaiting data) stays\n * the widget's own.\n */\n inactive?: PanelInactiveReason;\n /**\n * The panel's body, as one or more sections: the preferred way to give a\n * panel content. Each entry is normally a `Section`; Panel owns how they\n * flow, down one column in a portrait tile and across two or three in a\n * landscape one. A single node is fine, and a conditional `null` entry does\n * not render.\n *\n * A widget that is wholly a drawing (a map, a globe) passes one\n * `<Section fill>` holding it. Not to be confused with the boolean\n * `panelSections`, which is about the augment slot. A bare boolean is\n * rejected so `sections={false}` cannot render an empty panel.\n */\n sections?: Exclude<ReactNode, boolean> | readonly ReactNode[];\n /**\n * Narrowest a section column may be before the panel stops offering a\n * second one. Defaults to `13rem`, which gives two columns in a panel about\n * 470px wide.\n *\n * Raise it for a widget whose sections carry long rows and read badly at the\n * default width; set it to `100%` for one that should never columnise at all.\n */\n sectionMinWidth?: string;\n /**\n * Shorter forms of `panelTitle`, longest first, for tiles the full one will\n * not fit in, including the widget's own `minSize`. See\n * {@link PanelTitleProps.compact}.\n */\n compactTitle?: string | readonly string[];\n /**\n * Content for the right of the header row, beside the stream-status badge:\n * state chips, an `AugmentSlot` for Uplink badges, a small control such as a\n * select or a show/hide button. Keep it small: anything that wants real\n * layout belongs in the body or a hand-composed `Panel.Header`.\n */\n panelAside?: ReactNode;\n /**\n * The widget's own state badges (paused, no signal, full), drawn as standard\n * pills in the header aside ahead of the badges contributed to the widget's\n * `${componentId}.badges` slot. A contributed badge sharing an id with one of\n * these is dropped. Badges render alongside whatever `panelAside` supplies.\n */\n panelBadges?: readonly PanelBadge[];\n /**\n * This panel's own stream status, shown as a badge in the header aside and\n * announced when it changes. `live` shows nothing, and `\"none\"` suppresses\n * the badge.\n *\n * The dashboard sets the blackout grades (`recorded`,\n * `last-before-blackout`) itself, from the widget's declared channels. Any\n * other grade shows only when set here, so set it for a panel that reads one\n * specific Topic. It is merged, worst first, with the dashboard's status and\n * any badge drawn with `report`.\n */\n panelStatus?: StreamStatusValue | \"none\";\n /**\n * A full-width row of controls under the header, pinned outside the\n * scrolling body. For widgets whose controls are a row in their own right\n * (a map's layer pickers, a graph's series toggles); a single chip or select\n * belongs in `panelAside` instead. Drawn in a `Panel.Toolbar`.\n */\n panelToolbar?: ReactNode;\n /**\n * A filter from {@link useRowFilter} over the whole body. Its control is\n * pinned in the toolbar row under the header, so it stays above the list it\n * narrows while the body scrolls. For a filter over one list among others,\n * wrap that list in a {@link FilterRegion} instead.\n */\n panelFilter?: RowFilter;\n /**\n * Lays the content out for a small tile: centred in the room under the\n * header while it fits, top-aligned when it does not.\n *\n * It overrides `fill` on a section: with this set, every section stays an\n * ordinary grid item.\n */\n fitToSize?: boolean;\n /**\n * The tiny tile's header: the title row leaves the flow and the content\n * centres on the whole panel. The title stays an accessible heading, hidden\n * visually and drawn as a small pill top-left while the panel is hovered or\n * focused. The panel itself becomes a tab stop, named by that heading. The\n * header aside (badges, the status summary) stays drawn, pinned top-right,\n * and the delay rail keeps its strip above the content.\n */\n hoverTitle?: boolean;\n /**\n * A pinned strip at the very bottom of the panel, OUTSIDE the scrolling\n * body: the one readout an operator must never have to scroll for (a power\n * meter, a mission clock). It has its own top border and stays put while the\n * body scrolls. Keep it to a single row; anything taller belongs in the body\n * or a sidebar.\n */\n panelFooter?: ReactNode;\n /**\n * A trend across the panel's whole bottom edge, with no inset and on the\n * panel's own surface: the sparkline under a small readout. Handed the\n * strip's measured size, since a sparkline draws to fixed pixels.\n */\n panelTrend?: (size: { w: number; h: number }) => ReactNode;\n /**\n * Secondary content beside or below the body, in its own scrolling region:\n * an almanac for a diagram, a legend for a plot, a detail pane for the\n * selected row.\n *\n * It scrolls on its own, apart from the body, so scrolling it never moves\n * what it annotates. Unset, the panel has no sidebar.\n */\n panelSidebar?: ReactNode;\n /**\n * Which edge the sidebar sits against, logically. See {@link PanelSidebarSide}.\n * Defaults to `end`.\n */\n sidebarSide?: PanelSidebarSide;\n /**\n * Size of the sidebar track: a width when it sits beside the body, a height\n * when it sits under it. Defaults to `14rem`, and to a fixed five grid rows\n * under the body.\n */\n sidebarSize?: string;\n /**\n * Whether this panel mounts the standard `${componentId}.sections` augment\n * slot at the end of its body. Defaults to true, so an Uplink can add a\n * section to any widget without the widget declaring a slot.\n *\n * Set false only when the widget renders {@link WidgetSections} itself\n * (inside a tab, a named section, a column of a sidebar); with both mounted,\n * every bound augment renders twice.\n */\n panelSections?: boolean;\n}\n\n/**\n * A widget's own header badge: a standard badge entry, optionally with a\n * tooltip saying why it shows.\n *\n * @category Panel\n */\nexport interface PanelBadge extends BadgeEntry {\n /** Tooltip text saying why the badge shows. */\n title?: string;\n}\n\n/** The widget's own badges first, then the contributed ones whose ids it does not already use. */\nfunction mergeBadges(\n own: readonly PanelBadge[] | undefined,\n contributed: readonly BadgeEntry[] | null,\n): readonly PanelBadge[] {\n if (own === undefined || own.length === 0) return contributed ?? [];\n if (contributed === null || contributed.length === 0) return own;\n const taken = new Set(own.map((b) => b.id));\n return [...own, ...contributed.filter((b) => !taken.has(b.id))];\n}\n\n/**\n * The standard augment segments {@link Panel} mounts for every widget:\n * `sections` and `actions`. An Uplink binds `\"<widget-id>.sections\"` or\n * `\"<widget-id>.actions\"` with `registerAugment` for any widget, whether or\n * not the widget declares slots of its own. Neither passes props (see\n * {@link AugmentSegmentRegistry}). Where each renders is under Standard slots\n * on {@link Panel}.\n *\n * @category Panel\n */\nexport const FRAMEWORK_AUGMENT_SEGMENTS = [\"sections\", \"actions\"] as const;\n\nconst NO_SEGMENT_PROPS: Record<string, never> = Object.freeze({});\n\n/**\n * Renders every augment bound to the current widget's standard\n * `${componentId}.sections` slot, in priority order, skipping any whose\n * `requires` Domain is not present. The augments get no props.\n *\n * {@link Panel} already mounts this at the end of its body, so a widget\n * renders it itself only to place the sections somewhere else (inside a tab,\n * a named section), and must then pass `panelSections={false}` to its `Panel`.\n * Outside a widget it renders nothing.\n *\n * @example\n * ```tsx\n * <Panel\n * panelTitle=\"Power\"\n * panelSections={false}\n * sections={\n * <Tabs\n * tabs={[\n * { id: \"bus\", label: \"Bus\", content: <BusView /> },\n * { id: \"more\", label: \"More\", content: <WidgetSections /> },\n * ]}\n * />\n * }\n * />\n * ```\n *\n * @category Panel\n */\nexport function WidgetSections(): ReactElement {\n return <AugmentSlot segment=\"sections\" props={NO_SEGMENT_PROPS} />;\n}\n\n/**\n * The winning status contribution, rendered as the header aside badge. It\n * pulses once when its severity changes, then settles (reduced-motion\n * guarded). The panel's own status region announces the change, not this badge.\n */\nfunction PanelSummaryBadge({ summary }: { summary: StatusSummary }) {\n /*\n * Every severity change restarts the one-shot pulse by alternating between\n * two identical keyframe names, which a browser treats as a new animation.\n * A label-only change does not pulse.\n */\n const prevSeverity = useRef(summary.severity);\n const [pulseCount, setPulseCount] = useState(0);\n useEffect(() => {\n if (prevSeverity.current !== summary.severity) {\n prevSeverity.current = summary.severity;\n setPulseCount((k) => k + 1);\n }\n }, [summary.severity]);\n return (\n <PanelSummaryBadge__Pulse $pulse={pulseCount}>\n <Badge tone={summary.severity} size=\"sm\">\n {summary.label}\n </Badge>\n </PanelSummaryBadge__Pulse>\n );\n}\n\nconst PanelSummaryBadge__Pulse = styled.span<{ $pulse: number }>`\n display: inline-flex;\n ${({ $pulse }) =>\n $pulse > 0 &&\n css`\n @media (prefers-reduced-motion: no-preference) {\n animation: ${$pulse % 2 === 0 ? \"panel-status-pulse\" : \"panel-status-pulse-b\"}\n var(--duration-slow) var(--ease-emphasis);\n }\n `}\n @keyframes panel-status-pulse-b {\n 0% {\n transform: scale(1);\n }\n 35% {\n transform: scale(1.14);\n }\n 100% {\n transform: scale(1);\n }\n }\n @keyframes panel-status-pulse {\n 0% {\n transform: scale(1);\n }\n 35% {\n transform: scale(1.14);\n }\n 100% {\n transform: scale(1);\n }\n }\n`;\n\n/**\n * The strip pinned to the bottom of a panel, which `panelFooter` renders\n * into. It never scrolls or shrinks, and shares the body's side padding so its\n * content lines up with the rows above it.\n */\nexport const PanelFooter = styled.div`\n flex-shrink: 0;\n border-top: 1px solid var(--color-border-subtle);\n padding: var(--inset-panel-footer);\n background: var(--color-surface-panel);\n`;\n\n/**\n * The delay rail and the header, as one sticky unit at the top of the body\n * scroller, so the rail always sits on the header.\n */\nconst PanelStickyTop = styled.div`\n position: sticky;\n /* Reach the scroller's true top edge, cancelling the body's own top inset. */\n top: calc(-1 * var(--inset-panel-top));\n z-index: 2;\n /* Cancel the body's inset so the unit spans the full panel width. */\n margin: calc(-1 * var(--inset-panel-top)) calc(-1 * var(--panel-body-gutter))\n 0;\n display: flex;\n flex-direction: column;\n /* Never shrink, or a short tile crushes the band and the title. */\n flex-shrink: 0;\n\n /* The band is this unit's own first row, so the rail frame does not pull up into container padding. */\n & > [data-panel-rail-frame] {\n margin-top: 0;\n }\n`;\n\n/** The full-bleed strip `panelTrend` draws into, measured so the trend can be drawn to its pixels. */\nfunction PanelTrend({\n render,\n}: {\n render: (size: { w: number; h: number }) => ReactNode;\n}) {\n const { ref, size } = useElementSize<HTMLDivElement>({ w: 0, h: 0 });\n return (\n <PanelTrend__Strip ref={ref} data-panel-trend=\"\">\n {size.w > 0 && size.h > 0 && render(size)}\n </PanelTrend__Strip>\n );\n}\n\nconst PanelTrend__Strip = styled.div`\n flex: 0 0 var(--size-panel-trend);\n height: var(--size-panel-trend);\n min-width: 0;\n overflow: hidden;\n`;\n\n/* A filter control takes the toolbar row's whole width, so its search box is not squeezed beside other controls. */\nconst PanelFilterSlot = styled.div`\n flex: 1 1 100%;\n min-width: 0;\n`;\n\n/* The standard header inside the sticky unit. The unit's negative margins cancel the body's inset for the header alone. */\nconst PanelStickyHeader = styled(PanelHeader)`\n /* Transparent: the panel glow under it is its backing. */\n /* The rail band above is this header's top inset. */\n padding-top: 0;\n /* The one header that reads over scrolled content gets a title a notch brighter than the dim chrome token. */\n & h3 {\n color: color-mix(\n in srgb,\n var(--color-text-dim),\n var(--color-text-primary) 45%\n );\n }\n`;\n\n/**\n * The tiny tile's title-and-aside strip, out of flow above the centred\n * content: the title pill at the start, the aside pushed to the end. Empty\n * itself, so the whole panel stays clickable under it; only its children\n * re-enable pointer events.\n */\nconst PanelHoverTop = styled.div`\n position: absolute;\n top: var(--inset-tiny);\n left: var(--inset-tiny);\n right: var(--inset-tiny);\n /* Above the panel's own content (PanelGlow, trend, footer), local sibling ordering inside this panel's own stacking context. Not app-global chrome, so no named z rung. */\n z-index: 3;\n display: flex;\n align-items: flex-start;\n gap: var(--gap-panel-aside);\n pointer-events: none;\n`;\n\n/**\n * The tiny tile's title: an accessible heading at rest, sr-only through\n * `VisuallyHidden`, drawn as a pill hugging its text on hover or focus. Keyed\n * on `:focus` rather than `:focus-visible`, so a tap reveals it too.\n */\nconst PanelHoverTitlePill = styled(VisuallyHidden)`\n margin: 0;\n ${titleText}\n font-size: var(--font-size-caption);\n line-height: var(--line-height-flush);\n max-width: 100%;\n text-overflow: ellipsis;\n ${PanelContainer}:hover &,\n ${PanelContainer}:focus & {\n position: static;\n width: auto;\n height: auto;\n clip: auto;\n background: var(--color-surface-raised);\n border: 1px solid var(--color-border-strong);\n border-radius: var(--radius-pill);\n padding: var(--inset-chip);\n }\n`;\n\n/** The most severe stateful badge, for a tree with no status store to summarise them. */\nfunction worstBadge(\n badges: readonly PanelBadge[],\n): { severity: Severity; label: string } | null {\n let worst: { severity: Severity; label: string } | null = null;\n for (const entry of badges) {\n const { label, tone } = badgeFace(entry);\n if (tone === undefined || tone === \"neutral\") continue;\n if (worst === null || severityRank(tone) > severityRank(worst.severity)) {\n worst = { severity: tone, label };\n }\n }\n return worst;\n}\n\n/**\n * Publishes a header badge's state to the status store without drawing it, for\n * a header that shows state as a dot. A neutral badge carries no state.\n */\nfunction PanelBadgeReports({ badges }: { badges: readonly PanelBadge[] }) {\n return (\n <>\n {badges.map((b) => (\n <PanelBadgeReport key={b.id} entry={b} />\n ))}\n </>\n );\n}\n\nfunction PanelBadgeReport({ entry }: { entry: PanelBadge }) {\n const { label, tone } = badgeFace(entry);\n useStatusContribution(\n tone === undefined || tone === \"neutral\"\n ? null\n : { id: entry.id, severity: tone, label },\n );\n return null;\n}\n\n/** The tiny tile's whole status: the worst severity as a dot that never expands, its words in the tooltip. */\nfunction PanelHoverStatus({\n summary,\n announcement,\n}: {\n summary: { severity: Severity; label: string };\n /** Interrupts to say a no-go state, empty otherwise: a dot says nothing the ear can catch. */\n announcement: string | undefined;\n}) {\n return (\n <>\n <Tooltip text={summary.label} focusable>\n <PanelHoverStatus__Hit>\n <PanelStatusDot severity={summary.severity} />\n </PanelHoverStatus__Hit>\n </Tooltip>\n {/* Mounted with the dot, whatever the state, so the first no-go is a change to a region already watched. */}\n <LiveRegion visuallyHidden assertive>\n {announcement}\n </LiveRegion>\n </>\n );\n}\n\nconst PanelHoverStatus__Hit = styled.span`\n display: inline-flex;\n align-items: center;\n padding: var(--inset-chip);\n`;\n\n/** The tiny tile's aside, pinned top-right and always visible: unlike the pill it needs no reveal. */\nconst PanelHoverAside = styled.div`\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: var(--gap-panel-aside);\n pointer-events: auto;\n`;\n\n/**\n * `useId()`'s output is not scoped to the component that calls it: React\n * allocates from one sequence across the whole render, so a call added here\n * unconditionally shifts every OTHER widget's own `useId()`-derived strings\n * downstream of it (Navball's SVG clip path is one, generated deep inside a\n * widget that never sets `hoverTitle` at all). Two component identities,\n * chosen by the stable `hoverTitle` prop before either renders, keep the\n * call scoped to the panels that actually draw a tiny header; every other\n * panel's own ids are untouched.\n */\nfunction PanelRoot(props: PanelProps): ReactElement {\n return props.hoverTitle ? (\n <PanelRootWithHoverTitle {...props} />\n ) : (\n <PanelRootImpl {...props} hoverTitleId={undefined} />\n );\n}\n\nfunction PanelRootWithHoverTitle(props: PanelProps): ReactElement {\n const hoverTitleId = useId();\n return <PanelRootImpl {...props} hoverTitleId={hoverTitleId} />;\n}\n\nfunction PanelRootImpl({\n panelTitle,\n compactTitle,\n panelAside,\n panelBadges,\n panelStatus,\n panelToolbar,\n panelFilter,\n panelFooter,\n panelTrend,\n inactive,\n fitToSize,\n hoverTitle,\n hoverTitleId,\n panelSidebar,\n sidebarSide,\n sidebarSize,\n panelSections = true,\n sections,\n sectionMinWidth = DEFAULT_SECTION_MIN_WIDTH,\n children,\n ...rest\n}: PanelProps & { hoverTitleId: string | undefined }): ReactElement {\n const contextBadges = usePanelBadgesContext();\n // Asked as a boolean first, because an aside that exists at all is a padded box.\n const hasActionAugments = useWidgetSegmentBound(\"actions\");\n const hasSectionAugments = useWidgetSegmentBound(\"sections\");\n const badges = mergeBadges(panelBadges, contextBadges);\n const badgePills =\n badges.length === 0\n ? null\n : badges.map((b) => {\n const { label, tone, title } = badgeFace(b);\n return (\n <Tooltip key={b.id} text={title} focusable>\n <Badge\n tone={tone}\n /* A decorative chip stays out of the collapsed header's dot summary. */\n report={\n tone === undefined || tone === \"neutral\"\n ? undefined\n : { id: b.id }\n }\n >\n {label}\n </Badge>\n </Tooltip>\n );\n });\n const toolbar =\n panelFilter === undefined ? (\n panelToolbar\n ) : (\n <>\n {panelToolbar}\n <PanelFilterSlot>{filterControlOf(panelFilter)}</PanelFilterSlot>\n </>\n );\n /*\n * The header status merges alarms, `report` badges and the stream status\n * into one summary through the per-item status store. The stream half is the\n * widget's own `panelStatus`: \"absent\" means opposite things per topic, so\n * the host derives only the per-subject blackout grades.\n */\n const status = panelStatus ?? null;\n // A healthy stream contributes nothing, so it never draws a green pill.\n const streamStatus: StreamStatusValue | null =\n status !== null && status !== \"none\" && status !== \"live\" ? status : null;\n useStatusContribution(\n streamStatus\n ? {\n id: \"stream\",\n severity: severityFromStreamStatus(streamStatus),\n label: formatStreamStatus(streamStatus) ?? \"\",\n }\n : null,\n );\n const summary = useStatusSummary();\n /* A badge that already draws its own pill must not also win the summary, or its label shows twice. */\n const summaryDuplicatesABadgePill =\n summary !== null && badges.some((b) => b.id === summary.id);\n\n // With no status store in the tree, fall back to the stream badge.\n const streamLabel =\n streamStatus === null ? null : formatStreamStatus(streamStatus);\n const statusBadge = summaryDuplicatesABadgePill ? null : summary !== null ? (\n <PanelSummaryBadge summary={summary} />\n ) : streamStatus === null || streamLabel === null ? null : (\n <Badge tone={severityFromStreamStatus(streamStatus)} size=\"sm\">\n {streamLabel}\n </Badge>\n );\n /* Prefixed with a plain title, so a change is heard as belonging to a widget. */\n const statusLabel = summary?.label ?? streamLabel;\n const statusAnnouncement =\n statusLabel === null || statusLabel === \"\"\n ? \"\"\n : typeof panelTitle === \"string\"\n ? `${panelTitle}: ${statusLabel}`\n : `Status: ${statusLabel}`;\n // `undefined`, not `null`: a null child would still render the padded aside box.\n const aside =\n panelAside === undefined &&\n statusBadge === null &&\n badgePills === null &&\n !hasActionAugments ? undefined : (\n <>\n {panelAside}\n {/* Controls come ahead of the readout badges. */}\n {hasActionAugments && (\n <AugmentSlot segment=\"actions\" props={NO_SEGMENT_PROPS} />\n )}\n {badgePills}\n {statusBadge}\n </>\n );\n\n /*\n * The tiny tile's header carries state as ONE dot at the worst severity, with\n * the words in its tooltip, never as pills: a pill is as wide as its word and\n * would crowd the tile. The badges still publish to the status store, so the\n * dot knows about them.\n */\n const tinySummary =\n summary ??\n worstBadge(badges) ??\n (streamStatus === null || streamLabel === null\n ? null\n : {\n severity: severityFromStreamStatus(streamStatus),\n label: streamLabel,\n });\n const tinyAnnouncement =\n tinySummary?.severity !== \"nogo\"\n ? undefined\n : typeof panelTitle === \"string\"\n ? `${panelTitle}: ${tinySummary.label}`\n : `Status: ${tinySummary.label}`;\n const tinyAside =\n panelAside === undefined && tinySummary === null && !hasActionAugments ? (\n badges.length === 0 ? undefined : (\n <PanelBadgeReports badges={badges} />\n )\n ) : (\n <>\n {panelAside}\n {hasActionAugments && (\n <AugmentSlot segment=\"actions\" props={NO_SEGMENT_PROPS} />\n )}\n <PanelBadgeReports badges={badges} />\n {tinySummary !== null && (\n <PanelHoverStatus\n summary={tinySummary}\n announcement={tinyAnnouncement}\n />\n )}\n </>\n );\n\n /*\n * With sections, the universal `sections` augment segment moves inside the\n * grid, so an Uplink's section flows into a column beside the host's own.\n * `AugmentSlot` renders a fragment, so each bound augment is its own grid item.\n */\n const isInactive = inactive !== undefined;\n const sectionNodes = isInactive\n ? []\n : Children.toArray(sections as ReactNode);\n const hasSections = sectionNodes.length > 0;\n /* The body is one framed drawing and nothing else: no hand-composed children,\n no bound sections augment (it would add content beside the frame), not\n fitToSize, and the one section is a filling one holding only a frame. */\n const loneFrame =\n children === undefined &&\n !(panelSections && hasSectionAugments) &&\n !fitToSize &&\n sectionNodes.length === 1 &&\n holdsOnlyAFrame(sectionNodes[0]);\n /*\n * A filling section is lifted out of the grid into the body, the box that\n * knows the leftover height, since no grid row can be named for it. The\n * ordinary sections either side become grids of their own, keeping authored\n * order. Off under `fitToSize`.\n */\n const runs: { fill: boolean; nodes: ReactNode[] }[] = [];\n for (const node of sectionNodes) {\n const fill =\n !fitToSize &&\n isValidElement<{ fill?: boolean }>(node) &&\n node.props.fill === true;\n const open = runs.at(-1);\n if (!fill && open !== undefined && !open.fill) open.nodes.push(node);\n else runs.push({ fill, nodes: [node] });\n }\n /* The universal segment lands in the last grid; a body whose every section fills gets one opened for it. */\n if (panelSections && hasSections && !runs.some((run) => !run.fill)) {\n runs.push({ fill: false, nodes: [] });\n }\n const augmentRun = panelSections\n ? runs.reduce((last, run, i) => (run.fill ? last : i), -1)\n : -1;\n /* How many sections take a column, which floors the grid's track width. */\n const flowingIn = (nodes: ReactNode[]) =>\n Math.max(\n 1,\n nodes.filter(\n (node) =>\n !(\n isValidElement<{ full?: boolean }>(node) && node.props.full === true\n ),\n ).length,\n );\n const sectionRuns: ReactNode[] = [];\n for (const run of runs) {\n const index = sectionRuns.length;\n if (run.fill) {\n /* A filling section is never coalesced, so its run is exactly one node. */\n sectionRuns.push(run.nodes[0]);\n continue;\n }\n sectionRuns.push(\n <PanelSections__Grid\n key={`sections-${index}`}\n $min={sectionMinWidth}\n $columns={flowingIn(run.nodes)}\n >\n {run.nodes}\n {index === augmentRun && <WidgetSections />}\n </PanelSections__Grid>,\n );\n }\n const content = isInactive ? (\n <InactiveNotice reason={inactive} />\n ) : !hasSections ? (\n children\n ) : (\n <>\n {children}\n {sectionRuns}\n </>\n );\n\n const body = (\n <PanelBody\n fitToSize={fitToSize}\n loneFrame={loneFrame}\n hoverTitle={hoverTitle}\n >\n <PanelStickyTop data-panel-sticky-top=\"\">\n <PanelDelayRail tiny={hoverTitle} />\n {!hoverTitle && (\n <PanelStickyHeader\n title={panelTitle}\n compactTitle={compactTitle}\n aside={aside}\n toolbar={toolbar}\n />\n )}\n </PanelStickyTop>\n {fitToSize ? (\n <PanelFitBody hoverTitle={hoverTitle}>{content}</PanelFitBody>\n ) : (\n content\n )}\n {panelSections && !hasSections && !isInactive && <WidgetSections />}\n </PanelBody>\n );\n\n return (\n <PanelProviders>\n <PanelContainer\n $railTravels\n $hoverTitle={hoverTitle}\n {...(hoverTitle\n ? {\n tabIndex: 0,\n role: \"group\",\n \"aria-labelledby\": hoverTitleId,\n \"data-tiny-panel\": \"\",\n }\n : {})}\n {...rest}\n >\n <PanelGlow railBandAbove>\n {panelSidebar === undefined || isInactive ? (\n body\n ) : (\n <PanelSplit side={sidebarSide} size={sidebarSize} railBand>\n {body}\n {/* After the body in the DOM; `sidebarSide` moves it visually only. */}\n <PanelSidebar>{panelSidebar}</PanelSidebar>\n </PanelSplit>\n )}\n </PanelGlow>\n {!isInactive && panelTrend !== undefined && (\n <PanelTrend render={panelTrend} />\n )}\n {!isInactive && panelFooter !== undefined && (\n <PanelFooter>{panelFooter}</PanelFooter>\n )}\n {hoverTitle && (\n <PanelHoverTop data-panel-hover-title=\"\">\n <PanelHoverTitlePill as=\"h3\" id={hoverTitleId}>\n {panelTitle}\n </PanelHoverTitlePill>\n {tinyAside !== undefined && (\n <PanelHoverAside>{tinyAside}</PanelHoverAside>\n )}\n </PanelHoverTop>\n )}\n {/* Mounted empty, so the first status is a change to a region already watched; outside the aside, so collapsing does not unmount it. */}\n <LiveRegion visuallyHidden>\n {hoverTitle && tinyAnnouncement !== undefined\n ? \"\"\n : statusAnnouncement}\n </LiveRegion>\n </PanelContainer>\n </PanelProviders>\n );\n}\n\n/**\n * Whether a section is a filling `Section` whose whole content is one\n * `FramedDisplay` (and any `GraphNotice` beside it), reached through plain elements and fragments that each hold\n * only the next.\n */\nfunction holdsOnlyAFrame(section: ReactNode): boolean {\n if (!isValidElement<{ fill?: boolean; children?: ReactNode }>(section)) {\n return false;\n }\n if (section.type !== Section || section.props.fill !== true) return false;\n let node: ReactNode = section.props.children;\n for (;;) {\n // A graph's notice sits beside or under its frame, and a frame with its notice is still the one drawing.\n const only = Children.toArray(node).filter(\n (child) => !(isValidElement(child) && child.type === GraphNotice),\n );\n if (only.length !== 1) return false;\n const [child] = only;\n if (!isValidElement<{ children?: ReactNode }>(child)) return false;\n if (child.type === FramedDisplay) return true;\n if (child.type !== Fragment && typeof child.type !== \"string\") return false;\n node = child.props.children;\n }\n}\n\n/**\n * The frame every widget draws in: a bordered surface with a pinned header\n * (title plus a right-hand aside of controls and badges), a scrolling body\n * with a glow at whichever edge has more to scroll to, and optional toolbar,\n * sidebar, trend strip and footer. Give it content through `sections`: each\n * entry is normally a `Section`, and the panel flows them down one column in\n * a portrait tile and across two or three in a landscape one. A `Section fill`\n * takes the height left over. The panel also reserves a strip above the\n * header for the delay rail, which shows this widget's commands in flight.\n *\n * The header aside holds, in order, `panelAside`, the `actions` augments,\n * the badges (the widget's own `panelBadges`, then contributed ones), and a\n * status badge for the worst of the panel's reported states. When the title\n * and aside no longer fit side by side, the aside collapses to one status dot\n * per severity plus a toggle that opens it.\n *\n * ## Standard slots\n *\n * Every widget drawn in a `Panel` carries three extension slots without\n * declaring them, each named `${componentId}.<segment>` after the widget's\n * registered id (`\"crew-status.badges\"`, for example):\n *\n * - `${componentId}.sections`: an augment slot, bound with `registerAugment`.\n * Each augment renders after everything the widget draws in its body; when\n * the widget passes `sections`, each augment is its own item in the last\n * section grid, so returning a `Section` flows it into a column beside the\n * widget's own. Its component gets no props. A widget can move the slot with\n * {@link WidgetSections} and `panelSections={false}`\n * - `${componentId}.actions`: an augment slot for controls in the header\n * aside, after `panelAside` and before the badges. Its component gets no\n * props. Nothing is reserved for it while nothing is bound\n * - `${componentId}.badges`: a contribution slot, filled with an Uplink\n * handle's `registerContribution`, whose `compute` returns `BadgeEntry`\n * items (`id`, `label`, `tone`, and an optional `held` grade). The panel\n * draws them after the widget's own `panelBadges`, dropping any whose id the\n * widget already uses. A held entry shows its grade's word in place of its\n * label (see {@link badgeFace}), and any entry with a tone other than\n * `neutral` also feeds the panel's status summary\n *\n * An augment in `sections` or `actions` renders only while the Domain named in\n * its `requires` is present, and a badge contribution computes only while its\n * `requires` Domain is present, so an Uplink whose mod is not running leaves\n * the widget unchanged. An augment reads its own Topics; for what the widget\n * is focused on, it reads the widget's scope with `useWidgetScope`. The\n * segment lists are {@link FRAMEWORK_AUGMENT_SEGMENTS} and\n * {@link FRAMEWORK_CONTRIBUTION_SEGMENTS}.\n *\n * ## Parts\n *\n * `Panel` renders its parts and draws no markup of its own, so a widget can\n * compose a variant by hand from `Panel.Container` (the bordered frame),\n * `Panel.Context` (links the body's scroller to the glow), `Panel.Delay` (the\n * delay rail), `Panel.Header`, `Panel.Title`, `Panel.Toolbar`, `Panel.Glow`,\n * `Panel.Body` (the scrolling, inset content box), `Panel.Section`,\n * `Panel.Sidebar` and `Panel.Footer`.\n *\n * @example\n * A typical widget body:\n * ```tsx\n * <Panel\n * panelTitle=\"Fuel Status\"\n * compactTitle=\"Fuel\"\n * panelBadges={lowFuel ? [{ id: \"low\", label: \"Low\", tone: \"warn\" }] : undefined}\n * panelFooter={\n * <Text>\n * Total <Unit value={total} />\n * </Text>\n * }\n * sections={[\n * <Section key=\"stages\" title=\"Stages\">\n * <StageList stages={stages} />\n * </Section>,\n * <Section key=\"tanks\" title=\"Tanks\">\n * <TankList tanks={tanks} />\n * </Section>,\n * ]}\n * />\n * ```\n *\n * @example\n * An Uplink adding a section and a header badge to another widget through its\n * standard slots:\n * ```tsx\n * import {\n * defineUplinkClient,\n * registerAugment,\n * useTelemetry,\n * type Value,\n * } from \"@ksp-gonogo/sitrep-sdk\";\n * import { Section, Text, Unit } from \"@ksp-gonogo/ui-kit\";\n *\n * declare module \"@ksp-gonogo/sitrep-sdk\" {\n * interface TopicPayloadMap {\n * \"example.heartbeat\": { ut: Value<\"ut\"> };\n * }\n * }\n *\n * export const EXAMPLE = defineUplinkClient({\n * id: \"example\",\n * version: \"0.0.1\",\n * name: \"Example\",\n * });\n *\n * function CadenceSection() {\n * const heartbeat = useTelemetry(\"example.heartbeat\");\n * if (heartbeat.state !== \"observed\") return null;\n * return (\n * <Section title=\"Example\">\n * <Text>\n * Last beat at UT <Unit value={heartbeat.value.ut} />\n * </Text>\n * </Section>\n * );\n * }\n *\n * registerAugment({\n * id: \"example-cadence-section\",\n * augments: \"space-center-status.sections\",\n * requires: \"example\",\n * channels: [\"example.heartbeat\"],\n * component: CadenceSection,\n * owner: EXAMPLE,\n * });\n *\n * EXAMPLE.registerContribution({\n * id: \"cadence-badge\",\n * contributes: \"space-center-status.badges\",\n * deps: [\"example.heartbeat\"],\n * requires: \"example\",\n * compute: (topics) =>\n * topics[\"example.heartbeat\"]\n * ? [{ id: \"cadence\", label: \"Publishing\", tone: \"info\" as const }]\n * : null,\n * });\n * ```\n *\n * @category Panel\n */\nexport const Panel = Object.assign(PanelRoot, {\n Context: PanelContextProvider,\n Delay: PanelDelayRail,\n Container: PanelContainer,\n Header: PanelHeader,\n Toolbar: PanelToolbar,\n Footer: PanelFooter,\n Title: PanelTitle,\n Glow: PanelGlow,\n Body: PanelBody,\n Section,\n Sidebar: PanelSidebar,\n});\n","import { useId, useState } from \"react\";\nimport styled, { css } from \"styled-components\";\nimport { focusRingInset } from \"../focusRing\";\nimport { LiveRegion } from \"../LiveRegion\";\nimport { TONE_TEXT } from \"../tone\";\nimport { CommandDelay } from \"./CommandDelay\";\nimport { CommandList } from \"./CommandList\";\nimport { STREAM_MIN_DELAY_SECONDS } from \"./ControlDelayStream\";\nimport {\n commandFailedSentence,\n type RailFailed,\n} from \"./commandFailedSentence\";\nimport { commandFoundSentence, type RailFound } from \"./commandFoundSentence\";\nimport { commandLossSentence, type RailLoss } from \"./commandLossSentence\";\nimport {\n commandRefusalSentence,\n type RailRefusal,\n} from \"./commandRefusalSentence\";\nimport {\n commandUndeliveredSentence,\n type RailUndelivered,\n} from \"./commandUndeliveredSentence\";\nimport { type CommandHandle, useActiveHandles } from \"./DelayRailContext\";\nimport { railMark } from \"./railTags\";\n\n/**\n * Whether a handle's `CommandDelay` would draw anything, mirroring its own\n * null decision: a continuous handle needs real delay AND buffers, a discrete\n * one needs in-flight rows. An idle handle stays registered but draws no rail\n * chrome.\n */\nfunction handleHasContent(handle: CommandHandle): boolean {\n if (railMark(handle.tags) === \"ribbon\") {\n const delay = handle.effectiveDelaySeconds;\n const ribbons = handle.ribbons?.length ?? 0;\n const streams = handle.streams?.length ?? 0;\n if (delay === null || delay <= 0) return false;\n // The graph's own rule, so the rail never mounts around nothing: streams need its floor, ribbons do not.\n return ribbons > 0 || (delay >= STREAM_MIN_DELAY_SECONDS && streams > 0);\n }\n return handle.inFlight.length > 0;\n}\n\n/**\n * The panel's signal-delay rail: a strip along the top edge that shows each\n * command the widget has sent, through {@link CommandDelay}, reading them\n * from the nearest {@link DelayRailContext}.\n *\n * Every panel reserves the strip whether or not it has a command to show, so\n * a command going out never moves the widget. `Panel` draws it itself; in a\n * hand-composed panel, make it the container's first child.\n *\n * Collapsed, the rail sits in normal flow inside the band and moves nothing.\n * It is a disclosure: a native `<button>` laid over the strip controls the\n * detail beside it. Activating it (Esc collapses) PINS the rail, and pinning\n * GROWS it: each command switches to its `expanded` view and the title and\n * body are pushed down. Opened while the body is already scrolled, it extends\n * over the content instead, so the scroll position is kept. `aria-expanded`\n * carries the pin; hover is a transient preview that says nothing to assistive\n * tech. Pinned, the detail takes its own clicks and only the collapse hint\n * toggles.\n *\n * The strip is empty unless a command is in flight, refused, unanswered,\n * found, never sent, or answered with a fault.\n *\n * `tiny` is for a tiny panel with no header: the empty strip draws no\n * background, so it does not cover the panel's focus ring.\n */\nexport function PanelDelayRail({ tiny }: { tiny?: boolean } = {}) {\n const handles = useActiveHandles();\n const visible = handles.filter(handleHasContent);\n // Outcomes come from EVERY registered handle: a settled command has nothing in flight, so `handleHasContent` would hide it.\n const refusals: RailRefusal[] = handles.flatMap((h) =>\n (h.refusals ?? []).map((r) => ({ ...r, tags: h.tags })),\n );\n const losses: RailLoss[] = handles.flatMap((h) =>\n (h.losses ?? []).map((l) => ({ ...l, tags: h.tags })),\n );\n // A found answers an unconfirmed command, so it is never counted with the failures.\n const founds: RailFound[] = handles.flatMap((h) =>\n (h.founds ?? []).map((f) => ({ ...f, tags: h.tags })),\n );\n // An undelivered command provably did not run, so it counts with the failures.\n const undelivered: RailUndelivered[] = handles.flatMap((h) =>\n (h.undelivered ?? []).map((u) => ({ ...u, tags: h.tags })),\n );\n // The mod said what became of each of these, and none of them ran.\n const failures: RailFailed[] = handles.flatMap((h) =>\n (h.failures ?? []).map((f) => ({ ...f, tags: h.tags })),\n );\n // A loss may still have run, so it is counted as unconfirmed and never as failed.\n const failedCount = refusals.length + undelivered.length + failures.length;\n const hasContent =\n visible.length > 0 ||\n failedCount > 0 ||\n losses.length > 0 ||\n founds.length > 0;\n const [pinned, setPinned] = useState(false);\n // Hover preview is React state, not CSS `:hover`, so one flag decides both the height and which view each command draws.\n const [previewing, setPreviewing] = useState(false);\n /*\n * An un-pin click wins over a pointer still resting on the rail. Cleared on\n * the next real `mouseenter`, not on leave: collapsing under a stationary\n * pointer dispatches no `mouseleave`.\n */\n const [suppressHoverPreview, setSuppressHoverPreview] = useState(false);\n const grown = pinned || (previewing && !suppressHoverPreview);\n const detailId = useId();\n\n // Growing the rail moves the title and body by ordinary flow; there is no height to measure or publish.\n\n // Continuous on top, discrete underneath, each keeping its own order.\n const ordered = [\n ...visible.filter((h) => railMark(h.tags) === \"ribbon\"),\n ...visible.filter((h) => railMark(h.tags) !== \"ribbon\"),\n ];\n\n // Each dismiss routes to the handle that owns the outcome; absent when no handle can dismiss.\n const canDismissRefusal = handles.some(\n (h) => h.dismiss && (h.refusals?.length ?? 0) > 0,\n );\n const dismissRefusal = canDismissRefusal\n ? (id: string) =>\n handles.find((h) => h.refusals?.some((r) => r.id === id))?.dismiss?.(id)\n : undefined;\n const canDismissLoss = handles.some(\n (h) => h.dismiss && (h.losses?.length ?? 0) > 0,\n );\n const dismissLoss = canDismissLoss\n ? (id: string) =>\n handles.find((h) => h.losses?.some((l) => l.id === id))?.dismiss?.(id)\n : undefined;\n const canDismissUndelivered = handles.some(\n (h) => h.dismiss && (h.undelivered?.length ?? 0) > 0,\n );\n const dismissUndelivered = canDismissUndelivered\n ? (id: string) =>\n handles\n .find((h) => h.undelivered?.some((u) => u.id === id))\n ?.dismiss?.(id)\n : undefined;\n const canDismissFailure = handles.some(\n (h) => h.dismiss && (h.failures?.length ?? 0) > 0,\n );\n const dismissFailure = canDismissFailure\n ? (id: string) =>\n handles.find((h) => h.failures?.some((f) => f.id === id))?.dismiss?.(id)\n : undefined;\n const canDismissFound = handles.some(\n (h) => h.dismiss && (h.founds?.length ?? 0) > 0,\n );\n const dismissFound = canDismissFound\n ? (id: string) =>\n handles.find((h) => h.founds?.some((f) => f.id === id))?.dismiss?.(id)\n : undefined;\n\n return (\n // An EMPTY band carries only its own marker, since every widget renders it.\n <PanelDelayRail__Frame\n data-panel-rail-frame=\"\"\n $tiny={tiny}\n $hasContent={hasContent}\n >\n {/*\n The rail's one announcer, outside the toggle. Mounted once a command\n registers, before any outcome exists, so the first outcome lands in a\n region assistive tech is already watching.\n */}\n {handles.length > 0 && (\n <LiveRegion visuallyHidden additionsOnly>\n {refusals.map((r) => (\n <span key={`refusal:${r.id}`}>{commandRefusalSentence(r)}</span>\n ))}\n {losses.map((l) => (\n <span key={`loss:${l.id}`}>{commandLossSentence(l)}</span>\n ))}\n {undelivered.map((u) => (\n <span key={`undelivered:${u.id}`}>\n {commandUndeliveredSentence(u)}\n </span>\n ))}\n {failures.map((f) => (\n <span key={`failure:${f.id}`}>{commandFailedSentence(f)}</span>\n ))}\n {founds.map((f) => (\n <span key={`found:${f.id}`}>{commandFoundSentence(f)}</span>\n ))}\n </LiveRegion>\n )}\n {/* Nothing to draw leaves the band standing EMPTY: the strip is the panel's, not the traffic's. */}\n {!hasContent ? null : (\n <PanelDelayRail__Rail\n data-panel-rail=\"\"\n data-grown={grown}\n data-pinned={pinned}\n data-suppress-hover={suppressHoverPreview}\n onMouseEnter={() => {\n setSuppressHoverPreview(false);\n setPreviewing(true);\n }}\n onMouseLeave={() => setPreviewing(false)}\n onKeyDown={(e) => {\n if (e.key === \"Escape\" && pinned) {\n e.stopPropagation();\n setPinned(false);\n // As with the un-pin click, a resting pointer must not hold the preview open.\n setSuppressHoverPreview(true);\n }\n }}\n >\n <PanelDelayRail__Toggle\n type=\"button\"\n aria-expanded={pinned}\n aria-controls={detailId}\n aria-label=\"Signal-delay detail\"\n onClick={() => {\n setPinned((p) => {\n const next = !p;\n if (!next) setSuppressHoverPreview(true);\n return next;\n });\n }}\n >\n {/* Hidden rather than unmounted: a node leaving from under a resting pointer reads to React as the pointer entering the rail. */}\n <PanelDelayRail__CollapseHint aria-hidden=\"true\" hidden={!grown}>\n ▲\n </PanelDelayRail__CollapseHint>\n </PanelDelayRail__Toggle>\n <PanelDelayRail__Detail id={detailId} data-panel-rail-detail=\"\">\n {ordered.map((h) => (\n <CommandDelay\n key={h.id}\n handle={h}\n variant={grown ? \"expanded\" : \"rail\"}\n // A handle that names its own graph keeps that name at both heights.\n ariaLabel={h.ariaLabel ?? (grown ? \"Delay detail\" : undefined)}\n />\n ))}\n {!grown &&\n (failedCount > 0 || losses.length > 0 || founds.length > 0) && (\n // One run for every count, since they share the band's single grid cell.\n <PanelDelayRail__Summaries>\n {failedCount > 0 && (\n <PanelDelayRail__WarnSummary>\n {failedCount === 1\n ? \"1 command failed\"\n : `${failedCount} commands failed`}\n </PanelDelayRail__WarnSummary>\n )}\n {losses.length > 0 && (\n <PanelDelayRail__WarnSummary>\n {losses.length === 1\n ? \"1 command unconfirmed\"\n : `${losses.length} commands unconfirmed`}\n </PanelDelayRail__WarnSummary>\n )}\n {founds.length > 0 && (\n <PanelDelayRail__FoundSummary>\n {founds.length === 1\n ? \"1 unconfirmed command answered\"\n : `${founds.length} unconfirmed commands answered`}\n </PanelDelayRail__FoundSummary>\n )}\n </PanelDelayRail__Summaries>\n )}\n </PanelDelayRail__Detail>\n </PanelDelayRail__Rail>\n )}\n {/* Outside the rail, so the grown lists push the panel down rather than share the rail's grid cell. */}\n {grown && refusals.length > 0 && (\n <CommandList\n kind=\"refused\"\n entries={refusals}\n onDismiss={dismissRefusal}\n live={false}\n />\n )}\n {grown && losses.length > 0 && (\n <CommandList\n kind=\"lost\"\n entries={losses}\n onDismiss={dismissLoss}\n live={false}\n />\n )}\n {grown && undelivered.length > 0 && (\n <CommandList\n kind=\"undelivered\"\n entries={undelivered}\n onDismiss={dismissUndelivered}\n live={false}\n />\n )}\n {grown && failures.length > 0 && (\n <CommandList\n kind=\"failed\"\n entries={failures}\n onDismiss={dismissFailure}\n live={false}\n />\n )}\n {/* Last, so reading down the rail meets the silence and then its answer. */}\n {grown && founds.length > 0 && (\n <CommandList\n kind=\"found\"\n entries={founds}\n onDismiss={dismissFound}\n live={false}\n />\n )}\n </PanelDelayRail__Frame>\n );\n}\n\n/**\n * The rail's box: exactly the band tall when collapsed, pushing the header and\n * body down by every pixel it grows past it. The band is permanent, so a\n * command arriving never moves a watching widget's content. The negative top\n * margin pulls it into `PanelContainer`'s top inset in the container placement;\n * `PanelStickyTop` gives it back inside the sticky unit.\n */\nconst PanelDelayRail__Frame = styled.div<{\n $tiny?: boolean;\n $hasContent?: boolean;\n}>`\n /* Never shrinks below its content: the grown rail keeps its full height and the body gives up the difference. */\n flex: 0 0 auto;\n /* Fully opaque so content scrolling under the rail never ghosts through: except a tiny panel's empty band, which paints nothing so the panel's own focus ring stays visible behind it. */\n background: ${({ $tiny, $hasContent }) =>\n $tiny && !$hasContent ? \"transparent\" : \"var(--color-surface-panel)\"};\n /* Up into the container's top inset, the band; both read --panel-rail-band, so they cannot drift. */\n margin-top: calc(-1 * var(--panel-rail-band));\n min-height: var(--panel-rail-band);\n`;\n\n/**\n * The rail, filling the reserved band. Collapsed, every handle overlays in one\n * grid cell capped at the band's height. Grown (hover or pin), it becomes a\n * flex column that outgrows the band and pushes the title and body down.\n */\nconst grownRail = css`\n display: flex;\n flex-direction: column;\n gap: var(--gap-delay-rail);\n /* A generous cap the grown content fits inside; the visible height settles at the content height. */\n max-height: 800px;\n /* Full-bleed, so the stream graph spans the widget edge to edge; each child owns its own inset. */\n padding: 0;\n\n & > *,\n & > [data-panel-rail-detail] > * {\n grid-area: auto;\n }\n`;\n\nconst PanelDelayRail__Rail = styled.div`\n width: 100%;\n margin: 0;\n padding: 0;\n position: relative;\n\n /* Height follows min(content, max-height), so animating max-height opens and collapses the rail (auto is not animatable). */\n display: grid;\n height: auto;\n /* The band, and nothing over it: the strip a widget reserves is the strip the rail fills. */\n max-height: var(--panel-rail-band);\n overflow: hidden;\n transition: max-height var(--duration-slow) var(--ease-standard);\n\n & > *,\n & > [data-panel-rail-detail] > * {\n grid-area: 1 / 1;\n }\n\n &:not([data-pinned=\"true\"]) > [data-panel-rail-detail] > * {\n pointer-events: none;\n }\n\n @media (prefers-reduced-motion: reduce) {\n transition: none;\n }\n\n /* Open (pinned or hover preview) grows past the band, driven by React state because the published height must be the grown one only. */\n &[data-grown=\"true\"] {\n ${grownRail}\n }\n`;\n\n/**\n * The disclosure button, laid over the whole rail with no chrome of its own, so\n * the strip is one click target and the focus ring traces the rail's edge.\n * Pinned, it lets pointer events through to the detail and takes a click only\n * on its collapse hint.\n */\nconst PanelDelayRail__Toggle = styled.button`\n appearance: none;\n position: absolute;\n inset: 0;\n border: 0;\n margin: 0;\n padding: 0;\n background: transparent;\n color: inherit;\n font: inherit;\n cursor: pointer;\n\n ${focusRingInset}\n\n &[aria-expanded=\"true\"] {\n pointer-events: none;\n }\n\n`;\n\n/**\n * The detail the toggle controls. It generates no box, so each command stays\n * a direct grid or flex item of the rail and lays out exactly as a child of it.\n */\nconst PanelDelayRail__Detail = styled.div`\n display: contents;\n`;\n\n/**\n * The collapsed strip's failure count: only the count, since a sentence cannot\n * fit the band. Not a live region; the rail's own announcer reads each outcome.\n */\nconst PanelDelayRail__WarnSummary = styled.span`\n color: ${TONE_TEXT.warn};\n`;\n\n/** The end-aligned run both collapsed-strip counts sit in. */\nconst PanelDelayRail__Summaries = styled.span`\n align-self: center;\n justify-self: end;\n display: flex;\n gap: var(--gap-rail-summaries);\n padding: 0 var(--gutter-panel);\n font-size: var(--font-size-compact);\n /* Flush, so single-line chrome text fits the reserved band on a coarse pointer too. */\n line-height: var(--line-height-flush);\n font-weight: 700;\n letter-spacing: 0.04em;\n white-space: nowrap;\n pointer-events: none;\n`;\n\n/** The collapsed strip's found count, in the notice colour because it says the opposite of the failure count. */\nconst PanelDelayRail__FoundSummary = styled.span`\n color: var(--color-info-text);\n`;\n\n/**\n * The grown rail's sighted cue that it collapses; `aria-expanded` carries it\n * for assistive tech. Pinned, it is the rail's one click target, reaching past\n * the glyph.\n */\nconst PanelDelayRail__CollapseHint = styled.span`\n position: absolute;\n top: var(--offset-rail-hint);\n right: var(--gutter-panel);\n font-size: var(--font-size-caption);\n color: var(--color-text-muted);\n letter-spacing: 0.06em;\n text-transform: uppercase;\n pointer-events: auto;\n cursor: pointer;\n z-index: 1;\n\n &::before {\n content: \"\";\n position: absolute;\n inset: calc(-1 * var(--outset-rail-hint-target));\n }\n`;\n","import styled from \"styled-components\";\nimport { TONE_LABEL, TONE_MARK, TONE_ON_STATUS, TONE_STATUS } from \"../tone\";\nimport type { Severity } from \"./severity\";\n\nexport interface PanelStatusDotProps {\n severity: Severity;\n /**\n * How many contributors sit at this severity. The number is shown INSIDE the\n * dot only when greater than 1; a single contributor is just the coloured dot.\n * Defaults to 1.\n */\n count?: number;\n}\n\n/**\n * One per-severity status dot for a Panel's collapsed header: a small coloured\n * disc carrying the count when more than one contributor sits at that\n * severity, growing sideways into a pill when the count takes more than one\n * digit.\n *\n * It carries `role=\"img\"` and an accessible name (\"warning\", \"3 caution\")\n * rather than being hidden: in a collapsed header the dot row is the status\n * display, so a screen reader must reach it.\n */\nexport function PanelStatusDot({ severity, count = 1 }: PanelStatusDotProps) {\n const withCount = count > 1;\n return (\n <PanelStatusDot__Root\n data-panel-status-dot=\"\"\n data-severity={severity}\n role=\"img\"\n aria-label={\n withCount ? `${count} ${TONE_LABEL[severity]}` : TONE_LABEL[severity]\n }\n $mark={TONE_MARK[severity]}\n $digits={withCount ? String(count).length : 1}\n >\n {withCount && (\n <PanelStatusDot__Count\n $fill={TONE_STATUS[severity]}\n $ink={TONE_ON_STATUS[severity]}\n >\n {count}\n </PanelStatusDot__Count>\n )}\n </PanelStatusDot__Root>\n );\n}\n\n// Sized to the panel title's cap-height, not its font-size, so the dot sits inside the title's text line.\nconst DOT_DIAMETER = \"8px\";\n\nconst PanelStatusDot__Root = styled.span<{\n $mark: string;\n $digits: number;\n}>`\n position: relative;\n flex: 0 0 auto;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n /* One fixed diameter whether or not a count is shown; the count is out of flow, so it cannot affect this box's size. */\n height: ${DOT_DIAMETER};\n /* The count's own size, so the width below can be counted in its digits. */\n font-size: 7px;\n /* More than one digit grows the dot sideways into a pill, keeping its height and its place on the title's line. */\n width: ${({ $digits }) =>\n $digits > 1 ? `calc(${$digits}ch + 4px)` : DOT_DIAMETER};\n border-radius: ${({ $digits }) =>\n $digits > 1 ? \"var(--radius-pill)\" : \"var(--radius-circle)\"};\n background: ${({ $mark }) => $mark};\n /* A rim in a lighter tint of the dot's own fill plus a glow bloom, so the dot reads as a lit indicator. */\n box-shadow:\n 0 0 0 1px color-mix(in srgb, ${({ $mark }) => $mark} 78%, white),\n 0 0 4px 1px ${({ $mark }) => $mark};\n`;\n\n/* A lone dot is a mark; one carrying its count is a status fill under its own text, so the count paints that fill over the whole dot. */\nconst PanelStatusDot__Count = styled.span<{ $fill: string; $ink: string }>`\n /* Out of flow, centred over the fixed parent box with no say over its size. */\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: ${({ $fill }) => $fill};\n display: flex;\n align-items: center;\n justify-content: center;\n pointer-events: none;\n color: ${({ $ink }) => $ink};\n /* Below the --font-size-2xs floor, which would not fit the 8px dot. */\n font-size: inherit;\n font-variant-numeric: tabular-nums;\n font-weight: 700;\n line-height: var(--line-height-flush);\n`;\n","import { css } from \"styled-components\";\n\n/**\n * The one treatment for a container's own name: a panel's header and a modal's\n * title. It takes the value size, since a title names its container rather than\n * annotating something beside it, and keeps the uppercase, tracked, dim family\n * that gives the chrome its character.\n */\nexport const titleText = css`\n font-size: var(--font-size-value);\n font-weight: 600;\n letter-spacing: 0.15em;\n text-transform: uppercase;\n color: var(--color-text-dim);\n`;\n","import {\n type RefObject,\n useCallback,\n useEffect,\n useLayoutEffect,\n useRef,\n useState,\n} from \"react\";\n\n/**\n * Room to spare a longer form needs before it wins back. Without it a box\n * sitting exactly on the boundary alternates forever, because showing the\n * longer form is what makes it not fit.\n */\nconst RELENGTHEN_MARGIN_PX = 12;\n\n/**\n * One candidate's natural width, measured on an isolated clone of `el` with its\n * text replaced, so the font and spacing are the real ones and a flex-squeezed\n * live box cannot report its squeezed width.\n *\n * Every candidate carries its own text, the full form included: reading the\n * live element back after a shortening would measure the short text as the\n * full form's width and oscillate.\n */\nfunction measureCandidateWidth(el: HTMLElement | null, text: string): number {\n if (!el || typeof document === \"undefined\") return 0;\n const clone = el.cloneNode(true) as HTMLElement;\n clone.textContent = text;\n clone.style.position = \"fixed\";\n clone.style.visibility = \"hidden\";\n clone.style.pointerEvents = \"none\";\n clone.style.left = \"-99999px\";\n clone.style.top = \"-99999px\";\n // The live element truncates within its column; the clone must report the width the text wants.\n clone.style.width = \"max-content\";\n clone.style.maxWidth = \"none\";\n document.body.appendChild(clone);\n const width = clone.getBoundingClientRect().width;\n document.body.removeChild(clone);\n return width;\n}\n\n/**\n * Which candidate fits, as an index into a longest-first list.\n *\n * Anything unmeasured (a zero available width, or a first candidate that\n * measured zero) holds index 0, the full form: shortening a title on a\n * measurement that never happened is worse than leaving it long.\n */\nexport function fittedTitleIndex(\n previous: number,\n available: number,\n widths: readonly number[],\n): number {\n if (available <= 0 || widths.length === 0 || widths[0] <= 0) return 0;\n for (let i = 0; i < widths.length; i++) {\n // Growing back to a form longer than the current one has to clear the margin; shrinking to this one, or staying put, does not.\n const room = i < previous ? available - RELENGTHEN_MARGIN_PX : available;\n if (widths[i] <= room) return i;\n }\n return widths.length - 1;\n}\n\n/**\n * The title form to render, given the full one and any shorter alternatives,\n * chosen by measuring the box rather than by a column count.\n *\n * `compact` is longest-first, so \"ORBIT VIEW\" then \"OVIEW\" then \"OV\" is a full\n * title of `ORBIT VIEW` with `compact={[\"OVIEW\", \"OV\"]}`. The returned\n * `compacted` flag is what earns the full name an `aria-label` and a tooltip: a\n * screen reader hearing \"KSC\" has lost something a sighted operator only gave\n * up because the tile is small.\n */\nexport function useFittedTitle(\n titleRef: RefObject<HTMLElement | null>,\n full: string,\n compact: readonly string[],\n): { index: number; compacted: boolean } {\n const [index, setIndex] = useState(0);\n const indexRef = useRef(index);\n indexRef.current = index;\n\n // `compact` is a fresh array on most renders, so its contents are the key, joined on a newline because a short title may contain spaces.\n const key = [full, ...compact].join(\"\\n\");\n\n const recompute = useCallback(() => {\n const el = titleRef.current;\n const forms = key.split(\"\\n\");\n if (!el || forms.length < 2) {\n if (indexRef.current !== 0) {\n indexRef.current = 0;\n setIndex(0);\n }\n return;\n }\n const widths = forms.map((text) => measureCandidateWidth(el, text));\n const next = fittedTitleIndex(indexRef.current, el.clientWidth, widths);\n if (next !== indexRef.current) {\n indexRef.current = next;\n setIndex(next);\n }\n }, [titleRef, key]);\n\n // A changed title moves no box a ResizeObserver would see, so it recomputes through `recompute`'s identity.\n useLayoutEffect(() => {\n recompute();\n }, [recompute]);\n\n useEffect(() => {\n const el = titleRef.current;\n if (!el || typeof ResizeObserver === \"undefined\") return;\n const ro = new ResizeObserver(() => recompute());\n ro.observe(el);\n return () => ro.disconnect();\n }, [titleRef, recompute]);\n\n return { index, compacted: index > 0 };\n}\n","import { useEffect, useRef, useState } from \"react\";\n\n/**\n * Observe the registered scroller and derive a value from it, recomputed on\n * scroll and on any size or child-list change to the scroller. Drivable in\n * jsdom by dispatching a `scroll` event.\n */\nexport function useScrollerMetric<Metric>(\n el: HTMLElement | null,\n compute: (el: HTMLElement) => Metric,\n isEqual: (a: Metric, b: Metric) => boolean,\n initial: Metric,\n): Metric {\n const [value, setValue] = useState<Metric>(initial);\n // Latest closures without re-subscribing: the effect keys off `el` alone.\n const computeRef = useRef(compute);\n computeRef.current = compute;\n const equalRef = useRef(isEqual);\n equalRef.current = isEqual;\n const valueRef = useRef(value);\n valueRef.current = value;\n\n useEffect(() => {\n if (!el) return;\n // Held beside the state so an unchanged reading never reaches `setValue`: a subtree observer fires often, and a no-op set still schedules a render.\n let last = valueRef.current;\n const update = () => {\n const next = computeRef.current(el);\n if (equalRef.current(last, next)) return;\n last = next;\n setValue(next);\n };\n update();\n el.addEventListener(\"scroll\", update, { passive: true });\n const ro = new ResizeObserver(update);\n ro.observe(el);\n for (const child of Array.from(el.children)) ro.observe(child);\n const mo = new MutationObserver(() => {\n for (const child of Array.from(el.children)) ro.observe(child);\n update();\n });\n // The subtree, because content can overflow the scroller from a descendant without any observed box changing size.\n mo.observe(el, { childList: true, subtree: true });\n return () => {\n el.removeEventListener(\"scroll\", update);\n ro.disconnect();\n mo.disconnect();\n };\n }, [el]);\n\n return value;\n}\n\n/** Whether a scroller's content runs past its box on either axis. */\nexport function overflowsBox(el: HTMLElement): boolean {\n return el.scrollHeight > el.clientHeight || el.scrollWidth > el.clientWidth;\n}\n\n/**\n * The `tabIndex` a scroll body takes: 0 while its content overflows, so it can\n * be scrolled from the keyboard in WebKit, which never focuses a scroller on\n * its own; none while everything fits, so a body with nothing to scroll adds\n * no tab stop.\n */\nexport function useKeyboardScrollable(el: HTMLElement | null): 0 | undefined {\n const overflows = useScrollerMetric(el, overflowsBox, Object.is, false);\n return overflows ? 0 : undefined;\n}\n","import type { Tone } from \"@ksp-gonogo/sitrep-sdk\";\nimport type { ButtonHTMLAttributes } from \"react\";\nimport { forwardRef } from \"react\";\nimport { Button } from \"./Button\";\n\n/**\n * The fill an active {@link ToggleButton} takes: `go`, or `nogo` / `warn` when\n * being on is destructive or needs attention.\n *\n * @category Button\n */\nexport type ToggleButtonTone = Extract<Tone, \"go\" | \"nogo\" | \"warn\">;\n\n/**\n * {@link ToggleButton}'s size, the same steps as {@link ButtonSize}.\n *\n * @category Button\n */\nexport type ToggleButtonSize = \"sm\" | \"md\";\n\n/**\n * Props for {@link ToggleButton}. Any other `button` attribute passes through.\n *\n * @category Button\n */\nexport interface ToggleButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement> {\n /** Whether the toggle is on. Drives `aria-pressed` and the filled look. Defaults to `false`. */\n active?: boolean;\n /** The fill while active. Defaults to `go`. */\n tone?: ToggleButtonTone;\n /** Defaults to `md`. */\n size?: ToggleButtonSize;\n}\n\n/**\n * A two-state toggle: a real `<button>` whose `aria-pressed` follows `active`,\n * for controls that switch between on and off (mode pickers, filter toggles,\n * rate pickers). It draws as a {@link Button}, filled in `tone` while active\n * and plain while not.\n *\n * ToggleButton or {@link Switch}? They are different ARIA patterns:\n *\n * - ToggleButton is a button whose label is the thing being chosen, usually\n * one of several peers (warp rates, a filter row, a view mode). Pressing it\n * acts, and `aria-pressed` says which peer is on\n * - `Switch` is a single boolean setting with a label beside it, rendered as a\n * checkbox with a track and thumb. It configures rather than acts\n *\n * If the control sits in a row of alternatives, it is a ToggleButton. If it\n * sits in a settings list next to its own label, it is a Switch.\n *\n * @example\n * ```tsx\n * <Inline>\n * {RATES.map((rate) => (\n * <ToggleButton\n * key={rate}\n * size=\"sm\"\n * active={rate === current}\n * onClick={() => setRate(rate)}\n * >\n * {rate}x\n * </ToggleButton>\n * ))}\n * </Inline>\n * ```\n *\n * @category Button\n */\nexport const ToggleButton = forwardRef<HTMLButtonElement, ToggleButtonProps>(\n function ToggleButton(\n {\n active = false,\n tone = \"go\",\n size = \"md\",\n type = \"button\",\n \"aria-pressed\": ariaPressed,\n ...rest\n },\n ref,\n ) {\n return (\n <Button\n ref={ref}\n type={type}\n /* The tone colours the pressed fill only; an inactive toggle is a plain button. */\n tone={active ? tone : undefined}\n size={size}\n pressed={active}\n aria-pressed={ariaPressed ?? active}\n {...rest}\n />\n );\n },\n);\n","/**\n * How much room a widget tile has, derived from its `w` and `h` grid units by\n * {@link getSizeBucket} so every widget uses the same boundaries.\n *\n * - `tiny`: a single-glance status or one key readout\n * - `small`: essential numbers, minimal chrome\n * - `normal`: the full widget UI\n *\n * A widget that declares a tiny mode is drawn by the kit in `tiny`; every other\n * bucket, and every bucket of a widget without one, is the widget's own to lay\n * out.\n *\n * @category Layout\n */\nexport type SizeBucket = \"tiny\" | \"small\" | \"normal\";\n\nexport { TINY_SIZE } from \"@ksp-gonogo/sitrep-sdk\";\n\n/**\n * A tile narrower or shorter than this is `tiny`.\n *\n * @category Layout\n */\nexport const TINY_BELOW = { w: 5, h: 4 } as const;\nconst SMALL_W = 8;\nconst SMALL_H = 7;\n\n/**\n * The {@link SizeBucket} a `w` by `h` tile falls in: `tiny` below\n * {@link TINY_BELOW} on either axis, `small` below 8 wide or 7 tall, `normal`\n * otherwise. Returns `normal` while either dimension is `undefined`.\n *\n * @category Layout\n */\nexport function getSizeBucket(\n w: number | undefined,\n h: number | undefined,\n): SizeBucket {\n // Missing dims (e.g. before the grid has measured), assume normal so the first paint isn't a flash of compact UI.\n if (w === undefined || h === undefined) return \"normal\";\n if (w < TINY_BELOW.w || h < TINY_BELOW.h) return \"tiny\";\n if (w < SMALL_W || h < SMALL_H) return \"small\";\n return \"normal\";\n}\n\n/**\n * The orientation of a widget tile, independent of its {@link SizeBucket}: a\n * tile can be `tiny` and `landscape`, or `normal` and `portrait`. The bucket\n * says how much chrome to show; the shape says which way to flow it.\n *\n * - `portrait`: tall and narrow. Stack content in one column and dock any\n * secondary panel or legend below the primary content\n * - `landscape`: wide and short. Flow content into columns or place a\n * secondary panel beside the primary content\n * - `square`: neither axis dominates; the default near-1:1 layout, and the\n * value before the grid has measured the tile\n *\n * @category Layout\n */\nexport type WidgetShape = \"portrait\" | \"landscape\" | \"square\";\n\n/**\n * A tile's {@link WidgetShape} and the aspect ratio it was read from.\n *\n * @category Layout\n */\nexport interface WidgetShapeInfo {\n shape: WidgetShape;\n /**\n * Raw grid aspect ratio, `w / h`. > 1 is wider-than-tall, < 1 is\n * taller-than-wide, exactly 1 is square. Provided for widgets that want a\n * continuous signal (e.g. an interpolated gap or column count) rather than\n * the coarse three-way enum. `1` when dims are missing.\n */\n aspect: number;\n}\n\n/**\n * Landscape begins once a widget is this many times wider than it is tall.\n *\n * The render harness's two aspect-extreme modes are deliberately log-symmetric\n * around 1:1: `portrait-5x18` has aspect ≈ 0.28 and `landscape-18x5` ≈ 3.6,\n * and log(0.28) ≈ −1.27 mirrors log(3.6) ≈ +1.28. So we center the square band\n * on aspect = 1 and derive the portrait cutoff as the reciprocal of this one\n * constant: a single named threshold, no second magic number.\n *\n * The value (1.6) is picked to sit *above* the near-square auto-modes so they\n * stay `square` and take the unchanged single-column path, but well below the\n * 3.6 landscape extreme so that genuinely wide boxes reflow:\n *\n * - `default-5x7` → aspect 0.71 → square (between 0.625 and 1.6)\n * - `mobile-9x8` → aspect 1.125 → square (between 0.625 and 1.6)\n * - `landscape-18x5` → aspect 3.6 → landscape (≥ 1.6)\n * - `portrait-5x18` → aspect 0.28 → portrait (≤ 0.625 = 1/1.6)\n *\n * Boundaries are inclusive of the cutoff (`>=` / `<=`) so a box sitting exactly\n * on the threshold commits to the reflow rather than dithering at square.\n */\nconst LANDSCAPE_ASPECT = 1.6;\nconst PORTRAIT_ASPECT = 1 / LANDSCAPE_ASPECT; // ≈ 0.625\n\n/**\n * The {@link WidgetShape} of a `w` by `h` tile, with its aspect ratio.\n * `landscape` from an aspect of 1.6 up, `portrait` at 1/1.6 (0.625) and below,\n * `square` between. Returns `square` with aspect `1` while either dimension is\n * `undefined` or `h` is 0.\n *\n * @category Layout\n */\nexport function getWidgetShape(\n w: number | undefined,\n h: number | undefined,\n): WidgetShapeInfo {\n // Missing dims (e.g. before the grid has measured), assume `square`, the\n // neutral no-reflow default. The size-bucket analogue is `normal`: pick the\n // value that doesn't flash a divergent layout on first paint. Returning\n // `landscape` here would briefly show a multi-column layout before measure.\n if (w === undefined || h === undefined || h === 0) {\n return { shape: \"square\", aspect: 1 };\n }\n const aspect = w / h;\n if (aspect >= LANDSCAPE_ASPECT) return { shape: \"landscape\", aspect };\n if (aspect <= PORTRAIT_ASPECT) return { shape: \"portrait\", aspect };\n return { shape: \"square\", aspect };\n}\n","import type {\n ComponentDefinition,\n ComponentProps,\n TinyEssential,\n TinyGauge,\n TinyMode,\n Tone,\n Value,\n} from \"@ksp-gonogo/sitrep-sdk\";\nimport { Fragment, useLayoutEffect, useRef, useState } from \"react\";\nimport styled from \"styled-components\";\nimport { fillFraction } from \"./fillQuantity\";\nimport { HeldFigure } from \"./HeldMark\";\nimport { LevelBars } from \"./LevelBars\";\nimport { LiveRegion } from \"./LiveRegion\";\nimport { Panel } from \"./Panel\";\nimport { resolveCurrency } from \"./readingCurrency\";\nimport { Section } from \"./Section\";\nimport { ToggleButton } from \"./ToggleButton\";\nimport { Tooltip } from \"./Tooltip\";\nimport { TONE_MARK, TONE_TEXT } from \"./tone\";\nimport { Unit } from \"./Unit\";\nimport { VisuallyHidden } from \"./VisuallyHidden\";\nimport { TINY_BELOW, TINY_SIZE } from \"./widgetSize\";\n\n/**\n * Props for {@link TinyEssentials}: a widget's tiny heading and its essentials.\n *\n * @category TinyEssentials\n */\nexport interface TinyEssentialsProps {\n title: string;\n essentials: readonly TinyEssential[];\n}\n\n/**\n * The form every widget with a tiny mode takes at the tiny size, drawn in a\n * {@link Panel} with `hoverTitle` and `fitToSize`: its short heading over its\n * essential values, the first drawn large and the rest as\n * label and figure rows under it. A state word stands in a figure's place and\n * is announced through the tile's one polite live region, or, while its\n * essential declares it urgent, through an assertive one that interrupts.\n *\n * Rows are drawn in order while they fit the tile and the ones that do not\n * are dropped from the end, so every figure the tile shows is whole.\n *\n * @category TinyEssentials\n */\nexport function TinyEssentials({ title, essentials }: TinyEssentialsProps) {\n const [hero, ...rest] = essentials;\n const { rowsRef, drawn } = useRowsThatFit(rest);\n const mayInterrupt = essentials.some((e) => e.urgent !== undefined);\n // A lone figure named like the tile already has its caption in the heading.\n const heroNamedByTitle =\n hero !== undefined &&\n rest.length === 0 &&\n hero.label.toUpperCase() === title.toUpperCase();\n return (\n <Panel\n panelTitle={title}\n fitToSize\n hoverTitle\n sections={\n <Section full>\n <LiveRegion visuallyHidden>\n {spokenWords(essentials.filter((e) => e.urgent !== true))}\n </LiveRegion>\n {mayInterrupt && (\n <LiveRegion visuallyHidden assertive additionsOnly>\n {urgentWords(essentials).map((said) => (\n <Fragment key={said}>{`${said}. `}</Fragment>\n ))}\n </LiveRegion>\n )}\n {hero !== undefined && (\n <TinyEssentials__Hero data-tiny-essential=\"\">\n {heroNamedByTitle ? (\n <VisuallyHidden as=\"dt\">{hero.label}</VisuallyHidden>\n ) : (\n <TinyEssentials__Label>{hero.label}</TinyEssentials__Label>\n )}\n {hero.gauge === undefined ? (\n <TinyEssentials__HeroFigure $tone={hero.tone ?? \"neutral\"}>\n <EssentialFigure essential={hero} />\n </TinyEssentials__HeroFigure>\n ) : (\n <TinyEssentials__GaugedFigure $tone={hero.tone ?? \"neutral\"}>\n <TinyEssentials__FigureLine>\n <EssentialFigure essential={hero} />\n </TinyEssentials__FigureLine>\n <CompactGauge essential={hero} gauge={hero.gauge} />\n </TinyEssentials__GaugedFigure>\n )}\n </TinyEssentials__Hero>\n )}\n {rest.length > 0 && (\n <TinyEssentials__Rows ref={rowsRef}>\n {rest.slice(0, drawn).map((essential) => (\n <TinyEssentials__Row\n key={essential.label}\n data-tiny-essential=\"\"\n >\n <TinyEssentials__Label>\n {essential.label}\n </TinyEssentials__Label>\n <TinyEssentials__RowFigure\n $tone={essential.tone ?? \"neutral\"}\n >\n <EssentialFigure essential={essential} />\n </TinyEssentials__RowFigure>\n </TinyEssentials__Row>\n ))}\n </TinyEssentials__Rows>\n )}\n </Section>\n }\n />\n );\n}\n\n/**\n * How many of the rows are drawn: every one that fits the fit body's box under\n * the hero, in order. Starts from all of them whenever the box resizes or a\n * figure changes, and drops one per layout pass until the content fits, so a\n * tile that grows gets its rows back.\n */\nfunction useRowsThatFit(rows: readonly TinyEssential[]) {\n const rowsRef = useRef<HTMLDListElement | null>(null);\n const [drawn, setDrawn] = useState(rows.length);\n const [pass, setPass] = useState(0);\n const figures = rows\n .map((row) => `${row.label}\\u0000${row.word ?? figureKey(row.value)}`)\n .join(\"\\u0001\");\n\n // biome-ignore lint/correctness/useExhaustiveDependencies: a new figure or a resized box starts the count again.\n useLayoutEffect(() => {\n setDrawn(rows.length);\n }, [figures, pass, rows.length]);\n\n useLayoutEffect(() => {\n const box = rowsRef.current?.closest(\"[data-panel-fit-body]\");\n const content = box?.firstElementChild;\n if (!box || !content || drawn === 0) return;\n if (extentOf(content) > box.clientHeight) setDrawn(drawn - 1);\n });\n\n useLayoutEffect(() => {\n const box = rowsRef.current?.closest(\"[data-panel-fit-body]\");\n if (!box || typeof ResizeObserver === \"undefined\") return;\n let seen = box.clientHeight;\n const observer = new ResizeObserver(() => {\n if (box.clientHeight === seen) return;\n seen = box.clientHeight;\n setPass((n) => n + 1);\n });\n observer.observe(box);\n return () => observer.disconnect();\n }, []);\n\n return { rowsRef, drawn };\n}\n\n/** Enough of a figure to tell when it changed, whatever shape it came in. */\nfunction figureKey(value: TinyEssential[\"value\"]): string {\n try {\n return JSON.stringify(value) ?? \"\";\n } catch {\n return String(value);\n }\n}\n\n/** From the top of the content's first drawn child to the bottom of its last, descending into the section it is wrapped in. */\nfunction extentOf(content: Element): number {\n const tops: number[] = [];\n const bottoms: number[] = [];\n for (const el of Array.from(\n content.querySelectorAll(\"[data-tiny-essential]\"),\n )) {\n const r = el.getBoundingClientRect();\n tops.push(r.top);\n bottoms.push(r.bottom);\n }\n if (tops.length === 0) return 0;\n return Math.max(...bottoms) - Math.min(...tops);\n}\n\nfunction EssentialFigure({ essential }: { essential: TinyEssential }) {\n const { control } = essential;\n if (control !== undefined) {\n return (\n <Tooltip text={control.hint ?? control.title}>\n <ToggleButton\n size=\"sm\"\n active={control.active}\n disabled={control.disabled}\n onClick={control.onPress}\n aria-label={control.title}\n >\n {control.label}\n </ToggleButton>\n </Tooltip>\n );\n }\n return (\n <>\n {essential.level !== undefined && (\n <LevelBars\n lit={essential.level.lit}\n of={essential.level.of}\n tone={essential.tone}\n label={essential.label}\n />\n )}\n {essential.word !== undefined ? (\n essential.word\n ) : (\n <MarkedFigure essential={essential} />\n )}\n </>\n );\n}\n\n/**\n * The figure, with the mark the widget states for it. The Reading goes to\n * `<Unit>` whole, so a held or modelled one keeps its own kind and words, and\n * `elsewhere` empties whichever mark that gives. A kind the widget states for\n * a reading of now is drawn around the figure.\n */\nfunction MarkedFigure({ essential }: { essential: TinyEssential }) {\n const { mark, value, decimals } = essential;\n const { held } = resolveCurrency(value);\n const figure = (\n <Unit\n value={value}\n decimals={decimals}\n scale=\"compact\"\n elsewhere={\n mark?.elsewhere === true && (held || mark.kind === undefined)\n ? mark.caption\n : undefined\n }\n />\n );\n if (mark === undefined || held || mark.kind === undefined) return figure;\n return (\n <HeldFigure\n kind={mark.kind}\n elsewhere={mark.elsewhere}\n caption={mark.caption}\n >\n {figure}\n </HeldFigure>\n );\n}\n\n/**\n * A thin bar under the hero figure: the scale's bands as a dim track, and a\n * fill over it from the foot of the scale to the value. The figure carries the\n * number, so the drawing is hidden from the accessibility tree. A held\n * reading dims the fill, as a held meter does; no value draws the bands\n * alone.\n */\nfunction CompactGauge({\n essential,\n gauge,\n}: {\n essential: TinyEssential;\n gauge: TinyGauge;\n}) {\n const { shown, held } = resolveCurrency(essential.value);\n // Clamped in the algebra, so a bound on another rung of the same kind converts before it compares.\n const at = (q: Value): number =>\n fillFraction({\n amount: q.max(gauge.min).min(gauge.max).minus(gauge.min),\n capacity: gauge.max.minus(gauge.min),\n }) ?? 0;\n const share = shown == null ? null : at(shown);\n return (\n <TinyEssentials__Gauge\n aria-hidden=\"true\"\n data-tiny-gauge=\"\"\n data-held={held ? \"\" : undefined}\n viewBox=\"0 0 100 1\"\n preserveAspectRatio=\"none\"\n >\n {(gauge.bands ?? []).map((band) => (\n <rect\n key={`${at(band.from)}-${at(band.to)}`}\n x={at(band.from) * 100}\n y={0}\n width={(at(band.to) - at(band.from)) * 100}\n height={1}\n fill={TONE_MARK[band.tone]}\n opacity={0.35}\n />\n ))}\n {share !== null && (\n <rect\n data-tiny-gauge-fill=\"\"\n x={0}\n y={0}\n width={share * 100}\n height={1}\n fill={TONE_MARK[essential.tone ?? \"neutral\"]}\n opacity={held ? 0.55 : 1}\n />\n )}\n </TinyEssentials__Gauge>\n );\n}\n\n/** Every state word on the tile with its label, the only thing the tile says aloud. */\nfunction spokenWords(essentials: readonly TinyEssential[]): string {\n return essentials\n .filter((e) => e.word !== undefined)\n .map((e) => `${e.label} ${e.word}`)\n .join(\", \");\n}\n\n/**\n * Each urgent word with its label, one text node apiece and no element, so the\n * region is as hidden as the polite one. The assertive region reads only what\n * is added to it, so a word is said when it turns urgent or changes while\n * urgent, and an unchanged one is not said again on the next render.\n */\nfunction urgentWords(essentials: readonly TinyEssential[]): string[] {\n return essentials\n .filter((e) => e.urgent === true && e.word !== undefined)\n .map((e) => `${e.label} ${e.word}`);\n}\n\n/**\n * A registered widget's content at its current size: the kit's tiny form where\n * the widget declares one and the tile is tiny, its own component otherwise.\n * Mount a widget through this to see what the dashboard grid shows at a given\n * size. The phone layout decides by the phone's width instead of grid units.\n *\n * @category TinyEssentials\n */\nexport function WidgetBody({\n def,\n ...props\n}: ComponentProps & { def: ComponentDefinition }) {\n if (def.tiny !== undefined && showsTiny(def, props.w, props.h)) {\n return <TinyBody tiny={def.tiny} props={props} />;\n }\n const Widget = def.component;\n return <Widget {...props} />;\n}\n\n/**\n * Whether a widget shows its tiny form in a `w` by `h` tile (in grid units):\n * true for a widget with a tiny mode when the tile is narrower or shorter than\n * its `minSize`, where its own body stops fitting, or than {@link TINY_BELOW}\n * when it sets none. False for a widget without a tiny mode, and while either\n * dimension is unmeasured, so the widget's own body shows.\n *\n * @category TinyEssentials\n */\nexport function showsTiny(\n def: Pick<ComponentDefinition, \"tiny\" | \"minSize\">,\n w: number | undefined,\n h: number | undefined,\n): boolean {\n if (def.tiny === undefined || w === undefined || h === undefined) {\n return false;\n }\n const floor = def.minSize ?? TINY_BELOW;\n return w < floor.w || h < floor.h;\n}\n\n/**\n * The smallest tile, in grid units, the dashboard lets a widget be resized to:\n * the kit-wide {@link TINY_SIZE} for a widget with a tiny mode, its `minSize`\n * for one without, and `undefined` when that sets none.\n *\n * @category TinyEssentials\n */\nexport function gridFloor(\n def: Pick<ComponentDefinition, \"tiny\" | \"minSize\">,\n): { w: number; h: number } | undefined {\n return def.tiny !== undefined ? TINY_SIZE : def.minSize;\n}\n\nfunction TinyBody({ tiny, props }: { tiny: TinyMode; props: ComponentProps }) {\n const essentials = tiny.useEssentials(props);\n return <TinyEssentials title={tiny.title} essentials={essentials} />;\n}\n\n/* The label comes first for the reader and sits under the figure for the eye. */\nconst TinyEssentials__Hero = styled.dl`\n display: flex;\n flex-direction: column-reverse;\n align-items: center;\n gap: var(--gap-tiny-content);\n margin: 0;\n min-width: 0;\n`;\n\nconst TinyEssentials__HeroFigure = styled.dd<{ $tone: Tone }>`\n display: inline-flex;\n align-items: baseline;\n gap: var(--gap-figure-parts);\n margin: 0;\n /* Sized against the tile, which the fit body makes a query container. */\n font-size: clamp(\n var(--font-size-lg),\n 20cqw,\n calc(var(--font-size-lg) * 2)\n );\n font-weight: 700;\n line-height: var(--line-height-tight);\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n color: ${({ $tone }) => TONE_TEXT[$tone]};\n`;\n\n/* Flush, so the figure and the bar under it fit the tile's content box, which is shorter than one tight figure line plus a gap. */\nconst TinyEssentials__GaugedFigure = styled(TinyEssentials__HeroFigure)`\n flex-direction: column;\n align-items: stretch;\n gap: var(--gap-caption);\n line-height: var(--line-height-flush);\n`;\n\nconst TinyEssentials__FigureLine = styled.span`\n display: inline-flex;\n align-items: baseline;\n justify-content: center;\n gap: var(--gap-figure-parts);\n`;\n\nconst TinyEssentials__Gauge = styled.svg`\n display: block;\n width: 100%;\n height: var(--size-tiny-gauge);\n`;\n\nconst TinyEssentials__Label = styled.dt`\n font-size: var(--font-size-caption);\n letter-spacing: 0.1em;\n text-transform: uppercase;\n color: var(--color-text-muted);\n white-space: nowrap;\n`;\n\nconst TinyEssentials__Rows = styled.dl`\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: var(--gap-row-wrap);\n margin: 0;\n max-width: 100%;\n`;\n\n/* A row too wide for the tile drops its figure under its label rather than spilling past the edges. */\nconst TinyEssentials__Row = styled.div`\n display: flex;\n flex-wrap: wrap;\n justify-content: center;\n align-items: baseline;\n column-gap: var(--gap-label-value-tight);\n max-width: 100%;\n`;\n\nconst TinyEssentials__RowFigure = styled.dd<{ $tone: Tone }>`\n display: inline-flex;\n align-items: baseline;\n gap: var(--gap-figure-parts);\n margin: 0;\n font-size: var(--font-size-value);\n font-weight: 700;\n font-variant-numeric: tabular-nums;\n white-space: nowrap;\n color: ${({ $tone }) => TONE_TEXT[$tone]};\n`;\n","import type { Value } from \"@ksp-gonogo/sitrep-sdk\";\n\n/**\n * How much there is, and what that is a fraction of, for {@link TinyEssentials}.\n * Both halves are the same kind of quantity, which the types enforce, and the\n * kit divides them itself.\n *\n * @category Unit\n */\nexport interface FillQuantity<Unit extends string = string> {\n /** How much there is now. */\n amount: Value<Unit>;\n /** The full tank: what `amount` is read as a fraction of. */\n capacity: Value<Unit>;\n}\n\n/**\n * The pair, as the 0..1 a track is drawn from. `dividedBy` makes the quotient\n * dimensionless. A capacity of zero is no tank at all, so it yields `null`.\n *\n * @category Unit\n */\nexport function fillFraction<Unit extends string>(\n pair: FillQuantity<Unit> | null,\n): number | null {\n if (pair === null) return null;\n if (!pair.capacity.isPositive()) return null;\n return pair.amount.dividedBy(pair.capacity).magnitude;\n}\n","import type { BadgeEntry, Contributed } from \"@ksp-gonogo/sitrep-sdk\";\nimport { useContributionsBySlotId } from \"./contributionsRead\";\nimport { useWidgetMeta } from \"./WidgetMetaContext\";\n\n/**\n * The entries contributed to the current widget's standard\n * `${componentId}.badges` slot, each stamped with its contribution id and\n * owner. Empty outside a widget or without a {@link ContributionsProvider}.\n * The dashboard reads this for each widget and hands it to\n * {@link PanelBadgesProvider}; a widget drawn in a {@link Panel} gets the\n * badges without calling it.\n *\n * @category Panel\n */\nexport function useWidgetBadges(): readonly Contributed<BadgeEntry>[] {\n const meta = useWidgetMeta();\n const slot = meta ? `${meta.componentId}.badges` : \"\";\n return useContributionsBySlotId(slot) as readonly Contributed<BadgeEntry>[];\n}\n"],"mappings":"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","names":["css","Button","css","styled","css","styled","css","styled","jsx","styled","jsx","value","createContext","useContext","jsx","styled","css","jsx","value","css","styled","jsx","HOUR","MINUTE","tiers","STANDARD_GRAVITY","value","quantity","format","decimals","styled","jsx","quantity","jsx","styled","jsx","jsxs","styled","createContext","useContext","useSyncExternalStore","Fragment","jsx","useSyncExternalStore","NO_SUBSCRIBE","styled","css","css","jsx","css","SIZE_STYLES","styled","useEffect","useRef","styled","Fragment","jsx","jsxs","styled","styled","css","jsx","jsxs","css","styled","createContext","useContext","forwardRef","createElement","forwardRef","createElement","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","__iconNode","forwardRef","jsx","value","said","quantity","createContext","useContext","useState","useSyncExternalStore","jsx","format","createContext","useContext","useState","value","useSyncExternalStore","styled","css","Fragment","jsx","jsxs","styled","css","quantity","said","value","forwardRef","styled","jsx","forwardRef","Cluster","styled","value","commandRefusalSubject","commandRefusalSubject","said","sentence","commandRefusalSubject","createElement","jsx","createElement","jsx","hasHost","logger","hasHost","logger","useId","styled","Fragment","jsx","jsxs","value","useId","styled","styled","Fragment","jsx","jsxs","styled","quantity","value","Fragment","jsx","jsxs","value","format","useEffect","useRef","useState","words","value","useEffect","useId","useRef","useState","styled","css","Fragment","jsx","jsxs","value","useState","useRef","useEffect","useId","InFlightBadge","styled","css","jsx","commandRefusalSubject","describeErrorCode","said","commandRefusalSubject","commandRefusalSubject","styled","css","jsx","jsxs","commandRefusalSubject","css","styled","useSyncExternalStore","jsx","NO_SUBSCRIBE","useSyncExternalStore","useCallback","useSyncExternalStore","useCallback","useSyncExternalStore","hasHost","logger","useCallback","useEffect","useMemo","useRef","useSyncExternalStore","Fragment","jsx","jsxs","slotCache","cacheValid","useSyncExternalStore","useMemo","useEffect","useCallback","useRef","hasHost","logger","styled","jsx","jsxs","styled","useRef","styled","jsx","jsxs","value","useRef","styled","useId","useState","jsx","jsxs","useState","useId","styled","Fragment","jsx","jsxs","styled","css","jsx","css","styled","styled","jsx","styled","createContext","useContext","jsx","useSyncExternalStore","NO_SUBSCRIBE","useSyncExternalStore","useSyncExternalStore","NO_SUBSCRIBE","useSyncExternalStore","createContext","useCallback","useContext","useEffect","useLayoutEffect","useRef","useState","value","createContext","Fragment","forwardRef","useCallback","useContext","useEffect","useId","useLayoutEffect","useMemo","useRef","useState","styled","css","useId","useState","styled","css","jsx","jsxs","useState","useId","styled","css","styled","jsx","styled","css","useCallback","useEffect","useLayoutEffect","useRef","useState","compact","useEffect","useRef","useState","value","Fragment","jsx","jsxs","createContext","useState","value","useMemo","styled","forwardRef","PanelTitle","compact","useRef","css","useEffect","useContext","useCallback","useLayoutEffect","ScrollArea","useId","child","forwardRef","jsx","forwardRef","ToggleButton","Fragment","useLayoutEffect","useRef","useState","styled","Fragment","jsx","jsxs","said","useRef","useState","useLayoutEffect","value","styled"]}
|