@developer_tribe/react-builder 1.0.7 → 1.0.9

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 (217) hide show
  1. package/dist/build-components/BIcon/BIconProps.generated.d.ts +3 -0
  2. package/dist/build-components/BackgroundImage/BackgroundImageProps.generated.d.ts +1 -0
  3. package/dist/build-components/Button/ButtonProps.generated.d.ts +1 -0
  4. package/dist/build-components/Carousel/CarouselProps.generated.d.ts +5 -0
  5. package/dist/build-components/CarouselButtons/CarouselButtonsProps.generated.d.ts +1 -0
  6. package/dist/build-components/CarouselDots/CarouselDotsProps.generated.d.ts +1 -0
  7. package/dist/build-components/CarouselItem/CarouselItemProps.generated.d.ts +1 -0
  8. package/dist/build-components/CarouselProvider/CarouselProviderProps.generated.d.ts +1 -0
  9. package/dist/build-components/Image/ImageProps.generated.d.ts +1 -0
  10. package/dist/build-components/Main/MainProps.generated.d.ts +1 -1
  11. package/dist/build-components/Onboard/OnboardProps.generated.d.ts +1 -0
  12. package/dist/build-components/OnboardButton/OnboardButtonProps.generated.d.ts +1 -0
  13. package/dist/build-components/OnboardButtons/OnboardButtonsProps.generated.d.ts +1 -0
  14. package/dist/build-components/OnboardDot/OnboardDotProps.generated.d.ts +1 -0
  15. package/dist/build-components/OnboardFooter/OnboardFooterProps.generated.d.ts +3 -0
  16. package/dist/build-components/OnboardImage/OnboardImageProps.generated.d.ts +1 -0
  17. package/dist/build-components/OnboardItem/OnboardItemProps.generated.d.ts +1 -0
  18. package/dist/build-components/OnboardProvider/OnboardProviderProps.generated.d.ts +3 -0
  19. package/dist/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.d.ts +3 -0
  20. package/dist/build-components/OnboardTitle/OnboardTitleProps.generated.d.ts +3 -0
  21. package/dist/build-components/PaywallBackground/PaywallBackgroundProps.generated.d.ts +1 -1
  22. package/dist/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.d.ts +3 -1
  23. package/dist/build-components/PaywallOptions/PaywallOptionsProps.generated.d.ts +1 -1
  24. package/dist/build-components/PaywallProvider/PaywallContext.d.ts +12 -0
  25. package/dist/build-components/PaywallProvider/PaywallProviderProps.generated.d.ts +1 -1
  26. package/dist/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.d.ts +1 -0
  27. package/dist/build-components/RadioButton/RadioButtonProps.generated.d.ts +1 -1
  28. package/dist/build-components/Text/TextProps.generated.d.ts +3 -0
  29. package/dist/build-components/View/ViewProps.generated.d.ts +1 -0
  30. package/dist/build-components/patterns.generated.d.ts +372 -374
  31. package/dist/components/BuilderProvider.d.ts +2 -0
  32. package/dist/components/ParamsProvider.d.ts +5 -0
  33. package/dist/components/RenderErrorBoundary.d.ts +28 -0
  34. package/dist/hooks/useSyncHtmlThemeClass.d.ts +7 -0
  35. package/dist/index.cjs.js +5 -5
  36. package/dist/index.cjs.js.map +1 -1
  37. package/dist/index.d.ts +2 -0
  38. package/dist/index.esm.js +3 -3
  39. package/dist/index.esm.js.map +1 -1
  40. package/dist/index.native.cjs.js +4 -4
  41. package/dist/index.native.cjs.js.map +1 -1
  42. package/dist/index.native.d.ts +1 -0
  43. package/dist/index.native.esm.js +4 -4
  44. package/dist/index.native.esm.js.map +1 -1
  45. package/dist/migrations/migratePipe.d.ts +14 -0
  46. package/dist/migrations/migrations/1.1.0_normalize_style_attributes.d.ts +2 -0
  47. package/dist/migrations/semver.d.ts +8 -0
  48. package/dist/migrations/types.d.ts +8 -0
  49. package/dist/mockOS/components/SubscriptionModal.d.ts +7 -0
  50. package/dist/mockOS/context/MockOSContextBase.d.ts +1 -0
  51. package/dist/mockOS/hooks/useMockIap.d.ts +3 -0
  52. package/dist/mockOS/index.d.ts +4 -0
  53. package/dist/mockOS/managers/mockOSIapManager.d.ts +6 -0
  54. package/dist/mockOS/managers/subscriptionManager.d.ts +10 -0
  55. package/dist/pages/ProjectDebug.d.ts +14 -0
  56. package/dist/pages/ProjectMigrationPage.d.ts +23 -0
  57. package/dist/pages/ProjectValidationPage.d.ts +15 -0
  58. package/dist/styles.css +1 -1
  59. package/dist/types/Device.d.ts +5 -0
  60. package/dist/utils/__special_exceptions.d.ts +7 -0
  61. package/dist/utils/getImage.d.ts +23 -0
  62. package/dist/utils/pasteNode.d.ts +15 -0
  63. package/dist/utils/patterns.d.ts +1 -2
  64. package/package.json +6 -2
  65. package/scripts/migrate-patterns-to-v2.mjs +131 -0
  66. package/scripts/migrate-samples-to-current.ts +79 -0
  67. package/scripts/prebuild/utils/createGeneratedProps.js +4 -5
  68. package/scripts/prebuild/utils/validateAllComponentsOrThrow.js +32 -21
  69. package/scripts/prebuild/utils/validatePatternJson.js +12 -10
  70. package/src/.DS_Store +0 -0
  71. package/src/AttributesEditor.tsx +41 -11
  72. package/src/RenderPage.tsx +55 -0
  73. package/src/assets/.DS_Store +0 -0
  74. package/src/assets/devices.json +91 -0
  75. package/src/assets/samples/carousel-sample.json +141 -29
  76. package/src/assets/samples/getSamples.ts +9 -0
  77. package/src/assets/samples/paywall-1.json +119 -71
  78. package/src/assets/samples/simple-1.json +28 -16
  79. package/src/assets/samples/simple-2.json +157 -82
  80. package/src/assets/samples/unmigrated-builder1.json +42 -0
  81. package/src/assets/samples/unvalidated-builder1.json +49 -0
  82. package/src/assets/samples/unvalidated-crash1.json +19 -0
  83. package/src/assets/samples/unvalidated-crashcomponent1.json +16 -0
  84. package/src/assets/samples/vpn-onboard-1.json +91 -51
  85. package/src/assets/samples/vpn-onboard-2.json +318 -278
  86. package/src/assets/samples/vpn-onboard-3.json +286 -252
  87. package/src/assets/samples/vpn-onboard-4.json +286 -252
  88. package/src/assets/samples/vpn-onboard-5.json +434 -374
  89. package/src/assets/samples/vpn-onboard-6.json +290 -250
  90. package/src/attributes-editor/Field.tsx +1 -1
  91. package/src/attributes-editor/LayoutPreviewPicker.tsx +5 -2
  92. package/src/build-components/BIcon/BIconProps.generated.ts +3 -0
  93. package/src/build-components/BIcon/pattern.json +12 -9
  94. package/src/build-components/BackgroundImage/BackgroundImage.tsx +3 -1
  95. package/src/build-components/BackgroundImage/BackgroundImageProps.generated.ts +1 -0
  96. package/src/build-components/BackgroundImage/pattern.json +25 -16
  97. package/src/build-components/Button/Button.tsx +26 -3
  98. package/src/build-components/Button/ButtonProps.generated.ts +1 -0
  99. package/src/build-components/Button/pattern.json +10 -6
  100. package/src/build-components/Carousel/CarouselProps.generated.ts +5 -0
  101. package/src/build-components/Carousel/pattern.json +19 -8
  102. package/src/build-components/CarouselButtons/CarouselButtonsProps.generated.ts +1 -0
  103. package/src/build-components/CarouselButtons/pattern.json +11 -5
  104. package/src/build-components/CarouselDots/CarouselDotsProps.generated.ts +1 -0
  105. package/src/build-components/CarouselDots/pattern.json +5 -4
  106. package/src/build-components/CarouselItem/CarouselItemProps.generated.ts +1 -0
  107. package/src/build-components/CarouselItem/pattern.json +5 -4
  108. package/src/build-components/CarouselProvider/CarouselProvider.tsx +44 -2
  109. package/src/build-components/CarouselProvider/CarouselProviderProps.generated.ts +1 -0
  110. package/src/build-components/Image/Image.tsx +2 -1
  111. package/src/build-components/Image/ImageProps.generated.ts +1 -0
  112. package/src/build-components/Image/pattern.json +11 -5
  113. package/src/build-components/Main/MainProps.generated.ts +1 -1
  114. package/src/build-components/Main/pattern.json +12 -9
  115. package/src/build-components/Onboard/OnboardProps.generated.ts +1 -0
  116. package/src/build-components/Onboard/pattern.json +14 -9
  117. package/src/build-components/OnboardButton/OnboardButtonProps.generated.ts +1 -0
  118. package/src/build-components/OnboardButton/pattern.json +5 -4
  119. package/src/build-components/OnboardButtons/OnboardButtonsProps.generated.ts +1 -0
  120. package/src/build-components/OnboardButtons/pattern.json +5 -4
  121. package/src/build-components/OnboardDot/OnboardDotProps.generated.ts +1 -0
  122. package/src/build-components/OnboardDot/pattern.json +5 -4
  123. package/src/build-components/OnboardFooter/OnboardFooterProps.generated.ts +3 -0
  124. package/src/build-components/OnboardFooter/pattern.json +8 -5
  125. package/src/build-components/OnboardImage/OnboardImageProps.generated.ts +1 -0
  126. package/src/build-components/OnboardImage/pattern.json +7 -4
  127. package/src/build-components/OnboardItem/OnboardItemProps.generated.ts +1 -0
  128. package/src/build-components/OnboardItem/pattern.json +18 -9
  129. package/src/build-components/OnboardProvider/OnboardProviderProps.generated.ts +3 -0
  130. package/src/build-components/OnboardProvider/pattern.json +21 -6
  131. package/src/build-components/OnboardSubtitle/OnboardSubtitleProps.generated.ts +3 -0
  132. package/src/build-components/OnboardSubtitle/pattern.json +10 -6
  133. package/src/build-components/OnboardTitle/OnboardTitleProps.generated.ts +3 -0
  134. package/src/build-components/OnboardTitle/pattern.json +11 -7
  135. package/src/build-components/PaywallBackground/PaywallBackgroundProps.generated.ts +1 -1
  136. package/src/build-components/PaywallBackground/pattern.json +5 -4
  137. package/src/build-components/PaywallCloseButton/PaywallCloseButton.tsx +6 -1
  138. package/src/build-components/PaywallCloseButton/PaywallCloseButtonProps.generated.ts +3 -1
  139. package/src/build-components/PaywallCloseButton/pattern.json +15 -12
  140. package/src/build-components/PaywallOptions/PaywallOptionButton.tsx +0 -1
  141. package/src/build-components/PaywallOptions/PaywallOptions.tsx +3 -2
  142. package/src/build-components/PaywallOptions/PaywallOptionsProps.generated.ts +1 -1
  143. package/src/build-components/PaywallOptions/pattern.json +14 -11
  144. package/src/build-components/PaywallProvider/PaywallContext.ts +25 -0
  145. package/src/build-components/PaywallProvider/PaywallProvider.tsx +102 -5
  146. package/src/build-components/PaywallProvider/PaywallProviderProps.generated.ts +1 -1
  147. package/src/build-components/PaywallProvider/pattern.json +11 -8
  148. package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButton.tsx +7 -0
  149. package/src/build-components/PaywallSubscribeButton/PaywallSubscribeButtonProps.generated.ts +1 -0
  150. package/src/build-components/PaywallSubscribeButton/pattern.json +16 -13
  151. package/src/build-components/RadioButton/RadioButtonProps.generated.ts +1 -1
  152. package/src/build-components/RadioButton/pattern.json +5 -4
  153. package/src/build-components/Text/Text.tsx +107 -4
  154. package/src/build-components/Text/TextProps.generated.ts +3 -0
  155. package/src/build-components/Text/pattern.json +19 -4
  156. package/src/build-components/View/ViewProps.generated.ts +1 -0
  157. package/src/build-components/View/pattern.json +28 -13
  158. package/src/build-components/other.tsx +15 -0
  159. package/src/build-components/patterns.generated.ts +340 -235
  160. package/src/build-components/useNode.ts +22 -3
  161. package/src/components/BottomBar.tsx +45 -45
  162. package/src/components/Builder.tsx +20 -6
  163. package/src/components/BuilderButton.tsx +75 -38
  164. package/src/components/BuilderProvider.tsx +22 -2
  165. package/src/components/DeviceButton.tsx +12 -5
  166. package/src/components/EditorHeader.tsx +296 -38
  167. package/src/components/ParamsProvider.tsx +7 -0
  168. package/src/components/RenderErrorBoundary.tsx +200 -0
  169. package/src/hooks/useParams.ts +5 -1
  170. package/src/hooks/useSyncHtmlThemeClass.ts +19 -0
  171. package/src/index.native.ts +7 -0
  172. package/src/index.ts +8 -0
  173. package/src/migrations/migratePipe.ts +59 -0
  174. package/src/migrations/migrations/1.1.0_normalize_style_attributes.ts +80 -0
  175. package/src/migrations/semver.ts +24 -0
  176. package/src/migrations/types.ts +9 -0
  177. package/src/mockOS/components/PermissionModal.tsx +3 -2
  178. package/src/mockOS/components/SubscriptionModal.tsx +400 -0
  179. package/src/mockOS/context/MockOSContext.tsx +61 -10
  180. package/src/mockOS/context/MockOSContextBase.ts +1 -0
  181. package/src/mockOS/hooks/useMockIap.ts +11 -0
  182. package/src/mockOS/index.ts +7 -0
  183. package/src/mockOS/managers/mockOSIapManager.ts +10 -0
  184. package/src/mockOS/managers/subscriptionManager.ts +36 -0
  185. package/src/modals/IconPickerModal.tsx +1 -1
  186. package/src/pages/ProjectDebug.tsx +331 -0
  187. package/src/pages/ProjectMigrationPage.tsx +92 -0
  188. package/src/pages/ProjectPage.tsx +318 -166
  189. package/src/pages/ProjectValidationPage.tsx +54 -0
  190. package/src/styles/base/_global.scss +58 -11
  191. package/src/styles/components/_attributes-editor.scss +1 -1
  192. package/src/styles/components/_bottom-bar.scss +7 -4
  193. package/src/styles/components/_editor-shell.scss +126 -4
  194. package/src/styles/components/_mockos-router.scss +3 -2
  195. package/src/styles/components/_ui-components.scss +10 -5
  196. package/src/styles/foundation/_colors.scss +78 -11
  197. package/src/styles/foundation/_mixins.scss +4 -1
  198. package/src/styles/foundation/_sizes.scss +4 -2
  199. package/src/styles/index.scss +1 -0
  200. package/src/styles/layout/_builder.scss +61 -0
  201. package/src/styles/layout/_project-validation.scss +214 -0
  202. package/src/styles/modals/_add-component.scss +4 -2
  203. package/src/styles/modals/_color-modal.scss +4 -2
  204. package/src/styles/modals/_modal-shell.scss +3 -1
  205. package/src/types/Device.ts +5 -0
  206. package/src/utils/__special_exceptions.ts +88 -0
  207. package/src/utils/analyseNode.ts +8 -2
  208. package/src/utils/analyseNodeByPatterns.ts +43 -9
  209. package/src/utils/extractTextStyle.ts +19 -6
  210. package/src/utils/extractViewStyle.ts +68 -59
  211. package/src/utils/getImage.ts +76 -0
  212. package/src/utils/novaToJson.ts +2 -1
  213. package/src/utils/pasteNode.ts +172 -0
  214. package/src/utils/patterns.ts +4 -3
  215. package/dist/android.svg +0 -43
  216. package/dist/apple.svg +0 -16
  217. package/dist/background.jpg +0 -0
@@ -0,0 +1,214 @@
1
+ @use '../foundation/colors' as colors;
2
+ @use '../foundation/sizes' as sizes;
3
+ @use '../foundation/mixins' as *;
4
+
5
+ .rb-project-debug {
6
+ display: flex;
7
+ width: 100%;
8
+ height: calc(100vh - 60px);
9
+ overflow: hidden;
10
+ background: colors.$canvasColor;
11
+ }
12
+
13
+ .rb-project-debug__json {
14
+ @include thin-scrollbar;
15
+ flex: 0 0 34%;
16
+ min-width: 320px;
17
+ max-width: 640px;
18
+ border-right: 1px solid colors.$borderColor;
19
+ overflow: auto;
20
+ padding: sizes.$spaceComfy;
21
+ background: colors.$surfaceColor;
22
+ }
23
+
24
+ .rb-project-debug__preview {
25
+ @include thin-scrollbar;
26
+ position: relative;
27
+ flex: 1 1 auto;
28
+ overflow: auto;
29
+ padding: sizes.$spaceComfy;
30
+ display: flex;
31
+ align-items: center;
32
+ justify-content: center;
33
+ background-image: var(--rb-canvas-bg, none);
34
+ background-position: center;
35
+ background-repeat: repeat;
36
+ background-size: 500px auto;
37
+ }
38
+
39
+ /* Hide the canvas grid background in dark theme (Tailwind/shadcn convention). */
40
+ .dark .rb-project-debug__preview {
41
+ --rb-canvas-bg: none !important;
42
+ }
43
+
44
+ .rb-project-debug__error {
45
+ @include thin-scrollbar;
46
+ flex: 0 0 32%;
47
+ min-width: 280px;
48
+ max-width: 520px;
49
+ border-left: 1px solid colors.$borderColor;
50
+ overflow: auto;
51
+ padding: sizes.$spaceComfy;
52
+ background: colors.$surfaceColor;
53
+ }
54
+
55
+ .rb-project-debug__title {
56
+ margin: 0 0 sizes.$spaceTight 0;
57
+ font-size: 16px;
58
+ font-weight: 700;
59
+ color: colors.$textColor;
60
+ }
61
+
62
+ .rb-project-debug__error-block {
63
+ @include card;
64
+ padding: sizes.$spaceCozy;
65
+ margin: 0 0 sizes.$spaceCozy 0;
66
+ background: colors.$canvasColor;
67
+ }
68
+
69
+ .rb-project-debug__error-label {
70
+ font-size: 12px;
71
+ font-weight: 800;
72
+ color: colors.$textColor;
73
+ margin: 0 0 sizes.$spaceTight 0;
74
+ }
75
+
76
+ .rb-project-debug__error-pre {
77
+ margin: 0;
78
+ padding: sizes.$spaceTight;
79
+ border-radius: sizes.$radiusSoft;
80
+ background: colors.$surfaceColor;
81
+ border: 1px solid colors.$borderColor;
82
+ font-family:
83
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
84
+ 'Courier New', monospace;
85
+ font-size: 11px;
86
+ line-height: 1.45;
87
+ white-space: pre-wrap;
88
+ word-break: break-word;
89
+ overflow: auto;
90
+ max-height: 220px;
91
+ }
92
+
93
+ .rb-project-debug__meta {
94
+ margin: 0 0 sizes.$spaceCozy 0;
95
+ font-size: 12px;
96
+ color: colors.$mutedTextColor;
97
+ }
98
+
99
+ .rb-project-debug__meta-label {
100
+ font-weight: 700;
101
+ color: colors.$textColor;
102
+ }
103
+
104
+ .rb-project-debug__toolbar {
105
+ display: flex;
106
+ gap: sizes.$spaceTight;
107
+ align-items: center;
108
+ margin: 0 0 sizes.$spaceCozy 0;
109
+ }
110
+
111
+ .rb-project-debug__toolbar .editor-button {
112
+ min-width: 0;
113
+ height: 34px;
114
+ padding: 0 sizes.$spaceCozy;
115
+ }
116
+
117
+ .rb-project-debug__below-name {
118
+ margin: 0 0 sizes.$spaceCozy 0;
119
+ }
120
+
121
+ .rb-project-debug__error-head {
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: space-between;
125
+ gap: sizes.$spaceCozy;
126
+ margin: 0 0 sizes.$spaceCozy 0;
127
+ }
128
+
129
+ .rb-project-debug__error-head .rb-project-debug__title {
130
+ margin: 0;
131
+ }
132
+
133
+ .rb-project-debug__error-head .editor-button {
134
+ min-width: 0;
135
+ height: 34px;
136
+ padding: 0 sizes.$spaceCozy;
137
+ }
138
+
139
+ .rb-project-validation__actions {
140
+ display: flex;
141
+ gap: sizes.$spaceTight;
142
+ align-items: center;
143
+ }
144
+
145
+ .rb-project-validation__actions .editor-button {
146
+ min-width: 0;
147
+ height: 34px;
148
+ padding: 0 sizes.$spaceCozy;
149
+ }
150
+
151
+ .rb-project-debug__code {
152
+ @include card;
153
+ margin: 0;
154
+ padding: sizes.$spaceCozy;
155
+ font-family:
156
+ ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono',
157
+ 'Courier New', monospace;
158
+ font-size: 12px;
159
+ line-height: 1.5;
160
+ white-space: pre;
161
+ overflow: auto;
162
+ max-height: calc(100vh - 60px - 160px);
163
+ background: colors.$canvasColor;
164
+ }
165
+
166
+ /* Legacy selectors kept for backward compatibility (no-op if unused) */
167
+ .rb-project-validation__error {
168
+ margin: 0 0 sizes.$spaceCozy 0;
169
+ font-size: 13px;
170
+ line-height: 1.4;
171
+ color: colors.$dangerColor;
172
+ font-weight: 600;
173
+ }
174
+
175
+ .rb-project-validation__error--render {
176
+ color: colors.$accentColor;
177
+ }
178
+
179
+ .rb-project-validation__error-label {
180
+ font-weight: 800;
181
+ }
182
+
183
+ @media (max-width: 1000px) {
184
+ .rb-project-debug {
185
+ flex-direction: column;
186
+ height: calc(100vh - 60px);
187
+ }
188
+
189
+ .rb-project-debug__json {
190
+ flex: 0 0 auto;
191
+ width: 100%;
192
+ min-width: 0;
193
+ max-width: none;
194
+ border-right: none;
195
+ border-bottom: 1px solid colors.$borderColor;
196
+ max-height: 45%;
197
+ }
198
+
199
+ .rb-project-debug__preview {
200
+ flex: 1 1 auto;
201
+ width: 100%;
202
+ min-height: 0;
203
+ }
204
+
205
+ .rb-project-debug__error {
206
+ flex: 0 0 auto;
207
+ width: 100%;
208
+ min-width: 0;
209
+ max-width: none;
210
+ border-left: none;
211
+ border-top: 1px solid colors.$borderColor;
212
+ max-height: 40%;
213
+ }
214
+ }
@@ -49,7 +49,8 @@
49
49
  border-radius: 12px;
50
50
  padding: sizes.$spaceComfy;
51
51
  margin-bottom: sizes.$spaceCozy;
52
- box-shadow: 0 10px 20px hsl(var(--foreground, 220.9 39.3% 11%) / 0.05);
52
+ box-shadow: 0 10px 20px
53
+ hsl(var(--foreground, var(--rb-foreground, 220.9 39.3% 11%)) / 0.05);
53
54
  }
54
55
 
55
56
  .add-component-modal__group-title {
@@ -86,7 +87,8 @@
86
87
 
87
88
  .add-component-modal__card:hover {
88
89
  border-color: colors.$accentColor;
89
- box-shadow: 0 10px 25px hsl(var(--primary, 221.2 83.2% 53.3%) / 0.15);
90
+ box-shadow: 0 10px 25px
91
+ hsl(var(--primary, var(--rb-primary, 221.2 83.2% 53.3%)) / 0.15);
90
92
  transform: translateY(-2px);
91
93
  }
92
94
 
@@ -30,7 +30,8 @@
30
30
  width: 32px;
31
31
  height: 32px;
32
32
  border-radius: 6px;
33
- border: 1px solid hsl(var(--foreground, 220.9 39.3% 11%) / 0.1);
33
+ border: 1px solid
34
+ hsl(var(--foreground, var(--rb-foreground, 220.9 39.3% 11%)) / 0.1);
34
35
  }
35
36
 
36
37
  .color-modal__selected-label {
@@ -158,7 +159,8 @@
158
159
  }
159
160
 
160
161
  .color-modal__swatch-preview--light {
161
- border: 1px solid hsl(var(--foreground, 220.9 39.3% 11%) / 0.1);
162
+ border: 1px solid
163
+ hsl(var(--foreground, var(--rb-foreground, 220.9 39.3% 11%)) / 0.1);
162
164
  }
163
165
 
164
166
  .color-modal__swatch-label {
@@ -10,7 +10,9 @@
10
10
  .modal__overlay {
11
11
  position: absolute;
12
12
  inset: 0;
13
- background: hsl(var(--foreground, 220.9 39.3% 11%) / 0.4);
13
+ background: hsl(
14
+ var(--foreground, var(--rb-foreground, 220.9 39.3% 11%)) / 0.4
15
+ );
14
16
  z-index: 0;
15
17
  }
16
18
 
@@ -3,6 +3,11 @@ export interface Device {
3
3
  platform: string; // ios, android
4
4
  width: number;
5
5
  height: number;
6
+ /**
7
+ * High-level screen shape bucket derived from portrait aspect ratio (height / width).
8
+ * Useful for quickly filtering "tall" vs "wide-ish" devices without hardcoding sizes.
9
+ */
10
+ aspect?: 'tall' | 'regular' | 'wide';
6
11
  type: 'phone' | 'tablet';
7
12
  /**
8
13
  * Optional physical corner radius of the device screen area in CSS pixels.
@@ -0,0 +1,88 @@
1
+ import type { Node, NodeData, NodeDefaultAttribute } from '../types/Node';
2
+ import { isNodeArray, isNodeNullOrUndefined, isNodeString } from './nodeGuards';
3
+
4
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
5
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
6
+ }
7
+
8
+ function looksLikeSelectOptionObject(
9
+ value: unknown,
10
+ ): value is { value: unknown } {
11
+ if (!isPlainObject(value)) return false;
12
+ // Avoid unwrapping actual node-like objects or typed objects that use `type`.
13
+ if ('type' in value || 'children' in value || 'attributes' in value) {
14
+ return false;
15
+ }
16
+ if (!('value' in value)) return false;
17
+ const keys = Object.keys(value);
18
+ // Typical shapes: { value }, { value, label }, { value, label, id }
19
+ if (keys.length < 1 || keys.length > 3) return false;
20
+ const allowed = new Set(['value', 'label', 'id']);
21
+ for (const k of keys) if (!allowed.has(k)) return false;
22
+ const v = (value as Record<string, unknown>).value;
23
+ return (
24
+ v == null ||
25
+ typeof v === 'string' ||
26
+ typeof v === 'number' ||
27
+ typeof v === 'boolean'
28
+ );
29
+ }
30
+
31
+ function normalizeUnknownValue(value: unknown): unknown {
32
+ if (value == null) return value;
33
+
34
+ if (Array.isArray(value)) {
35
+ return value.map(normalizeUnknownValue);
36
+ }
37
+
38
+ if (!isPlainObject(value)) return value;
39
+
40
+ if (looksLikeSelectOptionObject(value)) {
41
+ return normalizeUnknownValue((value as { value: unknown }).value);
42
+ }
43
+
44
+ const out: Record<string, unknown> = {};
45
+ for (const [k, v] of Object.entries(value)) {
46
+ out[k] = normalizeUnknownValue(v);
47
+ }
48
+ return out;
49
+ }
50
+
51
+ /**
52
+ * Special-case normalization for persisted JSON before validation.
53
+ *
54
+ * Keep "exceptions" here rather than inside the core validation logic.
55
+ */
56
+ export function normalizeNodeForValidation(
57
+ node: Node<NodeDefaultAttribute>,
58
+ ): Node<NodeDefaultAttribute> {
59
+ if (isNodeNullOrUndefined(node) || isNodeString(node)) {
60
+ return node;
61
+ }
62
+
63
+ if (isNodeArray(node)) {
64
+ const nodeArray = node as unknown as Node<NodeDefaultAttribute>[];
65
+ return nodeArray.map(
66
+ normalizeNodeForValidation,
67
+ ) as unknown as Node<NodeDefaultAttribute>;
68
+ }
69
+
70
+ const recordData = node as NodeData<NodeDefaultAttribute>;
71
+
72
+ const attributes = isPlainObject(recordData.attributes)
73
+ ? (normalizeUnknownValue(recordData.attributes) as Record<string, unknown>)
74
+ : recordData.attributes;
75
+
76
+ const children =
77
+ recordData.children !== undefined
78
+ ? (normalizeNodeForValidation(
79
+ recordData.children as Node<NodeDefaultAttribute>,
80
+ ) as Node<NodeDefaultAttribute>)
81
+ : recordData.children;
82
+
83
+ return {
84
+ ...recordData,
85
+ attributes: attributes as any,
86
+ children,
87
+ };
88
+ }
@@ -3,6 +3,7 @@ import { generateRandomKeyForNode } from './generateRandomKeyForNode';
3
3
  import { analyseNodeByPatterns } from './analyseNodeByPatterns';
4
4
  import { analyseNodeStructural } from './analyseNodeStructural';
5
5
  import { isNodeArray, isNodeNullOrUndefined, isNodeString } from './nodeGuards';
6
+ import { normalizeNodeForValidation } from './__special_exceptions';
6
7
 
7
8
  export {
8
9
  isEmptyObject,
@@ -78,11 +79,16 @@ export function analyseAndProccess(
78
79
  return null;
79
80
  }
80
81
 
81
- const { valid, message } = analyseNode(node);
82
+ // Normalize persisted JSON "select option" objects into primitives before
83
+ // validation (e.g. { label, value } -> value). This commonly appears in
84
+ // attributes like events[].navigate_to.
85
+ const normalized = normalizeNodeForValidation(node);
86
+
87
+ const { valid, message } = analyseNode(normalized);
82
88
  if (!valid) {
83
89
  throw new Error(message ?? 'Node is not valid');
84
90
  }
85
91
 
86
92
  const usedKeys = new Set<string>();
87
- return assignMissingKeys(node, usedKeys);
93
+ return assignMissingKeys(normalized, usedKeys);
88
94
  }
@@ -24,7 +24,6 @@ type AttributeTypeSpec = string | string[];
24
24
  type AttributeSchema = Record<string, AttributeTypeSpec>;
25
25
 
26
26
  type PatternLike = {
27
- allowUnknownAttributes: boolean;
28
27
  pattern: {
29
28
  type: string;
30
29
  children: unknown;
@@ -56,7 +55,10 @@ function isNodeDataLike(
56
55
  ): value is NodeData<NodeDefaultAttribute> {
57
56
  if (!isPlainObject(value)) return false;
58
57
  const maybeType = (value as Record<string, unknown>).type;
59
- return typeof maybeType === 'string' && 'children' in value;
58
+ // `children` is optional in persisted JSON; treat missing as `undefined`.
59
+ // The structural validator handles overall shape; the pattern validator should
60
+ // focus on types/attributes/children rules, not require the key to exist.
61
+ return typeof maybeType === 'string';
60
62
  }
61
63
 
62
64
  function normalizeTypeOrFallback(type: string): string {
@@ -71,14 +73,19 @@ function getChildrenSpecTokens(pattern: PatternLike): string[] {
71
73
  }
72
74
 
73
75
  function splitChildrenSpec(tokens: string[]): {
74
- baseKinds: Set<'never' | 'string' | 'node' | 'array'>;
76
+ baseKinds: Set<'never' | 'string' | 'node'>;
75
77
  allowedComponentTypes: Set<string>;
76
78
  } {
77
- const baseKinds = new Set<'never' | 'string' | 'node' | 'array'>();
79
+ const baseKinds = new Set<'never' | 'string' | 'node'>();
78
80
  const allowedComponentTypes = new Set<string>();
79
81
  for (const t of tokens) {
80
- if (t === 'never' || t === 'string' || t === 'node' || t === 'array') {
82
+ // NOTE: pattern.children no longer uses the 'array' token.
83
+ // 'node' means "a node OR an array of nodes".
84
+ if (t === 'never' || t === 'string' || t === 'node') {
81
85
  baseKinds.add(t);
86
+ } else if (t === 'array') {
87
+ // Backwards-compat: treat 'array' as 'node'
88
+ baseKinds.add('node');
82
89
  } else if (typeof t === 'string' && t) {
83
90
  allowedComponentTypes.add(normalizeTypeOrFallback(t));
84
91
  }
@@ -88,7 +95,7 @@ function splitChildrenSpec(tokens: string[]): {
88
95
 
89
96
  function validateChildrenNeverRule(
90
97
  children: Node<NodeDefaultAttribute>,
91
- baseKinds: Set<'never' | 'string' | 'node' | 'array'>,
98
+ baseKinds: Set<'never' | 'string' | 'node'>,
92
99
  path: string,
93
100
  ): AnalyseResultWithPath {
94
101
  if (!baseKinds.has('never')) return ok();
@@ -98,7 +105,7 @@ function validateChildrenNeverRule(
98
105
 
99
106
  function validateChildrenKindRule(
100
107
  children: Node<NodeDefaultAttribute>,
101
- baseKinds: Set<'never' | 'string' | 'node' | 'array'>,
108
+ baseKinds: Set<'never' | 'string' | 'node'>,
102
109
  allowedComponentTypes: Set<string>,
103
110
  path: string,
104
111
  ): AnalyseResultWithPath {
@@ -122,7 +129,8 @@ function validateChildrenKindRule(
122
129
  : fail(`Children must not be a string`, path);
123
130
  }
124
131
  if (isNodeArray(children)) {
125
- return baseKinds.has('array')
132
+ // 'node' allows both a single node and an array of nodes
133
+ return baseKinds.has('node')
126
134
  ? ok()
127
135
  : fail(`Children must not be an array`, path);
128
136
  }
@@ -295,6 +303,11 @@ function validateAttributeValue(
295
303
  spec: AttributeTypeSpec,
296
304
  path: string,
297
305
  ): AnalyseResultWithPath {
306
+ // Pattern validation does not enforce requiredness (it only validates types for
307
+ // keys that exist). Treat null/undefined as "unset" so persisted JSON can use
308
+ // null to clear fields (e.g. events[].navigate_to = null).
309
+ if (value == null) return ok();
310
+
298
311
  if (Array.isArray(spec)) {
299
312
  return validateEnumValue(value, spec, path);
300
313
  }
@@ -338,10 +351,31 @@ function validateAttributesByPattern(
338
351
  pattern.pattern.attributes ??
339
352
  {}) as AttributeSchema;
340
353
 
354
+ // Validate nested `attributes.style` as an object; validate any style keys that also exist in schema.
355
+ const maybeStyle = (attrs as Record<string, unknown>).style;
356
+ if (maybeStyle != null) {
357
+ if (!isPlainObject(maybeStyle)) {
358
+ return fail(`style must be an object`, joinPath(path, 'style'));
359
+ }
360
+ for (const [styleKey, styleValue] of Object.entries(
361
+ maybeStyle as Record<string, unknown>,
362
+ )) {
363
+ const spec = schema?.[styleKey];
364
+ if (!spec) continue; // allow unknown style keys (free-form CSSProperties)
365
+ const res = validateAttributeValue(
366
+ pattern.pattern.type,
367
+ styleValue,
368
+ spec,
369
+ joinPath(joinPath(path, 'style'), styleKey),
370
+ );
371
+ if (!res.valid) return res;
372
+ }
373
+ }
374
+
341
375
  for (const [attrName, attrValue] of Object.entries(attrs)) {
376
+ if (attrName === 'style') continue;
342
377
  const attrSpec = schema?.[attrName];
343
378
  if (!attrSpec) {
344
- if (pattern.allowUnknownAttributes) continue;
345
379
  return fail(`Unknown attribute "${attrName}"`, joinPath(path, attrName));
346
380
  }
347
381
  const res = validateAttributeValue(
@@ -17,6 +17,16 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
17
17
  options: ExtractTextStyleOptions = {},
18
18
  ) {
19
19
  const attributes = node.attributes;
20
+ const styleBag = (attributes as any)?.style as
21
+ | Record<string, unknown>
22
+ | undefined;
23
+ const get = <K extends keyof TextPropsGenerated['attributes']>(
24
+ key: K,
25
+ ): TextPropsGenerated['attributes'][K] | undefined => {
26
+ const direct = (attributes as any)?.[key];
27
+ if (direct !== undefined && direct !== null) return direct;
28
+ return styleBag?.[key as unknown as string] as any;
29
+ };
20
30
  const resolvedAppConfig = options.appConfig ?? defaultAppConfig;
21
31
  const { screenStyle, theme } = resolvedAppConfig;
22
32
  const fallbackColor =
@@ -31,20 +41,23 @@ export function extractTextStyle<T extends TextPropsGenerated['attributes']>(
31
41
  }
32
42
 
33
43
  // Typography
34
- if (attributes.fontSize !== undefined) {
35
- const parsed = parseSize(attributes.fontSize);
44
+ const fontSize = get('fontSize') as any;
45
+ if (fontSize !== undefined) {
46
+ const parsed = parseSize(fontSize);
36
47
  style.fontSize = parsed as React.CSSProperties['fontSize'];
37
48
  } else {
38
49
  style.fontSize = fs(14);
39
50
  }
40
- if (attributes.fontWeight) style.fontWeight = attributes.fontWeight;
41
- const resolvedTextColor = parseColor(attributes.color, {
51
+ const fontWeight = get('fontWeight') as any;
52
+ if (fontWeight) style.fontWeight = fontWeight;
53
+ const resolvedTextColor = parseColor(get('color') as any, {
42
54
  projectColors: options.projectColors,
43
55
  appConfig: resolvedAppConfig,
44
56
  });
45
57
  style.color = resolvedTextColor ?? fallbackColor;
46
- if (attributes.textAlign)
47
- style.textAlign = attributes.textAlign as React.CSSProperties['textAlign'];
58
+ const textAlign = get('textAlign') as any;
59
+ if (textAlign)
60
+ style.textAlign = textAlign as React.CSSProperties['textAlign'];
48
61
 
49
62
  return { ...extractViewStyle(node, options), ...style };
50
63
  }