@cosmicdrift/kumiko-renderer 0.386.0 → 0.387.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/LICENSE +21 -57
  2. package/dist/app/action-form-body.d.ts +2 -2
  3. package/dist/app/action-form-body.js +2 -2
  4. package/dist/app/action-form-shim.d.ts +8 -8
  5. package/dist/app/action-form-shim.js +20 -22
  6. package/dist/app/app-features-context.js +4 -4
  7. package/dist/app/column-renderers.d.ts +7 -9
  8. package/dist/app/column-renderers.js +15 -18
  9. package/dist/app/config-edit-body.js +1 -1
  10. package/dist/app/config-edit-shim.d.ts +7 -8
  11. package/dist/app/config-edit-shim.js +18 -20
  12. package/dist/app/custom-screens.d.ts +3 -3
  13. package/dist/app/custom-screens.js +8 -8
  14. package/dist/app/dashboard-body.d.ts +2 -2
  15. package/dist/app/dashboard-body.js +2 -2
  16. package/dist/app/entity-edit-prefill.js +9 -9
  17. package/dist/app/entity-edit-screen.js +4 -4
  18. package/dist/app/entity-list-screen.js +3 -3
  19. package/dist/app/extension-form-submit.d.ts +1 -1
  20. package/dist/app/extension-form-submit.js +14 -15
  21. package/dist/app/extension-sections.d.ts +24 -25
  22. package/dist/app/extension-sections.js +10 -10
  23. package/dist/app/feature-schema.js +11 -11
  24. package/dist/app/form-schema.js +1 -1
  25. package/dist/app/header-actions-bar.js +1 -1
  26. package/dist/app/kumiko-screen.js +8 -10
  27. package/dist/app/layout-fields.js +1 -1
  28. package/dist/app/list-facets.js +3 -3
  29. package/dist/app/nav.d.ts +10 -10
  30. package/dist/app/nav.js +13 -13
  31. package/dist/app/projection-detail-body.js +4 -4
  32. package/dist/app/projection-detail-shim.d.ts +6 -5
  33. package/dist/app/projection-detail-shim.js +23 -22
  34. package/dist/app/projection-list-body.js +10 -10
  35. package/dist/app/projection-list-shim.d.ts +5 -5
  36. package/dist/app/projection-list-shim.js +17 -18
  37. package/dist/app/qn.js +10 -12
  38. package/dist/app/qualify-screen-id.js +1 -1
  39. package/dist/app/screen-access.js +2 -2
  40. package/dist/app/screen-shared.js +1 -1
  41. package/dist/app/secret-mint-body.js +4 -4
  42. package/dist/app/write-failed-error.js +8 -7
  43. package/dist/components/embedded-list-field.js +4 -4
  44. package/dist/components/grid-cell-for-field.d.ts +1 -1
  45. package/dist/components/reference-create-dialog.js +3 -3
  46. package/dist/components/related-list-section.d.ts +1 -1
  47. package/dist/components/related-list-section.js +1 -1
  48. package/dist/components/render-edit-logic.js +1 -1
  49. package/dist/components/render-edit-types.d.ts +6 -6
  50. package/dist/components/render-edit.js +63 -65
  51. package/dist/components/render-field.d.ts +7 -7
  52. package/dist/components/render-field.js +57 -59
  53. package/dist/components/render-list.d.ts +42 -43
  54. package/dist/components/render-list.js +63 -67
  55. package/dist/components/use-wizard-form-draft.js +10 -10
  56. package/dist/context/dispatcher-context.js +5 -5
  57. package/dist/context/user-roles-context.js +2 -2
  58. package/dist/headless/apex/index.d.ts +1 -2
  59. package/dist/headless/apex/index.js +5 -6
  60. package/dist/headless/form/form-controller.js +6 -6
  61. package/dist/headless/form/zod-bridge.js +2 -2
  62. package/dist/headless/format/money.js +1 -1
  63. package/dist/headless/index.js +2 -2
  64. package/dist/headless/view-model/edit.js +2 -2
  65. package/dist/headless/view-model/list.js +3 -4
  66. package/dist/headless/view-model/types.d.ts +3 -3
  67. package/dist/hooks/reference-limits.js +11 -12
  68. package/dist/hooks/use-list-url-state.d.ts +13 -13
  69. package/dist/hooks/use-list-url-state.js +21 -22
  70. package/dist/hooks/use-query.js +3 -3
  71. package/dist/hooks/use-reference-lookup.js +16 -17
  72. package/dist/hooks/use-store.js +18 -22
  73. package/dist/hooks/use-stream-handler.js +4 -2
  74. package/dist/i18n-defaults.js +2 -8
  75. package/dist/i18n.d.ts +10 -10
  76. package/dist/i18n.js +25 -25
  77. package/dist/index.js +5 -4
  78. package/dist/live/dispatcher-live.js +2 -2
  79. package/dist/primitives.d.ts +225 -221
  80. package/dist/primitives.js +1 -1
  81. package/dist/sse/live-events.d.ts +5 -5
  82. package/dist/sse/live-events.js +17 -17
  83. package/dist/tokens.d.ts +8 -8
  84. package/dist/tokens.js +16 -17
  85. package/dist/web/app/browser-locale.d.ts +9 -9
  86. package/dist/web/app/browser-locale.js +19 -19
  87. package/dist/web/app/client-plugin.d.ts +44 -52
  88. package/dist/web/app/client-plugin.js +4 -5
  89. package/dist/web/app/create-app.d.ts +28 -31
  90. package/dist/web/app/create-app.js +21 -22
  91. package/dist/web/app/document-lang-sync.js +1 -1
  92. package/dist/web/app/draft-storage.js +1 -1
  93. package/dist/web/app/nav-providers-context.d.ts +3 -3
  94. package/dist/web/app/nav-providers-context.js +3 -3
  95. package/dist/web/app/nav.d.ts +18 -17
  96. package/dist/web/app/nav.js +66 -64
  97. package/dist/web/app/rich-content-editor.js +1 -1
  98. package/dist/web/components/config-cascade.js +17 -18
  99. package/dist/web/components/config-source-badge.js +4 -4
  100. package/dist/web/layout/app-layout.d.ts +9 -9
  101. package/dist/web/layout/app-layout.js +2 -2
  102. package/dist/web/layout/avatar.d.ts +4 -3
  103. package/dist/web/layout/avatar.js +15 -17
  104. package/dist/web/layout/default-app-shell.d.ts +22 -23
  105. package/dist/web/layout/editor-panel.js +3 -3
  106. package/dist/web/layout/nav-tree.js +86 -84
  107. package/dist/web/layout/profile-menu.d.ts +1 -1
  108. package/dist/web/layout/sidebar-brand.d.ts +7 -7
  109. package/dist/web/layout/sidebar-user.d.ts +1 -1
  110. package/dist/web/layout/sidebar-user.js +4 -4
  111. package/dist/web/layout/sidebar.d.ts +8 -8
  112. package/dist/web/layout/sidebar.js +7 -7
  113. package/dist/web/layout/target-resolver-stub.d.ts +5 -5
  114. package/dist/web/layout/target-resolver-stub.js +20 -20
  115. package/dist/web/layout/target-url.d.ts +7 -7
  116. package/dist/web/layout/target-url.js +22 -22
  117. package/dist/web/layout/theme-toggle.js +4 -5
  118. package/dist/web/layout/workspace-shell.d.ts +21 -21
  119. package/dist/web/layout/workspace-shell.js +29 -30
  120. package/dist/web/lib/cn.d.ts +4 -4
  121. package/dist/web/lib/cn.js +4 -4
  122. package/dist/web/primitives/action-menu.d.ts +8 -8
  123. package/dist/web/primitives/action-menu.js +14 -13
  124. package/dist/web/primitives/calendar-popover.d.ts +1 -1
  125. package/dist/web/primitives/calendar-popover.js +10 -10
  126. package/dist/web/primitives/card-styles.js +1 -1
  127. package/dist/web/primitives/card.js +2 -2
  128. package/dist/web/primitives/combobox.d.ts +12 -12
  129. package/dist/web/primitives/combobox.js +34 -35
  130. package/dist/web/primitives/data-table-cell.js +2 -2
  131. package/dist/web/primitives/data-table.js +2 -3
  132. package/dist/web/primitives/date-field.js +2 -2
  133. package/dist/web/primitives/date-input.js +4 -4
  134. package/dist/web/primitives/default-form.js +2 -2
  135. package/dist/web/primitives/dropdown-menu.js +14 -15
  136. package/dist/web/primitives/embedded-list-input.js +3 -3
  137. package/dist/web/primitives/facet-filter.js +1 -1
  138. package/dist/web/primitives/field.js +9 -8
  139. package/dist/web/primitives/form.d.ts +1 -1
  140. package/dist/web/primitives/form.js +6 -6
  141. package/dist/web/primitives/input.js +2 -2
  142. package/dist/web/primitives/layout-elements.js +1 -1
  143. package/dist/web/primitives/layout.js +2 -2
  144. package/dist/web/primitives/located-timestamp-input.js +4 -4
  145. package/dist/web/primitives/money-input.js +13 -17
  146. package/dist/web/primitives/pager.js +1 -1
  147. package/dist/web/primitives/row-actions.js +3 -4
  148. package/dist/web/primitives/secret-reveal.js +1 -1
  149. package/dist/web/primitives/timestamp-input.d.ts +9 -9
  150. package/dist/web/primitives/timestamp-input.js +21 -21
  151. package/dist/web/primitives/toast.js +12 -13
  152. package/dist/web/sse/live-events.d.ts +6 -6
  153. package/dist/web/sse/live-events.js +3 -3
  154. package/dist/web/tokens.d.ts +11 -12
  155. package/dist/web/tokens.js +35 -37
  156. package/dist/web/version/update-checker.js +14 -13
  157. package/dist/web/widgets/ai-text-field.js +1 -1
  158. package/dist/web/widgets/charts.d.ts +10 -10
  159. package/dist/web/widgets/charts.js +8 -8
  160. package/dist/web/widgets/collapsible-section.d.ts +3 -3
  161. package/dist/web/widgets/collapsible-section.js +6 -6
  162. package/dist/web/widgets/detail-list.d.ts +1 -3
  163. package/dist/web/widgets/detail-list.js +1 -3
  164. package/dist/web/widgets/feed-list.d.ts +2 -2
  165. package/dist/web/widgets/feed-list.js +2 -2
  166. package/dist/web/widgets/form-fields.d.ts +14 -14
  167. package/dist/web/widgets/form-fields.js +14 -13
  168. package/dist/web/widgets/index.js +4 -4
  169. package/dist/web/widgets/infinity-list.js +3 -3
  170. package/dist/web/widgets/mode-switch.d.ts +2 -2
  171. package/dist/web/widgets/mode-switch.js +2 -2
  172. package/dist/web/widgets/progress-list.d.ts +1 -2
  173. package/dist/web/widgets/progress-list.js +1 -2
  174. package/dist/web/widgets/query-table.d.ts +6 -6
  175. package/dist/web/widgets/result-panel.d.ts +16 -16
  176. package/dist/web/widgets/result-panel.js +10 -10
  177. package/dist/web/widgets/section-card.d.ts +3 -3
  178. package/dist/web/widgets/section-card.js +3 -3
  179. package/dist/web/widgets/stat.d.ts +4 -4
  180. package/dist/web/widgets/stat.js +6 -6
  181. package/dist/web/widgets/states.d.ts +4 -4
  182. package/dist/web/widgets/states.js +4 -4
  183. package/dist/web/widgets/use-draft.d.ts +7 -7
  184. package/dist/web/widgets/use-draft.js +7 -7
  185. package/package.json +6 -6
@@ -10,9 +10,8 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigge
10
10
  import { useToast } from "./toast.js";
11
11
  import "../react-css-custom-properties.js";
12
12
  import { actionIconFor, SECONDARY_BUTTON_EXTRA_CLASSES } from "./button.js";
13
- // The primary row action is the one that always stays a visible text
14
- // button — `edit` if declared, else the first visible action (fw
15
- // bedienkonzept L3: "Bearbeiten steht in jeder Zeile, immer, als Text").
13
+ // The primary row action always remains a visible text button: `edit` when declared,
14
+ // otherwise the first visible action ("Edit appears in every row, always, as text").
16
15
  function primaryRowAction(actions) {
17
16
  return actions.find((a) => a.id === "edit") ?? actions[0];
18
17
  }
@@ -161,7 +160,7 @@ function RowActionsKebab({ row, actions, }) {
161
160
  await triggerNow(action);
162
161
  }, testId: `row-${row.id}-action-${pendingConfirm.id}-dialog` }))] }));
163
162
  }
164
- // Header/row-actions overflow menu (A7): the trigger carries the classes of a
163
+ // Header/row-actions overflow menu: the trigger carries the classes of a
165
164
  // secondary icon Button so it matches neighbouring icon buttons, including
166
165
  // the 44 px touch size on phones. Generic ActionMenuItemSpec items instead of
167
166
  // the DataTableRowAction schema (callers own confirm/danger handling per item).
@@ -42,7 +42,7 @@ function QrSecretValue({ value }) {
42
42
  // biome-ignore lint/security/noDangerouslySetInnerHtml: qrcode-generated SVG, no user input
43
43
  dangerouslySetInnerHTML: { __html: svg } }));
44
44
  }
45
- // One-time secret reveal (fw#2548, secretMint confirm phase) — monospaced
45
+ // One-time secret reveal (secretMint confirm phase) — monospaced
46
46
  // per-value display with a copy button that flips to `copiedLabel` on
47
47
  // success. Silent-catch on clipboard error mirrors the copy-link pattern in
48
48
  // create-app.tsx.
@@ -1,11 +1,11 @@
1
1
  import { type ReactNode } from "react";
2
- /** Form-State → datetime-local-Format (`yyyy-MM-ddTHH:mm`).
3
- * UTC-Instants (mit `Z`/Offset) werden in lokale Wall-Clock
4
- * umgerechnet; offset-lose Werte nur auf Minuten gekürzt. */
2
+ /** Form state → datetime-local format (`yyyy-MM-ddTHH:mm`).
3
+ * UTC instants (with `Z`/offset) are converted to the local wall clock;
4
+ * offset-free values are only truncated to minutes. */
5
5
  export declare function timestampToInputValue(value: string): string;
6
- /** datetime-local-Wert → Wire-Format. wallClock=true reicht die
7
- * Wall-Clock unverändert durch; sonst wird die lokale Zeit als
8
- * UTC-Instant mit `Z`-Suffix emittiert. */
6
+ /** datetime-local value → wire format. wallClock=true passes the wall clock
7
+ * through unchanged; otherwise the local time is emitted as a UTC instant
8
+ * with a `Z` suffix. */
9
9
  export declare function inputValueToTimestamp(raw: string, wallClock: boolean): string | undefined;
10
10
  export type TimestampInputProps = {
11
11
  readonly id: string;
@@ -18,9 +18,9 @@ export type TimestampInputProps = {
18
18
  readonly hasError?: boolean;
19
19
  readonly ariaLabel?: string;
20
20
  readonly locale?: string;
21
- /** Untere/obere Grenze als ISO-Datetime. Begrenzt den Kalender auf
22
- * Tages-Granularität; die exakte Uhrzeit-Grenze setzt die Zod-Validierung
23
- * durch. */
21
+ /** Lower and upper bounds as ISO datetimes. The calendar is constrained to
22
+ * day-level granularity; Zod validation enforces the exact time bounds.
23
+ */
24
24
  readonly min?: string;
25
25
  readonly max?: string;
26
26
  };
@@ -1,15 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // TimestampInput (kind:"timestamp") — gemeinsame Datums-Eingabe (DateField,
3
- // Tippen + Jahres-Kalender) plus ein Uhrzeit-Input. Bis #369 war dies ein
4
- // natives <input type="datetime-local"> — tippbar, aber mit nicht-stylebarem
5
- // Browser-Picker ohne Jahres-Navigation und divergent zu DateInput.
2
+ // TimestampInput (kind:"timestamp") — shared date input (DateField,
3
+ // typing + year calendar) plus a time input. This replaced the native
4
+ // <input type="datetime-local">: it was typeable, but had an unstyleable
5
+ // browser picker without year navigation and diverged from DateInput.
6
6
  //
7
- // Die Wire-Konvertierung bleibt unverändert: der Server validiert timestamp
8
- // als ISO-UTC mit `Z` (z.iso.datetime()) bzw. als Wall-Clock ohne Offset bei
9
- // locatedTimestamps (z.iso.datetime({ local: true })). Intern rechnen wir
10
- // über die datetime-local-Form `yyyy-MM-ddTHH:mm`; timestampToInputValue /
11
- // inputValueToTimestamp kapseln die UTC↔Wall-Clock-Umrechnung (Bug-Bash-2,
12
- // 2026-06-08) und sind separat getestet.
7
+ // Wire conversion remains unchanged: the server validates timestamp values
8
+ // as ISO UTC with `Z` (z.iso.datetime()) or as an offset-free wall clock for
9
+ // locatedTimestamps (z.iso.datetime({ local: true })). Internally, the
10
+ // datetime-local form `yyyy-MM-ddTHH:mm` is used; timestampToInputValue /
11
+ // inputValueToTimestamp encapsulate the UTC↔wall-clock conversion and are
12
+ // tested separately.
13
13
  import { useState } from "react";
14
14
  import { useTranslation } from "../../index.js";
15
15
  import { cn } from "../lib/cn.js";
@@ -19,9 +19,9 @@ const HAS_OFFSET = /(?:Z|[+-]\d{2}:\d{2})$/;
19
19
  function pad(n) {
20
20
  return String(n).padStart(2, "0");
21
21
  }
22
- /** Form-State → datetime-local-Format (`yyyy-MM-ddTHH:mm`).
23
- * UTC-Instants (mit `Z`/Offset) werden in lokale Wall-Clock
24
- * umgerechnet; offset-lose Werte nur auf Minuten gekürzt. */
22
+ /** Form state → datetime-local format (`yyyy-MM-ddTHH:mm`).
23
+ * UTC instants (with `Z`/offset) are converted to the local wall clock;
24
+ * offset-free values are only truncated to minutes. */
25
25
  export function timestampToInputValue(value) {
26
26
  if (value === "")
27
27
  return "";
@@ -34,16 +34,16 @@ export function timestampToInputValue(value) {
34
34
  const m = LOCAL_MINUTES.exec(value);
35
35
  return m !== null ? m[0] : "";
36
36
  }
37
- /** datetime-local-Wert → Wire-Format. wallClock=true reicht die
38
- * Wall-Clock unverändert durch; sonst wird die lokale Zeit als
39
- * UTC-Instant mit `Z`-Suffix emittiert. */
37
+ /** datetime-local value → wire format. wallClock=true passes the wall clock
38
+ * through unchanged; otherwise the local time is emitted as a UTC instant
39
+ * with a `Z` suffix. */
40
40
  export function inputValueToTimestamp(raw, wallClock) {
41
41
  if (raw === "")
42
42
  return undefined;
43
43
  if (wallClock)
44
44
  return raw;
45
- // Offset-loser Datetime-String wird von Date als LOKALE Zeit geparst
46
- // (ES2020+) — genau die Semantik die datetime-local liefert.
45
+ // An offset-free datetime string is parsed by Date as LOCAL time
46
+ // (ES2020+), matching the semantics provided by datetime-local.
47
47
  const d = new Date(raw);
48
48
  if (Number.isNaN(d.getTime()))
49
49
  return undefined;
@@ -64,9 +64,9 @@ export function TimestampInput({ id, name, value, onChange, wallClock, disabled,
64
64
  const t = useTranslation();
65
65
  const local = timestampToInputValue(value);
66
66
  const isoDate = local !== "" ? local.slice(0, 10) : "";
67
- // Uhrzeit ohne gesetztes Datum würde im Wire-Wert verschwinden — lokal
68
- // halten, bis ein Datum gewählt ist. Sobald `value` ein Datum trägt, ist
69
- // dessen Uhrzeit maßgeblich.
67
+ // A time without a selected date would be lost from the wire value, so keep
68
+ // it locally until a date is selected. Once `value` contains a date, its
69
+ // time takes precedence.
70
70
  const [timeText, setTimeText] = useState("");
71
71
  const effectiveTime = local !== "" ? local.slice(11, 16) : timeText;
72
72
  function emit(nextIsoDate, nextTime) {
@@ -1,12 +1,11 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // Toast-Primitive auf @radix-ui/react-toast. Ein globaler Provider
3
- // + ein useToast()-Hook der von überall in der App aus Toasts auslösen
4
- // kann. Kein Context-Boilerplate für Konsumenten — der Hook returned
5
- // einen `toast()`-Trigger, mehr Public-API gibt's nicht.
2
+ // Toast primitive built on @radix-ui/react-toast: a global provider and
3
+ // a useToast() hook for triggering toasts throughout the app. Consumers
4
+ // get only the `toast()` trigger, without Context boilerplate.
6
5
  //
7
- // Variant = StatusTone (dieselbe Farb-Familie wie StatusBadge), Default
8
- // "muted" (neutral). Auto-dismiss nach 5s, manuell schließbar via X-Button.
9
- // Der ARIA-Live-Region-Setup kommt komplett aus Radix.
6
+ // Variant is StatusTone, using the same color family as StatusBadge;
7
+ // the default is "muted" (neutral). Toasts auto-dismiss after 5s and can
8
+ // be closed manually with the X button. Radix provides the ARIA live region.
10
9
  import * as Primitive from "@radix-ui/react-toast";
11
10
  import { X } from "lucide-react";
12
11
  import { createContext, useCallback, useContext, useId, useMemo, useRef, useState, } from "react";
@@ -17,18 +16,18 @@ const ToastContext = createContext(null);
17
16
  export function useToast() {
18
17
  const ctx = useContext(ToastContext);
19
18
  if (ctx === null) {
20
- // Nicht throwen — Tests die einen Component ohne Provider rendern
21
- // sollen nicht crashen. No-op statt fehlerhaftes Mount.
19
+ // Do not throw: tests rendering a component without a provider must not
20
+ // crash. Use a no-op instead of failing the mount.
22
21
  return { toast: () => undefined };
23
22
  }
24
23
  return ctx;
25
24
  }
26
25
  export function ToastProvider({ children }) {
27
26
  const [entries, setEntries] = useState([]);
28
- // Monoton wachsender Counter via useRef — useState hätte einen
29
- // closure-Bug: zwei toast()-Calls im selben Tick lesen denselben
30
- // alten Wert aus dem Closure und kollidieren bei der ID. Ref ist
31
- // synchron-mutabel, jeder Aufruf bekommt eine garantiert neue ID.
27
+ // A monotonically increasing useRef counter avoids a closure bug: two
28
+ // toast() calls in the same tick would read the same stale useState value
29
+ // and collide on the ID. The ref is synchronously mutable, so each call
30
+ // receives a guaranteed-unique ID.
32
31
  const idPrefix = useId();
33
32
  const counterRef = useRef(0);
34
33
  const toast = useCallback((opts) => {
@@ -1,8 +1,8 @@
1
1
  import { type LiveEventSubscriber } from "../../index.ts";
2
2
  export type CreateEventSourceLiveEventsOptions = {
3
- /** URL des SSE-Endpoints. Default: /api/sse (das ist wo
4
- * createSseRoute im Kumiko-Server mountet). Override wenn der
5
- * Mountpath divergiert. */
3
+ /** URL of the SSE endpoint. Defaults to /api/sse, where
4
+ * createSseRoute mounts it on the Kumiko server. Override this when
5
+ * the mount path differs. */
6
6
  readonly url?: string;
7
7
  /** Fires once per connection when the session ended: the server refused the SSE
8
8
  * handshake, or opened the stream with a `session-ended` frame (stale auth
@@ -22,7 +22,7 @@ export type LiveEventTimers = {
22
22
  export declare const RECONNECT_BASE_DELAY_MS = 1000;
23
23
  export declare const RECONNECT_MAX_DELAY_MS = 30000;
24
24
  export declare function reconnectDelayMs(attempt: number, random: number): number;
25
- /** Liefert einen `LiveEventSubscriber` der EventSource-backed ist.
26
- * Normalerweise einmal im App-Bootstrap gerufen und als value an
27
- * `<LiveEventsProvider>` durchgereicht — createKumikoApp tut das. */
25
+ /** Returns an EventSource-backed `LiveEventSubscriber`.
26
+ * Typically called once during app bootstrap and passed as a value to
27
+ * `<LiveEventsProvider>`; `createKumikoApp` does this. */
28
28
  export declare function createEventSourceLiveEvents(options?: CreateEventSourceLiveEventsOptions): LiveEventSubscriber;
@@ -61,9 +61,9 @@ async function isSessionEndedRejection(url) {
61
61
  controller.abort();
62
62
  }
63
63
  }
64
- /** Liefert einen `LiveEventSubscriber` der EventSource-backed ist.
65
- * Normalerweise einmal im App-Bootstrap gerufen und als value an
66
- * `<LiveEventsProvider>` durchgereicht — createKumikoApp tut das. */
64
+ /** Returns an EventSource-backed `LiveEventSubscriber`.
65
+ * Typically called once during app bootstrap and passed as a value to
66
+ * `<LiveEventsProvider>`; `createKumikoApp` does this. */
67
67
  export function createEventSourceLiveEvents(options = {}) {
68
68
  const url = options.url ?? "/api/sse";
69
69
  const timers = options.timers ?? defaultTimers;
@@ -5,20 +5,19 @@ export declare const THEME_STORAGE_KEY = "kumiko:theme";
5
5
  * the host HTML (see header comment) does the same synchronously before the
6
6
  * first paint. */
7
7
  export declare function applyStoredThemeMode(): void;
8
- /** Nur für Tests: der once-per-page-load-Guard ist ein Module-Singleton —
9
- * ohne Reset wäre der Mount-Restore-Pfad nach der ersten Render im
10
- * Testfile strukturell unerreichbar. */
8
+ /** Tests only: the once-per-page-load guard is a module singleton; without a reset,
9
+ * the mount-restore path would be structurally unreachable after the first render
10
+ * in the test file. */
11
11
  export declare function __resetStoredModeAppliedForTests(): void;
12
- /** Hook der eine TokensApi für den Browser baut. Wird von
13
- * createKumikoApp genutzt; App-Code der einen eigenen Token-State
14
- * braucht (z.B. User-Präferenz aus localStorage) kann selber
15
- * `<TokensProvider value={...}>` mounten. */
12
+ /** Builds a TokensApi for the browser. Used by createKumikoApp; app code that needs
13
+ * its own token state (for example, a user preference from localStorage) can mount
14
+ * its own `<TokensProvider value={...}>`. */
16
15
  export declare function useBrowserTokensApi(): TokensApi;
17
- /** Default-Tokens — identisch zu `cssVarTokens` (var-string-refs).
18
- * Light- und Dark-Werte switchen via `.dark`-class auf <html>. */
16
+ /** Default tokens—identical to `cssVarTokens` (variable-string references).
17
+ * Light and dark values switch via the `.dark` class on <html>. */
19
18
  export declare const defaultTokens: Tokens;
20
19
  export declare const lightTokens: Tokens;
21
- /** Historisch: schrieb Tokens auf :root als CSS-vars. Jetzt no-op —
22
- * die CSS-vars leben in styles.css, nicht in JS. Bleibt als Export
23
- * damit alter App-Code nicht bricht. */
20
+ /** Historically wrote tokens to :root as CSS variables. Now a no-op—the CSS
21
+ * variables live in styles.css, not JS. Retained as an export so existing app code
22
+ * does not break. */
24
23
  export declare function applyTokensToCssVars(_tokens: Tokens): void;
@@ -2,26 +2,25 @@ import { useEffect, useState, useSyncExternalStore } from "react";
2
2
  import { createStore } from "../headless/index.js";
3
3
  import { cssVarTokens, } from "../index.js";
4
4
  import { DEFAULT_COLOR_SCHEME_VARIABLE } from "./default-color-scheme.js";
5
- // Web-spezifische TokensApi-Impl. Theme-Toggle via `.dark`-Class auf
6
- // <html>. Die echten Farben leben in styles.css; hier ist nur die
7
- // JS-Seite die den class-switch triggert und React-Consumer mit
8
- // useSyncExternalStore darüber informiert.
5
+ // Web-specific TokensApi implementation. The theme toggle uses the `.dark` class on
6
+ // <html>. The actual colors live in styles.css; this is only the JS side that triggers
7
+ // the class switch and informs React consumers through useSyncExternalStore.
9
8
  //
10
- // Source-of-truth ist der DOM (`<html class="dark">`); der Store ist
11
- // reiner Notification-Bus (Tick-Counter), den setMode/toggleMode bei
12
- // jedem Class-Wechsel hochzählen. So bleibt die DOM-Klasse die einzige
13
- // Wahrheit — readCurrentMode liest sie frisch bei jedem getSnapshot.
9
+ // The DOM (`<html class="dark">`) is the source of truth; the store is only a
10
+ // notification bus (a tick counter) incremented by setMode/toggleMode on every class
11
+ // change. This keeps the DOM class as the sole authority—readCurrentMode reads it
12
+ // freshly on every getSnapshot.
14
13
  //
15
- // Persistence: the choice is stored in localStorage (THEME_STORAGE_KEY) and
16
- // restored on the first hook mount; without it the toggle was lost on every
17
- // reload (prod bug 2026-06-07).
18
- // Preference: "auto" follows prefers-color-scheme live (matchMedia listener),
19
- // "light"/"dark" are explicit choices and win. Without a stored choice,
20
- // AppTheme.defaultColorScheme (CSS variable) applies, otherwise the HTML state.
14
+ // Persistence: the choice is stored in localStorage (THEME_STORAGE_KEY) and restored
15
+ // on the first hook mount; without it, the toggle would be lost on every reload.
16
+ // Preference: "auto" follows prefers-color-scheme live (matchMedia listener), while
17
+ // "light"/"dark" are explicit choices and take precedence. Without a stored choice,
18
+ // AppTheme.defaultColorScheme (CSS variable) applies; otherwise, the HTML state.
21
19
  //
22
- // Against FOUC the host HTML also needs a synchronous inline script BEFORE
23
- // the stylesheet link. Under a strict CSP its hash is the sha256 in the build's
24
- // csp-hashes.json, which runProdApp also hands to hostDispatch as deps.cspScriptHashes:
20
+ // To prevent FOUC, the host HTML also needs a synchronous inline script BEFORE the
21
+ // stylesheet link. Under a strict CSP, its hash is the sha256 in the build's
22
+ // csp-hashes.json, which runProdApp also passes to hostDispatch as
23
+ // deps.cspScriptHashes:
25
24
  //
26
25
  // <script>try{var s=localStorage.getItem("kumiko:theme");
27
26
  // if(s==="dark"||(s==="auto"&&matchMedia("(prefers-color-scheme: dark)").matches))
@@ -94,8 +93,8 @@ function persistPreference(preference) {
94
93
  window.localStorage.setItem(THEME_STORAGE_KEY, preference);
95
94
  }
96
95
  catch {
97
- // skip: localStorage kann werfen (Private-Mode/Quota) — Theme bleibt
98
- // dann sessionbasiert, der Class-Toggle hat trotzdem funktioniert.
96
+ // skip: localStorage can throw (private mode/quota); the theme then remains
97
+ // session-based, but the class toggle still works.
99
98
  }
100
99
  }
101
100
  function applyPreference(preference) {
@@ -123,23 +122,22 @@ export function applyStoredThemeMode() {
123
122
  applyPreference(declared);
124
123
  }
125
124
  let storedModeApplied = false;
126
- /** Nur für Tests: der once-per-page-load-Guard ist ein Module-Singleton —
127
- * ohne Reset wäre der Mount-Restore-Pfad nach der ersten Render im
128
- * Testfile strukturell unerreichbar. */
125
+ /** Tests only: the once-per-page-load guard is a module singleton; without a reset,
126
+ * the mount-restore path would be structurally unreachable after the first render
127
+ * in the test file. */
129
128
  export function __resetStoredModeAppliedForTests() {
130
129
  storedModeApplied = false;
131
130
  currentPreference = null;
132
131
  }
133
- /** Hook der eine TokensApi für den Browser baut. Wird von
134
- * createKumikoApp genutzt; App-Code der einen eigenen Token-State
135
- * braucht (z.B. User-Präferenz aus localStorage) kann selber
136
- * `<TokensProvider value={...}>` mounten. */
132
+ /** Builds a TokensApi for the browser. Used by createKumikoApp; app code that needs
133
+ * its own token state (for example, a user preference from localStorage) can mount
134
+ * its own `<TokensProvider value={...}>`. */
137
135
  export function useBrowserTokensApi() {
138
- // Einmal pro Page-Load: gespeicherte Wahl anwenden. Lazy statt
139
- // Modul-Side-Effect, damit Import ohne DOM (SSR/Tests) safe bleibt.
140
- // Als useState-Lazy-Initializer statt nackt im Render-Body: der
141
- // DOM-Side-Effect lief sonst potenziell in einem verworfenen
142
- // Concurrent-Render (React darf Render-Bodies wiederholen/abbrechen).
136
+ // Apply the stored choice once per page load. This is lazy rather than a module
137
+ // side effect so importing without a DOM (SSR/tests) remains safe.
138
+ // A useState lazy initializer is used instead of placing this directly in the
139
+ // render body because the DOM side effect could otherwise run during a discarded
140
+ // concurrent render (React may repeat or abandon render bodies).
143
141
  useState(() => {
144
142
  if (!storedModeApplied && typeof document !== "undefined") {
145
143
  storedModeApplied = true;
@@ -192,13 +190,13 @@ export function useBrowserTokensApi() {
192
190
  },
193
191
  };
194
192
  }
195
- /** Default-Tokens — identisch zu `cssVarTokens` (var-string-refs).
196
- * Light- und Dark-Werte switchen via `.dark`-class auf <html>. */
193
+ /** Default tokens—identical to `cssVarTokens` (variable-string references).
194
+ * Light and dark values switch via the `.dark` class on <html>. */
197
195
  export const defaultTokens = cssVarTokens;
198
196
  export const lightTokens = cssVarTokens;
199
- /** Historisch: schrieb Tokens auf :root als CSS-vars. Jetzt no-op —
200
- * die CSS-vars leben in styles.css, nicht in JS. Bleibt als Export
201
- * damit alter App-Code nicht bricht. */
197
+ /** Historically wrote tokens to :root as CSS variables. Now a no-op—the CSS
198
+ * variables live in styles.css, not JS. Retained as an export so existing app code
199
+ * does not break. */
202
200
  export function applyTokensToCssVars(_tokens) {
203
- // Absichtlich leer — siehe Kommentar oben.
201
+ // Intentionally empty—see the comment above.
204
202
  }
@@ -1,13 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // Update-Awareness — default an, kein Flag. Ein offener Tab erfährt sonst
3
- // nie von einem neuen Deploy (SPA bleibt geladen, index.html wird nicht neu
4
- // geholt → "muss hart reloaden"). Statt Service-Worker (der ist die *Ursache*
5
- // von "hängt auf alter Version") pollt diese Komponente die statische
6
- // build-info.json beim Tab-Fokus und zeigt ein Reload-Banner bei Drift.
2
+ // Update awareness is enabled by default, without a flag. Otherwise, an open tab
3
+ // never learns about a new deploy because the SPA stays loaded and does not fetch
4
+ // index.html again, so it requires a hard reload. Instead of a service worker—which
5
+ // causes the “stuck on old version” problem—this component polls the static
6
+ // build-info.json when the tab gains focus and shows a reload banner on drift.
7
7
  //
8
- // Versionsquelle ist build-info.json (Hash über die Asset-URLs, vom Prod-Build
9
- // geschrieben) — NICHT /api/version (live leer/404, Env-/Dockerfile-Falle).
10
- // Banner-Strings über kumikoDefaultTranslations (kumiko.version.*, de/en).
8
+ // The version source is build-info.json (a hash of the asset URLs written by the
9
+ // production build), not /api/version (which may be empty or 404 at runtime because
10
+ // of environment or Dockerfile configuration). Banner strings come from
11
+ // kumikoDefaultTranslations (kumiko.version.*, de/en).
11
12
  import { propertyOf } from "@cosmicdrift/kumiko-types/type-guards";
12
13
  import { useEffect, useState } from "react";
13
14
  import { useTranslation } from "../../index.js";
@@ -21,9 +22,9 @@ export function readLoadedBuild() {
21
22
  return undefined;
22
23
  return { id, builtAt: meta?.getAttribute("data-built-at") ?? "" };
23
24
  }
24
- // Korrektheitsgrenze der Update-Erkennung: ein Banner NUR bei echtem
25
- // ID-Drift. Kein geladener Stand (Dev/altes Bundle) oder kein Server-Stand
26
- // (Fetch-Fehler/kaputtes JSON → null) → nie ein Fake-Banner.
25
+ // Update detection is only valid for a real ID drift: a banner is shown only then.
26
+ // No loaded build (development or an old bundle), or no server build (fetch failure
27
+ // or invalid JSON → null), must never produce a false update banner.
27
28
  export function shouldShowUpdate(loadedId, server) {
28
29
  if (!loadedId || !server)
29
30
  return false;
@@ -44,7 +45,7 @@ async function fetchServerBuild() {
44
45
  return { id: raw.id, builtAt: typeof builtAt === "string" ? builtAt : "" };
45
46
  }
46
47
  catch {
47
- // Netzwerkfehler / kaputtes JSON → kein Banner. Nie ein Fake-Update zeigen.
48
+ // Network failure or invalid JSON → no banner; never show a false update.
48
49
  return null;
49
50
  }
50
51
  }
@@ -55,7 +56,7 @@ export function UpdateChecker() {
55
56
  useEffect(() => {
56
57
  const loadedId = loaded?.id;
57
58
  if (!loadedId)
58
- return; // Dev / altes Bundle → keine Awareness.
59
+ return; // Development or an old bundle → no update awareness.
59
60
  let cancelled = false;
60
61
  const checking = { current: false };
61
62
  const check = async () => {
@@ -1,6 +1,6 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // AiTextField / AiTextArea — Drop-in replacement for TextField/TextareaField
3
- // with AI-augmented actions (ai-text-primitive plan doc, Phase 3).
3
+ // with AI-augmented actions (from the ai-text-primitive plan).
4
4
  //
5
5
  // Built on raw <input>/<textarea> (not the generic Input primitive) because
6
6
  // ghost-text needs a keydown handler (Tab/Esc) and an overlay that the
@@ -4,10 +4,10 @@ import { type StatusTone } from "./status-badge.tsx";
4
4
  export type StatusBarEntry = {
5
5
  /** Stable key (z.B. ISO-Datum). */
6
6
  readonly key: string;
7
- /** Balkenhöhe 0..1 (z.B. operational=1, degraded=0.75, outage=0.25). */
7
+ /** Bar height from 0 to 1 (e.g. operational=1, degraded=0.75, outage=0.25). */
8
8
  readonly level: number;
9
9
  readonly tone: StatusTone;
10
- /** Tooltip-Text (<title>) — translated vom Caller. */
10
+ /** Tooltip text (<title>), supplied translated by the caller. */
11
11
  readonly label?: string;
12
12
  };
13
13
  /** Status bar strip (e.g. 90-day uptime): variable-height bars with
@@ -17,7 +17,7 @@ export type StatusBarEntry = {
17
17
  export declare function StatusBarChart({ entries, ariaLabel, startLabel, endLabel, highlightLast, dense, testId, }: {
18
18
  readonly entries: readonly StatusBarEntry[];
19
19
  readonly ariaLabel: string;
20
- /** Achsen-Beschriftung links/rechts unter dem Chart — translated. */
20
+ /** Left and right axis labels below the chart, supplied translated. */
21
21
  readonly startLabel?: string;
22
22
  readonly endLabel?: string;
23
23
  readonly highlightLast?: boolean;
@@ -25,15 +25,15 @@ export declare function StatusBarChart({ entries, ariaLabel, startLabel, endLabe
25
25
  readonly dense?: boolean;
26
26
  readonly testId?: string;
27
27
  }): ReactNode;
28
- /** Quadratic-durch-Mittelpunkte-Trick: glättet die Zick-Zack-Linie ohne
29
- * Overshoot (~5 Zeilen statt Catmull-Rom/Bezier-Fit). */
28
+ /** Quadratic midpoint technique: smooths the zigzag line without overshoot
29
+ * (~5 lines instead of a Catmull-Rom/Bezier fit). */
30
30
  export declare function smoothPath(pts: ReadonlyArray<{
31
31
  readonly x: number;
32
32
  readonly y: number;
33
33
  }>): string;
34
34
  export type TimeseriesPoint = {
35
35
  readonly atMs: number;
36
- /** null = Ausfall/kein Wert → fällt auf die Grundlinie (sichtbarer Einbruch). */
36
+ /** null = outage/no value → falls to the baseline for a visible drop. */
37
37
  readonly value: number | null;
38
38
  };
39
39
  export type TimeseriesYAxis = {
@@ -65,16 +65,16 @@ export type TimeseriesReferenceLine = {
65
65
  readonly label: string;
66
66
  readonly tone?: StatusTone;
67
67
  };
68
- /** Zeitreihen-Linien-Chart (geglättete Linie + Flächen-Verlauf). x-Achse =
69
- * ZEIT im Fenster windowStartMs..windowEndMs, nicht Index — 5 Min Daten in
70
- * einem 30-Tage-Fenster ergeben ehrlich einen schmalen Streifen rechts. */
68
+ /** Time-series line chart (smoothed line with area fill). The x-axis uses
69
+ * TIME within windowStartMs..windowEndMs rather than the index, so 5-minute
70
+ * data in a 30-day window correctly appears as a narrow strip on the right. */
71
71
  export declare function TimeseriesChart({ points, windowStartMs, windowEndMs, tone, ariaLabel, axisLabels, xAxis, yAxis, height, markers, formatMarkerTime, referenceLines, emptyContent, testId, }: {
72
72
  readonly points: readonly TimeseriesPoint[];
73
73
  readonly windowStartMs: number;
74
74
  readonly windowEndMs: number;
75
75
  readonly tone?: StatusTone;
76
76
  readonly ariaLabel: string;
77
- /** Achsen-Zeile unter dem Chart (translated/formatiert vom Caller). */
77
+ /** Axis row below the chart, formatted and translated by the caller. */
78
78
  readonly axisLabels?: {
79
79
  readonly start: string;
80
80
  readonly mid?: string;
@@ -5,9 +5,9 @@ import { NARROW_VIEWPORT_BREAKPOINT_PX, useIsViewportBelow, } from "../primitive
5
5
  import { bucketIndexAtFraction, initialWindowSelection, nearestIndex, pointsWithin, resolveStackedAreaWindow, snapBrushWindow, snapMovedBrushWindow, stackedAreaAxisTicks, stackedAreaDateFormatFor, } from "./chart-window.js";
6
6
  import { ModeSwitch } from "./mode-switch.js";
7
7
  import { STATUS_TONE_TEXT } from "./status-badge.js";
8
- // Inline-SVG-Charts — kein Chart-Dep. Farben ausschließlich über die
9
- // --color-status-* / --color-foreground Theme-Tokens; Achsen-Labels
10
- // kommen translated vom Caller (keine Locale-Annahmen im Widget).
8
+ // Inline SVG charts without a chart dependency. Colors come exclusively from the
9
+ // --color-status-* / --color-foreground theme tokens; axis labels are supplied
10
+ // translated by the caller, so the widget makes no locale assumptions.
11
11
  const HOUR_MS = 3_600_000;
12
12
  const DAY_MS = 24 * HOUR_MS;
13
13
  const TONE_VAR = {
@@ -47,8 +47,8 @@ const SINGLE_POINT_RADIUS = 3;
47
47
  function clampDotY(y, height) {
48
48
  return Math.max(SINGLE_POINT_RADIUS, Math.min(height - SINGLE_POINT_RADIUS, y));
49
49
  }
50
- /** Quadratic-durch-Mittelpunkte-Trick: glättet die Zick-Zack-Linie ohne
51
- * Overshoot (~5 Zeilen statt Catmull-Rom/Bezier-Fit). */
50
+ /** Quadratic midpoint technique: smooths the zigzag line without overshoot
51
+ * (~5 lines instead of a Catmull-Rom/Bezier fit). */
52
52
  export function smoothPath(pts) {
53
53
  const first = pts[0];
54
54
  if (!first)
@@ -85,9 +85,9 @@ function yTickPosition(index, count) {
85
85
  return "";
86
86
  return "-translate-y-1/2";
87
87
  }
88
- /** Zeitreihen-Linien-Chart (geglättete Linie + Flächen-Verlauf). x-Achse =
89
- * ZEIT im Fenster windowStartMs..windowEndMs, nicht Index — 5 Min Daten in
90
- * einem 30-Tage-Fenster ergeben ehrlich einen schmalen Streifen rechts. */
88
+ /** Time-series line chart (smoothed line with area fill). The x-axis uses
89
+ * TIME within windowStartMs..windowEndMs rather than the index, so 5-minute
90
+ * data in a 30-day window correctly appears as a narrow strip on the right. */
91
91
  export function TimeseriesChart({ points, windowStartMs, windowEndMs, tone = "ok", ariaLabel, axisLabels, xAxis, yAxis, height, markers, formatMarkerTime, referenceLines, emptyContent, testId, }) {
92
92
  const gradientId = useId();
93
93
  const descId = useId();
@@ -1,7 +1,7 @@
1
1
  import { type ReactNode } from "react";
2
- /** Aufklappbare Sektion (native <details>, aber React-kontrolliert: sonst
3
- * springt das open-Attribut bei jedem Parent-Re-Render auf den Initialwert
4
- * zurück). Eine Optik für alle „Erweitert"/„Mehr"-Abschnitte. */
2
+ /** Collapsible section using native <details> with React-controlled state; otherwise
3
+ * the open attribute resets to its initial value on every parent re-render.
4
+ * Provides consistent styling for all “Erweitert”/“Mehr” sections. */
5
5
  export declare function CollapsibleSection({ title, defaultOpen, children, testId, }: {
6
6
  readonly title: string;
7
7
  readonly defaultOpen?: boolean;
@@ -1,14 +1,14 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { ChevronRight } from "lucide-react";
3
3
  import { useEffect, useState } from "react";
4
- /** Aufklappbare Sektion (native <details>, aber React-kontrolliert: sonst
5
- * springt das open-Attribut bei jedem Parent-Re-Render auf den Initialwert
6
- * zurück). Eine Optik für alle „Erweitert"/„Mehr"-Abschnitte. */
4
+ /** Collapsible section using native <details> with React-controlled state; otherwise
5
+ * the open attribute resets to its initial value on every parent re-render.
6
+ * Provides consistent styling for all “Erweitert”/“Mehr” sections. */
7
7
  export function CollapsibleSection({ title, defaultOpen = false, children, testId, }) {
8
8
  const [open, setOpen] = useState(defaultOpen);
9
- // useState(defaultOpen) liest nur den Mount-Wert — ein async hydrierter
10
- // Draft flippt `defaultOpen` zu spät für den Lazy-Initializer. Open-only
11
- // Effect: kämpft nie gegen ein manuelles Zuklappen des Users.
9
+ // useState(defaultOpen) reads only the mount-time value; an asynchronously hydrated
10
+ // draft flips `defaultOpen` too late for the lazy initializer. An open-only
11
+ // effect never overrides the user's manual collapse.
12
12
  useEffect(() => {
13
13
  if (defaultOpen)
14
14
  setOpen(true);
@@ -1,7 +1,5 @@
1
1
  import type { ReactNode } from "react";
2
- /** Read-only Schlüssel-Wert-Liste für Detail-Masken (Label links gedimmt,
3
- * Wert rechts). Wert ist ReactNode → Badges/Chips möglich. `emphasize` hebt
4
- * eine Zeile hervor (z.B. das Endergebnis in einem Rechner). */
2
+ /** Read-only key-value list for detail views (dimmed label on the left, value on the right). The value is a ReactNode, so badges and chips are supported. `emphasize` highlights a row, such as the final result in a calculator. */
5
3
  export declare function DetailList({ rows, testId, }: {
6
4
  readonly rows: readonly {
7
5
  /** Optional stable key -- falls back to the label when omitted (fine as
@@ -1,8 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { cn } from "../lib/cn.js";
3
- /** Read-only Schlüssel-Wert-Liste für Detail-Masken (Label links gedimmt,
4
- * Wert rechts). Wert ist ReactNode → Badges/Chips möglich. `emphasize` hebt
5
- * eine Zeile hervor (z.B. das Endergebnis in einem Rechner). */
3
+ /** Read-only key-value list for detail views (dimmed label on the left, value on the right). The value is a ReactNode, so badges and chips are supported. `emphasize` highlights a row, such as the final result in a calculator. */
6
4
  export function DetailList({ rows, testId, }) {
7
5
  return (_jsx("dl", { "data-testid": testId, className: "@container flex flex-col divide-y", children: rows.map((row) => (_jsxs("div", { className: "grid grid-cols-1 gap-0.5 py-2.5 @md:grid-cols-[200px_1fr] @md:gap-4", children: [_jsx("dt", { className: cn("text-sm text-muted-foreground", row.emphasize === true && "font-semibold text-foreground"), children: row.label }), _jsx("dd", { className: cn("text-sm font-medium", row.emphasize === true && "font-semibold"), children: row.value })] }, row.id ?? row.label))) }));
8
6
  }