dynim-react 1.0.7 → 1.0.8

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.
@@ -2,10 +2,11 @@
2
2
  * BuilderProvider - React integration for the visual builder
3
3
  *
4
4
  * Integrates the visual builder UI (floating bar, drag engine, overlays)
5
- * from dynim-core.
5
+ * from dynim-core. Loads tenant bundles via dynamic import() and renders
6
+ * them as React components.
6
7
  */
7
8
  import { type ReactNode } from 'react';
8
- import type { BuilderInstance, CodeEdit, CodeEvent, CodeMessage, StyleChange } from 'dynim-core';
9
+ import type { BuilderInstance, CodeEdit, CodeEvent, CodeMessage } from 'dynim-core';
9
10
  export interface BuilderConfig {
10
11
  apiBase?: string;
11
12
  pageId?: string;
@@ -27,18 +28,8 @@ export interface BuilderConfig {
27
28
  onCodeEdit?: (edit: CodeEdit) => void;
28
29
  /** @deprecated Use onCodeMessageUpdate instead */
29
30
  onCodeMessage?: (event: CodeEvent) => void;
30
- /** @deprecated No longer needed - BuilderProvider creates internal container */
31
- previewContainer?: HTMLElement;
32
31
  /** Function to resolve bundle URL from project ID */
33
32
  getBundleUrl?: (projectId: string) => string | Promise<string>;
34
- /** If true, bundles replace children when loaded. If false, uses external previewContainer (default: true) */
35
- inPlacePreview?: boolean;
36
- /** Whether to auto-apply style changes from code edits (default: true) */
37
- autoApplyStyles?: boolean;
38
- /** Transition duration for bundle crossfade in ms (default: 300) */
39
- previewTransitionDuration?: number;
40
- /** Called when a style change is applied */
41
- onStyleApplied?: (change: StyleChange, elements: Element[]) => void;
42
33
  /** Called when bundle starts loading */
43
34
  onBundleLoadStart?: (bundleUrl: string) => void;
44
35
  /** Called when bundle finishes loading */
@@ -66,7 +57,7 @@ export interface BuilderContextValue {
66
57
  resetCodeMessage: () => void;
67
58
  /** @deprecated Use codeMessage.edits instead */
68
59
  codeEdits: CodeEdit[];
69
- /** Load a bundle into the preview container */
60
+ /** Load a bundle by URL */
70
61
  loadBundle: (bundleUrl: string) => Promise<void>;
71
62
  /** Load bundle for a project (uses getBundleUrl from config) */
72
63
  loadProjectBundle: (projectId: string) => Promise<void>;
@@ -74,19 +65,11 @@ export interface BuilderContextValue {
74
65
  loadSavedBundle: (projectId: string) => Promise<void>;
75
66
  /** Load temp bundle for a project (AI edits preview) */
76
67
  loadTempBundle: (projectId: string) => Promise<void>;
77
- /** Apply styles directly to preview DOM */
78
- applyStyles: (selector: string, styles: Record<string, string>) => void;
79
- /** Apply className to preview DOM */
80
- applyClassName: (selector: string, className: string) => void;
81
- /** Toggle CSS classes on preview DOM */
82
- toggleClasses: (selector: string, add: string[], remove: string[]) => void;
83
- /** Check if bundle manager is available */
84
- hasBundleManager: boolean;
85
68
  /** Check if a bundle is currently loaded */
86
69
  isBundleLoaded: boolean;
87
70
  /** Check if bundle is loading */
88
71
  isBundleLoading: boolean;
89
- /** Unload current bundle */
72
+ /** Unload current bundle (show children again) */
90
73
  unloadBundle: () => void;
91
74
  }
92
75
  export interface BuilderProviderProps {
@@ -96,6 +79,9 @@ export interface BuilderProviderProps {
96
79
  }
97
80
  /**
98
81
  * BuilderProvider - Wraps your app with builder support
82
+ *
83
+ * When a bundle is loaded, it replaces children with the bundle's App component.
84
+ * Bundles are loaded via dynamic import() and rendered as React components.
99
85
  */
100
86
  export declare function BuilderProvider({ children, config, onBuilderReady, }: BuilderProviderProps): JSX.Element;
101
87
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"BuilderProvider.d.ts","sourceRoot":"","sources":["../../src/builder/BuilderProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BuilderProvider.d.ts","sourceRoot":"","sources":["../../src/builder/BuilderProvider.tsx"],"names":[],"mappings":"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"}
@@ -1,29 +1,29 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
3
  * BuilderProvider - React integration for the visual builder
4
4
  *
5
5
  * Integrates the visual builder UI (floating bar, drag engine, overlays)
6
- * from dynim-core.
6
+ * from dynim-core. Loads tenant bundles via dynamic import() and renders
7
+ * them as React components.
7
8
  */
8
9
  import React, { createContext, useContext, useEffect, useRef, useCallback, useMemo, useState, } from 'react';
9
10
  import ReactDOM from 'react-dom';
10
- import { createBuilderClient, createBuilder, createCodeClient, createBundleManager, createStyleApplier, hasStyleChanges, } from 'dynim-core';
11
+ import { createBuilderClient, createBuilder, createCodeClient, } from 'dynim-core';
11
12
  import { createSharedContext } from '../inference/sharedContext';
12
13
  const BuilderContext = createContext(null);
13
14
  /**
14
15
  * BuilderProvider - Wraps your app with builder support
16
+ *
17
+ * When a bundle is loaded, it replaces children with the bundle's App component.
18
+ * Bundles are loaded via dynamic import() and rendered as React components.
15
19
  */
16
20
  export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
17
21
  const builderClientRef = useRef(null);
18
22
  const builderRef = useRef(null);
19
23
  const codeClientRef = useRef(null);
20
- const bundleManagerRef = useRef(null);
21
- const styleApplierRef = useRef(null);
22
- const [internalContainer, setInternalContainer] = useState(null);
23
24
  const [isBuilderActive, setIsBuilderActive] = useState(false);
24
25
  const [isBundleLoaded, setIsBundleLoaded] = useState(false);
25
26
  const [isBundleLoading, setIsBundleLoading] = useState(false);
26
- const [hasBundleManager, setHasBundleManager] = useState(false);
27
27
  const [codeMessage, setCodeMessage] = useState({
28
28
  thinking: '',
29
29
  text: '',
@@ -32,22 +32,35 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
32
32
  bundleReady: false,
33
33
  bundleError: undefined,
34
34
  });
35
+ // The loaded tenant app component
36
+ const [TenantApp, setTenantApp] = useState(null);
37
+ // Error state for bundle rendering failures
38
+ const [bundleError, setBundleError] = useState(null);
35
39
  // Set up shared context for bundles (exposes React, ReactDOM, packages, hooks, contexts)
36
40
  useEffect(() => {
37
41
  const { packages = {}, hooks = {}, contexts = {} } = config;
42
+ // Import useBuilder dynamically to register in packages (avoids circular dep at module level)
43
+ const sdkExports = {
44
+ useBuilder,
45
+ BuilderProvider,
46
+ // Add other commonly used exports bundles might need
47
+ };
38
48
  createSharedContext({
39
49
  React,
40
50
  ReactDOM,
41
- packages,
51
+ packages: {
52
+ 'dynim-react': sdkExports,
53
+ ...packages,
54
+ },
42
55
  hooks,
43
56
  contexts,
44
57
  });
45
- console.log('[BuilderProvider] Shared context initialized with packages:', Object.keys(packages));
58
+ console.log('[BuilderProvider] Shared context initialized with packages:', ['dynim-react', ...Object.keys(packages)]);
46
59
  }, [config]);
47
60
  // Initialize code client FIRST (before builder, so we can share it)
48
61
  useEffect(() => {
49
62
  const { apiBase = 'http://localhost:8080', sessionToken, refreshToken, getSession, onCodeMessageUpdate, onCodeMessage, // deprecated
50
- onCodeEdit, onError, autoApplyStyles = true, } = config;
63
+ onCodeEdit, onError, } = config;
51
64
  codeClientRef.current = createCodeClient({
52
65
  apiBase,
53
66
  sessionToken,
@@ -65,11 +78,6 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
65
78
  },
66
79
  onEdit: (edit) => {
67
80
  console.log('[BuilderProvider] Code edit:', edit);
68
- // Auto-apply style changes if enabled and style applier is available
69
- if (autoApplyStyles && styleApplierRef.current && hasStyleChanges(edit)) {
70
- const result = styleApplierRef.current.processEdit(edit);
71
- console.log('[BuilderProvider] Style changes:', result.changes.length, 'applied:', result.applied);
72
- }
73
81
  onCodeEdit?.(edit);
74
82
  },
75
83
  onError: (error) => {
@@ -121,80 +129,6 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
121
129
  },
122
130
  });
123
131
  }, [config]);
124
- // Initialize bundle manager
125
- useEffect(() => {
126
- const { previewContainer, inPlacePreview = true, previewTransitionDuration = 300, sessionToken, getSession, onStyleApplied, onBundleLoadStart, onBundleLoadComplete, onError, } = config;
127
- // Determine which container to use
128
- const container = inPlacePreview ? internalContainer : previewContainer;
129
- console.log('[BuilderProvider] Bundle manager init:', { inPlacePreview, hasInternalContainer: !!internalContainer, hasPreviewContainer: !!previewContainer });
130
- if (!container) {
131
- // For inPlacePreview, container may not be ready on first render
132
- console.log('[BuilderProvider] No container yet, skipping bundle manager init');
133
- return;
134
- }
135
- // Create auth token getter for bundle requests
136
- const getAuthToken = async () => {
137
- // First try config sessionToken
138
- if (sessionToken) {
139
- return sessionToken;
140
- }
141
- // Then try getSession callback
142
- if (getSession) {
143
- try {
144
- const session = await getSession();
145
- return session.token;
146
- }
147
- catch (e) {
148
- console.warn('[BuilderProvider] Failed to get session for bundle auth:', e);
149
- }
150
- }
151
- return null;
152
- };
153
- bundleManagerRef.current = createBundleManager({
154
- parent: container,
155
- transitionDuration: previewTransitionDuration,
156
- getAuthToken,
157
- onLoadStart: (url) => {
158
- setIsBundleLoading(true);
159
- onBundleLoadStart?.(url);
160
- },
161
- onLoadComplete: (url) => {
162
- setIsBundleLoading(false);
163
- setIsBundleLoaded(true);
164
- onBundleLoadComplete?.(url);
165
- },
166
- onError: (error) => {
167
- setIsBundleLoading(false);
168
- console.error('[BuilderProvider] Bundle load error:', error);
169
- onError?.(error);
170
- },
171
- onUnload: () => {
172
- setIsBundleLoaded(false);
173
- },
174
- });
175
- setHasBundleManager(true);
176
- console.log('[BuilderProvider] Bundle manager created');
177
- // Create style applier that targets the bundle manager's container
178
- styleApplierRef.current = createStyleApplier({
179
- queryElements: (selector) => {
180
- const container = bundleManagerRef.current?.getActiveContainer();
181
- return container?.querySelectorAll(selector) ?? document.querySelectorAll(selector);
182
- },
183
- onApply: (change, elements) => {
184
- console.log('[BuilderProvider] Style applied:', change, elements.length, 'elements');
185
- onStyleApplied?.(change, elements);
186
- },
187
- onError: (change, error) => {
188
- console.error('[BuilderProvider] Style apply error:', error, change);
189
- },
190
- });
191
- return () => {
192
- bundleManagerRef.current?.destroy();
193
- bundleManagerRef.current = null;
194
- styleApplierRef.current = null;
195
- setHasBundleManager(false);
196
- };
197
- }, [config, internalContainer]);
198
132
  // Enter builder mode - activates the visual builder UI
199
133
  const enterBuilder = useCallback(() => {
200
134
  if (builderRef.current && !builderRef.current.isActive()) {
@@ -213,14 +147,94 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
213
147
  await builderRef.current.save();
214
148
  }
215
149
  }, []);
216
- // Bundle manager methods (defined first since saveCode depends on loadProjectBundle)
150
+ /**
151
+ * Get auth token for bundle requests
152
+ */
153
+ const getAuthToken = useCallback(async () => {
154
+ const { sessionToken, getSession } = config;
155
+ if (sessionToken) {
156
+ return sessionToken;
157
+ }
158
+ if (getSession) {
159
+ try {
160
+ const session = await getSession();
161
+ return session.token;
162
+ }
163
+ catch (e) {
164
+ console.warn('[BuilderProvider] Failed to get session for bundle auth:', e);
165
+ }
166
+ }
167
+ return null;
168
+ }, [config]);
169
+ // Track loading state in ref to avoid stale closure issues
170
+ const isBundleLoadingRef = useRef(false);
171
+ /**
172
+ * Load a bundle via dynamic import()
173
+ * Fetches with auth, creates blob URL, imports as ES module
174
+ */
217
175
  const loadBundle = useCallback(async (bundleUrl) => {
218
- if (!bundleManagerRef.current) {
219
- console.warn('[BuilderProvider] Bundle manager not initialized (no previewContainer)');
176
+ const { onBundleLoadStart, onBundleLoadComplete, onError } = config;
177
+ if (isBundleLoadingRef.current) {
178
+ console.warn('[BuilderProvider] Already loading a bundle');
220
179
  return;
221
180
  }
222
- await bundleManagerRef.current.load(bundleUrl);
223
- }, []);
181
+ isBundleLoadingRef.current = true;
182
+ setIsBundleLoading(true);
183
+ setBundleError(null); // Clear any previous error
184
+ onBundleLoadStart?.(bundleUrl);
185
+ console.log('[BuilderProvider] Loading bundle:', bundleUrl);
186
+ try {
187
+ // Fetch bundle with auth headers
188
+ const token = await getAuthToken();
189
+ const headers = {
190
+ 'Accept': 'application/javascript',
191
+ };
192
+ if (token) {
193
+ headers['Authorization'] = `Bearer ${token}`;
194
+ }
195
+ const response = await fetch(bundleUrl, {
196
+ credentials: 'include',
197
+ headers,
198
+ });
199
+ if (!response.ok) {
200
+ throw new Error(`Failed to fetch bundle: ${response.status} ${response.statusText}`);
201
+ }
202
+ const code = await response.text();
203
+ console.log('[BuilderProvider] Bundle fetched, size:', code.length);
204
+ // Create blob URL for dynamic import
205
+ const blob = new Blob([code], { type: 'application/javascript' });
206
+ const blobUrl = URL.createObjectURL(blob);
207
+ try {
208
+ // Dynamic import the bundle as ES module
209
+ const module = await import(/* @vite-ignore */ blobUrl);
210
+ console.log('[BuilderProvider] Bundle imported, exports:', Object.keys(module));
211
+ // Get the App component (try App, then default export)
212
+ const App = module.App || module.default;
213
+ if (!App) {
214
+ throw new Error('Bundle has no App or default export');
215
+ }
216
+ // Store the component
217
+ setTenantApp(() => App);
218
+ setIsBundleLoaded(true);
219
+ console.log('[BuilderProvider] Bundle loaded successfully');
220
+ onBundleLoadComplete?.(bundleUrl);
221
+ }
222
+ finally {
223
+ // Clean up blob URL
224
+ URL.revokeObjectURL(blobUrl);
225
+ }
226
+ }
227
+ catch (error) {
228
+ console.error('[BuilderProvider] Bundle load failed:', error);
229
+ setBundleError(error);
230
+ onError?.(error);
231
+ // Don't throw - let the app continue with children
232
+ }
233
+ finally {
234
+ isBundleLoadingRef.current = false;
235
+ setIsBundleLoading(false);
236
+ }
237
+ }, [config, getAuthToken]);
224
238
  const loadProjectBundle = useCallback(async (projectId, forceReload = false) => {
225
239
  const { getBundleUrl, apiBase = 'http://localhost:8080' } = config;
226
240
  let bundleUrl;
@@ -230,74 +244,63 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
230
244
  else {
231
245
  bundleUrl = `${apiBase.replace(':8080', ':8081')}/bundles/preview?project_id=${projectId}`;
232
246
  }
233
- // Cache bust to force reload (bypasses bundle manager's same-URL skip)
247
+ // Cache bust to force reload
234
248
  if (forceReload) {
235
- bundleUrl += `&_t=${Date.now()}`;
249
+ bundleUrl += (bundleUrl.includes('?') ? '&' : '?') + `_t=${Date.now()}`;
236
250
  }
237
251
  await loadBundle(bundleUrl);
238
252
  }, [config, loadBundle]);
239
253
  // Load saved (permanent) bundle - used for initial load, after save, and after abandon
240
- // Uses the authenticated build-server endpoint which serves from Redis cache
241
- const loadSavedBundle = useCallback(async (projectId) => {
254
+ // Note: projectId is for API consistency; actual project comes from JWT
255
+ const loadSavedBundle = useCallback(async (_projectId) => {
242
256
  const { apiBase = 'http://localhost:8080' } = config;
243
- // Use build-server's authenticated bundle endpoint
244
257
  const bundleUrl = `${apiBase}/api/code/bundle`;
245
258
  console.log('[BuilderProvider] Loading saved bundle via:', bundleUrl);
246
259
  await loadBundle(bundleUrl);
247
- console.log('[BuilderProvider] Saved bundle loaded');
248
260
  }, [config, loadBundle]);
249
261
  // Load temp bundle - used when AI finishes editing to preview changes
250
- // Uses the authenticated build-server endpoint which serves from Redis cache
251
- const loadTempBundle = useCallback(async (projectId) => {
262
+ // Note: projectId is for API consistency; actual project comes from JWT
263
+ const loadTempBundle = useCallback(async (_projectId) => {
252
264
  const { apiBase = 'http://localhost:8080' } = config;
253
- // Use build-server's authenticated bundle endpoint (cache busting to force reload)
265
+ // Cache bust to force reload
254
266
  const bundleUrl = `${apiBase}/api/code/bundle?_t=${Date.now()}`;
255
267
  console.log('[BuilderProvider] Loading temp bundle via:', bundleUrl);
256
268
  await loadBundle(bundleUrl);
257
- console.log('[BuilderProvider] Temp bundle loaded');
258
269
  }, [config, loadBundle]);
259
- // Auto-reload temp bundle when bundle is ready and bundle manager is available
260
- // Uses hasBundleManager as dependency so it re-runs when manager initializes
270
+ // Auto-load temp bundle when bundle is ready
261
271
  useEffect(() => {
262
272
  console.log('[BuilderProvider] Auto-load check:', {
263
273
  bundleReady: codeMessage.bundleReady,
264
274
  projectId: codeMessage.projectId,
265
- hasBundleManager,
266
275
  });
267
- if (codeMessage.bundleReady && codeMessage.projectId && hasBundleManager) {
276
+ if (codeMessage.bundleReady && codeMessage.projectId) {
268
277
  console.log('[BuilderProvider] Bundle ready - loading temp bundle:', codeMessage.projectId);
269
278
  loadTempBundle(codeMessage.projectId);
270
279
  }
271
- }, [codeMessage.bundleReady, codeMessage.projectId, hasBundleManager, loadTempBundle]);
272
- const applyStyles = useCallback((selector, styles) => {
273
- bundleManagerRef.current?.applyStyles(selector, styles);
274
- }, []);
275
- const applyClassName = useCallback((selector, className) => {
276
- bundleManagerRef.current?.applyClassName(selector, className);
277
- }, []);
278
- const toggleClasses = useCallback((selector, add, remove) => {
279
- bundleManagerRef.current?.toggleClasses(selector, add, remove);
280
- }, []);
280
+ }, [codeMessage.bundleReady, codeMessage.projectId, loadTempBundle]);
281
+ // Unload bundle - show children again
281
282
  const unloadBundle = useCallback(() => {
282
- bundleManagerRef.current?.unload();
283
+ setTenantApp(null);
284
+ setIsBundleLoaded(false);
285
+ console.log('[BuilderProvider] Bundle unloaded');
283
286
  }, []);
284
- // Code client methods (projectId comes from JWT on server, or from codeMessage for bundles)
287
+ // Code client methods
285
288
  const sendCode = useCallback(async (query) => {
286
289
  await codeClientRef.current?.sendCode(query);
287
290
  }, []);
288
291
  const saveCode = useCallback(async () => {
289
292
  // 1. Persist changes to backend (promotes temp to saved)
290
293
  await codeClientRef.current?.saveCode();
291
- // 2. Load the newly saved bundle (projectId from last bundle_ready event)
292
- if (bundleManagerRef.current && codeMessage.projectId) {
294
+ // 2. Load the newly saved bundle
295
+ if (codeMessage.projectId) {
293
296
  await loadSavedBundle(codeMessage.projectId);
294
297
  }
295
298
  }, [loadSavedBundle, codeMessage.projectId]);
296
299
  const abandonCode = useCallback(async () => {
297
300
  // 1. Discard temp changes on server
298
301
  await codeClientRef.current?.abandonCode();
299
- // 2. Revert to saved bundle (projectId from last bundle_ready event)
300
- if (bundleManagerRef.current && codeMessage.projectId) {
302
+ // 2. Revert to saved bundle
303
+ if (codeMessage.projectId) {
301
304
  await loadSavedBundle(codeMessage.projectId);
302
305
  }
303
306
  }, [loadSavedBundle, codeMessage.projectId]);
@@ -313,24 +316,19 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
313
316
  saveBuilder,
314
317
  isBuilderActive,
315
318
  getBuilder: () => builderRef.current,
316
- // Code client methods (all AI chat goes through sendCode)
319
+ // Code client methods
317
320
  sendCode,
318
321
  saveCode,
319
322
  abandonCode,
320
323
  warmCache,
321
324
  codeMessage,
322
325
  resetCodeMessage,
323
- // Deprecated - use codeMessage.edits instead
324
326
  codeEdits: codeMessage.edits,
325
- // Bundle manager methods
327
+ // Bundle methods
326
328
  loadBundle,
327
329
  loadProjectBundle,
328
330
  loadSavedBundle,
329
331
  loadTempBundle,
330
- applyStyles,
331
- applyClassName,
332
- toggleClasses,
333
- hasBundleManager,
334
332
  isBundleLoaded,
335
333
  isBundleLoading,
336
334
  unloadBundle,
@@ -349,10 +347,6 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
349
347
  loadProjectBundle,
350
348
  loadSavedBundle,
351
349
  loadTempBundle,
352
- applyStyles,
353
- applyClassName,
354
- toggleClasses,
355
- hasBundleManager,
356
350
  isBundleLoaded,
357
351
  isBundleLoading,
358
352
  unloadBundle,
@@ -365,14 +359,30 @@ export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
365
359
  onBuilderReady(contextValue);
366
360
  }
367
361
  }, [contextValue, onBuilderReady]);
368
- const { inPlacePreview = true } = config;
369
- // Callback ref to capture container element
370
- const containerRefCallback = useCallback((node) => {
371
- if (node) {
372
- setInternalContainer(node);
362
+ // Render the appropriate content based on state
363
+ const renderContent = () => {
364
+ // Loading state
365
+ if (isBundleLoading) {
366
+ return children; // Show children while loading (no flash)
373
367
  }
374
- }, []);
375
- return (_jsxs(BuilderContext.Provider, { value: contextValue, children: [(!inPlacePreview || !isBundleLoaded) && children, inPlacePreview && (_jsx("div", { ref: containerRefCallback, style: { display: isBundleLoaded ? 'block' : 'none' } }))] }));
368
+ // Error state - show error banner with children
369
+ if (bundleError) {
370
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
371
+ padding: 12,
372
+ background: '#fef3c7',
373
+ borderBottom: '1px solid #fcd34d',
374
+ fontSize: 13,
375
+ color: '#92400e',
376
+ }, children: ["Bundle error: ", bundleError.message] }), children] }));
377
+ }
378
+ // Bundle loaded - render tenant app
379
+ if (TenantApp) {
380
+ return _jsx(TenantApp, {});
381
+ }
382
+ // Default - render children
383
+ return children;
384
+ };
385
+ return (_jsx(BuilderContext.Provider, { value: contextValue, children: renderContent() }));
376
386
  }
377
387
  /**
378
388
  * Hook to access builder functionality
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dynim-react",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",