@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
@@ -22,31 +22,19 @@ const AiLandingInput: React.FC = () => {
22
22
  const [isCreatingChannel, setIsCreatingChannel] = React.useState(false);
23
23
  const { modelConfig, updateModelConfig, getValidatedConfig, hasApiKey } = usePersistentModelConfig();
24
24
  // const [showModelConfig, setShowModelConfig] = React.useState(!hasApiKey);
25
- console.log('modelConfig...', modelConfig);
25
+
26
26
  return (
27
- <div className="flex items-center justify-center min-h-screen bg-gray-50">
27
+ <div className="flex items-center justify-center min-h-screen bg-white shadow-lg">
28
28
  <div className="w-full max-w-2xl mx-4">
29
- <div className="bg-white rounded-lg shadow-lg p-6">
29
+ <div className="bg-white rounded-lg shadow-lg p-6 w-full max-w-4xl overflow-hidden rounded-[28px] border border-gray-200 bg-card ">
30
30
  <div className="text-center mb-6">
31
- <h3 className="text-xl font-semibold text-gray-700 mb-2">What would you like to build?</h3>
31
+ <h3 className="text-xl font-semibold text-gray-700 mb-2">{`${
32
+ (config?.LAYOUT_SETTINGS as any)?.title ?? 'What would you like to build?'
33
+ }`}</h3>
32
34
  <p className="text-gray-500">Describe your idea and I'll help you create it step by step.</p>
33
35
  </div>
34
36
  <div className="mb-0">
35
- <div className={` ${!hasApiKey ? 'p-3 bg-gray-50 rounded-lg border' : ''}`}>
36
- {/* <div className="mb-4">
37
- <h3 className="text-sm font-medium text-gray-900 mb-2">Configuration</h3>
38
- <p className="text-xs text-gray-600 mb-3">
39
- Choose your AI model, template, and provide your API key before creating your
40
- project.
41
- </p>
42
- </div> */}
43
- {/* <ModelConfigPanel
44
- config={modelConfig}
45
- onConfigChange={updateModelConfig}
46
- isVisible={showModelConfig}
47
- onToggleVisibility={() => setShowModelConfig(!showModelConfig)}
48
- showTemplate={true}
49
- /> */}
37
+ <div className={` ${!hasApiKey ? 'p-3 bg-white rounded-lg border' : ''}`}>
50
38
  {!hasApiKey && (
51
39
  <>
52
40
  <div className="mb-4">
@@ -64,6 +52,8 @@ const AiLandingInput: React.FC = () => {
64
52
  </div>
65
53
  </div>
66
54
  <InputComponent
55
+ // textareaClassName="border-none focus:border-none active:border-none focus:outline-none focus:ring-0"
56
+ // toolBarParentClassName="border-t border-gray-200 focus:border-t-gray-400"
67
57
  handleSend={async (message: string, files: File[]) => {
68
58
  const validated = getValidatedConfig();
69
59
  if (!validated && !hasApiKey && !modelConfig?.extensionId && !modelConfig?.formId) {
@@ -156,99 +146,9 @@ const AiLandingInput: React.FC = () => {
156
146
  modelConfig={modelConfig}
157
147
  onModelConfigChange={updateModelConfig}
158
148
  />
159
- {/* <div className="mt-2">
160
- <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
161
- <div className="space-y-2">
162
- <div className="relative">
163
- <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
164
- <div className="w-5 h-5 bg-purple-500 rounded-full flex items-center justify-center">
165
- <span className="text-white text-xs font-bold">N</span>
166
- </div>
167
- </div>
168
- <select className="block w-full pl-10 pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-purple-500 bg-white">
169
- <option value="nextjs">Next.js</option>
170
- <option value="vuejs">Vue.js</option>
171
- <option value="vite-remix">Vite with Remix</option>
172
- </select>
173
- <div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
174
- <svg
175
- className="h-5 w-5 text-gray-400"
176
- fill="none"
177
- stroke="currentColor"
178
- viewBox="0 0 24 24"
179
- >
180
- <path
181
- strokeLinecap="round"
182
- strokeLinejoin="round"
183
- strokeWidth={2}
184
- d="M19 9l-7 7-7-7"
185
- />
186
- </svg>
187
- </div>
188
- </div>
189
- </div>
190
- <div className="space-y-2">
191
- <div className="relative">
192
- <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
193
- <div className="w-5 h-5 bg-pink-500 rounded-full flex items-center justify-center">
194
- <svg className="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
195
- <path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
196
- </svg>
197
- </div>
198
- </div>
199
- <select className="block w-full pl-10 pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-pink-500 focus:border-pink-500 bg-white">
200
- <option value="claude-3-5-sonnet">Claude 3.5 Sonnet</option>
201
- <option value="claude-3-5-haiku">Claude 3.5 Haiku</option>
202
- <option value="claude-3-opus">Claude 3 Opus</option>
203
- <option value="claude-3-sonnet">Claude 3 Sonnet</option>
204
- <option value="claude-3-haiku">Claude 3 Haiku</option>
205
- </select>
206
- <div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
207
- <svg
208
- className="h-5 w-5 text-gray-400"
209
- fill="none"
210
- stroke="currentColor"
211
- viewBox="0 0 24 24"
212
- >
213
- <path
214
- strokeLinecap="round"
215
- strokeLinejoin="round"
216
- strokeWidth={2}
217
- d="M19 9l-7 7-7-7"
218
- />
219
- </svg>
220
- </div>
221
- </div>
222
- </div>
223
- <div className="space-y-2">
224
- <div className="relative">
225
- <select className="block w-full pr-10 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white">
226
- <option value="">API Key</option>
227
- <option value="saved-key-1">••••••••••••</option>
228
- <option value="saved-key-2">••••••••••••</option>
229
- </select>
230
- <div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
231
- <svg
232
- className="h-5 w-5 text-gray-400"
233
- fill="none"
234
- stroke="currentColor"
235
- viewBox="0 0 24 24"
236
- >
237
- <path
238
- strokeLinecap="round"
239
- strokeLinejoin="round"
240
- strokeWidth={2}
241
- d="M19 9l-7 7-7-7"
242
- />
243
- </svg>
244
- </div>
245
- </div>
246
- </div>
247
- </div>
248
- </div> */}
249
- <div className="text-center text-sm text-gray-400">
149
+ {/* <div className="text-center text-sm text-gray-400">
250
150
  Each project gets its own workspace where you can iterate and refine your ideas.
251
- </div>
151
+ </div> */}
252
152
  </div>
253
153
  </div>
254
154
  {isCreatingChannel && <TailwindOverlaySpinner />}
@@ -11,13 +11,22 @@ interface InboxWithAiLoaderOutletProps {
11
11
  supportServices?: boolean;
12
12
  pathPrefix?: string;
13
13
  orgName?: string;
14
+ showDateSeparators?: boolean;
14
15
  [key: string]: any; // Allow other props to be passed through to Inbox
15
16
  }
16
17
 
17
18
  const InboxWithAiLoaderOutlet = (props: InboxWithAiLoaderOutletProps) => {
18
19
  const location = useLocation();
19
- const { messages, setMessages, selectedPost, setSelectedPost, setIsLoading, isLoading, sendMessageInput } =
20
- useOutletContext() as any;
20
+ const {
21
+ messages,
22
+ setMessages,
23
+ selectedPost,
24
+ setSelectedPost,
25
+ setIsLoading,
26
+ isLoading,
27
+ sendMessageInput,
28
+ isShowOnlyInbox,
29
+ } = useOutletContext() as any;
21
30
  const urlParams = location?.search ? new URLSearchParams(location.search) : null;
22
31
  const { id: pathChannelId } = useParams();
23
32
  const channelId = urlParams?.get('id') || pathChannelId;
@@ -36,6 +45,8 @@ const InboxWithAiLoaderOutlet = (props: InboxWithAiLoaderOutletProps) => {
36
45
  setIsLoading={setIsLoading}
37
46
  isLoading={isLoading}
38
47
  sendMessageInput={sendMessageInput}
48
+ isShowOnlyInbox={isShowOnlyInbox}
49
+ showDateSeparators={props?.showDateSeparators || false}
39
50
  {...props}
40
51
  />
41
52
  );
@@ -8,6 +8,7 @@ interface InboxContainerProps {
8
8
  supportServices?: boolean;
9
9
  orgName?: string;
10
10
  handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
11
+ isShowOnlyInbox?: boolean;
11
12
  [key: string]: any;
12
13
  }
13
14
 
@@ -22,14 +23,7 @@ const InboxSkeleton = React.memo(({ showRightSidebar = false }: { showRightSideb
22
23
  ));
23
24
 
24
25
  const InboxContainer: React.FC<InboxContainerProps> = (props) => {
25
- const {
26
- channelId,
27
- channelFilters: channelFilterProp,
28
- channelRole,
29
- supportServices,
30
- orgName,
31
- handleRecreateSandbox,
32
- } = props;
26
+ const { channelId } = props;
33
27
 
34
28
  if (!channelId) {
35
29
  return <InboxSkeleton showRightSidebar={true} />;
@@ -38,11 +38,13 @@ interface InboxWithAiLoaderProps {
38
38
  pathPrefix?: string;
39
39
  orgName?: string;
40
40
  handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
41
+ isShowOnlyInbox?: boolean;
42
+ isShowAiLanding?: boolean;
41
43
  [key: string]: any;
42
44
  }
43
45
 
44
46
  const InboxWithAiLoader = (props: InboxWithAiLoaderProps) => {
45
- const { channelRole: channelRoleProp, pathPrefix } = props;
47
+ const { channelRole: channelRoleProp, pathPrefix, isShowAiLanding = true } = props;
46
48
  const { orgName, channelRole: channelRoleParam } = useParams();
47
49
  const location = useLocation();
48
50
  const urlParams = location?.search ? new URLSearchParams(location.search) : null;
@@ -53,6 +55,7 @@ const InboxWithAiLoader = (props: InboxWithAiLoaderProps) => {
53
55
  orgName: orgName || '',
54
56
  pathPrefix: pathPrefix || '',
55
57
  handleRecreateSandbox: props.handleRecreateSandbox,
58
+ isShowOnlyInbox: props?.isShowOnlyInbox || false,
56
59
  }),
57
60
  [props, orgName, pathPrefix],
58
61
  );
@@ -63,7 +66,10 @@ const InboxWithAiLoader = (props: InboxWithAiLoaderProps) => {
63
66
  const channelId = urlParams?.get('id') || pathChannelId;
64
67
 
65
68
  // Render the input landing experience when no channel is selected
66
- if (!channelId) {
69
+ if (!isShowAiLanding && !channelId) {
70
+ return <div>Not Found</div>;
71
+ }
72
+ if (!channelId && isShowAiLanding) {
67
73
  return <AiLandingInput />;
68
74
  }
69
75
 
@@ -33,6 +33,7 @@ export interface InboxProps {
33
33
  data?: any;
34
34
  orgName?: string;
35
35
  handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
36
+ isShowOnlyInbox?: boolean;
36
37
  }
37
38
 
38
39
  // Static utility hooks and components
@@ -198,7 +199,7 @@ const InboxWithAiInternal = (props: InboxProps) => {
198
199
  {/* Main Container */}
199
200
  <div className="flex flex-col h-screen overflow-hidden">
200
201
  {/* Header with Design/Interact/Code tabs - now inside main container */}
201
- {channelId && (
202
+ {channelId && !props?.isShowOnlyInbox && (
202
203
  <div className="flex-shrink-0 bg-white ">
203
204
  <div className="flex items-center justify-between px-4 py-3">
204
205
  <div className="flex items-center">
@@ -332,7 +333,7 @@ const InboxWithAiInternal = (props: InboxProps) => {
332
333
  <div
333
334
  className="flex-1 grid overflow-hidden"
334
335
  style={{
335
- gridTemplateColumns: isDesktopView ? '35% 65%' : '1fr',
336
+ gridTemplateColumns: isDesktopView ? (props?.isShowOnlyInbox ? '1fr' : '35% 65%') : '1fr',
336
337
  height: 'calc(100vh - 80px)', // Subtract header height
337
338
  }}
338
339
  >
@@ -356,6 +357,7 @@ const InboxWithAiInternal = (props: InboxProps) => {
356
357
  setIsLoading={setIsLoading}
357
358
  isLoading={isLoading}
358
359
  sendMessageInput={sendMessageInput}
360
+ isShowOnlyInbox={props?.isShowOnlyInbox}
359
361
  />
360
362
  ) : (
361
363
  <EmptyState />
@@ -363,7 +365,7 @@ const InboxWithAiInternal = (props: InboxProps) => {
363
365
  </div>
364
366
 
365
367
  {/* Right Sidebar - 65% width on desktop only */}
366
- {channelId && isDesktopView && (
368
+ {channelId && isDesktopView && !props?.isShowOnlyInbox && (
367
369
  <div
368
370
  className="overflow-hidden border flex-1 w-full"
369
371
  style={{
@@ -406,6 +408,7 @@ const ContentComponent = React.memo((props: any) => {
406
408
  setIsLoading,
407
409
  isLoading,
408
410
  sendMessageInput,
411
+ isShowOnlyInbox,
409
412
  } = props;
410
413
 
411
414
  return (
@@ -426,6 +429,7 @@ const ContentComponent = React.memo((props: any) => {
426
429
  setIsLoading={setIsLoading}
427
430
  isLoading={isLoading}
428
431
  sendMessageInput={sendMessageInput}
432
+ isShowOnlyInbox={isShowOnlyInbox}
429
433
  />
430
434
  </>
431
435
  )}
@@ -0,0 +1,2 @@
1
+ export * from './rightSidebar.types';
2
+ export * from './rightSidebar.machine';
@@ -0,0 +1,139 @@
1
+ import { createMachine, assign, fromPromise } from 'xstate';
2
+ import type { RightSidebarContext, MachineEvent } from './rightSidebar.types';
3
+
4
+ export const rightSidebarMachine = createMachine({
5
+ id: 'rightSidebar',
6
+ types: {} as {
7
+ context: RightSidebarContext;
8
+ events: MachineEvent;
9
+ input: Partial<RightSidebarContext>;
10
+ },
11
+ context: ({ input }) => ({
12
+ channelId: input?.channelId ?? '',
13
+ isMinimized: input?.isMinimized ?? false,
14
+ windowWidth: input?.windowWidth ?? 0,
15
+ windowHeight: input?.windowHeight ?? 0,
16
+ isLoading: input?.isLoading ?? false,
17
+ activeFragment: input?.activeFragment ?? null,
18
+ currentFiles: input?.currentFiles ?? {},
19
+ canvasLayers: input?.canvasLayers ?? [],
20
+ previewStatus: input?.previewStatus ?? null,
21
+ }),
22
+ initial: 'idle',
23
+ states: {
24
+ idle: {
25
+ on: {
26
+ UPDATE: {
27
+ actions: assign(({ context, event }) => ({
28
+ ...context,
29
+ ...(event.value || {}),
30
+ })),
31
+ },
32
+ SET_FRAGMENT: {
33
+ target: 'probing',
34
+ actions: assign(({ context, event }) => ({
35
+ ...context,
36
+ activeFragment: event.fragment,
37
+ currentFiles: event.fragment?.files || context.currentFiles,
38
+ canvasLayers: (event.fragment?.canvasLayers as unknown[]) || context.canvasLayers,
39
+ previewStatus: event.fragment?.sandboxUrl ? { status: 'checking' } : null,
40
+ })),
41
+ },
42
+ PROBE: 'probing',
43
+ SET_LOADING: {
44
+ actions: assign(({ context, event }) => ({
45
+ ...context,
46
+ isLoading: event.isLoading,
47
+ })),
48
+ },
49
+ RECREATE_SANDBOX: 'recreating',
50
+ },
51
+ },
52
+ probing: {
53
+ invoke: {
54
+ id: 'probeUrl',
55
+ src: 'probeUrl',
56
+ input: ({ context, event }) => ({
57
+ url: (event as any).url ?? context.activeFragment?.sandboxUrl,
58
+ fragmentId: (event as any).fragmentId ?? context.activeFragment?.id,
59
+ }),
60
+ onDone: {
61
+ target: 'idle',
62
+ actions: assign(({ context }) => ({
63
+ ...context,
64
+ previewStatus: null,
65
+ isLoading: false,
66
+ })),
67
+ },
68
+ onError: {
69
+ target: 'idle',
70
+ actions: assign(({ context, event }) => ({
71
+ ...context,
72
+ previewStatus: {
73
+ status: 'error',
74
+ message: event.error instanceof Error ? event.error.message : 'Network error',
75
+ },
76
+ isLoading: false,
77
+ })),
78
+ },
79
+ },
80
+ },
81
+ recreating: {
82
+ invoke: {
83
+ id: 'recreateSandbox',
84
+ src: 'recreateSandbox',
85
+ input: ({ event }) => ({ id: (event as any).id }),
86
+ onDone: {
87
+ target: 'idle',
88
+ actions: assign(({ context }) => ({
89
+ ...context,
90
+ isLoading: false,
91
+ })),
92
+ },
93
+ onError: {
94
+ target: 'idle',
95
+ actions: assign(({ context, event }) => ({
96
+ ...context,
97
+ isLoading: false,
98
+ previewStatus: {
99
+ status: 'error',
100
+ message: event.error instanceof Error ? event.error.message : 'Recreate failed',
101
+ },
102
+ })),
103
+ },
104
+ },
105
+ },
106
+ },
107
+ }).provide({
108
+ actors: {
109
+ probeUrl: fromPromise(async ({ input }) => {
110
+ const { url } = input as { url?: string };
111
+ if (!url) return { ok: false } as const;
112
+ const controller = new AbortController();
113
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
114
+ try {
115
+ const res = await fetch(url, {
116
+ method: 'GET',
117
+ mode: 'cors',
118
+ redirect: 'follow',
119
+ signal: controller.signal,
120
+ });
121
+ clearTimeout(timeoutId);
122
+ if (!res.ok) {
123
+ throw new Error(`${res.status} ${res.statusText}`);
124
+ }
125
+ return { ok: true } as const;
126
+ } finally {
127
+ clearTimeout(timeoutId);
128
+ }
129
+ }),
130
+ recreateSandbox: fromPromise(async ({ input }) => {
131
+ // Placeholder actor: implement by injecting via provide in component if needed
132
+ const { id } = input as { id: string };
133
+ console.log('recreateSandbox actor invoked for id:', id);
134
+ return { success: true } as const;
135
+ }),
136
+ },
137
+ });
138
+
139
+ export type RightSidebarMachine = typeof rightSidebarMachine;
@@ -0,0 +1,55 @@
1
+ import type { ActorRefFrom } from 'xstate';
2
+
3
+ export type PreviewStatus = {
4
+ code?: number;
5
+ message?: string;
6
+ status?: 'checking' | 'error';
7
+ } | null;
8
+
9
+ export type ActiveFragment = {
10
+ id?: string;
11
+ sandboxUrl?: string;
12
+ sandboxId?: string;
13
+ title?: string;
14
+ files?: Record<string, string>;
15
+ projectId?: string;
16
+ modelConfig?: unknown;
17
+ canvasLayers?: unknown[];
18
+ summary?: unknown;
19
+ isError?: boolean;
20
+ } | null;
21
+
22
+ export type RightSidebarContext = {
23
+ channelId: string;
24
+ isMinimized: boolean;
25
+ windowWidth: number;
26
+ windowHeight: number;
27
+ isLoading: boolean;
28
+ activeFragment: ActiveFragment;
29
+ currentFiles: Record<string, string>;
30
+ canvasLayers: unknown[];
31
+ previewStatus: PreviewStatus;
32
+ };
33
+
34
+ export type UpdateEvent = {
35
+ type: 'UPDATE';
36
+ value: Partial<RightSidebarContext>;
37
+ };
38
+
39
+ export type SetFragmentEvent = {
40
+ type: 'SET_FRAGMENT';
41
+ fragment: NonNullable<ActiveFragment>;
42
+ };
43
+
44
+ export type ProbeEvent = { type: 'PROBE'; url?: string; fragmentId?: string };
45
+
46
+ export type RecreateSandboxEvent = { type: 'RECREATE_SANDBOX'; id: string };
47
+
48
+ export type MachineEvent =
49
+ | UpdateEvent
50
+ | SetFragmentEvent
51
+ | ProbeEvent
52
+ | RecreateSandboxEvent
53
+ | { type: 'SET_LOADING'; isLoading: boolean };
54
+
55
+ export type SubmitOrganizationActor = ActorRefFrom<any>;