@messenger-box/tailwind-ui-inbox 10.0.3-alpha.144 → 10.0.3-alpha.158

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/lib/components/AIAgent/AIAgent.d.ts.map +1 -1
  3. package/lib/components/AIAgent/AIAgent.js +1 -55
  4. package/lib/components/AIAgent/AIAgent.js.map +1 -1
  5. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts +1 -2
  6. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.d.ts.map +1 -1
  7. package/lib/components/InboxMessage/message-widgets/ErrorFixCard.js.map +1 -1
  8. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +2 -2
  9. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -1
  10. package/lib/components/ModelConfigPanel.d.ts +11 -0
  11. package/lib/components/ModelConfigPanel.d.ts.map +1 -1
  12. package/lib/components/ModelConfigPanel.js +29 -2
  13. package/lib/components/ModelConfigPanel.js.map +1 -1
  14. package/lib/components/filler-components/RightSiderBar.d.ts +1 -28
  15. package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -1
  16. package/lib/components/filler-components/RightSiderBar.js +531 -458
  17. package/lib/components/filler-components/RightSiderBar.js.map +1 -1
  18. package/lib/container/Inbox.js +1 -1
  19. package/lib/container/Inbox.js.map +1 -1
  20. package/lib/container/ServiceInbox.js +1 -1
  21. package/lib/container/ServiceInbox.js.map +1 -1
  22. package/lib/container/TestInboxWithAiLoader.d.ts.map +1 -1
  23. package/lib/container/TestInboxWithAiLoader.js +1 -11
  24. package/lib/container/TestInboxWithAiLoader.js.map +1 -1
  25. package/lib/container/ThreadMessages.js +1 -1
  26. package/lib/container/ThreadMessages.js.map +1 -1
  27. package/lib/container/ThreadMessagesInbox.js +1 -1
  28. package/lib/container/ThreadMessagesInbox.js.map +1 -1
  29. package/lib/container/Threads.js +1 -1
  30. package/lib/container/Threads.js.map +1 -1
  31. package/lib/hooks/index.d.ts +0 -1
  32. package/lib/hooks/index.d.ts.map +1 -1
  33. package/lib/hooks/usePersistentModelConfig.d.ts +1 -0
  34. package/lib/hooks/usePersistentModelConfig.d.ts.map +1 -1
  35. package/lib/hooks/usePersistentModelConfig.js +1 -0
  36. package/lib/hooks/usePersistentModelConfig.js.map +1 -1
  37. package/lib/index.js +1 -1
  38. package/lib/module.js +1 -1
  39. package/lib/module.js.map +1 -1
  40. package/lib/templates/InboxWithAi.d.ts.map +1 -1
  41. package/lib/templates/InboxWithAi.js +2 -15
  42. package/lib/templates/InboxWithAi.js.map +1 -1
  43. package/lib/templates/InboxWithAi.tsx +2 -24
  44. package/package.json +4 -4
  45. package/src/components/AIAgent/AIAgent.tsx +3 -54
  46. package/src/components/InboxMessage/message-widgets/ErrorFixCard.tsx +1 -2
  47. package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +2 -2
  48. package/src/components/ModelConfigPanel.tsx +59 -0
  49. package/src/components/filler-components/RightSiderBar.tsx +570 -566
  50. package/src/container/TestInboxWithAiLoader.tsx +0 -8
  51. package/src/hooks/index.ts +0 -1
  52. package/src/hooks/usePersistentModelConfig.ts +2 -0
  53. package/src/templates/InboxWithAi.tsx +2 -24
  54. package/lib/components/live-code-editor/hybrid-live-editor.js +0 -68
  55. package/lib/components/live-code-editor/hybrid-live-editor.js.map +0 -1
  56. package/lib/components/live-code-editor/live-code-editor.js +0 -207
  57. package/lib/components/live-code-editor/live-code-editor.js.map +0 -1
  58. package/lib/hooks/use-file-sync.d.ts +0 -16
  59. package/lib/hooks/use-file-sync.d.ts.map +0 -1
  60. package/lib/hooks/use-file-sync.js +0 -63
  61. package/lib/hooks/use-file-sync.js.map +0 -1
  62. package/lib/utils/utils.js +0 -3
  63. package/lib/utils/utils.js.map +0 -1
  64. package/lib/xstate/rightSidebar.machine.js +0 -325
  65. package/lib/xstate/rightSidebar.machine.js.map +0 -1
  66. package/src/hooks/use-file-sync.ts +0 -91
@@ -1,568 +1,572 @@
1
1
  import * as React from 'react';
2
- import { HybridLiveEditor, HybridLiveEditorRef } from '../live-code-editor/hybrid-live-editor';
3
- import { useOnChatMessageAddedSubscription } from 'common/graphql';
4
- import { AiAgentMessageRole, ICreateChannelInput, PostTypeEnum, RoomType } from 'common';
5
- import { Sandbox } from '@e2b/code-interpreter';
6
- import { useMachine } from '@xstate/react';
7
- import { fromPromise } from 'xstate';
8
- import { rightSidebarMachine } from '../../xstate';
9
-
10
- type RightSidebarProps = {
11
- channelId: string;
12
- detailSidebarOptions: {
13
- isMobileView: boolean;
14
- isSmallTabletView: boolean;
15
- isTabletView: boolean;
16
- isDesktopView: boolean;
17
- isLargeDesktopView: boolean;
18
- isSmallScreen: boolean;
19
- [key: string]: any;
20
- };
21
- windowWidth: number;
22
- windowHeight: number;
23
- activeTab?: string;
24
- selectedPost?: any;
25
- messages?: any[];
26
- setIsLoading?: (isLoading: boolean) => void;
27
- handleSendMessage?: (createChannelInput: ICreateChannelInput) => void;
28
- handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
29
- isMinimized?: boolean;
30
- isLoading?: boolean;
31
- isCreatingSandbox?: boolean;
32
- setIsCreatingSandbox?: (isCreatingSandbox: boolean) => void;
33
- [key: string]: any;
34
- };
35
-
36
- export const RightSidebarFillComponent = (props: RightSidebarProps) => {
37
- const {
38
- channelId,
39
- detailSidebarOptions,
40
- windowWidth,
41
- windowHeight,
42
- activeTab,
43
- messages,
44
- selectedPost,
45
- setIsLoading,
46
- isLoading,
47
- handleSendMessage,
48
- handleRecreateSandbox,
49
- isMinimized,
50
- isCreatingSandbox,
51
- setIsCreatingSandbox,
52
- } = props;
53
- const hybridLiveEditorRef = React.useRef<HybridLiveEditorRef>(null);
54
- const previewIframeRef = React.useRef<HTMLIFrameElement>(null);
55
- const previewIframeContainerRef = React.useRef<HTMLIFrameElement>(null);
56
- // State for subscription data
57
- const [subscriptionData, setSubscriptionData] = React.useState<any>(null);
58
- // XState machine for sidebar state
59
- const [state, send] = useMachine(
60
- rightSidebarMachine.provide({
61
- actors: {
62
- recreateSandbox: fromPromise(async ({ input }) => {
63
- const { id } = input as { id: string };
64
- if (handleRecreateSandbox) {
65
- await handleRecreateSandbox(id);
66
- }
67
- return { success: true } as const;
68
- }),
69
- },
70
- }),
71
- {
72
- input: {
73
- channelId: channelId || '',
74
- isMinimized: !!isMinimized,
75
- windowWidth,
76
- windowHeight,
77
- isLoading: !!isLoading,
78
- activeFragment: null,
79
- currentFiles: {},
80
- canvasLayers: [],
81
- previewStatus: null,
82
- },
83
- },
84
- );
85
- const activeFragment = state.context.activeFragment;
86
- const currentFiles = state.context.currentFiles;
87
- const canvasLayers = state.context.canvasLayers as any[];
88
- const previewStatus = state.context.previewStatus as { code?: number; message?: string; status?: string } | null;
89
- // const [isLoading, setIsLoading] = React.useState(false);
90
- const { data: chatMessageAddedData } = useOnChatMessageAddedSubscription({
91
- variables: { channelId: channelId?.toString() || '' },
92
- skip: !channelId,
93
- });
94
-
95
- // Component mount tracking
96
- const componentId = React.useMemo(
97
- () => `right-sidebar-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
98
- [],
99
- );
100
-
101
- const handleCopyToClipboard = React.useCallback((text: string) => {
102
- navigator.clipboard.writeText(text);
103
- }, []);
104
-
105
- const handleFileUpdate = React.useCallback(async (filePath: string, content: string) => {
106
- // TODO: Implement file update logic for the channel
107
- console.log('File update:', { filePath, content });
108
- }, []);
109
-
110
- // Create complete file context by merging currentFiles with activeFragment files
111
- const completeFileContext = React.useMemo(() => {
112
- if (!activeFragment?.files) {
113
- console.log('📂 Using currentFiles as complete context (no activeFragment.files)');
114
- return currentFiles;
115
- }
116
-
117
- // Merge currentFiles with activeFragment files, giving priority to activeFragment files
118
- const merged = { ...currentFiles };
119
-
120
- // Override with activeFragment files (these are the most recent changes)
121
- Object.entries(activeFragment.files).forEach(([path, content]) => {
122
- merged[path] = content as string;
123
- });
124
-
125
- // Validate file context integrity
126
- const hasPackageJson = 'package.json' in merged;
127
- const hasMainFiles = Object.keys(merged).some(
128
- (path) =>
129
- path.includes('index.') || path.includes('main.') || path.includes('App.') || path.includes('src/'),
130
- );
131
-
132
- console.log('📂 Complete file context created:', {
133
- currentFilesCount: Object.keys(currentFiles).length,
134
- activeFragmentFilesCount: Object.keys(activeFragment.files).length,
135
- mergedFilesCount: Object.keys(merged).length,
136
- hasPackageJson,
137
- hasMainFiles,
138
- activeFragmentFiles: Object.keys(activeFragment.files),
139
- allFiles: Object.keys(merged),
140
- validation: {
141
- isComplete: hasPackageJson && hasMainFiles,
142
- missingPackageJson: !hasPackageJson,
143
- missingMainFiles: !hasMainFiles,
144
- },
145
- });
146
-
147
- if (!hasPackageJson) {
148
- console.warn('⚠️ File context missing package.json - this may cause issues in sandbox');
149
- }
150
- if (!hasMainFiles) {
151
- console.warn('⚠️ File context missing main application files - this may cause issues in sandbox');
152
- }
153
-
154
- return merged;
155
- }, [currentFiles, activeFragment?.files]);
156
-
157
- // Determine if we should show preview or editor based on activeTab
158
- const isPreviewMode = activeTab === 'preview';
159
-
160
- // Turn on loader when new data is about to be processed
161
- React.useEffect(() => {
162
- if (chatMessageAddedData?.chatMessageAdded || selectedPost) {
163
- // sync external loader
164
- // setIsLoading?.(false);
165
- // send({ type: 'SET_LOADING', isLoading: false });
166
- }
167
- }, [chatMessageAddedData, selectedPost, send, setIsLoading]);
168
-
169
- // Handle subscription data updates - prefer response update structure like use-ai-messages.ts
170
- React.useEffect(() => {
171
- if (chatMessageAddedData?.chatMessageAdded || selectedPost) {
172
- // Prefer explicitly selected post to ensure UI updates when it changes
173
- const message = selectedPost || chatMessageAddedData?.chatMessageAdded;
174
-
175
- // Extract strictly from message.propsConfiguration (fragment only)
176
- let extracted: any = null;
177
- let fragmentSource:
178
- | 'propsConfiguration.contents.props.fragment'
179
- | 'propsConfiguration.contents.fragment'
180
- | 'propsConfiguration.fragment'
181
- | null = null;
182
-
183
- try {
184
- // 1) Preferred: contents.props.fragment
185
- const cfg: any = message.propsConfiguration || {};
186
- const contents: any = cfg.contents || cfg.content || {};
187
-
188
- if (contents?.props?.fragment) {
189
- const frag = contents.props.fragment;
190
- extracted = {
191
- sandboxUrl: frag.sandboxUrl || frag.url,
192
- sandboxId: frag.sandboxId,
193
- projectId: cfg.projectId || contents.projectId || frag.projectId,
194
- modelConfig: cfg.modelConfig || contents.modelConfig || frag.modelConfig,
195
- files: frag.files || {},
196
- canvasLayers: contents.canvasLayers || cfg.canvasLayers || frag.canvasLayers,
197
- summary: contents.summary || cfg.summary || frag.summary,
198
- title: frag.title || contents.title || cfg.title,
199
- isError: frag.isError,
200
- };
201
- fragmentSource = 'propsConfiguration.contents.props.fragment';
202
- }
203
-
204
- // 2) Fallback: contents.fragment
205
- if (!extracted && contents?.fragment) {
206
- const frag = contents.fragment;
207
- extracted = {
208
- sandboxUrl: frag.sandboxUrl || frag.url,
209
- sandboxId: frag.sandboxId,
210
- projectId: cfg.projectId || contents.projectId || frag.projectId,
211
- modelConfig: cfg.modelConfig || contents.modelConfig || frag.modelConfig,
212
- files: frag.files || {},
213
- canvasLayers: contents.canvasLayers || cfg.canvasLayers || frag.canvasLayers,
214
- summary: contents.summary || cfg.summary || frag.summary,
215
- title: frag.title || contents.title || cfg.title,
216
- isError: frag.isError,
217
- };
218
- fragmentSource = 'propsConfiguration.contents.fragment';
219
- }
220
-
221
- // 3) Fallback: propsConfiguration.fragment (top-level)
222
- if (!extracted && cfg?.fragment) {
223
- const frag = cfg.fragment;
224
- extracted = {
225
- sandboxUrl: frag.sandboxUrl || frag.url,
226
- sandboxId: frag.sandboxId,
227
- projectId: cfg.projectId || frag.projectId,
228
- modelConfig: cfg.modelConfig || frag.modelConfig,
229
- files: frag.files || {},
230
- canvasLayers: cfg.canvasLayers || frag.canvasLayers,
231
- summary: cfg.summary || frag.summary,
232
- title: frag.title || cfg.title,
233
- isError: frag.isError,
234
- };
235
- fragmentSource = 'propsConfiguration.fragment';
236
- }
237
- } catch (error) {
238
- console.error('Error extracting response from message:', error);
239
- }
240
-
241
- // console.log('Extracted response fragment source:', fragmentSource);
242
- // console.log('Extracted response:', extracted);
243
-
244
- if (extracted && (extracted.sandboxUrl || extracted.url)) {
245
- console.log('✅ Processing response update:', extracted);
246
-
247
- // Update subscription data snapshot
248
- setSubscriptionData(extracted);
249
-
250
- // Build active fragment
251
- const fragment = {
252
- id: message.id,
253
- sandboxUrl: extracted.sandboxUrl || extracted.url,
254
- sandboxId: extracted.sandboxId,
255
- title: extracted.title || 'Generated Code',
256
- files: extracted.files || {},
257
- projectId: extracted.projectId,
258
- modelConfig: extracted.modelConfig,
259
- canvasLayers: extracted.canvasLayers,
260
- summary: extracted.summary,
261
- isError: extracted.isError,
262
- };
263
-
264
- // If canvasLayers missing but present in summary, attempt to extract like use-ai-messages
265
- if ((!fragment.canvasLayers || !Array.isArray(fragment.canvasLayers)) && fragment.summary) {
266
- try {
267
- const match = (fragment.summary as string).match(/<canvas_layers>([\s\S]*?)<\/canvas_layers>/);
268
- if (match) {
269
- const parsed = JSON.parse(match[1]);
270
- if (Array.isArray(parsed)) {
271
- fragment.canvasLayers = parsed;
272
- }
273
- }
274
- } catch (e) {
275
- console.warn('Failed to parse canvas layers from summary:', e);
276
- }
277
- }
278
-
279
- console.log('✅ Setting active fragment via XState:', fragment);
280
- send({ type: 'SET_FRAGMENT', fragment });
281
- // setIsLoading?.(false);
282
- //send({ type: 'SET_LOADING', isLoading: false });
283
- } else {
284
- console.log('❌ No valid response found in message');
285
- // setIsLoading?.(false);
286
- // send({ type: 'SET_LOADING', isLoading: false });
287
- }
288
- }
289
- }, [chatMessageAddedData, selectedPost, send, setIsLoading]);
290
-
291
- // When switching tabs or when sandbox changes, show loader until ready
292
- // React.useEffect(() => {
293
- // if (isPreviewMode) {
294
- // if (activeFragment?.sandboxUrl) {
295
- // // setIsLoading(true);
296
- // }
297
- // } else {
298
- // // In code view, briefly show loader while editor reacts to new files
299
- // // setIsLoading(true);
300
- // const timeoutId = setTimeout(() => setIsLoading(false), 400);
301
- // return () => clearTimeout(timeoutId);
302
- // }
303
- // }, [isPreviewMode, activeFragment?.sandboxUrl, completeFileContext]);
304
-
305
- // Cleanup iframe resources on unmount to prevent renderer leaks in Chrome
306
- React.useEffect(() => {
307
- return () => {
308
- try {
309
- if (previewIframeRef.current) {
310
- previewIframeRef.current.src = 'about:blank';
311
- }
312
- } catch (e) {
313
- // no-op
314
- }
315
- };
316
- }, []);
317
-
318
- React.useEffect(() => {
319
- const handleVisualEditorMessage = (event: MessageEvent) => {
320
- // Handle AI changes
321
- if (event.data.type === 'VISUAL_EDITOR_CHANGES') {
322
- console.log('visual_editor_changes....', event.data.message);
323
-
324
- if (handleSendMessage && event.data.message) {
325
- // Call aiHandleSend directly - this is the handleSend function from AIAgent
326
- handleSendMessage({
327
- channelId,
328
- type: RoomType.Aiassistant,
329
- postData: {
330
- // postId: postId,
331
- type: PostTypeEnum.Aiassistant,
332
- content: event.data.message,
333
- },
334
- });
335
- } else {
336
- console.warn('⚠️ handleSendMessage not available or no message provided');
337
- }
338
- }
339
- };
340
-
341
- window.addEventListener('message', handleVisualEditorMessage);
342
- return () => window.removeEventListener('message', handleVisualEditorMessage);
343
- }, [handleSendMessage, channelId]);
344
-
345
- const recreateSandboxHandler = React.useCallback(
346
- async (id: string) => {
347
- if (id) {
348
- console.log('recreate sandbox id....', id);
349
- setIsLoading?.(true);
350
- send({ type: 'SET_LOADING', isLoading: true });
351
- try {
352
- send({ type: 'RECREATE_SANDBOX', id });
353
- } catch (e: any) {
354
- console.error('Error recreating sandbox:', e);
355
- setIsLoading?.(false);
356
- send({ type: 'SET_LOADING', isLoading: false });
357
- }
358
- }
359
- },
360
- [send, setIsLoading],
361
- );
362
-
363
- // Track if we've already attempted recreate for a given sandbox URL
364
- const recreateAttemptedByUrlRef = React.useRef<Record<string, boolean>>({});
365
-
366
- // Probe sandbox URL via XState actor
367
- React.useEffect(() => {
368
- const url = activeFragment?.sandboxUrl;
369
- if (url) {
370
- send({ type: 'PROBE', url, fragmentId: activeFragment?.id });
371
- }
372
- }, [activeFragment?.sandboxUrl, activeFragment?.id, send]);
373
-
374
- // Prefer using external loading when provided; otherwise fall back to machine state
375
- const derivedIsLoading = (typeof isLoading === 'boolean' ? isLoading : state.context.isLoading) as boolean;
376
-
377
- // Sync external loading prop with machine's loading state (no-op if setter not provided)
378
- // React.useEffect(() => {
379
- // console.log(
380
- // 'isLoading....',
381
- // isLoading,
382
- // ' state.context.isLoading',
383
- // state.context.isLoading,
384
- // ' previewStatus',
385
- // previewStatus,
386
- // );
387
- // if (!state.context.isLoading) {
388
- // setIsLoading?.(!!state.context.isLoading);
389
- // }
390
- // }, [state.context.isLoading, setIsLoading, isLoading]);
391
-
392
- return (
393
- <div ref={previewIframeContainerRef} className="right-sidebar-content h-full flex flex-col">
394
- <div className="flex-1 overflow-hidden" style={{ minHeight: windowHeight - 140 }}>
395
- {isPreviewMode ? (
396
- // Preview Mode - Show sandbox iframe or fallback
397
- (() => {
398
- if (activeFragment?.sandboxUrl) {
399
- return (
400
- <div
401
- className="w-full h-full relative"
402
- style={{
403
- width: isMinimized ? windowWidth * 0.8 + 'px' : '100%',
404
- height: windowHeight - 140,
405
- backgroundColor: '#f0f0f0',
406
- }}
407
- >
408
- {!previewStatus ? (
409
- <iframe
410
- key={activeFragment.sandboxUrl}
411
- ref={previewIframeRef}
412
- src={activeFragment.sandboxUrl}
413
- className="w-full w-fit h-full h-fit border border-gray-200 rounded-md bg-white shadow-sm"
414
- title="Live Preview"
415
- sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-orientation-lock allow-pointer-lock allow-presentation allow-storage-access-by-user-activation allow-top-navigation-by-user-activation"
416
- loading="lazy"
417
- onLoad={() => {
418
- console.log('🎯 Iframe loaded successfully');
419
- setIsLoading?.(false);
420
- setIsCreatingSandbox?.(false);
421
- send({ type: 'SET_LOADING', isLoading: false });
422
- }}
423
- onError={(e) => {
424
- console.error('❌ Iframe error:', e);
425
- setIsLoading?.(false);
426
- send({ type: 'SET_LOADING', isLoading: false });
427
- }}
428
- style={
429
- {
430
- // maxHeight: windowHeight - 140,
431
- // width: isMinimized ? windowWidth * 0.82 + 'px' : '100%',
432
- //padding: isMinimized ? '10px' : '0px',
433
- }
434
- }
435
- />
436
- ) : (
437
- <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
438
- {handleRecreateSandbox && (
439
- <>
440
- {derivedIsLoading || isCreatingSandbox ? (
441
- <div className="flex flex-col items-center justify-center gap-2">
442
- <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
443
- <p className="text-xs text-blue-700">Recreating...</p>
444
- </div>
445
- ) : (
446
- <div className="flex flex-col items-center justify-center gap-2">
447
- <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
448
- <p className="text-sm text-muted-foreground">
449
- {isLoading ? 'Recreating...' : 'Refresh'}{' '}
450
- {/* Sandbox is not available. */}
451
- </p>
452
- {/* <button
453
- type="button"
454
- className="themed-button focus-themed px-3 py-1.5 text-xs"
455
- onClick={() =>
456
- recreateSandboxHandler(activeFragment?.id)
457
- }
458
- >
459
- Recreate
460
- </button> */}
461
- </div>
462
- )}
463
- </>
464
- )}
465
- </div>
466
- )}
467
- <div className="absolute top-2 right-2 bg-green-100 text-green-800 px-2 py-1 rounded text-xs z-10">
468
- Live Preview
469
- </div>
470
- {derivedIsLoading ||
471
- (isCreatingSandbox && (
472
- <>
473
- <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
474
- <div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
475
- </div>
476
- <div className="absolute top-2 left-2 bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs z-30">
477
- Loading...
478
- </div>
479
- </>
480
- ))}
481
- </div>
482
- );
483
- } else {
484
- return (
485
- <div
486
- className="h-full flex items-center justify-center text-gray-500 border border-gray-200 rounded-md bg-gray-50 "
487
- style={{ width: '100%', height: '80vh' }}
488
- >
489
- <div className="text-center space-y-3">
490
- <p className="font-medium">No preview available</p>
491
- <div className="flex flex-col items-center justify-center space-y-3">
492
- {derivedIsLoading || isCreatingSandbox ? (
493
- <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
494
- ) : (
495
- <div className="w-8 h-8 bg-gray-300 rounded-full"></div>
496
- )}
497
- <p className="text-sm text-gray-400">
498
- {derivedIsLoading || isCreatingSandbox
499
- ? 'Generating code...'
500
- : 'Waiting for code generation...'}
501
- </p>
502
- </div>
503
- </div>
504
- </div>
505
- );
506
- }
507
- })()
508
- ) : (
509
- // Code Editor Mode - Show files from subscription or fallback
510
- <div
511
- className="h-full w-full relative"
512
- style={{
513
- minHeight: windowHeight - 140,
514
- height: windowHeight - 140,
515
- width: isMinimized ? windowWidth * 0.9 + 'px' : '100%',
516
- }}
517
- >
518
- <HybridLiveEditor
519
- ref={hybridLiveEditorRef}
520
- projectId={activeFragment?.projectId || channelId}
521
- fragmentId={activeFragment?.id || activeTab || 'default'}
522
- files={Object.keys(completeFileContext).length > 0 ? completeFileContext : {}}
523
- sandboxUrl={activeFragment?.sandboxUrl}
524
- onCopyToClipboard={handleCopyToClipboard}
525
- onFileUpdate={handleFileUpdate}
526
- readOnly={!activeFragment?.id}
527
- autoSave={false}
528
- debounceMs={500}
529
- //className={`h-full w-[100%] h-[75vh] border-b border-gray-200 rounded-md bg-white shadow-sm overflow-hidden`}
530
- className="h-full w-full"
531
- />
532
- {derivedIsLoading && (
533
- <>
534
- <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
535
- <div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
536
- </div>
537
- <div className="absolute top-4 right-4 bg-blue-100 border border-blue-400 text-blue-700 px-3 py-2 rounded text-sm z-30">
538
- <div className="flex items-center gap-2">
539
- <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div>
540
- Processing...
541
- </div>
542
- </div>
543
- </>
544
- )}
545
- </div>
546
- )}
547
- </div>
548
-
549
- {/* <div className="p-2 border-t bg-gray-50 text-xs text-gray-500">
550
- <div className="flex justify-between">
551
- <span>Mobile: {detailSidebarOptions.isMobileView ? 'Yes' : 'No'}</span>
552
- <span>Desktop: {detailSidebarOptions.isDesktopView ? 'Yes' : 'No'}</span>
553
- {activeFragment && (
554
- <span className="text-green-600">{activeFragment.isError ? 'Error' : 'Ready'}</span>
555
- )}
556
- </div>
557
- {activeFragment && (
558
- <div className="mt-1 text-xs">
559
- <span className="text-gray-400">Files: {Object.keys(completeFileContext).length}</span>
560
- {canvasLayers.length > 0 && (
561
- <span className="ml-2 text-gray-400">Layers: {canvasLayers.length}</span>
562
- )}
563
- </div>
564
- )}
565
- </div> */}
566
- </div>
567
- );
2
+ // import { HybridLiveEditor, HybridLiveEditorRef } from '../live-code-editor/hybrid-live-editor';
3
+ // import { useOnChatMessageAddedSubscription } from 'common/graphql';
4
+ // import { AiAgentMessageRole, ICreateChannelInput, PostTypeEnum, RoomType } from 'common';
5
+ // import { Sandbox } from '@e2b/code-interpreter';
6
+ // import { useMachine } from '@xstate/react';
7
+ // import { fromPromise } from 'xstate';
8
+ // import { rightSidebarMachine } from '../../xstate';
9
+
10
+ // type RightSidebarProps = {
11
+ // channelId: string;
12
+ // detailSidebarOptions: {
13
+ // isMobileView: boolean;
14
+ // isSmallTabletView: boolean;
15
+ // isTabletView: boolean;
16
+ // isDesktopView: boolean;
17
+ // isLargeDesktopView: boolean;
18
+ // isSmallScreen: boolean;
19
+ // [key: string]: any;
20
+ // };
21
+ // windowWidth: number;
22
+ // windowHeight: number;
23
+ // activeTab?: string;
24
+ // selectedPost?: any;
25
+ // messages?: any[];
26
+ // setIsLoading?: (isLoading: boolean) => void;
27
+ // handleSendMessage?: (createChannelInput: ICreateChannelInput) => void;
28
+ // handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
29
+ // isMinimized?: boolean;
30
+ // isLoading?: boolean;
31
+ // isCreatingSandbox?: boolean;
32
+ // setIsCreatingSandbox?: (isCreatingSandbox: boolean) => void;
33
+ // [key: string]: any;
34
+ // };
35
+
36
+ // export const RightSidebarFillComponent = (props: RightSidebarProps) => {
37
+ // const {
38
+ // channelId,
39
+ // detailSidebarOptions,
40
+ // windowWidth,
41
+ // windowHeight,
42
+ // activeTab,
43
+ // messages,
44
+ // selectedPost,
45
+ // setIsLoading,
46
+ // isLoading,
47
+ // handleSendMessage,
48
+ // handleRecreateSandbox,
49
+ // isMinimized,
50
+ // isCreatingSandbox,
51
+ // setIsCreatingSandbox,
52
+ // } = props;
53
+ // const hybridLiveEditorRef = React.useRef<HybridLiveEditorRef>(null);
54
+ // const previewIframeRef = React.useRef<HTMLIFrameElement>(null);
55
+ // const previewIframeContainerRef = React.useRef<HTMLIFrameElement>(null);
56
+ // // State for subscription data
57
+ // const [subscriptionData, setSubscriptionData] = React.useState<any>(null);
58
+ // // XState machine for sidebar state
59
+ // const [state, send] = useMachine(
60
+ // rightSidebarMachine.provide({
61
+ // actors: {
62
+ // recreateSandbox: fromPromise(async ({ input }) => {
63
+ // const { id } = input as { id: string };
64
+ // if (handleRecreateSandbox) {
65
+ // await handleRecreateSandbox(id);
66
+ // }
67
+ // return { success: true } as const;
68
+ // }),
69
+ // },
70
+ // }),
71
+ // {
72
+ // input: {
73
+ // channelId: channelId || '',
74
+ // isMinimized: !!isMinimized,
75
+ // windowWidth,
76
+ // windowHeight,
77
+ // isLoading: !!isLoading,
78
+ // activeFragment: null,
79
+ // currentFiles: {},
80
+ // canvasLayers: [],
81
+ // previewStatus: null,
82
+ // },
83
+ // },
84
+ // );
85
+ // const activeFragment = state.context.activeFragment;
86
+ // const currentFiles = state.context.currentFiles;
87
+ // const canvasLayers = state.context.canvasLayers as any[];
88
+ // const previewStatus = state.context.previewStatus as { code?: number; message?: string; status?: string } | null;
89
+ // // const [isLoading, setIsLoading] = React.useState(false);
90
+ // const { data: chatMessageAddedData } = useOnChatMessageAddedSubscription({
91
+ // variables: { channelId: channelId?.toString() || '' },
92
+ // skip: !channelId,
93
+ // });
94
+
95
+ // // Component mount tracking
96
+ // const componentId = React.useMemo(
97
+ // () => `right-sidebar-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
98
+ // [],
99
+ // );
100
+
101
+ // const handleCopyToClipboard = React.useCallback((text: string) => {
102
+ // navigator.clipboard.writeText(text);
103
+ // }, []);
104
+
105
+ // const handleFileUpdate = React.useCallback(async (filePath: string, content: string) => {
106
+ // // TODO: Implement file update logic for the channel
107
+ // console.log('File update:', { filePath, content });
108
+ // }, []);
109
+
110
+ // // Create complete file context by merging currentFiles with activeFragment files
111
+ // const completeFileContext = React.useMemo(() => {
112
+ // if (!activeFragment?.files) {
113
+ // console.log('📂 Using currentFiles as complete context (no activeFragment.files)');
114
+ // return currentFiles;
115
+ // }
116
+
117
+ // // Merge currentFiles with activeFragment files, giving priority to activeFragment files
118
+ // const merged = { ...currentFiles };
119
+
120
+ // // Override with activeFragment files (these are the most recent changes)
121
+ // Object.entries(activeFragment.files).forEach(([path, content]) => {
122
+ // merged[path] = content as string;
123
+ // });
124
+
125
+ // // Validate file context integrity
126
+ // const hasPackageJson = 'package.json' in merged;
127
+ // const hasMainFiles = Object.keys(merged).some(
128
+ // (path) =>
129
+ // path.includes('index.') || path.includes('main.') || path.includes('App.') || path.includes('src/'),
130
+ // );
131
+
132
+ // console.log('📂 Complete file context created:', {
133
+ // currentFilesCount: Object.keys(currentFiles).length,
134
+ // activeFragmentFilesCount: Object.keys(activeFragment.files).length,
135
+ // mergedFilesCount: Object.keys(merged).length,
136
+ // hasPackageJson,
137
+ // hasMainFiles,
138
+ // activeFragmentFiles: Object.keys(activeFragment.files),
139
+ // allFiles: Object.keys(merged),
140
+ // validation: {
141
+ // isComplete: hasPackageJson && hasMainFiles,
142
+ // missingPackageJson: !hasPackageJson,
143
+ // missingMainFiles: !hasMainFiles,
144
+ // },
145
+ // });
146
+
147
+ // if (!hasPackageJson) {
148
+ // console.warn('⚠️ File context missing package.json - this may cause issues in sandbox');
149
+ // }
150
+ // if (!hasMainFiles) {
151
+ // console.warn('⚠️ File context missing main application files - this may cause issues in sandbox');
152
+ // }
153
+
154
+ // return merged;
155
+ // }, [currentFiles, activeFragment?.files]);
156
+
157
+ // // Determine if we should show preview or editor based on activeTab
158
+ // const isPreviewMode = activeTab === 'preview';
159
+
160
+ // // Turn on loader when new data is about to be processed
161
+ // React.useEffect(() => {
162
+ // if (chatMessageAddedData?.chatMessageAdded || selectedPost) {
163
+ // // sync external loader
164
+ // // setIsLoading?.(false);
165
+ // // send({ type: 'SET_LOADING', isLoading: false });
166
+ // }
167
+ // }, [chatMessageAddedData, selectedPost, send, setIsLoading]);
168
+
169
+ // // Handle subscription data updates - prefer response update structure like use-ai-messages.ts
170
+ // React.useEffect(() => {
171
+ // if (chatMessageAddedData?.chatMessageAdded || selectedPost) {
172
+ // // Prefer explicitly selected post to ensure UI updates when it changes
173
+ // const message = selectedPost || chatMessageAddedData?.chatMessageAdded;
174
+
175
+ // // Extract strictly from message.propsConfiguration (fragment only)
176
+ // let extracted: any = null;
177
+ // let fragmentSource:
178
+ // | 'propsConfiguration.contents.props.fragment'
179
+ // | 'propsConfiguration.contents.fragment'
180
+ // | 'propsConfiguration.fragment'
181
+ // | null = null;
182
+
183
+ // try {
184
+ // // 1) Preferred: contents.props.fragment
185
+ // const cfg: any = message.propsConfiguration || {};
186
+ // const contents: any = cfg.contents || cfg.content || {};
187
+
188
+ // if (contents?.props?.fragment) {
189
+ // const frag = contents.props.fragment;
190
+ // extracted = {
191
+ // sandboxUrl: frag.sandboxUrl || frag.url,
192
+ // sandboxId: frag.sandboxId,
193
+ // projectId: cfg.projectId || contents.projectId || frag.projectId,
194
+ // modelConfig: cfg.modelConfig || contents.modelConfig || frag.modelConfig,
195
+ // files: frag.files || {},
196
+ // canvasLayers: contents.canvasLayers || cfg.canvasLayers || frag.canvasLayers,
197
+ // summary: contents.summary || cfg.summary || frag.summary,
198
+ // title: frag.title || contents.title || cfg.title,
199
+ // isError: frag.isError,
200
+ // };
201
+ // fragmentSource = 'propsConfiguration.contents.props.fragment';
202
+ // }
203
+
204
+ // // 2) Fallback: contents.fragment
205
+ // if (!extracted && contents?.fragment) {
206
+ // const frag = contents.fragment;
207
+ // extracted = {
208
+ // sandboxUrl: frag.sandboxUrl || frag.url,
209
+ // sandboxId: frag.sandboxId,
210
+ // projectId: cfg.projectId || contents.projectId || frag.projectId,
211
+ // modelConfig: cfg.modelConfig || contents.modelConfig || frag.modelConfig,
212
+ // files: frag.files || {},
213
+ // canvasLayers: contents.canvasLayers || cfg.canvasLayers || frag.canvasLayers,
214
+ // summary: contents.summary || cfg.summary || frag.summary,
215
+ // title: frag.title || contents.title || cfg.title,
216
+ // isError: frag.isError,
217
+ // };
218
+ // fragmentSource = 'propsConfiguration.contents.fragment';
219
+ // }
220
+
221
+ // // 3) Fallback: propsConfiguration.fragment (top-level)
222
+ // if (!extracted && cfg?.fragment) {
223
+ // const frag = cfg.fragment;
224
+ // extracted = {
225
+ // sandboxUrl: frag.sandboxUrl || frag.url,
226
+ // sandboxId: frag.sandboxId,
227
+ // projectId: cfg.projectId || frag.projectId,
228
+ // modelConfig: cfg.modelConfig || frag.modelConfig,
229
+ // files: frag.files || {},
230
+ // canvasLayers: cfg.canvasLayers || frag.canvasLayers,
231
+ // summary: cfg.summary || frag.summary,
232
+ // title: frag.title || cfg.title,
233
+ // isError: frag.isError,
234
+ // };
235
+ // fragmentSource = 'propsConfiguration.fragment';
236
+ // }
237
+ // } catch (error) {
238
+ // console.error('Error extracting response from message:', error);
239
+ // }
240
+
241
+ // // console.log('Extracted response fragment source:', fragmentSource);
242
+ // // console.log('Extracted response:', extracted);
243
+
244
+ // if (extracted && (extracted.sandboxUrl || extracted.url)) {
245
+ // console.log('✅ Processing response update:', extracted);
246
+
247
+ // // Update subscription data snapshot
248
+ // setSubscriptionData(extracted);
249
+
250
+ // // Build active fragment
251
+ // const fragment = {
252
+ // id: message.id,
253
+ // sandboxUrl: extracted.sandboxUrl || extracted.url,
254
+ // sandboxId: extracted.sandboxId,
255
+ // title: extracted.title || 'Generated Code',
256
+ // files: extracted.files || {},
257
+ // projectId: extracted.projectId,
258
+ // modelConfig: extracted.modelConfig,
259
+ // canvasLayers: extracted.canvasLayers,
260
+ // summary: extracted.summary,
261
+ // isError: extracted.isError,
262
+ // };
263
+
264
+ // // If canvasLayers missing but present in summary, attempt to extract like use-ai-messages
265
+ // if ((!fragment.canvasLayers || !Array.isArray(fragment.canvasLayers)) && fragment.summary) {
266
+ // try {
267
+ // const match = (fragment.summary as string).match(/<canvas_layers>([\s\S]*?)<\/canvas_layers>/);
268
+ // if (match) {
269
+ // const parsed = JSON.parse(match[1]);
270
+ // if (Array.isArray(parsed)) {
271
+ // fragment.canvasLayers = parsed;
272
+ // }
273
+ // }
274
+ // } catch (e) {
275
+ // console.warn('Failed to parse canvas layers from summary:', e);
276
+ // }
277
+ // }
278
+
279
+ // console.log('✅ Setting active fragment via XState:', fragment);
280
+ // send({ type: 'SET_FRAGMENT', fragment });
281
+ // // setIsLoading?.(false);
282
+ // //send({ type: 'SET_LOADING', isLoading: false });
283
+ // } else {
284
+ // console.log('❌ No valid response found in message');
285
+ // // setIsLoading?.(false);
286
+ // // send({ type: 'SET_LOADING', isLoading: false });
287
+ // }
288
+ // }
289
+ // }, [chatMessageAddedData, selectedPost, send, setIsLoading]);
290
+
291
+ // // When switching tabs or when sandbox changes, show loader until ready
292
+ // // React.useEffect(() => {
293
+ // // if (isPreviewMode) {
294
+ // // if (activeFragment?.sandboxUrl) {
295
+ // // // setIsLoading(true);
296
+ // // }
297
+ // // } else {
298
+ // // // In code view, briefly show loader while editor reacts to new files
299
+ // // // setIsLoading(true);
300
+ // // const timeoutId = setTimeout(() => setIsLoading(false), 400);
301
+ // // return () => clearTimeout(timeoutId);
302
+ // // }
303
+ // // }, [isPreviewMode, activeFragment?.sandboxUrl, completeFileContext]);
304
+
305
+ // // Cleanup iframe resources on unmount to prevent renderer leaks in Chrome
306
+ // React.useEffect(() => {
307
+ // return () => {
308
+ // try {
309
+ // if (previewIframeRef.current) {
310
+ // previewIframeRef.current.src = 'about:blank';
311
+ // }
312
+ // } catch (e) {
313
+ // // no-op
314
+ // }
315
+ // };
316
+ // }, []);
317
+
318
+ // React.useEffect(() => {
319
+ // const handleVisualEditorMessage = (event: MessageEvent) => {
320
+ // // Handle AI changes
321
+ // if (event.data.type === 'VISUAL_EDITOR_CHANGES') {
322
+ // console.log('visual_editor_changes....', event.data.message);
323
+
324
+ // if (handleSendMessage && event.data.message) {
325
+ // // Call aiHandleSend directly - this is the handleSend function from AIAgent
326
+ // handleSendMessage({
327
+ // channelId,
328
+ // type: RoomType.Aiassistant,
329
+ // postData: {
330
+ // // postId: postId,
331
+ // type: PostTypeEnum.Aiassistant,
332
+ // content: event.data.message,
333
+ // },
334
+ // });
335
+ // } else {
336
+ // console.warn('⚠️ handleSendMessage not available or no message provided');
337
+ // }
338
+ // }
339
+ // };
340
+
341
+ // window.addEventListener('message', handleVisualEditorMessage);
342
+ // return () => window.removeEventListener('message', handleVisualEditorMessage);
343
+ // }, [handleSendMessage, channelId]);
344
+
345
+ // const recreateSandboxHandler = React.useCallback(
346
+ // async (id: string) => {
347
+ // if (id) {
348
+ // console.log('recreate sandbox id....', id);
349
+ // setIsLoading?.(true);
350
+ // send({ type: 'SET_LOADING', isLoading: true });
351
+ // try {
352
+ // send({ type: 'RECREATE_SANDBOX', id });
353
+ // } catch (e: any) {
354
+ // console.error('Error recreating sandbox:', e);
355
+ // setIsLoading?.(false);
356
+ // send({ type: 'SET_LOADING', isLoading: false });
357
+ // }
358
+ // }
359
+ // },
360
+ // [send, setIsLoading],
361
+ // );
362
+
363
+ // // Track if we've already attempted recreate for a given sandbox URL
364
+ // const recreateAttemptedByUrlRef = React.useRef<Record<string, boolean>>({});
365
+
366
+ // // Probe sandbox URL via XState actor
367
+ // React.useEffect(() => {
368
+ // const url = activeFragment?.sandboxUrl;
369
+ // if (url) {
370
+ // send({ type: 'PROBE', url, fragmentId: activeFragment?.id });
371
+ // }
372
+ // }, [activeFragment?.sandboxUrl, activeFragment?.id, send]);
373
+
374
+ // // Prefer using external loading when provided; otherwise fall back to machine state
375
+ // const derivedIsLoading = (typeof isLoading === 'boolean' ? isLoading : state.context.isLoading) as boolean;
376
+
377
+ // // Sync external loading prop with machine's loading state (no-op if setter not provided)
378
+ // // React.useEffect(() => {
379
+ // // console.log(
380
+ // // 'isLoading....',
381
+ // // isLoading,
382
+ // // ' state.context.isLoading',
383
+ // // state.context.isLoading,
384
+ // // ' previewStatus',
385
+ // // previewStatus,
386
+ // // );
387
+ // // if (!state.context.isLoading) {
388
+ // // setIsLoading?.(!!state.context.isLoading);
389
+ // // }
390
+ // // }, [state.context.isLoading, setIsLoading, isLoading]);
391
+
392
+ // return (
393
+ // <div ref={previewIframeContainerRef} className="right-sidebar-content h-full flex flex-col">
394
+ // <div className="flex-1 overflow-hidden" style={{ minHeight: windowHeight - 140 }}>
395
+ // {isPreviewMode ? (
396
+ // // Preview Mode - Show sandbox iframe or fallback
397
+ // (() => {
398
+ // if (activeFragment?.sandboxUrl) {
399
+ // return (
400
+ // <div
401
+ // className="w-full h-full relative"
402
+ // style={{
403
+ // width: isMinimized ? windowWidth * 0.8 + 'px' : '100%',
404
+ // height: windowHeight - 140,
405
+ // backgroundColor: '#f0f0f0',
406
+ // }}
407
+ // >
408
+ // {!previewStatus ? (
409
+ // <iframe
410
+ // key={activeFragment.sandboxUrl}
411
+ // ref={previewIframeRef}
412
+ // src={activeFragment.sandboxUrl}
413
+ // className="w-full w-fit h-full h-fit border border-gray-200 rounded-md bg-white shadow-sm"
414
+ // title="Live Preview"
415
+ // sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-modals allow-orientation-lock allow-pointer-lock allow-presentation allow-storage-access-by-user-activation allow-top-navigation-by-user-activation"
416
+ // loading="lazy"
417
+ // onLoad={() => {
418
+ // console.log('🎯 Iframe loaded successfully');
419
+ // setIsLoading?.(false);
420
+ // setIsCreatingSandbox?.(false);
421
+ // send({ type: 'SET_LOADING', isLoading: false });
422
+ // }}
423
+ // onError={(e) => {
424
+ // console.error('❌ Iframe error:', e);
425
+ // setIsLoading?.(false);
426
+ // send({ type: 'SET_LOADING', isLoading: false });
427
+ // }}
428
+ // style={
429
+ // {
430
+ // // maxHeight: windowHeight - 140,
431
+ // // width: isMinimized ? windowWidth * 0.82 + 'px' : '100%',
432
+ // //padding: isMinimized ? '10px' : '0px',
433
+ // }
434
+ // }
435
+ // />
436
+ // ) : (
437
+ // <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
438
+ // {handleRecreateSandbox && (
439
+ // <>
440
+ // {derivedIsLoading || isCreatingSandbox ? (
441
+ // <div className="flex flex-col items-center justify-center gap-2">
442
+ // <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
443
+ // <p className="text-xs text-blue-700">Recreating...</p>
444
+ // </div>
445
+ // ) : (
446
+ // <div className="flex flex-col items-center justify-center gap-2">
447
+ // <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
448
+ // <p className="text-sm text-muted-foreground">
449
+ // {isLoading ? 'Recreating...' : 'Refresh'}{' '}
450
+ // {/* Sandbox is not available. */}
451
+ // </p>
452
+ // {/* <button
453
+ // type="button"
454
+ // className="themed-button focus-themed px-3 py-1.5 text-xs"
455
+ // onClick={() =>
456
+ // recreateSandboxHandler(activeFragment?.id)
457
+ // }
458
+ // >
459
+ // Recreate
460
+ // </button> */}
461
+ // </div>
462
+ // )}
463
+ // </>
464
+ // )}
465
+ // </div>
466
+ // )}
467
+ // <div className="absolute top-2 right-2 bg-green-100 text-green-800 px-2 py-1 rounded text-xs z-10">
468
+ // Live Preview
469
+ // </div>
470
+ // {derivedIsLoading ||
471
+ // (isCreatingSandbox && (
472
+ // <>
473
+ // <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
474
+ // <div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
475
+ // </div>
476
+ // <div className="absolute top-2 left-2 bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs z-30">
477
+ // Loading...
478
+ // </div>
479
+ // </>
480
+ // ))}
481
+ // </div>
482
+ // );
483
+ // } else {
484
+ // return (
485
+ // <div
486
+ // className="h-full flex items-center justify-center text-gray-500 border border-gray-200 rounded-md bg-gray-50 "
487
+ // style={{ width: '100%', height: '80vh' }}
488
+ // >
489
+ // <div className="text-center space-y-3">
490
+ // <p className="font-medium">No preview available</p>
491
+ // <div className="flex flex-col items-center justify-center space-y-3">
492
+ // {derivedIsLoading || isCreatingSandbox ? (
493
+ // <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
494
+ // ) : (
495
+ // <div className="w-8 h-8 bg-gray-300 rounded-full"></div>
496
+ // )}
497
+ // <p className="text-sm text-gray-400">
498
+ // {derivedIsLoading || isCreatingSandbox
499
+ // ? 'Generating code...'
500
+ // : 'Waiting for code generation...'}
501
+ // </p>
502
+ // </div>
503
+ // </div>
504
+ // </div>
505
+ // );
506
+ // }
507
+ // })()
508
+ // ) : (
509
+ // // Code Editor Mode - Show files from subscription or fallback
510
+ // <div
511
+ // className="h-full w-full relative"
512
+ // style={{
513
+ // minHeight: windowHeight - 140,
514
+ // height: windowHeight - 140,
515
+ // width: isMinimized ? windowWidth * 0.9 + 'px' : '100%',
516
+ // }}
517
+ // >
518
+ // <HybridLiveEditor
519
+ // ref={hybridLiveEditorRef}
520
+ // projectId={activeFragment?.projectId || channelId}
521
+ // fragmentId={activeFragment?.id || activeTab || 'default'}
522
+ // files={Object.keys(completeFileContext).length > 0 ? completeFileContext : {}}
523
+ // sandboxUrl={activeFragment?.sandboxUrl}
524
+ // onCopyToClipboard={handleCopyToClipboard}
525
+ // onFileUpdate={handleFileUpdate}
526
+ // readOnly={!activeFragment?.id}
527
+ // autoSave={false}
528
+ // debounceMs={500}
529
+ // //className={`h-full w-[100%] h-[75vh] border-b border-gray-200 rounded-md bg-white shadow-sm overflow-hidden`}
530
+ // className="h-full w-full"
531
+ // />
532
+ // {derivedIsLoading && (
533
+ // <>
534
+ // <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
535
+ // <div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
536
+ // </div>
537
+ // <div className="absolute top-4 right-4 bg-blue-100 border border-blue-400 text-blue-700 px-3 py-2 rounded text-sm z-30">
538
+ // <div className="flex items-center gap-2">
539
+ // <div className="animate-spin rounded-full h-4 w-4 border-b-2 border-blue-500"></div>
540
+ // Processing...
541
+ // </div>
542
+ // </div>
543
+ // </>
544
+ // )}
545
+ // </div>
546
+ // )}
547
+ // </div>
548
+
549
+ // {/* <div className="p-2 border-t bg-gray-50 text-xs text-gray-500">
550
+ // <div className="flex justify-between">
551
+ // <span>Mobile: {detailSidebarOptions.isMobileView ? 'Yes' : 'No'}</span>
552
+ // <span>Desktop: {detailSidebarOptions.isDesktopView ? 'Yes' : 'No'}</span>
553
+ // {activeFragment && (
554
+ // <span className="text-green-600">{activeFragment.isError ? 'Error' : 'Ready'}</span>
555
+ // )}
556
+ // </div>
557
+ // {activeFragment && (
558
+ // <div className="mt-1 text-xs">
559
+ // <span className="text-gray-400">Files: {Object.keys(completeFileContext).length}</span>
560
+ // {canvasLayers.length > 0 && (
561
+ // <span className="ml-2 text-gray-400">Layers: {canvasLayers.length}</span>
562
+ // )}
563
+ // </div>
564
+ // )}
565
+ // </div> */}
566
+ // </div>
567
+ // );
568
+ // };
569
+
570
+ export const RightSidebarFillComponent = (props: any) => {
571
+ return <></>;
568
572
  };