dynim-react 1.0.38 → 1.0.40
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/package.json +1 -1
- package/dist/builder/BuilderProvider.d.ts +0 -78
- package/dist/builder/BuilderProvider.d.ts.map +0 -1
- package/dist/builder/BuilderProvider.js +0 -505
- package/dist/builder/ChatContext.d.ts +0 -11
- package/dist/builder/ChatContext.d.ts.map +0 -1
- package/dist/builder/ChatContext.js +0 -18
- package/dist/builder/ChatInput.d.ts +0 -11
- package/dist/builder/ChatInput.d.ts.map +0 -1
- package/dist/builder/ChatInput.js +0 -20
- package/dist/builder/CodeChatPanel.d.ts +0 -24
- package/dist/builder/CodeChatPanel.d.ts.map +0 -1
- package/dist/builder/CodeChatPanel.js +0 -304
- package/dist/builder/MessageList.d.ts +0 -14
- package/dist/builder/MessageList.d.ts.map +0 -1
- package/dist/builder/MessageList.js +0 -23
- package/dist/builder/index.d.ts +0 -14
- package/dist/builder/index.d.ts.map +0 -1
- package/dist/builder/index.js +0 -11
- package/dist/builder/useChatbot.d.ts +0 -28
- package/dist/builder/useChatbot.d.ts.map +0 -1
- package/dist/builder/useChatbot.js +0 -60
- package/dist/inference/InferenceProvider.d.ts +0 -4
- package/dist/inference/InferenceProvider.d.ts.map +0 -1
- package/dist/inference/InferenceProvider.js +0 -244
- package/dist/inference/index.d.ts +0 -6
- package/dist/inference/index.d.ts.map +0 -1
- package/dist/inference/index.js +0 -5
package/package.json
CHANGED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* BuilderProvider - React integration for the visual builder
|
|
3
|
-
*
|
|
4
|
-
* Integrates the visual builder UI (floating bar, overlays) from dynim-core.
|
|
5
|
-
* Loads tenant bundles via dynamic import() and renders them as React components.
|
|
6
|
-
*/
|
|
7
|
-
import { type ReactNode } from 'react';
|
|
8
|
-
import type { BuilderInstance, CodeEdit, CodeMessage } from 'dynim-core';
|
|
9
|
-
export interface BuilderConfig {
|
|
10
|
-
/** JWT session token for authentication */
|
|
11
|
-
sessionToken?: string;
|
|
12
|
-
/** Refresh token for getting new session tokens */
|
|
13
|
-
refreshToken?: string;
|
|
14
|
-
/** Function to fetch a new session when current one expires */
|
|
15
|
-
getSession?: () => Promise<{
|
|
16
|
-
token: string;
|
|
17
|
-
refreshToken?: string;
|
|
18
|
-
}>;
|
|
19
|
-
/** Called when an error occurs */
|
|
20
|
-
onError?: (error: Event | Error) => void;
|
|
21
|
-
/** Called when the structured code message updates (recommended) */
|
|
22
|
-
onCodeMessageUpdate?: (message: CodeMessage) => void;
|
|
23
|
-
/** Called when a code edit is received */
|
|
24
|
-
onCodeEdit?: (edit: CodeEdit) => void;
|
|
25
|
-
/** NPM packages to expose to bundles (e.g., { 'react-router-dom': ReactRouterDOM }) */
|
|
26
|
-
packages?: Record<string, unknown>;
|
|
27
|
-
/** Custom hooks to expose to bundles */
|
|
28
|
-
hooks?: Record<string, unknown>;
|
|
29
|
-
/** React contexts to expose to bundles */
|
|
30
|
-
contexts?: Record<string, unknown>;
|
|
31
|
-
}
|
|
32
|
-
export interface BuilderContextValue {
|
|
33
|
-
enterBuilder: () => void;
|
|
34
|
-
exitBuilder: () => void;
|
|
35
|
-
isBuilderActive: boolean;
|
|
36
|
-
getBuilder: () => BuilderInstance | null;
|
|
37
|
-
sendCode: (query: string) => Promise<void>;
|
|
38
|
-
saveCode: () => Promise<void>;
|
|
39
|
-
abandonCode: () => Promise<void>;
|
|
40
|
-
warmCache: () => Promise<void>;
|
|
41
|
-
/** Current structured code message state */
|
|
42
|
-
codeMessage: CodeMessage;
|
|
43
|
-
/** Reset code message state */
|
|
44
|
-
resetCodeMessage: () => void;
|
|
45
|
-
/** @deprecated Use codeMessage.edits instead */
|
|
46
|
-
codeEdits: CodeEdit[];
|
|
47
|
-
/** Load a bundle by URL */
|
|
48
|
-
loadBundle: (bundleUrl: string) => Promise<void>;
|
|
49
|
-
/** Load bundle for a project */
|
|
50
|
-
loadProjectBundle: (projectId: string) => Promise<void>;
|
|
51
|
-
/** Load saved (permanent) bundle for a project */
|
|
52
|
-
loadSavedBundle: (projectId: string) => Promise<void>;
|
|
53
|
-
/** Load temp bundle for a project (AI edits preview) */
|
|
54
|
-
loadTempBundle: (projectId: string) => Promise<void>;
|
|
55
|
-
/** Check if a bundle is currently loaded */
|
|
56
|
-
isBundleLoaded: boolean;
|
|
57
|
-
/** Check if bundle is loading */
|
|
58
|
-
isBundleLoading: boolean;
|
|
59
|
-
/** Unload current bundle (show children again) */
|
|
60
|
-
unloadBundle: () => void;
|
|
61
|
-
}
|
|
62
|
-
export interface BuilderProviderProps {
|
|
63
|
-
children: ReactNode;
|
|
64
|
-
config?: BuilderConfig;
|
|
65
|
-
onBuilderReady?: (context: BuilderContextValue) => void;
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* BuilderProvider - Wraps your app with builder support
|
|
69
|
-
*
|
|
70
|
-
* When a bundle is loaded, it replaces children with the bundle's App component.
|
|
71
|
-
* Bundles are loaded via dynamic import() and rendered as React components.
|
|
72
|
-
*/
|
|
73
|
-
export declare function BuilderProvider({ children, config, onBuilderReady, }: BuilderProviderProps): JSX.Element;
|
|
74
|
-
/**
|
|
75
|
-
* Hook to access builder functionality
|
|
76
|
-
*/
|
|
77
|
-
export declare function useBuilder(): BuilderContextValue;
|
|
78
|
-
//# sourceMappingURL=BuilderProvider.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"BuilderProvider.d.ts","sourceRoot":"","sources":["../../src/builder/BuilderProvider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* BuilderProvider - React integration for the visual builder
|
|
4
|
-
*
|
|
5
|
-
* Integrates the visual builder UI (floating bar, overlays) from dynim-core.
|
|
6
|
-
* Loads tenant bundles via dynamic import() and renders them as React components.
|
|
7
|
-
*/
|
|
8
|
-
import React, { createContext, useContext, useEffect, useRef, useCallback, useMemo, useState, } from 'react';
|
|
9
|
-
import ReactDOM from 'react-dom';
|
|
10
|
-
import { createBuilderClient, createBuilder, createCodeClient, createBundleLoader, BundleNotFoundError, } from 'dynim-core';
|
|
11
|
-
import { createSharedContext } from '../inference/sharedContext';
|
|
12
|
-
const BuilderContext = createContext(null);
|
|
13
|
-
/**
|
|
14
|
-
* BuilderProvider - Wraps your app with builder support
|
|
15
|
-
*
|
|
16
|
-
* When a bundle is loaded, it replaces children with the bundle's App component.
|
|
17
|
-
* Bundles are loaded via dynamic import() and rendered as React components.
|
|
18
|
-
*/
|
|
19
|
-
export function BuilderProvider({ children, config = {}, onBuilderReady, }) {
|
|
20
|
-
const builderClientRef = useRef(null);
|
|
21
|
-
const builderRef = useRef(null);
|
|
22
|
-
const codeClientRef = useRef(null);
|
|
23
|
-
// Track when we're exiting to prevent auto-load from re-triggering
|
|
24
|
-
const isExitingRef = useRef(false);
|
|
25
|
-
// Track if initial bundle load has been attempted
|
|
26
|
-
const hasAttemptedInitialLoadRef = useRef(false);
|
|
27
|
-
// Track builder active state - use BOTH ref (for sync checks in callbacks) and state (for renders)
|
|
28
|
-
const isBuilderActiveRef = useRef(false);
|
|
29
|
-
const [isBuilderActive, setIsBuilderActive] = useState(false);
|
|
30
|
-
// Cached auth token and in-flight promise for fast subsequent loads
|
|
31
|
-
const cachedTokenRef = useRef(null);
|
|
32
|
-
const tokenPromiseRef = useRef(null);
|
|
33
|
-
// Signal counter for triggering bundle loads - ONLY incremented when we decide to load
|
|
34
|
-
// This avoids the dependency cascade problem with the old approach
|
|
35
|
-
const [bundleLoadSignal, setBundleLoadSignal] = useState(0);
|
|
36
|
-
const pendingBundleProjectIdRef = useRef(null);
|
|
37
|
-
const [isBundleLoaded, setIsBundleLoaded] = useState(false);
|
|
38
|
-
const [isBundleLoading, setIsBundleLoading] = useState(false);
|
|
39
|
-
// Track if initial bundle load has completed (to avoid flash of children)
|
|
40
|
-
const [isInitialLoadComplete, setIsInitialLoadComplete] = useState(false);
|
|
41
|
-
const [codeMessage, setCodeMessage] = useState({
|
|
42
|
-
thinking: '',
|
|
43
|
-
text: '',
|
|
44
|
-
edits: [],
|
|
45
|
-
status: 'idle',
|
|
46
|
-
bundleReady: false,
|
|
47
|
-
bundleError: undefined,
|
|
48
|
-
});
|
|
49
|
-
// The loaded tenant app component
|
|
50
|
-
const [TenantApp, setTenantApp] = useState(null);
|
|
51
|
-
// Error state for bundle rendering failures
|
|
52
|
-
const [bundleError, setBundleError] = useState(null);
|
|
53
|
-
// Bundle loader from core (created once)
|
|
54
|
-
const bundleLoaderRef = useRef(null);
|
|
55
|
-
// Keep config callbacks in refs so loader always gets latest values
|
|
56
|
-
const configCallbacksRef = useRef({
|
|
57
|
-
onError: config.onError,
|
|
58
|
-
});
|
|
59
|
-
configCallbacksRef.current = {
|
|
60
|
-
onError: config.onError,
|
|
61
|
-
};
|
|
62
|
-
// Set up shared context for bundles (exposes React, ReactDOM, packages, hooks, contexts)
|
|
63
|
-
useEffect(() => {
|
|
64
|
-
const { packages = {}, hooks = {}, contexts = {} } = config;
|
|
65
|
-
// Import useBuilder dynamically to register in packages (avoids circular dep at module level)
|
|
66
|
-
const sdkExports = {
|
|
67
|
-
useBuilder,
|
|
68
|
-
BuilderProvider,
|
|
69
|
-
// Add other commonly used exports bundles might need
|
|
70
|
-
};
|
|
71
|
-
createSharedContext({
|
|
72
|
-
React,
|
|
73
|
-
ReactDOM,
|
|
74
|
-
packages: {
|
|
75
|
-
'dynim-react': sdkExports,
|
|
76
|
-
...packages,
|
|
77
|
-
},
|
|
78
|
-
hooks,
|
|
79
|
-
contexts,
|
|
80
|
-
});
|
|
81
|
-
console.log('[BuilderProvider] Shared context initialized with packages:', ['dynim-react', ...Object.keys(packages)]);
|
|
82
|
-
}, [config]);
|
|
83
|
-
// Initialize code client FIRST (before builder, so we can share it)
|
|
84
|
-
useEffect(() => {
|
|
85
|
-
const { sessionToken, refreshToken, getSession, onCodeMessageUpdate, onCodeEdit, onError } = config;
|
|
86
|
-
codeClientRef.current = createCodeClient({
|
|
87
|
-
sessionToken,
|
|
88
|
-
refreshToken,
|
|
89
|
-
getSession,
|
|
90
|
-
onMessageUpdate: (message) => {
|
|
91
|
-
setCodeMessage(message);
|
|
92
|
-
onCodeMessageUpdate?.(message);
|
|
93
|
-
},
|
|
94
|
-
onEdit: (edit) => {
|
|
95
|
-
console.log('[BuilderProvider] Code edit:', edit);
|
|
96
|
-
onCodeEdit?.(edit);
|
|
97
|
-
},
|
|
98
|
-
onError: (error) => {
|
|
99
|
-
console.error('[BuilderProvider] Code error:', error);
|
|
100
|
-
onError?.(error);
|
|
101
|
-
},
|
|
102
|
-
});
|
|
103
|
-
}, [config]);
|
|
104
|
-
// Initialize visual builder (createBuilder from core) - AFTER codeClient
|
|
105
|
-
// We need loadBundle to be stable, so we use a ref
|
|
106
|
-
const loadBundleRef = useRef(null);
|
|
107
|
-
useEffect(() => {
|
|
108
|
-
const { sessionToken, refreshToken, getSession } = config;
|
|
109
|
-
// Create the visual builder instance, sharing our CodeClient
|
|
110
|
-
builderRef.current = createBuilder({
|
|
111
|
-
sessionToken,
|
|
112
|
-
refreshToken,
|
|
113
|
-
getSession,
|
|
114
|
-
codeClient: codeClientRef.current ?? undefined,
|
|
115
|
-
// Called IMMEDIATELY when exit starts, before any async work
|
|
116
|
-
// This prevents race condition where streaming events trigger temp bundle reload
|
|
117
|
-
onExitStart: () => {
|
|
118
|
-
isExitingRef.current = true;
|
|
119
|
-
isBuilderActiveRef.current = false; // Mark inactive immediately
|
|
120
|
-
console.log('[BuilderProvider] Exit started, isExiting=true');
|
|
121
|
-
// Abort any active streaming to prevent late bundle_ready events
|
|
122
|
-
codeClientRef.current?.abort();
|
|
123
|
-
// Reset message state to clear bundleReady flag
|
|
124
|
-
codeClientRef.current?.resetMessage();
|
|
125
|
-
},
|
|
126
|
-
// Pass loadBundle so exit() can restore permanent bundle
|
|
127
|
-
loadBundle: (bundleUrl) => {
|
|
128
|
-
// isExitingRef already set by onExitStart, but keep as backup
|
|
129
|
-
isExitingRef.current = true;
|
|
130
|
-
console.log('[BuilderProvider] Exit loadBundle called, isExiting=true');
|
|
131
|
-
if (loadBundleRef.current) {
|
|
132
|
-
return loadBundleRef.current(bundleUrl);
|
|
133
|
-
}
|
|
134
|
-
return Promise.resolve();
|
|
135
|
-
},
|
|
136
|
-
onEnter: () => {
|
|
137
|
-
console.log('[BuilderProvider] Visual builder entered');
|
|
138
|
-
isExitingRef.current = false;
|
|
139
|
-
isBuilderActiveRef.current = true;
|
|
140
|
-
setIsBuilderActive(true);
|
|
141
|
-
},
|
|
142
|
-
onExit: () => {
|
|
143
|
-
console.log('[BuilderProvider] Visual builder exited');
|
|
144
|
-
setIsBuilderActive(false);
|
|
145
|
-
// NOTE: Do NOT reset isExitingRef here - it must stay true until
|
|
146
|
-
// React finishes processing state updates. It gets reset in onEnter.
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
return () => {
|
|
150
|
-
builderRef.current?.destroy();
|
|
151
|
-
};
|
|
152
|
-
}, [config]);
|
|
153
|
-
// Initialize builder client for visual builder operations (preview, exit)
|
|
154
|
-
useEffect(() => {
|
|
155
|
-
const { sessionToken, refreshToken, getSession, onError } = config;
|
|
156
|
-
builderClientRef.current = createBuilderClient({
|
|
157
|
-
sessionToken,
|
|
158
|
-
refreshToken,
|
|
159
|
-
getSession,
|
|
160
|
-
onError: (error) => {
|
|
161
|
-
console.error('[BuilderProvider] Error:', error);
|
|
162
|
-
onError?.(error);
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
}, [config]);
|
|
166
|
-
// Enter builder mode - activates the visual builder UI
|
|
167
|
-
const enterBuilder = useCallback(() => {
|
|
168
|
-
if (builderRef.current && !builderRef.current.isActive()) {
|
|
169
|
-
builderRef.current.enter();
|
|
170
|
-
}
|
|
171
|
-
}, []);
|
|
172
|
-
// Exit builder mode - deactivates the visual builder UI
|
|
173
|
-
const exitBuilder = useCallback(() => {
|
|
174
|
-
if (builderRef.current && builderRef.current.isActive()) {
|
|
175
|
-
builderRef.current.exit();
|
|
176
|
-
}
|
|
177
|
-
}, []);
|
|
178
|
-
/**
|
|
179
|
-
* Get auth token for bundle requests (with caching for performance)
|
|
180
|
-
* Returns cached token if available, otherwise fetches and caches.
|
|
181
|
-
* Reuses in-flight promise to avoid duplicate requests.
|
|
182
|
-
*/
|
|
183
|
-
const getAuthToken = useCallback(async () => {
|
|
184
|
-
const { sessionToken, getSession } = config;
|
|
185
|
-
// Fast path: static token from config
|
|
186
|
-
if (sessionToken) {
|
|
187
|
-
return sessionToken;
|
|
188
|
-
}
|
|
189
|
-
// Fast path: return cached token
|
|
190
|
-
if (cachedTokenRef.current) {
|
|
191
|
-
return cachedTokenRef.current;
|
|
192
|
-
}
|
|
193
|
-
// Reuse in-flight promise if one exists (prevents duplicate getSession calls)
|
|
194
|
-
if (tokenPromiseRef.current) {
|
|
195
|
-
return tokenPromiseRef.current;
|
|
196
|
-
}
|
|
197
|
-
if (getSession) {
|
|
198
|
-
// Create and cache the promise
|
|
199
|
-
tokenPromiseRef.current = (async () => {
|
|
200
|
-
try {
|
|
201
|
-
const session = await getSession();
|
|
202
|
-
cachedTokenRef.current = session.token; // Cache for future use
|
|
203
|
-
return session.token;
|
|
204
|
-
}
|
|
205
|
-
catch (e) {
|
|
206
|
-
console.warn('[BuilderProvider] Failed to get session for bundle auth:', e);
|
|
207
|
-
return null;
|
|
208
|
-
}
|
|
209
|
-
finally {
|
|
210
|
-
tokenPromiseRef.current = null; // Clear in-flight promise
|
|
211
|
-
}
|
|
212
|
-
})();
|
|
213
|
-
return tokenPromiseRef.current;
|
|
214
|
-
}
|
|
215
|
-
return null;
|
|
216
|
-
}, [config]);
|
|
217
|
-
// Initialize bundle loader from core (lazy initialization)
|
|
218
|
-
const getBundleLoader = useCallback(() => {
|
|
219
|
-
if (!bundleLoaderRef.current) {
|
|
220
|
-
bundleLoaderRef.current = createBundleLoader({
|
|
221
|
-
getAuthToken,
|
|
222
|
-
includeCredentials: true, // Uses same-origin session cookies
|
|
223
|
-
onError: (error) => {
|
|
224
|
-
console.error('[BuilderProvider] Bundle load failed:', error);
|
|
225
|
-
configCallbacksRef.current.onError?.(error);
|
|
226
|
-
},
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
return bundleLoaderRef.current;
|
|
230
|
-
}, [getAuthToken]);
|
|
231
|
-
/**
|
|
232
|
-
* Load a bundle via dynamic import()
|
|
233
|
-
* Uses createBundleLoader from dynim-core
|
|
234
|
-
*/
|
|
235
|
-
const loadBundle = useCallback(async (bundleUrl) => {
|
|
236
|
-
const loader = getBundleLoader();
|
|
237
|
-
console.log('[BuilderProvider] loadBundle called:', bundleUrl, 'isExiting=', isExitingRef.current);
|
|
238
|
-
if (loader.isLoading()) {
|
|
239
|
-
console.warn('[BuilderProvider] Already loading a bundle, skipping');
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
242
|
-
setIsBundleLoading(true);
|
|
243
|
-
setBundleError(null); // Clear any previous error
|
|
244
|
-
try {
|
|
245
|
-
const { App, cleanup } = await loader.load(bundleUrl);
|
|
246
|
-
console.log('[BuilderProvider] Bundle imported successfully');
|
|
247
|
-
// Store the component (cleanup is called automatically by the loader)
|
|
248
|
-
setTenantApp(() => App);
|
|
249
|
-
setIsBundleLoaded(true);
|
|
250
|
-
// Cleanup blob URL immediately after import
|
|
251
|
-
cleanup();
|
|
252
|
-
}
|
|
253
|
-
catch (error) {
|
|
254
|
-
// 404 is expected when no permanent bundle exists yet (new project, no saved edits)
|
|
255
|
-
// Don't treat it as an error - unload current bundle to show base application (children)
|
|
256
|
-
if (error instanceof BundleNotFoundError) {
|
|
257
|
-
console.log('[BuilderProvider] Bundle not found (404) - unloading to show base application');
|
|
258
|
-
setTenantApp(null);
|
|
259
|
-
setIsBundleLoaded(false);
|
|
260
|
-
return;
|
|
261
|
-
}
|
|
262
|
-
// Note: Error already logged by loader.onError callback
|
|
263
|
-
setBundleError(error);
|
|
264
|
-
// Don't throw - let the app continue with children
|
|
265
|
-
}
|
|
266
|
-
finally {
|
|
267
|
-
setIsBundleLoading(false);
|
|
268
|
-
}
|
|
269
|
-
}, [getBundleLoader]);
|
|
270
|
-
// Keep the ref updated so the builder can call loadBundle
|
|
271
|
-
useEffect(() => {
|
|
272
|
-
loadBundleRef.current = loadBundle;
|
|
273
|
-
}, [loadBundle]);
|
|
274
|
-
// PERF: Eagerly prefetch auth token on mount (runs in parallel with render)
|
|
275
|
-
// This ensures the token is ready by the time we need to fetch the bundle
|
|
276
|
-
useEffect(() => {
|
|
277
|
-
const { sessionToken, getSession } = config;
|
|
278
|
-
if (!sessionToken && getSession) {
|
|
279
|
-
// Start fetching token immediately - don't await, just fire and forget
|
|
280
|
-
// The result will be cached in cachedTokenRef for when loadBundle needs it
|
|
281
|
-
getAuthToken();
|
|
282
|
-
}
|
|
283
|
-
}, []); // Empty deps - only run once on mount
|
|
284
|
-
// Auto-load saved bundle on mount (if authenticated)
|
|
285
|
-
// This ensures the permanent bundle is displayed immediately when the app loads
|
|
286
|
-
useEffect(() => {
|
|
287
|
-
if (hasAttemptedInitialLoadRef.current)
|
|
288
|
-
return;
|
|
289
|
-
const { sessionToken, getSession } = config;
|
|
290
|
-
// Need auth to load bundle - if no auth configured, just render children
|
|
291
|
-
if (!sessionToken && !getSession) {
|
|
292
|
-
console.log('[BuilderProvider] No auth configured, skipping initial bundle load');
|
|
293
|
-
setIsInitialLoadComplete(true); // Mark complete so children render
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
|
-
hasAttemptedInitialLoadRef.current = true;
|
|
297
|
-
const doInitialLoad = async () => {
|
|
298
|
-
console.log('[BuilderProvider] Auto-loading saved bundle on mount');
|
|
299
|
-
const bundleUrl = '/api/code/bundle';
|
|
300
|
-
if (loadBundleRef.current) {
|
|
301
|
-
try {
|
|
302
|
-
await loadBundleRef.current(bundleUrl);
|
|
303
|
-
}
|
|
304
|
-
catch (error) {
|
|
305
|
-
// Errors are already handled in loadBundle (sets bundleError state)
|
|
306
|
-
// 404 means no saved bundle - that's fine, children will render
|
|
307
|
-
console.log('[BuilderProvider] Initial bundle load completed (may be 404 if no saved bundle)');
|
|
308
|
-
}
|
|
309
|
-
finally {
|
|
310
|
-
setIsInitialLoadComplete(true); // Mark complete regardless of outcome
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
else {
|
|
314
|
-
setIsInitialLoadComplete(true); // No loader available, show children
|
|
315
|
-
}
|
|
316
|
-
};
|
|
317
|
-
doInitialLoad();
|
|
318
|
-
}, [config.sessionToken, config.getSession]);
|
|
319
|
-
const loadProjectBundle = useCallback(async (projectId, forceReload = false) => {
|
|
320
|
-
let bundleUrl = `/api/bundles/preview?project_id=${projectId}`;
|
|
321
|
-
// Cache bust to force reload
|
|
322
|
-
if (forceReload) {
|
|
323
|
-
bundleUrl += `&_t=${Date.now()}`;
|
|
324
|
-
}
|
|
325
|
-
await loadBundle(bundleUrl);
|
|
326
|
-
}, [loadBundle]);
|
|
327
|
-
// Load saved (permanent) bundle - used for initial load, after save, and after abandon
|
|
328
|
-
const loadSavedBundle = useCallback(async (_projectId) => {
|
|
329
|
-
const bundleUrl = '/api/code/bundle';
|
|
330
|
-
console.log('[BuilderProvider] Loading saved (perm) bundle via:', bundleUrl);
|
|
331
|
-
await loadBundle(bundleUrl);
|
|
332
|
-
}, [loadBundle]);
|
|
333
|
-
// Load temp bundle - used when AI finishes editing to preview changes
|
|
334
|
-
const loadTempBundle = useCallback(async (_projectId) => {
|
|
335
|
-
const bundleUrl = `/api/code/bundle?temp=true&_t=${Date.now()}`;
|
|
336
|
-
console.log('[BuilderProvider] Loading temp bundle via:', bundleUrl);
|
|
337
|
-
await loadBundle(bundleUrl);
|
|
338
|
-
}, [loadBundle]);
|
|
339
|
-
// Watch for bundleReady and decide if we should queue a load
|
|
340
|
-
// Uses refs for instant checks (no stale closure issues)
|
|
341
|
-
useEffect(() => {
|
|
342
|
-
console.log('[BuilderProvider] bundleReady effect:', {
|
|
343
|
-
bundleReady: codeMessage.bundleReady,
|
|
344
|
-
projectId: codeMessage.projectId,
|
|
345
|
-
isBuilderActiveRef: isBuilderActiveRef.current,
|
|
346
|
-
isExitingRef: isExitingRef.current,
|
|
347
|
-
});
|
|
348
|
-
if (codeMessage.bundleReady && codeMessage.projectId) {
|
|
349
|
-
// Check refs for current state (not stale closure values)
|
|
350
|
-
if (isBuilderActiveRef.current && !isExitingRef.current) {
|
|
351
|
-
console.log('[BuilderProvider] >>> QUEUEING LOAD:', codeMessage.projectId);
|
|
352
|
-
pendingBundleProjectIdRef.current = codeMessage.projectId;
|
|
353
|
-
setBundleLoadSignal(s => s + 1);
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
console.log('[BuilderProvider] >>> BLOCKING LOAD (exiting or inactive)');
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}, [codeMessage.bundleReady, codeMessage.projectId]);
|
|
360
|
-
// Effect that actually loads - ONLY triggered by the signal counter
|
|
361
|
-
// Uses refs for everything so it has ONLY ONE dependency: bundleLoadSignal
|
|
362
|
-
useEffect(() => {
|
|
363
|
-
console.log('[BuilderProvider] Load signal effect, signal=', bundleLoadSignal);
|
|
364
|
-
if (bundleLoadSignal === 0)
|
|
365
|
-
return; // Skip initial render
|
|
366
|
-
const projectId = pendingBundleProjectIdRef.current;
|
|
367
|
-
console.log('[BuilderProvider] Load signal triggered, projectId=', projectId, 'isExiting=', isExitingRef.current);
|
|
368
|
-
if (!projectId)
|
|
369
|
-
return;
|
|
370
|
-
// Final safety check before loading
|
|
371
|
-
if (isExitingRef.current) {
|
|
372
|
-
console.log('[BuilderProvider] >>> BLOCKED BY FINAL CHECK - exit in progress');
|
|
373
|
-
pendingBundleProjectIdRef.current = null;
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
// Use refs to avoid dependency on config/loadTempBundle
|
|
377
|
-
// temp=true requests temp bundle (falls back to perm if no temp exists)
|
|
378
|
-
const bundleUrl = `/api/code/bundle?temp=true&_t=${Date.now()}`;
|
|
379
|
-
console.log('[BuilderProvider] >>> LOADING TEMP BUNDLE:', bundleUrl);
|
|
380
|
-
if (loadBundleRef.current) {
|
|
381
|
-
loadBundleRef.current(bundleUrl);
|
|
382
|
-
}
|
|
383
|
-
pendingBundleProjectIdRef.current = null;
|
|
384
|
-
}, [bundleLoadSignal]); // ONLY depends on the signal!
|
|
385
|
-
// Unload bundle - show children again
|
|
386
|
-
const unloadBundle = useCallback(() => {
|
|
387
|
-
setTenantApp(null);
|
|
388
|
-
setIsBundleLoaded(false);
|
|
389
|
-
console.log('[BuilderProvider] Bundle unloaded');
|
|
390
|
-
}, []);
|
|
391
|
-
// Code client methods
|
|
392
|
-
const sendCode = useCallback(async (query) => {
|
|
393
|
-
await codeClientRef.current?.sendCode(query);
|
|
394
|
-
}, []);
|
|
395
|
-
const saveCode = useCallback(async () => {
|
|
396
|
-
// 1. Persist changes to backend (promotes temp to saved)
|
|
397
|
-
await codeClientRef.current?.saveCode();
|
|
398
|
-
// 2. Load the newly saved bundle
|
|
399
|
-
if (codeMessage.projectId) {
|
|
400
|
-
await loadSavedBundle(codeMessage.projectId);
|
|
401
|
-
}
|
|
402
|
-
}, [loadSavedBundle, codeMessage.projectId]);
|
|
403
|
-
const abandonCode = useCallback(async () => {
|
|
404
|
-
// 1. Discard temp changes on server
|
|
405
|
-
await codeClientRef.current?.abandonCode();
|
|
406
|
-
// 2. Revert to saved bundle
|
|
407
|
-
if (codeMessage.projectId) {
|
|
408
|
-
await loadSavedBundle(codeMessage.projectId);
|
|
409
|
-
}
|
|
410
|
-
}, [loadSavedBundle, codeMessage.projectId]);
|
|
411
|
-
const warmCache = useCallback(async () => {
|
|
412
|
-
await codeClientRef.current?.warmCache();
|
|
413
|
-
}, []);
|
|
414
|
-
const resetCodeMessage = useCallback(() => {
|
|
415
|
-
codeClientRef.current?.resetMessage();
|
|
416
|
-
}, []);
|
|
417
|
-
const contextValue = useMemo(() => ({
|
|
418
|
-
enterBuilder,
|
|
419
|
-
exitBuilder,
|
|
420
|
-
isBuilderActive,
|
|
421
|
-
getBuilder: () => builderRef.current,
|
|
422
|
-
// Code client methods
|
|
423
|
-
sendCode,
|
|
424
|
-
saveCode,
|
|
425
|
-
abandonCode,
|
|
426
|
-
warmCache,
|
|
427
|
-
codeMessage,
|
|
428
|
-
resetCodeMessage,
|
|
429
|
-
codeEdits: codeMessage.edits,
|
|
430
|
-
// Bundle methods
|
|
431
|
-
loadBundle,
|
|
432
|
-
loadProjectBundle,
|
|
433
|
-
loadSavedBundle,
|
|
434
|
-
loadTempBundle,
|
|
435
|
-
isBundleLoaded,
|
|
436
|
-
isBundleLoading,
|
|
437
|
-
unloadBundle,
|
|
438
|
-
}), [
|
|
439
|
-
enterBuilder,
|
|
440
|
-
exitBuilder,
|
|
441
|
-
isBuilderActive,
|
|
442
|
-
sendCode,
|
|
443
|
-
saveCode,
|
|
444
|
-
abandonCode,
|
|
445
|
-
warmCache,
|
|
446
|
-
codeMessage,
|
|
447
|
-
resetCodeMessage,
|
|
448
|
-
loadBundle,
|
|
449
|
-
loadProjectBundle,
|
|
450
|
-
loadSavedBundle,
|
|
451
|
-
loadTempBundle,
|
|
452
|
-
isBundleLoaded,
|
|
453
|
-
isBundleLoading,
|
|
454
|
-
unloadBundle,
|
|
455
|
-
]);
|
|
456
|
-
// Notify parent when ready (only once on mount)
|
|
457
|
-
const hasNotifiedRef = useRef(false);
|
|
458
|
-
useEffect(() => {
|
|
459
|
-
if (!hasNotifiedRef.current && onBuilderReady) {
|
|
460
|
-
hasNotifiedRef.current = true;
|
|
461
|
-
onBuilderReady(contextValue);
|
|
462
|
-
}
|
|
463
|
-
}, [contextValue, onBuilderReady]);
|
|
464
|
-
// Render the appropriate content based on state
|
|
465
|
-
const renderContent = () => {
|
|
466
|
-
// Initial load in progress - show nothing to prevent flash of children
|
|
467
|
-
// This only applies to the first load; subsequent loads show current content
|
|
468
|
-
if (!isInitialLoadComplete) {
|
|
469
|
-
return null;
|
|
470
|
-
}
|
|
471
|
-
// Loading state (after initial load) - keep showing current content
|
|
472
|
-
if (isBundleLoading) {
|
|
473
|
-
// If we have TenantApp, keep showing it during reload
|
|
474
|
-
// Otherwise show children (this is a subsequent load, not initial)
|
|
475
|
-
return TenantApp ? _jsx(TenantApp, {}) : children;
|
|
476
|
-
}
|
|
477
|
-
// Error state - show error banner with children
|
|
478
|
-
if (bundleError) {
|
|
479
|
-
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
|
|
480
|
-
padding: 12,
|
|
481
|
-
background: '#fef3c7',
|
|
482
|
-
borderBottom: '1px solid #fcd34d',
|
|
483
|
-
fontSize: 13,
|
|
484
|
-
color: '#92400e',
|
|
485
|
-
}, children: ["Bundle error: ", bundleError.message] }), children] }));
|
|
486
|
-
}
|
|
487
|
-
// Bundle loaded - render tenant app
|
|
488
|
-
if (TenantApp) {
|
|
489
|
-
return _jsx(TenantApp, {});
|
|
490
|
-
}
|
|
491
|
-
// Default - render children
|
|
492
|
-
return children;
|
|
493
|
-
};
|
|
494
|
-
return (_jsx(BuilderContext.Provider, { value: contextValue, children: renderContent() }));
|
|
495
|
-
}
|
|
496
|
-
/**
|
|
497
|
-
* Hook to access builder functionality
|
|
498
|
-
*/
|
|
499
|
-
export function useBuilder() {
|
|
500
|
-
const context = useContext(BuilderContext);
|
|
501
|
-
if (!context) {
|
|
502
|
-
throw new Error('useBuilder must be used within a BuilderProvider');
|
|
503
|
-
}
|
|
504
|
-
return context;
|
|
505
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* React Context for sharing chatbot state across components
|
|
3
|
-
*/
|
|
4
|
-
import { type ReactNode } from 'react';
|
|
5
|
-
import { type UseChatbotConfig, type UseChatbotReturn } from './useChatbot';
|
|
6
|
-
export interface ChatProviderProps extends UseChatbotConfig {
|
|
7
|
-
children: ReactNode;
|
|
8
|
-
}
|
|
9
|
-
export declare function ChatProvider({ children, ...config }: ChatProviderProps): JSX.Element;
|
|
10
|
-
export declare function useChatContext(): UseChatbotReturn;
|
|
11
|
-
//# sourceMappingURL=ChatContext.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../src/builder/ChatContext.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EAAc,KAAK,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAIxF,MAAM,WAAW,iBAAkB,SAAQ,gBAAgB;IACzD,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,GAAG,MAAM,EAAE,EAAE,iBAAiB,GAAG,GAAG,CAAC,OAAO,CAIpF;AAED,wBAAgB,cAAc,IAAI,gBAAgB,CAMjD"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* React Context for sharing chatbot state across components
|
|
4
|
-
*/
|
|
5
|
-
import { createContext, useContext } from 'react';
|
|
6
|
-
import { useChatbot } from './useChatbot';
|
|
7
|
-
const ChatContext = createContext(null);
|
|
8
|
-
export function ChatProvider({ children, ...config }) {
|
|
9
|
-
const chatbot = useChatbot(config);
|
|
10
|
-
return _jsx(ChatContext.Provider, { value: chatbot, children: children });
|
|
11
|
-
}
|
|
12
|
-
export function useChatContext() {
|
|
13
|
-
const context = useContext(ChatContext);
|
|
14
|
-
if (!context) {
|
|
15
|
-
throw new Error('useChatContext must be used within a ChatProvider');
|
|
16
|
-
}
|
|
17
|
-
return context;
|
|
18
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Chat input component
|
|
3
|
-
*/
|
|
4
|
-
export interface ChatInputProps {
|
|
5
|
-
className?: string;
|
|
6
|
-
placeholder?: string;
|
|
7
|
-
disabled?: boolean;
|
|
8
|
-
onSubmit?: (text: string) => void;
|
|
9
|
-
}
|
|
10
|
-
export declare function ChatInput({ className, placeholder, disabled, onSubmit, }: ChatInputProps): JSX.Element;
|
|
11
|
-
//# sourceMappingURL=ChatInput.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../src/builder/ChatInput.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACnC;AAED,wBAAgB,SAAS,CAAC,EACxB,SAAc,EACd,WAAiC,EACjC,QAAgB,EAChB,QAAQ,GACT,EAAE,cAAc,GAAG,GAAG,CAAC,OAAO,CAuC9B"}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
/**
|
|
3
|
-
* Chat input component
|
|
4
|
-
*/
|
|
5
|
-
import { useState, useCallback } from 'react';
|
|
6
|
-
import { useChatContext } from './ChatContext';
|
|
7
|
-
export function ChatInput({ className = '', placeholder = 'Type a message...', disabled = false, onSubmit, }) {
|
|
8
|
-
const { sendMessage, isLoading } = useChatContext();
|
|
9
|
-
const [value, setValue] = useState('');
|
|
10
|
-
const handleSubmit = useCallback((e) => {
|
|
11
|
-
e.preventDefault();
|
|
12
|
-
const text = value.trim();
|
|
13
|
-
if (!text || isLoading)
|
|
14
|
-
return;
|
|
15
|
-
setValue('');
|
|
16
|
-
onSubmit?.(text);
|
|
17
|
-
sendMessage(text);
|
|
18
|
-
}, [value, isLoading, sendMessage, onSubmit]);
|
|
19
|
-
return (_jsxs("form", { className: `chatbot-form ${className}`, onSubmit: handleSubmit, children: [_jsx("input", { type: "text", className: "chatbot-input", placeholder: placeholder, value: value, onChange: (e) => setValue(e.target.value), disabled: disabled || isLoading, autoComplete: "off" }), _jsx("button", { type: "submit", className: "chatbot-send", disabled: disabled || isLoading || !value.trim(), children: _jsx("svg", { viewBox: "0 0 24 24", width: "20", height: "20", fill: "currentColor", children: _jsx("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) }) })] }));
|
|
20
|
-
}
|