@developer_tribe/react-builder 1.0.8 → 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 (216) 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/Builder.tsx +20 -6
  162. package/src/components/BuilderButton.tsx +75 -38
  163. package/src/components/BuilderProvider.tsx +22 -2
  164. package/src/components/DeviceButton.tsx +12 -5
  165. package/src/components/EditorHeader.tsx +296 -38
  166. package/src/components/ParamsProvider.tsx +7 -0
  167. package/src/components/RenderErrorBoundary.tsx +200 -0
  168. package/src/hooks/useParams.ts +5 -1
  169. package/src/hooks/useSyncHtmlThemeClass.ts +19 -0
  170. package/src/index.native.ts +7 -0
  171. package/src/index.ts +8 -0
  172. package/src/migrations/migratePipe.ts +59 -0
  173. package/src/migrations/migrations/1.1.0_normalize_style_attributes.ts +80 -0
  174. package/src/migrations/semver.ts +24 -0
  175. package/src/migrations/types.ts +9 -0
  176. package/src/mockOS/components/PermissionModal.tsx +3 -2
  177. package/src/mockOS/components/SubscriptionModal.tsx +400 -0
  178. package/src/mockOS/context/MockOSContext.tsx +61 -10
  179. package/src/mockOS/context/MockOSContextBase.ts +1 -0
  180. package/src/mockOS/hooks/useMockIap.ts +11 -0
  181. package/src/mockOS/index.ts +7 -0
  182. package/src/mockOS/managers/mockOSIapManager.ts +10 -0
  183. package/src/mockOS/managers/subscriptionManager.ts +36 -0
  184. package/src/modals/IconPickerModal.tsx +1 -1
  185. package/src/pages/ProjectDebug.tsx +331 -0
  186. package/src/pages/ProjectMigrationPage.tsx +92 -0
  187. package/src/pages/ProjectPage.tsx +313 -161
  188. package/src/pages/ProjectValidationPage.tsx +54 -0
  189. package/src/styles/base/_global.scss +58 -11
  190. package/src/styles/components/_attributes-editor.scss +1 -1
  191. package/src/styles/components/_bottom-bar.scss +7 -4
  192. package/src/styles/components/_editor-shell.scss +126 -4
  193. package/src/styles/components/_mockos-router.scss +3 -2
  194. package/src/styles/components/_ui-components.scss +10 -5
  195. package/src/styles/foundation/_colors.scss +78 -11
  196. package/src/styles/foundation/_mixins.scss +4 -1
  197. package/src/styles/foundation/_sizes.scss +4 -2
  198. package/src/styles/index.scss +1 -0
  199. package/src/styles/layout/_builder.scss +61 -0
  200. package/src/styles/layout/_project-validation.scss +214 -0
  201. package/src/styles/modals/_add-component.scss +4 -2
  202. package/src/styles/modals/_color-modal.scss +4 -2
  203. package/src/styles/modals/_modal-shell.scss +3 -1
  204. package/src/types/Device.ts +5 -0
  205. package/src/utils/__special_exceptions.ts +88 -0
  206. package/src/utils/analyseNode.ts +8 -2
  207. package/src/utils/analyseNodeByPatterns.ts +43 -9
  208. package/src/utils/extractTextStyle.ts +19 -6
  209. package/src/utils/extractViewStyle.ts +68 -59
  210. package/src/utils/getImage.ts +76 -0
  211. package/src/utils/novaToJson.ts +2 -1
  212. package/src/utils/pasteNode.ts +172 -0
  213. package/src/utils/patterns.ts +4 -3
  214. package/dist/android.svg +0 -43
  215. package/dist/apple.svg +0 -16
  216. package/dist/background.jpg +0 -0
@@ -6,6 +6,11 @@ export type OtherParams = Record<string, string | boolean | number>;
6
6
  export type ParamsContextValue = {
7
7
  localizationParams: LocalizationParams;
8
8
  otherParams: OtherParams;
9
+ /**
10
+ * `true` when the value comes from an actual `<ParamsProvider />` in the tree.
11
+ * Used to detect nesting vs the default context value.
12
+ */
13
+ isProvided: boolean;
9
14
  };
10
15
 
11
16
  export type ParamsProviderProps = {
@@ -17,6 +22,7 @@ export type ParamsProviderProps = {
17
22
  export const ParamsContext = createContext<ParamsContextValue>({
18
23
  localizationParams: {},
19
24
  otherParams: {},
25
+ isProvided: false,
20
26
  });
21
27
 
22
28
  export function ParamsProvider({
@@ -28,6 +34,7 @@ export function ParamsProvider({
28
34
  () => ({
29
35
  localizationParams: localizationParams ?? {},
30
36
  otherParams: otherParams ?? {},
37
+ isProvided: true,
31
38
  }),
32
39
  [localizationParams, otherParams],
33
40
  );
@@ -0,0 +1,200 @@
1
+ import React from 'react';
2
+
3
+ export type RenderErrorBoundaryProps = {
4
+ children: React.ReactNode;
5
+ /**
6
+ * Optional label to show under the title, to help identify which preview crashed
7
+ * (e.g. "caught by BuilderProvider" vs "caught by ProjectDebug").
8
+ */
9
+ subtitle?: string;
10
+ /** Optional callback invoked when an error is caught. */
11
+ onError?: (error: Error, componentStack?: string) => void;
12
+ };
13
+
14
+ type RenderErrorBoundaryState = {
15
+ error: Error | null;
16
+ errorStack?: string;
17
+ componentStack?: string;
18
+ copied?: boolean;
19
+ copyError?: string | null;
20
+ };
21
+
22
+ export class RenderErrorBoundary extends React.Component<
23
+ RenderErrorBoundaryProps,
24
+ RenderErrorBoundaryState
25
+ > {
26
+ state: RenderErrorBoundaryState = {
27
+ error: null,
28
+ errorStack: undefined,
29
+ componentStack: undefined,
30
+ copied: false,
31
+ copyError: null,
32
+ };
33
+
34
+ static getDerivedStateFromError(
35
+ error: Error,
36
+ ): Partial<RenderErrorBoundaryState> {
37
+ return {
38
+ error,
39
+ errorStack: error?.stack,
40
+ copied: false,
41
+ copyError: null,
42
+ };
43
+ }
44
+
45
+ componentDidCatch(error: Error, info: React.ErrorInfo) {
46
+ const componentStack =
47
+ (info as unknown as { componentStack?: string })?.componentStack ??
48
+ undefined;
49
+
50
+ this.setState({
51
+ error,
52
+ errorStack: error?.stack,
53
+ componentStack,
54
+ });
55
+
56
+ this.props.onError?.(error, componentStack);
57
+ }
58
+
59
+ private buildDetails(): string {
60
+ const { error, errorStack, componentStack } = this.state;
61
+ return [
62
+ `Error: ${error?.message ?? ''}`,
63
+ '',
64
+ errorStack ? `Stack:\n${errorStack}` : 'Stack: (missing)',
65
+ '',
66
+ componentStack
67
+ ? `React component stack:\n${componentStack}`
68
+ : 'React component stack: (missing)',
69
+ ].join('\n');
70
+ }
71
+
72
+ private copyDetails = async () => {
73
+ const details = this.buildDetails();
74
+
75
+ try {
76
+ if (typeof navigator !== 'undefined' && navigator.clipboard?.writeText) {
77
+ await navigator.clipboard.writeText(details);
78
+ this.setState({ copied: true, copyError: null });
79
+ window.setTimeout(() => this.setState({ copied: false }), 900);
80
+ return;
81
+ }
82
+ } catch {
83
+ // fall through to legacy method
84
+ }
85
+
86
+ try {
87
+ if (typeof document === 'undefined') {
88
+ this.setState({ copied: false, copyError: 'Copy failed' });
89
+ return;
90
+ }
91
+ const el = document.createElement('textarea');
92
+ el.value = details;
93
+ el.setAttribute('readonly', 'true');
94
+ el.style.position = 'fixed';
95
+ el.style.opacity = '0';
96
+ document.body.appendChild(el);
97
+ el.select();
98
+ const ok = document.execCommand('copy');
99
+ document.body.removeChild(el);
100
+ if (!ok) {
101
+ this.setState({ copied: false, copyError: 'Copy failed' });
102
+ return;
103
+ }
104
+ this.setState({ copied: true, copyError: null });
105
+ window.setTimeout(() => this.setState({ copied: false }), 900);
106
+ } catch (e) {
107
+ this.setState({
108
+ copied: false,
109
+ copyError: e instanceof Error ? e.message : 'Copy failed',
110
+ });
111
+ }
112
+ };
113
+
114
+ private reset = () => {
115
+ this.setState({
116
+ error: null,
117
+ errorStack: undefined,
118
+ componentStack: undefined,
119
+ copied: false,
120
+ copyError: null,
121
+ });
122
+ };
123
+
124
+ render() {
125
+ const { error, copied, copyError } = this.state;
126
+ if (!error) return this.props.children;
127
+
128
+ const details = this.buildDetails();
129
+
130
+ return (
131
+ <div
132
+ role="alert"
133
+ style={{
134
+ padding: 16,
135
+ borderRadius: 12,
136
+ background: 'hsl(var(--card, var(--rb-card, 0 0% 100%)))',
137
+ color:
138
+ 'hsl(var(--card-foreground, var(--rb-card-foreground, 220.9 39.3% 11%)))',
139
+ border:
140
+ '1px solid hsl(var(--border, var(--rb-border, 220 13% 91%)) / 0.9)',
141
+ }}
142
+ >
143
+ <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
144
+ <div style={{ fontWeight: 700 }}>Preview crashed</div>
145
+ {this.props.subtitle && (
146
+ <div style={{ opacity: 0.8, fontSize: 12 }}>
147
+ ({this.props.subtitle})
148
+ </div>
149
+ )}
150
+ <div style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
151
+ <button
152
+ type="button"
153
+ className="editor-button"
154
+ onClick={this.copyDetails}
155
+ >
156
+ {copied ? 'Copied' : 'Copy details'}
157
+ </button>
158
+ <button
159
+ type="button"
160
+ className="editor-button"
161
+ onClick={this.reset}
162
+ >
163
+ Try again
164
+ </button>
165
+ </div>
166
+ </div>
167
+
168
+ {copyError && (
169
+ <div
170
+ style={{
171
+ marginTop: 8,
172
+ color:
173
+ 'hsl(var(--destructive, var(--rb-destructive, 0 84.2% 60.2%)))',
174
+ fontSize: 12,
175
+ }}
176
+ >
177
+ {copyError}
178
+ </div>
179
+ )}
180
+
181
+ <pre
182
+ style={{
183
+ marginTop: 12,
184
+ whiteSpace: 'pre-wrap',
185
+ wordBreak: 'break-word',
186
+ fontSize: 12,
187
+ lineHeight: 1.4,
188
+ background: 'hsl(var(--muted, var(--rb-muted, 220 14.3% 95.9%)))',
189
+ padding: 12,
190
+ borderRadius: 10,
191
+ overflow: 'auto',
192
+ maxHeight: 360,
193
+ }}
194
+ >
195
+ {details}
196
+ </pre>
197
+ </div>
198
+ );
199
+ }
200
+ }
@@ -3,6 +3,10 @@ import { ParamsContext } from '../components/ParamsProvider';
3
3
 
4
4
  export function useParams() {
5
5
  return (
6
- useContext(ParamsContext) ?? { localizationParams: {}, otherParams: {} }
6
+ useContext(ParamsContext) ?? {
7
+ localizationParams: {},
8
+ otherParams: {},
9
+ isProvided: false,
10
+ }
7
11
  );
8
12
  }
@@ -0,0 +1,19 @@
1
+ import { useEffect } from 'react';
2
+ import { useRenderStore } from '../store';
3
+
4
+ /**
5
+ * Syncs the builder's `appConfig.theme` to the DOM so CSS can react to `.dark`.
6
+ *
7
+ * - Uses `html.dark` as the toggle (shadcn/tailwind convention).
8
+ * - Also toggles `html.light` so system dark can be overridden back to light.
9
+ */
10
+ export function useSyncHtmlThemeClass() {
11
+ const theme = useRenderStore((s) => s.appConfig.theme);
12
+
13
+ useEffect(() => {
14
+ if (typeof document === 'undefined') return;
15
+ const root = document.documentElement;
16
+ root.classList.toggle('dark', theme === 'dark');
17
+ root.classList.toggle('light', theme === 'light');
18
+ }, [theme]);
19
+ }
@@ -57,6 +57,13 @@ export {
57
57
  export { novaToJson } from './utils/novaToJson';
58
58
  export { getDevices, getDefaultDevice } from './utils/getDevices';
59
59
  export { querySelector } from './utils/querySelector';
60
+ export {
61
+ getImage,
62
+ TRIBE_ASSETS_BASE_URL,
63
+ TribeAssetName,
64
+ parseTribeAssetName,
65
+ resolveImageSrc,
66
+ } from './utils/getImage';
60
67
  export {
61
68
  getPatternByType,
62
69
  getAttributeSchema,
package/src/index.ts CHANGED
@@ -8,6 +8,7 @@ export {
8
8
  } from './components/BuilderProvider';
9
9
  export { ParamsProvider } from './components/ParamsProvider';
10
10
  export { LocalizationParamsProvider } from './components/LocalizationParamsProvider';
11
+ export { RenderErrorBoundary } from './components/RenderErrorBoundary';
11
12
  export { useParams } from './hooks/useParams';
12
13
  export { useLocalizationParams } from './hooks/useLocalizationParams';
13
14
  export { useLocalize } from './hooks/useLocalize';
@@ -39,6 +40,13 @@ export { querySelector } from './utils/querySelector';
39
40
  export { extractViewStyle } from './utils/extractViewStyle';
40
41
  export { extractImageStyle } from './utils/extractImageStyle';
41
42
  export { extractTextStyle } from './utils/extractTextStyle';
43
+ export {
44
+ getImage,
45
+ TRIBE_ASSETS_BASE_URL,
46
+ TribeAssetName,
47
+ parseTribeAssetName,
48
+ resolveImageSrc,
49
+ } from './utils/getImage';
42
50
  export { ProjectPage } from './pages/ProjectPage';
43
51
  export type { ProjectPageProps } from './pages/ProjectPage';
44
52
  export { copyNode } from './utils/copyNode';
@@ -0,0 +1,59 @@
1
+ import type { Project } from '../types/Project';
2
+ import type { Migration } from './types';
3
+ import { isSemverLess } from './semver';
4
+ import { migration_1_1_0_normalize_style_attributes } from './migrations/1.1.0_normalize_style_attributes';
5
+
6
+ export const CURRENT_PROJECT_VERSION = '1.1.0';
7
+
8
+ const MIGRATIONS: Migration[] = [migration_1_1_0_normalize_style_attributes];
9
+
10
+ export type MigrationPipe = {
11
+ projectVersion: string;
12
+ requiredVersion: string;
13
+ required: boolean;
14
+ pending: Migration[];
15
+ };
16
+
17
+ export function getMigrationPipe(
18
+ project: Pick<Project, 'version'>,
19
+ ): MigrationPipe {
20
+ const projectVersion =
21
+ typeof project?.version === 'string' && project.version.trim()
22
+ ? project.version
23
+ : '0.0.0';
24
+
25
+ const required = isSemverLess(projectVersion, CURRENT_PROJECT_VERSION);
26
+
27
+ const pending = required
28
+ ? MIGRATIONS.filter((m) => isSemverLess(projectVersion, m.toVersion))
29
+ : [];
30
+
31
+ return {
32
+ projectVersion,
33
+ requiredVersion: CURRENT_PROJECT_VERSION,
34
+ required,
35
+ pending,
36
+ };
37
+ }
38
+
39
+ export function runProjectMigrations(project: Project): {
40
+ project: Project;
41
+ applied: Migration[];
42
+ } {
43
+ const pipe = getMigrationPipe(project);
44
+ let next = project;
45
+ const applied: Migration[] = [];
46
+
47
+ for (const m of pipe.pending) {
48
+ if (isSemverLess(next.version, m.toVersion)) {
49
+ next = m.run(next);
50
+ applied.push(m);
51
+ }
52
+ }
53
+
54
+ if (isSemverLess(next.version, CURRENT_PROJECT_VERSION)) {
55
+ next = { ...next, version: CURRENT_PROJECT_VERSION };
56
+ }
57
+
58
+ return { project: next, applied };
59
+ }
@@ -0,0 +1,80 @@
1
+ import type { Project } from '../../types/Project';
2
+ import type { Node, NodeData, NodeDefaultAttribute } from '../../types/Node';
3
+ import {
4
+ isEmptyObject,
5
+ isNodeArray,
6
+ isNodeNullOrUndefined,
7
+ isNodeString,
8
+ } from '../../utils/nodeGuards';
9
+ import { getAttributeSchema } from '../../utils/patterns';
10
+ import type { Migration } from '../types';
11
+
12
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
13
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
14
+ }
15
+
16
+ function buildStyleKeySet(): Set<string> {
17
+ const viewSchema = getAttributeSchema('View') ?? {};
18
+ const textSchema = getAttributeSchema('Text') ?? {};
19
+ return new Set([...Object.keys(viewSchema), ...Object.keys(textSchema)]);
20
+ }
21
+
22
+ function migrateNode(node: Node, styleKeys: Set<string>): Node {
23
+ if (
24
+ isNodeNullOrUndefined(node) ||
25
+ isNodeString(node) ||
26
+ isEmptyObject(node)
27
+ ) {
28
+ return node;
29
+ }
30
+ if (isNodeArray(node)) {
31
+ return (node as Node[]).map((n) => migrateNode(n, styleKeys));
32
+ }
33
+
34
+ const record = node as NodeData<NodeDefaultAttribute>;
35
+ const nextChildren = migrateNode(record.children as Node, styleKeys);
36
+
37
+ if (!isPlainObject(record.attributes)) {
38
+ return { ...record, children: nextChildren };
39
+ }
40
+
41
+ const nextAttrs: Record<string, unknown> = { ...(record.attributes ?? {}) };
42
+
43
+ const prevStyle = isPlainObject(nextAttrs.style) ? nextAttrs.style : {};
44
+ const nextStyle: Record<string, unknown> = { ...prevStyle };
45
+
46
+ for (const [k, v] of Object.entries(nextAttrs)) {
47
+ if (k === 'style') continue;
48
+ if (!styleKeys.has(k)) continue;
49
+
50
+ // If already set in style, prefer style (drop legacy flat key).
51
+ if (!(k in nextStyle)) nextStyle[k] = v;
52
+ delete nextAttrs[k];
53
+ }
54
+
55
+ if (Object.keys(nextStyle).length > 0) {
56
+ nextAttrs.style = nextStyle;
57
+ }
58
+
59
+ return {
60
+ ...record,
61
+ children: nextChildren,
62
+ attributes: nextAttrs as NodeDefaultAttribute,
63
+ };
64
+ }
65
+
66
+ export const migration_1_1_0_normalize_style_attributes: Migration = {
67
+ id: '1.1.0-normalize-style-attributes',
68
+ title: 'Normalize legacy style attributes into attributes.style',
69
+ fromVersion: '0.0.0',
70
+ toVersion: '1.1.0',
71
+ run: (project: Project): Project => {
72
+ const styleKeys = buildStyleKeySet();
73
+ const nextData = migrateNode(project.data as unknown as Node, styleKeys);
74
+ return {
75
+ ...project,
76
+ version: '1.1.0',
77
+ data: nextData as any,
78
+ };
79
+ },
80
+ };
@@ -0,0 +1,24 @@
1
+ export type Semver = { major: number; minor: number; patch: number };
2
+
3
+ export function parseSemver(input?: string | null): Semver {
4
+ const raw = typeof input === 'string' ? input.trim() : '';
5
+ const [maj, min, pat] = raw.split('.').slice(0, 3);
6
+ const toNum = (v?: string) => {
7
+ const n = Number(String(v ?? '').replace(/[^\d].*$/, ''));
8
+ return Number.isFinite(n) ? n : 0;
9
+ };
10
+ return { major: toNum(maj), minor: toNum(min), patch: toNum(pat) };
11
+ }
12
+
13
+ export function compareSemver(a: string, b: string): number {
14
+ const A = parseSemver(a);
15
+ const B = parseSemver(b);
16
+ if (A.major !== B.major) return A.major < B.major ? -1 : 1;
17
+ if (A.minor !== B.minor) return A.minor < B.minor ? -1 : 1;
18
+ if (A.patch !== B.patch) return A.patch < B.patch ? -1 : 1;
19
+ return 0;
20
+ }
21
+
22
+ export function isSemverLess(a: string, b: string): boolean {
23
+ return compareSemver(a, b) < 0;
24
+ }
@@ -0,0 +1,9 @@
1
+ import type { Project } from '../types/Project';
2
+
3
+ export type Migration = {
4
+ id: string;
5
+ title: string;
6
+ fromVersion: string;
7
+ toVersion: string;
8
+ run: (project: Project) => Project;
9
+ };
@@ -84,7 +84,8 @@ export const PermissionModal: React.FC<PermissionModalProps> = ({
84
84
  >
85
85
  <div
86
86
  style={{
87
- backgroundColor: 'rgba(255, 255, 255, 0.95)',
87
+ backgroundColor:
88
+ 'hsl(var(--card, var(--rb-card, 0 0% 100%)) / 0.95)',
88
89
  borderRadius: 14,
89
90
  width: '100%',
90
91
  maxWidth: 270,
@@ -187,7 +188,7 @@ export const PermissionModal: React.FC<PermissionModalProps> = ({
187
188
  >
188
189
  <div
189
190
  style={{
190
- backgroundColor: '#FFFFFF',
191
+ backgroundColor: 'hsl(var(--card, var(--rb-card, 0 0% 100%)))',
191
192
  borderRadius: 4,
192
193
  width: '100%',
193
194
  maxWidth: 280,