added backdrop and loading screen
Browse files
app/components/git/GitUrlImport.client.tsx
CHANGED
@@ -8,6 +8,8 @@ import { Chat } from '~/components/chat/Chat.client';
|
|
8 |
import { useGit } from '~/lib/hooks/useGit';
|
9 |
import { useChatHistory } from '~/lib/persistence';
|
10 |
import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands';
|
|
|
|
|
11 |
|
12 |
const IGNORE_PATTERNS = [
|
13 |
'node_modules/**',
|
@@ -38,6 +40,7 @@ export function GitUrlImport() {
|
|
38 |
const { ready: historyReady, importChat } = useChatHistory();
|
39 |
const { ready: gitReady, gitClone } = useGit();
|
40 |
const [imported, setImported] = useState(false);
|
|
|
41 |
|
42 |
const importRepo = async (repoUrl?: string) => {
|
43 |
if (!gitReady && !historyReady) {
|
@@ -109,9 +112,23 @@ ${file.content}
|
|
109 |
return;
|
110 |
}
|
111 |
|
112 |
-
importRepo(url)
|
|
|
|
|
|
|
|
|
|
|
113 |
setImported(true);
|
114 |
}, [searchParams, historyReady, gitReady, imported]);
|
115 |
|
116 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
117 |
}
|
|
|
8 |
import { useGit } from '~/lib/hooks/useGit';
|
9 |
import { useChatHistory } from '~/lib/persistence';
|
10 |
import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands';
|
11 |
+
import { LoadingOverlay } from '~/components/ui/LoadingOverlay';
|
12 |
+
import { toast } from 'react-toastify';
|
13 |
|
14 |
const IGNORE_PATTERNS = [
|
15 |
'node_modules/**',
|
|
|
40 |
const { ready: historyReady, importChat } = useChatHistory();
|
41 |
const { ready: gitReady, gitClone } = useGit();
|
42 |
const [imported, setImported] = useState(false);
|
43 |
+
const [loading, setLoading] = useState(true);
|
44 |
|
45 |
const importRepo = async (repoUrl?: string) => {
|
46 |
if (!gitReady && !historyReady) {
|
|
|
112 |
return;
|
113 |
}
|
114 |
|
115 |
+
importRepo(url).catch((error) => {
|
116 |
+
console.error('Error importing repo:', error);
|
117 |
+
toast.error('Failed to import repository');
|
118 |
+
setLoading(false);
|
119 |
+
window.location.href = '/';
|
120 |
+
});
|
121 |
setImported(true);
|
122 |
}, [searchParams, historyReady, gitReady, imported]);
|
123 |
|
124 |
+
return (
|
125 |
+
<ClientOnly fallback={<BaseChat />}>
|
126 |
+
{() => (
|
127 |
+
<>
|
128 |
+
<Chat />
|
129 |
+
{loading && <LoadingOverlay message="Please wait while we clone the repository..." />}
|
130 |
+
</>
|
131 |
+
)}
|
132 |
+
</ClientOnly>
|
133 |
+
);
|
134 |
}
|
app/components/ui/LoadingOverlay.tsx
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const LoadingOverlay = ({ message = 'Loading...' }) => {
|
2 |
+
return (
|
3 |
+
<div className="fixed inset-0 flex items-center justify-center bg-black/80 z-50 backdrop-blur-sm">
|
4 |
+
{/* Loading content */}
|
5 |
+
<div className="relative flex flex-col items-center gap-4 p-8 rounded-lg bg-bolt-elements-background-depth-2 shadow-lg">
|
6 |
+
<div
|
7 |
+
className={'i-svg-spinners:90-ring-with-bg text-bolt-elements-loader-progress'}
|
8 |
+
style={{ fontSize: '2rem' }}
|
9 |
+
></div>
|
10 |
+
<p className="text-lg text-bolt-elements-textTertiary">{message}</p>
|
11 |
+
</div>
|
12 |
+
</div>
|
13 |
+
);
|
14 |
+
};
|
app/components/ui/Settings.tsx
CHANGED
@@ -10,7 +10,6 @@ import { toast } from 'react-toastify';
|
|
10 |
import { useNavigate } from '@remix-run/react';
|
11 |
import commit from '~/commit.json';
|
12 |
import Cookies from 'js-cookie';
|
13 |
-
import { SettingsSlider } from './SettingsSlider';
|
14 |
import '~/styles/components/SettingsSlider.scss';
|
15 |
import '~/styles/components/Settings.scss';
|
16 |
|
@@ -219,9 +218,7 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
|
|
219 |
<button
|
220 |
key={tab.id}
|
221 |
onClick={() => setActiveTab(tab.id)}
|
222 |
-
className={classNames(
|
223 |
-
activeTab === tab.id ? 'active' : ''
|
224 |
-
)}
|
225 |
>
|
226 |
<div className={tab.icon} />
|
227 |
{tab.label}
|
@@ -250,7 +247,9 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
|
|
250 |
</div>
|
251 |
|
252 |
<div className="flex-1 flex flex-col p-8 bg-gray-50 dark:bg-gray-800">
|
253 |
-
<DialogTitle className="flex-shrink-0 text-lg font-semibold text-bolt-elements-textPrimary">
|
|
|
|
|
254 |
<div className="flex-1 overflow-y-auto">
|
255 |
{activeTab === 'chat-history' && (
|
256 |
<div className="p-4">
|
@@ -304,14 +303,20 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
|
|
304 |
checked={provider.isEnabled}
|
305 |
onChange={() => handleToggleProvider(provider.name)}
|
306 |
/>
|
307 |
-
<div
|
308 |
-
|
309 |
-
|
310 |
-
|
311 |
-
|
312 |
-
|
313 |
-
|
314 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
315 |
</label>
|
316 |
</div>
|
317 |
{/* Base URL input for configurable providers */}
|
@@ -343,14 +348,18 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
|
|
343 |
checked={isDebugEnabled}
|
344 |
onChange={() => setIsDebugEnabled(!isDebugEnabled)}
|
345 |
/>
|
346 |
-
<div
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
|
|
|
|
|
|
|
|
354 |
</label>
|
355 |
</div>
|
356 |
</div>
|
@@ -367,14 +376,18 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
|
|
367 |
checked={isJustSayEnabled}
|
368 |
onChange={() => setIsJustSayEnabled(!isJustSayEnabled)}
|
369 |
/>
|
370 |
-
<div
|
371 |
-
|
372 |
-
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
|
377 |
-
|
|
|
|
|
|
|
|
|
378 |
</label>
|
379 |
</div>
|
380 |
</div>
|
@@ -408,7 +421,9 @@ export const Settings = ({ open, onClose }: SettingsProps) => {
|
|
408 |
<ul>
|
409 |
<li className="text-bolt-elements-textSecondary">Ollama: {process.env.REACT_APP_OLLAMA_URL}</li>
|
410 |
<li className="text-bolt-elements-textSecondary">OpenAI: {process.env.REACT_APP_OPENAI_URL}</li>
|
411 |
-
<li className="text-bolt-elements-textSecondary">
|
|
|
|
|
412 |
</ul>
|
413 |
|
414 |
<h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Version Information</h4>
|
|
|
10 |
import { useNavigate } from '@remix-run/react';
|
11 |
import commit from '~/commit.json';
|
12 |
import Cookies from 'js-cookie';
|
|
|
13 |
import '~/styles/components/SettingsSlider.scss';
|
14 |
import '~/styles/components/Settings.scss';
|
15 |
|
|
|
218 |
<button
|
219 |
key={tab.id}
|
220 |
onClick={() => setActiveTab(tab.id)}
|
221 |
+
className={classNames(activeTab === tab.id ? 'active' : '')}
|
|
|
|
|
222 |
>
|
223 |
<div className={tab.icon} />
|
224 |
{tab.label}
|
|
|
247 |
</div>
|
248 |
|
249 |
<div className="flex-1 flex flex-col p-8 bg-gray-50 dark:bg-gray-800">
|
250 |
+
<DialogTitle className="flex-shrink-0 text-lg font-semibold text-bolt-elements-textPrimary">
|
251 |
+
Settings
|
252 |
+
</DialogTitle>
|
253 |
<div className="flex-1 overflow-y-auto">
|
254 |
{activeTab === 'chat-history' && (
|
255 |
<div className="p-4">
|
|
|
303 |
checked={provider.isEnabled}
|
304 |
onChange={() => handleToggleProvider(provider.name)}
|
305 |
/>
|
306 |
+
<div
|
307 |
+
className={classNames(
|
308 |
+
'settings-toggle__track',
|
309 |
+
provider.isEnabled
|
310 |
+
? 'settings-toggle__track--enabled'
|
311 |
+
: 'settings-toggle__track--disabled',
|
312 |
+
)}
|
313 |
+
></div>
|
314 |
+
<div
|
315 |
+
className={classNames(
|
316 |
+
'settings-toggle__thumb',
|
317 |
+
provider.isEnabled ? 'settings-toggle__thumb--enabled' : '',
|
318 |
+
)}
|
319 |
+
></div>
|
320 |
</label>
|
321 |
</div>
|
322 |
{/* Base URL input for configurable providers */}
|
|
|
348 |
checked={isDebugEnabled}
|
349 |
onChange={() => setIsDebugEnabled(!isDebugEnabled)}
|
350 |
/>
|
351 |
+
<div
|
352 |
+
className={classNames(
|
353 |
+
'settings-toggle__track',
|
354 |
+
isDebugEnabled ? 'settings-toggle__track--enabled' : 'settings-toggle__track--disabled',
|
355 |
+
)}
|
356 |
+
></div>
|
357 |
+
<div
|
358 |
+
className={classNames(
|
359 |
+
'settings-toggle__thumb',
|
360 |
+
isDebugEnabled ? 'settings-toggle__thumb--enabled' : '',
|
361 |
+
)}
|
362 |
+
></div>
|
363 |
</label>
|
364 |
</div>
|
365 |
</div>
|
|
|
376 |
checked={isJustSayEnabled}
|
377 |
onChange={() => setIsJustSayEnabled(!isJustSayEnabled)}
|
378 |
/>
|
379 |
+
<div
|
380 |
+
className={classNames(
|
381 |
+
'settings-toggle__track',
|
382 |
+
isJustSayEnabled ? 'settings-toggle__track--enabled' : 'settings-toggle__track--disabled',
|
383 |
+
)}
|
384 |
+
></div>
|
385 |
+
<div
|
386 |
+
className={classNames(
|
387 |
+
'settings-toggle__thumb',
|
388 |
+
isJustSayEnabled ? 'settings-toggle__thumb--enabled' : '',
|
389 |
+
)}
|
390 |
+
></div>
|
391 |
</label>
|
392 |
</div>
|
393 |
</div>
|
|
|
421 |
<ul>
|
422 |
<li className="text-bolt-elements-textSecondary">Ollama: {process.env.REACT_APP_OLLAMA_URL}</li>
|
423 |
<li className="text-bolt-elements-textSecondary">OpenAI: {process.env.REACT_APP_OPENAI_URL}</li>
|
424 |
+
<li className="text-bolt-elements-textSecondary">
|
425 |
+
LM Studio: {process.env.REACT_APP_LM_STUDIO_URL}
|
426 |
+
</li>
|
427 |
</ul>
|
428 |
|
429 |
<h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Version Information</h4>
|
app/components/ui/SettingsSlider.tsx
CHANGED
@@ -27,7 +27,7 @@ export const SettingsSlider = memo(<T,>({ selected, options, setSelected }: Sett
|
|
27 |
<motion.div
|
28 |
className={classNames(
|
29 |
'settings-slider__thumb',
|
30 |
-
isLeftSelected ? 'settings-slider__thumb--left' : 'settings-slider__thumb--right'
|
31 |
)}
|
32 |
initial={false}
|
33 |
animate={{
|
@@ -44,7 +44,7 @@ export const SettingsSlider = memo(<T,>({ selected, options, setSelected }: Sett
|
|
44 |
onClick={() => setSelected?.(options.left.value)}
|
45 |
className={classNames(
|
46 |
'settings-slider__button',
|
47 |
-
isLeftSelected ? 'settings-slider__button--selected' : 'settings-slider__button--unselected'
|
48 |
)}
|
49 |
>
|
50 |
{options.left.text}
|
@@ -53,7 +53,7 @@ export const SettingsSlider = memo(<T,>({ selected, options, setSelected }: Sett
|
|
53 |
onClick={() => setSelected?.(options.right.value)}
|
54 |
className={classNames(
|
55 |
'settings-slider__button',
|
56 |
-
!isLeftSelected ? 'settings-slider__button--selected' : 'settings-slider__button--unselected'
|
57 |
)}
|
58 |
>
|
59 |
{options.right.text}
|
|
|
27 |
<motion.div
|
28 |
className={classNames(
|
29 |
'settings-slider__thumb',
|
30 |
+
isLeftSelected ? 'settings-slider__thumb--left' : 'settings-slider__thumb--right',
|
31 |
)}
|
32 |
initial={false}
|
33 |
animate={{
|
|
|
44 |
onClick={() => setSelected?.(options.left.value)}
|
45 |
className={classNames(
|
46 |
'settings-slider__button',
|
47 |
+
isLeftSelected ? 'settings-slider__button--selected' : 'settings-slider__button--unselected',
|
48 |
)}
|
49 |
>
|
50 |
{options.left.text}
|
|
|
53 |
onClick={() => setSelected?.(options.right.value)}
|
54 |
className={classNames(
|
55 |
'settings-slider__button',
|
56 |
+
!isLeftSelected ? 'settings-slider__button--selected' : 'settings-slider__button--unselected',
|
57 |
)}
|
58 |
>
|
59 |
{options.right.text}
|