@messenger-box/tailwind-ui-inbox 10.0.3-alpha.122 → 10.0.3-alpha.124

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 (86) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/components/AIAgent/AIAgent.d.ts +2 -0
  3. package/lib/components/AIAgent/AIAgent.d.ts.map +1 -1
  4. package/lib/components/AIAgent/AIAgent.js +42 -26
  5. package/lib/components/AIAgent/AIAgent.js.map +1 -1
  6. package/lib/components/InboxMessage/InputComponent.d.ts +4 -1
  7. package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -1
  8. package/lib/components/InboxMessage/InputComponent.js +20 -304
  9. package/lib/components/InboxMessage/InputComponent.js.map +1 -1
  10. package/lib/components/InboxMessage/UploadImageButton.js +2 -6
  11. package/lib/components/InboxMessage/UploadImageButton.js.map +1 -1
  12. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +1 -0
  13. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -1
  14. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +15 -5
  15. package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -1
  16. package/lib/components/ModelConfigPanel.d.ts +10 -0
  17. package/lib/components/ModelConfigPanel.d.ts.map +1 -1
  18. package/lib/components/ModelConfigPanel.js +551 -2
  19. package/lib/components/ModelConfigPanel.js.map +1 -1
  20. package/lib/components/filler-components/RightSiderBar.d.ts +1 -0
  21. package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -1
  22. package/lib/components/filler-components/RightSiderBar.js +174 -140
  23. package/lib/components/filler-components/RightSiderBar.js.map +1 -1
  24. package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -1
  25. package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -1
  26. package/lib/config/env-config.d.ts +2 -0
  27. package/lib/config/env-config.d.ts.map +1 -1
  28. package/lib/config/env-config.js +7 -1
  29. package/lib/config/env-config.js.map +1 -1
  30. package/lib/container/AiLandingInput.d.ts.map +1 -1
  31. package/lib/container/AiLandingInput.js +11 -9
  32. package/lib/container/AiLandingInput.js.map +1 -1
  33. package/lib/container/Inbox.js +1 -1
  34. package/lib/container/Inbox.js.map +1 -1
  35. package/lib/container/InboxAiMessagesLoader.d.ts +1 -0
  36. package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -1
  37. package/lib/container/InboxAiMessagesLoader.js +4 -1
  38. package/lib/container/InboxAiMessagesLoader.js.map +1 -1
  39. package/lib/container/InboxContainer.d.ts +1 -0
  40. package/lib/container/InboxContainer.d.ts.map +1 -1
  41. package/lib/container/InboxContainer.js +1 -6
  42. package/lib/container/InboxContainer.js.map +1 -1
  43. package/lib/container/InboxWithAiLoader.d.ts +2 -0
  44. package/lib/container/InboxWithAiLoader.d.ts.map +1 -1
  45. package/lib/container/InboxWithAiLoader.js +8 -3
  46. package/lib/container/InboxWithAiLoader.js.map +1 -1
  47. package/lib/container/ServiceInbox.js +1 -1
  48. package/lib/container/ServiceInbox.js.map +1 -1
  49. package/lib/container/ThreadMessages.js +1 -1
  50. package/lib/container/ThreadMessages.js.map +1 -1
  51. package/lib/container/ThreadMessagesInbox.js +1 -1
  52. package/lib/container/ThreadMessagesInbox.js.map +1 -1
  53. package/lib/container/Threads.js +1 -1
  54. package/lib/container/Threads.js.map +1 -1
  55. package/lib/module.js +1 -1
  56. package/lib/module.js.map +1 -1
  57. package/lib/templates/InboxWithAi.d.ts +1 -0
  58. package/lib/templates/InboxWithAi.d.ts.map +1 -1
  59. package/lib/templates/InboxWithAi.js +9 -6
  60. package/lib/templates/InboxWithAi.js.map +1 -1
  61. package/lib/templates/InboxWithAi.tsx +7 -3
  62. package/lib/xstate/index.d.ts +3 -0
  63. package/lib/xstate/index.d.ts.map +1 -0
  64. package/lib/xstate/rightSidebar.machine.d.ts +4 -0
  65. package/lib/xstate/rightSidebar.machine.d.ts.map +1 -0
  66. package/lib/xstate/rightSidebar.machine.js +174 -0
  67. package/lib/xstate/rightSidebar.machine.js.map +1 -0
  68. package/lib/xstate/rightSidebar.types.d.ts +52 -0
  69. package/lib/xstate/rightSidebar.types.d.ts.map +1 -0
  70. package/package.json +4 -4
  71. package/src/components/AIAgent/AIAgent.tsx +35 -21
  72. package/src/components/InboxMessage/InputComponent.tsx +23 -375
  73. package/src/components/InboxMessage/UploadImageButton.tsx +4 -4
  74. package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +17 -0
  75. package/src/components/ModelConfigPanel.tsx +666 -0
  76. package/src/components/filler-components/RightSiderBar.tsx +189 -150
  77. package/src/components/slot-fill/right-sidebar-filler.tsx +1 -0
  78. package/src/config/env-config.ts +3 -1
  79. package/src/container/AiLandingInput.tsx +11 -111
  80. package/src/container/InboxAiMessagesLoader.tsx +13 -2
  81. package/src/container/InboxContainer.tsx +2 -8
  82. package/src/container/InboxWithAiLoader.tsx +8 -2
  83. package/src/templates/InboxWithAi.tsx +7 -3
  84. package/src/xstate/index.ts +2 -0
  85. package/src/xstate/rightSidebar.machine.ts +139 -0
  86. package/src/xstate/rightSidebar.types.ts +55 -0
@@ -1,7 +1,11 @@
1
1
  import * as React from 'react';
2
2
  import { HybridLiveEditor, HybridLiveEditorRef } from '../live-code-editor/hybrid-live-editor';
3
3
  import { useOnChatMessageAddedSubscription } from 'common/graphql';
4
- import { ICreateChannelInput } from 'common';
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';
5
9
 
6
10
  type RightSidebarProps = {
7
11
  channelId: string;
@@ -22,6 +26,7 @@ type RightSidebarProps = {
22
26
  setIsLoading?: (isLoading: boolean) => void;
23
27
  handleSendMessage?: (createChannelInput: ICreateChannelInput) => void;
24
28
  handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
29
+ isMinimized?: boolean;
25
30
  isLoading?: boolean;
26
31
  [key: string]: any;
27
32
  };
@@ -39,32 +44,49 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
39
44
  isLoading,
40
45
  handleSendMessage,
41
46
  handleRecreateSandbox,
47
+ isMinimized,
42
48
  } = props;
43
-
44
49
  const hybridLiveEditorRef = React.useRef<HybridLiveEditorRef>(null);
45
50
  const previewIframeRef = React.useRef<HTMLIFrameElement>(null);
46
-
47
- // State for subscription data and active fragment
51
+ const previewIframeContainerRef = React.useRef<HTMLIFrameElement>(null);
52
+ // State for subscription data
48
53
  const [subscriptionData, setSubscriptionData] = React.useState<any>(null);
49
- const [activeFragment, setActiveFragment] = React.useState<any>(null);
50
- const [currentFiles, setCurrentFiles] = React.useState<Record<string, string>>({});
51
- const [canvasLayers, setCanvasLayers] = React.useState<any[]>([]);
52
- const [sanboxRecreate, setSanboxRecreate] = React.useState(false);
53
- // const [isLoading, setIsLoading] = React.useState(false);
54
- const [previewStatus, setPreviewStatus] = React.useState<{ code?: number; message?: string } | null>(null);
55
- // Local loading fallback if parent didn't pass setIsLoading/isLoading
56
- const [localLoading, setLocalLoading] = React.useState(false);
57
- const loading = typeof isLoading === 'boolean' ? isLoading : localLoading;
58
- const setLoading = React.useCallback(
59
- (val: boolean) => {
60
- if (typeof setIsLoading === 'function') {
61
- setIsLoading(val);
62
- } else {
63
- setLocalLoading(val);
64
- }
54
+ // XState machine for sidebar state
55
+ const [state, send] = useMachine(
56
+ rightSidebarMachine.provide({
57
+ actors: {
58
+ recreateSandbox: fromPromise(async ({ input }) => {
59
+ const { id } = input as { id: string };
60
+ if (handleRecreateSandbox) {
61
+ await handleRecreateSandbox(id);
62
+ }
63
+ return { success: true } as const;
64
+ }),
65
+ },
66
+ }),
67
+ {
68
+ input: {
69
+ channelId: channelId || '',
70
+ isMinimized: !!isMinimized,
71
+ windowWidth,
72
+ windowHeight,
73
+ isLoading: !!isLoading,
74
+ activeFragment: null,
75
+ currentFiles: {},
76
+ canvasLayers: [],
77
+ previewStatus: null,
78
+ },
65
79
  },
66
- [setIsLoading],
67
80
  );
81
+ const activeFragment = state.context.activeFragment;
82
+ const currentFiles = state.context.currentFiles;
83
+ const canvasLayers = state.context.canvasLayers as any[];
84
+ const previewStatus = state.context.previewStatus as { code?: number; message?: string; status?: string } | null;
85
+ // const [isLoading, setIsLoading] = React.useState(false);
86
+ const { data: chatMessageAddedData } = useOnChatMessageAddedSubscription({
87
+ variables: { channelId: channelId?.toString() || '' },
88
+ skip: !channelId,
89
+ });
68
90
 
69
91
  // Component mount tracking
70
92
  const componentId = React.useMemo(
@@ -131,27 +153,20 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
131
153
  // Determine if we should show preview or editor based on activeTab
132
154
  const isPreviewMode = activeTab === 'preview';
133
155
 
134
- const { data: chatMessageAddedData } = useOnChatMessageAddedSubscription({
135
- variables: { channelId: channelId?.toString() || '' },
136
- skip: !channelId,
137
- });
138
-
139
156
  // Turn on loader when new data is about to be processed
140
157
  React.useEffect(() => {
141
158
  if (chatMessageAddedData?.chatMessageAdded || selectedPost) {
142
- // setIsLoading(true);
159
+ // sync external loader
160
+ // setIsLoading?.(false);
161
+ // send({ type: 'SET_LOADING', isLoading: false });
143
162
  }
144
- }, [chatMessageAddedData, selectedPost]);
163
+ }, [chatMessageAddedData, selectedPost, send, setIsLoading]);
145
164
 
146
165
  // Handle subscription data updates - prefer response update structure like use-ai-messages.ts
147
166
  React.useEffect(() => {
148
167
  if (chatMessageAddedData?.chatMessageAdded || selectedPost) {
149
168
  // Prefer explicitly selected post to ensure UI updates when it changes
150
169
  const message = selectedPost || chatMessageAddedData?.chatMessageAdded;
151
- // console.log('=== SUBSCRIPTION MESSAGE DEBUG ===');
152
- // console.log('Full message:', JSON.stringify(message, null, 2));
153
- // console.log('Message props:', message.props);
154
- // console.log('Message propsConfiguration:', message.propsConfiguration);
155
170
 
156
171
  // Extract strictly from message.propsConfiguration (fragment only)
157
172
  let extracted: any = null;
@@ -178,7 +193,6 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
178
193
  summary: contents.summary || cfg.summary || frag.summary,
179
194
  title: frag.title || contents.title || cfg.title,
180
195
  isError: frag.isError,
181
- syncStatus: frag.syncStatus,
182
196
  };
183
197
  fragmentSource = 'propsConfiguration.contents.props.fragment';
184
198
  }
@@ -196,7 +210,6 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
196
210
  summary: contents.summary || cfg.summary || frag.summary,
197
211
  title: frag.title || contents.title || cfg.title,
198
212
  isError: frag.isError,
199
- syncStatus: frag.syncStatus,
200
213
  };
201
214
  fragmentSource = 'propsConfiguration.contents.fragment';
202
215
  }
@@ -214,7 +227,6 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
214
227
  summary: cfg.summary || frag.summary,
215
228
  title: frag.title || cfg.title,
216
229
  isError: frag.isError,
217
- syncStatus: frag.syncStatus,
218
230
  };
219
231
  fragmentSource = 'propsConfiguration.fragment';
220
232
  }
@@ -235,6 +247,7 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
235
247
  const fragment = {
236
248
  id: message.id,
237
249
  sandboxUrl: extracted.sandboxUrl || extracted.url,
250
+ sandboxId: extracted.sandboxId,
238
251
  title: extracted.title || 'Generated Code',
239
252
  files: extracted.files || {},
240
253
  projectId: extracted.projectId,
@@ -242,7 +255,6 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
242
255
  canvasLayers: extracted.canvasLayers,
243
256
  summary: extracted.summary,
244
257
  isError: extracted.isError,
245
- syncStatus: extracted.syncStatus,
246
258
  };
247
259
 
248
260
  // If canvasLayers missing but present in summary, attempt to extract like use-ai-messages
@@ -260,69 +272,17 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
260
272
  }
261
273
  }
262
274
 
263
- console.log('✅ Setting active fragment:', fragment);
264
- setActiveFragment(fragment);
265
-
266
- // Update files into local state for editor
267
- if (fragment.files) {
268
- console.log('✅ Setting current files:', Object.keys(fragment.files));
269
- setCurrentFiles(fragment.files);
270
- }
271
-
272
- // Update canvas layers if available
273
- if (fragment.canvasLayers && Array.isArray(fragment.canvasLayers)) {
274
- console.log('Received canvas layers from AI:', fragment.canvasLayers);
275
- setCanvasLayers(fragment.canvasLayers);
276
- } else {
277
- console.log('No canvas layers found in response');
278
- }
279
-
280
- // setIsLoading(false);
275
+ console.log('✅ Setting active fragment via XState:', fragment);
276
+ send({ type: 'SET_FRAGMENT', fragment });
277
+ // setIsLoading?.(false);
278
+ //send({ type: 'SET_LOADING', isLoading: false });
281
279
  } else {
282
280
  console.log('❌ No valid response found in message');
283
- // setIsLoading(false);
281
+ // setIsLoading?.(false);
282
+ // send({ type: 'SET_LOADING', isLoading: false });
284
283
  }
285
284
  }
286
- }, [chatMessageAddedData, selectedPost]);
287
-
288
- // Probe sandbox URL to detect server-side errors (e.g., 502) before rendering iframe
289
- React.useEffect(() => {
290
- const url = activeFragment?.sandboxUrl;
291
- setPreviewStatus(null);
292
- if (!url) return;
293
-
294
- const controller = new AbortController();
295
- const signal = controller.signal;
296
- const run = async () => {
297
- try {
298
- //setLoading(true);
299
- // timeout to avoid indefinite loading
300
- const timeoutId = setTimeout(() => controller.abort(), 10000);
301
- const res = await fetch(url, {
302
- method: 'GET',
303
- mode: 'cors',
304
- redirect: 'follow',
305
- signal,
306
- });
307
- clearTimeout(timeoutId);
308
- console.log('res....', res);
309
- if (!res.ok) {
310
- setPreviewStatus({ code: res.status, message: res.statusText });
311
- if (!sanboxRecreate) setSanboxRecreate(true);
312
- } else {
313
- setPreviewStatus(null);
314
- }
315
- } catch (e: any) {
316
- // Network/CORS errors will end up here
317
- setPreviewStatus({ message: 'Network error' });
318
- // if(!sanboxRecreate) setSanboxRecreate(true);
319
- } finally {
320
- setLoading(false);
321
- }
322
- };
323
- run();
324
- return () => controller.abort();
325
- }, [activeFragment?.sandboxUrl, sanboxRecreate]);
285
+ }, [chatMessageAddedData, selectedPost, send, setIsLoading]);
326
286
 
327
287
  // When switching tabs or when sandbox changes, show loader until ready
328
288
  // React.useEffect(() => {
@@ -351,92 +311,158 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
351
311
  };
352
312
  }, []);
353
313
 
354
- const sandboxExpiryCheck = React.useCallback(() => {
355
- setSanboxRecreate(false);
356
- if (activeFragment?.id && handleRecreateSandbox) {
357
- Promise.resolve(handleRecreateSandbox(activeFragment?.id))
358
- .then((data: any) => {
359
- console.log('recreateSandbox data', data);
360
- setSanboxRecreate(false);
361
- })
362
- .catch(() => {
363
- setSanboxRecreate(false);
364
- });
314
+ React.useEffect(() => {
315
+ const handleVisualEditorMessage = (event: MessageEvent) => {
316
+ // Handle AI changes
317
+ if (event.data.type === 'VISUAL_EDITOR_CHANGES') {
318
+ console.log('visual_editor_changes....', event.data.message);
319
+
320
+ if (handleSendMessage && event.data.message) {
321
+ // Call aiHandleSend directly - this is the handleSend function from AIAgent
322
+ handleSendMessage({
323
+ channelId,
324
+ type: RoomType.Aiassistant,
325
+ postData: {
326
+ // postId: postId,
327
+ type: PostTypeEnum.Aiassistant,
328
+ content: event.data.message,
329
+ },
330
+ });
331
+ } else {
332
+ console.warn('⚠️ handleSendMessage not available or no message provided');
333
+ }
334
+ }
335
+ };
336
+
337
+ window.addEventListener('message', handleVisualEditorMessage);
338
+ return () => window.removeEventListener('message', handleVisualEditorMessage);
339
+ }, [handleSendMessage, channelId]);
340
+
341
+ const recreateSandboxHandler = React.useCallback(
342
+ async (id: string) => {
343
+ if (id) {
344
+ console.log('recreate sandbox id....', id);
345
+ setIsLoading?.(true);
346
+ send({ type: 'SET_LOADING', isLoading: true });
347
+ try {
348
+ send({ type: 'RECREATE_SANDBOX', id });
349
+ } catch (e: any) {
350
+ console.error('Error recreating sandbox:', e);
351
+ setIsLoading?.(false);
352
+ send({ type: 'SET_LOADING', isLoading: false });
353
+ }
354
+ }
355
+ },
356
+ [send, setIsLoading],
357
+ );
358
+
359
+ // Track if we've already attempted recreate for a given sandbox URL
360
+ const recreateAttemptedByUrlRef = React.useRef<Record<string, boolean>>({});
361
+
362
+ // Probe sandbox URL via XState actor
363
+ React.useEffect(() => {
364
+ const url = activeFragment?.sandboxUrl;
365
+ if (url) {
366
+ send({ type: 'PROBE', url, fragmentId: activeFragment?.id });
365
367
  }
366
- }, [activeFragment?.id, handleRecreateSandbox, setSanboxRecreate]);
368
+ }, [activeFragment?.sandboxUrl, activeFragment?.id, send]);
369
+
370
+ // Prefer using external loading when provided; otherwise fall back to machine state
371
+ const derivedIsLoading = (typeof isLoading === 'boolean' ? isLoading : state.context.isLoading) as boolean;
367
372
 
373
+ // Sync external loading prop with machine's loading state (no-op if setter not provided)
368
374
  React.useEffect(() => {
369
- if (sanboxRecreate) {
370
- sandboxExpiryCheck();
375
+ console.log(
376
+ 'isLoading....',
377
+ isLoading,
378
+ ' state.context.isLoading',
379
+ state.context.isLoading,
380
+ ' previewStatus',
381
+ previewStatus,
382
+ );
383
+ if (!state.context.isLoading) {
384
+ setIsLoading?.(!!state.context.isLoading);
371
385
  }
372
- }, [sanboxRecreate, sandboxExpiryCheck]);
386
+ }, [state.context.isLoading, setIsLoading, isLoading]);
373
387
 
374
388
  return (
375
- <div className="right-sidebar-content h-full flex flex-col">
376
- <div className="flex-1 overflow-hidden" style={{ height: 'calc(100vh - 140px)' }}>
389
+ <div ref={previewIframeContainerRef} className="right-sidebar-content h-full flex flex-col">
390
+ <div className="flex-1 overflow-hidden" style={{ minHeight: windowHeight - 140 }}>
377
391
  {isPreviewMode ? (
378
392
  // Preview Mode - Show sandbox iframe or fallback
379
393
  (() => {
380
394
  if (activeFragment?.sandboxUrl) {
381
395
  return (
382
396
  <div
383
- className="w-full h-full w-[80vw] relative"
397
+ className="w-full h-full relative"
384
398
  style={{
385
- //minHeight: '400px',
399
+ width: isMinimized ? windowWidth * 0.8 + 'px' : '100%',
400
+ height: windowHeight - 140,
386
401
  backgroundColor: '#f0f0f0',
387
- minHeight: 'calc(100vh - 140px)',
388
- width: '100%',
389
402
  }}
390
403
  >
391
404
  {!previewStatus ? (
392
405
  <iframe
393
406
  key={activeFragment.sandboxUrl}
394
407
  ref={previewIframeRef}
395
- src={!previewStatus ? activeFragment.sandboxUrl : 'about:blank'}
396
- className="w-full h-full border border-gray-200 rounded-md bg-white shadow-sm"
408
+ src={activeFragment.sandboxUrl}
409
+ className="w-full w-fit h-full h-fit border border-gray-200 rounded-md bg-white shadow-sm"
397
410
  title="Live Preview"
398
411
  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"
399
412
  loading="lazy"
400
413
  onLoad={() => {
401
414
  console.log('🎯 Iframe loaded successfully');
402
- setLoading(false);
403
- setPreviewStatus(null);
415
+ setIsLoading?.(false);
416
+ send({ type: 'SET_LOADING', isLoading: false });
404
417
  }}
405
418
  onError={(e) => {
406
419
  console.error('❌ Iframe error:', e);
407
- setLoading(false);
408
- }}
409
- style={{
410
- // minHeight: '400px'
411
- minHeight: 'calc(100vh - 140px)',
420
+ setIsLoading?.(false);
421
+ send({ type: 'SET_LOADING', isLoading: false });
412
422
  }}
423
+ style={
424
+ {
425
+ // maxHeight: windowHeight - 140,
426
+ // width: isMinimized ? windowWidth * 0.82 + 'px' : '100%',
427
+ //padding: isMinimized ? '10px' : '0px',
428
+ }
429
+ }
413
430
  />
414
431
  ) : (
415
- <div className="absolute inset-0 flex items-center justify-center bg-white border border-gray-200 rounded-md">
416
- <div className="text-center space-y-3 px-4">
417
- <div className="flex items-center justify-center gap-2 pt-1">
418
- <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
419
- <div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
420
- </div>
421
- <div className="absolute top-2 left-2 bg-blue-100 text-blue-800 px-2 py-1 rounded text-xs z-30">
422
- Loading...
423
- </div>
424
- {/* {handleRecreateSandbox && (
425
- <button
426
- className="px-3 py-1.5 text-xs rounded bg-blue-600 text-white hover:bg-blue-700"
427
- onClick={() => handleRecreateSandbox(activeFragment?.id)}
428
- >
429
- Recreate sandbox
430
- </button>
431
- )} */}
432
- </div>
433
- </div>
432
+ <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
433
+ {handleRecreateSandbox && (
434
+ <>
435
+ {derivedIsLoading ? (
436
+ <div className="flex flex-col items-center justify-center gap-2">
437
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
438
+ <p className="text-xs text-blue-700">Recreating...</p>
439
+ </div>
440
+ ) : (
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-sm text-muted-foreground">
444
+ {isLoading ? 'Recreating...' : 'Refresh'}{' '}
445
+ {/* Sandbox is not available. */}
446
+ </p>
447
+ {/* <button
448
+ type="button"
449
+ className="themed-button focus-themed px-3 py-1.5 text-xs"
450
+ onClick={() =>
451
+ recreateSandboxHandler(activeFragment?.id)
452
+ }
453
+ >
454
+ Recreate
455
+ </button> */}
456
+ </div>
457
+ )}
458
+ </>
459
+ )}
434
460
  </div>
435
461
  )}
436
462
  <div className="absolute top-2 right-2 bg-green-100 text-green-800 px-2 py-1 rounded text-xs z-10">
437
463
  Live Preview
438
464
  </div>
439
- {loading && (
465
+ {derivedIsLoading && (
440
466
  <>
441
467
  <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
442
468
  <div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
@@ -450,17 +476,22 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
450
476
  );
451
477
  } else {
452
478
  return (
453
- <div className="h-full flex items-center justify-center text-gray-500 border border-gray-200 rounded-md bg-gray-50">
479
+ <div
480
+ className="h-full flex items-center justify-center text-gray-500 border border-gray-200 rounded-md bg-gray-50 "
481
+ style={{ width: '100%', height: '80vh' }}
482
+ >
454
483
  <div className="text-center space-y-3">
455
484
  <p className="font-medium">No preview available</p>
456
485
  <div className="flex flex-col items-center justify-center space-y-3">
457
- {loading ? (
486
+ {derivedIsLoading ? (
458
487
  <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
459
488
  ) : (
460
489
  <div className="w-8 h-8 bg-gray-300 rounded-full"></div>
461
490
  )}
462
491
  <p className="text-sm text-gray-400">
463
- {loading ? 'Generating code...' : 'Waiting for code generation...'}
492
+ {derivedIsLoading
493
+ ? 'Generating code...'
494
+ : 'Waiting for code generation...'}
464
495
  </p>
465
496
  </div>
466
497
  </div>
@@ -470,7 +501,14 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
470
501
  })()
471
502
  ) : (
472
503
  // Code Editor Mode - Show files from subscription or fallback
473
- <div className="h-full w-full relative" style={{ height: '100%', minHeight: '500px' }}>
504
+ <div
505
+ className="h-full w-full relative"
506
+ style={{
507
+ minHeight: windowHeight - 140,
508
+ height: windowHeight - 140,
509
+ width: isMinimized ? windowWidth * 0.9 + 'px' : '100%',
510
+ }}
511
+ >
474
512
  <HybridLiveEditor
475
513
  ref={hybridLiveEditorRef}
476
514
  projectId={activeFragment?.projectId || channelId}
@@ -482,9 +520,10 @@ export const RightSidebarFillComponent = (props: RightSidebarProps) => {
482
520
  readOnly={!activeFragment?.id}
483
521
  autoSave={false}
484
522
  debounceMs={500}
523
+ //className={`h-full w-[100%] h-[75vh] border-b border-gray-200 rounded-md bg-white shadow-sm overflow-hidden`}
485
524
  className="h-full w-full"
486
525
  />
487
- {loading && (
526
+ {derivedIsLoading && (
488
527
  <>
489
528
  <div className="absolute inset-0 bg-white/60 flex items-center justify-center z-20">
490
529
  <div className="animate-spin rounded-full h-12 w-12 border-4 border-blue-500 border-t-transparent"></div>
@@ -23,6 +23,7 @@ type RightSidebarProps = {
23
23
  setIsLoading?: (isLoading: boolean) => void;
24
24
  handleSendMessage?: (createChannelInput: ICreateChannelInput) => void;
25
25
  handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
26
+ isMinimized?: boolean;
26
27
  isLoading?: boolean;
27
28
  [key: string]: any;
28
29
  };
@@ -1,4 +1,4 @@
1
- import { cleanEnv, num, str } from 'envalid';
1
+ import { cleanEnv, json, num, str } from 'envalid';
2
2
  import { getEnvironment } from '@common-stack/core';
3
3
 
4
4
  const env = getEnvironment();
@@ -19,4 +19,6 @@ export const config = cleanEnv(env, {
19
19
  NEWS_API_KEY: str({ default: '' }),
20
20
  OPENAI_API_KEY: str({ default: '' }),
21
21
  AI_MESSENGER_PATH: str({ default: '/ai-messenger/app' }),
22
+ LAYOUT_SETTINGS: json({ default: {} }),
23
+ APP_NAME: str({ default: 'Messenger Box' }),
22
24
  });