feat: add Starter template menu in homepage (#884)
Browse files* added icons and component
* updated unocss to add dynamic icons
* removed temp logs
* updated readme
- README.md +2 -1
- app/components/chat/BaseChat.tsx +19 -15
- app/components/chat/StarterTemplates.tsx +37 -0
- app/components/settings/data/DataTab.tsx +38 -37
- app/types/template.ts +8 -0
- app/utils/constants.ts +94 -0
- icons/angular.svg +1 -0
- icons/astro.svg +1 -0
- icons/nativescript.svg +1 -0
- icons/nextjs.svg +1 -0
- icons/nuxt.svg +1 -0
- icons/qwik.svg +1 -0
- icons/react.svg +1 -0
- icons/remix.svg +1 -0
- icons/remotion.svg +1 -0
- icons/slidev.svg +1 -0
- icons/svelte.svg +1 -0
- icons/typescript.svg +1 -0
- icons/vite.svg +1 -0
- icons/vue.svg +1 -0
- uno.config.ts +3 -0
README.md
CHANGED
@@ -62,6 +62,7 @@ bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMed
|
|
62 |
- β
Detect package.json and commands to auto install & run preview for folder and git import (@wonderwhy-er)
|
63 |
- β
Selection tool to target changes visually (@emcconnell)
|
64 |
- β
Detect terminal Errors and ask bolt to fix it (@thecodacus)
|
|
|
65 |
- β¬ **HIGH PRIORITY** - Prevent bolt from rewriting files as often (file locking and diffs)
|
66 |
- β¬ **HIGH PRIORITY** - Better prompting for smaller LLMs (code window sometimes doesn't start)
|
67 |
- β¬ **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
|
|
71 |
- β¬ Upload documents for knowledge - UI design templates, a code base to reference coding style, etc.
|
72 |
- β¬ Voice prompting
|
73 |
- β¬ Azure Open AI API Integration
|
74 |
-
-
|
75 |
- β¬ Vertex AI Integration
|
76 |
|
77 |
## Features
|
|
|
62 |
- β
Detect package.json and commands to auto install & run preview for folder and git import (@wonderwhy-er)
|
63 |
- β
Selection tool to target changes visually (@emcconnell)
|
64 |
- β
Detect terminal Errors and ask bolt to fix it (@thecodacus)
|
65 |
+
- β
Add Starter Template Options (@thecodacus)
|
66 |
- β¬ **HIGH PRIORITY** - Prevent bolt from rewriting files as often (file locking and diffs)
|
67 |
- β¬ **HIGH PRIORITY** - Better prompting for smaller LLMs (code window sometimes doesn't start)
|
68 |
- β¬ **HIGH PRIORITY** - Run agents in the backend as opposed to a single model call
|
|
|
72 |
- β¬ Upload documents for knowledge - UI design templates, a code base to reference coding style, etc.
|
73 |
- β¬ Voice prompting
|
74 |
- β¬ Azure Open AI API Integration
|
75 |
+
- β
Perplexity Integration (@meetpateltech)
|
76 |
- β¬ Vertex AI Integration
|
77 |
|
78 |
## Features
|
app/components/chat/BaseChat.tsx
CHANGED
@@ -28,6 +28,7 @@ import { SpeechRecognitionButton } from '~/components/chat/SpeechRecognition';
|
|
28 |
import type { IProviderSetting, ProviderInfo } from '~/types/model';
|
29 |
import { ScreenshotStateManager } from './ScreenshotStateManager';
|
30 |
import { toast } from 'react-toastify';
|
|
|
31 |
import type { ActionAlert } from '~/types/actions';
|
32 |
import ChatAlert from './ChatAlert';
|
33 |
|
@@ -569,21 +570,24 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
|
|
569 |
</div>
|
570 |
</div>
|
571 |
</div>
|
572 |
-
|
573 |
-
|
574 |
-
|
575 |
-
|
576 |
-
|
577 |
-
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
584 |
-
|
585 |
-
|
586 |
-
|
|
|
|
|
|
|
587 |
</div>
|
588 |
<ClientOnly>{() => <Workbench chatStarted={chatStarted} isStreaming={isStreaming} />}</ClientOnly>
|
589 |
</div>
|
|
|
28 |
import type { IProviderSetting, ProviderInfo } from '~/types/model';
|
29 |
import { ScreenshotStateManager } from './ScreenshotStateManager';
|
30 |
import { toast } from 'react-toastify';
|
31 |
+
import StarterTemplates from './StarterTemplates';
|
32 |
import type { ActionAlert } from '~/types/actions';
|
33 |
import ChatAlert from './ChatAlert';
|
34 |
|
|
|
570 |
</div>
|
571 |
</div>
|
572 |
</div>
|
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>
|
591 |
</div>
|
592 |
<ClientOnly>{() => <Workbench chatStarted={chatStarted} isStreaming={isStreaming} />}</ClientOnly>
|
593 |
</div>
|
app/components/chat/StarterTemplates.tsx
ADDED
@@ -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
CHANGED
@@ -5,7 +5,6 @@ import { toast } from 'react-toastify';
|
|
5 |
import { db, deleteById, getAll } from '~/lib/persistence';
|
6 |
import { logStore } from '~/lib/stores/logs';
|
7 |
import { classNames } from '~/utils/classNames';
|
8 |
-
import styles from '~/components/settings/Settings.module.scss';
|
9 |
|
10 |
// List of supported providers that can have API keys
|
11 |
const API_KEY_PROVIDERS = [
|
@@ -25,8 +24,6 @@ const API_KEY_PROVIDERS = [
|
|
25 |
'AzureOpenAI',
|
26 |
] as const;
|
27 |
|
28 |
-
type Provider = typeof API_KEY_PROVIDERS[number];
|
29 |
-
|
30 |
interface ApiKeys {
|
31 |
[key: string]: string;
|
32 |
}
|
@@ -52,6 +49,7 @@ export default function DataTab() {
|
|
52 |
const error = new Error('Database is not available');
|
53 |
logStore.logError('Failed to export chats - DB unavailable', error);
|
54 |
toast.error('Database is not available');
|
|
|
55 |
return;
|
56 |
}
|
57 |
|
@@ -83,11 +81,13 @@ export default function DataTab() {
|
|
83 |
const error = new Error('Database is not available');
|
84 |
logStore.logError('Failed to delete chats - DB unavailable', error);
|
85 |
toast.error('Database is not available');
|
|
|
86 |
return;
|
87 |
}
|
88 |
|
89 |
try {
|
90 |
setIsDeleting(true);
|
|
|
91 |
const allChats = await getAll(db);
|
92 |
await Promise.all(allChats.map((chat) => deleteById(db!, chat.id)));
|
93 |
logStore.logSystem('All chats deleted successfully', { count: allChats.length });
|
@@ -125,16 +125,22 @@ export default function DataTab() {
|
|
125 |
|
126 |
const handleImportSettings = (event: React.ChangeEvent<HTMLInputElement>) => {
|
127 |
const file = event.target.files?.[0];
|
128 |
-
|
|
|
|
|
|
|
129 |
|
130 |
const reader = new FileReader();
|
|
|
131 |
reader.onload = (e) => {
|
132 |
try {
|
133 |
const settings = JSON.parse(e.target?.result as string);
|
134 |
-
|
135 |
Object.entries(settings).forEach(([key, value]) => {
|
136 |
if (key === 'bolt_theme') {
|
137 |
-
if (value)
|
|
|
|
|
138 |
} else if (value) {
|
139 |
Cookies.set(key, value as string);
|
140 |
}
|
@@ -152,14 +158,14 @@ export default function DataTab() {
|
|
152 |
|
153 |
const handleExportApiKeyTemplate = () => {
|
154 |
const template: ApiKeys = {};
|
155 |
-
API_KEY_PROVIDERS.forEach(provider => {
|
156 |
template[`${provider}_API_KEY`] = '';
|
157 |
});
|
158 |
|
159 |
-
template
|
160 |
-
template
|
161 |
-
template
|
162 |
-
template
|
163 |
|
164 |
downloadAsJson(template, 'api-keys-template.json');
|
165 |
toast.success('API keys template exported successfully');
|
@@ -167,17 +173,22 @@ export default function DataTab() {
|
|
167 |
|
168 |
const handleImportApiKeys = (event: React.ChangeEvent<HTMLInputElement>) => {
|
169 |
const file = event.target.files?.[0];
|
170 |
-
|
|
|
|
|
|
|
171 |
|
172 |
const reader = new FileReader();
|
|
|
173 |
reader.onload = (e) => {
|
174 |
try {
|
175 |
const apiKeys = JSON.parse(e.target?.result as string);
|
176 |
let importedCount = 0;
|
177 |
const consolidatedKeys: Record<string, string> = {};
|
178 |
|
179 |
-
API_KEY_PROVIDERS.forEach(provider => {
|
180 |
const keyName = `${provider}_API_KEY`;
|
|
|
181 |
if (apiKeys[keyName]) {
|
182 |
consolidatedKeys[provider] = apiKeys[keyName];
|
183 |
importedCount++;
|
@@ -187,13 +198,14 @@ export default function DataTab() {
|
|
187 |
if (importedCount > 0) {
|
188 |
// Store all API keys in a single cookie as JSON
|
189 |
Cookies.set('apiKeys', JSON.stringify(consolidatedKeys));
|
190 |
-
|
191 |
// Also set individual cookies for backward compatibility
|
192 |
Object.entries(consolidatedKeys).forEach(([provider, key]) => {
|
193 |
Cookies.set(`${provider}_API_KEY`, key);
|
194 |
});
|
195 |
|
196 |
toast.success(`Successfully imported ${importedCount} API keys/URLs. Refreshing page to apply changes...`);
|
|
|
197 |
// Reload the page after a short delay to allow the toast to be seen
|
198 |
setTimeout(() => {
|
199 |
window.location.reload();
|
@@ -203,12 +215,13 @@ export default function DataTab() {
|
|
203 |
}
|
204 |
|
205 |
// 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(
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
211 |
-
|
|
|
212 |
} catch (error) {
|
213 |
toast.error('Failed to import API keys. Make sure the file is a valid JSON file.');
|
214 |
console.error('Failed to import API keys:', error);
|
@@ -226,9 +239,7 @@ export default function DataTab() {
|
|
226 |
<div className="flex flex-col gap-4">
|
227 |
<div>
|
228 |
<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>
|
232 |
<div className="flex gap-4">
|
233 |
<button
|
234 |
onClick={handleExportAllChats}
|
@@ -241,7 +252,7 @@ export default function DataTab() {
|
|
241 |
disabled={isDeleting}
|
242 |
className={classNames(
|
243 |
'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' : ''
|
245 |
)}
|
246 |
>
|
247 |
{isDeleting ? 'Deleting...' : 'Delete All Chats'}
|
@@ -263,12 +274,7 @@ export default function DataTab() {
|
|
263 |
</button>
|
264 |
<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">
|
265 |
Import Settings
|
266 |
-
<input
|
267 |
-
type="file"
|
268 |
-
accept=".json"
|
269 |
-
onChange={handleImportSettings}
|
270 |
-
className="hidden"
|
271 |
-
/>
|
272 |
</label>
|
273 |
</div>
|
274 |
</div>
|
@@ -287,12 +293,7 @@ export default function DataTab() {
|
|
287 |
</button>
|
288 |
<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">
|
289 |
Import API Keys
|
290 |
-
<input
|
291 |
-
type="file"
|
292 |
-
accept=".json"
|
293 |
-
onChange={handleImportApiKeys}
|
294 |
-
className="hidden"
|
295 |
-
/>
|
296 |
</label>
|
297 |
</div>
|
298 |
</div>
|
@@ -301,4 +302,4 @@ export default function DataTab() {
|
|
301 |
</div>
|
302 |
</div>
|
303 |
);
|
304 |
-
}
|
|
|
5 |
import { db, deleteById, getAll } from '~/lib/persistence';
|
6 |
import { logStore } from '~/lib/stores/logs';
|
7 |
import { classNames } from '~/utils/classNames';
|
|
|
8 |
|
9 |
// List of supported providers that can have API keys
|
10 |
const API_KEY_PROVIDERS = [
|
|
|
24 |
'AzureOpenAI',
|
25 |
] as const;
|
26 |
|
|
|
|
|
27 |
interface ApiKeys {
|
28 |
[key: string]: string;
|
29 |
}
|
|
|
49 |
const error = new Error('Database is not available');
|
50 |
logStore.logError('Failed to export chats - DB unavailable', error);
|
51 |
toast.error('Database is not available');
|
52 |
+
|
53 |
return;
|
54 |
}
|
55 |
|
|
|
81 |
const error = new Error('Database is not available');
|
82 |
logStore.logError('Failed to delete chats - DB unavailable', error);
|
83 |
toast.error('Database is not available');
|
84 |
+
|
85 |
return;
|
86 |
}
|
87 |
|
88 |
try {
|
89 |
setIsDeleting(true);
|
90 |
+
|
91 |
const allChats = await getAll(db);
|
92 |
await Promise.all(allChats.map((chat) => deleteById(db!, chat.id)));
|
93 |
logStore.logSystem('All chats deleted successfully', { count: allChats.length });
|
|
|
125 |
|
126 |
const handleImportSettings = (event: React.ChangeEvent<HTMLInputElement>) => {
|
127 |
const file = event.target.files?.[0];
|
128 |
+
|
129 |
+
if (!file) {
|
130 |
+
return;
|
131 |
+
}
|
132 |
|
133 |
const reader = new FileReader();
|
134 |
+
|
135 |
reader.onload = (e) => {
|
136 |
try {
|
137 |
const settings = JSON.parse(e.target?.result as string);
|
138 |
+
|
139 |
Object.entries(settings).forEach(([key, value]) => {
|
140 |
if (key === 'bolt_theme') {
|
141 |
+
if (value) {
|
142 |
+
localStorage.setItem(key, value as string);
|
143 |
+
}
|
144 |
} else if (value) {
|
145 |
Cookies.set(key, value as string);
|
146 |
}
|
|
|
158 |
|
159 |
const handleExportApiKeyTemplate = () => {
|
160 |
const template: ApiKeys = {};
|
161 |
+
API_KEY_PROVIDERS.forEach((provider) => {
|
162 |
template[`${provider}_API_KEY`] = '';
|
163 |
});
|
164 |
|
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 = '';
|
169 |
|
170 |
downloadAsJson(template, 'api-keys-template.json');
|
171 |
toast.success('API keys template exported successfully');
|
|
|
173 |
|
174 |
const handleImportApiKeys = (event: React.ChangeEvent<HTMLInputElement>) => {
|
175 |
const file = event.target.files?.[0];
|
176 |
+
|
177 |
+
if (!file) {
|
178 |
+
return;
|
179 |
+
}
|
180 |
|
181 |
const reader = new FileReader();
|
182 |
+
|
183 |
reader.onload = (e) => {
|
184 |
try {
|
185 |
const apiKeys = JSON.parse(e.target?.result as string);
|
186 |
let importedCount = 0;
|
187 |
const consolidatedKeys: Record<string, string> = {};
|
188 |
|
189 |
+
API_KEY_PROVIDERS.forEach((provider) => {
|
190 |
const keyName = `${provider}_API_KEY`;
|
191 |
+
|
192 |
if (apiKeys[keyName]) {
|
193 |
consolidatedKeys[provider] = apiKeys[keyName];
|
194 |
importedCount++;
|
|
|
198 |
if (importedCount > 0) {
|
199 |
// Store all API keys in a single cookie as JSON
|
200 |
Cookies.set('apiKeys', JSON.stringify(consolidatedKeys));
|
201 |
+
|
202 |
// Also set individual cookies for backward compatibility
|
203 |
Object.entries(consolidatedKeys).forEach(([provider, key]) => {
|
204 |
Cookies.set(`${provider}_API_KEY`, key);
|
205 |
});
|
206 |
|
207 |
toast.success(`Successfully imported ${importedCount} API keys/URLs. Refreshing page to apply changes...`);
|
208 |
+
|
209 |
// Reload the page after a short delay to allow the toast to be seen
|
210 |
setTimeout(() => {
|
211 |
window.location.reload();
|
|
|
215 |
}
|
216 |
|
217 |
// Set base URLs if they exist
|
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 |
+
);
|
225 |
} catch (error) {
|
226 |
toast.error('Failed to import API keys. Make sure the file is a valid JSON file.');
|
227 |
console.error('Failed to import API keys:', error);
|
|
|
239 |
<div className="flex flex-col gap-4">
|
240 |
<div>
|
241 |
<h4 className="text-bolt-elements-textPrimary mb-2">Chat History</h4>
|
242 |
+
<p className="text-sm text-bolt-elements-textSecondary mb-4">Export or delete all your chat history.</p>
|
|
|
|
|
243 |
<div className="flex gap-4">
|
244 |
<button
|
245 |
onClick={handleExportAllChats}
|
|
|
252 |
disabled={isDeleting}
|
253 |
className={classNames(
|
254 |
'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',
|
255 |
+
isDeleting ? 'opacity-50 cursor-not-allowed' : '',
|
256 |
)}
|
257 |
>
|
258 |
{isDeleting ? 'Deleting...' : 'Delete All Chats'}
|
|
|
274 |
</button>
|
275 |
<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">
|
276 |
Import Settings
|
277 |
+
<input type="file" accept=".json" onChange={handleImportSettings} className="hidden" />
|
|
|
|
|
|
|
|
|
|
|
278 |
</label>
|
279 |
</div>
|
280 |
</div>
|
|
|
293 |
</button>
|
294 |
<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">
|
295 |
Import API Keys
|
296 |
+
<input type="file" accept=".json" onChange={handleImportApiKeys} className="hidden" />
|
|
|
|
|
|
|
|
|
|
|
297 |
</label>
|
298 |
</div>
|
299 |
</div>
|
|
|
302 |
</div>
|
303 |
</div>
|
304 |
);
|
305 |
+
}
|
app/types/template.ts
ADDED
@@ -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 |
+
}
|
app/utils/constants.ts
CHANGED
@@ -2,6 +2,7 @@ import type { IProviderSetting } from '~/types/model';
|
|
2 |
|
3 |
import { LLMManager } from '~/lib/modules/llm/manager';
|
4 |
import type { ModelInfo } from '~/lib/modules/llm/types';
|
|
|
5 |
|
6 |
export const WORK_DIR_NAME = 'project';
|
7 |
export const WORK_DIR = `/home/${WORK_DIR_NAME}`;
|
@@ -359,3 +360,96 @@ async function initializeModelList(options: {
|
|
359 |
|
360 |
// initializeModelList({})
|
361 |
export { initializeModelList, providerBaseUrlEnvKeys, MODEL_LIST };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2 |
|
3 |
import { LLMManager } from '~/lib/modules/llm/manager';
|
4 |
import type { ModelInfo } from '~/lib/modules/llm/types';
|
5 |
+
import type { Template } from '~/types/template';
|
6 |
|
7 |
export const WORK_DIR_NAME = 'project';
|
8 |
export const WORK_DIR = `/home/${WORK_DIR_NAME}`;
|
|
|
360 |
|
361 |
// initializeModelList({})
|
362 |
export { initializeModelList, providerBaseUrlEnvKeys, MODEL_LIST };
|
363 |
+
|
364 |
+
// starter Templates
|
365 |
+
|
366 |
+
export const STARTER_TEMPLATES: Template[] = [
|
367 |
+
{
|
368 |
+
name: 'bolt-astro-basic',
|
369 |
+
label: 'Astro Basic',
|
370 |
+
description: 'Lightweight Astro starter template for building fast static websites',
|
371 |
+
githubRepo: 'thecodacus/bolt-astro-basic-template',
|
372 |
+
tags: ['astro', 'blog', 'performance'],
|
373 |
+
icon: 'i-bolt:astro',
|
374 |
+
},
|
375 |
+
{
|
376 |
+
name: 'bolt-nextjs-shadcn',
|
377 |
+
label: 'Next.js with shadcn/ui',
|
378 |
+
description: 'Next.js starter fullstack template integrated with shadcn/ui components and styling system',
|
379 |
+
githubRepo: 'thecodacus/bolt-nextjs-shadcn-template',
|
380 |
+
tags: ['nextjs', 'react', 'typescript', 'shadcn', 'tailwind'],
|
381 |
+
icon: 'i-bolt:nextjs',
|
382 |
+
},
|
383 |
+
{
|
384 |
+
name: 'bolt-qwik-ts',
|
385 |
+
label: 'Qwik TypeScript',
|
386 |
+
description: 'Qwik framework starter with TypeScript for building resumable applications',
|
387 |
+
githubRepo: 'thecodacus/bolt-qwik-ts-template',
|
388 |
+
tags: ['qwik', 'typescript', 'performance', 'resumable'],
|
389 |
+
icon: 'i-bolt:qwik',
|
390 |
+
},
|
391 |
+
{
|
392 |
+
name: 'bolt-remix-ts',
|
393 |
+
label: 'Remix TypeScript',
|
394 |
+
description: 'Remix framework starter with TypeScript for full-stack web applications',
|
395 |
+
githubRepo: 'thecodacus/bolt-remix-ts-template',
|
396 |
+
tags: ['remix', 'typescript', 'fullstack', 'react'],
|
397 |
+
icon: 'i-bolt:remix',
|
398 |
+
},
|
399 |
+
{
|
400 |
+
name: 'bolt-slidev',
|
401 |
+
label: 'Slidev Presentation',
|
402 |
+
description: 'Slidev starter template for creating developer-friendly presentations using Markdown',
|
403 |
+
githubRepo: 'thecodacus/bolt-slidev-template',
|
404 |
+
tags: ['slidev', 'presentation', 'markdown'],
|
405 |
+
icon: 'i-bolt:slidev',
|
406 |
+
},
|
407 |
+
{
|
408 |
+
name: 'bolt-sveltekit',
|
409 |
+
label: 'SvelteKit',
|
410 |
+
description: 'SvelteKit starter template for building fast, efficient web applications',
|
411 |
+
githubRepo: 'bolt-sveltekit-template',
|
412 |
+
tags: ['svelte', 'sveltekit', 'typescript'],
|
413 |
+
icon: 'i-bolt:svelte',
|
414 |
+
},
|
415 |
+
{
|
416 |
+
name: 'vanilla-vite',
|
417 |
+
label: 'Vanilla + Vite',
|
418 |
+
description: 'Minimal Vite starter template for vanilla JavaScript projects',
|
419 |
+
githubRepo: 'thecodacus/vanilla-vite-template',
|
420 |
+
tags: ['vite', 'vanilla-js', 'minimal'],
|
421 |
+
icon: 'i-bolt:vite',
|
422 |
+
},
|
423 |
+
{
|
424 |
+
name: 'bolt-vite-react',
|
425 |
+
label: 'React + Vite + typescript',
|
426 |
+
description: 'React starter template powered by Vite for fast development experience',
|
427 |
+
githubRepo: 'thecodacus/bolt-vite-react-ts-template',
|
428 |
+
tags: ['react', 'vite', 'frontend'],
|
429 |
+
icon: 'i-bolt:react',
|
430 |
+
},
|
431 |
+
{
|
432 |
+
name: 'bolt-vite-ts',
|
433 |
+
label: 'Vite + TypeScript',
|
434 |
+
description: 'Vite starter template with TypeScript configuration for type-safe development',
|
435 |
+
githubRepo: 'thecodacus/bolt-vite-ts-template',
|
436 |
+
tags: ['vite', 'typescript', 'minimal'],
|
437 |
+
icon: 'i-bolt:typescript',
|
438 |
+
},
|
439 |
+
{
|
440 |
+
name: 'bolt-vue',
|
441 |
+
label: 'Vue.js',
|
442 |
+
description: 'Vue.js starter template with modern tooling and best practices',
|
443 |
+
githubRepo: 'thecodacus/bolt-vue-template',
|
444 |
+
tags: ['vue', 'typescript', 'frontend'],
|
445 |
+
icon: 'i-bolt:vue',
|
446 |
+
},
|
447 |
+
{
|
448 |
+
name: 'bolt-angular',
|
449 |
+
label: 'Angular Starter',
|
450 |
+
description: 'A modern Angular starter template with TypeScript support and best practices configuration',
|
451 |
+
githubRepo: 'thecodacus/bolt-angular-template',
|
452 |
+
tags: ['angular', 'typescript', 'frontend', 'spa'],
|
453 |
+
icon: 'i-bolt:angular',
|
454 |
+
},
|
455 |
+
];
|
icons/angular.svg
ADDED
|
icons/astro.svg
ADDED
|
icons/nativescript.svg
ADDED
|
icons/nextjs.svg
ADDED
|
icons/nuxt.svg
ADDED
|
icons/qwik.svg
ADDED
|
icons/react.svg
ADDED
|
icons/remix.svg
ADDED
|
icons/remotion.svg
ADDED
|
icons/slidev.svg
ADDED
|
icons/svelte.svg
ADDED
|
icons/typescript.svg
ADDED
|
icons/vite.svg
ADDED
|
icons/vue.svg
ADDED
|
uno.config.ts
CHANGED
@@ -98,6 +98,9 @@ const COLOR_PRIMITIVES = {
|
|
98 |
};
|
99 |
|
100 |
export default defineConfig({
|
|
|
|
|
|
|
101 |
shortcuts: {
|
102 |
'bolt-ease-cubic-bezier': 'ease-[cubic-bezier(0.4,0,0.2,1)]',
|
103 |
'transition-theme': 'transition-[background-color,border-color,color] duration-150 bolt-ease-cubic-bezier',
|
|
|
98 |
};
|
99 |
|
100 |
export default defineConfig({
|
101 |
+
safelist: [
|
102 |
+
...Object.keys(customIconCollection[collectionName]||{}).map(x=>`i-bolt:${x}`)
|
103 |
+
],
|
104 |
shortcuts: {
|
105 |
'bolt-ease-cubic-bezier': 'ease-[cubic-bezier(0.4,0,0.2,1)]',
|
106 |
'transition-theme': 'transition-[background-color,border-color,color] duration-150 bolt-ease-cubic-bezier',
|