bolt.diy/uno.config.ts
Stijnus f33ba635e8 V1 : Release of the new Settings Dashboard
# 🚀 Release v1.0.0

## What's Changed 🌟

### 🎨 UI/UX Improvements
- **Dark Mode Support**
  - Implemented comprehensive dark theme across all components
  - Enhanced contrast and readability in dark mode
  - Added smooth theme transitions
  - Optimized dialog overlays and backdrops

### 🛠️ Settings Panel
- **Data Management**
  - Added chat history export/import functionality
  - Implemented settings backup and restore
  - Added secure data deletion with confirmations
  - Added profile customization options

- **Provider Management**
  - Added comprehensive provider configuration
  - Implemented URL-configurable providers
  - Added local model support (Ollama, LMStudio)
  - Added provider health checks
  - Added provider status indicators

- **Ollama Integration**
  - Added Ollama Model Manager with real-time updates
  - Implemented model version tracking
  - Added bulk update capability
  - Added progress tracking for model updates
  - Displays model details (parameter size, quantization)

- **GitHub Integration**
  - Added GitHub connection management
  - Implemented secure token storage
  - Added connection state persistence
  - Real-time connection status updates
  - Proper error handling and user feedback

### 📊 Event Logging
- **System Monitoring**
  - Added real-time event logging system
  - Implemented log filtering by type (info, warning, error, debug)
  - Added log export functionality
  - Added auto-scroll and search capabilities
  - Enhanced log visualization with color coding

### 💫 Animations & Interactions
- Added smooth page transitions
- Implemented loading states with spinners
- Added micro-interactions for better feedback
- Enhanced button hover and active states
- Added motion effects for UI elements

### 🔐 Security Features
- Secure token storage
- Added confirmation dialogs for destructive actions
- Implemented data validation
- Added file size and type validation
- Secure connection management

### ️ Accessibility
- Improved keyboard navigation
- Enhanced screen reader support
- Added ARIA labels and descriptions
- Implemented focus management
- Added proper dialog accessibility

### 🎯 Developer Experience
- Added comprehensive debug information
- Implemented system status monitoring
- Added version control integration
- Enhanced error handling and reporting
- Added detailed logging system

---

## 🔧 Technical Details
- **Frontend Stack**
  - React 18 with TypeScript
  - Framer Motion for animations
  - TailwindCSS for styling
  - Radix UI for accessible components

- **State Management**
  - Local storage for persistence
  - React hooks for state
  - Custom stores for global state

- **API Integration**
  - GitHub API integration
  - Ollama API integration
  - Provider API management
  - Error boundary implementation

## 📝 Notes
- Initial release focusing on core functionality and user experience
- Enhanced dark mode support across all components
- Improved accessibility and keyboard navigation
- Added comprehensive logging and debugging tools
- Implemented robust error handling and user feedback
2025-01-17 19:33:20 +01:00

318 lines
9.9 KiB
TypeScript

import { globSync } from 'fast-glob';
import fs from 'node:fs/promises';
import { basename, join } from 'node:path';
import { defineConfig, presetIcons, presetUno, transformerDirectives } from 'unocss';
import type { IconifyJSON } from '@iconify/types';
// Debug: Log the current working directory and icon paths
console.log('CWD:', process.cwd());
const iconPaths = globSync(join(process.cwd(), 'public/icons/*.svg'));
console.log('Found icons:', iconPaths);
const collectionName = 'bolt';
const customIconCollection = {
[collectionName]: iconPaths.reduce(
(acc, iconPath) => {
const [iconName] = basename(iconPath).split('.');
acc[iconName] = async () => {
try {
const content = await fs.readFile(iconPath, 'utf8');
return content
.replace(/fill="[^"]*"/g, '')
.replace(/fill='[^']*'/g, '')
.replace(/width="[^"]*"/g, '')
.replace(/height="[^"]*"/g, '')
.replace(/viewBox="[^"]*"/g, 'viewBox="0 0 24 24"')
.replace(/<svg([^>]*)>/, '<svg $1 fill="currentColor">');
} catch (error) {
console.error(`Error loading icon ${iconName}:`, error);
return '';
}
};
return acc;
},
{} as Record<string, () => Promise<string>>,
),
};
const BASE_COLORS = {
white: '#FFFFFF',
gray: {
50: '#FAFAFA',
100: '#F5F5F5',
200: '#E5E5E5',
300: '#D4D4D4',
400: '#A3A3A3',
500: '#737373',
600: '#525252',
700: '#404040',
800: '#262626',
900: '#171717',
950: '#0A0A0A',
},
accent: {
50: '#F8F5FF',
100: '#F0EBFF',
200: '#E1D6FF',
300: '#CEBEFF',
400: '#B69EFF',
500: '#9C7DFF',
600: '#8A5FFF',
700: '#7645E8',
800: '#6234BB',
900: '#502D93',
950: '#2D1959',
},
green: {
50: '#F0FDF4',
100: '#DCFCE7',
200: '#BBF7D0',
300: '#86EFAC',
400: '#4ADE80',
500: '#22C55E',
600: '#16A34A',
700: '#15803D',
800: '#166534',
900: '#14532D',
950: '#052E16',
},
orange: {
50: '#FFFAEB',
100: '#FEEFC7',
200: '#FEDF89',
300: '#FEC84B',
400: '#FDB022',
500: '#F79009',
600: '#DC6803',
700: '#B54708',
800: '#93370D',
900: '#792E0D',
},
red: {
50: '#FEF2F2',
100: '#FEE2E2',
200: '#FECACA',
300: '#FCA5A5',
400: '#F87171',
500: '#EF4444',
600: '#DC2626',
700: '#B91C1C',
800: '#991B1B',
900: '#7F1D1D',
950: '#450A0A',
},
};
const COLOR_PRIMITIVES = {
...BASE_COLORS,
alpha: {
white: generateAlphaPalette(BASE_COLORS.white),
gray: generateAlphaPalette(BASE_COLORS.gray[900]),
red: generateAlphaPalette(BASE_COLORS.red[500]),
accent: generateAlphaPalette(BASE_COLORS.accent[500]),
},
};
export default defineConfig({
safelist: [...Object.keys(customIconCollection[collectionName] || {}).map((x) => `i-bolt:${x}`)],
shortcuts: {
'bolt-ease-cubic-bezier': 'ease-[cubic-bezier(0.4,0,0.2,1)]',
'transition-theme': 'transition-[background-color,border-color,color] duration-150 bolt-ease-cubic-bezier',
kdb: 'bg-bolt-elements-code-background text-bolt-elements-code-text py-1 px-1.5 rounded-md',
'max-w-chat': 'max-w-[var(--chat-max-width)]',
},
rules: [
/**
* This shorthand doesn't exist in Tailwind and we overwrite it to avoid
* any conflicts with minified CSS classes.
*/
['b', {}],
],
theme: {
colors: {
...COLOR_PRIMITIVES,
bolt: {
elements: {
borderColor: 'var(--bolt-elements-borderColor)',
borderColorActive: 'var(--bolt-elements-borderColorActive)',
background: {
depth: {
1: 'var(--bolt-elements-bg-depth-1)',
2: 'var(--bolt-elements-bg-depth-2)',
3: 'var(--bolt-elements-bg-depth-3)',
4: 'var(--bolt-elements-bg-depth-4)',
},
},
textPrimary: 'var(--bolt-elements-textPrimary)',
textSecondary: 'var(--bolt-elements-textSecondary)',
textTertiary: 'var(--bolt-elements-textTertiary)',
code: {
background: 'var(--bolt-elements-code-background)',
text: 'var(--bolt-elements-code-text)',
},
button: {
primary: {
background: 'var(--bolt-elements-button-primary-background)',
backgroundHover: 'var(--bolt-elements-button-primary-backgroundHover)',
text: 'var(--bolt-elements-button-primary-text)',
},
secondary: {
background: 'var(--bolt-elements-button-secondary-background)',
backgroundHover: 'var(--bolt-elements-button-secondary-backgroundHover)',
text: 'var(--bolt-elements-button-secondary-text)',
},
danger: {
background: 'var(--bolt-elements-button-danger-background)',
backgroundHover: 'var(--bolt-elements-button-danger-backgroundHover)',
text: 'var(--bolt-elements-button-danger-text)',
},
},
item: {
contentDefault: 'var(--bolt-elements-item-contentDefault)',
contentActive: 'var(--bolt-elements-item-contentActive)',
contentAccent: 'var(--bolt-elements-item-contentAccent)',
contentDanger: 'var(--bolt-elements-item-contentDanger)',
backgroundDefault: 'var(--bolt-elements-item-backgroundDefault)',
backgroundActive: 'var(--bolt-elements-item-backgroundActive)',
backgroundAccent: 'var(--bolt-elements-item-backgroundAccent)',
backgroundDanger: 'var(--bolt-elements-item-backgroundDanger)',
},
actions: {
background: 'var(--bolt-elements-actions-background)',
code: {
background: 'var(--bolt-elements-actions-code-background)',
},
},
artifacts: {
background: 'var(--bolt-elements-artifacts-background)',
backgroundHover: 'var(--bolt-elements-artifacts-backgroundHover)',
borderColor: 'var(--bolt-elements-artifacts-borderColor)',
inlineCode: {
background: 'var(--bolt-elements-artifacts-inlineCode-background)',
text: 'var(--bolt-elements-artifacts-inlineCode-text)',
},
},
messages: {
background: 'var(--bolt-elements-messages-background)',
linkColor: 'var(--bolt-elements-messages-linkColor)',
code: {
background: 'var(--bolt-elements-messages-code-background)',
},
inlineCode: {
background: 'var(--bolt-elements-messages-inlineCode-background)',
text: 'var(--bolt-elements-messages-inlineCode-text)',
},
},
icon: {
success: 'var(--bolt-elements-icon-success)',
error: 'var(--bolt-elements-icon-error)',
primary: 'var(--bolt-elements-icon-primary)',
secondary: 'var(--bolt-elements-icon-secondary)',
tertiary: 'var(--bolt-elements-icon-tertiary)',
},
preview: {
addressBar: {
background: 'var(--bolt-elements-preview-addressBar-background)',
backgroundHover: 'var(--bolt-elements-preview-addressBar-backgroundHover)',
backgroundActive: 'var(--bolt-elements-preview-addressBar-backgroundActive)',
text: 'var(--bolt-elements-preview-addressBar-text)',
textActive: 'var(--bolt-elements-preview-addressBar-textActive)',
},
},
terminals: {
background: 'var(--bolt-elements-terminals-background)',
buttonBackground: 'var(--bolt-elements-terminals-buttonBackground)',
},
dividerColor: 'var(--bolt-elements-dividerColor)',
loader: {
background: 'var(--bolt-elements-loader-background)',
progress: 'var(--bolt-elements-loader-progress)',
},
prompt: {
background: 'var(--bolt-elements-prompt-background)',
},
sidebar: {
dropdownShadow: 'var(--bolt-elements-sidebar-dropdownShadow)',
buttonBackgroundDefault: 'var(--bolt-elements-sidebar-buttonBackgroundDefault)',
buttonBackgroundHover: 'var(--bolt-elements-sidebar-buttonBackgroundHover)',
buttonText: 'var(--bolt-elements-sidebar-buttonText)',
},
cta: {
background: 'var(--bolt-elements-cta-background)',
text: 'var(--bolt-elements-cta-text)',
},
},
},
},
},
transformers: [transformerDirectives()],
presets: [
presetUno({
dark: {
light: '[data-theme="light"]',
dark: '[data-theme="dark"]',
},
}),
presetIcons({
warn: true,
collections: {
bolt: customIconCollection.bolt,
ph: async () => {
const icons = await import('@iconify-json/ph/icons.json');
return icons.default as IconifyJSON;
},
},
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
width: '24px',
height: '24px',
},
customizations: {
customize(props) {
return {
...props,
width: '24px',
height: '24px',
};
},
},
}),
],
});
/**
* Generates an alpha palette for a given hex color.
*
* @param hex - The hex color code (without alpha) to generate the palette from.
* @returns An object where keys are opacity percentages and values are hex colors with alpha.
*
* Example:
*
* ```
* {
* '1': '#FFFFFF03',
* '2': '#FFFFFF05',
* '3': '#FFFFFF08',
* }
* ```
*/
function generateAlphaPalette(hex: string) {
return [1, 2, 3, 4, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100].reduce(
(acc, opacity) => {
const alpha = Math.round((opacity / 100) * 255)
.toString(16)
.padStart(2, '0');
acc[opacity] = `${hex}${alpha}`;
return acc;
},
{} as Record<number, string>,
);
}