Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,16 @@ You have two options for running Bolt.DIY: directly on your machine or using Doc
```bash
pnpm run dev
```



To run the application with a custom base path (for example, `/bolt`), use:

```bash
VITE_BASE_PATH=/bolt pnpm run dev
```

Replace `/bolt` with your desired base path if needed. This sets the frontend route base path.

### Option 2: Using Docker

This option requires some familiarity with Docker but provides a more isolated environment.
Expand Down Expand Up @@ -378,7 +387,7 @@ This method is recommended for developers who want to:
Hint: Be aware that this can have beta-features and more likely got bugs than the stable release

>**Open the WebUI to test (Default: http://localhost:5173)**
> - Beginners:
> - Beginners:
> - Try to use a sophisticated Provider/Model like Anthropic with Claude Sonnet 3.x Models to get best results
> - Explanation: The System Prompt currently implemented in bolt.diy cant cover the best performance for all providers and models out there. So it works better with some models, then other, even if the models itself are perfect for >programming
> - Future: Planned is a Plugin/Extentions-Library so there can be different System Prompts for different Models, which will help to get better results
Expand All @@ -396,7 +405,7 @@ To get the latest changes from the repository:
2. **Pull Latest Updates**:

```bash
git pull
git pull
```

3. **Update Dependencies**:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,10 @@ export function GitLabRepositorySelector({ onClone, className }: GitLabRepositor
setError(null);

try {
const response = await fetch('/api/gitlab-projects', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/gitlab-projects`.replace(/\/+/g, '/');
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
5 changes: 4 additions & 1 deletion app/components/@settings/tabs/supabase/SupabaseTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,10 @@ export default function SupabaseTab() {
});

try {
const response = await fetch('/api/supabase-user', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/supabase-user`.replace(/\/+/g, '/');
const response = await fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Expand Down
8 changes: 5 additions & 3 deletions app/components/chat/APIKeyManager.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export const APIKeyManager: React.FC<APIKeyManagerProps> = ({ provider, apiKey,
}

try {
const response = await fetch(`/api/check-env-key?provider=${encodeURIComponent(provider.name)}`);
const envBasePath = import.meta.env.VITE_BASE_PATH || '';
const basePath = envBasePath.replace(/\/$/, '');
const response = await fetch(`${basePath}/api/check-env-key?provider=${encodeURIComponent(provider.name)}`);
const data = await response.json();
const isSet = (data as { isSet: boolean }).isSet;

Expand Down Expand Up @@ -121,8 +123,8 @@ export const APIKeyManager: React.FC<APIKeyManagerProps> = ({ provider, apiKey,
value={tempKey}
placeholder="Enter API Key"
onChange={(e) => setTempKey(e.target.value)}
className="w-[300px] px-3 py-1.5 text-sm rounded border border-bolt-elements-borderColor
bg-bolt-elements-prompt-background text-bolt-elements-textPrimary
className="w-[300px] px-3 py-1.5 text-sm rounded border border-bolt-elements-borderColor
bg-bolt-elements-prompt-background text-bolt-elements-textPrimary
focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus"
/>
<IconButton
Expand Down
10 changes: 8 additions & 2 deletions app/components/chat/BaseChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,11 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
}

setIsModelLoading('all');
fetch('/api/models')

const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/models`.replace(/\/+/g, '/');
fetch(apiUrl)
.then((response) => response.json())
.then((data) => {
const typedData = data as { modelList: ModelInfo[] };
Expand All @@ -237,7 +241,9 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
let providerModels: ModelInfo[] = [];

try {
const response = await fetch(`/api/models/${encodeURIComponent(providerName)}`);
const envBasePath = import.meta.env.VITE_BASE_PATH || '';
const basePath = envBasePath.replace(/\/$/, '');
const response = await fetch(`${basePath}/api/models/${encodeURIComponent(providerName)}`);
const data = await response.json();
providerModels = (data as { modelList: ModelInfo[] }).modelList;
} catch (error) {
Expand Down
4 changes: 3 additions & 1 deletion app/components/chat/Chat.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ export const ChatImpl = memo(
const [selectedElement, setSelectedElement] = useState<ElementInfo | null>(null);
const mcpSettings = useMCPStore((state) => state.settings);

const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const {
messages,
isLoading,
Expand All @@ -165,7 +167,7 @@ export const ChatImpl = memo(
setData,
addToolResult,
} = useChat({
api: '/api/chat',
api: `${basePath}/api/chat`.replace(/\/+/g, '/'),
body: {
apiKeys,
files,
Expand Down
4 changes: 3 additions & 1 deletion app/components/chat/Messages.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ export const Messages = forwardRef<HTMLDivElement, MessagesProps>(
}

const urlId = await forkChat(db, chatId.get()!, messageId);
window.location.href = `/chat/${urlId}`;
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath && envBasePath !== '/' ? envBasePath.replace(/\/$/, '') : '';
window.location.href = `${basePath}/chat/${urlId}`;
} catch (error) {
toast.error('Failed to fork chat: ' + (error as Error).message);
}
Expand Down
6 changes: 5 additions & 1 deletion app/components/chat/StarterTemplates.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ interface FrameworkLinkProps {

const FrameworkLink: React.FC<FrameworkLinkProps> = ({ template }) => (
<a
href={`/git?url=https://github.com/${template.githubRepo}.git`}
href={`${
import.meta.env.VITE_BASE_PATH && import.meta.env.VITE_BASE_PATH !== '/'
? import.meta.env.VITE_BASE_PATH.replace(/\/$/, '')
: ''
}/git?url=https://github.com/${template.githubRepo}.git`}
data-state="closed"
data-discover="true"
className="items-center justify-center"
Expand Down
5 changes: 4 additions & 1 deletion app/components/chat/SupabaseAlert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ export function SupabaseChatAlert({ alert, clearAlert, postMessage }: Props) {
setIsExecuting(true);

try {
const response = await fetch('/api/supabase/query', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/supabase/query`.replace(/\/+/g, '/');
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
11 changes: 8 additions & 3 deletions app/components/chat/VercelDeploymentLink.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,14 @@ export function VercelDeploymentLink() {
}

// Fallback to API call if not found in fetched projects
const fallbackResponse = await fetch(`/api/vercel-deploy?projectId=${projectId}&token=${connection.token}`, {
method: 'GET',
});
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath && envBasePath !== '/' ? envBasePath.replace(/\/$/, '') : '';
const fallbackResponse = await fetch(
`${basePath}/api/vercel-deploy?projectId=${projectId}&token=${connection.token}`,
{
method: 'GET',
},
);

const data = await fallbackResponse.json();

Expand Down
5 changes: 4 additions & 1 deletion app/components/deploy/NetlifyDeploy.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,10 @@ export function useNetlifyDeploy() {
// Use chatId instead of artifact.id
const existingSiteId = localStorage.getItem(`netlify-site-${currentChatId}`);

const response = await fetch('/api/netlify-deploy', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/netlify-deploy`.replace(/\/+/g, '/');
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
5 changes: 4 additions & 1 deletion app/components/deploy/VercelDeploy.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,10 @@ export function useVercelDeploy() {
// Use chatId instead of artifact.id
const existingProjectId = localStorage.getItem(`vercel-project-${currentChatId}`);

const response = await fetch('/api/vercel-deploy', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/vercel-deploy`.replace(/\/+/g, '/');
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
15 changes: 12 additions & 3 deletions app/components/git/GitUrlImport.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,10 @@ ${escapeBoltTags(file.content)}
console.error('Error during import:', error);
toast.error('Failed to import repository');
setLoading(false);
window.location.href = '/';

const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath && envBasePath !== '/' ? envBasePath.replace(/\/$/, '') : '';
window.location.href = `${basePath}`;

return;
}
Expand All @@ -121,15 +124,21 @@ ${escapeBoltTags(file.content)}
const url = searchParams.get('url');

if (!url) {
window.location.href = '/';
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath && envBasePath !== '/' ? envBasePath.replace(/\/$/, '') : '';
window.location.href = `${basePath}`;

return;
}

importRepo(url).catch((error) => {
console.error('Error importing repo:', error);
toast.error('Failed to import repository');
setLoading(false);
window.location.href = '/';

const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath && envBasePath !== '/' ? envBasePath.replace(/\/$/, '') : '';
window.location.href = `${basePath}`;
});
setImported(true);
}, [searchParams, historyReady, gitReady, imported]);
Expand Down
11 changes: 8 additions & 3 deletions app/components/header/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ import { ChatDescription } from '~/lib/persistence/ChatDescription.client';
export function Header() {
const chat = useStore(chatStore);

// Ensure asset URLs always start with a leading slash
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const assetUrl = (file: string) => `${basePath}/${file}`.replace(/\/+/g, '/');

return (
<header
className={classNames('flex items-center px-4 border-b h-[var(--header-height)]', {
Expand All @@ -17,10 +22,10 @@ export function Header() {
>
<div className="flex items-center gap-2 z-logo text-bolt-elements-textPrimary cursor-pointer">
<div className="i-ph:sidebar-simple-duotone text-xl" />
<a href="/" className="text-2xl font-semibold text-accent flex items-center">
<a href={basePath || '/'} className="text-2xl font-semibold text-accent flex items-center">
{/* <span className="i-bolt:logo-text?mask w-[46px] inline-block" /> */}
<img src="/logo-light-styled.png" alt="logo" className="w-[90px] inline-block dark:hidden" />
<img src="/logo-dark-styled.png" alt="logo" className="w-[90px] inline-block hidden dark:block" />
<img src={assetUrl('logo-light-styled.png')} alt="logo" className="w-[90px] inline-block dark:hidden" />
<img src={assetUrl('logo-dark-styled.png')} alt="logo" className="w-[90px] inline-block hidden dark:block" />
</a>
</div>
{chat.started && ( // Display ChatDescription and HeaderActionButtons only when the chat has started.
Expand Down
2 changes: 1 addition & 1 deletion app/components/sidebar/HistoryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export function HistoryItem({
</form>
) : (
<a
href={`/chat/${item.urlId}`}
href={`${import.meta.env.VITE_BASE_PATH && import.meta.env.VITE_BASE_PATH !== '/' ? import.meta.env.VITE_BASE_PATH.replace(/\/$/, '') : ''}/chat/${item.urlId}`}
className="flex w-full relative truncate block"
onClick={selectionMode ? handleItemClick : undefined}
>
Expand Down
5 changes: 4 additions & 1 deletion app/components/sidebar/Menu.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,10 @@ export const Menu = () => {
if (chatId.get() === item.id) {
// hard page navigation to clear the stores
console.log('Navigating away from deleted chat');
window.location.pathname = '/';
window.location.pathname =
import.meta.env.VITE_BASE_PATH && import.meta.env.VITE_BASE_PATH !== '/'
? `${import.meta.env.VITE_BASE_PATH.replace(/\/$/, '')}/`
: '/';
}
})
.catch((error) => {
Expand Down
7 changes: 5 additions & 2 deletions app/components/ui/BranchSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ export function BranchSelector({
let response: Response;

if (provider === 'github') {
response = await fetch('/api/github-branches', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/github-branches`.replace(/\/+/g, '/');
response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
Expand All @@ -70,7 +73,7 @@ export function BranchSelector({
throw new Error('Project ID is required for GitLab repositories');
}

response = await fetch('/api/gitlab-branches', {
response = await fetch(`${import.meta.env.VITE_BASE_PATH || ''}/api/gitlab-branches`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
Expand Down
7 changes: 4 additions & 3 deletions app/lib/api/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ export const checkConnection = async (): Promise<ConnectionStatus> => {
}

// Try multiple endpoints in case one fails
const BASE_PATH = (import.meta.env.VITE_BASE_PATH || '').replace(/\/$/, '');
const endpoints = [
'/api/health',
'/', // Fallback to root route
'/favicon.ico', // Another common fallback
`${BASE_PATH}/api/health`.replace(/\/+/g, '/'),
`${BASE_PATH}/`, // Fallback to root route
`${BASE_PATH}/favicon.ico`, // Another common fallback
];

let latency = 0;
Expand Down
4 changes: 3 additions & 1 deletion app/lib/hooks/useDataOperations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -990,7 +990,9 @@ export function useDataOperations({
showProgress('Retrieving API keys', 25);

// Create a fetch request to get API keys from server
const response = await fetch('/api/export-api-keys');
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath && envBasePath !== '/' ? envBasePath.replace(/\/$/, '') : '';
const response = await fetch(`${basePath}/api/export-api-keys`);

if (!response.ok) {
throw new Error('Failed to retrieve API keys from server');
Expand Down
6 changes: 5 additions & 1 deletion app/lib/hooks/useGit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,11 @@ export function useGit() {
depth: 1,
singleBranch: true,
ref: branch,
corsProxy: '/api/git-proxy',
corsProxy: `${
import.meta.env.VITE_BASE_PATH && import.meta.env.VITE_BASE_PATH !== '/'
? import.meta.env.VITE_BASE_PATH.replace(/\/$/, '')
: ''
}/api/git-proxy`,
headers,
onProgress: (event) => {
console.log('Git clone progress:', event);
Expand Down
2 changes: 1 addition & 1 deletion app/lib/hooks/useGitHubConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ export function useGitHubConnection(): UseGitHubConnectionReturn {
const isServerSide = !connection.token;

if (isServerSide) {
const response = await fetch('/api/github-user');
const response = await fetch(`${import.meta.env.VITE_BASE_PATH || ''}/api/github-user`);
return response.ok;
}

Expand Down
4 changes: 3 additions & 1 deletion app/lib/hooks/useGitHubStats.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,9 @@ export function useGitHubStats(

if (isServerSide || !connection.token) {
// Use server-side API for stats
const response = await fetch('/api/github-stats');
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath && envBasePath !== '/' ? envBasePath.replace(/\/$/, '') : '';
const response = await fetch(`${basePath}/api/github-stats`);

if (!response.ok) {
if (response.status === 401) {
Expand Down
5 changes: 4 additions & 1 deletion app/lib/hooks/usePromptEnhancer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ export function usePromptEnhancer() {
requestBody.apiKeys = apiKeys;
}

const response = await fetch('/api/enhancer', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/enhancer`.replace(/\/+/g, '/');
const response = await fetch(apiUrl, {
method: 'POST',
body: JSON.stringify(requestBody),
});
Expand Down
5 changes: 4 additions & 1 deletion app/lib/hooks/useSupabaseConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ export function useSupabaseConnection() {
try {
const cleanToken = connection.token.trim();

const response = await fetch('/api/supabase', {
const envBasePath = import.meta.env.VITE_BASE_PATH;
const basePath = envBasePath ? (envBasePath.startsWith('/') ? envBasePath : '/' + envBasePath) : '';
const apiUrl = `${basePath}/api/supabase`.replace(/\/+/g, '/');
const response = await fetch(apiUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Expand Down
Loading