Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
409696f
fix netlify deploy output and uploads (#2107)
embire2 Feb 5, 2026
4e343f1
fix: remove 'use client' directives incompatible with Vite (#2033)
Gerome-Elassaad Feb 5, 2026
b7ef224
feat: add Cerebras and Fireworks AI LLM providers (#2113)
Stijnus Feb 7, 2026
2e254ac
feat: add web URL content fetcher for chat context
Stijnus Feb 5, 2026
abbcaf1
Add domain management admin routes, fix workflow error serialization
claude Feb 17, 2026
9f9adfe
Add coverage/ to .gitignore
claude Feb 17, 2026
961e8b5
Add URL row to site cards, auto-set first custom domain as primary
claude Feb 17, 2026
37c43b8
Fix JSON parse error, improve admin UI, raise domain limit to 10
claude Feb 17, 2026
075335f
Replace DIST DIR with ZIP file manager, fix business dropdown z-index
claude Feb 17, 2026
6adae39
Domain modal min-height, search input styling, expanded upload types
claude Feb 17, 2026
3ba0ece
Fix CSP for JSZip, hide site count until loaded, upload sw.js
claude Feb 17, 2026
41e9e17
Fix #deploy-selected-info margin-bottom: 24px
claude Feb 17, 2026
4b7b09d
Add Pre-Built search parity, domain management UI, GTM/GA tracking, s…
claude Feb 17, 2026
fb44135
Add Place ID removal/linking, AI generate from empty, CTA login flow
claude Feb 18, 2026
bd29cd3
Add site logs modal, subscription-aware delete, escaping fixes, secur…
claude Feb 18, 2026
9263065
Conversion-optimized How-it-Works, button states, UI polish
claude Feb 18, 2026
8cf1919
Fix site-card animation flash, escaping bug, ripple effect, search de…
claude Feb 18, 2026
dfcf41c
Inline editing for site title/slug, slug check API, search dedup, acc…
claude Feb 18, 2026
cddef6a
Combined slug/URL line, domain modal fixes, button quasar effects, 10…
claude Feb 18, 2026
f9ba69c
Login page compact footer, workflow audit logging, inline editor poli…
claude Feb 18, 2026
e7b32f5
Material ripple on all buttons, dynamic sizing, accessibility polish
claude Feb 18, 2026
be7ede8
Transparent inline slug editor, validation hints, save toast, accessi…
claude Feb 18, 2026
ebc348b
Title click-to-edit, AI validation, button stability, domain table, d…
claude Feb 18, 2026
8db56cc
Site card status badges, domain chips, real-time polling, AI tooltip,…
claude Feb 19, 2026
e53fe64
Slug input style sync, login centering, ARIA tabs, performance & acce…
claude Feb 19, 2026
bb743f1
Title style sync, granular workflow logs, relative time, Astro pages
claude Feb 19, 2026
06bdcb0
Normalize global.css formatting from background CSS extraction agent
claude Feb 19, 2026
7476514
Granular workflow logs, R2 file browser, domain UX fixes, Vito's E2E …
claude Feb 19, 2026
c346cec
Security hardening, granular workflow logs in UI, error toasts, E2E c…
claude Feb 19, 2026
fd3a39a
feat: inline edit no-jump icons, code editor, logs, CSP, domain URL m…
claude Feb 19, 2026
89f2191
fix: CSP wildcard, content-type bug, UI polish, security hardening
claude Feb 19, 2026
518c3a5
fix: inline slug edit positioning, CodeMirror editor, domain modal in…
claude Feb 19, 2026
b593718
fix: content-type charset, domain search availability, upgrade CTA
claude Feb 19, 2026
508eca9
fix: reset endpoint, slug R2 migration, editor toolbar, domain modal …
claude Feb 19, 2026
40d7417
feat: dropdown buttons, CNAME verification UI, audit logging, notific…
claude Feb 19, 2026
5a0430f
feat: topbar CTA, file tree navigator, slug rate limiting, comprehens…
claude Feb 19, 2026
1308df6
feat: DomainConnect, file delete, audit logging, search sorting, UI p…
claude Feb 19, 2026
439bdc9
Remove ps-topbar from admin site, fix audit logging pipeline, accessi…
claude Feb 19, 2026
e7477ee
Fix missing orgId in reset workflow (root cause of empty Logs), add u…
claude Feb 20, 2026
cb0a2f6
Fix ZodError build failures, domain search reset, Logs diff-append, e…
claude Feb 20, 2026
476b764
Add Copy Logs button, real-time timestamps, research.json endpoint, e…
claude Feb 20, 2026
c44007f
Fix nullable fields in Zod schemas — LLM returns null for hours.open/…
claude Feb 20, 2026
1ebaf66
fix: make selling-points and images Zod schemas resilient to LLM outp…
claude Feb 20, 2026
7811fec
fix: make score-website step non-blocking with text fallback parser
claude Feb 20, 2026
76cd043
fix: update _manifest.json during workflow upload step
claude Feb 20, 2026
9f5e8fc
feat: add production research.json E2E test and score debug labels
claude Feb 20, 2026
468b217
chore: gitignore local R2 download artifacts (apps/project-sites/sites/)
claude Feb 20, 2026
b56a53f
feat: confidence-weighted v3 research pipeline with Google Places enr…
claude Feb 20, 2026
208993a
feat: overhaul confidence system, add S3/R2 deploy, fix D1 bug, enhan…
claude Feb 20, 2026
a2c754f
test: add S3/R2 deploy unit tests + fix TypeScript errors across code…
claude Feb 20, 2026
220d887
feat: add domain management, file management, UI polish, and Deploy t…
claude Feb 21, 2026
3caa20e
fix: polish Files/Domains/Logs modals, fix preview scaling, remove Cl…
claude Feb 21, 2026
1ee84f3
feat: fix AI Edit to export actual chat messages + add E2E/unit tests
claude Feb 21, 2026
16eb9eb
style: normalize domain modal tabs, enhance UI polish across application
claude Feb 21, 2026
5b232e9
feat: add Stripe Link inline checkout, fix UI polish, enhance chat im…
claude Feb 21, 2026
25df533
fix: vertically center Building text in site card preview placeholder
claude Feb 21, 2026
29d1ada
fix: exclude spec/test files from Remix route compilation
claude Feb 21, 2026
607d2e5
feat: add universal tooltip system and convert all interactive elements
claude Feb 21, 2026
74e2b66
feat: inline Stripe checkout, billing portal modal, quality threshold…
claude Feb 21, 2026
8e3ff91
fix: resolve CORS error by aligning COEP header to credentialless
claude Feb 22, 2026
635432b
Merge branch 'main' into claude/add-domain-management-TFZYF
ProfessorManhattan Feb 22, 2026
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ _worker.bundle
package-lock.json
test-results/
playwright-report/
coverage/
apps/project-sites/sites/

Modelfile
modelfiles
Expand Down
1 change: 0 additions & 1 deletion app/components/chat/ModelSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { useEffect, useState, useRef, useMemo, useCallback } from 'react';
import type { KeyboardEvent } from 'react';
import type { ModelInfo } from '~/lib/modules/llm/types';
import { classNames } from '~/utils/classNames';
import { DEFAULT_MODEL } from '~/utils/constants';
import { LOCAL_PROVIDERS } from '~/lib/stores/settings';

// Fuzzy search utilities
Expand Down
241 changes: 235 additions & 6 deletions app/components/deploy/DeployButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,46 @@ import { useVercelDeploy } from '~/components/deploy/VercelDeploy.client';
import { useNetlifyDeploy } from '~/components/deploy/NetlifyDeploy.client';
import { useGitHubDeploy } from '~/components/deploy/GitHubDeploy.client';
import { useGitLabDeploy } from '~/components/deploy/GitLabDeploy.client';
import { useS3Deploy } from '~/components/deploy/S3Deploy.client';
import { GitHubDeploymentDialog } from '~/components/deploy/GitHubDeploymentDialog';
import { GitLabDeploymentDialog } from '~/components/deploy/GitLabDeploymentDialog';
import { s3Connection } from '~/lib/stores/s3';
import { toast } from 'react-toastify';
import { db, chatId, description as chatDescription } from '~/lib/persistence/useChatHistory';
import { getMessages } from '~/lib/persistence/db';

interface DeployButtonProps {
onVercelDeploy?: () => Promise<void>;
onNetlifyDeploy?: () => Promise<void>;
onGitHubDeploy?: () => Promise<void>;
onGitLabDeploy?: () => Promise<void>;
onS3Deploy?: () => Promise<void>;
onProjectSitesDeploy?: () => Promise<void>;
}

export const DeployButton = ({
onVercelDeploy,
onNetlifyDeploy,
onGitHubDeploy,
onGitLabDeploy,
onS3Deploy,
onProjectSitesDeploy,
}: DeployButtonProps) => {
const netlifyConn = useStore(netlifyConnection);
const vercelConn = useStore(vercelConnection);
const gitlabIsConnected = useStore(isGitLabConnected);
const s3Conn = useStore(s3Connection);
const [activePreviewIndex] = useState(0);
const previews = useStore(workbenchStore.previews);
const activePreview = previews[activePreviewIndex];
const [isDeploying, setIsDeploying] = useState(false);
const [deployingTo, setDeployingTo] = useState<'netlify' | 'vercel' | 'github' | 'gitlab' | null>(null);
const [deployingTo, setDeployingTo] = useState<'netlify' | 'vercel' | 'github' | 'gitlab' | 's3' | null>(null);
const isStreaming = useStore(streamingState);
const { handleVercelDeploy } = useVercelDeploy();
const { handleNetlifyDeploy } = useNetlifyDeploy();
const { handleGitHubDeploy } = useGitHubDeploy();
const { handleGitLabDeploy } = useGitLabDeploy();
const { handleS3Deploy } = useS3Deploy();
const [showGitHubDeploymentDialog, setShowGitHubDeploymentDialog] = useState(false);
const [showGitLabDeploymentDialog, setShowGitLabDeploymentDialog] = useState(false);
const [githubDeploymentFiles, setGithubDeploymentFiles] = useState<Record<string, string> | null>(null);
Expand Down Expand Up @@ -125,6 +136,134 @@ export const DeployButton = ({
}
};

const handleS3DeployClick = async () => {
setIsDeploying(true);
setDeployingTo('s3');

try {
if (onS3Deploy) {
await onS3Deploy();
} else {
await handleS3Deploy();
}
} finally {
setIsDeploying(false);
setDeployingTo(null);
}
};

const [showProjectSitesDialog, setShowProjectSitesDialog] = useState(false);
const [projectSitesSlug, setProjectSitesSlug] = useState('');
const [projectSitesBuildFolder, setProjectSitesBuildFolder] = useState('dist/');

const handleProjectSitesDeployClick = async () => {
if (onProjectSitesDeploy) {
setIsDeploying(true);
setDeployingTo(null);

try {
await onProjectSitesDeploy();
} finally {
setIsDeploying(false);
setDeployingTo(null);
}
} else {
setShowProjectSitesDialog(true);
}
};

const handleProjectSitesDeployConfirm = async () => {
if (!projectSitesSlug.trim()) {
toast.error('Please enter a site slug');
return;
}

setShowProjectSitesDialog(false);
setIsDeploying(true);
setDeployingTo(null);

try {
toast.info('Packaging site for Project Sites...');

// Get the project files and create a ZIP
const zip = await workbenchStore.getZipBlob(projectSitesBuildFolder || undefined);

if (!zip) {
toast.error('Failed to package project files');
return;
}

// Get actual chat messages from current session
let chatMessages: unknown[] = [];
let chatDesc = 'Deployed from Bolt';

try {
const currentChatId = chatId.get();

if (db && currentChatId) {
const chat = await getMessages(db, currentChatId);

if (chat && chat.messages && chat.messages.length > 0) {
chatMessages = chat.messages;
chatDesc = chat.description || chatDescription.get() || 'Deployed from Bolt';
}
}
} catch {
// Fall back to empty messages if chat retrieval fails
}

const chatData = {
messages: chatMessages,
description: chatDesc,
exportDate: new Date().toISOString(),
};

const formData = new FormData();
formData.append('zip', zip, 'site.zip');
formData.append('chat', new Blob([JSON.stringify(chatData)], { type: 'application/json' }), 'chat.json');
formData.append('dist_path', projectSitesBuildFolder || 'dist/');

// Find or create the site, then deploy
const siteBaseUrl = 'https://sites.megabyte.space';
const lookupRes = await fetch(`${siteBaseUrl}/api/sites/lookup?slug=${encodeURIComponent(projectSitesSlug)}`);

let siteId: string | null = null;

if (lookupRes.ok) {
const lookupData = (await lookupRes.json()) as { data?: { id: string } };
siteId = lookupData.data?.id || null;
}

if (!siteId) {
toast.info('Site not found. Please create the site at sites.megabyte.space first, then deploy.');
window.open(`${siteBaseUrl}/?create=${encodeURIComponent(projectSitesSlug)}`, '_blank');

return;
}

toast.info('Deploying to Project Sites...');

const deployRes = await fetch(`${siteBaseUrl}/api/sites/${siteId}/deploy`, {
method: 'POST',
body: formData,
});

if (!deployRes.ok) {
const errText = await deployRes.text();
toast.error('Deploy failed: ' + errText);

return;
}

toast.success(`Deployed to ${projectSitesSlug}-sites.megabyte.space!`);
} catch (err) {
toast.error('Deploy failed: ' + (err instanceof Error ? err.message : String(err)));
} finally {
setIsDeploying(false);
setDeployingTo(null);
}
};

return (
<>
<div className="flex border border-bolt-elements-borderColor rounded-md overflow-hidden text-sm">
Expand Down Expand Up @@ -236,18 +375,46 @@ export const DeployButton = ({
</DropdownMenu.Item>

<DropdownMenu.Item
disabled
className="flex items-center w-full rounded-md px-4 py-2 text-sm text-bolt-elements-textTertiary gap-2 opacity-60 cursor-not-allowed"
className={classNames(
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
{
'opacity-60 cursor-not-allowed': isDeploying || !activePreview || !s3Conn.connected,
},
)}
disabled={isDeploying || !activePreview || !s3Conn.connected}
onClick={handleS3DeployClick}
>
<img
className="w-5 h-5"
height="24"
width="24"
crossOrigin="anonymous"
src="https://cdn.simpleicons.org/cloudflare"
alt="cloudflare"
src="https://cdn.simpleicons.org/amazons3"
alt="s3"
/>
<span className="mx-auto">Deploy to Cloudflare (Coming Soon)</span>
<span className="mx-auto">
{!s3Conn.connected
? 'No S3/R2 Connection Configured'
: `Deploy to ${s3Conn.provider === 'r2' ? 'Cloudflare R2' : 'AWS S3'}`}
</span>
</DropdownMenu.Item>

<DropdownMenu.Separator className="h-px bg-bolt-elements-borderColor my-1" />

<DropdownMenu.Item
className={classNames(
'cursor-pointer flex items-center w-full px-4 py-2 text-sm text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive gap-2 rounded-md group relative',
{
'opacity-60 cursor-not-allowed': isDeploying || !activePreview,
},
)}
disabled={isDeploying || !activePreview}
onClick={handleProjectSitesDeployClick}
>
<div className="w-5 h-5 flex items-center justify-center">
<div className="i-ph:globe-simple-duotone text-lg text-purple-500" />
</div>
<span className="mx-auto">Deploy to Project Sites</span>
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Root>
Expand All @@ -272,6 +439,68 @@ export const DeployButton = ({
files={gitlabDeploymentFiles}
/>
)}

{/* Project Sites Deployment Dialog */}
{showProjectSitesDialog && (
<div className="fixed inset-0 z-[300] flex items-center justify-center bg-black/60 backdrop-blur-sm">
<div className="bg-bolt-elements-background-depth-2 border border-bolt-elements-borderColor rounded-xl p-6 w-full max-w-md shadow-2xl">
<h3 className="text-lg font-semibold text-bolt-elements-textPrimary mb-4 flex items-center gap-2">
<div className="i-ph:globe-simple-duotone text-xl text-purple-500" />
Deploy to Project Sites
</h3>

<div className="space-y-4">
<div>
<label className="block text-xs text-bolt-elements-textSecondary mb-1.5 font-medium">
Site Slug <span className="text-red-400">*</span>
</label>
<input
type="text"
value={projectSitesSlug}
onChange={(e) => setProjectSitesSlug(e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, ''))}
placeholder="my-site"
className="w-full bg-bolt-elements-background-depth-3 border border-bolt-elements-borderColor rounded-lg px-3 py-2 text-sm text-bolt-elements-textPrimary focus:outline-none focus:border-purple-500"
/>
<p className="text-xs text-bolt-elements-textTertiary mt-1">
Your site will be at{' '}
<span className="text-purple-400">{projectSitesSlug || 'slug'}-sites.megabyte.space</span>
</p>
</div>

<div>
<label className="block text-xs text-bolt-elements-textSecondary mb-1.5 font-medium">
Build Folder
</label>
<input
type="text"
value={projectSitesBuildFolder}
onChange={(e) => setProjectSitesBuildFolder(e.target.value)}
placeholder="dist/"
className="w-full bg-bolt-elements-background-depth-3 border border-bolt-elements-borderColor rounded-lg px-3 py-2 text-sm text-bolt-elements-textPrimary focus:outline-none focus:border-purple-500"
/>
<p className="text-xs text-bolt-elements-textTertiary mt-1">
The folder containing your built site (e.g. dist/, build/, public/)
</p>
</div>
</div>

<div className="flex gap-3 mt-6 justify-end">
<button
onClick={() => setShowProjectSitesDialog(false)}
className="px-4 py-2 text-sm rounded-lg border border-bolt-elements-borderColor text-bolt-elements-textSecondary hover:bg-bolt-elements-background-depth-3 transition-colors"
>
Cancel
</button>
<button
onClick={handleProjectSitesDeployConfirm}
className="px-4 py-2 text-sm rounded-lg bg-purple-600 text-white hover:bg-purple-700 transition-colors font-medium"
>
Deploy
</button>
</div>
</div>
</div>
)}
</>
);
};
Loading
Loading