dynim-react 1.0.57 → 1.0.59

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.
@@ -1,73 +1,42 @@
1
1
  /**
2
- * DynimProvider - Single provider for all Dynim functionality
2
+ * DynimProvider - React wrapper around DynimManager
3
3
  *
4
- * Handles:
5
- * - Loading tenant bundles
6
- * - Visual builder UI (when user has edit permissions)
7
- * - Shared context for bundles to access React, packages, etc.
4
+ * Framework-specific responsibilities:
5
+ * - Shared context setup (React, ReactDOM, packages)
6
+ * - React context provider + hook
7
+ * - Rendering TenantApp or children
8
+ * - Theme <style> injection
8
9
  */
9
10
  import { type ReactNode } from 'react';
10
- import type { CodeMessage, Checkpoint, RestoreResult } from 'dynim-core';
11
+ import { type Checkpoint, type RestoreResult, type CodeMessage } from 'dynim-core';
11
12
  export interface DynimConfig {
12
- /** Function to fetch a session token for authentication */
13
13
  getSession?: () => Promise<{
14
14
  token: string;
15
15
  refreshToken?: string;
16
16
  }>;
17
- /** Called when an error occurs */
18
17
  onError?: (error: Event | Error) => void;
19
- /** NPM packages to expose to bundles (e.g., { 'react-router-dom': ReactRouterDOM }) */
20
18
  packages?: Record<string, unknown>;
21
- /** Custom hooks to expose to bundles */
22
19
  hooks?: Record<string, unknown>;
23
- /** React contexts to expose to bundles */
24
20
  contexts?: Record<string, unknown>;
25
21
  }
26
22
  export interface DynimContextValue {
27
- /** Enter edit mode (shows builder UI) */
28
23
  enterBuilder: () => void;
29
- /** Exit edit mode */
30
24
  exitBuilder: () => void;
31
- /** Whether builder/edit mode is active */
32
25
  isEditing: boolean;
33
- /** Send a code edit request to AI */
34
26
  sendCode: (query: string) => Promise<void>;
35
- /** Save current edits */
36
27
  saveCode: () => Promise<void>;
37
- /** Abandon/discard current edits */
38
28
  abandonCode: () => Promise<void>;
39
- /** Get active checkpoints for the project */
40
29
  getCheckpoints: () => Promise<Checkpoint[]>;
41
- /** Restore to a specific checkpoint (reloads bundle on success) */
42
30
  restoreCheckpoint: (checkpointId: string) => Promise<RestoreResult>;
43
- /** Whether a checkpoint restore is in progress */
44
31
  isRestoring: boolean;
45
- /** Current code message state (thinking, edits, etc.) */
46
32
  codeMessage: CodeMessage;
47
- /** Whether a bundle is currently loaded */
48
33
  isBundleLoaded: boolean;
49
- /** Whether bundle is loading */
50
34
  isBundleLoading: boolean;
51
35
  }
52
36
  export interface DynimProviderProps {
53
37
  children: ReactNode;
54
38
  config?: DynimConfig;
55
39
  }
56
- /**
57
- * DynimProvider - Wrap your app with this to enable Dynim functionality
58
- *
59
- * @example
60
- * ```tsx
61
- * import { DynimProvider } from 'dynim-react';
62
- *
63
- * <DynimProvider>
64
- * <App />
65
- * </DynimProvider>
66
- * ```
67
- */
68
40
  export declare function DynimProvider({ children, config, }: DynimProviderProps): JSX.Element;
69
- /**
70
- * Hook to access Dynim functionality
71
- */
72
41
  export declare function useDynim(): DynimContextValue;
73
42
  //# sourceMappingURL=DynimProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DynimProvider.d.ts","sourceRoot":"","sources":["../src/DynimProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAc,EAQZ,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAWf,OAAO,KAAK,EAKV,WAAW,EACX,UAAU,EACV,aAAa,EAGd,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,WAAW;IAC1B,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,kCAAkC;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC;IACzC,uFAAuF;IACvF,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,wCAAwC;IACxC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,yCAAyC;IACzC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,qBAAqB;IACrB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,0CAA0C;IAC1C,SAAS,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,yBAAyB;IACzB,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,oCAAoC;IACpC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,6CAA6C;IAC7C,cAAc,EAAE,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAC5C,mEAAmE;IACnE,iBAAiB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,kDAAkD;IAClD,WAAW,EAAE,OAAO,CAAC;IACrB,yDAAyD;IACzD,WAAW,EAAE,WAAW,CAAC;IACzB,2CAA2C;IAC3C,cAAc,EAAE,OAAO,CAAC;IACxB,gCAAgC;IAChC,eAAe,EAAE,OAAO,CAAC;CAC1B;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,MAAW,GACZ,EAAE,kBAAkB,GAAG,GAAG,CAAC,OAAO,CAkdlC;AAED;;GAEG;AACH,wBAAgB,QAAQ,IAAI,iBAAiB,CAM5C"}
1
+ {"version":3,"file":"DynimProvider.d.ts","sourceRoot":"","sources":["../src/DynimProvider.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAc,EAMZ,KAAK,SAAS,EAEf,MAAM,OAAO,CAAC;AAEf,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,cAAc,EAAE,MAAM,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAC5C,iBAAiB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACpE,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,OAAO,CAAC;IACxB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAID,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,EAC5B,QAAQ,EACR,MAAW,GACZ,EAAE,kBAAkB,GAAG,GAAG,CAAC,OAAO,CAwGlC;AAED,wBAAgB,QAAQ,IAAI,iBAAiB,CAM5C"}
@@ -1,76 +1,36 @@
1
1
  import { jsxs as _jsxs, Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  /**
3
- * DynimProvider - Single provider for all Dynim functionality
3
+ * DynimProvider - React wrapper around DynimManager
4
4
  *
5
- * Handles:
6
- * - Loading tenant bundles
7
- * - Visual builder UI (when user has edit permissions)
8
- * - Shared context for bundles to access React, packages, etc.
5
+ * Framework-specific responsibilities:
6
+ * - Shared context setup (React, ReactDOM, packages)
7
+ * - React context provider + hook
8
+ * - Rendering TenantApp or children
9
+ * - Theme <style> injection
9
10
  */
10
- import React, { createContext, useContext, useEffect, useRef, useCallback, useMemo, useState, } from 'react';
11
+ import React, { createContext, useContext, useEffect, useMemo, useState, } from 'react';
11
12
  import ReactDOM from 'react-dom';
12
- import { createBuilderClient, createBuilder, createCodeClient, createBundleLoader, createCSSLoader, BundleNotFoundError, BundleAuthError, } from 'dynim-core';
13
+ import { createDynimManager, } from 'dynim-core';
13
14
  import { createSharedContext, updateSharedContext, isSharedContextReady } from './inference/sharedContext';
14
- import { generateThemeCSS } from './theme';
15
15
  const DynimContext = createContext(null);
16
- /**
17
- * DynimProvider - Wrap your app with this to enable Dynim functionality
18
- *
19
- * @example
20
- * ```tsx
21
- * import { DynimProvider } from 'dynim-react';
22
- *
23
- * <DynimProvider>
24
- * <App />
25
- * </DynimProvider>
26
- * ```
27
- */
28
16
  export function DynimProvider({ children, config = {}, }) {
29
- const builderClientRef = useRef(null);
30
- const builderRef = useRef(null);
31
- const codeClientRef = useRef(null);
32
- const bundleLoaderRef = useRef(null);
33
- const cssLoaderRef = useRef(null);
34
- // Track when we're exiting to prevent auto-load from re-triggering
35
- const isExitingRef = useRef(false);
36
- const hasAttemptedInitialLoadRef = useRef(false);
37
- const isBuilderActiveRef = useRef(false);
38
- const [isEditing, setIsEditing] = useState(false);
39
- const [isBundleLoaded, setIsBundleLoaded] = useState(false);
40
- const [isBundleLoading, setIsBundleLoading] = useState(false);
41
- const [isInitialLoadComplete, setIsInitialLoadComplete] = useState(false);
42
- const [TenantApp, setTenantApp] = useState(null);
43
- const [bundleError, setBundleError] = useState(null);
44
- const [isRestoring, setIsRestoring] = useState(false);
45
- const [codeMessage, setCodeMessage] = useState({
46
- thinking: '',
47
- text: '',
48
- edits: [],
49
- status: 'idle',
50
- bundleReady: false,
51
- bundleError: undefined,
52
- });
53
- // Cached auth token
54
- const cachedTokenRef = useRef(null);
55
- const tokenPromiseRef = useRef(null);
56
- // Bundle load signal
57
- const [bundleLoadSignal, setBundleLoadSignal] = useState(0);
58
- const pendingBundleProjectIdRef = useRef(null);
59
- // Theme state (fetched from API per project)
60
- const [theme, setTheme] = useState(null);
61
- const hasAttemptedThemeLoadRef = useRef(false);
62
- // Keep config in ref to avoid re-triggering effects on every render
63
- const configRef = useRef(config);
64
- configRef.current = config;
65
- // Set up shared context for bundles (only once on mount)
17
+ // Create manager once (stable across renders)
18
+ const manager = useMemo(() => createDynimManager({
19
+ getSession: config.getSession,
20
+ onError: config.onError,
21
+ }),
22
+ // eslint-disable-next-line react-hooks/exhaustive-deps
23
+ []);
24
+ // Subscribe to manager state
25
+ const [state, setState] = useState(() => manager.getSnapshot());
66
26
  useEffect(() => {
67
- const { packages = {}, hooks = {}, contexts = {} } = configRef.current;
27
+ // Set up shared context for bundles (framework-specific)
28
+ const { packages = {}, hooks = {}, contexts = {} } = config;
68
29
  const sdkPackages = {
69
30
  'dynim-react': { DynimProvider, useDynim },
70
31
  ...packages,
71
32
  };
72
33
  if (!isSharedContextReady()) {
73
- // Create fresh shared context
74
34
  createSharedContext({
75
35
  React,
76
36
  ReactDOM,
@@ -80,359 +40,57 @@ export function DynimProvider({ children, config = {}, }) {
80
40
  });
81
41
  }
82
42
  else {
83
- // Update existing context with packages (handles HMR)
84
43
  updateSharedContext({
85
44
  packages: sdkPackages,
86
45
  hooks,
87
46
  contexts,
88
47
  });
89
48
  }
90
- }, []);
91
- // Get auth token (cached after first call)
92
- const getAuthToken = useCallback(async () => {
93
- const { getSession } = configRef.current;
94
- // Return cached token if available
95
- if (cachedTokenRef.current)
96
- return cachedTokenRef.current;
97
- // Reuse in-flight promise if one exists
98
- if (tokenPromiseRef.current)
99
- return tokenPromiseRef.current;
100
- if (getSession) {
101
- tokenPromiseRef.current = (async () => {
102
- try {
103
- const session = await getSession();
104
- cachedTokenRef.current = session.token;
105
- return session.token;
106
- }
107
- catch {
108
- return null;
109
- }
110
- finally {
111
- tokenPromiseRef.current = null;
112
- }
113
- })();
114
- return tokenPromiseRef.current;
115
- }
116
- return null;
117
- }, []);
118
- // Initialize code client (only once on mount)
119
- useEffect(() => {
120
- const { getSession, onError } = configRef.current;
121
- codeClientRef.current = createCodeClient({
122
- getSession,
123
- onMessageUpdate: setCodeMessage,
124
- onError: (error) => {
125
- console.error('[DynimProvider] Code error:', error);
126
- configRef.current.onError?.(error);
127
- },
128
- });
129
- }, []);
130
- // Load bundle ref
131
- const loadBundleRef = useRef(null);
132
- // Initialize builder (only once on mount)
133
- useEffect(() => {
134
- const { getSession } = configRef.current;
135
- builderRef.current = createBuilder({
136
- getSession,
137
- codeClient: codeClientRef.current ?? undefined,
138
- onExitStart: () => {
139
- isExitingRef.current = true;
140
- isBuilderActiveRef.current = false;
141
- codeClientRef.current?.abort();
142
- codeClientRef.current?.resetMessage();
143
- },
144
- loadBundle: (bundleUrl) => {
145
- // Note: isExitingRef is already set by onExitStart for the exit flow.
146
- // Don't set it here — this callback is also used by checkpoint restore
147
- // while the builder is still active.
148
- return loadBundleRef.current?.(bundleUrl) ?? Promise.resolve();
149
- },
150
- onEnter: () => {
151
- isExitingRef.current = false;
152
- isBuilderActiveRef.current = true;
153
- setIsEditing(true);
154
- },
155
- onExit: () => {
156
- setIsEditing(false);
157
- },
158
- });
49
+ // Subscribe to state changes
50
+ const unsubscribe = manager.subscribe(setState);
51
+ // Initialize manager
52
+ manager.init();
159
53
  return () => {
160
- builderRef.current?.destroy();
161
- // Clean up CSS link element on unmount
162
- cssLoaderRef.current?.unload();
163
- };
164
- }, []);
165
- // Initialize builder client (only once on mount)
166
- useEffect(() => {
167
- const { getSession } = configRef.current;
168
- builderClientRef.current = createBuilderClient({
169
- getSession,
170
- onError: (error) => {
171
- console.error('[DynimProvider] Error:', error);
172
- configRef.current.onError?.(error);
173
- },
174
- });
175
- }, []);
176
- // Get bundle loader
177
- const getBundleLoader = useCallback(() => {
178
- if (!bundleLoaderRef.current) {
179
- bundleLoaderRef.current = createBundleLoader({
180
- getAuthToken,
181
- includeCredentials: true,
182
- onError: (error) => {
183
- console.error('[DynimProvider] Bundle load failed:', error);
184
- configRef.current.onError?.(error);
185
- },
186
- });
187
- }
188
- return bundleLoaderRef.current;
189
- }, [getAuthToken]);
190
- // Get CSS loader
191
- const getCSSLoader = useCallback(() => {
192
- if (!cssLoaderRef.current) {
193
- cssLoaderRef.current = createCSSLoader({
194
- linkId: 'dynim-bundle-css',
195
- onError: (error) => {
196
- console.warn('[DynimProvider] CSS load failed:', error);
197
- },
198
- });
199
- }
200
- return cssLoaderRef.current;
201
- }, []);
202
- // Load bundle (JS + CSS)
203
- const loadBundle = useCallback(async (bundleUrl, loadCSS = true) => {
204
- const loader = getBundleLoader();
205
- if (loader.isLoading())
206
- return;
207
- setIsBundleLoading(true);
208
- setBundleError(null);
209
- try {
210
- const { App, cleanup } = await loader.load(bundleUrl);
211
- setTenantApp(() => App);
212
- setIsBundleLoaded(true);
213
- cleanup();
214
- // Load CSS alongside JS bundle
215
- if (loadCSS) {
216
- const cssLoader = getCSSLoader();
217
- const isTemp = bundleUrl.includes('temp=true');
218
- const cssUrl = isTemp ? '/api/code/css?temp=true' : '/api/code/css';
219
- if (isTemp) {
220
- // Temp/preview CSS - bust cache to get latest
221
- cssLoader.loadWithCacheBust(cssUrl);
222
- }
223
- else {
224
- // Saved CSS - let browser cache
225
- cssLoader.load(cssUrl);
226
- }
227
- }
228
- }
229
- catch (error) {
230
- // 404 = no bundle, 401 = not authenticated - show children
231
- if (error instanceof BundleNotFoundError || error instanceof BundleAuthError) {
232
- setTenantApp(null);
233
- setIsBundleLoaded(false);
234
- // Also unload any CSS
235
- getCSSLoader().unload();
236
- return;
237
- }
238
- setBundleError(error);
239
- }
240
- finally {
241
- setIsBundleLoading(false);
242
- }
243
- }, [getBundleLoader, getCSSLoader]);
244
- // Keep ref updated
245
- useEffect(() => {
246
- loadBundleRef.current = loadBundle;
247
- }, [loadBundle]);
248
- // Theme is fetched lazily when entering builder mode
249
- // This avoids making HTTP requests on mount that could trigger auth redirects
250
- const fetchTheme = useCallback(async () => {
251
- if (hasAttemptedThemeLoadRef.current)
252
- return;
253
- if (!configRef.current.getSession)
254
- return;
255
- hasAttemptedThemeLoadRef.current = true;
256
- try {
257
- const token = await getAuthToken();
258
- const headers = {};
259
- if (token) {
260
- headers['Authorization'] = `Bearer ${token}`;
261
- }
262
- const response = await fetch('/api/code/theme', {
263
- headers,
264
- credentials: 'include',
265
- });
266
- if (response.ok) {
267
- const data = await response.json();
268
- setTheme(data);
269
- }
270
- }
271
- catch (error) {
272
- // Theme fetch failed - use defaults (non-critical)
273
- console.warn('[DynimProvider] Failed to fetch theme:', error);
274
- }
275
- }, [getAuthToken]);
276
- // Auto-load saved bundle on mount if auth is available
277
- useEffect(() => {
278
- if (hasAttemptedInitialLoadRef.current)
279
- return;
280
- hasAttemptedInitialLoadRef.current = true;
281
- const { getSession } = configRef.current;
282
- // No auth configured = skip bundle load
283
- if (!getSession) {
284
- setIsInitialLoadComplete(true);
285
- return;
286
- }
287
- // Load bundle with the provided token
288
- const doInitialLoad = async () => {
289
- try {
290
- await loadBundleRef.current?.('/api/code/bundle');
291
- }
292
- catch (error) {
293
- // Errors handled in loadBundle (404 = no bundle, etc.)
294
- console.log('[DynimProvider] Initial bundle load:', error.message);
295
- }
296
- finally {
297
- setIsInitialLoadComplete(true);
298
- }
54
+ unsubscribe();
55
+ manager.destroy();
299
56
  };
300
- doInitialLoad();
301
- // Prefetch theme after 3s delay (non-blocking, so it's ready when builder opens)
302
- const themeTimeout = setTimeout(() => {
303
- fetchTheme();
304
- }, 3000);
305
- return () => clearTimeout(themeTimeout);
306
- }, [fetchTheme]);
307
- // Load saved bundle
308
- const loadSavedBundle = useCallback(async () => {
309
- // Note: CSS is loaded separately by saveCode/abandonCode with cache busting
310
- await loadBundle('/api/code/bundle', false);
311
- }, [loadBundle]);
312
- // Watch for bundleReady
313
- useEffect(() => {
314
- if (codeMessage.bundleReady && codeMessage.projectId) {
315
- if (isBuilderActiveRef.current && !isExitingRef.current) {
316
- pendingBundleProjectIdRef.current = codeMessage.projectId;
317
- setBundleLoadSignal(s => s + 1);
318
- }
319
- }
320
- }, [codeMessage.bundleReady, codeMessage.projectId]);
321
- // Load temp bundle on signal (JS + CSS with cache busting)
322
- useEffect(() => {
323
- if (bundleLoadSignal === 0)
324
- return;
325
- const projectId = pendingBundleProjectIdRef.current;
326
- if (!projectId || isExitingRef.current) {
327
- pendingBundleProjectIdRef.current = null;
328
- return;
329
- }
330
- // Load JS bundle with cache busting
331
- const bundleUrl = `/api/code/bundle?temp=true&_t=${Date.now()}`;
332
- loadBundleRef.current?.(bundleUrl, true); // true = also load CSS
333
- pendingBundleProjectIdRef.current = null;
334
- }, [bundleLoadSignal]);
335
- // Public methods
336
- const enterBuilder = useCallback(() => {
337
- if (builderRef.current && !builderRef.current.isActive()) {
338
- builderRef.current.enter();
339
- // Fetch theme lazily when entering builder
340
- fetchTheme();
341
- }
342
- }, [fetchTheme]);
343
- const exitBuilder = useCallback(() => {
344
- if (builderRef.current && builderRef.current.isActive()) {
345
- builderRef.current.exit();
346
- }
347
- }, []);
348
- const sendCode = useCallback(async (query) => {
349
- await codeClientRef.current?.sendCode(query);
350
- }, []);
351
- const saveCode = useCallback(async () => {
352
- await codeClientRef.current?.saveCode();
353
- await loadSavedBundle();
354
- // Force reload CSS with cache bust since saved CSS has changed
355
- getCSSLoader().loadWithCacheBust('/api/code/css');
356
- }, [loadSavedBundle, getCSSLoader]);
357
- const abandonCode = useCallback(async () => {
358
- await codeClientRef.current?.abandonCode();
359
- await loadSavedBundle();
360
- // Reload saved CSS (may have been showing temp CSS)
361
- getCSSLoader().loadWithCacheBust('/api/code/css');
362
- }, [loadSavedBundle, getCSSLoader]);
363
- const getCheckpoints = useCallback(async () => {
364
- return codeClientRef.current?.getCheckpoints() ?? [];
365
- }, []);
366
- const restoreCheckpoint = useCallback(async (checkpointId) => {
367
- if (!codeClientRef.current) {
368
- throw new Error('Code client not initialized');
369
- }
370
- setIsRestoring(true);
371
- try {
372
- const result = await codeClientRef.current.restoreCheckpoint(checkpointId);
373
- // Reload bundle + CSS on success (loadBundle handles CSS when loadCSS=true)
374
- if (result.bundle_ready) {
375
- const bundleUrl = `/api/code/bundle?temp=true&_t=${Date.now()}`;
376
- await loadBundleRef.current?.(bundleUrl, true);
377
- }
378
- return result;
379
- }
380
- finally {
381
- setIsRestoring(false);
382
- }
57
+ // eslint-disable-next-line react-hooks/exhaustive-deps
383
58
  }, []);
384
59
  const contextValue = useMemo(() => ({
385
- enterBuilder,
386
- exitBuilder,
387
- isEditing,
388
- sendCode,
389
- saveCode,
390
- abandonCode,
391
- getCheckpoints,
392
- restoreCheckpoint,
393
- isRestoring,
394
- codeMessage,
395
- isBundleLoaded,
396
- isBundleLoading,
397
- }), [
398
- enterBuilder,
399
- exitBuilder,
400
- isEditing,
401
- sendCode,
402
- saveCode,
403
- abandonCode,
404
- getCheckpoints,
405
- restoreCheckpoint,
406
- isRestoring,
407
- codeMessage,
408
- isBundleLoaded,
409
- isBundleLoading,
410
- ]);
60
+ enterBuilder: manager.enterBuilder,
61
+ exitBuilder: manager.exitBuilder,
62
+ isEditing: state.isEditing,
63
+ sendCode: manager.sendCode,
64
+ saveCode: manager.saveCode,
65
+ abandonCode: manager.abandonCode,
66
+ getCheckpoints: manager.getCheckpoints,
67
+ restoreCheckpoint: manager.restoreCheckpoint,
68
+ isRestoring: state.isRestoring,
69
+ codeMessage: state.codeMessage,
70
+ isBundleLoaded: state.isBundleLoaded,
71
+ isBundleLoading: state.isBundleLoading,
72
+ }), [manager, state]);
411
73
  // Render content
412
74
  const renderContent = () => {
413
- if (!isInitialLoadComplete)
75
+ if (!state.isInitialLoadComplete)
414
76
  return null;
415
- if (bundleError) {
77
+ if (state.bundleError) {
416
78
  return (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
417
79
  padding: 12,
418
80
  background: '#fef3c7',
419
81
  borderBottom: '1px solid #fcd34d',
420
82
  fontSize: 13,
421
83
  color: '#92400e',
422
- }, children: ["Bundle error: ", bundleError.message] }), children] }));
84
+ }, children: ["Bundle error: ", state.bundleError.message] }), children] }));
423
85
  }
424
- if (TenantApp) {
86
+ if (state.tenantApp) {
87
+ const TenantApp = state.tenantApp;
425
88
  return _jsx(TenantApp, {});
426
89
  }
427
90
  return children;
428
91
  };
429
- // Generate theme CSS from API-fetched theme
430
- const themeCSS = useMemo(() => generateThemeCSS(theme ?? undefined), [theme]);
431
- return (_jsxs(DynimContext.Provider, { value: contextValue, children: [_jsx("style", { children: themeCSS }), renderContent()] }));
92
+ return (_jsxs(DynimContext.Provider, { value: contextValue, children: [_jsx("style", { children: state.themeCSS }), renderContent()] }));
432
93
  }
433
- /**
434
- * Hook to access Dynim functionality
435
- */
436
94
  export function useDynim() {
437
95
  const context = useContext(DynimContext);
438
96
  if (!context) {
@@ -1,54 +1,4 @@
1
- /**
2
- * Vite plugin for Dynim package exposure
3
- *
4
- * Generates a virtual module that exports all specified packages,
5
- * so you don't have to manually import and map each one.
6
- *
7
- * Usage:
8
- * ```ts
9
- * // vite.config.ts
10
- * import { dynimPackages } from 'dynim-react/vite'
11
- *
12
- * export default {
13
- * plugins: [
14
- * dynimPackages([
15
- * 'react-router-dom',
16
- * '@tanstack/react-query',
17
- * 'axios',
18
- * ])
19
- * ]
20
- * }
21
- * ```
22
- *
23
- * Then in your app:
24
- * ```tsx
25
- * import packages from 'virtual:dynim-packages'
26
- *
27
- * <DynimProvider config={{ packages }}>
28
- * ```
29
- */
30
- import type { Plugin } from 'vite';
31
- export interface DynimPackagesOptions {
32
- /**
33
- * List of package names to expose to dynim bundles.
34
- * These must be installed in your project's node_modules.
35
- */
36
- packages: string[];
37
- }
38
- /**
39
- * Vite plugin that generates a virtual module exporting all specified packages.
40
- *
41
- * @param packages - Array of package names, or options object
42
- * @returns Vite plugin
43
- *
44
- * @example
45
- * // Simple usage - array of package names
46
- * dynimPackages(['react-router-dom', 'axios'])
47
- *
48
- * @example
49
- * // Options object
50
- * dynimPackages({ packages: ['react-router-dom', 'axios'] })
51
- */
52
- export declare function dynimPackages(packagesOrOptions: string[] | DynimPackagesOptions): Plugin;
1
+ import { dynimPackages } from 'dynim-core/vite';
2
+ export { dynimPackages, type DynimPackagesOptions } from 'dynim-core/vite';
53
3
  export default dynimPackages;
54
4
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/vite/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAKlC,MAAM,WAAW,oBAAoB;IACnC;;;OAGG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,iBAAiB,EAAE,MAAM,EAAE,GAAG,oBAAoB,GACjD,MAAM,CAwCR;AAED,eAAe,aAAa,CAAA"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugins/vite/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAC3E,eAAe,aAAa,CAAC"}
@@ -1,82 +1,3 @@
1
- /**
2
- * Vite plugin for Dynim package exposure
3
- *
4
- * Generates a virtual module that exports all specified packages,
5
- * so you don't have to manually import and map each one.
6
- *
7
- * Usage:
8
- * ```ts
9
- * // vite.config.ts
10
- * import { dynimPackages } from 'dynim-react/vite'
11
- *
12
- * export default {
13
- * plugins: [
14
- * dynimPackages([
15
- * 'react-router-dom',
16
- * '@tanstack/react-query',
17
- * 'axios',
18
- * ])
19
- * ]
20
- * }
21
- * ```
22
- *
23
- * Then in your app:
24
- * ```tsx
25
- * import packages from 'virtual:dynim-packages'
26
- *
27
- * <DynimProvider config={{ packages }}>
28
- * ```
29
- */
30
- const VIRTUAL_MODULE_ID = 'virtual:dynim-packages';
31
- const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;
32
- /**
33
- * Vite plugin that generates a virtual module exporting all specified packages.
34
- *
35
- * @param packages - Array of package names, or options object
36
- * @returns Vite plugin
37
- *
38
- * @example
39
- * // Simple usage - array of package names
40
- * dynimPackages(['react-router-dom', 'axios'])
41
- *
42
- * @example
43
- * // Options object
44
- * dynimPackages({ packages: ['react-router-dom', 'axios'] })
45
- */
46
- export function dynimPackages(packagesOrOptions) {
47
- const packageNames = Array.isArray(packagesOrOptions)
48
- ? packagesOrOptions
49
- : packagesOrOptions.packages;
50
- return {
51
- name: 'dynim-packages',
52
- resolveId(id) {
53
- if (id === VIRTUAL_MODULE_ID) {
54
- return RESOLVED_VIRTUAL_MODULE_ID;
55
- }
56
- },
57
- load(id) {
58
- if (id === RESOLVED_VIRTUAL_MODULE_ID) {
59
- // Generate import statements for each package
60
- const imports = packageNames
61
- .map((name, i) => `import * as _pkg${i} from '${name}'`)
62
- .join('\n');
63
- // Generate the packages object entries
64
- // Spread namespace objects into plain objects so exports are enumerable
65
- const entries = packageNames
66
- .map((name, i) => ` '${name}': { ..._pkg${i} }`)
67
- .join(',\n');
68
- // Return the generated module code
69
- return `${imports}
70
-
71
- const packages = {
72
- ${entries}
73
- }
74
-
75
- export default packages
76
- export { packages }
77
- `;
78
- }
79
- },
80
- };
81
- }
1
+ import { dynimPackages } from 'dynim-core/vite';
2
+ export { dynimPackages } from 'dynim-core/vite';
82
3
  export default dynimPackages;
package/dist/theme.d.ts CHANGED
@@ -1,58 +1,2 @@
1
- /**
2
- * Dynim Theme System
3
- *
4
- * Theme is automatically fetched from the API per project.
5
- * CSS variables are injected with defaults matching the original design.
6
- */
7
- export interface DynimTheme {
8
- accent?: string;
9
- accentHover?: string;
10
- builderBg?: string;
11
- builderText?: string;
12
- builderFont?: string;
13
- builderRadius?: string;
14
- builderOpacity?: string;
15
- chatAccent?: string;
16
- chatBg?: string;
17
- chatText?: string;
18
- chatFont?: string;
19
- chatRadius?: string;
20
- chatOpacity?: string;
21
- selectionColor?: string;
22
- hoverColor?: string;
23
- selectionRadius?: string;
24
- selectionOpacity?: string;
25
- hoverOpacity?: string;
26
- }
27
- /**
28
- * Default theme values - matches original SDK styling exactly
29
- */
30
- export declare const defaultTheme: Required<DynimTheme>;
31
- /**
32
- * Generates CSS variable declarations from a theme object
33
- */
34
- export declare function generateThemeCSS(theme?: DynimTheme): string;
35
- /**
36
- * CSS variable references for use in styles
37
- */
38
- export declare const themeVars: {
39
- readonly accent: "var(--dynim-accent)";
40
- readonly accentHover: "var(--dynim-accent-hover)";
41
- readonly builderBg: "var(--dynim-builder-bg)";
42
- readonly builderText: "var(--dynim-builder-text)";
43
- readonly builderFont: "var(--dynim-builder-font)";
44
- readonly builderRadius: "var(--dynim-builder-radius)";
45
- readonly builderOpacity: "var(--dynim-builder-opacity)";
46
- readonly chatAccent: "var(--dynim-chat-accent)";
47
- readonly chatBg: "var(--dynim-chat-bg)";
48
- readonly chatText: "var(--dynim-chat-text)";
49
- readonly chatFont: "var(--dynim-chat-font)";
50
- readonly chatRadius: "var(--dynim-chat-radius)";
51
- readonly chatOpacity: "var(--dynim-chat-opacity)";
52
- readonly selectionColor: "var(--dynim-selection-color)";
53
- readonly hoverColor: "var(--dynim-hover-color)";
54
- readonly selectionRadius: "var(--dynim-selection-radius)";
55
- readonly selectionOpacity: "var(--dynim-selection-opacity)";
56
- readonly hoverOpacity: "var(--dynim-hover-opacity)";
57
- };
1
+ export { generateThemeCSS, defaultTheme, themeVars, type DynimTheme } from 'dynim-core';
58
2
  //# sourceMappingURL=theme.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,UAAU;IAEzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IAGxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IAGrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,QAAQ,CAAC,UAAU,CAwB7C,CAAC;AA+BF;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,UAAe,GAAG,MAAM,CAW/D;AAED;;GAEG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;CAwBZ,CAAC"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC"}
package/dist/theme.js CHANGED
@@ -1,97 +1 @@
1
- /**
2
- * Dynim Theme System
3
- *
4
- * Theme is automatically fetched from the API per project.
5
- * CSS variables are injected with defaults matching the original design.
6
- */
7
- /**
8
- * Default theme values - matches original SDK styling exactly
9
- */
10
- export const defaultTheme = {
11
- // Builder Bar - original values
12
- accent: '#3b82f6',
13
- accentHover: '#2563eb',
14
- builderBg: '#000000',
15
- builderText: '#ffffff',
16
- builderFont: 'system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
17
- builderRadius: '22px',
18
- builderOpacity: '0.08',
19
- // Chat Widget - original values
20
- chatAccent: '#3b82f6',
21
- chatBg: '#ffffff',
22
- chatText: '#1f2937',
23
- chatFont: 'system-ui, -apple-system, BlinkMacSystemFont, \'Segoe UI\', Roboto, sans-serif',
24
- chatRadius: '16px',
25
- chatOpacity: '0.08',
26
- // Selection - original values
27
- selectionColor: '#8b5cf6',
28
- hoverColor: '#0ea5e9',
29
- selectionRadius: '0',
30
- selectionOpacity: '0.1',
31
- hoverOpacity: '0.1',
32
- };
33
- /**
34
- * CSS variable names mapped to theme keys
35
- */
36
- const cssVarMap = {
37
- // Builder Bar
38
- accent: '--dynim-accent',
39
- accentHover: '--dynim-accent-hover',
40
- builderBg: '--dynim-builder-bg',
41
- builderText: '--dynim-builder-text',
42
- builderFont: '--dynim-builder-font',
43
- builderRadius: '--dynim-builder-radius',
44
- builderOpacity: '--dynim-builder-opacity',
45
- // Chat Widget
46
- chatAccent: '--dynim-chat-accent',
47
- chatBg: '--dynim-chat-bg',
48
- chatText: '--dynim-chat-text',
49
- chatFont: '--dynim-chat-font',
50
- chatRadius: '--dynim-chat-radius',
51
- chatOpacity: '--dynim-chat-opacity',
52
- // Selection
53
- selectionColor: '--dynim-selection-color',
54
- hoverColor: '--dynim-hover-color',
55
- selectionRadius: '--dynim-selection-radius',
56
- selectionOpacity: '--dynim-selection-opacity',
57
- hoverOpacity: '--dynim-hover-opacity',
58
- };
59
- /**
60
- * Generates CSS variable declarations from a theme object
61
- */
62
- export function generateThemeCSS(theme = {}) {
63
- const merged = { ...defaultTheme, ...theme };
64
- const vars = Object.entries(merged)
65
- .map(([key, value]) => {
66
- const cssVar = cssVarMap[key];
67
- return `${cssVar}: ${value};`;
68
- })
69
- .join('\n ');
70
- return `:root {\n ${vars}\n}`;
71
- }
72
- /**
73
- * CSS variable references for use in styles
74
- */
75
- export const themeVars = {
76
- // Builder Bar
77
- accent: 'var(--dynim-accent)',
78
- accentHover: 'var(--dynim-accent-hover)',
79
- builderBg: 'var(--dynim-builder-bg)',
80
- builderText: 'var(--dynim-builder-text)',
81
- builderFont: 'var(--dynim-builder-font)',
82
- builderRadius: 'var(--dynim-builder-radius)',
83
- builderOpacity: 'var(--dynim-builder-opacity)',
84
- // Chat Widget
85
- chatAccent: 'var(--dynim-chat-accent)',
86
- chatBg: 'var(--dynim-chat-bg)',
87
- chatText: 'var(--dynim-chat-text)',
88
- chatFont: 'var(--dynim-chat-font)',
89
- chatRadius: 'var(--dynim-chat-radius)',
90
- chatOpacity: 'var(--dynim-chat-opacity)',
91
- // Selection
92
- selectionColor: 'var(--dynim-selection-color)',
93
- hoverColor: 'var(--dynim-hover-color)',
94
- selectionRadius: 'var(--dynim-selection-radius)',
95
- selectionOpacity: 'var(--dynim-selection-opacity)',
96
- hoverOpacity: 'var(--dynim-hover-opacity)',
97
- };
1
+ export { generateThemeCSS, defaultTheme, themeVars } from 'dynim-core';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dynim-react",
3
- "version": "1.0.57",
3
+ "version": "1.0.59",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -27,7 +27,7 @@
27
27
  "dev": "tsc --watch"
28
28
  },
29
29
  "dependencies": {
30
- "dynim-core": "^1.0.31"
30
+ "dynim-core": "^1.0.33"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "react": ">=17.0.0",