@cosmicdrift/kumiko-renderer 0.327.0 → 0.328.1

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 (257) hide show
  1. package/dist/app/action-form-shim.d.ts +14 -0
  2. package/{src/app/action-form-shim.ts → dist/app/action-form-shim.js} +26 -44
  3. package/dist/app/app-features-context.d.ts +8 -0
  4. package/dist/app/app-features-context.js +16 -0
  5. package/dist/app/column-renderers.d.ts +30 -0
  6. package/dist/app/column-renderers.js +34 -0
  7. package/dist/app/config-edit-shim.d.ts +11 -0
  8. package/{src/app/config-edit-shim.ts → dist/app/config-edit-shim.js} +12 -25
  9. package/dist/app/content-editors.d.ts +38 -0
  10. package/dist/app/content-editors.js +26 -0
  11. package/dist/app/content-preview.d.ts +11 -0
  12. package/dist/app/content-preview.js +30 -0
  13. package/dist/app/custom-screens.d.ts +11 -0
  14. package/dist/app/custom-screens.js +20 -0
  15. package/dist/app/dashboard-body.d.ts +16 -0
  16. package/dist/app/dashboard-body.js +11 -0
  17. package/dist/app/extension-form-submit.d.ts +32 -0
  18. package/dist/app/extension-form-submit.js +73 -0
  19. package/dist/app/extension-sections.d.ts +72 -0
  20. package/dist/app/extension-sections.js +34 -0
  21. package/dist/app/feature-schema.d.ts +4 -0
  22. package/{src/app/feature-schema.ts → dist/app/feature-schema.js} +12 -22
  23. package/dist/app/form-schema.d.ts +4 -0
  24. package/dist/app/form-schema.js +85 -0
  25. package/dist/app/kumiko-screen.d.ts +43 -0
  26. package/dist/app/kumiko-screen.js +2206 -0
  27. package/dist/app/layout-fields.d.ts +3 -0
  28. package/dist/app/layout-fields.js +20 -0
  29. package/dist/app/list-facets.d.ts +48 -0
  30. package/dist/app/list-facets.js +185 -0
  31. package/dist/app/nav.d.ts +61 -0
  32. package/dist/app/nav.js +139 -0
  33. package/dist/app/projection-detail-shim.d.ts +8 -0
  34. package/dist/app/projection-detail-shim.js +71 -0
  35. package/dist/app/projection-list-shim.d.ts +13 -0
  36. package/{src/app/projection-list-shim.ts → dist/app/projection-list-shim.js} +26 -38
  37. package/dist/app/qn.d.ts +7 -0
  38. package/{src/app/qn.ts → dist/app/qn.js} +9 -11
  39. package/dist/app/qualify-screen-id.d.ts +2 -0
  40. package/{src/app/qualify-screen-id.ts → dist/app/qualify-screen-id.js} +5 -6
  41. package/dist/app/reference-facet-bridge.d.ts +10 -0
  42. package/dist/app/reference-facet-bridge.js +22 -0
  43. package/dist/app/return-to.d.ts +31 -0
  44. package/dist/app/return-to.js +188 -0
  45. package/dist/app/row-actions.d.ts +77 -0
  46. package/dist/app/row-actions.js +416 -0
  47. package/dist/app/screen-access.d.ts +8 -0
  48. package/dist/app/screen-access.js +48 -0
  49. package/dist/app/secret-mint-body.d.ts +10 -0
  50. package/dist/app/secret-mint-body.js +140 -0
  51. package/dist/app/secrets-edit-body.d.ts +8 -0
  52. package/dist/app/secrets-edit-body.js +76 -0
  53. package/dist/app/use-embedded-screen.d.ts +6 -0
  54. package/dist/app/use-embedded-screen.js +19 -0
  55. package/dist/app/variable-chips.d.ts +7 -0
  56. package/dist/app/variable-chips.js +16 -0
  57. package/dist/app/write-failed-error.d.ts +6 -0
  58. package/dist/app/write-failed-error.js +21 -0
  59. package/dist/components/embedded-list-field.d.ts +10 -0
  60. package/dist/components/embedded-list-field.js +259 -0
  61. package/dist/components/grid-cell-for-field.d.ts +25 -0
  62. package/dist/components/grid-cell-for-field.js +31 -0
  63. package/dist/components/multi-select-checkboxes.d.ts +14 -0
  64. package/dist/components/multi-select-checkboxes.js +36 -0
  65. package/dist/components/reference-create-dialog.d.ts +13 -0
  66. package/dist/components/reference-create-dialog.js +46 -0
  67. package/dist/components/related-list-section.d.ts +25 -0
  68. package/dist/components/related-list-section.js +273 -0
  69. package/dist/components/render-edit-action-button.d.ts +22 -0
  70. package/dist/components/render-edit-action-button.js +43 -0
  71. package/dist/components/render-edit-logic.d.ts +6 -0
  72. package/dist/components/render-edit-logic.js +63 -0
  73. package/dist/components/render-edit-types.d.ts +209 -0
  74. package/dist/components/render-edit-types.js +1 -0
  75. package/dist/components/render-edit.d.ts +6 -0
  76. package/dist/components/render-edit.js +1169 -0
  77. package/dist/components/render-field.d.ts +38 -0
  78. package/dist/components/render-field.js +544 -0
  79. package/dist/components/render-list.d.ts +117 -0
  80. package/dist/components/render-list.js +232 -0
  81. package/dist/components/write-form-section.d.ts +17 -0
  82. package/dist/components/write-form-section.js +104 -0
  83. package/dist/context/dispatcher-context.d.ts +10 -0
  84. package/{src/context/dispatcher-context.tsx → dist/context/dispatcher-context.js} +16 -29
  85. package/dist/context/draft-storage-context.d.ts +12 -0
  86. package/dist/context/draft-storage-context.js +18 -0
  87. package/dist/context/user-roles-context.d.ts +7 -0
  88. package/{src/context/user-roles-context.tsx → dist/context/user-roles-context.js} +7 -15
  89. package/dist/format-when.d.ts +1 -0
  90. package/{src/format-when.ts → dist/format-when.js} +7 -7
  91. package/dist/hooks/reference-limits.d.ts +3 -0
  92. package/{src/hooks/reference-limits.ts → dist/hooks/reference-limits.js} +0 -1
  93. package/dist/hooks/use-ai-text.d.ts +52 -0
  94. package/dist/hooks/use-ai-text.js +90 -0
  95. package/dist/hooks/use-disclosure.d.ts +7 -0
  96. package/dist/hooks/use-disclosure.js +8 -0
  97. package/dist/hooks/use-form.d.ts +7 -0
  98. package/dist/hooks/use-form.js +36 -0
  99. package/dist/hooks/use-list-url-state.d.ts +43 -0
  100. package/dist/hooks/use-list-url-state.js +131 -0
  101. package/dist/hooks/use-mutation.d.ts +9 -0
  102. package/dist/hooks/use-mutation.js +37 -0
  103. package/dist/hooks/use-query.d.ts +14 -0
  104. package/dist/hooks/use-query.js +118 -0
  105. package/dist/hooks/use-reference-lookup.d.ts +14 -0
  106. package/dist/hooks/use-reference-lookup.js +63 -0
  107. package/dist/hooks/use-store.d.ts +3 -0
  108. package/dist/hooks/use-store.js +38 -0
  109. package/dist/hooks/use-stream-handler.d.ts +14 -0
  110. package/dist/hooks/use-stream-handler.js +93 -0
  111. package/dist/i18n-defaults.d.ts +2 -0
  112. package/dist/i18n-defaults.js +201 -0
  113. package/dist/i18n.d.ts +64 -0
  114. package/dist/i18n.js +166 -0
  115. package/dist/index.d.ts +77 -0
  116. package/dist/index.js +55 -0
  117. package/dist/inside-drawer.d.ts +2 -0
  118. package/dist/inside-drawer.js +9 -0
  119. package/dist/page-header-slot.d.ts +2 -0
  120. package/dist/page-header-slot.js +9 -0
  121. package/dist/primitives.d.ts +1351 -0
  122. package/dist/primitives.js +73 -0
  123. package/dist/screen-fills-height.d.ts +3 -0
  124. package/dist/screen-fills-height.js +3 -0
  125. package/dist/share-links.d.ts +4 -0
  126. package/dist/share-links.js +11 -0
  127. package/dist/sort-by-accessor.d.ts +5 -0
  128. package/dist/sort-by-accessor.js +16 -0
  129. package/dist/sse/live-events.d.ts +23 -0
  130. package/dist/sse/live-events.js +28 -0
  131. package/dist/tokens.d.ts +83 -0
  132. package/dist/tokens.js +80 -0
  133. package/package.json +16 -9
  134. package/src/changes.json +52 -0
  135. package/src/__tests__/error-i18n-defaults.test.ts +0 -82
  136. package/src/__tests__/format-when.test.ts +0 -12
  137. package/src/__tests__/i18n.test.tsx +0 -283
  138. package/src/__tests__/merge-search-params-into-initial.test.ts +0 -525
  139. package/src/__tests__/qn.test.ts +0 -83
  140. package/src/__tests__/share-links.test.ts +0 -26
  141. package/src/__tests__/sort-by-accessor.test.ts +0 -48
  142. package/src/__tests__/stringify-nav-params.test.ts +0 -33
  143. package/src/__tests__/use-list-url-state.test.tsx +0 -250
  144. package/src/app/__tests__/action-form-field-labels.test.tsx +0 -92
  145. package/src/app/__tests__/action-form-shim.test.ts +0 -137
  146. package/src/app/__tests__/build-list-query-payload.test.ts +0 -107
  147. package/src/app/__tests__/config-edit-shim.test.ts +0 -57
  148. package/src/app/__tests__/content-editors.test.tsx +0 -128
  149. package/src/app/__tests__/content-preview.test.tsx +0 -184
  150. package/src/app/__tests__/entity-edit-create-search-params.test.tsx +0 -236
  151. package/src/app/__tests__/entity-edit-jsonb-embedded-submit.test.tsx +0 -239
  152. package/src/app/__tests__/entity-list-create-singleton.test.tsx +0 -128
  153. package/src/app/__tests__/entity-list-row-action-entity-target.test.tsx +0 -424
  154. package/src/app/__tests__/entity-list-row-action-icon.test.tsx +0 -248
  155. package/src/app/__tests__/entity-list-row-action-kumiko-actions-view.test.tsx +0 -162
  156. package/src/app/__tests__/entity-list-row-action-refetch.test.tsx +0 -401
  157. package/src/app/__tests__/entity-list-screen-padding.test.tsx +0 -111
  158. package/src/app/__tests__/feature-schema.test.ts +0 -40
  159. package/src/app/__tests__/form-schema.test.ts +0 -247
  160. package/src/app/__tests__/list-daterange-facet.test.ts +0 -165
  161. package/src/app/__tests__/list-filter-facets.test.tsx +0 -521
  162. package/src/app/__tests__/nav.test.ts +0 -198
  163. package/src/app/__tests__/projection-detail-actions.test.tsx +0 -719
  164. package/src/app/__tests__/projection-detail-reference-field.test.tsx +0 -165
  165. package/src/app/__tests__/projection-detail-shim.test.ts +0 -188
  166. package/src/app/__tests__/projection-detail-singleton.test.tsx +0 -191
  167. package/src/app/__tests__/projection-list-bad-shape.test.tsx +0 -152
  168. package/src/app/__tests__/projection-list-reference-column.test.tsx +0 -219
  169. package/src/app/__tests__/projection-list-search-sort.test.tsx +0 -212
  170. package/src/app/__tests__/qualified-names.test.ts +0 -37
  171. package/src/app/__tests__/return-to.test.ts +0 -396
  172. package/src/app/__tests__/row-actions-navigate-tab.test.ts +0 -223
  173. package/src/app/__tests__/row-default-edit-action.test.tsx +0 -324
  174. package/src/app/__tests__/screen-access-allows.test.ts +0 -34
  175. package/src/app/__tests__/secret-mint-body.test.tsx +0 -404
  176. package/src/app/__tests__/variable-chips.test.tsx +0 -70
  177. package/src/app/app-features-context.tsx +0 -25
  178. package/src/app/column-renderers.tsx +0 -68
  179. package/src/app/content-editors.tsx +0 -85
  180. package/src/app/content-preview.tsx +0 -67
  181. package/src/app/custom-screens.tsx +0 -29
  182. package/src/app/dashboard-body.tsx +0 -33
  183. package/src/app/extension-form-submit.tsx +0 -123
  184. package/src/app/extension-sections.tsx +0 -111
  185. package/src/app/form-schema.ts +0 -91
  186. package/src/app/kumiko-screen.tsx +0 -3614
  187. package/src/app/layout-fields.ts +0 -31
  188. package/src/app/list-facets.ts +0 -259
  189. package/src/app/nav.tsx +0 -258
  190. package/src/app/projection-detail-shim.ts +0 -85
  191. package/src/app/reference-facet-bridge.tsx +0 -58
  192. package/src/app/return-to.tsx +0 -234
  193. package/src/app/row-actions.ts +0 -639
  194. package/src/app/screen-access.ts +0 -63
  195. package/src/app/secret-mint-body.tsx +0 -259
  196. package/src/app/secrets-edit-body.tsx +0 -194
  197. package/src/app/use-embedded-screen.ts +0 -27
  198. package/src/app/variable-chips.tsx +0 -39
  199. package/src/app/write-failed-error.ts +0 -27
  200. package/src/components/__tests__/embedded-list-field.test.tsx +0 -531
  201. package/src/components/__tests__/multi-select-checkboxes.test.tsx +0 -162
  202. package/src/components/__tests__/related-list-section-empty-state.test.tsx +0 -143
  203. package/src/components/__tests__/related-list-section.test.tsx +0 -1669
  204. package/src/components/__tests__/render-edit-action-button.test.tsx +0 -205
  205. package/src/components/__tests__/render-edit-hidden-section.test.tsx +0 -128
  206. package/src/components/__tests__/render-edit-logic.test.ts +0 -314
  207. package/src/components/__tests__/render-edit-screen-description.test.tsx +0 -152
  208. package/src/components/__tests__/render-edit-submit-actions.test.tsx +0 -460
  209. package/src/components/__tests__/render-field-app-locale.test.tsx +0 -142
  210. package/src/components/__tests__/render-field-enum-option.test.tsx +0 -99
  211. package/src/components/__tests__/render-field-icon-derivation.test.tsx +0 -119
  212. package/src/components/__tests__/render-field-json-format.test.tsx +0 -164
  213. package/src/components/__tests__/render-field-money-embedded-list-consistency.test.tsx +0 -212
  214. package/src/components/__tests__/render-field-money-roundtrip.test.tsx +0 -217
  215. package/src/components/__tests__/render-field-number-grouping.test.tsx +0 -76
  216. package/src/components/__tests__/render-field-number-unit.test.tsx +0 -227
  217. package/src/components/__tests__/render-field-password-format.test.tsx +0 -101
  218. package/src/components/__tests__/render-field-select-display.test.tsx +0 -134
  219. package/src/components/__tests__/render-field-unit-format.test.tsx +0 -107
  220. package/src/components/__tests__/render-field-unsupported-types.test.tsx +0 -195
  221. package/src/components/__tests__/render-list-toolbar-icon-only.test.tsx +0 -146
  222. package/src/components/__tests__/write-form-section.test.tsx +0 -375
  223. package/src/components/embedded-list-field.tsx +0 -354
  224. package/src/components/grid-cell-for-field.tsx +0 -60
  225. package/src/components/multi-select-checkboxes.tsx +0 -93
  226. package/src/components/reference-create-dialog.tsx +0 -91
  227. package/src/components/related-list-section.tsx +0 -467
  228. package/src/components/render-edit-action-button.tsx +0 -106
  229. package/src/components/render-edit-logic.ts +0 -85
  230. package/src/components/render-edit-types.ts +0 -216
  231. package/src/components/render-edit.tsx +0 -1700
  232. package/src/components/render-field.tsx +0 -942
  233. package/src/components/render-list.tsx +0 -561
  234. package/src/components/write-form-section.tsx +0 -199
  235. package/src/context/draft-storage-context.tsx +0 -39
  236. package/src/hooks/__tests__/use-ai-text.test.tsx +0 -177
  237. package/src/hooks/__tests__/use-disclosure.test.tsx +0 -25
  238. package/src/hooks/__tests__/use-form.test.tsx +0 -65
  239. package/src/hooks/__tests__/use-mutation.test.tsx +0 -77
  240. package/src/hooks/__tests__/use-stream-handler.test.tsx +0 -107
  241. package/src/hooks/use-ai-text.ts +0 -172
  242. package/src/hooks/use-disclosure.ts +0 -20
  243. package/src/hooks/use-form.ts +0 -78
  244. package/src/hooks/use-list-url-state.ts +0 -171
  245. package/src/hooks/use-mutation.ts +0 -61
  246. package/src/hooks/use-query.ts +0 -132
  247. package/src/hooks/use-reference-lookup.ts +0 -76
  248. package/src/hooks/use-store.ts +0 -48
  249. package/src/hooks/use-stream-handler.ts +0 -134
  250. package/src/i18n-defaults.ts +0 -220
  251. package/src/i18n.tsx +0 -286
  252. package/src/index.ts +0 -272
  253. package/src/primitives.tsx +0 -1446
  254. package/src/share-links.ts +0 -12
  255. package/src/sort-by-accessor.ts +0 -20
  256. package/src/sse/live-events.tsx +0 -56
  257. package/src/tokens.tsx +0 -170
@@ -1,76 +0,0 @@
1
- // Tier 2.7e-4: Renderer-Side Eagerload für Reference-Felder.
2
- //
3
- // Pro Reference-Spalte einer Liste wird einmal `<feature>:query:
4
- // <refEntity>:list` (limit:200) gerufen, eine Map<uuid, displayValue>
5
- // gebaut, und der Renderer nutzt sie als Cell-Display-Renderer.
6
- //
7
- // Strategy-Trade-offs:
8
- // - Server-side Drizzle-Joins wären effizienter (eine Query statt
9
- // N+1), würden aber die executor-API um lookupTables erweitern
10
- // müssen. Für das MVP geht der Renderer-Side-Pfad vor.
11
- // - Limit:200 ist eine harte UX-Grenze: bei mehr Entries in der
12
- // referenced Entity zeigen die letzten Rows nur noch UUIDs.
13
- // Searchable-Combobox (Tier 2.1c) + Server-Eagerload mit
14
- // ID-Whitelist heben das später auf.
15
- //
16
- // Dieser Hook ist call-stable: Lookup-Map wird durch useQuery's
17
- // internen Cache shared zwischen List + Edit-Form für die gleiche
18
- // Entity, Live-Updates kommen via SSE (use-query-live).
19
-
20
- import {
21
- REFERENCE_LOOKUP_SOURCES,
22
- SYSTEM_REFERENCE_LABELS,
23
- } from "@cosmicdrift/kumiko-framework/ui-types";
24
- import { useMemo } from "react";
25
- import { toKebab } from "../app/qn";
26
- import { useTranslation } from "../i18n";
27
- import { REFERENCE_LIST_LOOKUP_LIMIT } from "./reference-limits";
28
- import { useQuery } from "./use-query";
29
-
30
- export type ReferenceLookupMap = ReadonlyMap<string, string>;
31
-
32
- /** Where a reference target's label rows come from: the central override for
33
- * that `feature:entity` if one exists, else the entity-convention list
34
- * handler plus the caller's own label field. */
35
- export function referenceLookupSource(
36
- refFeature: string,
37
- refEntity: string,
38
- labelField: string,
39
- ): { readonly queryQn: string; readonly labelKey: string } {
40
- const override = REFERENCE_LOOKUP_SOURCES[`${refFeature}:${refEntity}`];
41
- if (override !== undefined) return override;
42
- return {
43
- queryQn: `${toKebab(refFeature)}:query:${toKebab(refEntity)}:list`,
44
- labelKey: labelField,
45
- };
46
- }
47
-
48
- /** Empty while the query loads (or fails), so callers fall back to the raw id.
49
- * `featureName` is the reference's target feature, not the screen's. */
50
- export function useReferenceLookup(
51
- featureName: string,
52
- refEntity: string,
53
- labelField: string,
54
- ): { readonly map: ReferenceLookupMap; readonly loading: boolean } {
55
- const { queryQn, labelKey } = referenceLookupSource(featureName, refEntity, labelField);
56
- const result = useQuery<{ rows: ReadonlyArray<Record<string, unknown>> }>(queryQn, {
57
- limit: REFERENCE_LIST_LOOKUP_LIMIT,
58
- });
59
- const translate = useTranslation();
60
- const systemLabel = SYSTEM_REFERENCE_LABELS[`${featureName}:${refEntity}`];
61
- const map = useMemo(() => {
62
- const out = new Map<string, string>();
63
- for (const row of result.data?.rows ?? []) {
64
- const id = row["id"];
65
- if (id === undefined || id === null) continue;
66
- const idStr = String(id);
67
- const label = row[labelKey] ?? id;
68
- out.set(idStr, String(label));
69
- }
70
- // System-scope ids (e.g. SYSTEM_TENANT_ID) never have a backing row, so
71
- // the bulk lookup above never covers them — inject the label directly.
72
- if (systemLabel !== undefined) out.set(systemLabel.id, translate(systemLabel.labelKey));
73
- return out;
74
- }, [result.data, labelKey, systemLabel, translate]);
75
- return { map, loading: result.loading };
76
- }
@@ -1,48 +0,0 @@
1
- import type { Store } from "@cosmicdrift/kumiko-headless";
2
- import { useRef, useSyncExternalStore } from "react";
3
-
4
- // React-Bindings für Kumikos Store-Primitive (`createStore` aus
5
- // @cosmicdrift/kumiko-headless). Beide Hooks sind dünne Wrapper um React's
6
- // useSyncExternalStore — die ganze Subscribe/Notify-Mechanik lebt im
7
- // Store selbst, hier wird nur React verdrahtet.
8
- //
9
- // useStore: gibt den ganzen Snapshot zurück. Re-rendert wenn der
10
- // Store seine Identität wechselt. Object.is-Gate im Store selbst
11
- // verhindert no-op-Updates.
12
- //
13
- // useStoreSelector: gibt eine abgeleitete Sicht zurück. Selectors die
14
- // Object-/Array-Literale zurückgeben (z.B. `s => ({ a, b })`) erzeugen
15
- // pro Render eine neue Identität — useSyncExternalStore würde das als
16
- // "Change" lesen und in eine Re-Render-Schleife laufen. Der dritte
17
- // Arg `equals` dient genau dafür: gleiche Auswahl → cached
18
- // Identität zurück, kein Re-Render.
19
-
20
- // @wrapper-known semantic-alias
21
- export function useStore<T>(store: Store<T>): T {
22
- return useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
23
- }
24
-
25
- export function useStoreSelector<T, S>(
26
- store: Store<T>,
27
- select: (snapshot: T) => S,
28
- equals: (a: S, b: S) => boolean = Object.is,
29
- ): S {
30
- // Cache der letzten Auswahl. Beim nächsten getSnapshot-Call vergleicht
31
- // der Wrapper die neu berechnete Auswahl mit dem Cache und gibt — falls
32
- // gleich — die cached Identität zurück. Das ist der Trick, der
33
- // Selector-Returns wie `s => ({ a, b })` stabil hält.
34
- const lastSelected = useRef<S>(undefined as S);
35
- const initialized = useRef(false);
36
-
37
- const getSelectedSnapshot = (): S => {
38
- const next = select(store.getSnapshot());
39
- if (initialized.current && equals(lastSelected.current, next)) {
40
- return lastSelected.current;
41
- }
42
- lastSelected.current = next;
43
- initialized.current = true;
44
- return next;
45
- };
46
-
47
- return useSyncExternalStore(store.subscribe, getSelectedSnapshot, getSelectedSnapshot);
48
- }
@@ -1,134 +0,0 @@
1
- import type { DispatcherError } from "@cosmicdrift/kumiko-headless";
2
- import { useCallback, useEffect, useRef, useState } from "react";
3
- import { useDispatcher } from "../context/dispatcher-context";
4
-
5
- // React wrapper around dispatcher.stream (#1382). Accumulates yielded
6
- // chunks into `chunks` (unlike useQuery({live:true}), which only
7
- // invalidates and re-fetches). Each start() owns an AbortController;
8
- // a newer start() / unmount aborts the previous run so late chunks
9
- // cannot clobber fresher state.
10
-
11
- export type StreamStatus = "idle" | "streaming" | "done" | "error";
12
-
13
- export type UseStreamHandlerResult<TChunk> = {
14
- readonly chunks: readonly TChunk[];
15
- readonly status: StreamStatus;
16
- readonly error: DispatcherError | null;
17
- readonly start: (payload?: unknown) => Promise<void>;
18
- readonly abort: () => void;
19
- readonly reset: () => void;
20
- };
21
-
22
- export type UseStreamHandlerOptions = {
23
- // When true, start() runs once on mount with the initial payload.
24
- // Default false — streams are usually user-triggered (unlike queries).
25
- readonly autoStart?: boolean;
26
- };
27
-
28
- export function useStreamHandler<TChunk = unknown>(
29
- type: string,
30
- payload: unknown = {},
31
- options: UseStreamHandlerOptions = {},
32
- ): UseStreamHandlerResult<TChunk> {
33
- const dispatcher = useDispatcher();
34
- const { autoStart = false } = options;
35
-
36
- const [chunks, setChunks] = useState<readonly TChunk[]>([]);
37
- const [status, setStatus] = useState<StreamStatus>("idle");
38
- const [error, setError] = useState<DispatcherError | null>(null);
39
-
40
- const activeCtrl = useRef<AbortController | null>(null);
41
- const payloadRef = useRef(payload);
42
- payloadRef.current = payload;
43
-
44
- const abort = useCallback((): void => {
45
- activeCtrl.current?.abort();
46
- activeCtrl.current = null;
47
- // User cancel — exit streaming so UI can re-enable the start button.
48
- setStatus((s) => (s === "streaming" ? "idle" : s));
49
- }, []);
50
-
51
- const reset = useCallback((): void => {
52
- abort();
53
- setChunks([]);
54
- setStatus("idle");
55
- setError(null);
56
- }, [abort]);
57
-
58
- // Shared by every abort-detection branch below: only touch status if this
59
- // run is still the active one (or activeCtrl was already cleared by abort()).
60
- const settleAborted = useCallback((ctrl: AbortController): void => {
61
- if (activeCtrl.current === null || activeCtrl.current === ctrl) setStatus("idle");
62
- }, []);
63
-
64
- const start = useCallback(
65
- async (overridePayload?: unknown): Promise<void> => {
66
- activeCtrl.current?.abort();
67
- const ctrl = new AbortController();
68
- activeCtrl.current = ctrl;
69
-
70
- setChunks([]);
71
- setError(null);
72
- setStatus("streaming");
73
-
74
- const body = overridePayload !== undefined ? overridePayload : payloadRef.current;
75
- try {
76
- for await (const chunk of dispatcher.stream<TChunk>(type, body, { signal: ctrl.signal })) {
77
- // skip: a newer start() already superseded this run
78
- if (ctrl.signal.aborted) {
79
- settleAborted(ctrl);
80
- return;
81
- }
82
- setChunks((prev) => [...prev, chunk]);
83
- }
84
- // skip: aborted after last chunk, don't mark done
85
- if (ctrl.signal.aborted) {
86
- settleAborted(ctrl);
87
- return;
88
- }
89
- setStatus("done");
90
- } catch (e) {
91
- // skip: abort is not an error toast
92
- if (ctrl.signal.aborted) {
93
- settleAborted(ctrl);
94
- return;
95
- }
96
- const mapped = asDispatcherError(e);
97
- // skip: dispatcher-mapped abort (fetch cancelled)
98
- if (mapped.code === "aborted") {
99
- settleAborted(ctrl);
100
- return;
101
- }
102
- setError(mapped);
103
- setStatus("error");
104
- }
105
- },
106
- [dispatcher, type, settleAborted],
107
- );
108
-
109
- useEffect(() => {
110
- // skip: autoStart off — streams are usually user-triggered
111
- if (!autoStart) return;
112
- void start();
113
- }, [autoStart, start]);
114
-
115
- useEffect(() => {
116
- return () => {
117
- activeCtrl.current?.abort();
118
- };
119
- }, []);
120
-
121
- return { chunks, status, error, start, abort, reset };
122
- }
123
-
124
- function asDispatcherError(e: unknown): DispatcherError {
125
- if (e && typeof e === "object" && "code" in e && "message" in e && "i18nKey" in e) {
126
- return e as DispatcherError;
127
- }
128
- return {
129
- code: "stream_error",
130
- httpStatus: 0,
131
- i18nKey: "errors.unknown",
132
- message: e instanceof Error ? e.message : String(e),
133
- };
134
- }
@@ -1,220 +0,0 @@
1
- // Framework-Default-Bundle. Strings die die Renderer-Components hart
2
- // brauchen (Save/Cancel/Delete-Buttons, Empty-States, Search-Placeholder,
3
- // Nav-Toggle-aria-Labels, Validation-Reasons). createKumikoApp hängt das
4
- // als ALLERLETZTEN Fallback in den LocaleProvider — Apps können
5
- // einzelne Keys via clientFeatures.translations überschreiben.
6
- //
7
- // Convention: alle Keys mit `kumiko.`-Prefix damit sie nicht mit
8
- // App-Keys kollidieren. Sub-Pfade gruppieren nach Bereich (actions /
9
- // list / nav / form / validation).
10
-
11
- import type { TranslationsByLocale } from "./i18n";
12
-
13
- export const kumikoDefaultTranslations: TranslationsByLocale = {
14
- en: {
15
- "kumiko.actions.save": "Save",
16
- "kumiko.actions.cancel": "Cancel",
17
- "kumiko.actions.delete": "Delete",
18
- "kumiko.actions.delete-confirm": "Confirm delete?",
19
- "kumiko.actions.reload": "Reload",
20
- "kumiko.actions.create": "New",
21
- "kumiko.actions.edit": "Edit",
22
- "kumiko.actions.view": "View",
23
- "kumiko.actions.copyLink": "Copy link",
24
- "kumiko.actions.copyLinkCopied": "Copied!",
25
- "kumiko.actions.next": "Next",
26
- "kumiko.actions.back": "Back",
27
- "kumiko.actions.finish": "Finish",
28
-
29
- "kumiko.wizard.step": "Step {current} of {total}",
30
- "kumiko.wizard.step-with-title": "Step {current} of {total} · {title}",
31
-
32
- "kumiko.version.update-available": "A new version is available.",
33
-
34
- "kumiko.toast.learn-more": "Learn more",
35
-
36
- "kumiko.field.open-calendar": "Open calendar",
37
- "kumiko.field.dateField.placeholderYear": "Y",
38
- "kumiko.field.dateField.placeholderMonth": "M",
39
- "kumiko.field.dateField.placeholderDay": "D",
40
- "kumiko.field.time": "Time",
41
- "kumiko.field.timezone": "Time zone",
42
- "kumiko.field.locatedTzHint": "Time local to the given location",
43
- "kumiko.field.reference-created-no-id":
44
- "Record was created but could not be selected automatically. Please select it manually.",
45
- "kumiko.field.unsupported": "This field type can't be edited here yet.",
46
- "kumiko.field.embedded-list.add-row": "Add row",
47
- "kumiko.field.embedded-list.remove-row": "Remove row",
48
- "kumiko.field.embedded-list.duplicate-row": "Duplicate row",
49
- "kumiko.field.embedded-list.move-up": "Move up",
50
- "kumiko.field.embedded-list.move-down": "Move down",
51
- "kumiko.field.embedded-list.empty": "No rows yet.",
52
- "kumiko.field.embedded-list.empty-cta": "Add first row",
53
- "kumiko.field.embedded-list.paste-rows-truncated":
54
- "{count} pasted row(s) were dropped (max row count reached).",
55
- "kumiko.field.embedded-list.paste-cells-unmatched":
56
- "{count} cell(s) had no matching option and were left unchanged.",
57
- "kumiko.field.multiSelect.select-all": "Select all",
58
- "kumiko.field.multiSelect.deselect-all": "Deselect all",
59
-
60
- "kumiko.list.search-placeholder": "Search…",
61
- "kumiko.list.empty.title": "No entries yet.",
62
- "kumiko.list.empty.hint": "Create the first one to get started.",
63
- "kumiko.list.no-entries": "No entries.",
64
- "kumiko.list.end-of-list": "— End of list —",
65
- "kumiko.list.sort.label": "Sort",
66
- "kumiko.list.sort.unsorted": "Unsorted",
67
- "kumiko.list.filter.reset": "Reset",
68
- "kumiko.list.related-list-truncated":
69
- "Showing the first {count} entries. More are available but not loaded — this list does not paginate.",
70
- "kumiko.reference.system-tenant": "System",
71
- "kumiko.reference.system-user": "System",
72
-
73
- "kumiko.pager.status": "{from}–{to} of {total}",
74
- "kumiko.pager.previousPage": "Previous page",
75
- "kumiko.pager.nextPage": "Next page",
76
- "kumiko.pager.page": "Page {entry}",
77
-
78
- "kumiko.combobox.search-placeholder": "Search…",
79
- "kumiko.combobox.empty": "No matches.",
80
- "kumiko.combobox.loading": "Loading…",
81
- "kumiko.combobox.placeholder": "—",
82
-
83
- "kumiko.dashboard.filter.all": "All",
84
-
85
- "kumiko.app-boot.loading": "Loading…",
86
- "kumiko.app-boot.unauthorized": "You need to sign in to see this.",
87
- "kumiko.app-boot.failed": "Couldn't load the app. Check your connection and try again.",
88
- "kumiko.app-boot.no-open-screen":
89
- "This app has no screen that's accessible without a role restriction.",
90
-
91
- "kumiko.widget.loading": "Loading…",
92
- "kumiko.widget.error.title": "Couldn't load.",
93
-
94
- "kumiko.widget.upload.uploading": "Uploading…",
95
- "kumiko.widget.upload.done": "Uploaded",
96
- "kumiko.widget.upload.error": "Failed",
97
- "kumiko.widget.upload.rejected-type": "File type not allowed",
98
-
99
- "kumiko.widget.step-bar.done": "Done",
100
-
101
- "kumiko.widget.drawer.restore": "Restore drawer width",
102
- "kumiko.widget.drawer.maximize": "Maximize drawer width",
103
- "kumiko.widget.drawer.resize": "Resize drawer",
104
-
105
- "kumiko.widget.floatingPanel.move": "Move panel",
106
- "kumiko.widget.floatingPanel.resizeTop": "Resize panel from the top edge",
107
- "kumiko.widget.floatingPanel.resizeRight": "Resize panel from the right edge",
108
- "kumiko.widget.floatingPanel.resizeBottom": "Resize panel from the bottom edge",
109
- "kumiko.widget.floatingPanel.resizeLeft": "Resize panel from the left edge",
110
-
111
- "kumiko.planCard.current": "Current plan",
112
- "kumiko.planCard.priceUnavailable": "Price not available",
113
-
114
- "kumiko.nav.expand": "Expand",
115
- "kumiko.nav.collapse": "Collapse",
116
- "kumiko.nav.search": "Search navigation…",
117
- "kumiko.nav.language": "Language",
118
-
119
- "kumiko.workspace.switch": "Switch workspace",
120
- // Workspace-Switcher fallback label when activeId points at no visible workspace.
121
- "kumiko.workspace.select": "Select workspace",
122
-
123
- "kumiko.dialog.confirm": "Confirm",
124
- "kumiko.dialog.cancel": "Cancel",
125
- "kumiko.dialog.close": "Close",
126
-
127
- "kumiko.aiText.acceptHint": "Tab = accept, Esc = discard",
128
- "kumiko.aiText.correct": "Correct",
129
- "kumiko.aiText.translate": "Translate",
130
- "kumiko.aiText.rewrite": "Rewrite",
131
- "kumiko.aiText.diff.before": "Before",
132
- "kumiko.aiText.diff.after": "After",
133
- "kumiko.aiText.diff.generating": "Generating…",
134
- "kumiko.aiText.style.formal": "Formal",
135
- "kumiko.aiText.style.casual": "Casual",
136
- "kumiko.aiText.style.concise": "Concise",
137
- "kumiko.aiText.style.expand": "Expand",
138
- "kumiko.aiText.capExceeded": "Monthly AI limit reached.",
139
-
140
- "kumiko.rowAction.failed": "Action failed",
141
-
142
- "kumiko.contentEditor.insertVariable": "Insert {name}",
143
- "kumiko.contentEditor.preview": "Preview",
144
- "kumiko.contentEditor.editMode": "Edit",
145
- "kumiko.contentEditor.bold": "Bold",
146
- "kumiko.contentEditor.italic": "Italic",
147
- "kumiko.contentEditor.heading1": "Heading 1",
148
- "kumiko.contentEditor.heading2": "Heading 2",
149
- "kumiko.contentEditor.bulletList": "Bullet list",
150
- "kumiko.contentEditor.orderedList": "Numbered list",
151
-
152
- "kumiko.config.source.user": "My value",
153
- "kumiko.config.source.tenant": "Tenant",
154
- "kumiko.config.source.system": "System",
155
- "kumiko.config.source.appOverride": "App override",
156
- "kumiko.config.source.computed": "Computed",
157
- "kumiko.config.source.default": "Default",
158
- "kumiko.config.source.missing": "Missing",
159
- "kumiko.config.cascade.noValue": "No value set",
160
- "kumiko.config.cascade.activeMarker": "active",
161
- "kumiko.config.cascade.resetTo": "Reset override ({scope})",
162
-
163
- "kumiko.form.error.generic": "Something went wrong.",
164
- "kumiko.form.error.version-conflict":
165
- "Record was modified in the meantime. Reload and try again.",
166
- "kumiko.form.extension.save-failed": "A custom field could not be saved.",
167
- "kumiko.form.draft.resume-multiple":
168
- "Found multiple open drafts for this form. Which one do you want to resume?",
169
- "kumiko.form.draft.resume-single":
170
- "Found an open draft for this form. Do you want to resume it?",
171
- "kumiko.form.draft.start-new": "Start new",
172
-
173
- "kumiko.secretMint.title": "Your new secret",
174
- "kumiko.secretMint.warning":
175
- "Copy this now — it is shown only this once and cannot be retrieved later.",
176
- "kumiko.secretMint.confirm": "I have copied it",
177
- "kumiko.secretMint.copy": "Copy",
178
- "kumiko.secretMint.copied": "Copied",
179
- "kumiko.secretMint.done": "Done.",
180
-
181
- "kumiko.validation.required": "Required.",
182
- "kumiko.validation.invalid": "Invalid value.",
183
- "kumiko.validation.too-short": "Too short (at least {min} characters).",
184
- "kumiko.validation.too-long": "Too long (at most {max} characters).",
185
- "kumiko.validation.out-of-range": "Value out of allowed range.",
186
-
187
- "errors.validation.invalid_type": "Invalid value.",
188
- "errors.validation.too_small": "Too small or too short (minimum: {minimum}).",
189
- "errors.validation.too_big": "Too big or too long (maximum: {maximum}).",
190
- "errors.validation.invalid_format": "Invalid format.",
191
- "errors.validation.not_multiple_of": "Must be a multiple of {divisor}.",
192
- "errors.validation.unrecognized_keys": "Unknown fields.",
193
- "errors.validation.invalid_union": "Invalid value.",
194
- "errors.validation.invalid_key": "Invalid key.",
195
- "errors.validation.invalid_element": "Invalid entry.",
196
- "errors.validation.invalid_value": "Invalid choice.",
197
- "errors.validation.custom": "Invalid value.",
198
- "errors.validation.unexpected_field": "Unknown field.",
199
- "errors.validation.out_of_bounds": "Value out of allowed range.",
200
- "errors.validation.invalid_option": "Invalid choice.",
201
- "errors.validation.failed": "Validation failed.",
202
-
203
- "errors.feature.disabled": "This feature is currently unavailable.",
204
- "errors.access.denied": "You don't have permission to do this.",
205
- "errors.notFound": "Not found.",
206
- "errors.conflict": "Conflict — the operation could not be completed.",
207
- "errors.versionConflict": "The record was modified in the meantime. Reload and try again.",
208
- "errors.preconditionFailed": "The record changed in the meantime. Reload and try again.",
209
- "errors.uniqueViolation": "This entry already exists.",
210
- "errors.unprocessable": "The request could not be processed.",
211
- "errors.unconfigured": "This feature isn't configured yet.",
212
- "errors.internal": "Something went wrong. Please try again later.",
213
- "errors.rate_limited": "Too many requests. Please try again shortly.",
214
- "errors.cap.exceeded": "Limit reached. Upgrade your plan or wait for the next period.",
215
- "errors.download.urlMissing": "Download unavailable — please try again.",
216
- "auth.errors.originNotAllowed": "Requests from this origin are not allowed.",
217
- "dispatcher.errors.network": "Network error. Please check your connection and try again.",
218
- "dispatcher.errors.aborted": "Request was cancelled.",
219
- },
220
- };