@cosmicdrift/kumiko-renderer 0.337.1 → 0.339.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.
@@ -2386,6 +2386,23 @@ function mapFailedConfigCommandIssuesToField(error, failedIndex, commandFieldNam
2386
2386
  },
2387
2387
  };
2388
2388
  }
2389
+ // A number/money field without a declared default starts empty: buildInitialValues
2390
+ // maps it to 0, which a save would persist as a deliberate "0" override.
2391
+ function buildConfigEditDefaults(fields) {
2392
+ const defaults = buildInitialValues(fields); // @cast-boundary render-helper
2393
+ for (const [shortName, fieldDef] of Object.entries(fields)) {
2394
+ const { type, default: fieldDefault } = fieldDef; // @cast-boundary schema-walk
2395
+ if ((type === "number" || type === "money") && fieldDefault === undefined) {
2396
+ defaults[shortName] = undefined;
2397
+ }
2398
+ }
2399
+ return defaults;
2400
+ }
2401
+ function isEmptyNumericFormValue(value) {
2402
+ if (value === undefined || value === null || value === "")
2403
+ return true;
2404
+ return typeof value === "number" && !Number.isFinite(value);
2405
+ }
2389
2406
  function ConfigEditBody({ schema, screen, translate, }) {
2390
2407
  const { ConfigCascadeView } = usePrimitives();
2391
2408
  const t = useTranslation();
@@ -2408,7 +2425,7 @@ function ConfigEditBody({ schema, screen, translate, }) {
2408
2425
  if (valuesQuery.data === null)
2409
2426
  return null;
2410
2427
  const out = {};
2411
- const defaults = buildInitialValues(screen.fields); // @cast-boundary render-helper
2428
+ const defaults = buildConfigEditDefaults(screen.fields);
2412
2429
  for (const [shortName, fieldDef] of Object.entries(screen.fields)) {
2413
2430
  const qualified = screen.configKeys[shortName];
2414
2431
  if (qualified === undefined) {
@@ -2512,6 +2529,16 @@ function ConfigEditBody({ schema, screen, translate, }) {
2512
2529
  });
2513
2530
  continue;
2514
2531
  }
2532
+ // Cleared number/money: drop this scope's override instead of storing an empty value.
2533
+ if ((ftype === "number" || ftype === "money") &&
2534
+ isEmptyNumericFormValue(ftype === "money" ? unwrapMoneyValue(value) : value)) {
2535
+ commandFieldNames.push(shortName);
2536
+ commands.push({
2537
+ type: "config:write:reset",
2538
+ payload: { key: qualified, scope: screen.scope },
2539
+ });
2540
+ continue;
2541
+ }
2515
2542
  commandFieldNames.push(shortName);
2516
2543
  commands.push({
2517
2544
  type: "config:write:set",
@@ -23,6 +23,8 @@ export const kumikoDefaultTranslations = {
23
23
  "kumiko.actions.next": "Next",
24
24
  "kumiko.actions.back": "Back",
25
25
  "kumiko.actions.finish": "Finish",
26
+ "kumiko.boolean.no": "No",
27
+ "kumiko.boolean.yes": "Yes",
26
28
  "kumiko.wizard.step": "Step {current} of {total}",
27
29
  "kumiko.wizard.up-next": "Up next",
28
30
  "kumiko.wizard.step-with-title": "Step {current} of {total} · {title}",
@@ -76,6 +78,7 @@ export const kumikoDefaultTranslations = {
76
78
  "kumiko.list.count.other": "{count} entries",
77
79
  "kumiko.list.row-actions.more": "More actions",
78
80
  "kumiko.page-header.actions": "Page actions",
81
+ "kumiko.theme.auto": "Automatic theme",
79
82
  "kumiko.theme.dark": "Dark theme",
80
83
  "kumiko.theme.light": "Light theme",
81
84
  "kumiko.list.row.collapse": "Collapse {title}",
@@ -136,6 +139,9 @@ export const kumikoDefaultTranslations = {
136
139
  "kumiko.dialog.confirm": "Confirm",
137
140
  "kumiko.dialog.cancel": "Cancel",
138
141
  "kumiko.dialog.close": "Close",
142
+ "kumiko.lightbox.previous": "Previous image",
143
+ "kumiko.lightbox.next": "Next image",
144
+ "kumiko.lightbox.position": "{current} / {total}",
139
145
  "kumiko.drawer.discard.title": "Discard changes?",
140
146
  "kumiko.drawer.discard.body": "Your input in this form will be lost.",
141
147
  "kumiko.drawer.discard.confirm": "Discard",
package/dist/index.d.ts CHANGED
@@ -70,7 +70,7 @@ export { createStaticLocaleResolver, FormalityProvider, LocaleProvider, mergeTra
70
70
  export { kumikoDefaultTranslations } from "./i18n-defaults.js";
71
71
  export { InsideDrawerProvider, useInsideDrawer } from "./inside-drawer.js";
72
72
  export { PageHeaderCompactProvider, PageHeaderSlotAvailableProvider, usePageHeaderCompact, usePageHeaderSlotAvailable, } from "./page-header-slot.js";
73
- export type { ActionMenuItemSpec, ActionOverflowMenuProps, AppPrimitives, BannerProps, ButtonProps, CardOptions, CardProps, CardSlots, CopyButtonProps, CorePrimitives, DataTableDateRangeFacet, DataTableFacet, DataTableFacetChip, DataTableProps, DataTableRowAction, DataTableRowActionMode, DataTableRowGrouping, DataTableSort, DataTableSortDir, DialogProps, DrawerProps, EmbeddedListCellType, EmbeddedListColumn, EmbeddedListInputProps, EmbeddedListTotal, FieldCellWidth, FieldProps, FillContainerProps, FooterActionMarker, FooterActionRole, FormProps, FormSectionNavItem, FormWidth, GridCellProps, GridProps, HeadingProps, InputProps, JsonViewProps, LightboxProps, LinkProps, MetricBandProps, MetricProps, ModalProps, PageHeaderProps, PrimitivesProviderProps, PrimitivesRegistry, ProgressProps, ProgressTone, PromoPanelAction, PromoPanelProps, RuntimeRenderer, SecretRevealProps, SecretRevealValue, SectionProps, ShareButtonProps, StatusBadgeProps, StatusTone, StepBarProps, StickyActionBarProps, StickyPrimaryActionMarker, TabsProps, TextProps, TrailingInputAction, WizardStepGroupProps, } from "./primitives.js";
73
+ export type { ActionMenuItemSpec, ActionOverflowMenuProps, AppPrimitives, BannerProps, ButtonProps, CardOptions, CardProps, CardSlots, CopyButtonProps, CorePrimitives, DataTableDateRangeFacet, DataTableFacet, DataTableFacetChip, DataTableProps, DataTableRowAction, DataTableRowActionMode, DataTableRowGrouping, DataTableSort, DataTableSortDir, DialogProps, DrawerProps, EmbeddedListCellType, EmbeddedListColumn, EmbeddedListInputProps, EmbeddedListTotal, FieldCellWidth, FieldProps, FillContainerProps, FooterActionMarker, FooterActionRole, FormProps, FormSectionNavItem, FormWidth, GridCellProps, GridProps, HeadingProps, InputProps, JsonViewProps, LightboxImage, LightboxProps, LinkProps, MetricBandProps, MetricProps, ModalProps, PageHeaderProps, PrimitivesProviderProps, PrimitivesRegistry, ProgressProps, ProgressTone, PromoPanelAction, PromoPanelProps, RuntimeRenderer, SecretRevealProps, SecretRevealValue, SectionProps, ShareButtonProps, StatusBadgeProps, StatusTone, StepBarProps, StickyActionBarProps, StickyPrimaryActionMarker, TabsProps, TextProps, TrailingInputAction, WizardStepGroupProps, } from "./primitives.js";
74
74
  export { FOOTER_ACTION_ROLE_PROP, NARROW_LABEL_PROP, PrimitivesProvider, STICKY_PRIMARY_ACTION_PROP, shouldRenderActionsIconOnly, statusToneForOptionTone, statusToneForValue, usePrimitives, } from "./primitives.js";
75
75
  export type { SessionEndedSignal, SessionEndedSignalProviderProps, } from "./session/session-ended.js";
76
76
  export { createSessionEndedSignal, isSessionEndedError, SESSION_ENDED_ERROR_CODES, SessionEndedSignalProvider, useSessionEndedSignal, withSessionEndedDetection, } from "./session/session-ended.js";
@@ -78,5 +78,5 @@ export { buildWhatsAppShareUrl } from "./share-links.js";
78
78
  export { sortByAccessor } from "./sort-by-accessor.js";
79
79
  export type { LiveEvent, LiveEventSubscriber, LiveEventsProviderProps } from "./sse/live-events.js";
80
80
  export { LiveEventsProvider, useLiveEvents } from "./sse/live-events.js";
81
- export type { AppTokens, ColorTokens, CoreTokens, FontTokens, RadiusTokens, ShadowTokens, SpacingTokens, ThemeMode, Tokens, TokensApi, TokensProviderProps, } from "./tokens.js";
81
+ export type { AppTokens, ColorTokens, CoreTokens, FontTokens, RadiusTokens, ShadowTokens, SpacingTokens, ThemeMode, ThemePreference, Tokens, TokensApi, TokensProviderProps, } from "./tokens.js";
82
82
  export { cssVarTokens, TokensProvider, useTokenController, useTokens } from "./tokens.js";
@@ -1069,15 +1069,27 @@ export type DrawerProps = {
1069
1069
  readonly children: ReactNode;
1070
1070
  readonly testId?: string;
1071
1071
  };
1072
- /** Image lightbox — full-size preview on click. Web renders Radix overlay;
1073
- * trigger (thumbnail) and open state live in the app. */
1074
- export type LightboxProps = {
1075
- readonly open: boolean;
1076
- readonly onOpenChange: (open: boolean) => void;
1072
+ export type LightboxImage = {
1077
1073
  readonly src: string;
1078
1074
  readonly alt: string;
1075
+ };
1076
+ type LightboxBaseProps = {
1077
+ readonly open: boolean;
1078
+ readonly onOpenChange: (open: boolean) => void;
1079
1079
  readonly testId?: string;
1080
1080
  };
1081
+ /** Image lightbox — full-size preview on click. Web renders Radix overlay;
1082
+ * trigger (thumbnail) and open state live in the app. Takes either one image
1083
+ * (`src`/`alt`) or a browsable set (`images` + controlled `index`);
1084
+ * navigation wraps around at both ends. */
1085
+ export type LightboxProps = LightboxBaseProps & ({
1086
+ readonly src: string;
1087
+ readonly alt: string;
1088
+ } | {
1089
+ readonly images: readonly LightboxImage[];
1090
+ readonly index: number;
1091
+ readonly onIndexChange: (index: number) => void;
1092
+ });
1081
1093
  /** Source-badge for one cascade step (User / Tenant / System / …).
1082
1094
  * Used inline next to a config value to indicate where it came from.
1083
1095
  * Requires a LocaleProvider above it (labels run through useTranslation)
package/dist/tokens.d.ts CHANGED
@@ -58,10 +58,19 @@ export interface AppTokens {
58
58
  }
59
59
  export type Tokens = CoreTokens & AppTokens;
60
60
  export type ThemeMode = "light" | "dark";
61
+ /** "auto" follows the OS color scheme live; light/dark are explicit choices. */
62
+ export type ThemePreference = ThemeMode | "auto";
61
63
  export type TokensApi = {
62
64
  readonly tokens: Tokens;
65
+ /** The scheme currently applied (resolved from the preference when it is "auto"). */
63
66
  readonly mode: ThemeMode;
67
+ /** Optional so hand-built TokensApi values (tests, custom providers) stay valid:
68
+ * absent means a plain light/dark implementation, read as `mode`. */
69
+ readonly preference?: ThemePreference;
70
+ readonly setPreference?: (preference: ThemePreference) => void;
71
+ /** Explicit choice: leaves "auto". */
64
72
  readonly setMode: (mode: ThemeMode) => void;
73
+ /** Explicit flip of the resolved mode: leaves "auto". */
65
74
  readonly toggleMode: () => void;
66
75
  };
67
76
  export type TokensProviderProps = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cosmicdrift/kumiko-renderer",
3
- "version": "0.337.1",
3
+ "version": "0.339.0",
4
4
  "description": "Platform-agnostic React renderer for Kumiko screens. Contains the shared logic — primitives-contract, hooks, KumikoScreen, navigation & SSE abstractions — that any platform-specific renderer (web, native) composes. No DOM, no EventSource, no react-dom.",
5
5
  "license": "BUSL-1.1",
6
6
  "author": "Marc Frost <marc@cosmicdriftgamestudio.com>",
@@ -15,9 +15,9 @@
15
15
  }
16
16
  },
17
17
  "dependencies": {
18
- "@cosmicdrift/kumiko-framework": "0.337.1",
19
- "@cosmicdrift/kumiko-headless": "0.337.1",
20
- "@cosmicdrift/kumiko-types": "0.337.1",
18
+ "@cosmicdrift/kumiko-framework": "0.339.0",
19
+ "@cosmicdrift/kumiko-headless": "0.339.0",
20
+ "@cosmicdrift/kumiko-types": "0.339.0",
21
21
  "react": "^19.2.6",
22
22
  "temporal-polyfill": "^0.3.2",
23
23
  "zod": "^4.4.3"
@@ -28,7 +28,7 @@
28
28
  "@types/react-dom": "^19.2.3",
29
29
  "jsdom": "^29.1.1",
30
30
  "react-dom": "^19.2.6",
31
- "@cosmicdrift/kumiko-locale-de": "0.337.1"
31
+ "@cosmicdrift/kumiko-locale-de": "0.339.0"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",
package/src/changes.json CHANGED
@@ -1,4 +1,16 @@
1
1
  [
2
+ {
3
+ "version": "0.339.0",
4
+ "type": "improvement",
5
+ "title": "Lightbox pages through multiple images",
6
+ "detail": "`LightboxProps` is now a union: the existing `src`/`alt` form is unchanged, and the new `images` + `index` + `onIndexChange` form renders previous/next buttons, a position counter and ArrowLeft/ArrowRight navigation with wrap-around when more than one image is given. The Apex lightbox collects every `.shot-frame img` on open and gains previous/next buttons; its CSP script hash changed. New i18n keys: `kumiko.lightbox.previous`, `kumiko.lightbox.next`, `kumiko.lightbox.position`."
7
+ },
8
+ {
9
+ "version": "0.338.0",
10
+ "type": "fix",
11
+ "title": "configEdit number and money fields start empty without a default",
12
+ "detail": "Number and money fields without a default no longer show 0, so saving does not write an unintended 0. Clearing a stored value resets this scope's override instead of sending an empty number."
13
+ },
2
14
  {
3
15
  "version": "0.337.1",
4
16
  "type": "improvement",