@messenger-box/tailwind-ui-inbox 10.0.3-alpha.143 → 10.0.3-alpha.154

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 (35) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/lib/components/InboxMessage/RightSidebarAi.d.ts +6 -0
  3. package/lib/components/InboxMessage/RightSidebarAi.d.ts.map +1 -1
  4. package/lib/components/InboxMessage/RightSidebarAi.js.map +1 -1
  5. package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -1
  6. package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -1
  7. package/lib/container/InboxContainer.d.ts +5 -0
  8. package/lib/container/InboxContainer.d.ts.map +1 -1
  9. package/lib/container/InboxContainer.js.map +1 -1
  10. package/lib/container/InboxWithAiLoader.d.ts +5 -0
  11. package/lib/container/InboxWithAiLoader.d.ts.map +1 -1
  12. package/lib/container/InboxWithAiLoader.js +6 -2
  13. package/lib/container/InboxWithAiLoader.js.map +1 -1
  14. package/lib/container/TestInboxWithAiLoader.d.ts.map +1 -1
  15. package/lib/container/TestInboxWithAiLoader.js +2 -1
  16. package/lib/container/TestInboxWithAiLoader.js.map +1 -1
  17. package/lib/templates/InboxWithAi.d.ts +5 -0
  18. package/lib/templates/InboxWithAi.d.ts.map +1 -1
  19. package/lib/templates/InboxWithAi.js +52 -2
  20. package/lib/templates/InboxWithAi.js.map +1 -1
  21. package/lib/templates/InboxWithAi.tsx +70 -0
  22. package/lib/xstate/rightSidebar.machine.d.ts.map +1 -1
  23. package/lib/xstate/rightSidebar.machine.js +172 -21
  24. package/lib/xstate/rightSidebar.machine.js.map +1 -1
  25. package/lib/xstate/rightSidebar.types.d.ts +6 -1
  26. package/lib/xstate/rightSidebar.types.d.ts.map +1 -1
  27. package/package.json +4 -4
  28. package/src/components/InboxMessage/RightSidebarAi.tsx +3 -0
  29. package/src/components/slot-fill/right-sidebar-filler.tsx +3 -0
  30. package/src/container/InboxContainer.tsx +2 -0
  31. package/src/container/InboxWithAiLoader.tsx +6 -0
  32. package/src/container/TestInboxWithAiLoader.tsx +1 -0
  33. package/src/templates/InboxWithAi.tsx +70 -0
  34. package/src/xstate/rightSidebar.machine.ts +187 -22
  35. package/src/xstate/rightSidebar.types.ts +5 -2
@@ -1,5 +1,6 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
1
2
  import { createMachine, assign, fromPromise } from 'xstate';
2
- import type { RightSidebarContext, MachineEvent } from './rightSidebar.types';
3
+ import type { RightSidebarContext, MachineEvent, ProbeEvent, RecreateSandboxEvent } from './rightSidebar.types';
3
4
 
4
5
  export const rightSidebarMachine = createMachine({
5
6
  id: 'rightSidebar',
@@ -30,13 +31,13 @@ export const rightSidebarMachine = createMachine({
30
31
  })),
31
32
  },
32
33
  SET_FRAGMENT: {
33
- target: 'probing',
34
34
  actions: assign(({ context, event }) => ({
35
35
  ...context,
36
36
  activeFragment: event.fragment,
37
37
  currentFiles: event.fragment?.files || context.currentFiles,
38
38
  canvasLayers: (event.fragment?.canvasLayers as unknown[]) || context.canvasLayers,
39
- previewStatus: event.fragment?.sandboxUrl ? { status: 'checking' } : null,
39
+ // Don't set previewStatus to 'checking' automatically - only probe when explicitly needed
40
+ previewStatus: null,
40
41
  })),
41
42
  },
42
43
  PROBE: 'probing',
@@ -46,17 +47,58 @@ export const rightSidebarMachine = createMachine({
46
47
  isLoading: event.isLoading,
47
48
  })),
48
49
  },
49
- RECREATE_SANDBOX: 'recreating',
50
+ CLEAR_PREVIEW_STATUS: {
51
+ actions: assign(({ context }) => ({
52
+ ...context,
53
+ previewStatus: null,
54
+ })),
55
+ },
56
+ RECREATE_SANDBOX: [
57
+ {
58
+ // Only transition if we're not already recreating
59
+ guard: ({ context }) => {
60
+ // Check if we have a fragment ID to recreate
61
+ const hasFragmentId = !!context.activeFragment?.id;
62
+ if (!hasFragmentId) {
63
+ console.log('🚫 RECREATE_SANDBOX: No fragment ID, ignoring');
64
+ return false;
65
+ }
66
+ return true;
67
+ },
68
+ target: 'recreating',
69
+ actions: assign(({ context, event }) => {
70
+ const recreateEvent = event as RecreateSandboxEvent;
71
+ console.log('🎯 RECREATE_SANDBOX event received and accepted:', {
72
+ id: recreateEvent.id,
73
+ fragmentId: context.activeFragment?.id,
74
+ });
75
+ return context;
76
+ }),
77
+ },
78
+ {
79
+ // If guard fails or already recreating, just log and stay in current state
80
+ actions: ({ context, event }) => {
81
+ const recreateEvent = event as RecreateSandboxEvent;
82
+ console.log('⏭️ RECREATE_SANDBOX ignored (already recreating or no fragment):', {
83
+ id: recreateEvent.id,
84
+ fragmentId: context.activeFragment?.id,
85
+ });
86
+ },
87
+ },
88
+ ],
50
89
  },
51
90
  },
52
91
  probing: {
53
92
  invoke: {
54
93
  id: 'probeUrl',
55
94
  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
- }),
95
+ input: ({ context, event }) => {
96
+ const probeEvent = event as ProbeEvent;
97
+ return {
98
+ url: probeEvent.url ?? context.activeFragment?.sandboxUrl,
99
+ fragmentId: probeEvent.fragmentId ?? context.activeFragment?.id,
100
+ };
101
+ },
60
102
  onDone: {
61
103
  target: 'idle',
62
104
  actions: assign(({ context }) => ({
@@ -65,29 +107,95 @@ export const rightSidebarMachine = createMachine({
65
107
  isLoading: false,
66
108
  })),
67
109
  },
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',
110
+ onError: [
111
+ {
112
+ // Only recreate if we have a fragment ID AND the error suggests the sandbox is truly unavailable
113
+ // Don't recreate on transient network errors (timeouts, CORS, etc.)
114
+ guard: ({ context, event }): boolean => {
115
+ if (!context.activeFragment?.id) {
116
+ console.log('🚫 Guard: No fragment ID, skipping recreation');
117
+ return false;
118
+ }
119
+ const errorMessage =
120
+ event.error instanceof Error ? event.error.message : String(event.error);
121
+
122
+ // Check for explicit HTTP status codes (4xx, 5xx)
123
+ const isServerError = /^[45]\d{2}/.test(errorMessage);
124
+
125
+ // Also check for CORS/network errors that likely indicate expired sandbox (502)
126
+ // CORS errors on sandbox URLs usually mean the sandbox is expired/down
127
+ const isCorsNetworkError: boolean =
128
+ errorMessage.includes('502 CORS/Network Error') ||
129
+ errorMessage.includes('Failed to fetch') ||
130
+ errorMessage.includes('CORS') ||
131
+ errorMessage.includes('NetworkError') ||
132
+ errorMessage.includes('Network request failed');
133
+
134
+ const willRecreate: boolean = isServerError || isCorsNetworkError;
135
+
136
+ console.log('🔍 Guard check:', {
137
+ errorMessage,
138
+ isServerError,
139
+ isCorsNetworkError,
140
+ willRecreate,
141
+ });
142
+
143
+ return willRecreate;
75
144
  },
76
- isLoading: false,
77
- })),
78
- },
145
+ target: 'recreating',
146
+ actions: assign(({ context, event }) => {
147
+ console.log('🔄 Transitioning to recreating state:', {
148
+ fragmentId: context.activeFragment?.id,
149
+ error: event.error instanceof Error ? event.error.message : String(event.error),
150
+ });
151
+ return {
152
+ ...context,
153
+ previewStatus: {
154
+ status: 'error',
155
+ message: event.error instanceof Error ? event.error.message : 'Network error',
156
+ },
157
+ isLoading: true,
158
+ };
159
+ }),
160
+ },
161
+ {
162
+ // For other errors (network, CORS, timeout), just show error but don't recreate
163
+ target: 'idle',
164
+ actions: assign(({ context, event }) => ({
165
+ ...context,
166
+ previewStatus: {
167
+ status: 'error',
168
+ message: event.error instanceof Error ? event.error.message : 'Network error',
169
+ },
170
+ isLoading: false,
171
+ })),
172
+ },
173
+ ],
79
174
  },
80
175
  },
81
176
  recreating: {
82
177
  invoke: {
83
178
  id: 'recreateSandbox',
84
179
  src: 'recreateSandbox',
85
- input: ({ event }) => ({ id: (event as any).id }),
180
+ input: ({ context, event }) => {
181
+ const recreateEvent = event as RecreateSandboxEvent;
182
+ const id = recreateEvent.id ?? context.activeFragment?.id ?? '';
183
+ console.log('📥 recreateSandbox input:', {
184
+ id,
185
+ hasRecreateEventId: !!recreateEvent.id,
186
+ hasActiveFragmentId: !!context.activeFragment?.id,
187
+ activeFragmentId: context.activeFragment?.id,
188
+ });
189
+ return {
190
+ id,
191
+ };
192
+ },
86
193
  onDone: {
87
194
  target: 'idle',
88
195
  actions: assign(({ context }) => ({
89
196
  ...context,
90
197
  isLoading: false,
198
+ previewStatus: { status: 'checking' },
91
199
  })),
92
200
  },
93
201
  onError: {
@@ -102,16 +210,30 @@ export const rightSidebarMachine = createMachine({
102
210
  })),
103
211
  },
104
212
  },
213
+ on: {
214
+ COMPLETE_RECREATION: {
215
+ target: 'idle',
216
+ actions: assign(({ context }) => ({
217
+ ...context,
218
+ isLoading: false,
219
+ previewStatus: null,
220
+ })),
221
+ },
222
+ },
105
223
  },
106
224
  },
107
225
  }).provide({
108
226
  actors: {
109
227
  probeUrl: fromPromise(async ({ input }) => {
110
228
  const { url } = input as { url?: string };
111
- if (!url) return { ok: false } as const;
229
+ console.log('🔍 probeUrl actor called with URL:', url);
230
+ if (!url) {
231
+ throw new Error('No URL provided for probing');
232
+ }
112
233
  const controller = new AbortController();
113
234
  const timeoutId = setTimeout(() => controller.abort(), 10000);
114
235
  try {
236
+ console.log('📡 Fetching URL to probe:', url);
115
237
  const res = await fetch(url, {
116
238
  method: 'GET',
117
239
  mode: 'cors',
@@ -119,12 +241,55 @@ export const rightSidebarMachine = createMachine({
119
241
  signal: controller.signal,
120
242
  });
121
243
  clearTimeout(timeoutId);
244
+
245
+ console.log('📊 Probe response status:', res.status, res.statusText);
246
+
247
+ // Check for HTTP error status codes (4xx, 5xx)
122
248
  if (!res.ok) {
123
- throw new Error(`${res.status} ${res.statusText}`);
249
+ const statusCode = res.status;
250
+ const statusText = res.statusText || 'Unknown Error';
251
+ const errorMessage = `${statusCode} ${statusText}`;
252
+ console.log('❌ Probe detected error:', errorMessage);
253
+ // Throw error with status code format that matches the guard regex: /^[45]\d{2}/
254
+ throw new Error(errorMessage);
124
255
  }
256
+
257
+ console.log('✅ Probe successful - URL is accessible');
125
258
  return { ok: true } as const;
126
- } finally {
259
+ } catch (error) {
127
260
  clearTimeout(timeoutId);
261
+
262
+ // If it's an abort (timeout), don't treat as server error
263
+ if (error instanceof Error && error.name === 'AbortError') {
264
+ throw new Error('TIMEOUT Request timeout');
265
+ }
266
+
267
+ // If it's already a formatted error (from res.ok check), rethrow it
268
+ if (error instanceof Error && /^\d{3}/.test(error.message)) {
269
+ throw error;
270
+ }
271
+
272
+ // Handle CORS/network errors - these often mask 502 errors from expired sandboxes
273
+ // When we get a CORS error or "Failed to fetch", it's likely because:
274
+ // 1. The sandbox is expired (502) and the server isn't responding properly
275
+ // 2. The sandbox is down and can't set CORS headers
276
+ // We'll throw a special error that the guard can recognize
277
+ if (error instanceof Error) {
278
+ const isCorsError =
279
+ error.message.includes('Failed to fetch') ||
280
+ error.message.includes('CORS') ||
281
+ error.message.includes('NetworkError') ||
282
+ error.message.includes('Network request failed');
283
+
284
+ if (isCorsError) {
285
+ console.log('⚠️ CORS/Network error detected - likely expired sandbox (502):', error.message);
286
+ // Throw as 502 error so guard will match it
287
+ throw new Error('502 CORS/Network Error - Sandbox likely expired');
288
+ }
289
+ }
290
+
291
+ // For other network errors, throw as-is and let guard decide
292
+ throw error;
128
293
  }
129
294
  }),
130
295
  recreateSandbox: fromPromise(async ({ input }) => {
@@ -17,6 +17,7 @@ export type ActiveFragment = {
17
17
  canvasLayers?: unknown[];
18
18
  summary?: unknown;
19
19
  isError?: boolean;
20
+ isCreatingSandbox?: boolean;
20
21
  } | null;
21
22
 
22
23
  export type RightSidebarContext = {
@@ -50,6 +51,8 @@ export type MachineEvent =
50
51
  | SetFragmentEvent
51
52
  | ProbeEvent
52
53
  | RecreateSandboxEvent
53
- | { type: 'SET_LOADING'; isLoading: boolean };
54
+ | { type: 'SET_LOADING'; isLoading: boolean }
55
+ | { type: 'CLEAR_PREVIEW_STATUS' }
56
+ | { type: 'COMPLETE_RECREATION' };
54
57
 
55
- export type SubmitOrganizationActor = ActorRefFrom<any>;
58
+ export type SubmitOrganizationActor = ActorRefFrom<unknown>;