@messenger-box/tailwind-ui-inbox 10.0.3-alpha.121 → 10.0.3-alpha.123
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 +8 -0
- package/lib/components/AIAgent/AIAgent.d.ts +2 -0
- package/lib/components/AIAgent/AIAgent.d.ts.map +1 -1
- package/lib/components/AIAgent/AIAgent.js +48 -29
- package/lib/components/AIAgent/AIAgent.js.map +1 -1
- package/lib/components/InboxMessage/InputComponent.d.ts +4 -1
- package/lib/components/InboxMessage/InputComponent.d.ts.map +1 -1
- package/lib/components/InboxMessage/InputComponent.js +20 -261
- package/lib/components/InboxMessage/InputComponent.js.map +1 -1
- package/lib/components/InboxMessage/UploadImageButton.js +2 -6
- package/lib/components/InboxMessage/UploadImageButton.js.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts +1 -0
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.d.ts.map +1 -1
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js +15 -5
- package/lib/components/InboxMessage/message-widgets/ModernMessageGroup.js.map +1 -1
- package/lib/components/ModelConfigPanel.d.ts +10 -0
- package/lib/components/ModelConfigPanel.d.ts.map +1 -1
- package/lib/components/ModelConfigPanel.js +551 -2
- package/lib/components/ModelConfigPanel.js.map +1 -1
- package/lib/components/filler-components/RightSiderBar.d.ts +1 -0
- package/lib/components/filler-components/RightSiderBar.d.ts.map +1 -1
- package/lib/components/filler-components/RightSiderBar.js +173 -148
- package/lib/components/filler-components/RightSiderBar.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/config/env-config.d.ts +5 -0
- package/lib/config/env-config.d.ts.map +1 -1
- package/lib/config/env-config.js +16 -1
- package/lib/config/env-config.js.map +1 -1
- package/lib/container/AiLandingInput.d.ts.map +1 -1
- package/lib/container/AiLandingInput.js +19 -15
- package/lib/container/AiLandingInput.js.map +1 -1
- package/lib/container/Inbox.js +1 -1
- package/lib/container/Inbox.js.map +1 -1
- package/lib/container/InboxAiMessagesLoader.d.ts +1 -0
- package/lib/container/InboxAiMessagesLoader.d.ts.map +1 -1
- package/lib/container/InboxAiMessagesLoader.js +4 -1
- package/lib/container/InboxAiMessagesLoader.js.map +1 -1
- package/lib/container/InboxContainer.d.ts +1 -0
- package/lib/container/InboxContainer.d.ts.map +1 -1
- package/lib/container/InboxContainer.js +1 -6
- package/lib/container/InboxContainer.js.map +1 -1
- package/lib/container/InboxWithAiLoader.d.ts +1 -0
- package/lib/container/InboxWithAiLoader.d.ts.map +1 -1
- package/lib/container/InboxWithAiLoader.js +2 -1
- package/lib/container/InboxWithAiLoader.js.map +1 -1
- package/lib/container/ServiceInbox.js +1 -1
- package/lib/container/ServiceInbox.js.map +1 -1
- package/lib/container/ThreadMessages.js +1 -1
- package/lib/container/ThreadMessages.js.map +1 -1
- package/lib/container/ThreadMessagesInbox.js +1 -1
- package/lib/container/ThreadMessagesInbox.js.map +1 -1
- package/lib/container/Threads.js +1 -1
- package/lib/container/Threads.js.map +1 -1
- package/lib/hooks/usePersistentModelConfig.d.ts +5 -2
- package/lib/hooks/usePersistentModelConfig.d.ts.map +1 -1
- package/lib/hooks/usePersistentModelConfig.js +30 -10
- package/lib/hooks/usePersistentModelConfig.js.map +1 -1
- package/lib/module.js +1 -1
- package/lib/module.js.map +1 -1
- package/lib/templates/InboxWithAi.d.ts +1 -0
- package/lib/templates/InboxWithAi.d.ts.map +1 -1
- package/lib/templates/InboxWithAi.js +9 -6
- package/lib/templates/InboxWithAi.js.map +1 -1
- package/lib/templates/InboxWithAi.tsx +7 -3
- package/lib/xstate/index.d.ts +3 -0
- package/lib/xstate/index.d.ts.map +1 -0
- package/lib/xstate/rightSidebar.machine.d.ts +4 -0
- package/lib/xstate/rightSidebar.machine.d.ts.map +1 -0
- package/lib/xstate/rightSidebar.machine.js +174 -0
- package/lib/xstate/rightSidebar.machine.js.map +1 -0
- package/lib/xstate/rightSidebar.types.d.ts +52 -0
- package/lib/xstate/rightSidebar.types.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/components/AIAgent/AIAgent.tsx +55 -28
- package/src/components/InboxMessage/InputComponent.tsx +23 -325
- package/src/components/InboxMessage/UploadImageButton.tsx +4 -4
- package/src/components/InboxMessage/message-widgets/ModernMessageGroup.tsx +17 -0
- package/src/components/ModelConfigPanel.tsx +666 -0
- package/src/components/filler-components/RightSiderBar.tsx +193 -146
- package/src/components/slot-fill/right-sidebar-filler.tsx +1 -0
- package/src/config/env-config.ts +6 -1
- package/src/container/AiLandingInput.tsx +32 -119
- package/src/container/InboxAiMessagesLoader.tsx +13 -2
- package/src/container/InboxContainer.tsx +2 -8
- package/src/container/InboxWithAiLoader.tsx +2 -0
- package/src/hooks/usePersistentModelConfig.ts +26 -13
- package/src/templates/InboxWithAi.tsx +7 -3
- package/src/xstate/index.ts +2 -0
- package/src/xstate/rightSidebar.machine.ts +139 -0
- package/src/xstate/rightSidebar.types.ts +55 -0
|
@@ -22,32 +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
|
-
const [showModelConfig, setShowModelConfig] = React.useState(true);
|
|
26
25
|
|
|
27
26
|
return (
|
|
28
|
-
<div className="flex items-center justify-center min-h-screen bg-
|
|
27
|
+
<div className="flex items-center justify-center min-h-screen bg-white shadow-lg">
|
|
29
28
|
<div className="w-full max-w-2xl mx-4">
|
|
30
|
-
<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 ">
|
|
31
30
|
<div className="text-center mb-6">
|
|
32
|
-
<h3 className="text-xl font-semibold text-gray-700 mb-2">
|
|
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>
|
|
33
34
|
<p className="text-gray-500">Describe your idea and I'll help you create it step by step.</p>
|
|
34
35
|
</div>
|
|
35
36
|
<div className="mb-0">
|
|
36
|
-
<div className={` ${!hasApiKey ? 'p-3 bg-
|
|
37
|
-
{/* <div className="mb-4">
|
|
38
|
-
<h3 className="text-sm font-medium text-gray-900 mb-2">Configuration</h3>
|
|
39
|
-
<p className="text-xs text-gray-600 mb-3">
|
|
40
|
-
Choose your AI model, template, and provide your API key before creating your
|
|
41
|
-
project.
|
|
42
|
-
</p>
|
|
43
|
-
</div> */}
|
|
44
|
-
{/* <ModelConfigPanel
|
|
45
|
-
config={modelConfig}
|
|
46
|
-
onConfigChange={updateModelConfig}
|
|
47
|
-
isVisible={showModelConfig}
|
|
48
|
-
onToggleVisibility={() => setShowModelConfig(!showModelConfig)}
|
|
49
|
-
showTemplate={true}
|
|
50
|
-
/> */}
|
|
37
|
+
<div className={` ${!hasApiKey ? 'p-3 bg-white rounded-lg border' : ''}`}>
|
|
51
38
|
{!hasApiKey && (
|
|
52
39
|
<>
|
|
53
40
|
<div className="mb-4">
|
|
@@ -65,10 +52,11 @@ const AiLandingInput: React.FC = () => {
|
|
|
65
52
|
</div>
|
|
66
53
|
</div>
|
|
67
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"
|
|
68
57
|
handleSend={async (message: string, files: File[]) => {
|
|
69
58
|
const validated = getValidatedConfig();
|
|
70
|
-
if (!validated && !hasApiKey) {
|
|
71
|
-
setShowModelConfig(true);
|
|
59
|
+
if (!validated && !hasApiKey && !modelConfig?.extensionId && !modelConfig?.formId) {
|
|
72
60
|
return;
|
|
73
61
|
}
|
|
74
62
|
console.log('Message sent without channel:', message, files);
|
|
@@ -77,8 +65,13 @@ const AiLandingInput: React.FC = () => {
|
|
|
77
65
|
// const postId = objectId();
|
|
78
66
|
const channelId = objectId();
|
|
79
67
|
// Do not include extensionId inside modelConfig when sending additionalProperties
|
|
80
|
-
const {
|
|
81
|
-
|
|
68
|
+
const {
|
|
69
|
+
extensionId: _omitExtensionId,
|
|
70
|
+
formId: _omitFormId,
|
|
71
|
+
functionId: _omitFunctionId,
|
|
72
|
+
stepName: _omitStepName,
|
|
73
|
+
...modelConfigWithoutExtensionId
|
|
74
|
+
} = modelConfig || ({} as any);
|
|
82
75
|
createChannelWorkflowJob({
|
|
83
76
|
variables: {
|
|
84
77
|
createChannelInput: {
|
|
@@ -107,10 +100,20 @@ const AiLandingInput: React.FC = () => {
|
|
|
107
100
|
},
|
|
108
101
|
},
|
|
109
102
|
extensionInput: {
|
|
110
|
-
extensionId:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
103
|
+
extensionId:
|
|
104
|
+
modelConfig?.extensionId || config?.MESSENGER_BOX_FORM_EXTENSION_ID,
|
|
105
|
+
stepName:
|
|
106
|
+
modelConfig?.stepName ||
|
|
107
|
+
config?.MESSENGER_BOX_FORM_EXTENSION_STEP_NAME ||
|
|
108
|
+
'extractCodeFromExtension',
|
|
109
|
+
formId:
|
|
110
|
+
modelConfig?.formId ||
|
|
111
|
+
config?.MESSENGER_BOX_FORM_EXTENSION_FORM_ID ||
|
|
112
|
+
'form-builder-proxy',
|
|
113
|
+
functionId:
|
|
114
|
+
modelConfig?.functionId ||
|
|
115
|
+
config?.MESSENGER_BOX_FORM_EXTENSION_FUNCTION_ID ||
|
|
116
|
+
'create-channel-function',
|
|
114
117
|
metadata: {},
|
|
115
118
|
source: 'final-submission',
|
|
116
119
|
},
|
|
@@ -143,99 +146,9 @@ const AiLandingInput: React.FC = () => {
|
|
|
143
146
|
modelConfig={modelConfig}
|
|
144
147
|
onModelConfigChange={updateModelConfig}
|
|
145
148
|
/>
|
|
146
|
-
{/* <div className="
|
|
147
|
-
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
|
148
|
-
<div className="space-y-2">
|
|
149
|
-
<div className="relative">
|
|
150
|
-
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
151
|
-
<div className="w-5 h-5 bg-purple-500 rounded-full flex items-center justify-center">
|
|
152
|
-
<span className="text-white text-xs font-bold">N</span>
|
|
153
|
-
</div>
|
|
154
|
-
</div>
|
|
155
|
-
<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">
|
|
156
|
-
<option value="nextjs">Next.js</option>
|
|
157
|
-
<option value="vuejs">Vue.js</option>
|
|
158
|
-
<option value="vite-remix">Vite with Remix</option>
|
|
159
|
-
</select>
|
|
160
|
-
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
161
|
-
<svg
|
|
162
|
-
className="h-5 w-5 text-gray-400"
|
|
163
|
-
fill="none"
|
|
164
|
-
stroke="currentColor"
|
|
165
|
-
viewBox="0 0 24 24"
|
|
166
|
-
>
|
|
167
|
-
<path
|
|
168
|
-
strokeLinecap="round"
|
|
169
|
-
strokeLinejoin="round"
|
|
170
|
-
strokeWidth={2}
|
|
171
|
-
d="M19 9l-7 7-7-7"
|
|
172
|
-
/>
|
|
173
|
-
</svg>
|
|
174
|
-
</div>
|
|
175
|
-
</div>
|
|
176
|
-
</div>
|
|
177
|
-
<div className="space-y-2">
|
|
178
|
-
<div className="relative">
|
|
179
|
-
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
|
180
|
-
<div className="w-5 h-5 bg-pink-500 rounded-full flex items-center justify-center">
|
|
181
|
-
<svg className="w-3 h-3 text-white" fill="currentColor" viewBox="0 0 20 20">
|
|
182
|
-
<path d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
183
|
-
</svg>
|
|
184
|
-
</div>
|
|
185
|
-
</div>
|
|
186
|
-
<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">
|
|
187
|
-
<option value="claude-3-5-sonnet">Claude 3.5 Sonnet</option>
|
|
188
|
-
<option value="claude-3-5-haiku">Claude 3.5 Haiku</option>
|
|
189
|
-
<option value="claude-3-opus">Claude 3 Opus</option>
|
|
190
|
-
<option value="claude-3-sonnet">Claude 3 Sonnet</option>
|
|
191
|
-
<option value="claude-3-haiku">Claude 3 Haiku</option>
|
|
192
|
-
</select>
|
|
193
|
-
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
194
|
-
<svg
|
|
195
|
-
className="h-5 w-5 text-gray-400"
|
|
196
|
-
fill="none"
|
|
197
|
-
stroke="currentColor"
|
|
198
|
-
viewBox="0 0 24 24"
|
|
199
|
-
>
|
|
200
|
-
<path
|
|
201
|
-
strokeLinecap="round"
|
|
202
|
-
strokeLinejoin="round"
|
|
203
|
-
strokeWidth={2}
|
|
204
|
-
d="M19 9l-7 7-7-7"
|
|
205
|
-
/>
|
|
206
|
-
</svg>
|
|
207
|
-
</div>
|
|
208
|
-
</div>
|
|
209
|
-
</div>
|
|
210
|
-
<div className="space-y-2">
|
|
211
|
-
<div className="relative">
|
|
212
|
-
<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">
|
|
213
|
-
<option value="">API Key</option>
|
|
214
|
-
<option value="saved-key-1">••••••••••••</option>
|
|
215
|
-
<option value="saved-key-2">••••••••••••</option>
|
|
216
|
-
</select>
|
|
217
|
-
<div className="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
|
|
218
|
-
<svg
|
|
219
|
-
className="h-5 w-5 text-gray-400"
|
|
220
|
-
fill="none"
|
|
221
|
-
stroke="currentColor"
|
|
222
|
-
viewBox="0 0 24 24"
|
|
223
|
-
>
|
|
224
|
-
<path
|
|
225
|
-
strokeLinecap="round"
|
|
226
|
-
strokeLinejoin="round"
|
|
227
|
-
strokeWidth={2}
|
|
228
|
-
d="M19 9l-7 7-7-7"
|
|
229
|
-
/>
|
|
230
|
-
</svg>
|
|
231
|
-
</div>
|
|
232
|
-
</div>
|
|
233
|
-
</div>
|
|
234
|
-
</div>
|
|
235
|
-
</div> */}
|
|
236
|
-
<div className="text-center text-sm text-gray-400">
|
|
149
|
+
{/* <div className="text-center text-sm text-gray-400">
|
|
237
150
|
Each project gets its own workspace where you can iterate and refine your ideas.
|
|
238
|
-
</div>
|
|
151
|
+
</div> */}
|
|
239
152
|
</div>
|
|
240
153
|
</div>
|
|
241
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 {
|
|
20
|
-
|
|
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,6 +38,7 @@ interface InboxWithAiLoaderProps {
|
|
|
38
38
|
pathPrefix?: string;
|
|
39
39
|
orgName?: string;
|
|
40
40
|
handleRecreateSandbox?: (messageId: string) => void | Promise<any>;
|
|
41
|
+
isShowOnlyInbox?: boolean;
|
|
41
42
|
[key: string]: any;
|
|
42
43
|
}
|
|
43
44
|
|
|
@@ -53,6 +54,7 @@ const InboxWithAiLoader = (props: InboxWithAiLoaderProps) => {
|
|
|
53
54
|
orgName: orgName || '',
|
|
54
55
|
pathPrefix: pathPrefix || '',
|
|
55
56
|
handleRecreateSandbox: props.handleRecreateSandbox,
|
|
57
|
+
isShowOnlyInbox: props?.isShowOnlyInbox || false,
|
|
56
58
|
}),
|
|
57
59
|
[props, orgName, pathPrefix],
|
|
58
60
|
);
|
|
@@ -7,17 +7,33 @@ export interface ModelConfig {
|
|
|
7
7
|
model: string;
|
|
8
8
|
apiKey: string;
|
|
9
9
|
template: 'vite-react' | 'nextjs' | 'vue';
|
|
10
|
-
extensionId
|
|
10
|
+
extensionId: string;
|
|
11
|
+
formId: string;
|
|
12
|
+
functionId?: string;
|
|
13
|
+
stepName?: string;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
const STORAGE_KEY = 'mbx:model-config';
|
|
14
17
|
|
|
18
|
+
const DEFAULTS: ModelConfig = {
|
|
19
|
+
provider: 'anthropic',
|
|
20
|
+
// model: 'claude-3-5-sonnet-20241022',
|
|
21
|
+
model: 'claude-sonnet-4-5-20250929',
|
|
22
|
+
apiKey: '',
|
|
23
|
+
template: 'vite-react',
|
|
24
|
+
extensionId: '',
|
|
25
|
+
formId: '',
|
|
26
|
+
functionId: '',
|
|
27
|
+
stepName: '',
|
|
28
|
+
};
|
|
29
|
+
|
|
15
30
|
function loadFromStorage(): ModelConfig | null {
|
|
16
31
|
try {
|
|
17
32
|
const raw = typeof window !== 'undefined' ? window.localStorage.getItem(STORAGE_KEY) : null;
|
|
18
33
|
if (!raw) return null;
|
|
19
34
|
const parsed = JSON.parse(raw);
|
|
20
|
-
|
|
35
|
+
// Merge with defaults to ensure all fields are present
|
|
36
|
+
return { ...DEFAULTS, ...parsed } as ModelConfig;
|
|
21
37
|
} catch {
|
|
22
38
|
return null;
|
|
23
39
|
}
|
|
@@ -33,23 +49,20 @@ function saveToStorage(config: ModelConfig) {
|
|
|
33
49
|
}
|
|
34
50
|
}
|
|
35
51
|
|
|
36
|
-
const DEFAULTS: ModelConfig = {
|
|
37
|
-
provider: 'anthropic',
|
|
38
|
-
model: 'claude-sonnet-4-5-20250929',
|
|
39
|
-
apiKey: '',
|
|
40
|
-
template: 'vite-react',
|
|
41
|
-
extensionId: '',
|
|
42
|
-
};
|
|
43
|
-
|
|
44
52
|
export function usePersistentModelConfig() {
|
|
45
|
-
const [modelConfig, setModelConfig] = useState<ModelConfig>(() =>
|
|
53
|
+
const [modelConfig, setModelConfig] = useState<ModelConfig>(() => {
|
|
54
|
+
const loaded = loadFromStorage();
|
|
55
|
+
// Merge with defaults to ensure all fields are present even if loaded config is partial
|
|
56
|
+
return loaded ? { ...DEFAULTS, ...loaded } : DEFAULTS;
|
|
57
|
+
});
|
|
46
58
|
|
|
47
59
|
useEffect(() => {
|
|
48
60
|
saveToStorage(modelConfig);
|
|
49
61
|
}, [modelConfig]);
|
|
50
62
|
|
|
51
|
-
const updateModelConfig = useCallback((newConfig: ModelConfig) => {
|
|
52
|
-
|
|
63
|
+
const updateModelConfig = useCallback((newConfig: Partial<ModelConfig> | ModelConfig) => {
|
|
64
|
+
// Merge with current config and defaults to ensure all fields are present
|
|
65
|
+
setModelConfig((prev) => ({ ...DEFAULTS, ...prev, ...newConfig } as ModelConfig));
|
|
53
66
|
}, []);
|
|
54
67
|
|
|
55
68
|
const hasApiKey = useMemo(() => Boolean(modelConfig.apiKey && modelConfig.apiKey.trim().length > 0), [modelConfig]);
|
|
@@ -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,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>;
|