@netlisian/softconfig 0.1.4 → 0.1.6

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.
package/README.md CHANGED
@@ -1,122 +1,62 @@
1
- # Puck Soft Config
2
-
3
- Enhanced configuration and UI utilities for Puck that add “soft components” (composable, versioned, and remodelable components) plus ready-to-use UI chrome.
4
-
5
- ## Packages & Exports
6
-
7
- - `@netlisian/softconfig` → bundled CJS/ESM + types (`./dist/index.{js,mjs,d.ts}`)
8
- - `@netlisian/softconfig/puck` → Puck-focused entry (`./dist/puck/index.*`)
9
- - Styles: `@netlisian/softconfig/styles.css` (alias `./dist/index.css`) and `@netlisian/softconfig/puck/index.css`
10
-
11
- Peer dependencies: `react@^18`, `@measured/puck@0.20.x`.
12
-
13
- ## Install
14
-
15
- ```bash
16
- pnpm add @netlisian/softconfig @measured/puck react
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ```tsx
22
- import { SoftConfigProvider } from "@netlisian/softconfig/puck";
23
- import "@netlisian/softconfig/styles.css";
24
-
25
- const hardConfig = { components: {/* your hard Puck components */} };
26
- const softComponents = {/* optional persisted soft components */};
27
- const overrides = {/* optional UI overrides like map/action bar/etc. */};
28
-
29
- export default function App({ children }: { children: React.ReactNode }) {
30
- return (
31
- <SoftConfigProvider
32
- hardConfig={hardConfig}
33
- softComponents={softComponents}
34
- overrides={overrides}
35
- >
36
- {(softConfig, softComponentsRegistry) => (
37
- /* render Puck with the soft-config-driven config */
38
- <YourPuckHost config={softConfig} softComponents={softComponentsRegistry} />
39
- )}
40
- </SoftConfigProvider>
41
- );
42
- }
43
- ```
44
-
45
- `SoftConfigProvider` accepts an optional `value` prop if you want to bring your own Zustand store (see `src/puck/context/storeProvider.tsx`). Children are rendered via a render-prop to give you the hydrated `softConfig` and `softComponents` to pass directly to Puck.
46
-
47
- ## Field Mapping & Transforms
48
-
49
- - Soft components support `_map` entries with optional transforms or CVA-like configs. Because functions are not persisted, the library regenerates missing transforms at runtime (including during remodel/decompose) using the stored CVA config.
50
- - Field defaults are read from `_fieldSettings` when resolving mappings, so mapped props fall back to configured defaults when source data is absent.
51
-
52
- ## Styles
53
-
54
- Import once in your app root:
55
-
56
- ```ts
57
- import "@netlisian/softconfig/styles.css";
58
- ```
59
-
60
- ## Overrides
61
-
62
- You can supply custom overrides (action bar, headers, map UI, etc.) through the `overrides` prop. See `src/puck/types/Overrides.ts` for the full surface.
63
-
64
- ## Action Lifecycle Events
65
-
66
- `SoftConfigProvider` supports `onActions` for lifecycle callbacks:
67
-
68
- - `build` → when build mode starts
69
- - `remodel` → when remodel mode starts
70
- - `complete` → when a build/remodel is finalized
71
- - `inspect` → when inspect is requested
72
- - `demolish` → when a soft component is deleted
73
- - `setDefaultVersion`, `decompose`, `cancel`, `publish`
74
-
75
- Version-aware payloads are included for build finalization and inspection flows:
76
-
77
- ```ts
78
- type ActionEventPayload =
79
- | {
80
- type: "remodel";
81
- payload: {
82
- id: string;
83
- version?: string;
84
- softComponent?: VersionedSoftComponent["versions"][string];
85
- };
86
- }
87
- | {
88
- type: "complete";
89
- payload: {
90
- id: string;
91
- version: string;
92
- componentData: Record<string, any>;
93
- softComponent: VersionedSoftComponent["versions"][string];
94
- };
95
- }
96
- | {
97
- type: "inspect";
98
- payload: {
99
- id: string;
100
- version?: string;
101
- softComponent?: VersionedSoftComponent["versions"][string];
102
- };
103
- }
104
- | {
105
- type: "demolish";
106
- payload: {
107
- id: string;
108
- };
109
- };
110
- ```
111
-
112
- This contract allows downstream consumers (for example, save/sync bridges) to persist using exact version metadata without relying on inferred/default versions.
113
-
114
- ## Development
115
-
116
- - Build: `pnpm --filter @netlisian/softconfig build`
117
- - Dev (watch): `pnpm --filter @netlisian/softconfig dev`
118
- - Lint: `pnpm --filter @netlisian/softconfig lint`
119
-
120
- ## License
121
-
122
- MIT
1
+ # @netlisian/soft-config
2
+
3
+ The core library for building and managing **Soft Components** within the Netlisian ecosystem. It provides type-safe field definitions, recursive mapping logic, and state management for the Puck editor.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @netlisian/soft-config
9
+ ```
10
+
11
+ ## Features
12
+
13
+ - **Soft Component Builder**: Easy-to-use interface for creating versioned components.
14
+ - **Dynamic Field Options**: Automatically generates dot-notated mapping paths for nested objects and arrays.
15
+ - **Custom Field Support**: Extend the editor with custom UI while maintaining return type safety.
16
+ - **Puck Overrides**: Pre-built component overrides (ActionBar, Header, ComponentList) to seamlessly integrate Soft Components into the Puck editor.
17
+
18
+ ## Key Concepts
19
+
20
+ ### Soft Component Definitions
21
+
22
+ A Soft Component consists of fields, default props, and typed components.
23
+
24
+ ```typescript
25
+ import { SoftFieldDefinition } from "@netlisian/soft-config";
26
+
27
+ const galleryField: SoftFieldDefinition = {
28
+ name: "gallery",
29
+ type: "array",
30
+ subFields: [
31
+ { name: "src", type: "text" },
32
+ { name: "alt", type: "text" }
33
+ ]
34
+ };
35
+ ```
36
+
37
+ ### Custom Field Extensibility
38
+
39
+ Define custom field types with specified return types (`string`, `number`, `boolean`, `object`, `array`).
40
+
41
+ ```typescript
42
+ import { CustomFields } from "@netlisian/soft-config";
43
+
44
+ export const myCustomFields: CustomFields = {
45
+ "my-color": {
46
+ field: { type: "custom", render: () => <div>Color Picker</div> },
47
+ returnType: "string"
48
+ }
49
+ };
50
+ ```
51
+
52
+ ### Field Mapping
53
+
54
+ The library handles the logic for mapping configuration data to component props, including recursive array iteration using `[]` syntax (e.g., `features[].title`).
55
+
56
+ ## Documentation
57
+
58
+ For detailed guides and API references, check the [Netlisian Docs](https://docs.netlisian.com).
59
+
60
+ ## License
61
+
62
+ MIT
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["css-module:/media/manual_mount/osamuProjects/netlisian/packages/soft-config/src/puck/components/error-boundary/styles.module.css/#css-module-data","css-module:/media/manual_mount/osamuProjects/netlisian/packages/soft-config/src/puck/overrides/Header.module.css/#css-module-data","css-module:/media/manual_mount/osamuProjects/netlisian/packages/soft-config/src/puck/overrides/ActionBar.module.css/#css-module-data","css-module:/media/manual_mount/osamuProjects/netlisian/packages/soft-config/src/puck/overrides/DrawerItem.module.css/#css-module-data","css-module:/media/manual_mount/osamuProjects/netlisian/packages/soft-config/src/puck/components/modal/styles.module.css/#css-module-data","css-module:/media/manual_mount/osamuProjects/netlisian/packages/soft-config/src/puck/overrides/Drawer.module.css/#css-module-data"],"sourcesContent":["/* ErrorBoundary - Following SUIT CSS naming convention */\r\n\r\n._ErrorBoundary_1xl05_5 {\r\n padding: 20px;\r\n border: 1px solid #ff5858;\r\n border-radius: 4px;\r\n background-color: #fff0f0;\r\n margin: 10px 0;\r\n}\r\n\r\n._ErrorBoundary-title_1xl05_21 {\r\n color: #d32f2f;\r\n margin: 0 0 10px 0;\r\n}\r\n\r\n._ErrorBoundary-details_1xl05_31 {\r\n white-space: pre-wrap;\r\n}\r\n\r\n._ErrorBoundary-button_1xl05_39 {\r\n margin-top: 10px;\r\n padding: 5px 10px;\r\n background-color: #d32f2f;\r\n color: white;\r\n border: none;\r\n border-radius: 4px;\r\n cursor: pointer;\r\n}\r\n\r\n._ErrorBoundary-button_1xl05_39:hover {\r\n background-color: #b71c1c;\r\n}\r\n","._Header_19oj9_1 {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n gap: 0.5rem;\r\n flex-wrap: wrap;\r\n}\r\n","/* ActionBar - SUIT CSS naming convention (renamed from CustomActionBar) */\r\n\r\n._ActionBar_pvuie_5 {\r\n align-items: center;\r\n cursor: default;\r\n display: flex;\r\n width: auto;\r\n padding: 4px;\r\n padding-inline-start: 0;\r\n padding-inline-end: 0;\r\n border-top-left-radius: 8px;\r\n border-top-right-radius: 8px;\r\n border-radius: 8px;\r\n background: var(--puck-color-grey-01);\r\n color: var(--puck-color-white);\r\n font-family: var(--puck-font-family);\r\n min-height: 26px;\r\n}\r\n\r\n._ActionBar-label_pvuie_39 {\r\n color: var(--puck-color-grey-08);\r\n font-size: var(--puck-font-size-xxxs);\r\n font-weight: 500;\r\n padding-inline-start: 8px;\r\n padding-inline-end: 8px;\r\n margin-inline-start: 4px;\r\n margin-inline-end: 4px;\r\n text-overflow: ellipsis;\r\n white-space: nowrap;\r\n}\r\n\r\n._ActionBar-action_pvuie_63 + ._ActionBar-label_pvuie_39 {\r\n padding-inline-start: 0;\r\n}\r\n\r\n._ActionBar-label_pvuie_39 + ._ActionBar-action_pvuie_63 {\r\n margin-inline-start: -4px;\r\n}\r\n\r\n._ActionBar-group_pvuie_79 {\r\n align-items: center;\r\n border-inline-start: 0.5px solid var(--puck-color-grey-05);\r\n display: flex;\r\n height: 100%;\r\n padding-inline-start: 4px;\r\n padding-inline-end: 4px;\r\n}\r\n\r\n._ActionBar-group_pvuie_79:first-of-type {\r\n border-inline-start: 0;\r\n}\r\n\r\n._ActionBar-group_pvuie_79:empty {\r\n display: none;\r\n}\r\n\r\n._ActionBar-action_pvuie_63 {\r\n background: transparent;\r\n border: none;\r\n color: var(--puck-color-grey-08);\r\n cursor: pointer;\r\n padding: 6px 8px;\r\n margin-inline-start: 4px;\r\n margin-inline-end: 4px;\r\n border-radius: 4px;\r\n overflow: hidden;\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n transition: color 50ms ease-in;\r\n}\r\n\r\n._ActionBar-action_pvuie_63 svg {\r\n max-width: none !important;\r\n}\r\n\r\n._ActionBar-action_pvuie_63:focus-visible {\r\n outline: 2px solid var(--puck-color-azure-05);\r\n outline-offset: -2px;\r\n}\r\n\r\n@media (hover: hover) and (pointer: fine) {\r\n ._ActionBar-action_pvuie_63:hover {\r\n color: var(--puck-color-azure-06);\r\n transition: none;\r\n }\r\n}\r\n\r\n._ActionBar-action_pvuie_63:active {\r\n color: var(--puck-color-azure-07);\r\n transition: none;\r\n}\r\n\r\n._ActionBar-group_pvuie_79 * {\r\n margin: 0;\r\n}\r\n","._DrawerItem_182aj_1 {\n background: var(--puck-color-white);\n cursor: grab;\n border: 1px solid var(--puck-color-grey-09);\n font-size: var(--puck-font-size-xxs);\n border-radius: 4px;\n justify-content: space-between;\n align-items: center;\n padding: 12px;\n transition: background-color 50ms ease-in, color 50ms ease-in;\n display: flex;\n}\n\n._DrawerItem--insertDisabled_182aj_14 {\n cursor: not-allowed;\n color: var(--puck-color-grey-5);\n background: var(--puck-color-grey-11);\n pointer-events: none; /* Optional: prevents all interactions including settings */\n}\n\n._DrawerItem-content_182aj_21 {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n text-overflow: ellipsis;\n white-space: nowrap;\n overflow-x: hidden;\n}\n\n._DrawerItem-name_182aj_31 {\n font-weight: 400;\n}\n\n._DrawerItem-version_182aj_35 {\n font-size: 10px;\n color: var(--puck-color-grey-05);\n}\n\n._DrawerItem-actions_182aj_40 {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n._DrawerItem-settingsButton_182aj_46 {\n opacity: 0;\n transition: opacity 120ms ease;\n color: var(--puck-color-grey-05);\n}\n\n._DrawerItem_182aj_1:hover ._DrawerItem-settingsButton_182aj_46 {\n opacity: 1;\n}\n\n._DrawerItem-grip_182aj_56 {\n color: var(--puck-color-grey-05);\n display: flex;\n align-items: center;\n}\n\n/* Modal styles */\n._DrawerItem-modal_182aj_63 {\n background: var(--puck-color-white);\n display: flex;\n flex-direction: column;\n height: min(90dvh, 100%);\n min-height: 0;\n}\n\n._DrawerItem-modalHeader_182aj_71 {\n padding: 24px 32px;\n border-bottom: 1px solid var(--puck-color-grey-09);\n flex-shrink: 0;\n}\n\n._DrawerItem-modalTitle_182aj_77 {\n margin: 0 0 4px 0;\n font-size: 24px;\n font-weight: 600;\n color: var(--puck-color-black);\n}\n\n._DrawerItem-modalSubtitle_182aj_84 {\n margin: 0;\n font-size: 14px;\n color: var(--puck-color-grey-04);\n}\n\n._DrawerItem-modalBody_182aj_90 {\n flex: 1;\n min-height: 0;\n padding: 32px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 32px;\n}\n\n._DrawerItem-section_182aj_100 {\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n._DrawerItem-sectionTitle_182aj_106 {\n margin: 0;\n font-size: 16px;\n font-weight: 600;\n color: var(--puck-color-black);\n}\n\n._DrawerItem-sectionDescription_182aj_113 {\n margin: 0;\n font-size: 14px;\n color: var(--puck-color-grey-04);\n}\n\n._DrawerItem-versionList_182aj_119 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n._DrawerItem-versionRow_182aj_125 {\n display: flex;\n justify-content: space-between;\n align-items: center;\n padding: 16px;\n border-radius: 6px;\n border: 1px solid var(--puck-color-grey-09);\n background: var(--puck-color-white);\n transition: all 150ms ease;\n}\n\n._DrawerItem-versionRow--isDefault_182aj_136 {\n border-color: var(--puck-color-azure-07);\n background: var(--puck-color-azure-10);\n}\n\n._DrawerItem-versionRow--isMarkedForDeletion_182aj_141 {\n opacity: 0.6;\n background: var(--puck-color-grey-11);\n}\n\n._DrawerItem-versionInfo_182aj_146 {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n}\n\n._DrawerItem-versionNumber_182aj_153 {\n font-size: 14px;\n font-weight: 500;\n color: var(--puck-color-black);\n}\n\n._DrawerItem-defaultBadge_182aj_159 {\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n background: var(--puck-color-azure-07);\n color: var(--puck-color-white);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n._DrawerItem-deleteBadge_182aj_170 {\n font-size: 11px;\n padding: 3px 8px;\n border-radius: 4px;\n background: var(--puck-color-grey-07);\n color: var(--puck-color-white);\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n}\n\n._DrawerItem-versionActions_182aj_181 {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n._DrawerItem-migrationOptions_182aj_187 {\n width: 100%;\n}\n\n._DrawerItem-migrationList_182aj_191 {\n display: flex;\n flex-direction: column;\n gap: 8px;\n max-height: 180px;\n overflow-y: auto;\n padding: 8px;\n border: 1px solid var(--puck-color-grey-09);\n border-radius: 6px;\n background: var(--puck-color-white);\n}\n\n._DrawerItem-migrationOption_182aj_187 {\n width: 100%;\n padding: 10px 12px;\n border: 1px solid var(--puck-color-grey-09);\n border-radius: 6px;\n font-size: 14px;\n background: var(--puck-color-white);\n color: var(--puck-color-black);\n cursor: pointer;\n transition: border-color 150ms ease;\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n text-align: start;\n}\n\n._DrawerItem-migrationOption_182aj_187:hover {\n border-color: var(--puck-color-grey-07);\n}\n\n._DrawerItem-migrationOption_182aj_187:focus {\n outline: none;\n border-color: var(--puck-color-azure-07);\n}\n\n._DrawerItem-migrationOption--isSelected_182aj_229 {\n border-color: var(--puck-color-azure-07);\n background: var(--puck-color-azure-10);\n}\n\n._DrawerItem-migrationOptionLabel_182aj_234 {\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n._DrawerItem-modalFooter_182aj_240 {\n padding: 20px 32px;\n border-top: 1px solid var(--puck-color-grey-09);\n display: flex;\n justify-content: space-between;\n align-items: center;\n background: var(--puck-color-grey-11);\n flex-shrink: 0;\n}\n\n._DrawerItem-footerLeft_182aj_250 {\n display: flex;\n gap: 12px;\n}\n\n._DrawerItem-footerRight_182aj_255 {\n display: flex;\n gap: 12px;\n}\n","._Modal_1t9ot_1 {\r\n background: color-mix(in srgb, var(--puck-color-black) 75%, transparent);\r\n display: none;\r\n justify-content: center;\r\n align-items: center;\r\n position: fixed;\r\n top: 0;\r\n left: 0;\r\n bottom: 0;\r\n right: 0;\r\n z-index: 1000;\r\n padding: 32px;\r\n}\r\n\r\n._Modal--isOpen_1t9ot_29 {\r\n display: flex;\r\n}\r\n\r\n._Modal-inner_1t9ot_37 {\r\n width: 100%;\r\n max-width: 1024px;\r\n border-radius: 8px;\r\n overflow: visible;\r\n background: var(--puck-color-white);\r\n display: flex;\r\n flex-direction: column;\r\n max-height: 90dvh;\r\n min-height: 0;\r\n}\r\n","._Drawer_12zq5_1 {\n display: flex;\n flex-direction: column;\n gap: 0;\n}\n\n._Drawer-category_12zq5_7 {\n max-width: 100%;\n}\n\n._Drawer-category_12zq5_7 + ._Drawer-category_12zq5_7 {\n margin-top: 4px;\n}\n\n._Drawer-category--isExpanded_12zq5_15 + ._Drawer-category_12zq5_7 {\n margin-top: 12px;\n}\n\n._Drawer-categoryContent_12zq5_19 {\n display: none;\n}\n\n._Drawer-category--isExpanded_12zq5_15 > ._Drawer-categoryContent_12zq5_19 {\n display: block;\n}\n\n._Drawer-categoryTitle_12zq5_27 {\n background-color: transparent;\n border: 0;\n color: var(--puck-color-grey-05);\n cursor: pointer;\n display: flex;\n font: inherit;\n font-size: var(--puck-font-size-xxxs);\n list-style: none;\n margin-bottom: 6px;\n padding: 8px;\n text-transform: uppercase;\n transition: background-color 50ms ease-in, color 50ms ease-in;\n gap: 4px;\n border-radius: 4px;\n width: 100%;\n}\n\n._Drawer-categoryTitle_12zq5_27:focus-visible {\n outline: 2px solid var(--puck-color-azure-05);\n outline-offset: 2px;\n}\n\n@media (hover: hover) and (pointer: fine) {\n ._Drawer-categoryTitle_12zq5_27:hover {\n background-color: var(--puck-color-azure-11);\n color: var(--puck-color-azure-04);\n transition: none;\n }\n}\n\n._Drawer-categoryTitle_12zq5_27:active {\n background-color: var(--puck-color-azure-10);\n transition: none;\n}\n\n._Drawer-categoryTitleIcon_12zq5_63 {\n margin-inline-start: auto;\n}\n"],"mappings":"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","names":[]}
@@ -1,36 +1,104 @@
1
1
  import * as zustand from 'zustand';
2
2
  import { StoreApi } from 'zustand';
3
3
  import * as _measured_puck from '@measured/puck';
4
- import { DefaultComponentProps, Field, Config, Fields, History, AppState, PuckApi, ComponentData, ComponentConfig, RootData, AsFieldProps, WithChildren, Metadata, ResolveDataTrigger, PuckAction, Data } from '@measured/puck';
4
+ import { Field, Fields, Config, DefaultComponentProps, History, AppState, PuckApi, ComponentData, ComponentConfig, RootData, AsFieldProps, WithChildren, Metadata, ResolveDataTrigger, PuckAction, Data } from '@measured/puck';
5
5
  import * as React$1 from 'react';
6
6
  import React__default, { ReactNode, ReactElement } from 'react';
7
7
  import * as react_jsx_runtime from 'react/jsx-runtime';
8
8
 
9
+ type BuiltInSoftFieldType = "text" | "textarea" | "number" | "select" | "radio" | "array" | "object" | "reference";
10
+ type SoftFieldType = BuiltInSoftFieldType | string;
11
+ type CustomFieldReturnType = "string" | "number" | "boolean" | "object" | "array";
12
+ type SoftFieldDefinition = {
13
+ name: string;
14
+ type: SoftFieldType;
15
+ };
16
+ type FieldOption = {
17
+ label: string;
18
+ value: string | number | boolean | object | null | undefined;
19
+ };
20
+ interface SharedFieldSettings<TSubFieldSettings> {
21
+ defaultValue?: unknown;
22
+ min?: number;
23
+ max?: number;
24
+ step?: number;
25
+ options?: FieldOption[];
26
+ summary?: string;
27
+ summaryField?: string;
28
+ summaryExpression?: string;
29
+ subFields?: SoftFieldDefinition[];
30
+ subFieldSettings?: TSubFieldSettings;
31
+ customFieldType?: string;
32
+ customFieldReturnType?: CustomFieldReturnType;
33
+ }
34
+ interface SoftFieldSettingsEntry extends SharedFieldSettings<SoftFieldSettings> {
35
+ }
36
+ type SoftFieldSettings = Record<string, SoftFieldSettingsEntry>;
37
+ interface FieldSettingsEntry extends SharedFieldSettings<FieldSettings> {
38
+ label: string;
39
+ }
40
+ type FieldSettings = Record<string, FieldSettingsEntry>;
41
+ type CustomFieldSettingsOverrideProps = {
42
+ fieldName: string;
43
+ fieldType: string;
44
+ fieldSettings?: SoftFieldSettingsEntry;
45
+ originalFieldSettings: Fields;
46
+ };
47
+ type CustomFieldDefinition = {
48
+ field: Field;
49
+ returnType: CustomFieldReturnType;
50
+ subFields?: SoftFieldDefinition[];
51
+ subFieldSettings?: SoftFieldSettings;
52
+ fieldSettingsOverride?: (props: CustomFieldSettingsOverrideProps) => Fields;
53
+ };
54
+ type CustomFields = Record<string, CustomFieldDefinition>;
55
+
56
+ type MapEntry = {
57
+ mode?: "simple" | "cva";
58
+ from?: string | string[];
59
+ to?: string | string[];
60
+ cva?: {
61
+ base?: string;
62
+ variants?: Array<{
63
+ fieldId?: string;
64
+ classes?: Record<string, string>;
65
+ }>;
66
+ };
67
+ transform?: (values: unknown[], props: Record<string, unknown>) => unknown;
68
+ unmappedArrayItemDefaultValues?: Record<string, unknown>;
69
+ /** @deprecated in favour of unmappedArrayItemDefaultValues – kept for back-compat */
70
+ defaultOverrides?: Record<string, unknown>;
71
+ };
72
+ type ApplyMappingResult = {
73
+ newProps: Record<string, unknown>;
74
+ mappedArrayPaths: Set<string>;
75
+ changed: boolean;
76
+ };
77
+ type ApplyMappingOptions = {
78
+ sourceProps?: Record<string, unknown>;
79
+ arrayDefaults?: Record<string, unknown[]>;
80
+ };
81
+ type MappingOption = {
82
+ label: string;
83
+ value: string;
84
+ type: Field["type"] | "reference";
85
+ };
86
+
9
87
  type BuilderRootConfig = {
10
88
  _name: string;
11
89
  _category?: string;
12
90
  _version?: string;
13
91
  _versions?: string[];
14
- _fields?: {
15
- name: string;
16
- type: Field["type"];
17
- }[];
18
- _fieldSettings?: {
19
- [key: string]: any;
20
- };
21
- [key: string]: any;
92
+ _fields?: SoftFieldDefinition[];
93
+ _fieldSettings?: SoftFieldSettings;
94
+ [key: string]: unknown;
22
95
  };
23
96
  type BuilderComponentConfig = {
24
97
  _slot?: {
25
98
  slot: string;
26
99
  }[];
27
- _map?: {
28
- to: string | string[];
29
- from: string | string[];
30
- transform?: (inputs: any[], props: DefaultComponentProps) => any;
31
- [key: string]: any;
32
- }[];
33
- [key: string]: any;
100
+ _map?: MapEntry[];
101
+ [key: string]: unknown;
34
102
  };
35
103
  type BuilderConfig = Config<any, BuilderRootConfig>;
36
104
 
@@ -50,9 +118,9 @@ type SoftComponent = {
50
118
  name: string;
51
119
  category?: string;
52
120
  fields: Fields;
53
- fieldSettings?: Record<string, any>;
121
+ fieldSettings?: SoftFieldSettings;
54
122
  defaultProps: DefaultComponentProps;
55
- rootProps?: Record<string, any>;
123
+ rootProps?: Record<string, unknown>;
56
124
  components: SoftSubComponent;
57
125
  slots: {
58
126
  [slot: string]: DefaultComponentProps;
@@ -65,9 +133,9 @@ type VersionedSoftComponent = {
65
133
  versions: {
66
134
  [version: string]: {
67
135
  fields: Fields;
68
- fieldSettings?: Record<string, any>;
136
+ fieldSettings?: SoftFieldSettings;
69
137
  defaultProps: DefaultComponentProps;
70
- rootProps?: Record<string, any>;
138
+ rootProps?: Record<string, unknown>;
71
139
  components: SoftSubComponent;
72
140
  slots: {
73
141
  [slot: string]: DefaultComponentProps;
@@ -232,9 +300,7 @@ type ActionEventPayload = {
232
300
  };
233
301
  type OnActionsCallback = (event: ActionEventPayload) => void | Promise<void>;
234
302
 
235
- type RenderFunc<Props extends {
236
- [key: string]: any;
237
- } = {
303
+ type RenderFunc<Props extends Record<string, unknown> = {
238
304
  children: ReactNode;
239
305
  }> = (props: Props) => ReactElement;
240
306
  type Overrides = {
@@ -243,20 +309,12 @@ type Overrides = {
243
309
  state: "building" | "remodeling" | "ready" | "inspecting";
244
310
  }) => string;
245
311
  componentKeyToName?: (key: string) => string;
246
- onRemodel?: (key: string) => Record<string, any>;
312
+ onRemodel?: (key: string) => Record<string, unknown>;
247
313
  additionalRootFields?: Record<string, Field>;
248
314
  map?: RenderFunc<{
249
315
  rootProps: BuilderRootConfig;
250
- toOptions: {
251
- label: string;
252
- value: string;
253
- type: Field["type"] | "reference";
254
- }[];
255
- fromOptions: {
256
- label: string;
257
- value: string;
258
- type: Field["type"] | "reference";
259
- }[];
316
+ toOptions: MappingOption[];
317
+ fromOptions: MappingOption[];
260
318
  props: DefaultComponentProps;
261
319
  value: BuilderComponentConfig["_map"];
262
320
  onChange: (value: BuilderComponentConfig["_map"]) => void;
@@ -267,7 +325,7 @@ type Overrides = {
267
325
  version: string;
268
326
  subComponentPath: string[];
269
327
  softComponent: VersionedSoftComponent["versions"][string];
270
- }) => ((inputs: any[], props: DefaultComponentProps) => any) | undefined;
328
+ }) => ((inputs: unknown[], props: Record<string, unknown>) => unknown) | undefined;
271
329
  onActions?: OnActionsCallback;
272
330
  name?: Field<string>;
273
331
  categories?: Field<string | undefined>;
@@ -284,6 +342,7 @@ type Overrides = {
284
342
  props: RootData<AsFieldProps<WithChildren<BuilderRootConfig>>> | Promise<RootData<AsFieldProps<WithChildren<BuilderRootConfig>>>>;
285
343
  readOnly: Readonly<Record<string, boolean>> | undefined;
286
344
  };
345
+ mapComponentConfig?: (componentName: string, defaultConfig: ComponentConfig) => Partial<ComponentConfig>;
287
346
  };
288
347
 
289
348
  type Status = "building" | "remodeling" | "ready" | "inspecting";
@@ -295,6 +354,7 @@ type AppStore = {
295
354
  originalHistory: History[];
296
355
  storedConfig?: Config;
297
356
  overrides: Overrides;
357
+ customFields: CustomFields;
298
358
  onActions?: OnActionsCallback;
299
359
  itemSelector: {
300
360
  index: number;
@@ -368,7 +428,7 @@ type AppStore = {
368
428
  setShowVersionFields: (show: boolean) => void;
369
429
  };
370
430
  type AppStoreApi = StoreApi<AppStore>;
371
- declare const createSoftConfigStore: (hardConfig?: Config, softComponents?: SoftComponents, overrides?: Overrides, onActions?: OnActionsCallback, showVersionFields?: boolean) => zustand.UseBoundStore<Omit<StoreApi<AppStore>, "subscribe"> & {
431
+ declare const createSoftConfigStore: (hardConfig?: Config, softComponents?: SoftComponents, overrides?: Overrides, onActions?: OnActionsCallback, showVersionFields?: boolean, customFields?: CustomFields) => zustand.UseBoundStore<Omit<StoreApi<AppStore>, "subscribe"> & {
372
432
  subscribe: {
373
433
  (listener: (selectedState: AppStore, previousSelectedState: AppStore) => void): () => void;
374
434
  <U>(selector: (state: AppStore) => U, listener: (selectedState: U, previousSelectedState: U) => void, options?: {
@@ -378,10 +438,11 @@ declare const createSoftConfigStore: (hardConfig?: Config, softComponents?: Soft
378
438
  };
379
439
  }>;
380
440
 
381
- declare const SoftConfigProvider: ({ children, hardConfig, softComponents, overrides, value, onActions, useVersioning, }: {
441
+ declare const SoftConfigProvider: ({ children, hardConfig, softComponents, customFields, overrides, value, onActions, useVersioning, }: {
382
442
  children: (softConfig: Config, softComponents: SoftComponents, iframeDoc: AppStore["setIframeDoc"], validateAction: (action: PuckAction) => boolean) => ReactNode;
383
443
  hardConfig: Config;
384
444
  softComponents: SoftComponents;
445
+ customFields?: CustomFields;
385
446
  overrides?: Overrides;
386
447
  value?: StoreApi<AppStore>;
387
448
  onActions?: OnActionsCallback;
@@ -402,9 +463,9 @@ declare const useRemodel: () => {
402
463
  version: string;
403
464
  softComponent: {
404
465
  fields: _measured_puck.Fields;
405
- fieldSettings?: Record<string, any>;
466
+ fieldSettings?: SoftFieldSettings;
406
467
  defaultProps: DefaultComponentProps;
407
- rootProps?: Record<string, any>;
468
+ rootProps?: Record<string, unknown>;
408
469
  components: SoftSubComponent;
409
470
  slots: {
410
471
  [slot: string]: DefaultComponentProps;
@@ -462,11 +523,13 @@ declare const ActionBarOverride: (props: {
462
523
 
463
524
  declare const DrawerItem: (props: {
464
525
  name: string;
526
+ label?: string;
465
527
  children: React.ReactNode;
466
528
  }) => React.ReactElement;
467
529
  /** @deprecated Use DrawerItem instead */
468
530
  declare const ComponentItem: (props: {
469
531
  name: string;
532
+ label?: string;
470
533
  children: React.ReactNode;
471
534
  }) => React.ReactElement;
472
535
 
@@ -541,4 +604,45 @@ declare const Modal: ({ children, onClose, isOpen, }: {
541
604
  isOpen: boolean;
542
605
  }) => react_jsx_runtime.JSX.Element;
543
606
 
544
- export { ActionBarOverride as ActionBar, type ActionEventPayload, type AppStore, type AppStoreApi, type BuilderComponentConfig, type BuilderConfig, type BuilderRootConfig, ComponentItem, Drawer as ComponentList, Drawer, DrawerItem, Header, Modal, type OnActionsCallback, type Overrides, type SoftComponent, type SoftComponents, SoftConfigProvider, type VersionedSoftComponent, confirm, createActionCallback, createSoftConfigStore, createUseSoftConfig, notify, resolveSoftConfig, setConfirmHandler, setNotificationHandler, useBuild, useCancel, useComplete, useDecompose, useDemolish, useInspect, useRemodel, useSetDefaultVersion, useSoftConfig };
607
+ /**
608
+ * Filters toOptions based on the selected fromPath.
609
+ * - If fromPath contains an array segment, only return array-path to-options.
610
+ * - If fromPath is a bare array, only return bare array to-options.
611
+ * - Otherwise, hide array-path to-options (prevent array-to-scalar mismatches).
612
+ */
613
+ declare function filterToOptionsForFrom(fromPath: string | undefined, toOptions: MappingOption[]): MappingOption[];
614
+
615
+ /**
616
+ * Shared helper: evaluates `_map` entries and applies mapped values to component props.
617
+ *
618
+ * Used by both the live builder effect (root-config.tsx) and the runtime
619
+ * resolver (resolve-soft-component-data.ts) so the logic stays in one place.
620
+ *
621
+ * Array construction order (for array targets like `items[].imageUrl`):
622
+ * 1. Start from `unmappedArrayItemDefaultValues` on the map entry (initially populated
623
+ * from the component's `defaultItemProps`). These are the single source of
624
+ * truth for the item "template".
625
+ * 2. Overlay the mapped value for each array index.
626
+ * The result is a freshly-constructed array whose length equals the mapped
627
+ * source array and whose unmapped props come from the defaults above.
628
+ */
629
+
630
+ declare const resolveValueByPath: (source: any, path: string) => any;
631
+ /**
632
+ * Evaluate every `_map` entry and apply the results to `props`.
633
+ *
634
+ * @param props The component props to mutate (caller should pass a shallow copy).
635
+ * @param fieldSettings Root-level `_fieldSettings` for resolving `from` paths.
636
+ * @param map The `_map` array from the component.
637
+ * @param resolveInput Strategy for resolving `from` values. Two modes:
638
+ * - `"fieldSettings"` (builder): read from `fieldSettings` only.
639
+ * - `"propsFirst"` (runtime): prefer live prop values, fall back
640
+ * to fieldSettings `defaultValue`.
641
+ */
642
+ declare function applyMapping(props: Record<string, any>, fieldSettings: Record<string, any>, map: MapEntry[], resolveInput?: "fieldSettings" | "propsFirst", options?: ApplyMappingOptions): ApplyMappingResult;
643
+
644
+ declare const isArrayMappingPath: (path: string) => boolean;
645
+ declare const getArrayBasePath: (arrayPath: string) => string | null;
646
+ declare const getArrayItemSubPath: (arrayPath: string) => string | null;
647
+
648
+ export { ActionBarOverride as ActionBar, type ActionEventPayload, type AppStore, type AppStoreApi, type ApplyMappingOptions, type ApplyMappingResult, type BuilderComponentConfig, type BuilderConfig, type BuilderRootConfig, ComponentItem, Drawer as ComponentList, type CustomFieldDefinition, type CustomFieldReturnType, type CustomFields, Drawer, DrawerItem, type FieldSettings, Header, type MapEntry, Modal, type OnActionsCallback, type Overrides, type SoftComponent, type SoftComponents, SoftConfigProvider, type SoftFieldDefinition, type SoftFieldSettings, type VersionedSoftComponent, applyMapping, confirm, createActionCallback, createSoftConfigStore, createUseSoftConfig, filterToOptionsForFrom, getArrayBasePath, getArrayItemSubPath, isArrayMappingPath, notify, resolveSoftConfig, resolveValueByPath, setConfirmHandler, setNotificationHandler, useBuild, useCancel, useComplete, useDecompose, useDemolish, useInspect, useRemodel, useSetDefaultVersion, useSoftConfig };