@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.
- package/CHANGELOG.md +32 -0
- package/lib/components/InboxMessage/RightSidebarAi.d.ts +6 -0
- package/lib/components/InboxMessage/RightSidebarAi.d.ts.map +1 -1
- package/lib/components/InboxMessage/RightSidebarAi.js.map +1 -1
- package/lib/components/slot-fill/right-sidebar-filler.d.ts.map +1 -1
- package/lib/components/slot-fill/right-sidebar-filler.js.map +1 -1
- package/lib/container/InboxContainer.d.ts +5 -0
- package/lib/container/InboxContainer.d.ts.map +1 -1
- package/lib/container/InboxContainer.js.map +1 -1
- package/lib/container/InboxWithAiLoader.d.ts +5 -0
- package/lib/container/InboxWithAiLoader.d.ts.map +1 -1
- package/lib/container/InboxWithAiLoader.js +6 -2
- package/lib/container/InboxWithAiLoader.js.map +1 -1
- package/lib/container/TestInboxWithAiLoader.d.ts.map +1 -1
- package/lib/container/TestInboxWithAiLoader.js +2 -1
- package/lib/container/TestInboxWithAiLoader.js.map +1 -1
- package/lib/templates/InboxWithAi.d.ts +5 -0
- package/lib/templates/InboxWithAi.d.ts.map +1 -1
- package/lib/templates/InboxWithAi.js +52 -2
- package/lib/templates/InboxWithAi.js.map +1 -1
- package/lib/templates/InboxWithAi.tsx +70 -0
- package/lib/xstate/rightSidebar.machine.d.ts.map +1 -1
- package/lib/xstate/rightSidebar.machine.js +172 -21
- package/lib/xstate/rightSidebar.machine.js.map +1 -1
- package/lib/xstate/rightSidebar.types.d.ts +6 -1
- package/lib/xstate/rightSidebar.types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/components/InboxMessage/RightSidebarAi.tsx +3 -0
- package/src/components/slot-fill/right-sidebar-filler.tsx +3 -0
- package/src/container/InboxContainer.tsx +2 -0
- package/src/container/InboxWithAiLoader.tsx +6 -0
- package/src/container/TestInboxWithAiLoader.tsx +1 -0
- package/src/templates/InboxWithAi.tsx +70 -0
- package/src/xstate/rightSidebar.machine.ts +187 -22
- 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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
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 }) =>
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}
|
|
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<
|
|
58
|
+
export type SubmitOrganizationActor = ActorRefFrom<unknown>;
|