Skip to content

Commit 477e6c5

Browse files
committed
Merge remote-tracking branch 'upstream/main'
2 parents 4ae860c + fc4f89f commit 477e6c5

21 files changed

+215
-53
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMed
6262
- ✅ Detect package.json and commands to auto install & run preview for folder and git import (@wonderwhy-er)
6363
- ✅ Selection tool to target changes visually (@emcconnell)
6464
- ✅ Detect terminal Errors and ask bolt to fix it (@thecodacus)
65+
- ✅ Add Starter Template Options (@thecodacus)
6566
-**HIGH PRIORITY** - Prevent bolt from rewriting files as often (file locking and diffs)
6667
-**HIGH PRIORITY** - Better prompting for smaller LLMs (code window sometimes doesn't start)
6768
-**HIGH PRIORITY** - Run agents in the backend as opposed to a single model call
@@ -71,7 +72,7 @@ bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMed
7172
- ⬜ Upload documents for knowledge - UI design templates, a code base to reference coding style, etc.
7273
- ⬜ Voice prompting
7374
- ⬜ Azure Open AI API Integration
74-
- Perplexity Integration
75+
- Perplexity Integration (@meetpateltech)
7576
- ⬜ Vertex AI Integration
7677

7778
## Features

app/components/chat/BaseChat.tsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition';
2828
import type { IProviderSetting, ProviderInfo } from '~/types/model';
2929
import { ScreenshotStateManager } from './ScreenshotStateManager';
3030
import { toast } from 'react-toastify';
31+
import StarterTemplates from './StarterTemplates';
3132
import type { ActionAlert } from '~/types/actions';
3233
import ChatAlert from './ChatAlert';
3334

@@ -569,21 +570,24 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
569570
</div>
570571
</div>
571572
</div>
572-
{!chatStarted && (
573-
<div className="flex justify-center gap-2">
574-
{ImportButtons(importChat)}
575-
<GitCloneButton importChat={importChat} />
576-
</div>
577-
)}
578-
{!chatStarted &&
579-
ExamplePrompts((event, messageInput) => {
580-
if (isStreaming) {
581-
handleStop?.();
582-
return;
583-
}
584-
585-
handleSendMessage?.(event, messageInput);
586-
})}
573+
<div className="flex flex-col justify-center gap-5">
574+
{!chatStarted && (
575+
<div className="flex justify-center gap-2">
576+
{ImportButtons(importChat)}
577+
<GitCloneButton importChat={importChat} />
578+
</div>
579+
)}
580+
{!chatStarted &&
581+
ExamplePrompts((event, messageInput) => {
582+
if (isStreaming) {
583+
handleStop?.();
584+
return;
585+
}
586+
587+
handleSendMessage?.(event, messageInput);
588+
})}
589+
{!chatStarted && <StarterTemplates />}
590+
</div>
587591
</div>
588592
<ClientOnly>{() => <Workbench chatStarted={chatStarted} isStreaming={isStreaming} />}</ClientOnly>
589593
</div>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import React from 'react';
2+
import type { Template } from '~/types/template';
3+
import { STARTER_TEMPLATES } from '~/utils/constants';
4+
5+
interface FrameworkLinkProps {
6+
template: Template;
7+
}
8+
9+
const FrameworkLink: React.FC<FrameworkLinkProps> = ({ template }) => (
10+
<a
11+
href={`/git?url=https://github.com/${template.githubRepo}.git`}
12+
data-state="closed"
13+
data-discover="true"
14+
className="items-center justify-center "
15+
>
16+
<div
17+
className={`inline-block ${template.icon} w-8 h-8 text-4xl transition-theme opacity-25 hover:opacity-75 transition-all`}
18+
/>
19+
</a>
20+
);
21+
22+
const StarterTemplates: React.FC = () => {
23+
return (
24+
<div className="flex flex-col items-center gap-4">
25+
<span className="text-sm text-gray-500">or start a blank app with your favorite stack</span>
26+
<div className="flex justify-center">
27+
<div className="flex w-70 flex-wrap items-center justify-center gap-4">
28+
{STARTER_TEMPLATES.map((template) => (
29+
<FrameworkLink key={template.name} template={template} />
30+
))}
31+
</div>
32+
</div>
33+
</div>
34+
);
35+
};
36+
37+
export default StarterTemplates;

app/components/settings/data/DataTab.tsx

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { toast } from 'react-toastify';
55
import { db, deleteById, getAll } from '~/lib/persistence';
66
import { logStore } from '~/lib/stores/logs';
77
import { classNames } from '~/utils/classNames';
8-
import styles from '~/components/settings/Settings.module.scss';
98

109
// List of supported providers that can have API keys
1110
const API_KEY_PROVIDERS = [
@@ -25,8 +24,6 @@ const API_KEY_PROVIDERS = [
2524
'AzureOpenAI',
2625
] as const;
2726

28-
type Provider = typeof API_KEY_PROVIDERS[number];
29-
3027
interface ApiKeys {
3128
[key: string]: string;
3229
}
@@ -52,6 +49,7 @@ export default function DataTab() {
5249
const error = new Error('Database is not available');
5350
logStore.logError('Failed to export chats - DB unavailable', error);
5451
toast.error('Database is not available');
52+
5553
return;
5654
}
5755

@@ -83,11 +81,13 @@ export default function DataTab() {
8381
const error = new Error('Database is not available');
8482
logStore.logError('Failed to delete chats - DB unavailable', error);
8583
toast.error('Database is not available');
84+
8685
return;
8786
}
8887

8988
try {
9089
setIsDeleting(true);
90+
9191
const allChats = await getAll(db);
9292
await Promise.all(allChats.map((chat) => deleteById(db!, chat.id)));
9393
logStore.logSystem('All chats deleted successfully', { count: allChats.length });
@@ -125,16 +125,22 @@ export default function DataTab() {
125125

126126
const handleImportSettings = (event: React.ChangeEvent<HTMLInputElement>) => {
127127
const file = event.target.files?.[0];
128-
if (!file) return;
128+
129+
if (!file) {
130+
return;
131+
}
129132

130133
const reader = new FileReader();
134+
131135
reader.onload = (e) => {
132136
try {
133137
const settings = JSON.parse(e.target?.result as string);
134-
138+
135139
Object.entries(settings).forEach(([key, value]) => {
136140
if (key === 'bolt_theme') {
137-
if (value) localStorage.setItem(key, value as string);
141+
if (value) {
142+
localStorage.setItem(key, value as string);
143+
}
138144
} else if (value) {
139145
Cookies.set(key, value as string);
140146
}
@@ -152,32 +158,37 @@ export default function DataTab() {
152158

153159
const handleExportApiKeyTemplate = () => {
154160
const template: ApiKeys = {};
155-
API_KEY_PROVIDERS.forEach(provider => {
161+
API_KEY_PROVIDERS.forEach((provider) => {
156162
template[`${provider}_API_KEY`] = '';
157163
});
158164

159-
template['OPENAI_LIKE_API_BASE_URL'] = '';
160-
template['LMSTUDIO_API_BASE_URL'] = '';
161-
template['OLLAMA_API_BASE_URL'] = '';
162-
template['TOGETHER_API_BASE_URL'] = '';
165+
template.OPENAI_LIKE_API_BASE_URL = '';
166+
template.LMSTUDIO_API_BASE_URL = '';
167+
template.OLLAMA_API_BASE_URL = '';
168+
template.TOGETHER_API_BASE_URL = '';
163169

164170
downloadAsJson(template, 'api-keys-template.json');
165171
toast.success('API keys template exported successfully');
166172
};
167173

168174
const handleImportApiKeys = (event: React.ChangeEvent<HTMLInputElement>) => {
169175
const file = event.target.files?.[0];
170-
if (!file) return;
176+
177+
if (!file) {
178+
return;
179+
}
171180

172181
const reader = new FileReader();
182+
173183
reader.onload = (e) => {
174184
try {
175185
const apiKeys = JSON.parse(e.target?.result as string);
176186
let importedCount = 0;
177187
const consolidatedKeys: Record<string, string> = {};
178188

179-
API_KEY_PROVIDERS.forEach(provider => {
189+
API_KEY_PROVIDERS.forEach((provider) => {
180190
const keyName = `${provider}_API_KEY`;
191+
181192
if (apiKeys[keyName]) {
182193
consolidatedKeys[provider] = apiKeys[keyName];
183194
importedCount++;
@@ -187,13 +198,14 @@ export default function DataTab() {
187198
if (importedCount > 0) {
188199
// Store all API keys in a single cookie as JSON
189200
Cookies.set('apiKeys', JSON.stringify(consolidatedKeys));
190-
201+
191202
// Also set individual cookies for backward compatibility
192203
Object.entries(consolidatedKeys).forEach(([provider, key]) => {
193204
Cookies.set(`${provider}_API_KEY`, key);
194205
});
195206

196207
toast.success(`Successfully imported ${importedCount} API keys/URLs. Refreshing page to apply changes...`);
208+
197209
// Reload the page after a short delay to allow the toast to be seen
198210
setTimeout(() => {
199211
window.location.reload();
@@ -203,12 +215,13 @@ export default function DataTab() {
203215
}
204216

205217
// Set base URLs if they exist
206-
['OPENAI_LIKE_API_BASE_URL', 'LMSTUDIO_API_BASE_URL', 'OLLAMA_API_BASE_URL', 'TOGETHER_API_BASE_URL'].forEach(baseUrl => {
207-
if (apiKeys[baseUrl]) {
208-
Cookies.set(baseUrl, apiKeys[baseUrl]);
209-
}
210-
});
211-
218+
['OPENAI_LIKE_API_BASE_URL', 'LMSTUDIO_API_BASE_URL', 'OLLAMA_API_BASE_URL', 'TOGETHER_API_BASE_URL'].forEach(
219+
(baseUrl) => {
220+
if (apiKeys[baseUrl]) {
221+
Cookies.set(baseUrl, apiKeys[baseUrl]);
222+
}
223+
},
224+
);
212225
} catch (error) {
213226
toast.error('Failed to import API keys. Make sure the file is a valid JSON file.');
214227
console.error('Failed to import API keys:', error);
@@ -226,9 +239,7 @@ export default function DataTab() {
226239
<div className="flex flex-col gap-4">
227240
<div>
228241
<h4 className="text-bolt-elements-textPrimary mb-2">Chat History</h4>
229-
<p className="text-sm text-bolt-elements-textSecondary mb-4">
230-
Export or delete all your chat history.
231-
</p>
242+
<p className="text-sm text-bolt-elements-textSecondary mb-4">Export or delete all your chat history.</p>
232243
<div className="flex gap-4">
233244
<button
234245
onClick={handleExportAllChats}
@@ -241,7 +252,7 @@ export default function DataTab() {
241252
disabled={isDeleting}
242253
className={classNames(
243254
'px-4 py-2 bg-bolt-elements-button-danger-background hover:bg-bolt-elements-button-danger-backgroundHover text-bolt-elements-button-danger-text rounded-lg transition-colors',
244-
isDeleting ? 'opacity-50 cursor-not-allowed' : ''
255+
isDeleting ? 'opacity-50 cursor-not-allowed' : '',
245256
)}
246257
>
247258
{isDeleting ? 'Deleting...' : 'Delete All Chats'}
@@ -263,12 +274,7 @@ export default function DataTab() {
263274
</button>
264275
<label className="px-4 py-2 bg-bolt-elements-button-primary-background hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-textPrimary rounded-lg transition-colors cursor-pointer">
265276
Import Settings
266-
<input
267-
type="file"
268-
accept=".json"
269-
onChange={handleImportSettings}
270-
className="hidden"
271-
/>
277+
<input type="file" accept=".json" onChange={handleImportSettings} className="hidden" />
272278
</label>
273279
</div>
274280
</div>
@@ -287,12 +293,7 @@ export default function DataTab() {
287293
</button>
288294
<label className="px-4 py-2 bg-bolt-elements-button-primary-background hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-textPrimary rounded-lg transition-colors cursor-pointer">
289295
Import API Keys
290-
<input
291-
type="file"
292-
accept=".json"
293-
onChange={handleImportApiKeys}
294-
className="hidden"
295-
/>
296+
<input type="file" accept=".json" onChange={handleImportApiKeys} className="hidden" />
296297
</label>
297298
</div>
298299
</div>
@@ -301,4 +302,4 @@ export default function DataTab() {
301302
</div>
302303
</div>
303304
);
304-
}
305+
}

app/types/template.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export interface Template {
2+
name: string;
3+
label: string;
4+
description: string;
5+
githubRepo: string;
6+
tags?: string[];
7+
icon?: string;
8+
}

0 commit comments

Comments
 (0)