@cosmicdrift/kumiko-renderer 0.386.0 → 0.387.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/LICENSE +21 -57
  2. package/dist/app/action-form-body.d.ts +2 -2
  3. package/dist/app/action-form-body.js +2 -2
  4. package/dist/app/action-form-shim.d.ts +8 -8
  5. package/dist/app/action-form-shim.js +20 -22
  6. package/dist/app/app-features-context.js +4 -4
  7. package/dist/app/column-renderers.d.ts +7 -9
  8. package/dist/app/column-renderers.js +15 -18
  9. package/dist/app/config-edit-body.js +1 -1
  10. package/dist/app/config-edit-shim.d.ts +7 -8
  11. package/dist/app/config-edit-shim.js +18 -20
  12. package/dist/app/custom-screens.d.ts +3 -3
  13. package/dist/app/custom-screens.js +8 -8
  14. package/dist/app/dashboard-body.d.ts +2 -2
  15. package/dist/app/dashboard-body.js +2 -2
  16. package/dist/app/entity-edit-prefill.js +9 -9
  17. package/dist/app/entity-edit-screen.js +4 -4
  18. package/dist/app/entity-list-screen.js +3 -3
  19. package/dist/app/extension-form-submit.d.ts +1 -1
  20. package/dist/app/extension-form-submit.js +14 -15
  21. package/dist/app/extension-sections.d.ts +24 -25
  22. package/dist/app/extension-sections.js +10 -10
  23. package/dist/app/feature-schema.js +11 -11
  24. package/dist/app/form-schema.js +1 -1
  25. package/dist/app/header-actions-bar.js +1 -1
  26. package/dist/app/kumiko-screen.js +8 -10
  27. package/dist/app/layout-fields.js +1 -1
  28. package/dist/app/list-facets.js +3 -3
  29. package/dist/app/nav.d.ts +10 -10
  30. package/dist/app/nav.js +13 -13
  31. package/dist/app/projection-detail-body.js +4 -4
  32. package/dist/app/projection-detail-shim.d.ts +6 -5
  33. package/dist/app/projection-detail-shim.js +23 -22
  34. package/dist/app/projection-list-body.js +10 -10
  35. package/dist/app/projection-list-shim.d.ts +5 -5
  36. package/dist/app/projection-list-shim.js +17 -18
  37. package/dist/app/qn.js +10 -12
  38. package/dist/app/qualify-screen-id.js +1 -1
  39. package/dist/app/screen-access.js +2 -2
  40. package/dist/app/screen-shared.js +1 -1
  41. package/dist/app/secret-mint-body.js +4 -4
  42. package/dist/app/write-failed-error.js +8 -7
  43. package/dist/components/embedded-list-field.js +4 -4
  44. package/dist/components/grid-cell-for-field.d.ts +1 -1
  45. package/dist/components/reference-create-dialog.js +3 -3
  46. package/dist/components/related-list-section.d.ts +1 -1
  47. package/dist/components/related-list-section.js +1 -1
  48. package/dist/components/render-edit-logic.js +1 -1
  49. package/dist/components/render-edit-types.d.ts +6 -6
  50. package/dist/components/render-edit.js +63 -65
  51. package/dist/components/render-field.d.ts +7 -7
  52. package/dist/components/render-field.js +57 -59
  53. package/dist/components/render-list.d.ts +42 -43
  54. package/dist/components/render-list.js +63 -67
  55. package/dist/components/use-wizard-form-draft.js +10 -10
  56. package/dist/context/dispatcher-context.js +5 -5
  57. package/dist/context/user-roles-context.js +2 -2
  58. package/dist/headless/apex/index.d.ts +1 -2
  59. package/dist/headless/apex/index.js +5 -6
  60. package/dist/headless/form/form-controller.js +6 -6
  61. package/dist/headless/form/zod-bridge.js +2 -2
  62. package/dist/headless/format/money.js +1 -1
  63. package/dist/headless/index.js +2 -2
  64. package/dist/headless/view-model/edit.js +2 -2
  65. package/dist/headless/view-model/list.js +3 -4
  66. package/dist/headless/view-model/types.d.ts +3 -3
  67. package/dist/hooks/reference-limits.js +11 -12
  68. package/dist/hooks/use-list-url-state.d.ts +13 -13
  69. package/dist/hooks/use-list-url-state.js +21 -22
  70. package/dist/hooks/use-query.js +3 -3
  71. package/dist/hooks/use-reference-lookup.js +16 -17
  72. package/dist/hooks/use-store.js +18 -22
  73. package/dist/hooks/use-stream-handler.js +4 -2
  74. package/dist/i18n-defaults.js +2 -8
  75. package/dist/i18n.d.ts +10 -10
  76. package/dist/i18n.js +25 -25
  77. package/dist/index.js +5 -4
  78. package/dist/live/dispatcher-live.js +2 -2
  79. package/dist/primitives.d.ts +225 -221
  80. package/dist/primitives.js +1 -1
  81. package/dist/sse/live-events.d.ts +5 -5
  82. package/dist/sse/live-events.js +17 -17
  83. package/dist/tokens.d.ts +8 -8
  84. package/dist/tokens.js +16 -17
  85. package/dist/web/app/browser-locale.d.ts +9 -9
  86. package/dist/web/app/browser-locale.js +19 -19
  87. package/dist/web/app/client-plugin.d.ts +44 -52
  88. package/dist/web/app/client-plugin.js +4 -5
  89. package/dist/web/app/create-app.d.ts +28 -31
  90. package/dist/web/app/create-app.js +21 -22
  91. package/dist/web/app/document-lang-sync.js +1 -1
  92. package/dist/web/app/draft-storage.js +1 -1
  93. package/dist/web/app/nav-providers-context.d.ts +3 -3
  94. package/dist/web/app/nav-providers-context.js +3 -3
  95. package/dist/web/app/nav.d.ts +18 -17
  96. package/dist/web/app/nav.js +66 -64
  97. package/dist/web/app/rich-content-editor.js +1 -1
  98. package/dist/web/components/config-cascade.js +17 -18
  99. package/dist/web/components/config-source-badge.js +4 -4
  100. package/dist/web/layout/app-layout.d.ts +9 -9
  101. package/dist/web/layout/app-layout.js +2 -2
  102. package/dist/web/layout/avatar.d.ts +4 -3
  103. package/dist/web/layout/avatar.js +15 -17
  104. package/dist/web/layout/default-app-shell.d.ts +22 -23
  105. package/dist/web/layout/editor-panel.js +3 -3
  106. package/dist/web/layout/nav-tree.js +86 -84
  107. package/dist/web/layout/profile-menu.d.ts +1 -1
  108. package/dist/web/layout/sidebar-brand.d.ts +7 -7
  109. package/dist/web/layout/sidebar-user.d.ts +1 -1
  110. package/dist/web/layout/sidebar-user.js +4 -4
  111. package/dist/web/layout/sidebar.d.ts +8 -8
  112. package/dist/web/layout/sidebar.js +7 -7
  113. package/dist/web/layout/target-resolver-stub.d.ts +5 -5
  114. package/dist/web/layout/target-resolver-stub.js +20 -20
  115. package/dist/web/layout/target-url.d.ts +7 -7
  116. package/dist/web/layout/target-url.js +22 -22
  117. package/dist/web/layout/theme-toggle.js +4 -5
  118. package/dist/web/layout/workspace-shell.d.ts +21 -21
  119. package/dist/web/layout/workspace-shell.js +29 -30
  120. package/dist/web/lib/cn.d.ts +4 -4
  121. package/dist/web/lib/cn.js +4 -4
  122. package/dist/web/primitives/action-menu.d.ts +8 -8
  123. package/dist/web/primitives/action-menu.js +14 -13
  124. package/dist/web/primitives/calendar-popover.d.ts +1 -1
  125. package/dist/web/primitives/calendar-popover.js +10 -10
  126. package/dist/web/primitives/card-styles.js +1 -1
  127. package/dist/web/primitives/card.js +2 -2
  128. package/dist/web/primitives/combobox.d.ts +12 -12
  129. package/dist/web/primitives/combobox.js +34 -35
  130. package/dist/web/primitives/data-table-cell.js +2 -2
  131. package/dist/web/primitives/data-table.js +2 -3
  132. package/dist/web/primitives/date-field.js +2 -2
  133. package/dist/web/primitives/date-input.js +4 -4
  134. package/dist/web/primitives/default-form.js +2 -2
  135. package/dist/web/primitives/dropdown-menu.js +14 -15
  136. package/dist/web/primitives/embedded-list-input.js +3 -3
  137. package/dist/web/primitives/facet-filter.js +1 -1
  138. package/dist/web/primitives/field.js +9 -8
  139. package/dist/web/primitives/form.d.ts +1 -1
  140. package/dist/web/primitives/form.js +6 -6
  141. package/dist/web/primitives/input.js +2 -2
  142. package/dist/web/primitives/layout-elements.js +1 -1
  143. package/dist/web/primitives/layout.js +2 -2
  144. package/dist/web/primitives/located-timestamp-input.js +4 -4
  145. package/dist/web/primitives/money-input.js +13 -17
  146. package/dist/web/primitives/pager.js +1 -1
  147. package/dist/web/primitives/row-actions.js +3 -4
  148. package/dist/web/primitives/secret-reveal.js +1 -1
  149. package/dist/web/primitives/timestamp-input.d.ts +9 -9
  150. package/dist/web/primitives/timestamp-input.js +21 -21
  151. package/dist/web/primitives/toast.js +12 -13
  152. package/dist/web/sse/live-events.d.ts +6 -6
  153. package/dist/web/sse/live-events.js +3 -3
  154. package/dist/web/tokens.d.ts +11 -12
  155. package/dist/web/tokens.js +35 -37
  156. package/dist/web/version/update-checker.js +14 -13
  157. package/dist/web/widgets/ai-text-field.js +1 -1
  158. package/dist/web/widgets/charts.d.ts +10 -10
  159. package/dist/web/widgets/charts.js +8 -8
  160. package/dist/web/widgets/collapsible-section.d.ts +3 -3
  161. package/dist/web/widgets/collapsible-section.js +6 -6
  162. package/dist/web/widgets/detail-list.d.ts +1 -3
  163. package/dist/web/widgets/detail-list.js +1 -3
  164. package/dist/web/widgets/feed-list.d.ts +2 -2
  165. package/dist/web/widgets/feed-list.js +2 -2
  166. package/dist/web/widgets/form-fields.d.ts +14 -14
  167. package/dist/web/widgets/form-fields.js +14 -13
  168. package/dist/web/widgets/index.js +4 -4
  169. package/dist/web/widgets/infinity-list.js +3 -3
  170. package/dist/web/widgets/mode-switch.d.ts +2 -2
  171. package/dist/web/widgets/mode-switch.js +2 -2
  172. package/dist/web/widgets/progress-list.d.ts +1 -2
  173. package/dist/web/widgets/progress-list.js +1 -2
  174. package/dist/web/widgets/query-table.d.ts +6 -6
  175. package/dist/web/widgets/result-panel.d.ts +16 -16
  176. package/dist/web/widgets/result-panel.js +10 -10
  177. package/dist/web/widgets/section-card.d.ts +3 -3
  178. package/dist/web/widgets/section-card.js +3 -3
  179. package/dist/web/widgets/stat.d.ts +4 -4
  180. package/dist/web/widgets/stat.js +6 -6
  181. package/dist/web/widgets/states.d.ts +4 -4
  182. package/dist/web/widgets/states.js +4 -4
  183. package/dist/web/widgets/use-draft.d.ts +7 -7
  184. package/dist/web/widgets/use-draft.js +7 -7
  185. package/package.json +6 -6
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { createContext, useContext, } from "react";
3
3
  /** Structural marker a top-level `actions` node can carry so DefaultForm's
4
- * sticky-footer split (fw#1918) treats it as the pinned primary action even
4
+ * sticky-footer split treats it as the pinned primary action even
5
5
  * though it isn't a `Button` with `type="submit"` — e.g. RenderEdit's
6
6
  * footer-slot mount, whose actual rendered button is opaque app code the
7
7
  * split can't inspect (see `ScreenSlots.footerPrimary`). */
@@ -14,15 +14,15 @@ export type LiveEvent = {
14
14
  /** Synthetic eventType delivered to every subscribed entity after the stream
15
15
  * reconnected: frames during the gap are lost, so consumers must refetch. */
16
16
  export declare const LIVE_RESYNC_EVENT_TYPE = "live.resync";
17
- /** Abonniert Live-Events für eine Entity (aggregateType). Returnt die
18
- * Unsubscribe-Funktion. Mehrere Subscriptions parallel auf dieselbe
19
- * Entity sind erlaubt — jede bekommt ihr eigenes Event. */
17
+ /** Subscribes to live events for an entity (aggregateType) and returns the
18
+ * unsubscribe function. Multiple subscriptions to the same entity are
19
+ * allowed in parallel; each receives its own event. */
20
20
  export type LiveEventSubscriber = (entityName: string, listener: (event: LiveEvent) => void) => () => void;
21
21
  export type LiveEventsProviderProps = {
22
22
  readonly children: ReactNode;
23
23
  readonly value: LiveEventSubscriber;
24
24
  };
25
25
  export declare function LiveEventsProvider({ children, value }: LiveEventsProviderProps): ReactNode;
26
- /** Hook für useQuery-like Consumer. Liefert die Subscribe-Function
27
- * aus dem Context. Wenn kein Provider da ist, ist's ein No-op. */
26
+ /** Hook for a useQuery-like consumer. Returns the subscription function
27
+ * from the context. Without a provider, it is a no-op. */
28
28
  export declare function useLiveEvents(): LiveEventSubscriber;
@@ -1,31 +1,31 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // Live-Events-Contract, plattform-neutral. Die Plattform (Web mit
3
- // EventSource, Native mit polyfill) liefert einen `LiveEventSubscriber`
4
- // via `<LiveEventsProvider value={...}>`; der shared-Layer konsumiert
5
- // nur das Interface.
2
+ // Platform-neutral live-events contract. The platform (Web with
3
+ // EventSource, Native with polyfill) supplies a `LiveEventSubscriber`
4
+ // via `<LiveEventsProvider value={...}>`; the shared layer consumes
5
+ // only the interface.
6
6
  //
7
- // Warum ein Context statt Module-Singleton wie vor dem Split: ein
8
- // Module-Singleton koppelt an eine globale Verbindung. Mit Context
9
- // kann der Caller pro Tree eine andere Subscribe-Quelle durchreichen
10
- // — nützlich in Tests (Fake-Feed), bei Multi-Tenant-Bridges, und
11
- // beim Native-Renderer wo die Verbindungs-Lifecycle oft nicht dem
12
- // App-Lifecycle entspricht.
7
+ // A Context is used instead of a module singleton as before the split:
8
+ // a module singleton couples the code to a global connection. Context
9
+ // lets the caller provide a different subscription source per tree,
10
+ // which is useful for tests (Fake-Feed), multi-tenant bridges, and
11
+ // the Native renderer, where connection lifecycle often differs from
12
+ // the app lifecycle.
13
13
  import { createContext, useContext } from "react";
14
14
  /** Synthetic eventType delivered to every subscribed entity after the stream
15
15
  * reconnected: frames during the gap are lost, so consumers must refetch. */
16
16
  export const LIVE_RESYNC_EVENT_TYPE = "live.resync";
17
- // Wenn kein Provider da ist, liefern wir einen No-op-Subscriber statt
18
- // zu crashen. Grund: useQuery({ live: true }) kann optimistisch
19
- // ausgehakt werden ohne den ganzen Baum abzureißen, wenn die Plattform
20
- // z.B. SSE temporär deaktiviert hat. Fehler-Signale würden stumm
21
- // geschluckt — das ist explizit dokumentiert.
17
+ // If no provider is present, return a no-op subscriber instead of
18
+ // throwing. This allows useQuery({ live: true }) to be optimistically
19
+ // disabled without tearing down the entire tree when the platform has
20
+ // temporarily disabled SSE. Error signals are silently swallowed; this
21
+ // is intentional and documented.
22
22
  const noopSubscriber = () => () => { };
23
23
  const LiveEventsContext = createContext(noopSubscriber);
24
24
  export function LiveEventsProvider({ children, value }) {
25
25
  return _jsx(LiveEventsContext.Provider, { value: value, children: children });
26
26
  }
27
- /** Hook für useQuery-like Consumer. Liefert die Subscribe-Function
28
- * aus dem Context. Wenn kein Provider da ist, ist's ein No-op. */
27
+ /** Hook for a useQuery-like consumer. Returns the subscription function
28
+ * from the context. Without a provider, it is a no-op. */
29
29
  export function useLiveEvents() {
30
30
  return useContext(LiveEventsContext);
31
31
  }
package/dist/tokens.d.ts CHANGED
@@ -44,7 +44,7 @@ export type CoreTokens = {
44
44
  readonly shadow: ShadowTokens;
45
45
  readonly spacing: SpacingTokens;
46
46
  };
47
- /** Erweiterung für App-eigene Token-Kategorien. Devs augmentieren:
47
+ /** Extension point for app-specific token categories. Augment it, for example:
48
48
  *
49
49
  * declare module "@cosmicdrift/kumiko-renderer" {
50
50
  * interface AppTokens {
@@ -52,8 +52,8 @@ export type CoreTokens = {
52
52
  * }
53
53
  * }
54
54
  *
55
- * Und reichen im App-CSS die passenden `--chart-*` Variablen rein.
56
- * Kumikos Default-Primitives nutzen nur CoreTokens. */
55
+ * Then provide the matching `--chart-*` variables in the app's CSS.
56
+ * Kumiko's default primitives use only CoreTokens. */
57
57
  export interface AppTokens {
58
58
  }
59
59
  export type Tokens = CoreTokens & AppTokens;
@@ -78,12 +78,12 @@ export type TokensProviderProps = {
78
78
  readonly value: TokensApi;
79
79
  };
80
80
  export declare function TokensProvider({ children, value }: TokensProviderProps): ReactNode;
81
- /** Read-only Tokens-Hook. Liefert CSS-var-Strings — nutzbar in
82
- * style-Props oder Tailwind-arbitrary-values wie
83
- * `bg-[var(--color-primary)]` (selten, die shadcn-Tailwind-Klassen
84
- * `bg-primary` sind idiomatischer). */
81
+ /** Read-only tokens hook. Provides CSS variable strings for use in
82
+ * style props or Tailwind arbitrary values such as
83
+ * `bg-[var(--color-primary)]` (rarely needed; the shadcn Tailwind class
84
+ * `bg-primary` is more idiomatic). */
85
85
  export declare function useTokens(): Tokens;
86
- /** Volle API inkl. Theme-Toggle. Für den Dark/Light-Switch-Button. */
86
+ /** Full API including theme toggling, for a dark/light switch button. */
87
87
  export declare function useTokenController(): TokensApi;
88
88
  /** Tokens with var-string values. Constant: the "values" are references,
89
89
  * the real colors change via CSS without mutating the object. Platform
package/dist/tokens.js CHANGED
@@ -1,32 +1,31 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- // Design-Tokens im shadcn-Namensschema. Werte sind CSS-Variable-
3
- // Referenzen (`var(--color-primary)`) — die echten Zahlen leben in
4
- // styles.css (Web) bzw. in einer StyleSheet.create-Config (Native,
5
- // später). Der Hook ist vor allem für Custom-Components die mal
6
- // einen Token-Wert in eine JS-style-prop schieben müssen; die
7
- // Default-Primitives nutzen direkt Tailwind-Klassen und brauchen
8
- // ihn nicht.
2
+ // Design tokens using the shadcn naming scheme. Values are CSS variable
3
+ // references (`var(--color-primary)`); the actual values live in
4
+ // styles.css (Web) or a StyleSheet.create configuration (Native,
5
+ // later). The hook is mainly for custom components that need to pass a
6
+ // token value to a JS style prop; the default primitives use Tailwind
7
+ // classes directly and do not need it.
9
8
  //
10
- // Theme-Toggle läuft über `.dark`-Class auf `<html>` — kein JS-State
11
- // nötig, CSS rendert sofort neu. `useTokenController().toggleTheme()`
12
- // ist nur ein DOM-Wrapper, kein eigener React-State.
9
+ // Theme toggling uses the `.dark` class on `<html>`, so no JS state is
10
+ // needed and CSS updates immediately. `useTokenController().toggleTheme()`
11
+ // is only a DOM wrapper, not separate React state.
13
12
  //
14
- // Erweiterbar: AppTokens-Interface wie bei Primitives. Dev kann
15
- // eigene Token-Kategorien deklarieren und mit var-Strings befüllen.
13
+ // Extensible: augment the AppTokens interface as with primitives. Apps can
14
+ // declare custom token categories and populate them with var strings.
16
15
  import { createContext, useContext } from "react";
17
16
  // ---- Context + Provider + Hooks ----
18
17
  const TokensContext = createContext(undefined);
19
18
  export function TokensProvider({ children, value }) {
20
19
  return _jsx(TokensContext.Provider, { value: value, children: children });
21
20
  }
22
- /** Read-only Tokens-Hook. Liefert CSS-var-Strings — nutzbar in
23
- * style-Props oder Tailwind-arbitrary-values wie
24
- * `bg-[var(--color-primary)]` (selten, die shadcn-Tailwind-Klassen
25
- * `bg-primary` sind idiomatischer). */
21
+ /** Read-only tokens hook. Provides CSS variable strings for use in
22
+ * style props or Tailwind arbitrary values such as
23
+ * `bg-[var(--color-primary)]` (rarely needed; the shadcn Tailwind class
24
+ * `bg-primary` is more idiomatic). */
26
25
  export function useTokens() {
27
26
  return useTokenController().tokens;
28
27
  }
29
- /** Volle API inkl. Theme-Toggle. Für den Dark/Light-Switch-Button. */
28
+ /** Full API including theme toggling, for a dark/light switch button. */
30
29
  export function useTokenController() {
31
30
  const api = useContext(TokensContext);
32
31
  if (api === undefined) {
@@ -1,10 +1,10 @@
1
1
  import { type LocaleResolver } from "../../headless/index.ts";
2
2
  export type CreateBrowserLocaleResolverOptions = {
3
- /** localStorage-Key unter dem die aktive Locale persistiert wird.
4
- * Default: `"kumiko:locale"`. Apps die mehrere Kumiko-Instanzen auf
5
- * derselben Origin mounten (selten), setzen verschiedene Keys. */
3
+ /** localStorage key used to persist the active locale.
4
+ * Default: `"kumiko:locale"`. Apps that mount multiple Kumiko instances on
5
+ * the same origin (rarely) should use different keys. */
6
6
  readonly storageKey?: string;
7
- /** Fallback wenn weder localStorage noch navigator.language liefern.
7
+ /** Fallback when neither localStorage nor navigator.language provides a locale.
8
8
  * Default: `"en"`. */
9
9
  readonly defaultLocale?: string;
10
10
  /** Maps a stored or browser-reported tag onto one of the app's locales;
@@ -14,9 +14,9 @@ export type CreateBrowserLocaleResolverOptions = {
14
14
  readonly normalizeLocale?: (tag: string) => string | undefined;
15
15
  };
16
16
  export declare const BROWSER_LOCALE_STORAGE_KEY = "kumiko:locale";
17
- /** Default-Resolver wenn createKumikoApp ohne `locale`-Option gebootet
18
- * wird. Stateful: setLocale() persistiert in localStorage und ruft
19
- * subscribed listeners. Apps die eine vollwertige i18n-Schicht haben
20
- * (i18next, FormatJS, eigener Store), reichen stattdessen ihre eigene
21
- * Resolver-Impl via `createKumikoApp({ locale })`. */
17
+ /** Default resolver when createKumikoApp is booted without a `locale` option.
18
+ * Stateful: setLocale() persists to localStorage and notifies subscribed
19
+ * listeners. Apps with a full i18n layer (i18next, FormatJS, or a custom
20
+ * store) should instead pass their own resolver via
21
+ * `createKumikoApp({ locale })`. */
22
22
  export declare function createBrowserLocaleResolver(options?: CreateBrowserLocaleResolverOptions): LocaleResolver;
@@ -1,15 +1,15 @@
1
- // Browser-spezifischer LocaleResolver-Default für createKumikoApp.
1
+ // Browser-specific LocaleResolver default for createKumikoApp.
2
2
  //
3
- // Stateful: hält die aktuelle Locale in localStorage, bricht subscribe-
4
- // listener bei setLocale(). Initial-wert kommt aus localStorage wenn
5
- // gespeichert, sonst aus navigator.language, sonst aus defaultLocale.
3
+ // Stateful: stores the current locale in localStorage and notifies subscribed
4
+ // listeners when setLocale() is called. The initial value comes from
5
+ // localStorage when available, otherwise from navigator.language, and finally
6
+ // from defaultLocale.
6
7
  //
7
- // App-Code setzt die Locale programmatisch via resolver.setLocale() —
8
- // der LanguageSwitcher-Component macht genau das. Persistenz über
9
- // localStorage heißt: nach Page-Reload gleiche Sprache, ohne Server-
10
- // Roundtrip. Für device-übergreifende Persistenz würde ein
11
- // user:write:update auf user.locale zusätzlich gesetzt (separater
12
- // App-Code, nicht im Resolver).
8
+ // App code sets the locale programmatically via resolver.setLocale(), as the
9
+ // LanguageSwitcher component does. localStorage persistence preserves the
10
+ // language after a page reload without a server round trip. Cross-device
11
+ // persistence would additionally require a user:write:update for user.locale
12
+ // in separate app code, not in the resolver.
13
13
  import { createStore } from "../../headless/index.js";
14
14
  export const BROWSER_LOCALE_STORAGE_KEY = "kumiko:locale";
15
15
  function detectInitialLocale(storageKey, fallback, normalizeLocale) {
@@ -23,8 +23,8 @@ function detectInitialLocale(storageKey, fallback, normalizeLocale) {
23
23
  }
24
24
  }
25
25
  catch {
26
- // localStorage kann throwen (safari private mode, disabled) —
27
- // leise auf navigator zurückfallen.
26
+ // localStorage can throw (Safari private mode, disabled) —
27
+ // silently fall back to navigator.
28
28
  }
29
29
  }
30
30
  if (typeof navigator !== "undefined" && navigator.language) {
@@ -42,11 +42,11 @@ function detectTimeZone() {
42
42
  return "UTC";
43
43
  }
44
44
  }
45
- /** Default-Resolver wenn createKumikoApp ohne `locale`-Option gebootet
46
- * wird. Stateful: setLocale() persistiert in localStorage und ruft
47
- * subscribed listeners. Apps die eine vollwertige i18n-Schicht haben
48
- * (i18next, FormatJS, eigener Store), reichen stattdessen ihre eigene
49
- * Resolver-Impl via `createKumikoApp({ locale })`. */
45
+ /** Default resolver when createKumikoApp is booted without a `locale` option.
46
+ * Stateful: setLocale() persists to localStorage and notifies subscribed
47
+ * listeners. Apps with a full i18n layer (i18next, FormatJS, or a custom
48
+ * store) should instead pass their own resolver via
49
+ * `createKumikoApp({ locale })`. */
50
50
  export function createBrowserLocaleResolver(options = {}) {
51
51
  const storageKey = options.storageKey ?? BROWSER_LOCALE_STORAGE_KEY;
52
52
  const fallback = options.defaultLocale ?? "en";
@@ -69,8 +69,8 @@ export function createBrowserLocaleResolver(options = {}) {
69
69
  localStorage.setItem(storageKey, next);
70
70
  }
71
71
  catch {
72
- // Persistenz fehlgeschlagen ist nicht fatal — die Session-Locale
73
- // bleibt trotzdem gesetzt, nur der nächste Reload verliert sie.
72
+ // Persistence failure is not fatal — the session locale remains set,
73
+ // but the next reload will lose it.
74
74
  }
75
75
  }
76
76
  },
@@ -12,62 +12,57 @@ export type CollectionNavProviders = {
12
12
  };
13
13
  export type ClientFeatureDefinition = {
14
14
  readonly name: string;
15
- /** Context-Provider die um den kompletten Renderer-Tree gewrapped
16
- * werden. Reihenfolge: erstes Element = äußerster Provider. Alle
17
- * Provider stehen VOR allen Gates im Baum, damit jeder Gate und
18
- * jedes Screen-Child darauf Zugriff hat. */
15
+ /** Context providers wrapped around the complete renderer tree. The first
16
+ * array element is the outermost provider. All providers precede all gates
17
+ * in the tree so every gate and screen child can access them. */
19
18
  readonly providers?: readonly ComponentType<{
20
19
  children: ReactNode;
21
20
  }>[];
22
- /** Screen-Decorators die zwischen dem Provider-Stack und dem Shell/
23
- * Screen-Render sitzen. Typisches Muster: AuthGate rendert den
24
- * LoginScreen statt children solange der User nicht eingeloggt ist.
25
- * Reihenfolge: erstes Element = äußerster Gate. */
21
+ /** Screen decorators placed between the provider stack and the shell or
22
+ * screen render. A typical pattern is AuthGate rendering LoginScreen
23
+ * instead of children while the user is not signed in. The first array
24
+ * element is the outermost gate. */
26
25
  readonly gates?: readonly ComponentType<{
27
26
  children: ReactNode;
28
27
  }>[];
29
- /** Default-Translations die das Feature für seine UI-Strings
30
- * mitbringt. Werden in den LocaleProvider als Fallback-Bundle
31
- * eingehängt — der App-Resolver (z.B. i18next) hat Vorrang und kann
32
- * einzelne Keys überschreiben, ohne dass die Feature-Bundles
33
- * komplett ausgetauscht werden müssen. */
28
+ /** Default translations supplied by the feature for its UI strings. They
29
+ * are added to the LocaleProvider as a fallback bundle; the app resolver
30
+ * (for example, i18next) takes precedence and can override individual keys
31
+ * without replacing the feature bundles entirely. */
34
32
  readonly translations?: TranslationsByLocale;
35
- /** Custom-Screen-Components — Map screenId → React-Component. Wenn
36
- * ein Schema-Screen `type: "custom"` hat, schaut KumikoScreen in
37
- * diese Map (gemerged aus allen ClientFeatures) und rendert die
38
- * passende Component. So muss kein Sample mehr im AppShell-Wrapper
39
- * selbst routen. Convention: screenId entspricht dem `id` aus
40
- * `r.screen({ id, type: "custom", ... })` im server-side Feature. */
33
+ /** Custom screen components, mapped by screenId. When a schema screen has
34
+ * `type: "custom"`, KumikoScreen looks up and renders the matching component
35
+ * in this map, merged from all client features. This keeps routing out of
36
+ * the AppShell wrapper. The convention is that screenId matches the `id` in
37
+ * `r.screen({ id, type: "custom", ... })` in the server-side feature. */
41
38
  readonly components?: Readonly<Record<string, ComponentType>>;
42
- /** Column-Renderer-Components — Map renderer-name → React-Component.
43
- * Schema deklariert eine Column mit
44
- * `renderer: { react: { __component: "ColorSwatch" } }`; client-side
45
- * zieht der DataTable-Cell-Renderer den Component hier raus.
46
- * Schemas bleiben so serializable (nur ein String-Key auf der Wire),
47
- * echte JSX-Renderer leben im Client-Bundle. Last-Wins bei Key-
48
- * Kollision über mehrere Features. */
39
+ /** Column renderer components, mapped by renderer name. A schema declares a
40
+ * column with `renderer: { react: { __component: "ColorSwatch" } }`; the
41
+ * client-side DataTable cell renderer looks up the component here. Schemas
42
+ * remain serializable because only a string key crosses the wire, while the
43
+ * actual JSX renderers live in the client bundle. Later features win on key
44
+ * collisions. */
49
45
  readonly columnRenderers?: Readonly<Record<string, ColumnRendererComponent>>;
50
- /** Extension-Section-Components — Map `__component`-name → React-
51
- * Component. Schema deklariert eine entityEdit-Section mit
52
- * `kind: "extension"` + `component: { react: { __component: "X" } }`;
53
- * RenderEdit zieht den Component hier raus und mountet ihn mit
54
- * `{ entityName, entityId }`. Pattern wie columnRenderers — Last-Wins
55
- * bei Key-Kollision über mehrere Features. */
46
+ /** Extension-section components, mapped by `__component` name. A schema
47
+ * declares an entityEdit section with `kind: "extension"` and
48
+ * `component: { react: { __component: "X" } }`; RenderEdit looks up and
49
+ * mounts the component here with `{ entityName, entityId }`. As with
50
+ * columnRenderers, later features win on key collisions. */
56
51
  readonly extensionSectionComponents?: Readonly<Record<string, ExtensionSectionComponent>>;
57
52
  /** Handlers for RowAction `kind: "clientAction"`, keyed by the action name
58
53
  * `<feature>:<kebab>`. Last-wins with a warning on key collisions, like
59
54
  * columnRenderers. */
60
55
  readonly clientActions?: Readonly<Record<string, ClientActionHandler>>;
61
- /** Nav-Provider für die EINE Nav (Visual-Tree-Merge): hängt dynamische
62
- * Children an einen statischen `r.nav({ provider: true })`-Knoten. Keyed
63
- * auf die LOKALE nav-id (create-app qualifiziert zu `<feature>:nav:<id>`;
64
- * bereits qualifizierte QNs gehen durch). Anders als `treeProvider` (ein
65
- * Top-Level-Branch pro Feature) attacht ein navProvider an einen konkreten
66
- * Nav-Knoten — der idiomatic Weg seit dem Tree→Nav-Merge. */
56
+ /** Nav providers for the single nav visual-tree merge: they attach dynamic
57
+ * children to a static `r.nav({ provider: true })` node. Keys use the local
58
+ * nav ID; create-app qualifies it as `<feature>:nav:<id>`, while already
59
+ * qualified names pass through. Unlike `treeProvider`, which creates one
60
+ * top-level branch per feature, a navProvider attaches to a specific nav
61
+ * node—the idiomatic approach since the tree-to-nav merge. */
67
62
  readonly navProviders?: Readonly<Record<string, TreeChildrenSubscribe>>;
68
- /** SSE-Entity-Listen pro nav-id für den Live-Refresh der navProviders
69
- * (analog `treeEntities`). Live-Event für eine Entity → Provider des
70
- * Knotens wird neu aufgerufen → neue Kinder erscheinen live. */
63
+ /** SSE entity lists per nav ID for live refresh of navProviders, analogous
64
+ * to `treeEntities`. A live event for an entity re-invokes the node's
65
+ * provider so new children appear immediately. */
71
66
  readonly navEntities?: Readonly<Record<string, readonly string[]>>;
72
67
  /** Nav providers derived from the app schema — called with every
73
68
  * `r.contentCollection()` the app declares. A feature that serves any
@@ -85,22 +80,19 @@ export type ClientFeatureDefinition = {
85
80
  * format. Same last-wins semantics as columnRenderers. Public component
86
81
  * contract: `{ value, onChange, variables, readOnly }`. */
87
82
  readonly contentEditors?: Partial<Record<ContentEditorFormat, ContentEditorComponent>>;
88
- /** Editor-Resolver-Komponenten pro featureId:action-Key. Wenn ein
89
- * TreeNode mit target angeklickt wird, schlägt der EditorPanel das
90
- * Component hier nach und rendert es. Komponenten erhalten target
91
- * (mit args) und eine onClose-Callback. Ohne registrierten Resolver
92
- * zeigt der EditorPanel einen Info-Fallback.
93
- * Siehe visual-tree.md V.1.2. */
83
+ /** Editor resolver components keyed by featureId:action. When a TreeNode
84
+ * with a target is clicked, EditorPanel looks up and renders the component
85
+ * here. Components receive target (with args) and an onClose callback. If
86
+ * no resolver is registered, EditorPanel shows an informational fallback. */
94
87
  readonly resolvers?: Readonly<Record<string, ComponentType<{
95
88
  readonly target: TargetRef;
96
89
  readonly onClose: () => void;
97
90
  }>>>;
98
91
  };
99
- /** Wickelt einen ReactNode durch eine Liste von Providern/Gates von
100
- * innen nach außen — erstes Array-Element ist äußerste Hülle.
101
- * Der Key nutzt den Component-Display-Namen (falls gesetzt) plus
102
- * Index, damit Mounts stabil bleiben solange die Wrapper-Liste
103
- * ihre Reihenfolge nicht ändert. */
92
+ /** Wraps a ReactNode with a list of providers or gates from inside out; the
93
+ * first array element is the outermost wrapper. The key uses the component's
94
+ * display name, when available, plus its index, keeping mounts stable while
95
+ * the wrapper list's order does not change. */
104
96
  export declare function stackWrappers(wrappers: readonly ComponentType<{
105
97
  children: ReactNode;
106
98
  }>[], inner: ReactNode): ReactNode;
@@ -1,9 +1,8 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- /** Wickelt einen ReactNode durch eine Liste von Providern/Gates von
3
- * innen nach außen — erstes Array-Element ist äußerste Hülle.
4
- * Der Key nutzt den Component-Display-Namen (falls gesetzt) plus
5
- * Index, damit Mounts stabil bleiben solange die Wrapper-Liste
6
- * ihre Reihenfolge nicht ändert. */
2
+ /** Wraps a ReactNode with a list of providers or gates from inside out; the
3
+ * first array element is the outermost wrapper. The key uses the component's
4
+ * display name, when available, plus its index, keeping mounts stable while
5
+ * the wrapper list's order does not change. */
7
6
  export function stackWrappers(wrappers, inner) {
8
7
  return wrappers.reduceRight((acc, Wrapper, i) => {
9
8
  const key = `${Wrapper.displayName ?? Wrapper.name ?? "wrapper"}-${i}`;
@@ -42,7 +42,7 @@ export type CreateKumikoAppOptions = {
42
42
  * `createLiveDispatcher`); a cross-origin URL is fetched with
43
43
  * `credentials: "include"`. */
44
44
  readonly schemaUrl?: string;
45
- /** RenderEdit's create-mode draftId storage (issue #1913) — where a
45
+ /** RenderEdit's create-mode draftId storage — where a
46
46
  * same-tab reload finds which of several parallel create-sessions on a
47
47
  * screen to resume. Default: `createBrowserDraftStorage()`
48
48
  * (sessionStorage-backed). Apps that don't want any client-side draftId
@@ -52,51 +52,48 @@ export type CreateKumikoAppOptions = {
52
52
  readonly screenQn?: string;
53
53
  readonly translate?: Translate;
54
54
  /** Row-click fallback for entities with no declared `detailFor` screen
55
- * (fw#2164) — a `detailFor` screen always wins over this option. */
55
+ * — a `detailFor` screen always wins over this option. */
56
56
  readonly onRowClick?: (row: ListRowViewModel, entityName: string) => void;
57
- /** App-Shell. Bekommt das resolved `schema` als Prop — so können
58
- * AppShell-Komponenten an WorkspaceShell/DefaultAppShell durchreichen
59
- * ohne selbst das Schema zu importieren oder auf window-Globals zu
60
- * greifen. */
57
+ /** App shell. Receives the resolved `schema` as a prop so AppShell
58
+ * components can pass it through to WorkspaceShell/DefaultAppShell
59
+ * without importing the schema themselves or accessing window globals. */
61
60
  readonly shell?: (props: {
62
61
  readonly children: ReactNode;
63
62
  readonly schema: AppSchema;
64
63
  }) => ReactNode;
65
64
  readonly primitives?: Partial<PrimitivesRegistry>;
66
- /** Feature-gelieferte Client-Extensions. Jedes Element bringt
67
- * Provider + Gates mit — siehe ClientFeatureDefinition. Beispiel:
68
- * `clientFeatures: [emailPasswordClient()]` für Session+Login. */
65
+ /** Client extensions supplied by features. Each element provides a
66
+ * provider and gates; see ClientFeatureDefinition. Example:
67
+ * `clientFeatures: [emailPasswordClient()]` for session and login. */
69
68
  readonly clientFeatures?: readonly ClientFeatureDefinition[];
70
- /** App-level LocaleResolver. Typischerweise ein Adapter um i18next
71
- * oder eine eigene Store-Impl. Wenn nicht gesetzt → Static-Default
72
- * (`locale: "en"`, `translate: key → key`); Plugin-Translations
73
- * springen dann als Fallback ein. */
69
+ /** App-level LocaleResolver. Typically an adapter around i18next
70
+ * or a custom store. If unset, the static default is used
71
+ * (`locale: "en"`, `translate: key → key`); plugin translations then
72
+ * serve as fallbacks. */
74
73
  readonly locale?: LocaleResolver;
75
- /** Nav-Adapter — ein React-Hook der eine NavApi-Instanz liefert
76
- * (route + navigate + hrefFor). Default: `useBrowserNavApi`, das
77
- * window.history als Source-of-Truth benutzt. Wer einen anderen
78
- * Router anbinden will (TanStack Router, Expo Linking auf Mobile,
79
- * Memory-Router in Tests) übergibt hier seinen eigenen Hook.
74
+ /** Navigation adapter — a React hook that returns a NavApi instance
75
+ * (route + navigate + hrefFor). Default: `useBrowserNavApi`, which uses
76
+ * window.history as the source of truth. To integrate another router
77
+ * (TanStack Router, Expo Linking on mobile, or a memory router in tests),
78
+ * provide your own hook here.
80
79
  *
81
- * Der Hook wird EINMAL im Component-Tree aufgerufen (siehe
82
- * `BrowserNavBoot`), muss also den React-Rules-of-Hooks folgen —
83
- * `useSyncExternalStore` auf der zugrundeliegenden Router-State
84
- * ist das gängige Pattern.
80
+ * The hook is called ONCE in the component tree (see `BrowserNavBoot`),
81
+ * so it must follow the React Rules of Hooks — `useSyncExternalStore` on
82
+ * the underlying router state is the usual pattern.
85
83
  *
86
- * Wenn das Schema Workspaces deklariert, wird der Adapter mit
87
- * `{ hasWorkspaces: true }` aufgerufen — der Default-Hook nutzt das
88
- * um das URL-Pattern auf `/<workspace>/<screen>[/<entityId>]`
89
- * umzustellen. Eigene Adapter dürfen die Option ignorieren wenn ihr
90
- * Router das anders löst. `features` ist `app.features` — der
91
- * Default-Hook braucht sie für resolveTarget() (ObjectTarget →
92
- * ScreenTarget); eigene Adapter die nie ein ObjectTarget bekommen,
93
- * dürfen sie ignorieren. */
84
+ * When the schema declares workspaces, the adapter is called with
85
+ * `{ hasWorkspaces: true }`; the default hook uses this to change the URL
86
+ * pattern to `/<workspace>/<screen>[/<entityId>]`. Custom adapters may
87
+ * ignore this option if their router handles it differently. `features` is
88
+ * `app.features`; the default hook needs it for resolveTarget()
89
+ * (ObjectTarget → ScreenTarget). Adapters that never receive an
90
+ * ObjectTarget may ignore it. */
94
91
  readonly navAdapter?: (options?: {
95
92
  readonly hasWorkspaces?: boolean;
96
93
  readonly features?: readonly FeatureSchema[];
97
94
  }) => NavApi;
98
95
  /** App-wide default width for form/detail screens that don't set their
99
- * own `layout.width` (fw#2656). Default "4xl" (unchanged behavior) — a
96
+ * own `layout.width`. Default "4xl" (unchanged behavior) — a
100
97
  * consumer app that wants e.g. bundled-feature screens to match its own
101
98
  * full-width lists passes `screenWidth: "full"` once here instead of
102
99
  * forking every screen it doesn't own. Per-screen `layout.width` always