@cosmicdrift/kumiko-renderer 0.386.0 → 0.388.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 (222) 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 +12 -4
  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/action-form-support.d.ts +1 -0
  7. package/dist/app/action-form-support.js +1 -1
  8. package/dist/app/app-features-context.js +4 -4
  9. package/dist/app/column-renderers.d.ts +7 -9
  10. package/dist/app/column-renderers.js +15 -18
  11. package/dist/app/config-edit-body.js +7 -3
  12. package/dist/app/config-edit-shim.d.ts +7 -8
  13. package/dist/app/config-edit-shim.js +18 -20
  14. package/dist/app/custom-screens.d.ts +3 -3
  15. package/dist/app/custom-screens.js +8 -8
  16. package/dist/app/dashboard-body.d.ts +2 -2
  17. package/dist/app/dashboard-body.js +2 -2
  18. package/dist/app/entity-edit-prefill.d.ts +11 -0
  19. package/dist/app/entity-edit-prefill.js +18 -13
  20. package/dist/app/entity-edit-screen.js +4 -4
  21. package/dist/app/entity-list-screen.js +3 -3
  22. package/dist/app/extension-form-submit.d.ts +1 -1
  23. package/dist/app/extension-form-submit.js +14 -15
  24. package/dist/app/extension-sections.d.ts +29 -28
  25. package/dist/app/extension-sections.js +10 -10
  26. package/dist/app/feature-schema.js +11 -11
  27. package/dist/app/form-schema.js +2 -2
  28. package/dist/app/header-actions-bar.js +2 -2
  29. package/dist/app/kumiko-screen.js +8 -10
  30. package/dist/app/layout-fields.js +1 -1
  31. package/dist/app/list-facets.js +3 -3
  32. package/dist/app/nav.d.ts +10 -10
  33. package/dist/app/nav.js +13 -13
  34. package/dist/app/projection-detail-body.js +4 -4
  35. package/dist/app/projection-detail-shim.d.ts +6 -5
  36. package/dist/app/projection-detail-shim.js +23 -22
  37. package/dist/app/projection-list-body.js +10 -10
  38. package/dist/app/projection-list-shim.d.ts +5 -5
  39. package/dist/app/projection-list-shim.js +17 -18
  40. package/dist/app/qn.js +10 -12
  41. package/dist/app/qualify-screen-id.js +1 -1
  42. package/dist/app/screen-access.js +2 -2
  43. package/dist/app/screen-shared.js +1 -1
  44. package/dist/app/secret-mint-body.js +4 -4
  45. package/dist/app/write-failed-error.js +8 -7
  46. package/dist/components/edit-mounts.d.ts +2 -2
  47. package/dist/components/embedded-list-field.js +5 -5
  48. package/dist/components/grid-cell-for-field.d.ts +1 -1
  49. package/dist/components/reference-create-dialog.js +3 -3
  50. package/dist/components/related-list-section.d.ts +1 -1
  51. package/dist/components/related-list-section.js +15 -2
  52. package/dist/components/render-edit-action-button.d.ts +1 -3
  53. package/dist/components/render-edit-action-button.js +2 -2
  54. package/dist/components/render-edit-logic.js +1 -1
  55. package/dist/components/render-edit-types.d.ts +6 -6
  56. package/dist/components/render-edit.js +74 -73
  57. package/dist/components/render-field.d.ts +7 -7
  58. package/dist/components/render-field.js +62 -61
  59. package/dist/components/render-list.d.ts +42 -43
  60. package/dist/components/render-list.js +93 -84
  61. package/dist/components/use-wizard-form-draft.js +10 -10
  62. package/dist/context/dispatcher-context.js +5 -5
  63. package/dist/context/user-roles-context.js +2 -2
  64. package/dist/flow-diagram/render.js +9 -1
  65. package/dist/headless/apex/index.d.ts +1 -2
  66. package/dist/headless/apex/index.js +5 -6
  67. package/dist/headless/form/form-controller.js +14 -6
  68. package/dist/headless/form/types.d.ts +1 -0
  69. package/dist/headless/form/zod-bridge.js +2 -2
  70. package/dist/headless/format/index.js +89 -64
  71. package/dist/headless/format/money.js +1 -1
  72. package/dist/headless/index.js +2 -2
  73. package/dist/headless/view-model/edit.js +4 -2
  74. package/dist/headless/view-model/list.js +184 -150
  75. package/dist/headless/view-model/types.d.ts +6 -3
  76. package/dist/hooks/live-refetch-scheduler.js +4 -0
  77. package/dist/hooks/reference-limits.js +11 -12
  78. package/dist/hooks/use-list-url-state.d.ts +13 -13
  79. package/dist/hooks/use-list-url-state.js +21 -22
  80. package/dist/hooks/use-query.js +3 -3
  81. package/dist/hooks/use-reference-lookup.d.ts +9 -1
  82. package/dist/hooks/use-reference-lookup.js +32 -19
  83. package/dist/hooks/use-store.js +18 -22
  84. package/dist/hooks/use-stream-handler.js +4 -2
  85. package/dist/i18n-defaults.js +6 -8
  86. package/dist/i18n.d.ts +10 -10
  87. package/dist/i18n.js +25 -25
  88. package/dist/index.d.ts +1 -0
  89. package/dist/index.js +6 -4
  90. package/dist/live/dispatcher-live.js +2 -2
  91. package/dist/primitives.d.ts +244 -221
  92. package/dist/primitives.js +1 -1
  93. package/dist/sse/live-events.d.ts +5 -5
  94. package/dist/sse/live-events.js +17 -17
  95. package/dist/tokens.d.ts +8 -8
  96. package/dist/tokens.js +16 -17
  97. package/dist/web/app/browser-locale.d.ts +9 -9
  98. package/dist/web/app/browser-locale.js +19 -19
  99. package/dist/web/app/client-plugin.d.ts +44 -52
  100. package/dist/web/app/client-plugin.js +4 -5
  101. package/dist/web/app/create-app.d.ts +28 -31
  102. package/dist/web/app/create-app.js +29 -25
  103. package/dist/web/app/dashboard-body.js +17 -1
  104. package/dist/web/app/dashboard-panels.js +12 -2
  105. package/dist/web/app/document-lang-sync.js +1 -1
  106. package/dist/web/app/draft-storage.js +1 -1
  107. package/dist/web/app/nav-providers-context.d.ts +3 -3
  108. package/dist/web/app/nav-providers-context.js +3 -3
  109. package/dist/web/app/nav.d.ts +18 -17
  110. package/dist/web/app/nav.js +66 -64
  111. package/dist/web/app/rich-content-editor.js +1 -1
  112. package/dist/web/components/config-cascade.js +17 -18
  113. package/dist/web/components/config-source-badge.js +4 -4
  114. package/dist/web/index.d.ts +1 -1
  115. package/dist/web/index.js +1 -1
  116. package/dist/web/layout/app-layout.d.ts +9 -9
  117. package/dist/web/layout/app-layout.js +2 -2
  118. package/dist/web/layout/avatar.d.ts +4 -3
  119. package/dist/web/layout/avatar.js +15 -17
  120. package/dist/web/layout/default-app-shell.d.ts +22 -23
  121. package/dist/web/layout/editor-panel.js +3 -3
  122. package/dist/web/layout/nav-tree.js +86 -84
  123. package/dist/web/layout/profile-menu.d.ts +1 -1
  124. package/dist/web/layout/shell-header.js +2 -1
  125. package/dist/web/layout/sidebar-brand.d.ts +7 -7
  126. package/dist/web/layout/sidebar-user.d.ts +1 -1
  127. package/dist/web/layout/sidebar-user.js +4 -4
  128. package/dist/web/layout/sidebar.d.ts +8 -8
  129. package/dist/web/layout/sidebar.js +7 -7
  130. package/dist/web/layout/target-resolver-stub.d.ts +5 -5
  131. package/dist/web/layout/target-resolver-stub.js +20 -20
  132. package/dist/web/layout/target-url.d.ts +7 -7
  133. package/dist/web/layout/target-url.js +22 -22
  134. package/dist/web/layout/theme-toggle.js +4 -5
  135. package/dist/web/layout/uniform-bar-controls.d.ts +1 -0
  136. package/dist/web/layout/uniform-bar-controls.js +4 -0
  137. package/dist/web/layout/workspace-shell.d.ts +21 -21
  138. package/dist/web/layout/workspace-shell.js +29 -30
  139. package/dist/web/lib/cn.d.ts +4 -4
  140. package/dist/web/lib/cn.js +4 -4
  141. package/dist/web/lib/file-ref-download.js +4 -1
  142. package/dist/web/primitives/action-menu.d.ts +8 -8
  143. package/dist/web/primitives/action-menu.js +14 -13
  144. package/dist/web/primitives/calendar-popover.d.ts +1 -1
  145. package/dist/web/primitives/calendar-popover.js +10 -10
  146. package/dist/web/primitives/card-styles.js +1 -1
  147. package/dist/web/primitives/card.js +2 -2
  148. package/dist/web/primitives/code-input.d.ts +6 -0
  149. package/dist/web/primitives/code-input.js +31 -0
  150. package/dist/web/primitives/combobox.d.ts +12 -12
  151. package/dist/web/primitives/combobox.js +34 -35
  152. package/dist/web/primitives/data-table-cell.js +2 -2
  153. package/dist/web/primitives/data-table.js +6 -6
  154. package/dist/web/primitives/date-field.js +2 -2
  155. package/dist/web/primitives/date-input.js +4 -4
  156. package/dist/web/primitives/default-form.js +2 -2
  157. package/dist/web/primitives/dropdown-menu.js +14 -15
  158. package/dist/web/primitives/embedded-list-input.js +3 -3
  159. package/dist/web/primitives/facet-filter.js +2 -2
  160. package/dist/web/primitives/field.js +9 -8
  161. package/dist/web/primitives/file-thumbnail.d.ts +3 -0
  162. package/dist/web/primitives/file-thumbnail.js +5 -0
  163. package/dist/web/primitives/form.d.ts +1 -1
  164. package/dist/web/primitives/form.js +6 -6
  165. package/dist/web/primitives/input.js +8 -3
  166. package/dist/web/primitives/layout-elements.js +7 -5
  167. package/dist/web/primitives/layout.js +2 -2
  168. package/dist/web/primitives/located-timestamp-input.js +4 -4
  169. package/dist/web/primitives/money-input.js +13 -17
  170. package/dist/web/primitives/page-header.js +3 -1
  171. package/dist/web/primitives/pager.js +1 -1
  172. package/dist/web/primitives/row-actions.js +6 -7
  173. package/dist/web/primitives/secret-reveal.js +1 -1
  174. package/dist/web/primitives/timestamp-input.d.ts +9 -9
  175. package/dist/web/primitives/timestamp-input.js +21 -21
  176. package/dist/web/primitives/toast.js +12 -13
  177. package/dist/web/primitives/use-focus-on-mount.d.ts +2 -0
  178. package/dist/web/primitives/use-focus-on-mount.js +9 -0
  179. package/dist/web/sse/live-events.d.ts +6 -6
  180. package/dist/web/sse/live-events.js +3 -3
  181. package/dist/web/tokens.d.ts +11 -12
  182. package/dist/web/tokens.js +35 -37
  183. package/dist/web/version/update-checker.js +14 -13
  184. package/dist/web/widgets/ai-text-field.js +1 -1
  185. package/dist/web/widgets/bounded-load-cache.d.ts +1 -1
  186. package/dist/web/widgets/bounded-load-cache.js +10 -4
  187. package/dist/web/widgets/charts.d.ts +10 -10
  188. package/dist/web/widgets/charts.js +8 -8
  189. package/dist/web/widgets/choice-cards.d.ts +17 -0
  190. package/dist/web/widgets/choice-cards.js +12 -0
  191. package/dist/web/widgets/collapsible-section.d.ts +3 -3
  192. package/dist/web/widgets/collapsible-section.js +7 -7
  193. package/dist/web/widgets/detail-list.d.ts +5 -3
  194. package/dist/web/widgets/detail-list.js +5 -3
  195. package/dist/web/widgets/feed-list.d.ts +2 -2
  196. package/dist/web/widgets/feed-list.js +2 -2
  197. package/dist/web/widgets/form-fields.d.ts +14 -14
  198. package/dist/web/widgets/form-fields.js +14 -13
  199. package/dist/web/widgets/index.d.ts +1 -0
  200. package/dist/web/widgets/index.js +5 -4
  201. package/dist/web/widgets/infinity-list.js +3 -3
  202. package/dist/web/widgets/mode-switch.d.ts +2 -2
  203. package/dist/web/widgets/mode-switch.js +2 -2
  204. package/dist/web/widgets/pdf-preview.js +1 -1
  205. package/dist/web/widgets/plan-card.d.ts +2 -0
  206. package/dist/web/widgets/plan-card.js +1 -1
  207. package/dist/web/widgets/progress-list.d.ts +1 -2
  208. package/dist/web/widgets/progress-list.js +1 -2
  209. package/dist/web/widgets/query-table.d.ts +6 -6
  210. package/dist/web/widgets/result-panel.d.ts +16 -16
  211. package/dist/web/widgets/result-panel.js +10 -10
  212. package/dist/web/widgets/section-card.d.ts +3 -3
  213. package/dist/web/widgets/section-card.js +3 -3
  214. package/dist/web/widgets/stat.d.ts +4 -4
  215. package/dist/web/widgets/stat.js +6 -6
  216. package/dist/web/widgets/states.d.ts +4 -4
  217. package/dist/web/widgets/states.js +4 -4
  218. package/dist/web/widgets/upload-zone.js +13 -5
  219. package/dist/web/widgets/use-draft.d.ts +7 -7
  220. package/dist/web/widgets/use-draft.js +7 -7
  221. package/package.json +6 -6
  222. package/src/changes.json +49 -0
@@ -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 = {
@@ -202,20 +203,21 @@ export type InputProps = {
202
203
  readonly name: string;
203
204
  readonly value: string;
204
205
  readonly onChange: (v: string) => void;
206
+ readonly autoFocus?: boolean;
205
207
  readonly disabled?: boolean;
206
208
  readonly required?: boolean;
207
209
  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. */
210
+ /** Hint text shown when the field is empty. Used for search inputs in
211
+ * toolbars ("Suchen…") where no label is practical. */
210
212
  readonly placeholder?: string;
211
- /** Browser-Autofill / Native-Keyboard-Hint. Web setzt das auf
212
- * `<input autocomplete=...>`, Native auf `textContentType`. */
213
+ /** Browser autofill / native keyboard hint. Web applies this to
214
+ * `<input autocomplete=...>`; native applies it to `textContentType`. */
213
215
  readonly autoComplete?: string;
214
- /** `data-testid` aufs echte `<input>` — App-Tests adressieren das
215
- * Element direkt (getByTestId + readOnly/disabled-Assertions). */
216
+ /** Sets `data-testid` on the actual `<input>` so app tests can address the
217
+ * element directly (getByTestId + readOnly/disabled assertions). */
216
218
  readonly testId?: string;
217
- /** Read-only Input (z.B. gewürfelter Free-Tier-Slug). Nicht `disabled`
218
- * — bleibt fokussier-/kopierbar. */
219
+ /** Read-only input (for example, a generated free-tier slug). Not
220
+ * `disabled`, so it remains focusable and copyable. */
219
221
  readonly readOnly?: boolean;
220
222
  /** Closed FieldIconKey vocabulary (FIELD_ICONS registry, renderer/web). */
221
223
  readonly icon?: FieldIconKey;
@@ -227,6 +229,24 @@ export type InputProps = {
227
229
  /** Raw keydown access (e.g. Enter-to-submit at the field instead of
228
230
  * the surrounding Form). Forwarded to the element. */
229
231
  readonly onKeyDown?: (event: KeyboardEvent<HTMLInputElement>) => void;
232
+ } | {
233
+ /** Fixed-length numeric one-time code. `onComplete` fires once when input,
234
+ * paste or autofill brings the value to full length; the value holds
235
+ * digits only. */
236
+ readonly kind: "code";
237
+ readonly id: string;
238
+ readonly name: string;
239
+ readonly value: string;
240
+ readonly onChange: (v: string) => void;
241
+ readonly length: number;
242
+ readonly onComplete?: (code: string) => void;
243
+ readonly autoFocus?: boolean;
244
+ readonly disabled?: boolean;
245
+ readonly required?: boolean;
246
+ readonly hasError?: boolean;
247
+ readonly testId?: string;
248
+ /** Default "one-time-code". */
249
+ readonly autoComplete?: string;
230
250
  } | {
231
251
  readonly kind: "email";
232
252
  readonly id: string;
@@ -237,8 +257,8 @@ export type InputProps = {
237
257
  readonly required?: boolean;
238
258
  readonly hasError?: boolean;
239
259
  readonly placeholder?: string;
240
- /** Default "email". Apps die "username" wollen (Login-Form mit
241
- * Username-or-Email) reichen das durch. */
260
+ /** Default "email". Apps that need "username" (a username-or-email login
261
+ * form) pass that value through. */
242
262
  readonly autoComplete?: string;
243
263
  readonly testId?: string;
244
264
  } | {
@@ -263,9 +283,9 @@ export type InputProps = {
263
283
  readonly hasError?: boolean;
264
284
  /** Hint text shown when the field is empty (analog to kind:"text"). */
265
285
  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". */
286
+ /** "current-password" for login, "new-password" for reset/signup—browser
287
+ * password managers use this to decide whether to save the value.
288
+ * Native uses textContentType="password" / "newPassword". */
269
289
  readonly autoComplete?: "current-password" | "new-password";
270
290
  /** Icon button inside the field's trailing edge (clear a stored value,
271
291
  * undo a pending removal). `label` is its accessible name + tooltip. */
@@ -326,7 +346,7 @@ export type InputProps = {
326
346
  readonly kind: "file" | "image";
327
347
  readonly id: string;
328
348
  readonly name: string;
329
- /** FileRef-UUID (gespeicherte Datei) oder null (kein Upload). */
349
+ /** FileRef UUID (stored file) or null (no upload). */
330
350
  readonly value: string | null;
331
351
  /** Implementations must offer their own remove control that calls
332
352
  * `onChange(null)`: FileListField relies on it to drop a file from an
@@ -338,8 +358,8 @@ export type InputProps = {
338
358
  /** Erlaubte Extensions/MIME (z.B. ["jpg","png"]) → `accept`-Attribut. */
339
359
  readonly accept?: readonly string[];
340
360
  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). */
361
+ /** For the upload POST `/api/files`: the endpoint validates maxSize/accept
362
+ * against the field definition for (entityType, fieldName). */
343
363
  readonly entityType?: string;
344
364
  /** Screen QN of the actionForm the field belongs to; sent as `actionForm` instead of
345
365
  * `entityType`, so the upload stays pending for that form's field. */
@@ -388,9 +408,9 @@ export type InputProps = {
388
408
  readonly name: string;
389
409
  readonly value: string;
390
410
  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. */
411
+ /** Locale for display and input parsing. Default = browser locale via
412
+ * navigator.language. Apps with their own LocaleResolver can pass through
413
+ * their current locale. */
394
414
  readonly locale?: string;
395
415
  /** Datumsgrenzen als ISO `yyyy-mm-dd` — begrenzen Picker (Jahres-
396
416
  * Dropdown-Range + ausgegraute Tage). */
@@ -405,10 +425,10 @@ export type InputProps = {
405
425
  readonly name: string;
406
426
  readonly value: string;
407
427
  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). */
428
+ /** Allowed values. An empty array produces a dropdown with no options, so a
429
+ * required field cannot be satisfied (an author warning, not an end-user
430
+ * message). The string form is for static lists (value == label); the
431
+ * {value,label} form is for database-backed references. */
412
432
  readonly options: readonly string[] | readonly {
413
433
  readonly value: string;
414
434
  readonly label: string;
@@ -450,14 +470,14 @@ export type InputProps = {
450
470
  readonly placeholder?: string;
451
471
  readonly searchPlaceholder?: string;
452
472
  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). */
473
+ /** Remote search: when set, the combobox uses remote mode (local cmdk
474
+ * filtering is disabled and the search input calls the caller's
475
+ * debounced onSearchChange). */
456
476
  readonly onSearchChange?: (q: string) => void;
457
477
  readonly loading?: boolean;
458
478
  /** Fixed "+ createLabel" footer row that fires instead of
459
479
  * selecting an option — omit for the plain select-existing
460
- * combobox (kumiko-framework#1681). */
480
+ * combobox. */
461
481
  readonly onCreate?: () => void;
462
482
  readonly createLabel?: string;
463
483
  } & ({
@@ -472,18 +492,18 @@ export type InputProps = {
472
492
  readonly kind: "money";
473
493
  readonly id: string;
474
494
  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 = `""`. */
495
+ /** Internal format: cents/minor units as an integer (for example, 1299 for
496
+ * 12,99 EUR). The renderer converts this to major units for the UI with
497
+ * locale-formatted decimal places. Empty state = `""`. */
478
498
  readonly value: number | "";
479
499
  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). */
500
+ /** ISO-4217 currency code, for example "EUR" / "USD" / "CHF". Default
501
+ * "EUR". The renderer displays the symbol as a suffix and formats the
502
+ * decimal places accordingly (EUR/USD/CHF use 2; JPY uses 0). */
483
503
  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. */
504
+ /** Locale for number formatting. Default "de-DE" (comma as the decimal
505
+ * separator). Apps with their own LocaleResolver can pass through their
506
+ * current locale. */
487
507
  readonly locale?: string;
488
508
  readonly disabled?: boolean;
489
509
  readonly required?: boolean;
@@ -492,22 +512,22 @@ export type InputProps = {
492
512
  readonly kind: "timestamp";
493
513
  readonly id: string;
494
514
  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. */
515
+ /** ISO-8601 datetime string. A UTC instant uses `Z`
516
+ * ("2026-04-25T13:45:00Z"); a wall-clock value has no offset
517
+ * ("2026-04-25T13:45", only with wallClock). Empty state = `""`.
518
+ * Web renders separate date (typable + calendar) and time inputs and
519
+ * converts the result. */
500
520
  readonly value: string;
501
521
  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. */
522
+ /** true = locatedTimestamp (wall-clock without an offset; the server
523
+ * validates z.iso.datetime({local:true})). false/undefined = a UTC
524
+ * instant; onChange MUST emit a `Z` suffix. */
505
525
  readonly wallClock?: boolean;
506
- /** Locale für Anzeige UND Eingabe-Parsing des Datums-Teils.
507
- * Default = Browser-Locale. */
526
+ /** Locale for displaying and parsing the date portion. Default = browser
527
+ * locale. */
508
528
  readonly locale?: string;
509
- /** Datumsgrenzen als ISO-Datetime — begrenzen den Kalender auf
510
- * Tages-Granularität. */
529
+ /** Date bounds as ISO datetimes; they constrain the calendar to day-level
530
+ * granularity. */
511
531
  readonly min?: string;
512
532
  readonly max?: string;
513
533
  readonly disabled?: boolean;
@@ -517,9 +537,9 @@ export type InputProps = {
517
537
  readonly kind: "locatedTimestamp";
518
538
  readonly id: string;
519
539
  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 }`. */
540
+ /** Located-timestamp value: wall-clock `at` (without an offset) plus an IANA
541
+ * `tz`. `utc` is present when reading (computed by the server) and ignored
542
+ * when writing. Empty state = `""`. onChange emits `{ at, tz }`. */
523
543
  readonly value: {
524
544
  readonly at: string;
525
545
  readonly tz: string;
@@ -529,9 +549,9 @@ export type InputProps = {
529
549
  readonly at: string;
530
550
  readonly tz: string;
531
551
  } | undefined) => void;
532
- /** Locale für den Datums-Teil. Default = Browser-Locale. */
552
+ /** Locale for the date portion. Default = browser locale. */
533
553
  readonly locale?: string;
534
- /** Datumsgrenzen als ISO-Datetime — begrenzen den Kalender. */
554
+ /** Date bounds as ISO datetimes; they constrain the calendar. */
535
555
  readonly min?: string;
536
556
  readonly max?: string;
537
557
  readonly disabled?: boolean;
@@ -555,15 +575,15 @@ export type InputProps = {
555
575
  readonly onChange: (v: string) => void;
556
576
  /** Visible rows. Acts as a minimum height — the field starts this tall
557
577
  * and still grows with its content. Omitted, the default primitive
558
- * falls back to its own floor (#2677). */
578
+ * falls back to its own floor. */
559
579
  readonly rows?: number;
560
580
  readonly disabled?: boolean;
561
581
  readonly required?: boolean;
562
582
  readonly hasError?: boolean;
563
583
  /** Hint text shown when the field is empty (analog to kind:"text"). */
564
584
  readonly placeholder?: string;
565
- /** Read-only Textarea. Nicht `disabled` — bleibt fokussier-/
566
- * kopierbar (analog zu kind:"text"). */
585
+ /** Read-only Textarea. Not `disabled` — remains focusable and
586
+ * copyable (analogous to kind:"text"). */
567
587
  readonly readOnly?: boolean;
568
588
  /** Ctrl/Cmd+Enter submits instead of inserting a newline. Plain
569
589
  * Enter still inserts a newline. */
@@ -582,7 +602,7 @@ export type DataTableSort = {
582
602
  };
583
603
  export type DataTableRowActionMode = "adaptive" | "inline";
584
604
  export type DataTableRowAction = {
585
- /** Stable id für aria-labels und data-testids. */
605
+ /** Stable ID for aria-labels and data-testids. */
586
606
  readonly id: string;
587
607
  /** Translated Label. */
588
608
  readonly label: string;
@@ -590,22 +610,22 @@ export type DataTableRowAction = {
590
610
  * primitive. Whether it also forces a confirm dialog is governed by
591
611
  * `confirmRequired` (falls back to `style === "danger"` when unset). */
592
612
  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. */
613
+ /** Translated confirmation prompt (dialog description). When set, a modal
614
+ * opens before execution. For style=danger without an explicit confirm,
615
+ * the renderer should show a generic default. */
596
616
  readonly confirm?: string;
597
- /** Translated Confirm-Button-Label im Dialog. Default = `label`
598
- * (Action-Label wird wiederverwendet). */
617
+ /** Translated confirm-button label in the dialog. Defaults to `label`
618
+ * (the action label is reused). */
599
619
  readonly confirmLabel?: string;
600
620
  /** Overrides the default "danger implies a confirm dialog" rule. Schema-driven
601
621
  * navigate/drawer actions set it to false: the colour marks the action as
602
622
  * destructive, but the target form is the confirmation. */
603
623
  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. */
624
+ /** Called with the clicked row's `ListRowViewModel`. May be async, allowing
625
+ * the renderer to show a loading state on the button while the promise resolves. */
607
626
  readonly onTrigger: (row: ListRowViewModel) => Promise<void> | void;
608
- /** Conditional Visibility pro Row (z.B. "Start" nur wenn status==="scheduled"). */
627
+ /** Controls per-row visibility (e.g. show "Start" only when
628
+ * `status==="scheduled"`). */
609
629
  readonly isVisible?: (row: ListRowViewModel) => boolean;
610
630
  /** This action is what a click on the row does. With `onRowClick` the table
611
631
  * (except `"inline"` mode or editable cells) and the card layout drop it
@@ -632,8 +652,8 @@ export type DataTableFacet = {
632
652
  readonly field: string;
633
653
  /** Translated Button-Label ("Status", "Active"). */
634
654
  readonly label: string;
635
- /** Auswählbare Werte mit Anzeige-Label. value = roher Filterwert
636
- * (select-option / "true"|"false"), als String transportiert. */
655
+ /** Selectable values with display labels. `value` is the raw filter value
656
+ * (a select option or `"true"`/`"false"`), transported as a string. */
637
657
  readonly options: readonly {
638
658
  readonly value: string;
639
659
  readonly label: string;
@@ -679,20 +699,20 @@ export type DataTableProps = {
679
699
  /** Tints a row. Renderers without row tinting ignore it. */
680
700
  readonly rowTone?: (row: ListRowViewModel) => SelectOptionTone | undefined;
681
701
  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. */
702
+ /** Current sorting, or null when unsorted. If columns contain a
703
+ * `sortable: true` field and `onSortChange` is set, the renderer makes
704
+ * headers clickable and shows ascending/descending indicators. Without
705
+ * `onSortChange`, header clicks remain disabled and `columns.sortable` is
706
+ * only semantic metadata. */
687
707
  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. */
708
+ /** Called with the next sort state after a header click.
709
+ * The conventional three-state cycle is asc → desc → null. The caller
710
+ * uses it to update URL state or a query parameter and refetch. */
691
711
  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. */
712
+ /** Per-row actions rendered in a right-hand column as inline buttons
713
+ * (≤2 actions) or a kebab dropdown (>2). The caller supplies the resolved
714
+ * form, with translated labels and wired `onTrigger`; DataTable handles
715
+ * only rendering and the confirmation dialog. */
696
716
  readonly rowActions?: readonly DataTableRowAction[];
697
717
  /** How the row-action column renders:
698
718
  * - `"adaptive"` (default): with `onRowClick` all actions except the
@@ -703,45 +723,46 @@ export type DataTableProps = {
703
723
  * kebab). Actions then sit at the same x-position across all rows
704
724
  * (no drifting with label width) and all lists in an app look alike. */
705
725
  readonly rowActionMode?: DataTableRowActionMode;
706
- /** Custom Empty-State-Inhalt (z. B. Icon + Heading + CTA-Button).
707
- * Default-Renderer rahmt ihn in einer dashed-border Box. */
726
+ /** Custom empty-state content (e.g. Icon + Heading + CTA-Button).
727
+ * The default renderer wraps it in a dashed-border box. */
708
728
  readonly emptyState?: ReactNode;
709
- /** Optionaler Titel-Slot ganz links der Toolbar — Screen-Titel
710
- * ("Items", "Bestellungen"). Web rendert als font-medium Heading. */
729
+ /** Optional title slot at the left of the toolbar, typically the screen
730
+ * title ("Items", "Bestellungen"). The web renderer uses a medium-weight heading. */
711
731
  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. */
732
+ /** Toolbar slot in the middle, typically a search input. The renderer
733
+ * controls the layout; the web implementation gives it `flex-1` with a
734
+ * maximum width, while native implementations may use a header search bar. */
715
735
  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. */
736
+ /** Toolbar slot on the right, typically for a + New button, filters, or a
737
+ * view switch. The web implementation moves the cluster to the right edge
738
+ * with `ml-auto`. */
718
739
  readonly toolbarEnd?: ReactNode;
719
- /** Faceted-Filter-Dropdowns (select/boolean-Felder) — der Renderer
720
- * setzt sie neben das Search-Input. Leer/undefined → keine Filter. */
740
+ /** Faceted filter dropdowns for select/boolean fields. The renderer places
741
+ * them beside the search input; an empty or undefined value renders no filters. */
721
742
  readonly filterFacets?: readonly DataTableFacet[];
722
- /** Aktuell ausgewählte Werte je Facet-Feld (vom URL-State). */
743
+ /** Currently selected values for each facet field, supplied by URL state. */
723
744
  readonly filterValues?: Readonly<Record<string, readonly string[]>>;
724
- /** Toggle eines Facet-Werts → Caller schreibt URL-State + refetch. */
745
+ /** Toggling a facet value: the caller updates URL state and refetches. */
725
746
  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. */
747
+ /** Clears all active facets. The renderer shows the reset button only when
748
+ * at least one facet is active. */
728
749
  readonly onFilterReset?: () => void;
729
- /** Time-range filters beside the facet dropdowns (fw#3104). */
750
+ /** Time-range filters beside the facet dropdowns. */
730
751
  readonly dateRangeFacets?: readonly DataTableDateRangeFacet[];
731
752
  /** One bound changed ("" clears it) → caller writes URL state + refetch. */
732
753
  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. */
754
+ /** Pagination state and callback. When set, the renderer shows a pager below
755
+ * the table (web: a footer bar with ← 1 ... N →). `total`, `limit`, and
756
+ * `page` are one-based for the UI; the caller translates them to
757
+ * `offset = (page-1)*limit` for the server. The caller should clamp a page
758
+ * greater than `total/limit` beforehand. */
738
759
  readonly pager?: {
739
760
  readonly page: number;
740
761
  readonly limit: number;
741
762
  readonly total: number;
742
763
  readonly onPageChange: (next: number) => void;
743
764
  /** Offered page sizes; with `onPageSizeChange` the footer shows a
744
- * "N pro Seite" select. */
765
+ * "N per page" select. */
745
766
  readonly pageSizeOptions?: readonly number[];
746
767
  readonly onPageSizeChange?: (next: number) => void;
747
768
  };
@@ -750,16 +771,16 @@ export type DataTableProps = {
750
771
  /** Noun for the footer count ("1–19 of 19 leases", "1 position"),
751
772
  * resolved for the given count. Omitted = generic "entries" wording. */
752
773
  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. */
774
+ /** Infinite-scroll callback. When set, the renderer adds a bottom sentinel
775
+ * and calls `onReachEnd` when it enters the viewport (web:
776
+ * `IntersectionObserver`). The caller manages accumulation, the cursor, and
777
+ * `hasMore`. `loadingMore=true` shows a spinner below the table while the
778
+ * next page loads. Incompatible with `pager`: use either pagination or
779
+ * infinite scrolling, not both. */
759
780
  readonly onReachEnd?: () => void;
760
781
  readonly loadingMore?: boolean;
761
- /** Wenn false und onReachEnd gesetzt, rendert der Renderer einen
762
- * "Ende der Liste"-Hinweis statt des Sentinels. Default true. */
782
+ /** When false and `onReachEnd` is set, the renderer shows an end-of-list
783
+ * message instead of the sentinel. Defaults to true. */
763
784
  readonly hasMore?: boolean;
764
785
  readonly testId?: string;
765
786
  /** Wires an edit path into a component column-renderer: `DataTableCell`
@@ -775,14 +796,14 @@ export type DataTableProps = {
775
796
  /** Overrides the cell `data-testid` (default `cell-${row.id}-${field}`). */
776
797
  readonly getCellTestId?: (row: ListRowViewModel, field: string) => string;
777
798
  /** Drops the table's own rounded/border/card background — for a host
778
- * that already provides the surrounding boundary (a tab panel, fw#2722)
799
+ * that already provides the surrounding boundary (a tab panel)
779
800
  * and would otherwise show a card nested inside that boundary. Default
780
801
  * false: unchanged card-framed table. */
781
802
  readonly chromeless?: boolean;
782
803
  /** Fills the available height of its flex container and scrolls rows
783
804
  * internally instead of growing with row count — for a host that would
784
805
  * 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
806
+ * leave dead space below it (a tab panel). Requires the same
786
807
  * flex-fill chain FormProps.fillHeight sets up above it; without that
787
808
  * ancestor chain this collapses to zero height (Web: `flex-1 min-h-0`
788
809
  * has no effect outside a sized flex-col ancestor). When true the table
@@ -795,7 +816,7 @@ export type DataTableProps = {
795
816
  /** Uses the shared screen padding (wider bottom inset) instead of the
796
817
  * table's symmetric embedded inset — for a table that IS the screen body
797
818
  * (entityList/projectionList), so a list screen ends at the same footer
798
- * distance as a form or custom screen (fw#2640). Ignored when
819
+ * distance as a form or custom screen. Ignored when
799
820
  * `scrollBody` is true: the wider bottom inset would compete with
800
821
  * `scrollBody`'s flex-fill height budget.
801
822
  * Default false: unchanged symmetric inset for an embedded table. */
@@ -921,8 +942,9 @@ export type FormProps = {
921
942
  readonly focusFirstErrorKey?: number;
922
943
  readonly children: ReactNode;
923
944
  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. */
945
+ /** Optional muted subtitle below the title (e.g. "Add a new item to
946
+ * your catalog"), providing context in the form header rather than only
947
+ * a label. */
926
948
  readonly subtitle?: ReactNode;
927
949
  readonly actions?: ReactNode;
928
950
  /** Secondary, record-related actions — rendered on the left on desktop,
@@ -934,7 +956,7 @@ export type FormProps = {
934
956
  readonly width?: FormWidth;
935
957
  /** Pins `actions` to the viewport bottom on narrow screens (`<640px`)
936
958
  * instead of normal document flow, so it stays reachable when a virtual
937
- * keyboard shrinks the visible viewport (fw#1918). Desktop/tablet
959
+ * keyboard shrinks the visible viewport. Desktop/tablet
938
960
  * unaffected. */
939
961
  readonly stickyActions?: boolean;
940
962
  /** Extra content rendered above the card, inside the same page padding
@@ -948,7 +970,7 @@ export type FormProps = {
948
970
  /** Sizes the form to fill its container's height (instead of the page's
949
971
  * natural content height) so a single scrolling child — a lone
950
972
  * relatedList tab's table — can scroll internally instead of stretching
951
- * the whole page (fw#2722). Set by RenderEdit for a lone relatedList tab
973
+ * the whole page. Set by RenderEdit for a lone relatedList tab
952
974
  * section and for screens that fill the shell height (together with
953
975
  * `stickyActions`: sections scroll, footer stays pinned without
954
976
  * `position: fixed`); every other caller leaves it unset and keeps normal
@@ -982,18 +1004,18 @@ export type FormProps = {
982
1004
  * navigation). */
983
1005
  readonly sideRail?: ReactNode;
984
1006
  };
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. */
1007
+ /** Titled group of fields. Web: `<fieldset>` + `<legend>`; native
1008
+ * implementations use a `View` with header text and may render the title as
1009
+ * an accordion or collapsible section. */
988
1010
  export type SectionProps = {
989
1011
  /** DOM id — the scroll target of a form's "on this page" navigation. */
990
1012
  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. */
1013
+ /** Optional; without a title, the Section renders only the grouping and no
1014
+ * header. RenderEdit omits it when it would exactly repeat the screen title
1015
+ * in the action bar. */
994
1016
  readonly title?: string;
995
- /** Optionaler muted Untertitel unter dem Titel — gibt der Section
996
- * Kontext statt nur ein Label (analog FormProps.subtitle). */
1017
+ /** Optional muted subtitle below the title, providing section context rather
1018
+ * than only a label (analogous to `FormProps.subtitle`). */
997
1019
  readonly subtitle?: ReactNode;
998
1020
  readonly children: ReactNode;
999
1021
  /** Rendered top-right in the title row — never a footer. A
@@ -1016,7 +1038,7 @@ export type SectionProps = {
1016
1038
  /** Chromeless flex-fill layout host — no title, no card frame, no padding,
1017
1039
  * just a container that sizes to its content and, once the ancestor chain
1018
1040
  * is height-constrained, shrinks so one scrolling child can scroll
1019
- * internally instead of the page growing (fw#2722, height fw#2778). Web:
1041
+ * internally instead of the page growing. Web:
1020
1042
  * `<div className="flex min-h-0 flex-col">`. Native: Views are
1021
1043
  * already flex-column and the parent is already a bounded viewport there,
1022
1044
  * so a native impl may render this as a bare Fragment. Used only as the
@@ -1033,15 +1055,15 @@ export type FillContainerProps = {
1033
1055
  * height. */
1034
1056
  readonly grow?: boolean;
1035
1057
  };
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.
1058
+ /** Column-based layout. Web uses CSS grid; native implementations use
1059
+ * flex-wrap with percentages or react-native-grid. Direct children can be
1060
+ * wrapped in `GridCell` to control their span.
1039
1061
  *
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. */
1062
+ * `columns: "auto"` disables the N-column stretch layout: children retain
1063
+ * their content width and wrap onto new lines (for example, self-sized
1064
+ * metric tiles or an inline label-and-badge row) instead of stretching into
1065
+ * equal-width tracks. `GridCell` span wrapping is meaningless in this mode
1066
+ * and should be skipped. */
1045
1067
  export type GridProps = {
1046
1068
  readonly columns: number | "auto";
1047
1069
  readonly children: ReactNode;
@@ -1063,19 +1085,19 @@ export type GridProps = {
1063
1085
  };
1064
1086
  /** Width class of a field cell inside a `flow` grid: sized by what the field holds. */
1065
1087
  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. */
1088
+ /** Span wrapper for a child within a grid. Web: `style={{gridColumn: span N}}`;
1089
+ * native implementations calculate the width themselves. */
1068
1090
  export type GridCellProps = {
1069
1091
  readonly span?: number;
1070
1092
  /** Only read inside a `flow` grid. */
1071
1093
  readonly width?: FieldCellWidth;
1072
1094
  readonly children: ReactNode;
1073
1095
  };
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. */
1096
+ /** Semantic text. Variants map to standard typography roles: `body` is
1097
+ * the default, `small` for secondary labels, `code` for inline monospace
1098
+ * (entityId, screen-id), `required-mark` for the asterisk after labels,
1099
+ * `muted` for dimmed body text (text-sm muted-foreground). Custom impls map
1100
+ * these to their own type scale. */
1079
1101
  export type TextProps = {
1080
1102
  readonly variant?: "body" | "small" | "code" | "required-mark" | "muted";
1081
1103
  /** Pure ornament (e.g. a separator glyph): hidden from assistive tech. */
@@ -1083,30 +1105,31 @@ export type TextProps = {
1083
1105
  readonly children: ReactNode;
1084
1106
  readonly testId?: string;
1085
1107
  };
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. */
1108
+ /** Heading with two roles: `page` is a page title (web: h1), while `section`
1109
+ * is a group subheading (web: h2 with uppercase and muted styling). Native
1110
+ * implementations map these to `<Text>` with the corresponding font weight
1111
+ * and size. */
1089
1112
  export type HeadingProps = {
1090
1113
  readonly variant?: "page" | "section";
1091
1114
  readonly children: ReactNode;
1092
1115
  readonly testId?: string;
1093
1116
  };
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)`.
1117
+ /** Modal dialog for confirmations or compact sub-forms. The web renderer
1118
+ * uses Radix Dialog with focus trapping, Escape-to-close, and overlay-click
1119
+ * handling; native implementations may use a native modal. Apps control it
1120
+ * through external state (`open` + `onOpenChange`); confirmation runs through
1121
+ * `onConfirm`, while cancellation closes it via `onOpenChange(false)`.
1099
1122
  *
1100
- * Variant `danger` markiert destruktive Bestätigungen visuell
1101
- * (rote Confirm-Button-Klasse), `default` für neutrale Dialoge. */
1123
+ * Variant `danger` visually marks destructive confirmations with a red
1124
+ * confirm-button class; `default` is for neutral dialogs. */
1102
1125
  export type DialogProps = {
1103
1126
  readonly open: boolean;
1104
1127
  readonly onOpenChange: (open: boolean) => void;
1105
1128
  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."). */
1129
+ /** Optional description, typically a sentence explaining the consequence
1130
+ * ("This action cannot be undone."). */
1108
1131
  readonly description?: string;
1109
- /** Confirm-Button-Label. Default kommt aus i18n
1132
+ /** Confirm-button label. Defaults to the i18n value
1110
1133
  * (`kumiko.dialog.confirm`). */
1111
1134
  readonly confirmLabel?: string;
1112
1135
  /** Cancel-Button-Label. Default `kumiko.dialog.cancel`. */
@@ -1118,12 +1141,11 @@ export type DialogProps = {
1118
1141
  * `children` are present. `"cancel"` for
1119
1142
  * dialogs where the safe choice must be the one Enter triggers. */
1120
1143
  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). */
1144
+ /** Called when the user confirms. Async functions are supported; the dialog
1145
+ * automatically sets a loading state and then calls `onOpenChange(false)`. */
1124
1146
  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). */
1147
+ /** Optional additional content between the description and buttons, such as
1148
+ * an input when the dialog also collects data. */
1127
1149
  readonly children?: ReactNode;
1128
1150
  readonly testId?: string;
1129
1151
  };
@@ -1144,7 +1166,7 @@ export type ModalProps = {
1144
1166
  /** Side panel for hosting an existing self-contained form/widget (own
1145
1167
  * submit/cancel buttons) without leaving the underlying screen — e.g. a
1146
1168
  * toolbar action's `kind: "drawer"` mounts an actionForm here instead of
1147
- * navigating to a full page (fw#2225). Same "bare content shell, no
1169
+ * navigating to a full page. Same "bare content shell, no
1148
1170
  * footer buttons of its own" contract as `Modal`, slide-in instead of
1149
1171
  * centered overlay. */
1150
1172
  export type DrawerProps = {
@@ -1209,10 +1231,10 @@ export type ConfigCascadeViewProps = {
1209
1231
  * value). Absent = the raw value; a missing value keeps the no-value text. */
1210
1232
  readonly renderValue?: (value: string | number | boolean) => ReactNode;
1211
1233
  };
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. */
1234
+ /** Card slots are named content regions, all optional. Adding slots is
1235
+ * additive, so consumers do not need migration. `header` is an escape hatch:
1236
+ * a raw node replaces the default header (`title`/`subtitle`/`headerActions`)
1237
+ * when the convenience slots are insufficient. */
1216
1238
  export type CardSlots = {
1217
1239
  readonly header?: ReactNode;
1218
1240
  readonly title?: ReactNode;
@@ -1221,9 +1243,9 @@ export type CardSlots = {
1221
1243
  * title/subtitle/`headerActions` row, within the same header block — for a
1222
1244
  * host-supplied header (projectionDetail `slots.header`). */
1223
1245
  readonly headerContent?: ReactNode;
1224
- /** Rechtsbündiger Header-Slot (Range-Umschalter, Filter, Badge …). */
1246
+ /** Right-aligned header slot, for example a range switcher, filter, or badge. */
1225
1247
  readonly headerActions?: ReactNode;
1226
- /** Footer-Inhalt, abgehoben (border-t). Default rechtsbündig. */
1248
+ /** Footer content, separated by a top border; aligned to the right by default. */
1227
1249
  readonly footer?: ReactNode;
1228
1250
  };
1229
1251
  /** Card-Options — Verhalten/Optik, ebenfalls additiv erweiterbar. */
@@ -1232,12 +1254,12 @@ export type CardOptions = {
1232
1254
  readonly padded?: boolean;
1233
1255
  /** Eckenradius. Default "xl" (= Entity-Card). */
1234
1256
  readonly radius?: "lg" | "xl";
1235
- /** Footer abgehoben mit border-t + bg-muted/30. Default true. */
1257
+ /** Separates the footer with `border-t` and `bg-muted/30`. Defaults to true. */
1236
1258
  readonly footerBordered?: boolean;
1237
1259
  /** Footer horizontal padding. false = none, so footer content aligns with the card edge. Default true. */
1238
1260
  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
1261
+ /** Joins the ancestor fillHeight flex chain — a tabs-mode
1262
+ * relatedList table sizing to the panel instead of the page — instead of
1241
1263
  * normal document-flow height. Default false. */
1242
1264
  readonly fillHeight?: boolean;
1243
1265
  /** false drops border, background, shadow and radius but keeps padding and
@@ -1249,13 +1271,15 @@ export type CardOptions = {
1249
1271
  * shell edge. Default false. */
1250
1272
  readonly screenBody?: boolean;
1251
1273
  };
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. */
1274
+ /** Generic card chrome (border/background/radius/shadow plus
1275
+ * header/body/footer) as a primitive, providing one implementation for all
1276
+ * card surfaces. It is slot- and option-based so the contract can evolve
1277
+ * additively. */
1255
1278
  export type CardProps = {
1256
1279
  readonly slots?: CardSlots;
1257
1280
  readonly options?: CardOptions;
1258
- /** Body-Inhalt (Normalfall; Header/Footer kommen über slots). */
1281
+ /** Body content in the normal case; header and footer are supplied through
1282
+ * `slots`. */
1259
1283
  readonly children?: ReactNode;
1260
1284
  readonly className?: string;
1261
1285
  readonly testId?: string;
@@ -1320,8 +1344,8 @@ export type StepBarProps = {
1320
1344
  * native constraint-validation concept (web: `<fieldset disabled>`, spec-
1321
1345
  * guaranteed to also bar descendants from participating) must bar it too,
1322
1346
  * 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
1347
+ * full-form check against fields on unvisited, unfocusable steps.
1348
+ * Implementations without that concept can gate
1325
1349
  * on `hidden` alone. */
1326
1350
  export type WizardStepGroupProps = {
1327
1351
  readonly hidden: boolean;
@@ -1409,7 +1433,7 @@ export type PageHeaderProps = {
1409
1433
  * the route names, for a locked screen's fallback. Wins over `hideBreadcrumb`. */
1410
1434
  readonly breadcrumbScreenId?: string;
1411
1435
  };
1412
- /** One item in an `ActionOverflowMenu` (A7: header/row actions beyond the
1436
+ /** One item in an `ActionOverflowMenu` (header/row actions beyond the
1413
1437
  * primary one collapse into this menu instead of a wall of buttons). */
1414
1438
  export type ActionMenuItemSpec = {
1415
1439
  readonly id: string;
@@ -1427,7 +1451,7 @@ export type ActionOverflowMenuProps = {
1427
1451
  readonly label: string;
1428
1452
  readonly testId?: string;
1429
1453
  };
1430
- /** One revealed value on a `secretMint` screen's confirm phase (fw#2548). */
1454
+ /** One revealed value on a `secretMint` screen's confirm phase. */
1431
1455
  export type SecretRevealValue = {
1432
1456
  readonly label: string;
1433
1457
  readonly value: string;
@@ -1577,7 +1601,7 @@ export type CorePrimitives = {
1577
1601
  /** Optional — additive rollout, see StickyActionBar. */
1578
1602
  readonly MetricBand?: ComponentType<MetricBandProps>;
1579
1603
  /** Optional: without an implementation, callers with >2 header/row
1580
- * actions fall back to today's all-buttons-inline rendering (A7). */
1604
+ * actions fall back to today's all-buttons-inline rendering. */
1581
1605
  readonly ActionOverflowMenu?: ComponentType<ActionOverflowMenuProps>;
1582
1606
  /** Optional (unlike the other Core-Primitives) so existing partial
1583
1607
  * CorePrimitives mocks in tests keep compiling — additive rollout of
@@ -1594,8 +1618,8 @@ export type CorePrimitives = {
1594
1618
  /** Optional — additive rollout, see StickyActionBar. */
1595
1619
  readonly PromoPanel?: ComponentType<PromoPanelProps>;
1596
1620
  };
1597
- /** Offene Extension-Zone für App-eigene Primitives. Devs erweitern
1598
- * dieses Interface via TypeScript Module-Augmentation:
1621
+ /** Extension point for app-owned primitives. Developers extend this
1622
+ * interface through TypeScript module augmentation:
1599
1623
  *
1600
1624
  * declare module "@cosmicdrift/kumiko-renderer" {
1601
1625
  * interface AppPrimitives {
@@ -1604,12 +1628,11 @@ export type CorePrimitives = {
1604
1628
  * }
1605
1629
  * }
1606
1630
  *
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). */
1631
+ * After augmentation, the keys appear in `PrimitivesRegistry`,
1632
+ * `createKumikoApp({ primitives: { Chip, Accordion } })`, and
1633
+ * `usePrimitives().Chip` with TypeScript support. Kumiko's own components
1634
+ * use only `CorePrimitives`; app primitives are exclusively for developer
1635
+ * code such as custom screens, shells, and app-owned components. */
1613
1636
  export interface AppPrimitives {
1614
1637
  }
1615
1638
  export type PrimitivesRegistry = CorePrimitives & AppPrimitives;