@cosmicdrift/kumiko-renderer 0.385.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 +28 -5
  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 +43 -44
  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
@@ -8,7 +8,7 @@ export type { RuntimeRenderer };
8
8
  * element. */
9
9
  export type DataAttributes = Readonly<Record<`data-${string}`, string>>;
10
10
  /** Structural marker a top-level `actions` node can carry so DefaultForm's
11
- * sticky-footer split (fw#1918) treats it as the pinned primary action even
11
+ * sticky-footer split treats it as the pinned primary action even
12
12
  * though it isn't a `Button` with `type="submit"` — e.g. RenderEdit's
13
13
  * footer-slot mount, whose actual rendered button is opaque app code the
14
14
  * split can't inspect (see `ScreenSlots.footerPrimary`). */
@@ -24,10 +24,10 @@ export type FooterActionMarker = {
24
24
  readonly [FOOTER_ACTION_ROLE_PROP]?: FooterActionRole;
25
25
  readonly [NARROW_LABEL_PROP]?: string;
26
26
  };
27
- /** Standard-Button. `loading` zeigt einen Spinner statt der Children
28
- * und sollte mit `disabled` kombiniert werden, wenn die Action wirklich
29
- * blockiert bis das Loading durch ist (z.B. async submit). Native-
30
- * Impls können den Spinner als Activity-Indicator rendern. */
27
+ /** Standard button. `loading` displays a spinner instead of the children and
28
+ * should be combined with `disabled` when the action must remain blocked until
29
+ * loading finishes (for example, during an async submit). Native
30
+ * implementations may render the spinner as an activity indicator. */
31
31
  export type ButtonProps = ButtonBaseProps & ({
32
32
  readonly children: ReactNode;
33
33
  } | {
@@ -39,9 +39,9 @@ type ButtonBaseProps = {
39
39
  readonly type?: "button" | "submit";
40
40
  readonly onClick?: () => void | Promise<void>;
41
41
  readonly disabled?: boolean;
42
- /** Spinner statt Children rendern. Caller sollte `disabled` mit-
43
- * setzen wenn die Action blockiert bis das Loading abgeschlossen
44
- * ist (verhindert Double-Submit). */
42
+ /** Render a spinner instead of the children. Callers should also set
43
+ * `disabled` when the action is blocked until loading completes, preventing
44
+ * double submission. */
45
45
  readonly loading?: boolean;
46
46
  /** Semantic class — default="primary". Custom impls decide what this
47
47
  * becomes visually; the renderers use "primary" for Save, "danger" for
@@ -50,11 +50,11 @@ type ButtonBaseProps = {
50
50
  * destructive action as red text instead of a red fill, "ghost" for a
51
51
  * low-emphasis primary-colored text action beside a filled primary. */
52
52
  readonly variant?: "primary" | "secondary" | "danger" | "link" | "danger-ghost" | "ghost";
53
- /** Größe — default="md". "sm" für kompakte Inline-Aktionen (Toolbar,
54
- * Listen-Zeilen), "icon" für quadratische Icon-only-Buttons. */
53
+ /** Size—default="md". "sm" is for compact inline actions (toolbars,
54
+ * list rows); "icon" is for square icon-only buttons. */
55
55
  readonly size?: "sm" | "md" | "icon";
56
- /** Barrierefreies Label — Pflicht bei icon-only-Buttons (children ist nur
57
- * ein Icon/Zeichen), sonst hat der Button keinen zugänglichen Namen. */
56
+ /** Accessible label—required for icon-only buttons (when children contain
57
+ * only an icon or character), otherwise the button has no accessible name. */
58
58
  readonly ariaLabel?: string;
59
59
  /** Tooltip — rendered as the `title` attribute. */
60
60
  readonly title?: string;
@@ -62,9 +62,9 @@ type ButtonBaseProps = {
62
62
  readonly pressed?: boolean;
63
63
  /** Disclosure state for buttons that open a menu or panel — web sets `aria-expanded`. */
64
64
  readonly expanded?: boolean;
65
- /** Breite — default="auto" (inhaltsbreit). "full" streckt CTA-Buttons auf
66
- * die Container-Breite (Karten/Panels). Andere Breiten sind Layout-Sache
67
- * des Containers, kein Button-Prop (Kit hält arbiträres Sizing draußen). */
65
+ /** Width—default="auto" (content width). "full" stretches CTA buttons to
66
+ * the container width (cards/panels). Other widths belong to the container's
67
+ * layout, not to a button prop; the kit excludes arbitrary sizing here. */
68
68
  readonly width?: "full" | "auto";
69
69
  readonly testId?: string;
70
70
  /** Layout extras, merged via cn(). */
@@ -78,10 +78,10 @@ type ButtonBaseProps = {
78
78
  readonly iconEnd?: NavIconKey;
79
79
  readonly dataAttributes?: DataAttributes;
80
80
  };
81
- /** Navigations-Link. `variant="button"` rendert die Button-Optik auf einem
82
- * semantischen Anchor (z.B. „Zum Login" nach Reset-Success), `muted` den
83
- * dezenten Sekundär-Link. Interne SPA-Navigation läuft über den Browser-
84
- * Default (History-Integration liegt beim Nav-Layer, nicht am Primitive). */
81
+ /** Navigation link. `variant="button"` applies button styling to a semantic
82
+ * anchor (for example, “Zum Login" after reset success); `muted` makes it a
83
+ * subtle secondary link. Internal SPA navigation uses the browser default;
84
+ * history integration belongs to the navigation layer, not this primitive. */
85
85
  export type LinkProps = {
86
86
  readonly href: string;
87
87
  readonly variant?: "default" | "button" | "muted";
@@ -98,10 +98,10 @@ export type LinkProps = {
98
98
  readonly testId?: string;
99
99
  readonly dataAttributes?: DataAttributes;
100
100
  };
101
- /** Banner für inline-Message ODER Page-State (z.B. "Loading…",
102
- * "Screen not found"). `padded` setzt einen Außenabstand damit der
103
- * Banner nicht edge-to-edge an den Main-Border klebt — relevant
104
- * seit `<main>` kein eigenes Padding mehr hat. */
101
+ /** Banner for an inline message or page state (for example, "Loading…" or
102
+ * "Screen not found"). `padded` adds outer spacing so the banner does not
103
+ * touch the main border edge to edge, which matters because `<main>` no
104
+ * longer provides its own padding. */
105
105
  export type BannerProps = {
106
106
  /** "error" for alerts (conflict, network error), "info" for neutral
107
107
  * placeholders (not-found, loading), "loading" for load states, "warning"
@@ -113,17 +113,17 @@ export type BannerProps = {
113
113
  readonly title?: ReactNode;
114
114
  readonly titleTestId?: string;
115
115
  readonly children: ReactNode;
116
- /** Optional — weitere Knöpfe/Elemente rechts vom Text (z.B. "Neu
117
- * laden"). Inline, nicht als eigener Block. */
116
+ /** Optional—additional buttons or elements to the right of the text (for
117
+ * example, "Neu laden"). Inline, not a separate block. */
118
118
  readonly actions?: ReactNode;
119
- /** Setzt einen Außenabstand um den Banner — für Page-States wo der
120
- * Banner alleine im Main rendert (KumikoScreen "not-found",
121
- * "loading", etc.). Web fügt p-6 als Margin um den Banner. */
119
+ /** Adds outer spacing around the banner for page states where it renders
120
+ * alone in the main area (KumikoScreen "not-found", "loading", etc.). The
121
+ * web implementation adds p-6 as margin around the banner. */
122
122
  readonly padded?: boolean;
123
123
  readonly testId?: string;
124
124
  /** Set when a `<Field>` wraps this Banner as its control (no actual
125
125
  * input exists for the label to target — see render-field.tsx's
126
- * unsupported-field-type fallback, #1834). The web implementation uses
126
+ * unsupported-field-type fallback). The web implementation uses
127
127
  * it to derive `role="group"` + `aria-labelledby`, not `htmlFor`: a
128
128
  * `<label for>` on a non-labelable element like a `<div>` is inert for
129
129
  * screen readers. */
@@ -150,9 +150,9 @@ export type FieldProps = {
150
150
  readonly testId?: string;
151
151
  };
152
152
  readonly children: ReactNode;
153
- /** Label-Layout. `stacked` (Default): Label über dem Control. `inline`:
154
- * Control + Label nebeneinander (Box links, Label rechts) — für
155
- * Checkbox/Switch-Felder (boolean), wie das shadcn-Muster. */
153
+ /** Label layout. `stacked` (default) places the label above the control.
154
+ * `inline` places the control and label beside each other (box left, label
155
+ * right), for checkbox/switch fields (boolean), following the shadcn pattern. */
156
156
  readonly layout?: "stacked" | "inline" | "row";
157
157
  /** Marks a field whose value is set at this level (differs from the
158
158
  * inherited one). Only `layout="row"` draws it: a 2px primary line on the
@@ -168,9 +168,10 @@ export type FieldProps = {
168
168
  readonly changed?: boolean;
169
169
  readonly testId?: string;
170
170
  };
171
- /** Discriminated union — jede Input-Sorte hat ihre eigene value/onChange
172
- * Signatur. Custom-Impls dispatchen intern, rendern anders (Toggle
173
- * statt Checkbox), oder nur einzelne kinds unterschiedlich. */
171
+ /** Discriminated union: each input kind has its own value/onChange
172
+ * signature. Custom implementations can dispatch internally, render a
173
+ * different control (toggle instead of checkbox), or customize individual
174
+ * kinds. */
174
175
  /** Button beside a single-line text/number input that fills it with a
175
176
  * suggestion. Stays usable on a `disabled` input (the readOnly+fill case). */
176
177
  export type InputFillAction = {
@@ -205,17 +206,17 @@ export type InputProps = {
205
206
  readonly disabled?: boolean;
206
207
  readonly required?: boolean;
207
208
  readonly hasError?: boolean;
208
- /** Hint-Text wenn das Feld leer ist. Used für Search-Inputs in
209
- * Toolbars ("Suchen…") wo kein Label sinnvoll ist. */
209
+ /** Hint text shown when the field is empty. Used for search inputs in
210
+ * toolbars ("Suchen…") where no label is practical. */
210
211
  readonly placeholder?: string;
211
- /** Browser-Autofill / Native-Keyboard-Hint. Web setzt das auf
212
- * `<input autocomplete=...>`, Native auf `textContentType`. */
212
+ /** Browser autofill / native keyboard hint. Web applies this to
213
+ * `<input autocomplete=...>`; native applies it to `textContentType`. */
213
214
  readonly autoComplete?: string;
214
- /** `data-testid` aufs echte `<input>` — App-Tests adressieren das
215
- * Element direkt (getByTestId + readOnly/disabled-Assertions). */
215
+ /** Sets `data-testid` on the actual `<input>` so app tests can address the
216
+ * element directly (getByTestId + readOnly/disabled assertions). */
216
217
  readonly testId?: string;
217
- /** Read-only Input (z.B. gewürfelter Free-Tier-Slug). Nicht `disabled`
218
- * — bleibt fokussier-/kopierbar. */
218
+ /** Read-only input (for example, a generated free-tier slug). Not
219
+ * `disabled`, so it remains focusable and copyable. */
219
220
  readonly readOnly?: boolean;
220
221
  /** Closed FieldIconKey vocabulary (FIELD_ICONS registry, renderer/web). */
221
222
  readonly icon?: FieldIconKey;
@@ -237,8 +238,8 @@ export type InputProps = {
237
238
  readonly required?: boolean;
238
239
  readonly hasError?: boolean;
239
240
  readonly placeholder?: string;
240
- /** Default "email". Apps die "username" wollen (Login-Form mit
241
- * Username-or-Email) reichen das durch. */
241
+ /** Default "email". Apps that need "username" (a username-or-email login
242
+ * form) pass that value through. */
242
243
  readonly autoComplete?: string;
243
244
  readonly testId?: string;
244
245
  } | {
@@ -263,9 +264,9 @@ export type InputProps = {
263
264
  readonly hasError?: boolean;
264
265
  /** Hint text shown when the field is empty (analog to kind:"text"). */
265
266
  readonly placeholder?: string;
266
- /** "current-password" für Login, "new-password" für Reset/Signup —
267
- * Browser-Password-Manager nutzen das für die Speicherentscheidung.
268
- * Native: textContentType="password" / "newPassword". */
267
+ /** "current-password" for login, "new-password" for reset/signup—browser
268
+ * password managers use this to decide whether to save the value.
269
+ * Native uses textContentType="password" / "newPassword". */
269
270
  readonly autoComplete?: "current-password" | "new-password";
270
271
  /** Icon button inside the field's trailing edge (clear a stored value,
271
272
  * undo a pending removal). `label` is its accessible name + tooltip. */
@@ -326,7 +327,7 @@ export type InputProps = {
326
327
  readonly kind: "file" | "image";
327
328
  readonly id: string;
328
329
  readonly name: string;
329
- /** FileRef-UUID (gespeicherte Datei) oder null (kein Upload). */
330
+ /** FileRef UUID (stored file) or null (no upload). */
330
331
  readonly value: string | null;
331
332
  /** Implementations must offer their own remove control that calls
332
333
  * `onChange(null)`: FileListField relies on it to drop a file from an
@@ -338,8 +339,8 @@ export type InputProps = {
338
339
  /** Erlaubte Extensions/MIME (z.B. ["jpg","png"]) → `accept`-Attribut. */
339
340
  readonly accept?: readonly string[];
340
341
  readonly maxSize?: string;
341
- /** Für den Upload-POST `/api/files` — der Endpoint prüft maxSize/accept
342
- * gegen die Field-Def von (entityType, fieldName). */
342
+ /** For the upload POST `/api/files`: the endpoint validates maxSize/accept
343
+ * against the field definition for (entityType, fieldName). */
343
344
  readonly entityType?: string;
344
345
  /** Screen QN of the actionForm the field belongs to; sent as `actionForm` instead of
345
346
  * `entityType`, so the upload stays pending for that form's field. */
@@ -388,9 +389,9 @@ export type InputProps = {
388
389
  readonly name: string;
389
390
  readonly value: string;
390
391
  readonly onChange: (v: string | undefined) => void;
391
- /** Locale für Anzeige UND Eingabe-Parsing. Default = Browser-Locale
392
- * via navigator.language. Apps mit eigenem LocaleResolver können
393
- * ihren current locale durchreichen. */
392
+ /** Locale for display and input parsing. Default = browser locale via
393
+ * navigator.language. Apps with their own LocaleResolver can pass through
394
+ * their current locale. */
394
395
  readonly locale?: string;
395
396
  /** Datumsgrenzen als ISO `yyyy-mm-dd` — begrenzen Picker (Jahres-
396
397
  * Dropdown-Range + ausgegraute Tage). */
@@ -405,10 +406,10 @@ export type InputProps = {
405
406
  readonly name: string;
406
407
  readonly value: string;
407
408
  readonly onChange: (v: string) => void;
408
- /** Erlaubte Werte. Leeres Array → Dropdown ohne Optionen, ein
409
- * required Field ist dann nicht erfüllbar (Author-Hinweis, nicht
410
- * Endnutzer). String-Form für statische Listen (Wert == Label),
411
- * {value,label}-Form für DB-getragene Refs (Tier 2.7e-3). */
409
+ /** Allowed values. An empty array produces a dropdown with no options, so a
410
+ * required field cannot be satisfied (an author warning, not an end-user
411
+ * message). The string form is for static lists (value == label); the
412
+ * {value,label} form is for database-backed references. */
412
413
  readonly options: readonly string[] | readonly {
413
414
  readonly value: string;
414
415
  readonly label: string;
@@ -450,14 +451,14 @@ export type InputProps = {
450
451
  readonly placeholder?: string;
451
452
  readonly searchPlaceholder?: string;
452
453
  readonly emptyText?: string;
453
- /** Tier 2.7e Remote-Search: gesetzt = Combobox läuft im Remote-
454
- * Mode (cmdk-Local-Filter aus, Search-Input ruft onSearchChange
455
- * debounced an den Caller). */
454
+ /** Remote search: when set, the combobox uses remote mode (local cmdk
455
+ * filtering is disabled and the search input calls the caller's
456
+ * debounced onSearchChange). */
456
457
  readonly onSearchChange?: (q: string) => void;
457
458
  readonly loading?: boolean;
458
459
  /** Fixed "+ createLabel" footer row that fires instead of
459
460
  * selecting an option — omit for the plain select-existing
460
- * combobox (kumiko-framework#1681). */
461
+ * combobox. */
461
462
  readonly onCreate?: () => void;
462
463
  readonly createLabel?: string;
463
464
  } & ({
@@ -472,18 +473,18 @@ export type InputProps = {
472
473
  readonly kind: "money";
473
474
  readonly id: string;
474
475
  readonly name: string;
475
- /** Internal-Format: Cents/Minor-Units als Integer (z.B. 1299 für
476
- * 12,99 EUR). Renderer übersetzt fürs UI in Major-Units mit
477
- * Locale-formatierten Dezimalstellen. Empty-State = `""`. */
476
+ /** Internal format: cents/minor units as an integer (for example, 1299 for
477
+ * 12,99 EUR). The renderer converts this to major units for the UI with
478
+ * locale-formatted decimal places. Empty state = `""`. */
478
479
  readonly value: number | "";
479
480
  readonly onChange: (v: number | undefined) => void;
480
- /** ISO-4217 Currency-Code, z.B. "EUR" / "USD" / "CHF". Default
481
- * "EUR". Renderer zeigt das Symbol als Suffix und formatiert die
482
- * Decimal-Stellen entsprechend (EUR/USD/CHF haben 2, JPY hat 0). */
481
+ /** ISO-4217 currency code, for example "EUR" / "USD" / "CHF". Default
482
+ * "EUR". The renderer displays the symbol as a suffix and formats the
483
+ * decimal places accordingly (EUR/USD/CHF use 2; JPY uses 0). */
483
484
  readonly currency?: string;
484
- /** Locale für Zahlen-Formatierung. Default "de-DE" (Komma als
485
- * Dezimaltrenner). Apps mit eigenem LocaleResolver können ihren
486
- * current locale durchreichen. */
485
+ /** Locale for number formatting. Default "de-DE" (comma as the decimal
486
+ * separator). Apps with their own LocaleResolver can pass through their
487
+ * current locale. */
487
488
  readonly locale?: string;
488
489
  readonly disabled?: boolean;
489
490
  readonly required?: boolean;
@@ -492,22 +493,22 @@ export type InputProps = {
492
493
  readonly kind: "timestamp";
493
494
  readonly id: string;
494
495
  readonly name: string;
495
- /** ISO-8601 Datetime-String. UTC-Instant mit `Z`
496
- * ("2026-04-25T13:45:00Z") oder Wall-Clock ohne Offset
497
- * ("2026-04-25T13:45", nur bei wallClock). Empty-State = `""`.
498
- * Web rendert getrennte Datums- (tippbar + Kalender) und
499
- * Uhrzeit-Eingabe und konvertiert. */
496
+ /** ISO-8601 datetime string. A UTC instant uses `Z`
497
+ * ("2026-04-25T13:45:00Z"); a wall-clock value has no offset
498
+ * ("2026-04-25T13:45", only with wallClock). Empty state = `""`.
499
+ * Web renders separate date (typable + calendar) and time inputs and
500
+ * converts the result. */
500
501
  readonly value: string;
501
502
  readonly onChange: (v: string | undefined) => void;
502
- /** true = locatedTimestamp (Wall-Clock ohne Offset, Server
503
- * validiert z.iso.datetime({local:true})). false/undefined =
504
- * UTC-Instant, onChange MUSS mit `Z`-Suffix emittieren. */
503
+ /** true = locatedTimestamp (wall-clock without an offset; the server
504
+ * validates z.iso.datetime({local:true})). false/undefined = a UTC
505
+ * instant; onChange MUST emit a `Z` suffix. */
505
506
  readonly wallClock?: boolean;
506
- /** Locale für Anzeige UND Eingabe-Parsing des Datums-Teils.
507
- * Default = Browser-Locale. */
507
+ /** Locale for displaying and parsing the date portion. Default = browser
508
+ * locale. */
508
509
  readonly locale?: string;
509
- /** Datumsgrenzen als ISO-Datetime — begrenzen den Kalender auf
510
- * Tages-Granularität. */
510
+ /** Date bounds as ISO datetimes; they constrain the calendar to day-level
511
+ * granularity. */
511
512
  readonly min?: string;
512
513
  readonly max?: string;
513
514
  readonly disabled?: boolean;
@@ -517,9 +518,9 @@ export type InputProps = {
517
518
  readonly kind: "locatedTimestamp";
518
519
  readonly id: string;
519
520
  readonly name: string;
520
- /** Located-Timestamp-Wert: Wall-Clock `at` (ohne Offset) + IANA-`tz`.
521
- * `utc` ist beim Read da (server-berechnet) und wird beim Write
522
- * ignoriert. Empty-State = `""`. onChange emittiert `{ at, tz }`. */
521
+ /** Located-timestamp value: wall-clock `at` (without an offset) plus an IANA
522
+ * `tz`. `utc` is present when reading (computed by the server) and ignored
523
+ * when writing. Empty state = `""`. onChange emits `{ at, tz }`. */
523
524
  readonly value: {
524
525
  readonly at: string;
525
526
  readonly tz: string;
@@ -529,9 +530,9 @@ export type InputProps = {
529
530
  readonly at: string;
530
531
  readonly tz: string;
531
532
  } | undefined) => void;
532
- /** Locale für den Datums-Teil. Default = Browser-Locale. */
533
+ /** Locale for the date portion. Default = browser locale. */
533
534
  readonly locale?: string;
534
- /** Datumsgrenzen als ISO-Datetime — begrenzen den Kalender. */
535
+ /** Date bounds as ISO datetimes; they constrain the calendar. */
535
536
  readonly min?: string;
536
537
  readonly max?: string;
537
538
  readonly disabled?: boolean;
@@ -555,15 +556,15 @@ export type InputProps = {
555
556
  readonly onChange: (v: string) => void;
556
557
  /** Visible rows. Acts as a minimum height — the field starts this tall
557
558
  * and still grows with its content. Omitted, the default primitive
558
- * falls back to its own floor (#2677). */
559
+ * falls back to its own floor. */
559
560
  readonly rows?: number;
560
561
  readonly disabled?: boolean;
561
562
  readonly required?: boolean;
562
563
  readonly hasError?: boolean;
563
564
  /** Hint text shown when the field is empty (analog to kind:"text"). */
564
565
  readonly placeholder?: string;
565
- /** Read-only Textarea. Nicht `disabled` — bleibt fokussier-/
566
- * kopierbar (analog zu kind:"text"). */
566
+ /** Read-only Textarea. Not `disabled` — remains focusable and
567
+ * copyable (analogous to kind:"text"). */
567
568
  readonly readOnly?: boolean;
568
569
  /** Ctrl/Cmd+Enter submits instead of inserting a newline. Plain
569
570
  * Enter still inserts a newline. */
@@ -582,7 +583,7 @@ export type DataTableSort = {
582
583
  };
583
584
  export type DataTableRowActionMode = "adaptive" | "inline";
584
585
  export type DataTableRowAction = {
585
- /** Stable id für aria-labels und data-testids. */
586
+ /** Stable ID for aria-labels and data-testids. */
586
587
  readonly id: string;
587
588
  /** Translated Label. */
588
589
  readonly label: string;
@@ -590,22 +591,22 @@ export type DataTableRowAction = {
590
591
  * primitive. Whether it also forces a confirm dialog is governed by
591
592
  * `confirmRequired` (falls back to `style === "danger"` when unset). */
592
593
  readonly style?: "primary" | "secondary" | "danger";
593
- /** Translated Confirm-Prompt (Description im Dialog) — wenn gesetzt,
594
- * öffnet ein Modal vor der Ausführung. Bei style=danger ohne expliziten
595
- * confirm sollte der Renderer einen generischen Default zeigen. */
594
+ /** Translated confirmation prompt (dialog description). When set, a modal
595
+ * opens before execution. For style=danger without an explicit confirm,
596
+ * the renderer should show a generic default. */
596
597
  readonly confirm?: string;
597
- /** Translated Confirm-Button-Label im Dialog. Default = `label`
598
- * (Action-Label wird wiederverwendet). */
598
+ /** Translated confirm-button label in the dialog. Defaults to `label`
599
+ * (the action label is reused). */
599
600
  readonly confirmLabel?: string;
600
601
  /** Overrides the default "danger implies a confirm dialog" rule. Schema-driven
601
602
  * navigate/drawer actions set it to false: the colour marks the action as
602
603
  * destructive, but the target form is the confirmation. */
603
604
  readonly confirmRequired?: boolean;
604
- /** Wird mit der ListRowViewModel der geklickten Row aufgerufen. Async
605
- * erlaubt — der Renderer kann während der Promise-Resolution einen
606
- * Loading-State auf dem Button zeigen. */
605
+ /** Called with the clicked row's `ListRowViewModel`. May be async, allowing
606
+ * the renderer to show a loading state on the button while the promise resolves. */
607
607
  readonly onTrigger: (row: ListRowViewModel) => Promise<void> | void;
608
- /** Conditional Visibility pro Row (z.B. "Start" nur wenn status==="scheduled"). */
608
+ /** Controls per-row visibility (e.g. show "Start" only when
609
+ * `status==="scheduled"`). */
609
610
  readonly isVisible?: (row: ListRowViewModel) => boolean;
610
611
  /** This action is what a click on the row does. With `onRowClick` the table
611
612
  * (except `"inline"` mode or editable cells) and the card layout drop it
@@ -632,8 +633,8 @@ export type DataTableFacet = {
632
633
  readonly field: string;
633
634
  /** Translated Button-Label ("Status", "Active"). */
634
635
  readonly label: string;
635
- /** Auswählbare Werte mit Anzeige-Label. value = roher Filterwert
636
- * (select-option / "true"|"false"), als String transportiert. */
636
+ /** Selectable values with display labels. `value` is the raw filter value
637
+ * (a select option or `"true"`/`"false"`), transported as a string. */
637
638
  readonly options: readonly {
638
639
  readonly value: string;
639
640
  readonly label: string;
@@ -679,20 +680,20 @@ export type DataTableProps = {
679
680
  /** Tints a row. Renderers without row tinting ignore it. */
680
681
  readonly rowTone?: (row: ListRowViewModel) => SelectOptionTone | undefined;
681
682
  readonly onRowClick?: (row: ListRowViewModel) => void;
682
- /** Aktuelle Sortierung (oder null = unsorted). Wenn columns ein
683
- * `sortable: true`-Feld haben und onSortChange gesetzt ist, rendert
684
- * der Renderer Click-Header mit Asc/Desc-Indikator. Ohne onSortChange
685
- * bleibt die Header-Click-Mechanik aus, columns.sortable ist dann nur
686
- * semantischer Hinweis. */
683
+ /** Current sorting, or null when unsorted. If columns contain a
684
+ * `sortable: true` field and `onSortChange` is set, the renderer makes
685
+ * headers clickable and shows ascending/descending indicators. Without
686
+ * `onSortChange`, header clicks remain disabled and `columns.sortable` is
687
+ * only semantic metadata. */
687
688
  readonly sort?: DataTableSort | null;
688
- /** Wird gerufen mit dem nächsten Sort-State nach einem Header-Klick.
689
- * 3-State-Toggle (Convention): asc → desc → null. Caller setzt damit
690
- * seinen URL-State / Query-Param und triggert ein refetch. */
689
+ /** Called with the next sort state after a header click.
690
+ * The conventional three-state cycle is asc → desc → null. The caller
691
+ * uses it to update URL state or a query parameter and refetch. */
691
692
  readonly onSortChange?: (next: DataTableSort | null) => void;
692
- /** Pro-Row-Aktionen — eine Spalte am rechten Rand mit Inline-Buttons
693
- * (≤2 Aktionen) oder Kebab-Dropdown (>2). Caller liefert Resolved-
694
- * Form (Labels + onTrigger schon verdrahtet); DataTable kümmert
695
- * sich nur um Render + Confirm-Dialog. */
693
+ /** Per-row actions rendered in a right-hand column as inline buttons
694
+ * (≤2 actions) or a kebab dropdown (>2). The caller supplies the resolved
695
+ * form, with translated labels and wired `onTrigger`; DataTable handles
696
+ * only rendering and the confirmation dialog. */
696
697
  readonly rowActions?: readonly DataTableRowAction[];
697
698
  /** How the row-action column renders:
698
699
  * - `"adaptive"` (default): with `onRowClick` all actions except the
@@ -703,45 +704,46 @@ export type DataTableProps = {
703
704
  * kebab). Actions then sit at the same x-position across all rows
704
705
  * (no drifting with label width) and all lists in an app look alike. */
705
706
  readonly rowActionMode?: DataTableRowActionMode;
706
- /** Custom Empty-State-Inhalt (z. B. Icon + Heading + CTA-Button).
707
- * Default-Renderer rahmt ihn in einer dashed-border Box. */
707
+ /** Custom empty-state content (e.g. Icon + Heading + CTA-Button).
708
+ * The default renderer wraps it in a dashed-border box. */
708
709
  readonly emptyState?: ReactNode;
709
- /** Optionaler Titel-Slot ganz links der Toolbar — Screen-Titel
710
- * ("Items", "Bestellungen"). Web rendert als font-medium Heading. */
710
+ /** Optional title slot at the left of the toolbar, typically the screen
711
+ * title ("Items", "Bestellungen"). The web renderer uses a medium-weight heading. */
711
712
  readonly toolbarTitle?: ReactNode;
712
- /** Toolbar-Slot mittig (typisch Search-Input). Renderer entscheidet
713
- * das Layout — Web spreizt das Element als flex-1 mit max-Breite,
714
- * Native könnte es als Header-Suchleiste rendern. */
713
+ /** Toolbar slot in the middle, typically a search input. The renderer
714
+ * controls the layout; the web implementation gives it `flex-1` with a
715
+ * maximum width, while native implementations may use a header search bar. */
715
716
  readonly toolbarStart?: ReactNode;
716
- /** Toolbar-Slot rechts (typisch + Neu Button, Filter, View-Switch).
717
- * Web zieht den Cluster mit ml-auto an die rechte Kante. */
717
+ /** Toolbar slot on the right, typically for a + New button, filters, or a
718
+ * view switch. The web implementation moves the cluster to the right edge
719
+ * with `ml-auto`. */
718
720
  readonly toolbarEnd?: ReactNode;
719
- /** Faceted-Filter-Dropdowns (select/boolean-Felder) — der Renderer
720
- * setzt sie neben das Search-Input. Leer/undefined → keine Filter. */
721
+ /** Faceted filter dropdowns for select/boolean fields. The renderer places
722
+ * them beside the search input; an empty or undefined value renders no filters. */
721
723
  readonly filterFacets?: readonly DataTableFacet[];
722
- /** Aktuell ausgewählte Werte je Facet-Feld (vom URL-State). */
724
+ /** Currently selected values for each facet field, supplied by URL state. */
723
725
  readonly filterValues?: Readonly<Record<string, readonly string[]>>;
724
- /** Toggle eines Facet-Werts → Caller schreibt URL-State + refetch. */
726
+ /** Toggling a facet value: the caller updates URL state and refetches. */
725
727
  readonly onFilterChange?: (field: string, values: readonly string[]) => void;
726
- /** Reset-Button: löscht alle aktiven Facets. Renderer zeigt ihn nur
727
- * wenn mindestens ein Facet aktiv ist. */
728
+ /** Clears all active facets. The renderer shows the reset button only when
729
+ * at least one facet is active. */
728
730
  readonly onFilterReset?: () => void;
729
- /** Time-range filters beside the facet dropdowns (fw#3104). */
731
+ /** Time-range filters beside the facet dropdowns. */
730
732
  readonly dateRangeFacets?: readonly DataTableDateRangeFacet[];
731
733
  /** One bound changed ("" clears it) → caller writes URL state + refetch. */
732
734
  readonly onDateRangeChange?: (field: string, bound: "from" | "to", value: string) => void;
733
- /** Pagination-State + Callback. Wenn gesetzt, rendert der Renderer
734
- * einen Pager unter der Tabelle (Web: Footer-Bar mit ← 1 ... N →).
735
- * total/limit/page sind 1-basiert für die UI; Server-Translation
736
- * zu offset = (page-1)*limit liegt beim Caller. Wenn page > total/limit
737
- * würde, soll der Caller das vorab clampen. */
735
+ /** Pagination state and callback. When set, the renderer shows a pager below
736
+ * the table (web: a footer bar with ← 1 ... N →). `total`, `limit`, and
737
+ * `page` are one-based for the UI; the caller translates them to
738
+ * `offset = (page-1)*limit` for the server. The caller should clamp a page
739
+ * greater than `total/limit` beforehand. */
738
740
  readonly pager?: {
739
741
  readonly page: number;
740
742
  readonly limit: number;
741
743
  readonly total: number;
742
744
  readonly onPageChange: (next: number) => void;
743
745
  /** Offered page sizes; with `onPageSizeChange` the footer shows a
744
- * "N pro Seite" select. */
746
+ * "N per page" select. */
745
747
  readonly pageSizeOptions?: readonly number[];
746
748
  readonly onPageSizeChange?: (next: number) => void;
747
749
  };
@@ -750,16 +752,16 @@ export type DataTableProps = {
750
752
  /** Noun for the footer count ("1–19 of 19 leases", "1 position"),
751
753
  * resolved for the given count. Omitted = generic "entries" wording. */
752
754
  readonly itemNoun?: (count: number) => string;
753
- /** Infinite-Scroll Callback. Wenn gesetzt, rendert der Renderer einen
754
- * Bottom-Sentinel und ruft `onReachEnd` wenn der ins Viewport rückt
755
- * (Web: IntersectionObserver). Caller verwaltet accumulation +
756
- * cursor + hasMore. `loadingMore=true` zeigt einen Spinner unter
757
- * der Tabelle solange die nächste Page lädt. Inkompatibel mit
758
- * `pager` — entweder Pager ODER Infinite-Scroll, nicht beides. */
755
+ /** Infinite-scroll callback. When set, the renderer adds a bottom sentinel
756
+ * and calls `onReachEnd` when it enters the viewport (web:
757
+ * `IntersectionObserver`). The caller manages accumulation, the cursor, and
758
+ * `hasMore`. `loadingMore=true` shows a spinner below the table while the
759
+ * next page loads. Incompatible with `pager`: use either pagination or
760
+ * infinite scrolling, not both. */
759
761
  readonly onReachEnd?: () => void;
760
762
  readonly loadingMore?: boolean;
761
- /** Wenn false und onReachEnd gesetzt, rendert der Renderer einen
762
- * "Ende der Liste"-Hinweis statt des Sentinels. Default true. */
763
+ /** When false and `onReachEnd` is set, the renderer shows an end-of-list
764
+ * message instead of the sentinel. Defaults to true. */
763
765
  readonly hasMore?: boolean;
764
766
  readonly testId?: string;
765
767
  /** Wires an edit path into a component column-renderer: `DataTableCell`
@@ -775,14 +777,14 @@ export type DataTableProps = {
775
777
  /** Overrides the cell `data-testid` (default `cell-${row.id}-${field}`). */
776
778
  readonly getCellTestId?: (row: ListRowViewModel, field: string) => string;
777
779
  /** Drops the table's own rounded/border/card background — for a host
778
- * that already provides the surrounding boundary (a tab panel, fw#2722)
780
+ * that already provides the surrounding boundary (a tab panel)
779
781
  * and would otherwise show a card nested inside that boundary. Default
780
782
  * false: unchanged card-framed table. */
781
783
  readonly chromeless?: boolean;
782
784
  /** Fills the available height of its flex container and scrolls rows
783
785
  * internally instead of growing with row count — for a host that would
784
786
  * otherwise have a long table stretch the whole page, or a short table
785
- * leave dead space below it (a tab panel, fw#2722). Requires the same
787
+ * leave dead space below it (a tab panel). Requires the same
786
788
  * flex-fill chain FormProps.fillHeight sets up above it; without that
787
789
  * ancestor chain this collapses to zero height (Web: `flex-1 min-h-0`
788
790
  * has no effect outside a sized flex-col ancestor). When true the table
@@ -795,7 +797,7 @@ export type DataTableProps = {
795
797
  /** Uses the shared screen padding (wider bottom inset) instead of the
796
798
  * table's symmetric embedded inset — for a table that IS the screen body
797
799
  * (entityList/projectionList), so a list screen ends at the same footer
798
- * distance as a form or custom screen (fw#2640). Ignored when
800
+ * distance as a form or custom screen. Ignored when
799
801
  * `scrollBody` is true: the wider bottom inset would compete with
800
802
  * `scrollBody`'s flex-fill height budget.
801
803
  * Default false: unchanged symmetric inset for an embedded table. */
@@ -921,8 +923,9 @@ export type FormProps = {
921
923
  readonly focusFirstErrorKey?: number;
922
924
  readonly children: ReactNode;
923
925
  readonly title?: ReactNode;
924
- /** Optionaler muted Untertitel unter dem Titel (z.B. "Add a new item to
925
- * your catalog") — gibt dem Form-Header Kontext statt nur ein Label. */
926
+ /** Optional muted subtitle below the title (e.g. "Add a new item to
927
+ * your catalog"), providing context in the form header rather than only
928
+ * a label. */
926
929
  readonly subtitle?: ReactNode;
927
930
  readonly actions?: ReactNode;
928
931
  /** Secondary, record-related actions — rendered on the left on desktop,
@@ -934,7 +937,7 @@ export type FormProps = {
934
937
  readonly width?: FormWidth;
935
938
  /** Pins `actions` to the viewport bottom on narrow screens (`<640px`)
936
939
  * instead of normal document flow, so it stays reachable when a virtual
937
- * keyboard shrinks the visible viewport (fw#1918). Desktop/tablet
940
+ * keyboard shrinks the visible viewport. Desktop/tablet
938
941
  * unaffected. */
939
942
  readonly stickyActions?: boolean;
940
943
  /** Extra content rendered above the card, inside the same page padding
@@ -948,7 +951,7 @@ export type FormProps = {
948
951
  /** Sizes the form to fill its container's height (instead of the page's
949
952
  * natural content height) so a single scrolling child — a lone
950
953
  * relatedList tab's table — can scroll internally instead of stretching
951
- * the whole page (fw#2722). Set by RenderEdit for a lone relatedList tab
954
+ * the whole page. Set by RenderEdit for a lone relatedList tab
952
955
  * section and for screens that fill the shell height (together with
953
956
  * `stickyActions`: sections scroll, footer stays pinned without
954
957
  * `position: fixed`); every other caller leaves it unset and keeps normal
@@ -982,18 +985,18 @@ export type FormProps = {
982
985
  * navigation). */
983
986
  readonly sideRail?: ReactNode;
984
987
  };
985
- /** Titled Gruppe von Feldern. Web: `<fieldset>` + `<legend>`, Native:
986
- * View mit Header-Text. Native-Impls können den Title als Accordion
987
- * oder Collapsible rendern. */
988
+ /** Titled group of fields. Web: `<fieldset>` + `<legend>`; native
989
+ * implementations use a `View` with header text and may render the title as
990
+ * an accordion or collapsible section. */
988
991
  export type SectionProps = {
989
992
  /** DOM id — the scroll target of a form's "on this page" navigation. */
990
993
  readonly id?: string;
991
- /** Optional — ohne Titel rendert die Section nur die Gruppierung
992
- * (kein Header). RenderEdit lässt ihn weg, wenn er den Screen-Titel
993
- * der Action-Bar 1:1 wiederholen würde. */
994
+ /** Optional; without a title, the Section renders only the grouping and no
995
+ * header. RenderEdit omits it when it would exactly repeat the screen title
996
+ * in the action bar. */
994
997
  readonly title?: string;
995
- /** Optionaler muted Untertitel unter dem Titel — gibt der Section
996
- * Kontext statt nur ein Label (analog FormProps.subtitle). */
998
+ /** Optional muted subtitle below the title, providing section context rather
999
+ * than only a label (analogous to `FormProps.subtitle`). */
997
1000
  readonly subtitle?: ReactNode;
998
1001
  readonly children: ReactNode;
999
1002
  /** Rendered top-right in the title row — never a footer. A
@@ -1016,7 +1019,7 @@ export type SectionProps = {
1016
1019
  /** Chromeless flex-fill layout host — no title, no card frame, no padding,
1017
1020
  * just a container that sizes to its content and, once the ancestor chain
1018
1021
  * is height-constrained, shrinks so one scrolling child can scroll
1019
- * internally instead of the page growing (fw#2722, height fw#2778). Web:
1022
+ * internally instead of the page growing. Web:
1020
1023
  * `<div className="flex min-h-0 flex-col">`. Native: Views are
1021
1024
  * already flex-column and the parent is already a bounded viewport there,
1022
1025
  * so a native impl may render this as a bare Fragment. Used only as the
@@ -1033,15 +1036,15 @@ export type FillContainerProps = {
1033
1036
  * height. */
1034
1037
  readonly grow?: boolean;
1035
1038
  };
1036
- /** Columns-basiertes Layout. Web: CSS grid, Native: Flex-Wrap mit
1037
- * Width-%, oder react-native-grid. Jedes direkte Child kann eine
1038
- * `GridCell`-Wrapping bekommen für span-Kontrolle.
1039
+ /** Column-based layout. Web uses CSS grid; native implementations use
1040
+ * flex-wrap with percentages or react-native-grid. Direct children can be
1041
+ * wrapped in `GridCell` to control their span.
1039
1042
  *
1040
- * `columns: "auto"` opts out of the N-column stretch layout entirely —
1041
- * children keep their own content width and wrap onto new lines (e.g. a
1042
- * metrics band of self-sized tiles, or an inline label+badge row) instead
1043
- * of being stretched into equal-width tracks. `GridCell` span-wrapping is
1044
- * meaningless in this mode and should be skipped. */
1043
+ * `columns: "auto"` disables the N-column stretch layout: children retain
1044
+ * their content width and wrap onto new lines (for example, self-sized
1045
+ * metric tiles or an inline label-and-badge row) instead of stretching into
1046
+ * equal-width tracks. `GridCell` span wrapping is meaningless in this mode
1047
+ * and should be skipped. */
1045
1048
  export type GridProps = {
1046
1049
  readonly columns: number | "auto";
1047
1050
  readonly children: ReactNode;
@@ -1063,19 +1066,19 @@ export type GridProps = {
1063
1066
  };
1064
1067
  /** Width class of a field cell inside a `flow` grid: sized by what the field holds. */
1065
1068
  export type FieldCellWidth = "text" | "number" | "money" | "date" | "timestamp" | "select" | "full" | "toggle";
1066
- /** Span-Wrapper für ein Kind innerhalb eines Grid. Web: `style={{gridColumn: span N}}`,
1067
- * Native: eigenes Width-Rechnen. */
1069
+ /** Span wrapper for a child within a grid. Web: `style={{gridColumn: span N}}`;
1070
+ * native implementations calculate the width themselves. */
1068
1071
  export type GridCellProps = {
1069
1072
  readonly span?: number;
1070
1073
  /** Only read inside a `flow` grid. */
1071
1074
  readonly width?: FieldCellWidth;
1072
1075
  readonly children: ReactNode;
1073
1076
  };
1074
- /** Semantischer Text. Variants bilden Standard-Typografie-Rollen ab —
1075
- * `body` ist Default, `small` für sekundäre Labels, `code` für inline
1076
- * monospace (entityId, screen-id), `required-mark` für das Sternchen
1077
- * hinter Labels, `muted` für gedimmten Fließtext (text-sm muted-
1078
- * foreground). Custom-Impls mappen auf ihren TypeScale. */
1077
+ /** Semantic text. Variants map to standard typography roles: `body` is
1078
+ * the default, `small` for secondary labels, `code` for inline monospace
1079
+ * (entityId, screen-id), `required-mark` for the asterisk after labels,
1080
+ * `muted` for dimmed body text (text-sm muted-foreground). Custom impls map
1081
+ * these to their own type scale. */
1079
1082
  export type TextProps = {
1080
1083
  readonly variant?: "body" | "small" | "code" | "required-mark" | "muted";
1081
1084
  /** Pure ornament (e.g. a separator glyph): hidden from assistive tech. */
@@ -1083,30 +1086,31 @@ export type TextProps = {
1083
1086
  readonly children: ReactNode;
1084
1087
  readonly testId?: string;
1085
1088
  };
1086
- /** Heading mit zwei Rollen — `page` als Page-Titel (Web: h1), `section`
1087
- * als Sub-Header über einer Group (Web: h2 mit uppercase + muted).
1088
- * Native-Impls mappen auf `<Text>` mit entsprechendem fontWeight/Size. */
1089
+ /** Heading with two roles: `page` is a page title (web: h1), while `section`
1090
+ * is a group subheading (web: h2 with uppercase and muted styling). Native
1091
+ * implementations map these to `<Text>` with the corresponding font weight
1092
+ * and size. */
1089
1093
  export type HeadingProps = {
1090
1094
  readonly variant?: "page" | "section";
1091
1095
  readonly children: ReactNode;
1092
1096
  readonly testId?: string;
1093
1097
  };
1094
- /** Modal-Dialog für Bestätigungen oder kompakte Sub-Forms. Web rendert
1095
- * Radix-Dialog (Focus-Trap, Esc-Schließen, Overlay-Click); Native
1096
- * würde ein Native-Modal nutzen. Apps öffnen den Dialog über einen
1097
- * External-State (`open` + `onOpenChange`); Confirm-Action läuft
1098
- * durch `onConfirm`, Cancel klappt zu via `onOpenChange(false)`.
1098
+ /** Modal dialog for confirmations or compact sub-forms. The web renderer
1099
+ * uses Radix Dialog with focus trapping, Escape-to-close, and overlay-click
1100
+ * handling; native implementations may use a native modal. Apps control it
1101
+ * through external state (`open` + `onOpenChange`); confirmation runs through
1102
+ * `onConfirm`, while cancellation closes it via `onOpenChange(false)`.
1099
1103
  *
1100
- * Variant `danger` markiert destruktive Bestätigungen visuell
1101
- * (rote Confirm-Button-Klasse), `default` für neutrale Dialoge. */
1104
+ * Variant `danger` visually marks destructive confirmations with a red
1105
+ * confirm-button class; `default` is for neutral dialogs. */
1102
1106
  export type DialogProps = {
1103
1107
  readonly open: boolean;
1104
1108
  readonly onOpenChange: (open: boolean) => void;
1105
1109
  readonly title: string;
1106
- /** Optional Beschreibung — typisch ein Satz der die Konsequenz
1107
- * erklärt ("Diese Aktion lässt sich nicht rückgängig machen."). */
1110
+ /** Optional description, typically a sentence explaining the consequence
1111
+ * ("This action cannot be undone."). */
1108
1112
  readonly description?: string;
1109
- /** Confirm-Button-Label. Default kommt aus i18n
1113
+ /** Confirm-button label. Defaults to the i18n value
1110
1114
  * (`kumiko.dialog.confirm`). */
1111
1115
  readonly confirmLabel?: string;
1112
1116
  /** Cancel-Button-Label. Default `kumiko.dialog.cancel`. */
@@ -1118,12 +1122,11 @@ export type DialogProps = {
1118
1122
  * `children` are present. `"cancel"` for
1119
1123
  * dialogs where the safe choice must be the one Enter triggers. */
1120
1124
  readonly initialFocus?: "confirm" | "cancel";
1121
- /** Wird gefeuert wenn der User Confirm drückt. Async-Funktion ist
1122
- * ok — Dialog setzt automatisch loading-State, ruft danach
1123
- * onOpenChange(false). */
1125
+ /** Called when the user confirms. Async functions are supported; the dialog
1126
+ * automatically sets a loading state and then calls `onOpenChange(false)`. */
1124
1127
  readonly onConfirm: () => void | Promise<void>;
1125
- /** Optional zusätzlicher Inhalt zwischen description und Buttons
1126
- * (z.B. ein Input wenn der Dialog auch Eingaben sammelt). */
1128
+ /** Optional additional content between the description and buttons, such as
1129
+ * an input when the dialog also collects data. */
1127
1130
  readonly children?: ReactNode;
1128
1131
  readonly testId?: string;
1129
1132
  };
@@ -1144,7 +1147,7 @@ export type ModalProps = {
1144
1147
  /** Side panel for hosting an existing self-contained form/widget (own
1145
1148
  * submit/cancel buttons) without leaving the underlying screen — e.g. a
1146
1149
  * toolbar action's `kind: "drawer"` mounts an actionForm here instead of
1147
- * navigating to a full page (fw#2225). Same "bare content shell, no
1150
+ * navigating to a full page. Same "bare content shell, no
1148
1151
  * footer buttons of its own" contract as `Modal`, slide-in instead of
1149
1152
  * centered overlay. */
1150
1153
  export type DrawerProps = {
@@ -1209,10 +1212,10 @@ export type ConfigCascadeViewProps = {
1209
1212
  * value). Absent = the raw value; a missing value keeps the no-value text. */
1210
1213
  readonly renderValue?: (value: string | number | boolean) => ReactNode;
1211
1214
  };
1212
- /** Card-Slots — benannte Inhalts-Regionen, alle optional. Neue Slots später =
1213
- * rein additiv → Consumer müssen NIE migriert werden. `header` ist der Escape-
1214
- * Hatch: ein roher Knoten ersetzt den Default-Header (title/subtitle/
1215
- * headerActions) komplett, wenn die Convenience-Slots nicht reichen. */
1215
+ /** Card slots are named content regions, all optional. Adding slots is
1216
+ * additive, so consumers do not need migration. `header` is an escape hatch:
1217
+ * a raw node replaces the default header (`title`/`subtitle`/`headerActions`)
1218
+ * when the convenience slots are insufficient. */
1216
1219
  export type CardSlots = {
1217
1220
  readonly header?: ReactNode;
1218
1221
  readonly title?: ReactNode;
@@ -1221,9 +1224,9 @@ export type CardSlots = {
1221
1224
  * title/subtitle/`headerActions` row, within the same header block — for a
1222
1225
  * host-supplied header (projectionDetail `slots.header`). */
1223
1226
  readonly headerContent?: ReactNode;
1224
- /** Rechtsbündiger Header-Slot (Range-Umschalter, Filter, Badge …). */
1227
+ /** Right-aligned header slot, for example a range switcher, filter, or badge. */
1225
1228
  readonly headerActions?: ReactNode;
1226
- /** Footer-Inhalt, abgehoben (border-t). Default rechtsbündig. */
1229
+ /** Footer content, separated by a top border; aligned to the right by default. */
1227
1230
  readonly footer?: ReactNode;
1228
1231
  };
1229
1232
  /** Card-Options — Verhalten/Optik, ebenfalls additiv erweiterbar. */
@@ -1232,12 +1235,12 @@ export type CardOptions = {
1232
1235
  readonly padded?: boolean;
1233
1236
  /** Eckenradius. Default "xl" (= Entity-Card). */
1234
1237
  readonly radius?: "lg" | "xl";
1235
- /** Footer abgehoben mit border-t + bg-muted/30. Default true. */
1238
+ /** Separates the footer with `border-t` and `bg-muted/30`. Defaults to true. */
1236
1239
  readonly footerBordered?: boolean;
1237
1240
  /** Footer horizontal padding. false = none, so footer content aligns with the card edge. Default true. */
1238
1241
  readonly footerInset?: boolean;
1239
- /** Joins the ancestor fillHeight flex chain (fw#2722/#2778 — a tabs-mode
1240
- * relatedList table sizing to the panel instead of the page) instead of
1242
+ /** Joins the ancestor fillHeight flex chain — a tabs-mode
1243
+ * relatedList table sizing to the panel instead of the page — instead of
1241
1244
  * normal document-flow height. Default false. */
1242
1245
  readonly fillHeight?: boolean;
1243
1246
  /** false drops border, background, shadow and radius but keeps padding and
@@ -1249,13 +1252,15 @@ export type CardOptions = {
1249
1252
  * shell edge. Default false. */
1250
1253
  readonly screenBody?: boolean;
1251
1254
  };
1252
- /** Generische Card-Chrome (border/bg/radius/shadow + Header/Body/Footer) als
1253
- * Primitive — eine Quelle für alle Card-Flächen, statt pro App nachgebaut.
1254
- * Slot- + Options-basiert, damit der Contract stabil bleibt. */
1255
+ /** Generic card chrome (border/background/radius/shadow plus
1256
+ * header/body/footer) as a primitive, providing one implementation for all
1257
+ * card surfaces. It is slot- and option-based so the contract can evolve
1258
+ * additively. */
1255
1259
  export type CardProps = {
1256
1260
  readonly slots?: CardSlots;
1257
1261
  readonly options?: CardOptions;
1258
- /** Body-Inhalt (Normalfall; Header/Footer kommen über slots). */
1262
+ /** Body content in the normal case; header and footer are supplied through
1263
+ * `slots`. */
1259
1264
  readonly children?: ReactNode;
1260
1265
  readonly className?: string;
1261
1266
  readonly testId?: string;
@@ -1320,8 +1325,8 @@ export type StepBarProps = {
1320
1325
  * native constraint-validation concept (web: `<fieldset disabled>`, spec-
1321
1326
  * guaranteed to also bar descendants from participating) must bar it too,
1322
1327
  * or a `type="submit"` step-advance button triggers the browser's
1323
- * full-form check against fields on unvisited, unfocusable steps
1324
- * (kumiko-framework#1976). Implementations without that concept can gate
1328
+ * full-form check against fields on unvisited, unfocusable steps.
1329
+ * Implementations without that concept can gate
1325
1330
  * on `hidden` alone. */
1326
1331
  export type WizardStepGroupProps = {
1327
1332
  readonly hidden: boolean;
@@ -1409,7 +1414,7 @@ export type PageHeaderProps = {
1409
1414
  * the route names, for a locked screen's fallback. Wins over `hideBreadcrumb`. */
1410
1415
  readonly breadcrumbScreenId?: string;
1411
1416
  };
1412
- /** One item in an `ActionOverflowMenu` (A7: header/row actions beyond the
1417
+ /** One item in an `ActionOverflowMenu` (header/row actions beyond the
1413
1418
  * primary one collapse into this menu instead of a wall of buttons). */
1414
1419
  export type ActionMenuItemSpec = {
1415
1420
  readonly id: string;
@@ -1427,7 +1432,7 @@ export type ActionOverflowMenuProps = {
1427
1432
  readonly label: string;
1428
1433
  readonly testId?: string;
1429
1434
  };
1430
- /** One revealed value on a `secretMint` screen's confirm phase (fw#2548). */
1435
+ /** One revealed value on a `secretMint` screen's confirm phase. */
1431
1436
  export type SecretRevealValue = {
1432
1437
  readonly label: string;
1433
1438
  readonly value: string;
@@ -1577,7 +1582,7 @@ export type CorePrimitives = {
1577
1582
  /** Optional — additive rollout, see StickyActionBar. */
1578
1583
  readonly MetricBand?: ComponentType<MetricBandProps>;
1579
1584
  /** Optional: without an implementation, callers with >2 header/row
1580
- * actions fall back to today's all-buttons-inline rendering (A7). */
1585
+ * actions fall back to today's all-buttons-inline rendering. */
1581
1586
  readonly ActionOverflowMenu?: ComponentType<ActionOverflowMenuProps>;
1582
1587
  /** Optional (unlike the other Core-Primitives) so existing partial
1583
1588
  * CorePrimitives mocks in tests keep compiling — additive rollout of
@@ -1594,8 +1599,8 @@ export type CorePrimitives = {
1594
1599
  /** Optional — additive rollout, see StickyActionBar. */
1595
1600
  readonly PromoPanel?: ComponentType<PromoPanelProps>;
1596
1601
  };
1597
- /** Offene Extension-Zone für App-eigene Primitives. Devs erweitern
1598
- * dieses Interface via TypeScript Module-Augmentation:
1602
+ /** Extension point for app-owned primitives. Developers extend this
1603
+ * interface through TypeScript module augmentation:
1599
1604
  *
1600
1605
  * declare module "@cosmicdrift/kumiko-renderer" {
1601
1606
  * interface AppPrimitives {
@@ -1604,12 +1609,11 @@ export type CorePrimitives = {
1604
1609
  * }
1605
1610
  * }
1606
1611
  *
1607
- * Nach der Augmentation tauchen die Keys in `PrimitivesRegistry`,
1608
- * `createKumikoApp({ primitives: { Chip, Accordion } })`, und
1609
- * `usePrimitives().Chip` auf — TypeScript-gestützt. Kumikos eigene
1610
- * Components nutzen ausschließlich `CorePrimitives`; App-Primitives
1611
- * sind ausschließlich für Dev-Code (Custom-Screens, Shell, eigene
1612
- * Components). */
1612
+ * After augmentation, the keys appear in `PrimitivesRegistry`,
1613
+ * `createKumikoApp({ primitives: { Chip, Accordion } })`, and
1614
+ * `usePrimitives().Chip` with TypeScript support. Kumiko's own components
1615
+ * use only `CorePrimitives`; app primitives are exclusively for developer
1616
+ * code such as custom screens, shells, and app-owned components. */
1613
1617
  export interface AppPrimitives {
1614
1618
  }
1615
1619
  export type PrimitivesRegistry = CorePrimitives & AppPrimitives;