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',
|