);
};
diff --git a/app/components/settings/user/UsersWindow.tsx b/app/components/settings/user/UsersWindow.tsx
index 4d0e799c..f2e33311 100644
--- a/app/components/settings/user/UsersWindow.tsx
+++ b/app/components/settings/user/UsersWindow.tsx
@@ -1,6 +1,7 @@
import * as RadixDialog from '@radix-ui/react-dialog';
+import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
import { motion } from 'framer-motion';
-import { useState } from 'react';
+import { useState, useEffect } from 'react';
import { classNames } from '~/utils/classNames';
import { DialogTitle } from '~/components/ui/Dialog';
import { Switch } from '~/components/ui/Switch';
@@ -117,6 +118,24 @@ export const UsersWindow = ({ open, onClose }: UsersWindowProps) => {
const { hasConnectionIssues, currentIssue, acknowledgeIssue } = useConnectionStatus();
const { hasActiveWarnings, activeIssues, acknowledgeAllIssues } = useDebugStatus();
+ const [profile, setProfile] = useState(() => {
+ const saved = localStorage.getItem('bolt_user_profile');
+ return saved ? JSON.parse(saved) : { avatar: null, notifications: true };
+ });
+
+ useEffect(() => {
+ const handleStorageChange = (e: StorageEvent) => {
+ if (e.key === 'bolt_user_profile') {
+ const newProfile = e.newValue ? JSON.parse(e.newValue) : { avatar: null, notifications: true };
+ setProfile(newProfile);
+ }
+ };
+
+ window.addEventListener('storage', handleStorageChange);
+
+ return () => window.removeEventListener('storage', handleStorageChange);
+ }, []);
+
const handleDeveloperModeChange = (checked: boolean) => {
setDeveloperMode(checked);
};
@@ -127,7 +146,14 @@ export const UsersWindow = ({ open, onClose }: UsersWindowProps) => {
// Only show tabs that are assigned to the user window AND are visible
const visibleUserTabs = tabConfiguration.userTabs
- .filter((tab: TabVisibilityConfig) => tab.window === 'user' && tab.visible)
+ .filter((tab) => {
+ // Hide notifications tab if notifications are disabled
+ if (tab.id === 'notifications' && !profile.notifications) {
+ return false;
+ }
+
+ return tab.visible;
+ })
.sort((a: TabVisibilityConfig, b: TabVisibilityConfig) => (a.order || 0) - (b.order || 0));
const moveTab = (dragIndex: number, hoverIndex: number) => {
@@ -240,6 +266,142 @@ export const UsersWindow = ({ open, onClose }: UsersWindowProps) => {
}
};
+ const renderHeader = () => (
+
setDeveloperMode(false)} />
@@ -273,64 +435,7 @@ export const UsersWindow = ({ open, onClose }: UsersWindowProps) => {
transition={{ duration: 0.2 }}
>
{/* Header */}
-
-
- {activeTab ? (
-
-
-
- ) : (
-
- )}
-
- {activeTab ? TAB_LABELS[activeTab] : 'Bolt Control Panel'}
-
-
-
-
-
-
-
-
-
-
-
-
+ {renderHeader()}
{/* Content */}
{
+ const type: NotificationType =
+ log.details?.type === 'update'
+ ? 'update'
+ : log.level === 'error'
+ ? 'error'
+ : log.level === 'warning'
+ ? 'warning'
+ : 'info';
+
+ const baseNotification: Notification = {
+ id: log.id,
+ title: log.category.charAt(0).toUpperCase() + log.category.slice(1),
+ message: log.message,
+ type,
+ read: log.read || false,
+ timestamp: log.timestamp,
+ };
+
+ if (log.details) {
+ return {
+ ...baseNotification,
+ details: log.details as NotificationDetails,
+ };
+ }
+
+ return baseNotification;
+};
+
export const getNotifications = async (): Promise => {
- /*
- * TODO: Implement actual notifications logic
- * This is a mock implementation
- */
- return [
- {
- id: 'notif-1',
- title: 'Welcome to Bolt',
- message: 'Get started by exploring the features',
- type: 'info',
- read: true,
- timestamp: new Date().toISOString(),
- },
- {
- id: 'notif-2',
- title: 'New Update Available',
- message: 'Version 1.0.1 is now available',
- type: 'info',
- read: false,
- timestamp: new Date().toISOString(),
- },
- ];
+ const logs = Object.values(logStore.logs.get()) as LogEntryWithRead[];
+
+ return logs
+ .filter((log) => {
+ if (log.details?.type === 'update') {
+ return true;
+ }
+
+ return log.level === 'error' || log.level === 'warning';
+ })
+ .map(mapLogToNotification)
+ .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
};
export const markNotificationRead = async (notificationId: string): Promise => {
- /*
- * TODO: Implement actual notification read logic
- */
- console.log(`Marking notification ${notificationId} as read`);
+ logStore.markAsRead(notificationId);
+};
+
+export const clearNotifications = async (): Promise => {
+ logStore.clearLogs();
+};
+
+export const getUnreadCount = (): number => {
+ const logs = Object.values(logStore.logs.get()) as LogEntryWithRead[];
+
+ return logs.filter((log) => {
+ if (!log.read) {
+ if (log.details?.type === 'update') {
+ return true;
+ }
+
+ return log.level === 'error' || log.level === 'warning';
+ }
+
+ return false;
+ }).length;
};
diff --git a/app/lib/hooks/useNotifications.ts b/app/lib/hooks/useNotifications.ts
index 00aaee39..151dd39e 100644
--- a/app/lib/hooks/useNotifications.ts
+++ b/app/lib/hooks/useNotifications.ts
@@ -1,34 +1,17 @@
import { useState, useEffect } from 'react';
import { getNotifications, markNotificationRead, type Notification } from '~/lib/api/notifications';
-
-const READ_NOTIFICATIONS_KEY = 'bolt_read_notifications';
-
-const getReadNotifications = (): string[] => {
- try {
- const stored = localStorage.getItem(READ_NOTIFICATIONS_KEY);
- return stored ? JSON.parse(stored) : [];
- } catch {
- return [];
- }
-};
-
-const setReadNotifications = (notificationIds: string[]) => {
- try {
- localStorage.setItem(READ_NOTIFICATIONS_KEY, JSON.stringify(notificationIds));
- } catch (error) {
- console.error('Failed to persist read notifications:', error);
- }
-};
+import { logStore } from '~/lib/stores/logs';
+import { useStore } from '@nanostores/react';
export const useNotifications = () => {
const [hasUnreadNotifications, setHasUnreadNotifications] = useState(false);
const [unreadNotifications, setUnreadNotifications] = useState([]);
- const [readNotificationIds, setReadNotificationIds] = useState(() => getReadNotifications());
+ const logs = useStore(logStore.logs);
const checkNotifications = async () => {
try {
const notifications = await getNotifications();
- const unread = notifications.filter((n) => !readNotificationIds.includes(n.id));
+ const unread = notifications.filter((n) => !logStore.isRead(n.id));
setUnreadNotifications(unread);
setHasUnreadNotifications(unread.length > 0);
} catch (error) {
@@ -43,17 +26,12 @@ export const useNotifications = () => {
const interval = setInterval(checkNotifications, 60 * 1000);
return () => clearInterval(interval);
- }, [readNotificationIds]);
+ }, [logs]); // Re-run when logs change
const markAsRead = async (notificationId: string) => {
try {
await markNotificationRead(notificationId);
-
- const newReadIds = [...readNotificationIds, notificationId];
- setReadNotificationIds(newReadIds);
- setReadNotifications(newReadIds);
- setUnreadNotifications((prev) => prev.filter((n) => n.id !== notificationId));
- setHasUnreadNotifications(unreadNotifications.length > 1);
+ await checkNotifications();
} catch (error) {
console.error('Failed to mark notification as read:', error);
}
@@ -61,13 +39,9 @@ export const useNotifications = () => {
const markAllAsRead = async () => {
try {
- await Promise.all(unreadNotifications.map((n) => markNotificationRead(n.id)));
-
- const newReadIds = [...readNotificationIds, ...unreadNotifications.map((n) => n.id)];
- setReadNotificationIds(newReadIds);
- setReadNotifications(newReadIds);
- setUnreadNotifications([]);
- setHasUnreadNotifications(false);
+ const notifications = await getNotifications();
+ await Promise.all(notifications.map((n) => markNotificationRead(n.id)));
+ await checkNotifications();
} catch (error) {
console.error('Failed to mark all notifications as read:', error);
}
diff --git a/app/lib/stores/logs.ts b/app/lib/stores/logs.ts
index 1992a4d1..d36caff9 100644
--- a/app/lib/stores/logs.ts
+++ b/app/lib/stores/logs.ts
@@ -19,12 +19,25 @@ export interface LogEntry {
const MAX_LOGS = 1000; // Maximum number of logs to keep in memory
class LogStore {
+ logInfo(message: string, details: { type: string; message: string }) {
+ return this.addLog(message, 'info', 'system', details);
+ }
+
+ logSuccess(message: string, details: { type: string; message: string }) {
+ return this.addLog(message, 'info', 'system', { ...details, success: true });
+ }
private _logs = map>({});
showLogs = atom(true);
+ private _readLogs = new Set();
constructor() {
// Load saved logs from cookies on initialization
this._loadLogs();
+
+ // Only load read logs in browser environment
+ if (typeof window !== 'undefined') {
+ this._loadReadLogs();
+ }
}
// Expose the logs store for subscription
@@ -45,11 +58,36 @@ class LogStore {
}
}
+ private _loadReadLogs() {
+ if (typeof window === 'undefined') {
+ return;
+ }
+
+ const savedReadLogs = localStorage.getItem('bolt_read_logs');
+
+ if (savedReadLogs) {
+ try {
+ const parsedReadLogs = JSON.parse(savedReadLogs);
+ this._readLogs = new Set(parsedReadLogs);
+ } catch (error) {
+ logger.error('Failed to parse read logs:', error);
+ }
+ }
+ }
+
private _saveLogs() {
const currentLogs = this._logs.get();
Cookies.set('eventLogs', JSON.stringify(currentLogs));
}
+ private _saveReadLogs() {
+ if (typeof window === 'undefined') {
+ return;
+ }
+
+ localStorage.setItem('bolt_read_logs', JSON.stringify(Array.from(this._readLogs)));
+ }
+
private _generateId(): string {
return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
@@ -210,6 +248,20 @@ class LogStore {
return matchesLevel && matchesCategory && matchesSearch;
});
}
+
+ markAsRead(logId: string) {
+ this._readLogs.add(logId);
+ this._saveReadLogs();
+ }
+
+ isRead(logId: string): boolean {
+ return this._readLogs.has(logId);
+ }
+
+ clearReadLogs() {
+ this._readLogs.clear();
+ this._saveReadLogs();
+ }
}
export const logStore = new LogStore();
diff --git a/package.json b/package.json
index e9877f93..e8b4003c 100644
--- a/package.json
+++ b/package.json
@@ -90,6 +90,7 @@
"js-cookie": "^3.0.5",
"jszip": "^3.10.1",
"nanostores": "^0.10.3",
+ "next": "^15.1.5",
"ollama-ai-provider": "^0.15.2",
"react": "^18.3.1",
"react-dnd": "^16.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 40a37309..9edd7588 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -191,6 +191,9 @@ importers:
nanostores:
specifier: ^0.10.3
version: 0.10.3
+ next:
+ specifier: ^15.1.5
+ version: 15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
ollama-ai-provider:
specifier: ^0.15.2
version: 0.15.2(zod@3.23.8)
@@ -849,6 +852,9 @@ packages:
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
engines: {node: '>=12'}
+ '@emnapi/runtime@1.3.1':
+ resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==}
+
'@emotion/hash@0.9.2':
resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==}
@@ -1506,6 +1512,111 @@ packages:
'@iconify/utils@2.1.33':
resolution: {integrity: sha512-jP9h6v/g0BIZx0p7XGJJVtkVnydtbgTgt9mVNcGDYwaa7UhdHdI9dvoq+gKj9sijMSJKxUPEG2JyjsgXjxL7Kw==}
+ '@img/sharp-darwin-arm64@0.33.5':
+ resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.33.5':
+ resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-s390x@1.0.4':
+ resolution: {integrity: sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ resolution: {integrity: sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ resolution: {integrity: sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linux-arm64@0.33.5':
+ resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linux-arm@0.33.5':
+ resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@img/sharp-linux-s390x@0.33.5':
+ resolution: {integrity: sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+
+ '@img/sharp-linux-x64@0.33.5':
+ resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ resolution: {integrity: sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ resolution: {integrity: sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+
+ '@img/sharp-wasm32@0.33.5':
+ resolution: {integrity: sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-ia32@0.33.5':
+ resolution: {integrity: sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.33.5':
+ resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
'@isaacs/cliui@8.0.2':
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
@@ -1577,6 +1688,57 @@ packages:
nanostores: ^0.9.0 || ^0.10.0 || ^0.11.0
react: '>=18.0.0'
+ '@next/env@15.1.5':
+ resolution: {integrity: sha512-jg8ygVq99W3/XXb9Y6UQsritwhjc+qeiO7QrGZRYOfviyr/HcdnhdBQu4gbp2rBIh2ZyBYTBMWbPw3JSCb0GHw==}
+
+ '@next/swc-darwin-arm64@15.1.5':
+ resolution: {integrity: sha512-5ttHGE75Nw9/l5S8zR2xEwR8OHEqcpPym3idIMAZ2yo+Edk0W/Vf46jGqPOZDk+m/SJ+vYZDSuztzhVha8rcdA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@15.1.5':
+ resolution: {integrity: sha512-8YnZn7vDURUUTInfOcU5l0UWplZGBqUlzvqKKUFceM11SzfNEz7E28E1Arn4/FsOf90b1Nopboy7i7ufc4jXag==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@15.1.5':
+ resolution: {integrity: sha512-rDJC4ctlYbK27tCyFUhgIv8o7miHNlpCjb2XXfTLQszwAUOSbcMN9q2y3urSrrRCyGVOd9ZR9a4S45dRh6JF3A==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-arm64-musl@15.1.5':
+ resolution: {integrity: sha512-FG5RApf4Gu+J+pHUQxXPM81oORZrKBYKUaBTylEIQ6Lz17hKVDsLbSXInfXM0giclvXbyiLXjTv42sQMATmZ0A==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-x64-gnu@15.1.5':
+ resolution: {integrity: sha512-NX2Ar3BCquAOYpnoYNcKz14eH03XuF7SmSlPzTSSU4PJe7+gelAjxo3Y7F2m8+hLT8ZkkqElawBp7SWBdzwqQw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-linux-x64-musl@15.1.5':
+ resolution: {integrity: sha512-EQgqMiNu3mrV5eQHOIgeuh6GB5UU57tu17iFnLfBEhYfiOfyK+vleYKh2dkRVkV6ayx3eSqbIYgE7J7na4hhcA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-win32-arm64-msvc@15.1.5':
+ resolution: {integrity: sha512-HPULzqR/VqryQZbZME8HJE3jNFmTGcp+uRMHabFbQl63TtDPm+oCXAz3q8XyGv2AoihwNApVlur9Up7rXWRcjg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@15.1.5':
+ resolution: {integrity: sha512-n74fUb/Ka1dZSVYfjwQ+nSJ+ifUff7jGurFcTuJNKZmI62FFOxQXUYit/uZXPTj2cirm1rvGWHG2GhbSol5Ikw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
'@nodelib/fs.scandir@2.1.5':
resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
engines: {node: '>= 8'}
@@ -2475,6 +2637,9 @@ packages:
peerDependencies:
eslint: '>=8.40.0'
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
'@swc/helpers@0.5.15':
resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
@@ -2991,6 +3156,10 @@ packages:
peerDependencies:
esbuild: '>=0.18'
+ busboy@1.6.0:
+ resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+ engines: {node: '>=10.16.0'}
+
bytes@3.1.2:
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
engines: {node: '>= 0.8'}
@@ -3104,6 +3273,13 @@ packages:
color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+
+ color@4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+
colorette@2.0.20:
resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
@@ -3305,6 +3481,10 @@ packages:
resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+ detect-libc@2.0.3:
+ resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
+ engines: {node: '>=8'}
+
detect-node-es@1.1.0:
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
@@ -3961,6 +4141,9 @@ packages:
resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==}
engines: {node: '>= 0.4'}
+ is-arrayish@0.3.2:
+ resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+
is-binary-path@2.1.0:
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
engines: {node: '>=8'}
@@ -4650,6 +4833,27 @@ packages:
resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
engines: {node: '>= 0.6'}
+ next@15.1.5:
+ resolution: {integrity: sha512-Cf/TEegnt01hn3Hoywh6N8fvkhbOuChO4wFje24+a86wKOubgVaWkDqxGVgoWlz2Hp9luMJ9zw3epftujdnUOg==}
+ engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.41.2
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
node-domexception@1.0.0:
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
engines: {node: '>=10.5.0'}
@@ -4942,6 +5146,10 @@ packages:
postcss-value-parser@4.2.0:
resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
postcss@8.4.49:
resolution: {integrity: sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==}
engines: {node: ^10 || ^12 || >=14}
@@ -5496,6 +5704,10 @@ packages:
resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==}
hasBin: true
+ sharp@0.33.5:
+ resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
shebang-command@2.0.0:
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
engines: {node: '>=8'}
@@ -5527,6 +5739,9 @@ packages:
simple-get@4.0.1:
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+ simple-swizzle@0.2.2:
+ resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+
sirv@2.0.4:
resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
engines: {node: '>= 10'}
@@ -5598,6 +5813,10 @@ packages:
stream-slice@0.1.2:
resolution: {integrity: sha512-QzQxpoacatkreL6jsxnVb7X5R/pGw9OUv2qWTYWnmLpg4NdN31snPy/f3TdQE1ZUXaThRvj1Zw4/OGg0ZkaLMA==}
+ streamsearch@1.1.0:
+ resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+ engines: {node: '>=10.0.0'}
+
string-hash@1.1.3:
resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
@@ -5650,6 +5869,19 @@ packages:
style-to-object@1.0.8:
resolution: {integrity: sha512-xT47I/Eo0rwJmaXC4oilDGDWLohVhR6o/xAQcPQN8q6QBuZVL8qMYL85kLmST5cPjAorwvqIA4qXTRQoYHaL6g==}
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+
supports-color@7.2.0:
resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
engines: {node: '>=8'}
@@ -7161,6 +7393,11 @@ snapshots:
dependencies:
'@jridgewell/trace-mapping': 0.3.9
+ '@emnapi/runtime@1.3.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
'@emotion/hash@0.9.2': {}
'@esbuild-plugins/node-globals-polyfill@0.2.3(esbuild@0.17.19)':
@@ -7556,6 +7793,81 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@img/sharp-darwin-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.0.5':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.0.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.0.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.0.5
+ optional: true
+
+ '@img/sharp-linux-s390x@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.0.4
+ optional: true
+
+ '@img/sharp-linux-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.33.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+ optional: true
+
+ '@img/sharp-wasm32@0.33.5':
+ dependencies:
+ '@emnapi/runtime': 1.3.1
+ optional: true
+
+ '@img/sharp-win32-ia32@0.33.5':
+ optional: true
+
+ '@img/sharp-win32-x64@0.33.5':
+ optional: true
+
'@isaacs/cliui@8.0.2':
dependencies:
string-width: 5.1.2
@@ -7673,6 +7985,32 @@ snapshots:
nanostores: 0.10.3
react: 18.3.1
+ '@next/env@15.1.5': {}
+
+ '@next/swc-darwin-arm64@15.1.5':
+ optional: true
+
+ '@next/swc-darwin-x64@15.1.5':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@15.1.5':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@15.1.5':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@15.1.5':
+ optional: true
+
+ '@next/swc-linux-x64-musl@15.1.5':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@15.1.5':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@15.1.5':
+ optional: true
+
'@nodelib/fs.scandir@2.1.5':
dependencies:
'@nodelib/fs.stat': 2.0.5
@@ -8739,6 +9077,8 @@ snapshots:
- supports-color
- typescript
+ '@swc/counter@0.1.3': {}
+
'@swc/helpers@0.5.15':
dependencies:
tslib: 2.8.1
@@ -9432,6 +9772,10 @@ snapshots:
esbuild: 0.23.1
load-tsconfig: 0.2.5
+ busboy@1.6.0:
+ dependencies:
+ streamsearch: 1.1.0
+
bytes@3.1.2: {}
cac@6.7.14: {}
@@ -9546,6 +9890,18 @@ snapshots:
color-name@1.1.4: {}
+ color-string@1.9.1:
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ optional: true
+
+ color@4.2.3:
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ optional: true
+
colorette@2.0.20: {}
colorjs.io@0.5.2: {}
@@ -9711,6 +10067,9 @@ snapshots:
destroy@1.2.0: {}
+ detect-libc@2.0.3:
+ optional: true
+
detect-node-es@1.1.0: {}
devlop@1.1.0:
@@ -10569,6 +10928,9 @@ snapshots:
call-bind: 1.0.7
has-tostringtag: 1.0.2
+ is-arrayish@0.3.2:
+ optional: true
+
is-binary-path@2.1.0:
dependencies:
binary-extensions: 2.3.0
@@ -11605,6 +11967,32 @@ snapshots:
negotiator@0.6.3: {}
+ next@15.1.5(@babel/core@7.26.0)(@opentelemetry/api@1.9.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@next/env': 15.1.5
+ '@swc/counter': 0.1.3
+ '@swc/helpers': 0.5.15
+ busboy: 1.6.0
+ caniuse-lite: 1.0.30001685
+ postcss: 8.4.31
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 15.1.5
+ '@next/swc-darwin-x64': 15.1.5
+ '@next/swc-linux-arm64-gnu': 15.1.5
+ '@next/swc-linux-arm64-musl': 15.1.5
+ '@next/swc-linux-x64-gnu': 15.1.5
+ '@next/swc-linux-x64-musl': 15.1.5
+ '@next/swc-win32-arm64-msvc': 15.1.5
+ '@next/swc-win32-x64-msvc': 15.1.5
+ '@opentelemetry/api': 1.9.0
+ sharp: 0.33.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
node-domexception@1.0.0: {}
node-fetch-native@1.6.4: {}
@@ -11929,6 +12317,12 @@ snapshots:
postcss-value-parser@4.2.0: {}
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.8
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
postcss@8.4.49:
dependencies:
nanoid: 3.3.8
@@ -12512,6 +12906,33 @@ snapshots:
inherits: 2.0.4
safe-buffer: 5.2.1
+ sharp@0.33.5:
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.3
+ semver: 7.6.3
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.33.5
+ '@img/sharp-darwin-x64': 0.33.5
+ '@img/sharp-libvips-darwin-arm64': 1.0.4
+ '@img/sharp-libvips-darwin-x64': 1.0.4
+ '@img/sharp-libvips-linux-arm': 1.0.5
+ '@img/sharp-libvips-linux-arm64': 1.0.4
+ '@img/sharp-libvips-linux-s390x': 1.0.4
+ '@img/sharp-libvips-linux-x64': 1.0.4
+ '@img/sharp-libvips-linuxmusl-arm64': 1.0.4
+ '@img/sharp-libvips-linuxmusl-x64': 1.0.4
+ '@img/sharp-linux-arm': 0.33.5
+ '@img/sharp-linux-arm64': 0.33.5
+ '@img/sharp-linux-s390x': 0.33.5
+ '@img/sharp-linux-x64': 0.33.5
+ '@img/sharp-linuxmusl-arm64': 0.33.5
+ '@img/sharp-linuxmusl-x64': 0.33.5
+ '@img/sharp-wasm32': 0.33.5
+ '@img/sharp-win32-ia32': 0.33.5
+ '@img/sharp-win32-x64': 0.33.5
+ optional: true
+
shebang-command@2.0.0:
dependencies:
shebang-regex: 3.0.0
@@ -12548,6 +12969,11 @@ snapshots:
once: 1.4.0
simple-concat: 1.0.1
+ simple-swizzle@0.2.2:
+ dependencies:
+ is-arrayish: 0.3.2
+ optional: true
+
sirv@2.0.4:
dependencies:
'@polka/url': 1.0.0-next.28
@@ -12616,6 +13042,8 @@ snapshots:
stream-slice@0.1.2: {}
+ streamsearch@1.1.0: {}
+
string-hash@1.1.3: {}
string-width@4.2.3:
@@ -12669,6 +13097,13 @@ snapshots:
dependencies:
inline-style-parser: 0.2.4
+ styled-jsx@5.1.6(@babel/core@7.26.0)(react@18.3.1):
+ dependencies:
+ client-only: 0.0.1
+ react: 18.3.1
+ optionalDependencies:
+ '@babel/core': 7.26.0
+
supports-color@7.2.0:
dependencies:
has-flag: 4.0.0
diff --git a/scripts/update.sh b/scripts/update.sh
new file mode 100755
index 00000000..cb570e15
--- /dev/null
+++ b/scripts/update.sh
@@ -0,0 +1,52 @@
+#!/bin/bash
+
+# Exit on any error
+set -e
+
+echo "Starting Bolt.DIY update process..."
+
+# Get the current directory
+SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
+
+# Store current version
+CURRENT_VERSION=$(cat "$PROJECT_ROOT/package.json" | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]')
+
+echo "Current version: $CURRENT_VERSION"
+echo "Fetching latest version..."
+
+# Create temp directory
+TMP_DIR=$(mktemp -d)
+cd "$TMP_DIR"
+
+# Download latest release
+LATEST_RELEASE_URL=$(curl -s https://api.github.com/repos/stackblitz-labs/bolt.diy/releases/latest | grep "browser_download_url.*zip" | cut -d : -f 2,3 | tr -d \")
+if [ -z "$LATEST_RELEASE_URL" ]; then
+ echo "Error: Could not find latest release download URL"
+ exit 1
+fi
+
+echo "Downloading latest release..."
+curl -L -o latest.zip "$LATEST_RELEASE_URL"
+
+echo "Extracting update..."
+unzip -q latest.zip
+
+# Backup current installation
+echo "Creating backup..."
+BACKUP_DIR="$PROJECT_ROOT/backup_$(date +%Y%m%d_%H%M%S)"
+mkdir -p "$BACKUP_DIR"
+cp -r "$PROJECT_ROOT"/* "$BACKUP_DIR/"
+
+# Install update
+echo "Installing update..."
+cp -r ./* "$PROJECT_ROOT/"
+
+# Clean up
+cd "$PROJECT_ROOT"
+rm -rf "$TMP_DIR"
+
+echo "Update completed successfully!"
+echo "Please restart the application to apply the changes."
+
+exit 0