14 Commits

Author SHA1 Message Date
github-actions[bot]
5a0489f3c3 chore: release version 0.0.6 2025-01-22 20:54:02 +00:00
Anirban Kar
3c56346e83 feat: enhance context handling by adding code context selection and implementing summary generation (#1091) #release
* feat: add context annotation types and enhance file handling in LLM processing

* feat: enhance context handling by adding chatId to annotations and implementing summary generation

* removed useless changes

* feat: updated token counts to include optimization requests

* prompt fix

* logging added

* useless logs removed
2025-01-22 22:48:13 +05:30
Leex
2ae897aae7 fix: get environment variables for docker #1120
fix: get environment variables for docker
2025-01-22 13:01:10 +01:00
Anirban Kar
46f15bdde6 fix: updated system prompt to have correct indentations (#1139)
* updated system prompt to have correct indentations

* removed a section
2025-01-22 01:59:07 +05:30
Anirban Kar
0ad4aa56d3 feat: added deepseek reasoner model in deepseek provider (#1151) 2025-01-22 01:58:31 +05:30
Anirban Kar
f29380e147 fix: auto select starter template bugfix (#1148) 2025-01-22 00:30:31 +05:30
Leex
d62e211d09 Merge pull request #1122 from Stijnus/FEAT_BoltDYI_PREVIEW_V3
fix: for Open preview in a new tab.
2025-01-19 00:17:13 +01:00
Leex
031e679aff Merge branch 'main' into FEAT_BoltDYI_PREVIEW_V3 2025-01-19 00:14:09 +01:00
Leex
6ae1ac25d2 Merge pull request #1094 from lewis617/patch-1
docs: replace docker-compose with docker compose
2025-01-19 00:11:39 +01:00
Leex
b842e0c920 Merge pull request #1124 from stackblitz-labs/leex279-patch-readme-changes-v1
docs: update README.md
2025-01-18 23:41:05 +01:00
Stijnus
48f4999f32 Update Preview.tsx 2025-01-18 21:45:29 +01:00
Stijnus
b732f20233 bug fix for Open preview in a new tab. 2025-01-18 19:25:01 +01:00
Ken Jenney
7341b1292b Get environment variables from .env.local
Environment variables are not being passed to the container in the development profile. Adding env_file to pass them so they can be used by the application.
2025-01-18 12:38:34 -05:00
lewis liu
9958496468 fix: replace docker-compose with docker compose 2025-01-15 17:47:48 +08:00
28 changed files with 1414 additions and 438 deletions

View File

@@ -144,7 +144,7 @@ docker build . --target bolt-ai-development
**Option 3: Docker Compose Profile** **Option 3: Docker Compose Profile**
```bash ```bash
docker-compose --profile development up docker compose --profile development up
``` ```
#### Running the Development Container #### Running the Development Container
@@ -171,7 +171,7 @@ docker build . --target bolt-ai-production
**Option 3: Docker Compose Profile** **Option 3: Docker Compose Profile**
```bash ```bash
docker-compose --profile production up docker compose --profile production up
``` ```
#### Running the Production Container #### Running the Production Container

View File

@@ -1,4 +1,5 @@
# bolt.diy (Previously oTToDev) # bolt.diy (Previously oTToDev)
[![bolt.diy: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.diy) [![bolt.diy: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.diy)
Welcome to bolt.diy, the official open source version of Bolt.new (previously known as oTToDev and bolt.new ANY LLM), which allows you to choose the LLM that you use for each prompt! Currently, you can use OpenAI, Anthropic, Ollama, OpenRouter, Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, or Groq models - and it is easily extended to use any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models. Welcome to bolt.diy, the official open source version of Bolt.new (previously known as oTToDev and bolt.new ANY LLM), which allows you to choose the LLM that you use for each prompt! Currently, you can use OpenAI, Anthropic, Ollama, OpenRouter, Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, or Groq models - and it is easily extended to use any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models.
@@ -67,7 +68,7 @@ project, please check the [project management guide](./PROJECT.md) to get starte
- ✅ Together Integration (@mouimet-infinisoft) - ✅ Together Integration (@mouimet-infinisoft)
- ✅ Mobile friendly (@qwikode) - ✅ Mobile friendly (@qwikode)
- ✅ Better prompt enhancing (@SujalXplores) - ✅ Better prompt enhancing (@SujalXplores)
- ✅ Attach images to prompts (@atrokhym) - ✅ Attach images to prompts (@atrokhym)(@stijnus)
- ✅ Added Git Clone button (@thecodacus) - ✅ Added Git Clone button (@thecodacus)
- ✅ Git Import from url (@thecodacus) - ✅ Git Import from url (@thecodacus)
- ✅ PromptLibrary to have different variations of prompts for different use cases (@thecodacus) - ✅ PromptLibrary to have different variations of prompts for different use cases (@thecodacus)
@@ -89,7 +90,8 @@ project, please check the [project management guide](./PROJECT.md) to get starte
- ⬜ Azure Open AI API Integration - ⬜ Azure Open AI API Integration
- ⬜ Vertex AI Integration - ⬜ Vertex AI Integration
- ⬜ Granite Integration - ⬜ Granite Integration
- Popout Window for Web Container - Popout Window for Web Container(@stijnus)
- ✅ Ability to change Popout window size (@stijnus)
## Features ## Features
@@ -113,9 +115,6 @@ Let's get you up and running with the stable version of Bolt.DIY!
- Next **click source.zip** - Next **click source.zip**
## Prerequisites ## Prerequisites
Before you begin, you'll need to install two important pieces of software: Before you begin, you'll need to install two important pieces of software:
@@ -148,16 +147,19 @@ You have two options for running Bolt.DIY: directly on your machine or using Doc
### Option 1: Direct Installation (Recommended for Beginners) ### Option 1: Direct Installation (Recommended for Beginners)
1. **Install Package Manager (pnpm)**: 1. **Install Package Manager (pnpm)**:
```bash ```bash
npm install -g pnpm npm install -g pnpm
``` ```
2. **Install Project Dependencies**: 2. **Install Project Dependencies**:
```bash ```bash
pnpm install pnpm install
``` ```
3. **Start the Application**: 3. **Start the Application**:
```bash ```bash
pnpm run dev pnpm run dev
``` ```
@@ -169,11 +171,13 @@ You have two options for running Bolt.DIY: directly on your machine or using Doc
This option requires some familiarity with Docker but provides a more isolated environment. This option requires some familiarity with Docker but provides a more isolated environment.
#### Additional Prerequisite #### Additional Prerequisite
- Install Docker: [Download Docker](https://www.docker.com/) - Install Docker: [Download Docker](https://www.docker.com/)
#### Steps: #### Steps:
1. **Build the Docker Image**: 1. **Build the Docker Image**:
```bash ```bash
# Using npm script: # Using npm script:
npm run dockerbuild npm run dockerbuild
@@ -184,12 +188,9 @@ This option requires some familiarity with Docker but provides a more isolated e
2. **Run the Container**: 2. **Run the Container**:
```bash ```bash
docker-compose --profile development up docker compose --profile development up
``` ```
## Configuring API Keys and Providers ## Configuring API Keys and Providers
### Adding Your API Keys ### Adding Your API Keys
@@ -218,6 +219,7 @@ For providers that support custom base URLs (such as Ollama or LM Studio), follo
> **Note**: Custom base URLs are particularly useful when running local instances of AI models or using custom API endpoints. > **Note**: Custom base URLs are particularly useful when running local instances of AI models or using custom API endpoints.
### Supported Providers ### Supported Providers
- Ollama - Ollama
- LM Studio - LM Studio
- OpenAILike - OpenAILike
@@ -225,23 +227,27 @@ For providers that support custom base URLs (such as Ollama or LM Studio), follo
## Setup Using Git (For Developers only) ## Setup Using Git (For Developers only)
This method is recommended for developers who want to: This method is recommended for developers who want to:
- Contribute to the project - Contribute to the project
- Stay updated with the latest changes - Stay updated with the latest changes
- Switch between different versions - Switch between different versions
- Create custom modifications - Create custom modifications
#### Prerequisites #### Prerequisites
1. Install Git: [Download Git](https://git-scm.com/downloads) 1. Install Git: [Download Git](https://git-scm.com/downloads)
#### Initial Setup #### Initial Setup
1. **Clone the Repository**: 1. **Clone the Repository**:
```bash ```bash
# Using HTTPS # Using HTTPS
git clone https://github.com/stackblitz-labs/bolt.diy.git git clone https://github.com/stackblitz-labs/bolt.diy.git
``` ```
2. **Navigate to Project Directory**: 2. **Navigate to Project Directory**:
```bash ```bash
cd bolt.diy cd bolt.diy
``` ```
@@ -251,6 +257,7 @@ This method is recommended for developers who want to:
git checkout main git checkout main
``` ```
4. **Install Dependencies**: 4. **Install Dependencies**:
```bash ```bash
pnpm install pnpm install
``` ```
@@ -265,16 +272,19 @@ This method is recommended for developers who want to:
To get the latest changes from the repository: To get the latest changes from the repository:
1. **Save Your Local Changes** (if any): 1. **Save Your Local Changes** (if any):
```bash ```bash
git stash git stash
``` ```
2. **Pull Latest Updates**: 2. **Pull Latest Updates**:
```bash ```bash
git pull origin main git pull origin main
``` ```
3. **Update Dependencies**: 3. **Update Dependencies**:
```bash ```bash
pnpm install pnpm install
``` ```
@@ -289,6 +299,7 @@ To get the latest changes from the repository:
If you encounter issues: If you encounter issues:
1. **Clean Installation**: 1. **Clean Installation**:
```bash ```bash
# Remove node modules and lock files # Remove node modules and lock files
rm -rf node_modules pnpm-lock.yaml rm -rf node_modules pnpm-lock.yaml

View File

@@ -1,6 +1,8 @@
import { memo } from 'react'; import { memo } from 'react';
import { Markdown } from './Markdown'; import { Markdown } from './Markdown';
import type { JSONValue } from 'ai'; import type { JSONValue } from 'ai';
import type { ProgressAnnotation } from '~/types/context';
import Popover from '~/components/ui/Popover';
interface AssistantMessageProps { interface AssistantMessageProps {
content: string; content: string;
@@ -10,7 +12,12 @@ interface AssistantMessageProps {
export const AssistantMessage = memo(({ content, annotations }: AssistantMessageProps) => { export const AssistantMessage = memo(({ content, annotations }: AssistantMessageProps) => {
const filteredAnnotations = (annotations?.filter( const filteredAnnotations = (annotations?.filter(
(annotation: JSONValue) => annotation && typeof annotation === 'object' && Object.keys(annotation).includes('type'), (annotation: JSONValue) => annotation && typeof annotation === 'object' && Object.keys(annotation).includes('type'),
) || []) as { type: string; value: any }[]; ) || []) as { type: string; value: any } & { [key: string]: any }[];
let progressAnnotation: ProgressAnnotation[] = filteredAnnotations.filter(
(annotation) => annotation.type === 'progress',
) as ProgressAnnotation[];
progressAnnotation = progressAnnotation.sort((a, b) => b.value - a.value);
const usage: { const usage: {
completionTokens: number; completionTokens: number;
@@ -20,11 +27,18 @@ export const AssistantMessage = memo(({ content, annotations }: AssistantMessage
return ( return (
<div className="overflow-hidden w-full"> <div className="overflow-hidden w-full">
{usage && ( <>
<div className="text-sm text-bolt-elements-textSecondary mb-2"> <div className=" flex gap-2 items-center text-sm text-bolt-elements-textSecondary mb-2">
Tokens: {usage.totalTokens} (prompt: {usage.promptTokens}, completion: {usage.completionTokens}) {progressAnnotation.length > 0 && (
<Popover trigger={<div className="i-ph:info" />}>{progressAnnotation[0].message}</Popover>
)}
{usage && (
<div>
Tokens: {usage.totalTokens} (prompt: {usage.promptTokens}, completion: {usage.completionTokens})
</div>
)}
</div> </div>
)} </>
<Markdown html>{content}</Markdown> <Markdown html>{content}</Markdown>
</div> </div>
); );

View File

@@ -274,7 +274,7 @@ export const ChatImpl = memo(
runAnimation(); runAnimation();
if (!chatStarted && messageInput && autoSelectTemplate) { if (!chatStarted && _input && autoSelectTemplate) {
setFakeLoading(true); setFakeLoading(true);
setMessages([ setMessages([
{ {
@@ -296,7 +296,7 @@ export const ChatImpl = memo(
// reload(); // reload();
const { template, title } = await selectStarterTemplate({ const { template, title } = await selectStarterTemplate({
message: messageInput, message: _input,
model, model,
provider, provider,
}); });
@@ -319,7 +319,7 @@ export const ChatImpl = memo(
{ {
id: `${new Date().getTime()}`, id: `${new Date().getTime()}`,
role: 'user', role: 'user',
content: messageInput, content: _input,
// annotations: ['hidden'], // annotations: ['hidden'],
}, },

View File

@@ -0,0 +1,20 @@
import * as Popover from '@radix-ui/react-popover';
import type { PropsWithChildren, ReactNode } from 'react';
export default ({ children, trigger }: PropsWithChildren<{ trigger: ReactNode }>) => (
<Popover.Root>
<Popover.Trigger asChild>{trigger}</Popover.Trigger>
<Popover.Anchor />
<Popover.Portal>
<Popover.Content
sideOffset={10}
side="top"
align="center"
className="bg-bolt-elements-background-depth-2 text-bolt-elements-item-contentAccent p-2 rounded-md shadow-xl z-workbench"
>
{children}
<Popover.Arrow className="bg-bolt-elements-item-background-depth-2" />
</Popover.Content>
</Popover.Portal>
</Popover.Root>
);

View File

@@ -11,13 +11,14 @@ interface WindowSize {
name: string; name: string;
width: number; width: number;
height: number; height: number;
icon: string;
} }
const WINDOW_SIZES: WindowSize[] = [ const WINDOW_SIZES: WindowSize[] = [
{ name: 'Mobile (375x667)', width: 375, height: 667 }, { name: 'Mobile', width: 375, height: 667, icon: 'i-ph:device-mobile' },
{ name: 'Tablet (768x1024)', width: 768, height: 1024 }, { name: 'Tablet', width: 768, height: 1024, icon: 'i-ph:device-tablet' },
{ name: 'Laptop (1366x768)', width: 1366, height: 768 }, { name: 'Laptop', width: 1366, height: 768, icon: 'i-ph:laptop' },
{ name: 'Desktop (1920x1080)', width: 1920, height: 1080 }, { name: 'Desktop', width: 1920, height: 1080, icon: 'i-ph:monitor' },
]; ];
export const Preview = memo(() => { export const Preview = memo(() => {
@@ -249,14 +250,17 @@ export const Preview = memo(() => {
{isPortDropdownOpen && ( {isPortDropdownOpen && (
<div className="z-iframe-overlay w-full h-full absolute" onClick={() => setIsPortDropdownOpen(false)} /> <div className="z-iframe-overlay w-full h-full absolute" onClick={() => setIsPortDropdownOpen(false)} />
)} )}
<div className="bg-bolt-elements-background-depth-2 p-2 flex items-center gap-1.5"> <div className="bg-bolt-elements-background-depth-2 p-2 flex items-center gap-2">
<IconButton icon="i-ph:arrow-clockwise" onClick={reloadPreview} /> <div className="flex items-center gap-2">
<IconButton <IconButton icon="i-ph:arrow-clockwise" onClick={reloadPreview} />
icon="i-ph:selection" <IconButton
onClick={() => setIsSelectionMode(!isSelectionMode)} icon="i-ph:selection"
className={isSelectionMode ? 'bg-bolt-elements-background-depth-3' : ''} onClick={() => setIsSelectionMode(!isSelectionMode)}
/> className={isSelectionMode ? 'bg-bolt-elements-background-depth-3' : ''}
<div className="flex items-center gap-1 flex-grow bg-bolt-elements-preview-addressBar-background border border-bolt-elements-borderColor text-bolt-elements-preview-addressBar-text rounded-full px-3 py-1 text-sm hover:bg-bolt-elements-preview-addressBar-backgroundHover hover:focus-within:bg-bolt-elements-preview-addressBar-backgroundActive focus-within:bg-bolt-elements-preview-addressBar-backgroundActive focus-within-border-bolt-elements-borderColorActive focus-within:text-bolt-elements-preview-addressBar-textActive"> />
</div>
<div className="flex-grow flex items-center gap-1 bg-bolt-elements-preview-addressBar-background border border-bolt-elements-borderColor text-bolt-elements-preview-addressBar-text rounded-full px-3 py-1 text-sm hover:bg-bolt-elements-preview-addressBar-backgroundHover hover:focus-within:bg-bolt-elements-preview-addressBar-backgroundActive focus-within:bg-bolt-elements-preview-addressBar-backgroundActive focus-within-border-bolt-elements-borderColorActive focus-within:text-bolt-elements-preview-addressBar-textActive">
<input <input
title="URL" title="URL"
ref={inputRef} ref={inputRef}
@@ -278,68 +282,80 @@ export const Preview = memo(() => {
/> />
</div> </div>
{previews.length > 1 && ( <div className="flex items-center gap-2">
<PortDropdown {previews.length > 1 && (
activePreviewIndex={activePreviewIndex} <PortDropdown
setActivePreviewIndex={setActivePreviewIndex} activePreviewIndex={activePreviewIndex}
isDropdownOpen={isPortDropdownOpen} setActivePreviewIndex={setActivePreviewIndex}
setHasSelectedPreview={(value) => (hasSelectedPreview.current = value)} isDropdownOpen={isPortDropdownOpen}
setIsDropdownOpen={setIsPortDropdownOpen} setHasSelectedPreview={(value) => (hasSelectedPreview.current = value)}
previews={previews} setIsDropdownOpen={setIsPortDropdownOpen}
/> previews={previews}
)} />
<IconButton
icon="i-ph:devices"
onClick={toggleDeviceMode}
title={isDeviceModeOn ? 'Switch to Responsive Mode' : 'Switch to Device Mode'}
/>
<IconButton
icon="i-ph:layout-light"
onClick={() => setIsPreviewOnly(!isPreviewOnly)}
title={isPreviewOnly ? 'Show Full Interface' : 'Show Preview Only'}
/>
<IconButton
icon={isFullscreen ? 'i-ph:arrows-in' : 'i-ph:arrows-out'}
onClick={toggleFullscreen}
title={isFullscreen ? 'Exit Full Screen' : 'Full Screen'}
/>
<div className="relative">
<IconButton
icon="i-ph:arrow-square-out"
onClick={() => openInNewWindow(selectedWindowSize)}
title={`Open Preview in ${selectedWindowSize.name} Window`}
/>
<IconButton
icon="i-ph:caret-down"
onClick={() => setIsWindowSizeDropdownOpen(!isWindowSizeDropdownOpen)}
className="ml-1"
title="Select Window Size"
/>
{isWindowSizeDropdownOpen && (
<>
<div className="fixed inset-0 z-50" onClick={() => setIsWindowSizeDropdownOpen(false)} />
<div className="absolute right-0 top-full mt-1 z-50 bg-bolt-elements-background-depth-2 rounded-lg shadow-lg border border-bolt-elements-borderColor overflow-hidden">
{WINDOW_SIZES.map((size) => (
<button
key={size.name}
className="w-full px-4 py-2 text-left hover:bg-bolt-elements-background-depth-3 text-sm whitespace-nowrap"
onClick={() => {
setSelectedWindowSize(size);
setIsWindowSizeDropdownOpen(false);
openInNewWindow(size);
}}
>
{size.name}
</button>
))}
</div>
</>
)} )}
<IconButton
icon="i-ph:devices"
onClick={toggleDeviceMode}
title={isDeviceModeOn ? 'Switch to Responsive Mode' : 'Switch to Device Mode'}
/>
<IconButton
icon="i-ph:layout-light"
onClick={() => setIsPreviewOnly(!isPreviewOnly)}
title={isPreviewOnly ? 'Show Full Interface' : 'Show Preview Only'}
/>
<IconButton
icon={isFullscreen ? 'i-ph:arrows-in' : 'i-ph:arrows-out'}
onClick={toggleFullscreen}
title={isFullscreen ? 'Exit Full Screen' : 'Full Screen'}
/>
<div className="flex items-center relative">
<IconButton
icon="i-ph:arrow-square-out"
onClick={() => openInNewWindow(selectedWindowSize)}
title={`Open Preview in ${selectedWindowSize.name} Window`}
/>
<IconButton
icon="i-ph:caret-down"
onClick={() => setIsWindowSizeDropdownOpen(!isWindowSizeDropdownOpen)}
className="ml-1"
title="Select Window Size"
/>
{isWindowSizeDropdownOpen && (
<>
<div className="fixed inset-0 z-50" onClick={() => setIsWindowSizeDropdownOpen(false)} />
<div className="absolute right-0 top-full mt-2 z-50 min-w-[240px] bg-white dark:bg-black rounded-xl shadow-2xl border border-[#E5E7EB] dark:border-[rgba(255,255,255,0.1)] overflow-hidden">
{WINDOW_SIZES.map((size) => (
<button
key={size.name}
className="w-full px-4 py-3.5 text-left text-[#111827] dark:text-gray-300 text-sm whitespace-nowrap flex items-center gap-3 group hover:bg-[#F5EEFF] dark:hover:bg-gray-900 bg-white dark:bg-black"
onClick={() => {
setSelectedWindowSize(size);
setIsWindowSizeDropdownOpen(false);
openInNewWindow(size);
}}
>
<div
className={`${size.icon} w-5 h-5 text-[#6B7280] dark:text-gray-400 group-hover:text-[#6D28D9] dark:group-hover:text-[#6D28D9] transition-colors duration-200`}
/>
<div className="flex flex-col">
<span className="font-medium group-hover:text-[#6D28D9] dark:group-hover:text-[#6D28D9] transition-colors duration-200">
{size.name}
</span>
<span className="text-xs text-[#6B7280] dark:text-gray-400 group-hover:text-[#6D28D9] dark:group-hover:text-[#6D28D9] transition-colors duration-200">
{size.width} × {size.height}
</span>
</div>
</button>
))}
</div>
</>
)}
</div>
</div> </div>
</div> </div>
@@ -349,7 +365,7 @@ export const Preview = memo(() => {
width: isDeviceModeOn ? `${widthPercent}%` : '100%', width: isDeviceModeOn ? `${widthPercent}%` : '100%',
height: '100%', height: '100%',
overflow: 'visible', overflow: 'visible',
background: '#fff', background: 'var(--bolt-elements-background-depth-1)',
position: 'relative', position: 'relative',
display: 'flex', display: 'flex',
}} }}
@@ -359,7 +375,7 @@ export const Preview = memo(() => {
<iframe <iframe
ref={iframeRef} ref={iframeRef}
title="preview" title="preview"
className="border-none w-full h-full bg-white" className="border-none w-full h-full bg-bolt-elements-background-depth-1"
src={iframeUrl} src={iframeUrl}
sandbox="allow-scripts allow-forms allow-popups allow-modals allow-storage-access-by-user-activation allow-same-origin" sandbox="allow-scripts allow-forms allow-popups allow-modals allow-storage-access-by-user-activation allow-same-origin"
allow="cross-origin-isolated" allow="cross-origin-isolated"
@@ -371,7 +387,9 @@ export const Preview = memo(() => {
/> />
</> </>
) : ( ) : (
<div className="flex w-full h-full justify-center items-center bg-white">No preview available</div> <div className="flex w-full h-full justify-center items-center bg-bolt-elements-background-depth-1 text-bolt-elements-textPrimary">
No preview available
</div>
)} )}
{isDeviceModeOn && ( {isDeviceModeOn && (

View File

@@ -1,4 +1,4 @@
import type { AppLoadContext, EntryContext } from '@remix-run/cloudflare'; import type { AppLoadContext } from '@remix-run/cloudflare';
import { RemixServer } from '@remix-run/react'; import { RemixServer } from '@remix-run/react';
import { isbot } from 'isbot'; import { isbot } from 'isbot';
import { renderToReadableStream } from 'react-dom/server'; import { renderToReadableStream } from 'react-dom/server';
@@ -10,7 +10,7 @@ export default async function handleRequest(
request: Request, request: Request,
responseStatusCode: number, responseStatusCode: number,
responseHeaders: Headers, responseHeaders: Headers,
remixContext: EntryContext, remixContext: any,
_loadContext: AppLoadContext, _loadContext: AppLoadContext,
) { ) {
// await initializeModelList({}); // await initializeModelList({});

View File

@@ -3,3 +3,36 @@ export const MAX_TOKENS = 8000;
// limits the number of model responses that can be returned in a single request // limits the number of model responses that can be returned in a single request
export const MAX_RESPONSE_SEGMENTS = 2; export const MAX_RESPONSE_SEGMENTS = 2;
export interface File {
type: 'file';
content: string;
isBinary: boolean;
}
export interface Folder {
type: 'folder';
}
type Dirent = File | Folder;
export type FileMap = Record<string, Dirent | undefined>;
export const IGNORE_PATTERNS = [
'node_modules/**',
'.git/**',
'dist/**',
'build/**',
'.next/**',
'coverage/**',
'.cache/**',
'.vscode/**',
'.idea/**',
'**/*.log',
'**/.DS_Store',
'**/npm-debug.log*',
'**/yarn-debug.log*',
'**/yarn-error.log*',
'**/*lock.json',
'**/*lock.yml',
];

View File

@@ -0,0 +1,138 @@
import { generateText, type CoreTool, type GenerateTextResult, type Message } from 'ai';
import type { IProviderSetting } from '~/types/model';
import { DEFAULT_MODEL, DEFAULT_PROVIDER, PROVIDER_LIST } from '~/utils/constants';
import { extractCurrentContext, extractPropertiesFromMessage, simplifyBoltActions } from './utils';
import { createScopedLogger } from '~/utils/logger';
import { LLMManager } from '~/lib/modules/llm/manager';
const logger = createScopedLogger('create-summary');
export async function createSummary(props: {
messages: Message[];
env?: Env;
apiKeys?: Record<string, string>;
providerSettings?: Record<string, IProviderSetting>;
promptId?: string;
contextOptimization?: boolean;
onFinish?: (resp: GenerateTextResult<Record<string, CoreTool<any, any>>, never>) => void;
}) {
const { messages, env: serverEnv, apiKeys, providerSettings, contextOptimization, onFinish } = props;
let currentModel = DEFAULT_MODEL;
let currentProvider = DEFAULT_PROVIDER.name;
const processedMessages = messages.map((message) => {
if (message.role === 'user') {
const { model, provider, content } = extractPropertiesFromMessage(message);
currentModel = model;
currentProvider = provider;
return { ...message, content };
} else if (message.role == 'assistant') {
let content = message.content;
if (contextOptimization) {
content = simplifyBoltActions(content);
}
return { ...message, content };
}
return message;
});
const provider = PROVIDER_LIST.find((p) => p.name === currentProvider) || DEFAULT_PROVIDER;
const staticModels = LLMManager.getInstance().getStaticModelListFromProvider(provider);
let modelDetails = staticModels.find((m) => m.name === currentModel);
if (!modelDetails) {
const modelsList = [
...(provider.staticModels || []),
...(await LLMManager.getInstance().getModelListFromProvider(provider, {
apiKeys,
providerSettings,
serverEnv: serverEnv as any,
})),
];
if (!modelsList.length) {
throw new Error(`No models found for provider ${provider.name}`);
}
modelDetails = modelsList.find((m) => m.name === currentModel);
if (!modelDetails) {
// Fallback to first model
logger.warn(
`MODEL [${currentModel}] not found in provider [${provider.name}]. Falling back to first model. ${modelsList[0].name}`,
);
modelDetails = modelsList[0];
}
}
let slicedMessages = processedMessages;
const { summary } = extractCurrentContext(processedMessages);
let summaryText: string | undefined = undefined;
let chatId: string | undefined = undefined;
if (summary && summary.type === 'chatSummary') {
chatId = summary.chatId;
summaryText = `Below is the Chat Summary till now, this is chat summary before the conversation provided by the user
you should also use this as historical message while providing the response to the user.
${summary.summary}`;
if (chatId) {
let index = 0;
for (let i = 0; i < processedMessages.length; i++) {
if (processedMessages[i].id === chatId) {
index = i;
break;
}
}
slicedMessages = processedMessages.slice(index + 1);
}
}
const extractTextContent = (message: Message) =>
Array.isArray(message.content)
? (message.content.find((item) => item.type === 'text')?.text as string) || ''
: message.content;
// select files from the list of code file from the project that might be useful for the current request from the user
const resp = await generateText({
system: `
You are a software engineer. You are working on a project. tou need to summarize the work till now and provide a summary of the chat till now.
${summaryText}
RULES:
* Only provide the summary of the chat till now.
* Do not provide any new information.
`,
prompt: `
please provide a summary of the chat till now.
below is the latest chat:
---
${slicedMessages
.map((x) => {
return `---\n[${x.role}] ${extractTextContent(x)}\n---`;
})
.join('\n')}
---
`,
model: provider.getModelInstance({
model: currentModel,
serverEnv,
apiKeys,
providerSettings,
}),
});
const response = resp.text;
if (onFinish) {
onFinish(resp);
}
return response;
}

View File

@@ -0,0 +1,233 @@
import { generateText, type CoreTool, type GenerateTextResult, type Message } from 'ai';
import ignore from 'ignore';
import type { IProviderSetting } from '~/types/model';
import { IGNORE_PATTERNS, type FileMap } from './constants';
import { DEFAULT_MODEL, DEFAULT_PROVIDER, PROVIDER_LIST } from '~/utils/constants';
import { createFilesContext, extractCurrentContext, extractPropertiesFromMessage, simplifyBoltActions } from './utils';
import { createScopedLogger } from '~/utils/logger';
import { LLMManager } from '~/lib/modules/llm/manager';
// Common patterns to ignore, similar to .gitignore
const ig = ignore().add(IGNORE_PATTERNS);
const logger = createScopedLogger('select-context');
export async function selectContext(props: {
messages: Message[];
env?: Env;
apiKeys?: Record<string, string>;
files: FileMap;
providerSettings?: Record<string, IProviderSetting>;
promptId?: string;
contextOptimization?: boolean;
summary: string;
onFinish?: (resp: GenerateTextResult<Record<string, CoreTool<any, any>>, never>) => void;
}) {
const { messages, env: serverEnv, apiKeys, files, providerSettings, contextOptimization, summary, onFinish } = props;
let currentModel = DEFAULT_MODEL;
let currentProvider = DEFAULT_PROVIDER.name;
const processedMessages = messages.map((message) => {
if (message.role === 'user') {
const { model, provider, content } = extractPropertiesFromMessage(message);
currentModel = model;
currentProvider = provider;
return { ...message, content };
} else if (message.role == 'assistant') {
let content = message.content;
if (contextOptimization) {
content = simplifyBoltActions(content);
}
return { ...message, content };
}
return message;
});
const provider = PROVIDER_LIST.find((p) => p.name === currentProvider) || DEFAULT_PROVIDER;
const staticModels = LLMManager.getInstance().getStaticModelListFromProvider(provider);
let modelDetails = staticModels.find((m) => m.name === currentModel);
if (!modelDetails) {
const modelsList = [
...(provider.staticModels || []),
...(await LLMManager.getInstance().getModelListFromProvider(provider, {
apiKeys,
providerSettings,
serverEnv: serverEnv as any,
})),
];
if (!modelsList.length) {
throw new Error(`No models found for provider ${provider.name}`);
}
modelDetails = modelsList.find((m) => m.name === currentModel);
if (!modelDetails) {
// Fallback to first model
logger.warn(
`MODEL [${currentModel}] not found in provider [${provider.name}]. Falling back to first model. ${modelsList[0].name}`,
);
modelDetails = modelsList[0];
}
}
const { codeContext } = extractCurrentContext(processedMessages);
let filePaths = getFilePaths(files || {});
filePaths = filePaths.filter((x) => {
const relPath = x.replace('/home/project/', '');
return !ig.ignores(relPath);
});
let context = '';
const currrentFiles: string[] = [];
const contextFiles: FileMap = {};
if (codeContext?.type === 'codeContext') {
const codeContextFiles: string[] = codeContext.files;
Object.keys(files || {}).forEach((path) => {
let relativePath = path;
if (path.startsWith('/home/project/')) {
relativePath = path.replace('/home/project/', '');
}
if (codeContextFiles.includes(relativePath)) {
contextFiles[relativePath] = files[path];
currrentFiles.push(relativePath);
}
});
context = createFilesContext(contextFiles);
}
const summaryText = `Here is the summary of the chat till now: ${summary}`;
const extractTextContent = (message: Message) =>
Array.isArray(message.content)
? (message.content.find((item) => item.type === 'text')?.text as string) || ''
: message.content;
const lastUserMessage = processedMessages.filter((x) => x.role == 'user').pop();
if (!lastUserMessage) {
throw new Error('No user message found');
}
// select files from the list of code file from the project that might be useful for the current request from the user
const resp = await generateText({
system: `
You are a software engineer. You are working on a project. You have access to the following files:
AVAILABLE FILES PATHS
---
${filePaths.map((path) => `- ${path}`).join('\n')}
---
You have following code loaded in the context buffer that you can refer to:
CURRENT CONTEXT BUFFER
---
${context}
---
Now, you are given a task. You need to select the files that are relevant to the task from the list of files above.
RESPONSE FORMAT:
your response shoudl be in following format:
---
<updateContextBuffer>
<includeFile path="path/to/file"/>
<excludeFile path="path/to/file"/>
</updateContextBuffer>
---
* Your should start with <updateContextBuffer> and end with </updateContextBuffer>.
* You can include multiple <includeFile> and <excludeFile> tags in the response.
* You should not include any other text in the response.
* You should not include any file that is not in the list of files above.
* You should not include any file that is already in the context buffer.
* If no changes are needed, you can leave the response empty updateContextBuffer tag.
`,
prompt: `
${summaryText}
Users Question: ${extractTextContent(lastUserMessage)}
update the context buffer with the files that are relevant to the task from the list of files above.
CRITICAL RULES:
* Only include relevant files in the context buffer.
* context buffer should not include any file that is not in the list of files above.
* context buffer is extremlly expensive, so only include files that are absolutely necessary.
* If no changes are needed, you can leave the response empty updateContextBuffer tag.
* Only 5 files can be placed in the context buffer at a time.
* if the buffer is full, you need to exclude files that is not needed and include files that is relevent.
`,
model: provider.getModelInstance({
model: currentModel,
serverEnv,
apiKeys,
providerSettings,
}),
});
const response = resp.text;
const updateContextBuffer = response.match(/<updateContextBuffer>([\s\S]*?)<\/updateContextBuffer>/);
if (!updateContextBuffer) {
throw new Error('Invalid response. Please follow the response format');
}
const includeFiles =
updateContextBuffer[1]
.match(/<includeFile path="(.*?)"/gm)
?.map((x) => x.replace('<includeFile path="', '').replace('"', '')) || [];
const excludeFiles =
updateContextBuffer[1]
.match(/<excludeFile path="(.*?)"/gm)
?.map((x) => x.replace('<excludeFile path="', '').replace('"', '')) || [];
const filteredFiles: FileMap = {};
excludeFiles.forEach((path) => {
delete contextFiles[path];
});
includeFiles.forEach((path) => {
let fullPath = path;
if (!path.startsWith('/home/project/')) {
fullPath = `/home/project/${path}`;
}
if (!filePaths.includes(fullPath)) {
throw new Error(`File ${path} is not in the list of files above.`);
}
if (currrentFiles.includes(path)) {
return;
}
filteredFiles[path] = files[fullPath];
});
if (onFinish) {
onFinish(resp);
}
return filteredFiles;
// generateText({
}
export function getFilePaths(files: FileMap) {
let filePaths = Object.keys(files);
filePaths = filePaths.filter((x) => {
const relPath = x.replace('/home/project/', '');
return !ig.ignores(relPath);
});
return filePaths;
}

View File

@@ -1,167 +1,48 @@
import { convertToCoreMessages, streamText as _streamText } from 'ai'; import { convertToCoreMessages, streamText as _streamText, type Message } from 'ai';
import { MAX_TOKENS } from './constants'; import { MAX_TOKENS, type FileMap } from './constants';
import { getSystemPrompt } from '~/lib/common/prompts/prompts'; import { getSystemPrompt } from '~/lib/common/prompts/prompts';
import { import { DEFAULT_MODEL, DEFAULT_PROVIDER, MODIFICATIONS_TAG_NAME, PROVIDER_LIST, WORK_DIR } from '~/utils/constants';
DEFAULT_MODEL,
DEFAULT_PROVIDER,
MODEL_REGEX,
MODIFICATIONS_TAG_NAME,
PROVIDER_LIST,
PROVIDER_REGEX,
WORK_DIR,
} from '~/utils/constants';
import ignore from 'ignore';
import type { IProviderSetting } from '~/types/model'; import type { IProviderSetting } from '~/types/model';
import { PromptLibrary } from '~/lib/common/prompt-library'; import { PromptLibrary } from '~/lib/common/prompt-library';
import { allowedHTMLElements } from '~/utils/markdown'; import { allowedHTMLElements } from '~/utils/markdown';
import { LLMManager } from '~/lib/modules/llm/manager'; import { LLMManager } from '~/lib/modules/llm/manager';
import { createScopedLogger } from '~/utils/logger'; import { createScopedLogger } from '~/utils/logger';
import { createFilesContext, extractPropertiesFromMessage, simplifyBoltActions } from './utils';
interface ToolResult<Name extends string, Args, Result> { import { getFilePaths } from './select-context';
toolCallId: string;
toolName: Name;
args: Args;
result: Result;
}
interface Message {
role: 'user' | 'assistant';
content: string;
toolInvocations?: ToolResult<string, unknown, unknown>[];
model?: string;
}
export type Messages = Message[]; export type Messages = Message[];
export type StreamingOptions = Omit<Parameters<typeof _streamText>[0], 'model'>; export type StreamingOptions = Omit<Parameters<typeof _streamText>[0], 'model'>;
export interface File {
type: 'file';
content: string;
isBinary: boolean;
}
export interface Folder {
type: 'folder';
}
type Dirent = File | Folder;
export type FileMap = Record<string, Dirent | undefined>;
export function simplifyBoltActions(input: string): string {
// Using regex to match boltAction tags that have type="file"
const regex = /(<boltAction[^>]*type="file"[^>]*>)([\s\S]*?)(<\/boltAction>)/g;
// Replace each matching occurrence
return input.replace(regex, (_0, openingTag, _2, closingTag) => {
return `${openingTag}\n ...\n ${closingTag}`;
});
}
// Common patterns to ignore, similar to .gitignore
const IGNORE_PATTERNS = [
'node_modules/**',
'.git/**',
'dist/**',
'build/**',
'.next/**',
'coverage/**',
'.cache/**',
'.vscode/**',
'.idea/**',
'**/*.log',
'**/.DS_Store',
'**/npm-debug.log*',
'**/yarn-debug.log*',
'**/yarn-error.log*',
'**/*lock.json',
'**/*lock.yml',
];
const ig = ignore().add(IGNORE_PATTERNS);
function createFilesContext(files: FileMap) {
let filePaths = Object.keys(files);
filePaths = filePaths.filter((x) => {
const relPath = x.replace('/home/project/', '');
return !ig.ignores(relPath);
});
const fileContexts = filePaths
.filter((x) => files[x] && files[x].type == 'file')
.map((path) => {
const dirent = files[path];
if (!dirent || dirent.type == 'folder') {
return '';
}
const codeWithLinesNumbers = dirent.content
.split('\n')
.map((v, i) => `${i + 1}|${v}`)
.join('\n');
return `<file path="${path}">\n${codeWithLinesNumbers}\n</file>`;
});
return `Below are the code files present in the webcontainer:\ncode format:\n<line number>|<line content>\n <codebase>${fileContexts.join('\n\n')}\n\n</codebase>`;
}
function extractPropertiesFromMessage(message: Message): { model: string; provider: string; content: string } {
const textContent = Array.isArray(message.content)
? message.content.find((item) => item.type === 'text')?.text || ''
: message.content;
const modelMatch = textContent.match(MODEL_REGEX);
const providerMatch = textContent.match(PROVIDER_REGEX);
/*
* Extract model
* const modelMatch = message.content.match(MODEL_REGEX);
*/
const model = modelMatch ? modelMatch[1] : DEFAULT_MODEL;
/*
* Extract provider
* const providerMatch = message.content.match(PROVIDER_REGEX);
*/
const provider = providerMatch ? providerMatch[1] : DEFAULT_PROVIDER.name;
const cleanedContent = Array.isArray(message.content)
? message.content.map((item) => {
if (item.type === 'text') {
return {
type: 'text',
text: item.text?.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, ''),
};
}
return item; // Preserve image_url and other types as is
})
: textContent.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '');
return { model, provider, content: cleanedContent };
}
const logger = createScopedLogger('stream-text'); const logger = createScopedLogger('stream-text');
export async function streamText(props: { export async function streamText(props: {
messages: Messages; messages: Omit<Message, 'id'>[];
env: Env; env?: Env;
options?: StreamingOptions; options?: StreamingOptions;
apiKeys?: Record<string, string>; apiKeys?: Record<string, string>;
files?: FileMap; files?: FileMap;
providerSettings?: Record<string, IProviderSetting>; providerSettings?: Record<string, IProviderSetting>;
promptId?: string; promptId?: string;
contextOptimization?: boolean; contextOptimization?: boolean;
contextFiles?: FileMap;
summary?: string;
}) { }) {
const { messages, env: serverEnv, options, apiKeys, files, providerSettings, promptId, contextOptimization } = props; const {
messages,
// console.log({serverEnv}); env: serverEnv,
options,
apiKeys,
files,
providerSettings,
promptId,
contextOptimization,
contextFiles,
summary,
} = props;
let currentModel = DEFAULT_MODEL; let currentModel = DEFAULT_MODEL;
let currentProvider = DEFAULT_PROVIDER.name; let currentProvider = DEFAULT_PROVIDER.name;
const processedMessages = messages.map((message) => { let processedMessages = messages.map((message) => {
if (message.role === 'user') { if (message.role === 'user') {
const { model, provider, content } = extractPropertiesFromMessage(message); const { model, provider, content } = extractPropertiesFromMessage(message);
currentModel = model; currentModel = model;
@@ -219,13 +100,44 @@ export async function streamText(props: {
modificationTagName: MODIFICATIONS_TAG_NAME, modificationTagName: MODIFICATIONS_TAG_NAME,
}) ?? getSystemPrompt(); }) ?? getSystemPrompt();
if (files && contextOptimization) { if (files && contextFiles && contextOptimization) {
const codeContext = createFilesContext(files); const codeContext = createFilesContext(contextFiles, true);
systemPrompt = `${systemPrompt}\n\n ${codeContext}`; const filePaths = getFilePaths(files);
systemPrompt = `${systemPrompt}
Below are all the files present in the project:
---
${filePaths.join('\n')}
---
Below is the context loaded into context buffer for you to have knowledge of and might need changes to fullfill current user request.
CONTEXT BUFFER:
---
${codeContext}
---
`;
if (summary) {
systemPrompt = `${systemPrompt}
below is the chat history till now
CHAT SUMMARY:
---
${props.summary}
---
`;
const lastMessage = processedMessages.pop();
if (lastMessage) {
processedMessages = [lastMessage];
}
}
} }
logger.info(`Sending llm call to ${provider.name} with model ${modelDetails.name}`); logger.info(`Sending llm call to ${provider.name} with model ${modelDetails.name}`);
// console.log(systemPrompt,processedMessages);
return await _streamText({ return await _streamText({
model: provider.getModelInstance({ model: provider.getModelInstance({
model: modelDetails.name, model: modelDetails.name,

View File

@@ -0,0 +1,128 @@
import { type Message } from 'ai';
import { DEFAULT_MODEL, DEFAULT_PROVIDER, MODEL_REGEX, PROVIDER_REGEX } from '~/utils/constants';
import { IGNORE_PATTERNS, type FileMap } from './constants';
import ignore from 'ignore';
import type { ContextAnnotation } from '~/types/context';
export function extractPropertiesFromMessage(message: Omit<Message, 'id'>): {
model: string;
provider: string;
content: string;
} {
const textContent = Array.isArray(message.content)
? message.content.find((item) => item.type === 'text')?.text || ''
: message.content;
const modelMatch = textContent.match(MODEL_REGEX);
const providerMatch = textContent.match(PROVIDER_REGEX);
/*
* Extract model
* const modelMatch = message.content.match(MODEL_REGEX);
*/
const model = modelMatch ? modelMatch[1] : DEFAULT_MODEL;
/*
* Extract provider
* const providerMatch = message.content.match(PROVIDER_REGEX);
*/
const provider = providerMatch ? providerMatch[1] : DEFAULT_PROVIDER.name;
const cleanedContent = Array.isArray(message.content)
? message.content.map((item) => {
if (item.type === 'text') {
return {
type: 'text',
text: item.text?.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, ''),
};
}
return item; // Preserve image_url and other types as is
})
: textContent.replace(MODEL_REGEX, '').replace(PROVIDER_REGEX, '');
return { model, provider, content: cleanedContent };
}
export function simplifyBoltActions(input: string): string {
// Using regex to match boltAction tags that have type="file"
const regex = /(<boltAction[^>]*type="file"[^>]*>)([\s\S]*?)(<\/boltAction>)/g;
// Replace each matching occurrence
return input.replace(regex, (_0, openingTag, _2, closingTag) => {
return `${openingTag}\n ...\n ${closingTag}`;
});
}
export function createFilesContext(files: FileMap, useRelativePath?: boolean) {
const ig = ignore().add(IGNORE_PATTERNS);
let filePaths = Object.keys(files);
filePaths = filePaths.filter((x) => {
const relPath = x.replace('/home/project/', '');
return !ig.ignores(relPath);
});
const fileContexts = filePaths
.filter((x) => files[x] && files[x].type == 'file')
.map((path) => {
const dirent = files[path];
if (!dirent || dirent.type == 'folder') {
return '';
}
const codeWithLinesNumbers = dirent.content
.split('\n')
// .map((v, i) => `${i + 1}|${v}`)
.join('\n');
let filePath = path;
if (useRelativePath) {
filePath = path.replace('/home/project/', '');
}
return `<file path="${filePath}">\n${codeWithLinesNumbers}\n</file>`;
});
return `<codebase>${fileContexts.join('\n\n')}\n\n</codebase>`;
}
export function extractCurrentContext(messages: Message[]) {
const lastAssistantMessage = messages.filter((x) => x.role == 'assistant').slice(-1)[0];
if (!lastAssistantMessage) {
return { summary: undefined, codeContext: undefined };
}
let summary: ContextAnnotation | undefined;
let codeContext: ContextAnnotation | undefined;
if (!lastAssistantMessage.annotations?.length) {
return { summary: undefined, codeContext: undefined };
}
for (let i = 0; i < lastAssistantMessage.annotations.length; i++) {
const annotation = lastAssistantMessage.annotations[i];
if (!annotation || typeof annotation !== 'object') {
continue;
}
if (!(annotation as any).type) {
continue;
}
const annotationObject = annotation as any;
if (annotationObject.type === 'codeContext') {
codeContext = annotationObject;
break;
} else if (annotationObject.type === 'chatSummary') {
summary = annotationObject;
break;
}
}
return { summary, codeContext };
}

View File

@@ -99,16 +99,12 @@ Examples:
Certainly, I can help you create a JavaScript function to calculate the factorial of a number. Certainly, I can help you create a JavaScript function to calculate the factorial of a number.
<boltArtifact id="factorial-function" title="JavaScript Factorial Function"> <boltArtifact id="factorial-function" title="JavaScript Factorial Function">
<boltAction type="file" filePath="index.js"> <boltAction type="file" filePath="index.js">function factorial(n) {
function factorial(n) {
... ...
} }
... ...</boltAction>
</boltAction> <boltAction type="shell">node index.js</boltAction>
<boltAction type="shell">
node index.js
</boltAction>
</boltArtifact> </boltArtifact>
</assistant_response> </assistant_response>
</example> </example>
@@ -119,24 +115,16 @@ node index.js
Certainly! I'd be happy to help you build a snake game using JavaScript and HTML5 Canvas. This will be a basic implementation that you can later expand upon. Let's create the game step by step. Certainly! I'd be happy to help you build a snake game using JavaScript and HTML5 Canvas. This will be a basic implementation that you can later expand upon. Let's create the game step by step.
<boltArtifact id="snake-game" title="Snake Game in HTML and JavaScript"> <boltArtifact id="snake-game" title="Snake Game in HTML and JavaScript">
<boltAction type="file" filePath="package.json"> <boltAction type="file" filePath="package.json">{
{
"name": "snake", "name": "snake",
"scripts": { "scripts": {
"dev": "vite" "dev": "vite"
} }
... ...
} }</boltAction>
</boltAction> <boltAction type="shell">npm install --save-dev vite</boltAction>
<boltAction type="shell"> <boltAction type="file" filePath="index.html">...</boltAction>
npm install --save-dev vite <boltAction type="start">npm run dev</boltAction>
</boltAction>
<boltAction type="file" filePath="index.html">
...
</boltAction>
<boltAction type="start">
npm run dev
</boltAction>
</boltArtifact> </boltArtifact>
Now you can play the Snake game by opening the provided local server URL in your browser. Use the arrow keys to control the snake. Eat the red food to grow and increase your score. The game ends if you hit the wall or your own tail. Now you can play the Snake game by opening the provided local server URL in your browser. Use the arrow keys to control the snake. Eat the red food to grow and increase your score. The game ends if you hit the wall or your own tail.
@@ -149,8 +137,7 @@ npm run dev
Certainly! I'll create a bouncing ball with real gravity using React. We'll use the react-spring library for physics-based animations. Certainly! I'll create a bouncing ball with real gravity using React. We'll use the react-spring library for physics-based animations.
<boltArtifact id="bouncing-ball-react" title="Bouncing Ball with Gravity in React"> <boltArtifact id="bouncing-ball-react" title="Bouncing Ball with Gravity in React">
<boltAction type="file" filePath="package.json"> <boltAction type="file" filePath="package.json">{
{
"name": "bouncing-ball", "name": "bouncing-ball",
"private": true, "private": true,
"version": "0.0.0", "version": "0.0.0",
@@ -171,23 +158,12 @@ npm run dev
"@vitejs/plugin-react": "^3.1.0", "@vitejs/plugin-react": "^3.1.0",
"vite": "^4.2.0" "vite": "^4.2.0"
} }
} }</boltAction>
</boltAction> <boltAction type="file" filePath="index.html">...</boltAction>
<boltAction type="file" filePath="index.html"> <boltAction type="file" filePath="src/main.jsx">...</boltAction>
... <boltAction type="file" filePath="src/index.css">...</boltAction>
</boltAction> <boltAction type="file" filePath="src/App.jsx">...</boltAction>
<boltAction type="file" filePath="src/main.jsx"> <boltAction type="start">npm run dev</boltAction>
...
</boltAction>
<boltAction type="file" filePath="src/index.css">
...
</boltAction>
<boltAction type="file" filePath="src/App.jsx">
...
</boltAction>
<boltAction type="start">
npm run dev
</boltAction>
</boltArtifact> </boltArtifact>
You can now view the bouncing ball animation in the preview. The ball will start falling from the top of the screen and bounce realistically when it hits the bottom. You can now view the bouncing ball animation in the preview. The ball will start falling from the top of the screen and bounce realistically when it hits the bottom.

View File

@@ -231,17 +231,12 @@ Here are some examples of correct usage of artifacts:
Certainly, I can help you create a JavaScript function to calculate the factorial of a number. Certainly, I can help you create a JavaScript function to calculate the factorial of a number.
<boltArtifact id="factorial-function" title="JavaScript Factorial Function"> <boltArtifact id="factorial-function" title="JavaScript Factorial Function">
<boltAction type="file" filePath="index.js"> <boltAction type="file" filePath="index.js">function factorial(n) {
function factorial(n) { ...
... }
} ...</boltAction>
... <boltAction type="shell">node index.js</boltAction>
</boltAction>
<boltAction type="shell">
node index.js
</boltAction>
</boltArtifact> </boltArtifact>
</assistant_response> </assistant_response>
</example> </example>
@@ -253,27 +248,19 @@ Here are some examples of correct usage of artifacts:
Certainly! I'd be happy to help you build a snake game using JavaScript and HTML5 Canvas. This will be a basic implementation that you can later expand upon. Let's create the game step by step. Certainly! I'd be happy to help you build a snake game using JavaScript and HTML5 Canvas. This will be a basic implementation that you can later expand upon. Let's create the game step by step.
<boltArtifact id="snake-game" title="Snake Game in HTML and JavaScript"> <boltArtifact id="snake-game" title="Snake Game in HTML and JavaScript">
<boltAction type="file" filePath="package.json"> <boltAction type="file" filePath="package.json">{
{ "name": "snake",
"name": "snake", "scripts": {
"scripts": { "dev": "vite"
"dev": "vite" }
} ...
... }</boltAction>
}
</boltAction>
<boltAction type="shell"> <boltAction type="shell">npm install --save-dev vite</boltAction>
npm install --save-dev vite
</boltAction>
<boltAction type="file" filePath="index.html"> <boltAction type="file" filePath="index.html">...</boltAction>
...
</boltAction>
<boltAction type="start"> <boltAction type="start">npm run dev</boltAction>
npm run dev
</boltAction>
</boltArtifact> </boltArtifact>
Now you can play the Snake game by opening the provided local server URL in your browser. Use the arrow keys to control the snake. Eat the red food to grow and increase your score. The game ends if you hit the wall or your own tail. Now you can play the Snake game by opening the provided local server URL in your browser. Use the arrow keys to control the snake. Eat the red food to grow and increase your score. The game ends if you hit the wall or your own tail.
@@ -287,50 +274,38 @@ Here are some examples of correct usage of artifacts:
Certainly! I'll create a bouncing ball with real gravity using React. We'll use the react-spring library for physics-based animations. Certainly! I'll create a bouncing ball with real gravity using React. We'll use the react-spring library for physics-based animations.
<boltArtifact id="bouncing-ball-react" title="Bouncing Ball with Gravity in React"> <boltArtifact id="bouncing-ball-react" title="Bouncing Ball with Gravity in React">
<boltAction type="file" filePath="package.json"> <boltAction type="file" filePath="package.json">{
{ "name": "bouncing-ball",
"name": "bouncing-ball", "private": true,
"private": true, "version": "0.0.0",
"version": "0.0.0", "type": "module",
"type": "module", "scripts": {
"scripts": { "dev": "vite",
"dev": "vite", "build": "vite build",
"build": "vite build", "preview": "vite preview"
"preview": "vite preview" },
}, "dependencies": {
"dependencies": { "react": "^18.2.0",
"react": "^18.2.0", "react-dom": "^18.2.0",
"react-dom": "^18.2.0", "react-spring": "^9.7.1"
"react-spring": "^9.7.1" },
}, "devDependencies": {
"devDependencies": { "@types/react": "^18.0.28",
"@types/react": "^18.0.28", "@types/react-dom": "^18.0.11",
"@types/react-dom": "^18.0.11", "@vitejs/plugin-react": "^3.1.0",
"@vitejs/plugin-react": "^3.1.0", "vite": "^4.2.0"
"vite": "^4.2.0" }
} }</boltAction>
}
</boltAction>
<boltAction type="file" filePath="index.html"> <boltAction type="file" filePath="index.html">...</boltAction>
...
</boltAction>
<boltAction type="file" filePath="src/main.jsx"> <boltAction type="file" filePath="src/main.jsx">...</boltAction>
...
</boltAction>
<boltAction type="file" filePath="src/index.css"> <boltAction type="file" filePath="src/index.css">...</boltAction>
...
</boltAction>
<boltAction type="file" filePath="src/App.jsx"> <boltAction type="file" filePath="src/App.jsx">...</boltAction>
...
</boltAction>
<boltAction type="start"> <boltAction type="start">npm run dev</boltAction>
npm run dev
</boltAction>
</boltArtifact> </boltArtifact>
You can now view the bouncing ball animation in the preview. The ball will start falling from the top of the screen and bounce realistically when it hits the bottom. You can now view the bouncing ball animation in the preview. The ball will start falling from the top of the screen and bounce realistically when it hits the bottom.

View File

@@ -111,7 +111,7 @@ export abstract class BaseProvider implements ProviderInfo {
abstract getModelInstance(options: { abstract getModelInstance(options: {
model: string; model: string;
serverEnv: Env; serverEnv?: Env;
apiKeys?: Record<string, string>; apiKeys?: Record<string, string>;
providerSettings?: Record<string, IProviderSetting>; providerSettings?: Record<string, IProviderSetting>;
}): LanguageModelV1; }): LanguageModelV1;

View File

@@ -15,6 +15,7 @@ export default class DeepseekProvider extends BaseProvider {
staticModels: ModelInfo[] = [ staticModels: ModelInfo[] = [
{ name: 'deepseek-coder', label: 'Deepseek-Coder', provider: 'Deepseek', maxTokenAllowed: 8000 }, { name: 'deepseek-coder', label: 'Deepseek-Coder', provider: 'Deepseek', maxTokenAllowed: 8000 },
{ name: 'deepseek-chat', label: 'Deepseek-Chat', provider: 'Deepseek', maxTokenAllowed: 8000 }, { name: 'deepseek-chat', label: 'Deepseek-Chat', provider: 'Deepseek', maxTokenAllowed: 8000 },
{ name: 'deepseek-reasoner', label: 'Deepseek-Reasoner', provider: 'Deepseek', maxTokenAllowed: 8000 },
]; ];
getModelInstance(options: { getModelInstance(options: {

View File

@@ -11,7 +11,8 @@ export default class GithubProvider extends BaseProvider {
config = { config = {
apiTokenKey: 'GITHUB_API_KEY', apiTokenKey: 'GITHUB_API_KEY',
}; };
// find more in https://github.com/marketplace?type=models
// find more in https://github.com/marketplace?type=models
staticModels: ModelInfo[] = [ staticModels: ModelInfo[] = [
{ name: 'gpt-4o', label: 'GPT-4o', provider: 'Github', maxTokenAllowed: 8000 }, { name: 'gpt-4o', label: 'GPT-4o', provider: 'Github', maxTokenAllowed: 8000 },
{ name: 'o1', label: 'o1-preview', provider: 'Github', maxTokenAllowed: 100000 }, { name: 'o1', label: 'o1-preview', provider: 'Github', maxTokenAllowed: 100000 },

View File

@@ -1,11 +1,15 @@
import { type ActionFunctionArgs } from '@remix-run/cloudflare'; import { type ActionFunctionArgs } from '@remix-run/cloudflare';
import { createDataStream } from 'ai'; import { createDataStream, generateId } from 'ai';
import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS } from '~/lib/.server/llm/constants'; import { MAX_RESPONSE_SEGMENTS, MAX_TOKENS, type FileMap } from '~/lib/.server/llm/constants';
import { CONTINUE_PROMPT } from '~/lib/common/prompts/prompts'; import { CONTINUE_PROMPT } from '~/lib/common/prompts/prompts';
import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text'; import { streamText, type Messages, type StreamingOptions } from '~/lib/.server/llm/stream-text';
import SwitchableStream from '~/lib/.server/llm/switchable-stream'; import SwitchableStream from '~/lib/.server/llm/switchable-stream';
import type { IProviderSetting } from '~/types/model'; import type { IProviderSetting } from '~/types/model';
import { createScopedLogger } from '~/utils/logger'; import { createScopedLogger } from '~/utils/logger';
import { getFilePaths, selectContext } from '~/lib/.server/llm/select-context';
import type { ContextAnnotation, ProgressAnnotation } from '~/types/context';
import { WORK_DIR } from '~/utils/constants';
import { createSummary } from '~/lib/.server/llm/create-summary';
export async function action(args: ActionFunctionArgs) { export async function action(args: ActionFunctionArgs) {
return chatAction(args); return chatAction(args);
@@ -52,23 +56,121 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
promptTokens: 0, promptTokens: 0,
totalTokens: 0, totalTokens: 0,
}; };
const encoder: TextEncoder = new TextEncoder();
let progressCounter: number = 1;
try { try {
const options: StreamingOptions = { const totalMessageContent = messages.reduce((acc, message) => acc + message.content, '');
toolChoice: 'none', logger.debug(`Total message length: ${totalMessageContent.split(' ').length}, words`);
onFinish: async ({ text: content, finishReason, usage }) => {
logger.debug('usage', JSON.stringify(usage));
if (usage) { const dataStream = createDataStream({
cumulativeUsage.completionTokens += usage.completionTokens || 0; async execute(dataStream) {
cumulativeUsage.promptTokens += usage.promptTokens || 0; const filePaths = getFilePaths(files || {});
cumulativeUsage.totalTokens += usage.totalTokens || 0; let filteredFiles: FileMap | undefined = undefined;
let summary: string | undefined = undefined;
if (filePaths.length > 0 && contextOptimization) {
dataStream.writeData('HI ');
logger.debug('Generating Chat Summary');
dataStream.writeMessageAnnotation({
type: 'progress',
value: progressCounter++,
message: 'Generating Chat Summary',
} as ProgressAnnotation);
// Create a summary of the chat
console.log(`Messages count: ${messages.length}`);
summary = await createSummary({
messages: [...messages],
env: context.cloudflare?.env,
apiKeys,
providerSettings,
promptId,
contextOptimization,
onFinish(resp) {
if (resp.usage) {
logger.debug('createSummary token usage', JSON.stringify(resp.usage));
cumulativeUsage.completionTokens += resp.usage.completionTokens || 0;
cumulativeUsage.promptTokens += resp.usage.promptTokens || 0;
cumulativeUsage.totalTokens += resp.usage.totalTokens || 0;
}
},
});
dataStream.writeMessageAnnotation({
type: 'chatSummary',
summary,
chatId: messages.slice(-1)?.[0]?.id,
} as ContextAnnotation);
// Update context buffer
logger.debug('Updating Context Buffer');
dataStream.writeMessageAnnotation({
type: 'progress',
value: progressCounter++,
message: 'Updating Context Buffer',
} as ProgressAnnotation);
// Select context files
console.log(`Messages count: ${messages.length}`);
filteredFiles = await selectContext({
messages: [...messages],
env: context.cloudflare?.env,
apiKeys,
files,
providerSettings,
promptId,
contextOptimization,
summary,
onFinish(resp) {
if (resp.usage) {
logger.debug('selectContext token usage', JSON.stringify(resp.usage));
cumulativeUsage.completionTokens += resp.usage.completionTokens || 0;
cumulativeUsage.promptTokens += resp.usage.promptTokens || 0;
cumulativeUsage.totalTokens += resp.usage.totalTokens || 0;
}
},
});
if (filteredFiles) {
logger.debug(`files in context : ${JSON.stringify(Object.keys(filteredFiles))}`);
}
dataStream.writeMessageAnnotation({
type: 'codeContext',
files: Object.keys(filteredFiles).map((key) => {
let path = key;
if (path.startsWith(WORK_DIR)) {
path = path.replace(WORK_DIR, '');
}
return path;
}),
} as ContextAnnotation);
dataStream.writeMessageAnnotation({
type: 'progress',
value: progressCounter++,
message: 'Context Buffer Updated',
} as ProgressAnnotation);
logger.debug('Context Buffer Updated');
} }
if (finishReason !== 'length') { // Stream the text
const encoder = new TextEncoder(); const options: StreamingOptions = {
const usageStream = createDataStream({ toolChoice: 'none',
async execute(dataStream) { onFinish: async ({ text: content, finishReason, usage }) => {
logger.debug('usage', JSON.stringify(usage));
if (usage) {
cumulativeUsage.completionTokens += usage.completionTokens || 0;
cumulativeUsage.promptTokens += usage.promptTokens || 0;
cumulativeUsage.totalTokens += usage.totalTokens || 0;
}
if (finishReason !== 'length') {
dataStream.writeMessageAnnotation({ dataStream.writeMessageAnnotation({
type: 'usage', type: 'usage',
value: { value: {
@@ -77,80 +179,89 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
totalTokens: cumulativeUsage.totalTokens, totalTokens: cumulativeUsage.totalTokens,
}, },
}); });
}, await new Promise((resolve) => setTimeout(resolve, 0));
onError: (error: any) => `Custom error: ${error.message}`,
}).pipeThrough(
new TransformStream({
transform: (chunk, controller) => {
// Convert the string stream to a byte stream
const str = typeof chunk === 'string' ? chunk : JSON.stringify(chunk);
controller.enqueue(encoder.encode(str));
},
}),
);
await stream.switchSource(usageStream);
await new Promise((resolve) => setTimeout(resolve, 0));
stream.close();
return; // stream.close();
} return;
}
if (stream.switches >= MAX_RESPONSE_SEGMENTS) { if (stream.switches >= MAX_RESPONSE_SEGMENTS) {
throw Error('Cannot continue message: Maximum segments reached'); throw Error('Cannot continue message: Maximum segments reached');
} }
const switchesLeft = MAX_RESPONSE_SEGMENTS - stream.switches; const switchesLeft = MAX_RESPONSE_SEGMENTS - stream.switches;
logger.info(`Reached max token limit (${MAX_TOKENS}): Continuing message (${switchesLeft} switches left)`); logger.info(`Reached max token limit (${MAX_TOKENS}): Continuing message (${switchesLeft} switches left)`);
messages.push({ role: 'assistant', content }); messages.push({ id: generateId(), role: 'assistant', content });
messages.push({ role: 'user', content: CONTINUE_PROMPT }); messages.push({ id: generateId(), role: 'user', content: CONTINUE_PROMPT });
const result = await streamText({
messages,
env: context.cloudflare?.env,
options,
apiKeys,
files,
providerSettings,
promptId,
contextOptimization,
});
result.mergeIntoDataStream(dataStream);
(async () => {
for await (const part of result.fullStream) {
if (part.type === 'error') {
const error: any = part.error;
logger.error(`${error}`);
return;
}
}
})();
return;
},
};
const result = await streamText({ const result = await streamText({
messages, messages,
env: context.cloudflare.env, env: context.cloudflare?.env,
options, options,
apiKeys, apiKeys,
files, files,
providerSettings, providerSettings,
promptId, promptId,
contextOptimization, contextOptimization,
contextFiles: filteredFiles,
summary,
}); });
stream.switchSource(result.toDataStream()); (async () => {
for await (const part of result.fullStream) {
if (part.type === 'error') {
const error: any = part.error;
logger.error(`${error}`);
return; return;
}
}
})();
result.mergeIntoDataStream(dataStream);
}, },
}; onError: (error: any) => `Custom error: ${error.message}`,
const totalMessageContent = messages.reduce((acc, message) => acc + message.content, ''); }).pipeThrough(
logger.debug(`Total message length: ${totalMessageContent.split(' ').length}, words`); new TransformStream({
transform: (chunk, controller) => {
// Convert the string stream to a byte stream
const str = typeof chunk === 'string' ? chunk : JSON.stringify(chunk);
controller.enqueue(encoder.encode(str));
},
}),
);
const result = await streamText({ return new Response(dataStream, {
messages,
env: context.cloudflare.env,
options,
apiKeys,
files,
providerSettings,
promptId,
contextOptimization,
});
(async () => {
for await (const part of result.fullStream) {
if (part.type === 'error') {
const error: any = part.error;
logger.error(`${error}`);
return;
}
}
})();
stream.switchSource(result.toDataStream());
// return createrespo
return new Response(stream.readable, {
status: 200, status: 200,
headers: { headers: {
'Content-Type': 'text/event-stream; charset=utf-8', 'Content-Type': 'text/event-stream; charset=utf-8',

View File

@@ -74,7 +74,7 @@ async function enhancerAction({ context, request }: ActionFunctionArgs) {
`, `,
}, },
], ],
env: context.cloudflare.env, env: context.cloudflare?.env as any,
apiKeys, apiKeys,
providerSettings, providerSettings,
}); });

View File

@@ -63,7 +63,7 @@ async function llmCallAction({ context, request }: ActionFunctionArgs) {
content: `${message}`, content: `${message}`,
}, },
], ],
env: context.cloudflare.env, env: context.cloudflare?.env as any,
apiKeys, apiKeys,
providerSettings, providerSettings,
}); });
@@ -91,7 +91,7 @@ async function llmCallAction({ context, request }: ActionFunctionArgs) {
} }
} else { } else {
try { try {
const models = await getModelList({ apiKeys, providerSettings, serverEnv: context.cloudflare.env as any }); const models = await getModelList({ apiKeys, providerSettings, serverEnv: context.cloudflare?.env as any });
const modelDetails = models.find((m: ModelInfo) => m.name === model); const modelDetails = models.find((m: ModelInfo) => m.name === model);
if (!modelDetails) { if (!modelDetails) {
@@ -116,7 +116,7 @@ async function llmCallAction({ context, request }: ActionFunctionArgs) {
], ],
model: providerInfo.getModelInstance({ model: providerInfo.getModelInstance({
model: modelDetails.name, model: modelDetails.name,
serverEnv: context.cloudflare.env as any, serverEnv: context.cloudflare?.env as any,
apiKeys, apiKeys,
providerSettings, providerSettings,
}), }),

View File

@@ -219,7 +219,7 @@
--header-height: 54px; --header-height: 54px;
--chat-max-width: 37rem; --chat-max-width: 37rem;
--chat-min-width: 640px; --chat-min-width: 640px;
--workbench-width: min(calc(100% - var(--chat-min-width)), 1536px); --workbench-width: min(calc(100% - var(--chat-min-width)), 2536px);
--workbench-inner-width: var(--workbench-width); --workbench-inner-width: var(--workbench-width);
--workbench-left: calc(100% - var(--workbench-width)); --workbench-left: calc(100% - var(--workbench-width));

16
app/types/context.ts Normal file
View File

@@ -0,0 +1,16 @@
export type ContextAnnotation =
| {
type: 'codeContext';
files: string[];
}
| {
type: 'chatSummary';
summary: string;
chatId: string;
};
export type ProgressAnnotation = {
type: 'progress';
value: number;
message: string;
};

View File

@@ -1,14 +1,81 @@
# 🚀 Release v0.0.5 # 🚀 Release v0.0.6
## What's Changed 🌟 ## What's Changed 🌟
### 🔄 Changes since v0.0.4 ### 🔄 Changes since v0.0.5
### ✨ Features
* implement Claude 3, Claude3.5, Nova Pro, Nova Lite and Mistral model integration with AWS Bedrock ([#974](https://github.com/stackblitz-labs/bolt.diy/pull/974)) by @kunjabijukchhe
* enhance chat import with multi-format support ([#936](https://github.com/stackblitz-labs/bolt.diy/pull/936)) by @sidbetatester
* added Github provider ([#1109](https://github.com/stackblitz-labs/bolt.diy/pull/1109)) by @newnol
* added the "Open Preview in a New Tab" ([#1101](https://github.com/stackblitz-labs/bolt.diy/pull/1101)) by @Stijnus
* configure dynamic providers via .env ([#1108](https://github.com/stackblitz-labs/bolt.diy/pull/1108)) by @mrsimpson
* added deepseek reasoner model in deepseek provider ([#1151](https://github.com/stackblitz-labs/bolt.diy/pull/1151)) by @thecodacus
* enhance context handling by adding code context selection and implementing summary generation ([#1091](https://github.com/stackblitz-labs/bolt.diy/pull/1091)) by @thecodacus
### 🐛 Bug Fixes ### 🐛 Bug Fixes
* hotfix auto select starter template works without github token #release ([#959](https://github.com/stackblitz-labs/bolt.diy/pull/959)) by @thecodacus * show warning on starter template failure and continue ([#960](https://github.com/stackblitz-labs/bolt.diy/pull/960)) by @thecodacus
* updated hyperbolic link ([#961](https://github.com/stackblitz-labs/bolt.diy/pull/961)) by @Gaurav-Wankhede
* introduce our own cors proxy for git import to fix 403 errors on isometric git cors proxy ([#924](https://github.com/stackblitz-labs/bolt.diy/pull/924)) by @wonderwhy-er
* git private clone with custom proxy ([#1010](https://github.com/stackblitz-labs/bolt.diy/pull/1010)) by @thecodacus
* added XAI to docker config ([#274](https://github.com/stackblitz-labs/bolt.diy/pull/274)) by @siddartha-10
* ollama and lm studio url issue fix for docker and build ([#1008](https://github.com/stackblitz-labs/bolt.diy/pull/1008)) by @thecodacus
* streaming issue fixed for build versions ([#1006](https://github.com/stackblitz-labs/bolt.diy/pull/1006)) by @thecodacus
* added ui indicator on how apikeys are set (UI/Env) for api-key-manager component ([#732](https://github.com/stackblitz-labs/bolt.diy/pull/732)) by @Adithyan777
* bugfix in fetching API Key on base llm provider. ([#1063](https://github.com/stackblitz-labs/bolt.diy/pull/1063)) by @GaryStimson
* cors issues from preview fixed by changing embedder policies ([#1056](https://github.com/stackblitz-labs/bolt.diy/pull/1056)) by @wonderwhy-er
* api-key manager cleanup and log error on llm call ([#1077](https://github.com/stackblitz-labs/bolt.diy/pull/1077)) by @thecodacus
* fallback model name not working ([#1095](https://github.com/stackblitz-labs/bolt.diy/pull/1095)) by @lewis617
* for Open preview in a new tab. ([#1122](https://github.com/stackblitz-labs/bolt.diy/pull/1122)) by @Stijnus
* auto select starter template bugfix ([#1148](https://github.com/stackblitz-labs/bolt.diy/pull/1148)) by @thecodacus
* updated system prompt to have correct indentations ([#1139](https://github.com/stackblitz-labs/bolt.diy/pull/1139)) by @thecodacus
* get environment variables for docker #1120 (2ae897a) by @leex279
### 📚 Documentation
* updating copyright in LICENSE ([#796](https://github.com/stackblitz-labs/bolt.diy/pull/796)) by @coleam00
* bugfix/formatting faq docs ([#1027](https://github.com/stackblitz-labs/bolt.diy/pull/1027)) by @leex279
* document how we work ([#809](https://github.com/stackblitz-labs/bolt.diy/pull/809)) by @mrsimpson
* update README.md ([#1124](https://github.com/stackblitz-labs/bolt.diy/pull/1124)) by @leex279
* replace docker-compose with docker compose ([#1094](https://github.com/stackblitz-labs/bolt.diy/pull/1094)) by @lewis617
### ⚙️ CI
* docker Image creation pipeline ([#1011](https://github.com/stackblitz-labs/bolt.diy/pull/1011)) by @twsl
* fix docker image workflow permissions ([#1013](https://github.com/stackblitz-labs/bolt.diy/pull/1013)) by @twsl
* added visibility change to public for docker image publish ([#1017](https://github.com/stackblitz-labs/bolt.diy/pull/1017)) by @thecodacus
* added arm64 platform for docker published images ([#1021](https://github.com/stackblitz-labs/bolt.diy/pull/1021)) by @thecodacus
### 🔍 Other Changes
* reverted visibility change ([#1018](https://github.com/stackblitz-labs/bolt.diy/pull/1018)) by @thecodacus
* Updating README with resources and small fixes. (354f416) by @coleam00
* Adding resources page to index.md for docs. (441b797) by @coleam00
* updated docs ([#1025](https://github.com/stackblitz-labs/bolt.diy/pull/1025)) by @thecodacus
* Update README.md (12c6b7a) by @Digitl-Alchemyst
## ✨ First-time Contributors
A huge thank you to our amazing new contributors! Your first contribution marks the start of an exciting journey! 🌟
* 🌟 [@Adithyan777](https://github.com/Adithyan777)
* 🌟 [@Digitl-Alchemyst](https://github.com/Digitl-Alchemyst)
* 🌟 [@GaryStimson](https://github.com/GaryStimson)
* 🌟 [@kunjabijukchhe](https://github.com/kunjabijukchhe)
* 🌟 [@leex279](https://github.com/leex279)
* 🌟 [@lewis617](https://github.com/lewis617)
* 🌟 [@newnol](https://github.com/newnol)
* 🌟 [@sidbetatester](https://github.com/sidbetatester)
* 🌟 [@siddartha-10](https://github.com/siddartha-10)
* 🌟 [@twsl](https://github.com/twsl)
## 📈 Stats ## 📈 Stats
**Full Changelog**: [`v0.0.4..v0.0.5`](https://github.com/stackblitz-labs/bolt.diy/compare/v0.0.4...v0.0.5) **Full Changelog**: [`v0.0.5..v0.0.6`](https://github.com/stackblitz-labs/bolt.diy/compare/v0.0.5...v0.0.6)

View File

@@ -37,6 +37,7 @@ services:
image: bolt-ai:development image: bolt-ai:development
build: build:
target: bolt-ai-development target: bolt-ai-development
env_file: ".env.local"
environment: environment:
- NODE_ENV=development - NODE_ENV=development
- VITE_HMR_PROTOCOL=ws - VITE_HMR_PROTOCOL=ws

View File

@@ -144,7 +144,7 @@ docker build . --target bolt-ai-development
**Option 3: Docker Compose Profile** **Option 3: Docker Compose Profile**
```bash ```bash
docker-compose --profile development up docker compose --profile development up
``` ```
#### Running the Development Container #### Running the Development Container
@@ -171,7 +171,7 @@ docker build . --target bolt-ai-production
**Option 3: Docker Compose Profile** **Option 3: Docker Compose Profile**
```bash ```bash
docker-compose --profile production up docker compose --profile production up
``` ```
#### Running the Production Container #### Running the Production Container

View File

@@ -156,7 +156,7 @@ Once you've configured your keys, the application will be ready to use the selec
2. **Run the Container**: 2. **Run the Container**:
Use Docker Compose profiles to manage environments: Use Docker Compose profiles to manage environments:
```bash ```bash
docker-compose --profile development up docker compose --profile development up
``` ```
- With the development profile, changes to your code will automatically reflect in the running container (hot reloading). - With the development profile, changes to your code will automatically reflect in the running container (hot reloading).
@@ -188,7 +188,7 @@ To keep your local version of bolt.diy up to date with the latest changes, follo
- **If using Docker**, ensure you rebuild the Docker image to avoid using a cached version: - **If using Docker**, ensure you rebuild the Docker image to avoid using a cached version:
```bash ```bash
docker-compose --profile development up --build docker compose --profile development up --build
``` ```
- **If not using Docker**, you can start the application as usual with: - **If not using Docker**, you can start the application as usual with:

View File

@@ -5,7 +5,7 @@
"license": "MIT", "license": "MIT",
"sideEffects": false, "sideEffects": false,
"type": "module", "type": "module",
"version": "0.0.5", "version": "0.0.6",
"scripts": { "scripts": {
"deploy": "npm run build && wrangler pages deploy", "deploy": "npm run build && wrangler pages deploy",
"build": "remix vite:build", "build": "remix vite:build",
@@ -30,12 +30,12 @@
"node": ">=18.18.0" "node": ">=18.18.0"
}, },
"dependencies": { "dependencies": {
"@ai-sdk/amazon-bedrock": "1.0.6",
"@ai-sdk/anthropic": "^0.0.39", "@ai-sdk/anthropic": "^0.0.39",
"@ai-sdk/cohere": "^1.0.3", "@ai-sdk/cohere": "^1.0.3",
"@ai-sdk/google": "^0.0.52", "@ai-sdk/google": "^0.0.52",
"@ai-sdk/mistral": "^0.0.43", "@ai-sdk/mistral": "^0.0.43",
"@ai-sdk/openai": "^0.0.66", "@ai-sdk/openai": "^0.0.66",
"@ai-sdk/amazon-bedrock": "1.0.6",
"@codemirror/autocomplete": "^6.18.3", "@codemirror/autocomplete": "^6.18.3",
"@codemirror/commands": "^6.7.1", "@codemirror/commands": "^6.7.1",
"@codemirror/lang-cpp": "^6.0.2", "@codemirror/lang-cpp": "^6.0.2",
@@ -62,6 +62,7 @@
"@radix-ui/react-context-menu": "^2.2.2", "@radix-ui/react-context-menu": "^2.2.2",
"@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-dropdown-menu": "^2.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-popover": "^1.1.4",
"@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-switch": "^1.1.1",
"@radix-ui/react-tooltip": "^1.1.4", "@radix-ui/react-tooltip": "^1.1.4",

320
pnpm-lock.yaml generated
View File

@@ -107,6 +107,9 @@ importers:
'@radix-ui/react-dropdown-menu': '@radix-ui/react-dropdown-menu':
specifier: ^2.1.2 specifier: ^2.1.2
version: 2.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 2.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-popover':
specifier: ^1.1.4
version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-separator': '@radix-ui/react-separator':
specifier: ^1.1.0 specifier: ^1.1.0
version: 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -1647,6 +1650,9 @@ packages:
'@radix-ui/primitive@1.1.0': '@radix-ui/primitive@1.1.0':
resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==}
'@radix-ui/primitive@1.1.1':
resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==}
'@radix-ui/react-arrow@1.1.0': '@radix-ui/react-arrow@1.1.0':
resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==} resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==}
peerDependencies: peerDependencies:
@@ -1660,6 +1666,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-arrow@1.1.1':
resolution: {integrity: sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-collection@1.1.0': '@radix-ui/react-collection@1.1.0':
resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==}
peerDependencies: peerDependencies:
@@ -1682,6 +1701,15 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
'@radix-ui/react-compose-refs@1.1.1':
resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-context-menu@2.2.2': '@radix-ui/react-context-menu@2.2.2':
resolution: {integrity: sha512-99EatSTpW+hRYHt7m8wdDlLtkmTovEe8Z/hnxUPV+SKuuNL5HWNhQI4QSdjZqNSgXHay2z4M3Dym73j9p2Gx5Q==} resolution: {integrity: sha512-99EatSTpW+hRYHt7m8wdDlLtkmTovEe8Z/hnxUPV+SKuuNL5HWNhQI4QSdjZqNSgXHay2z4M3Dym73j9p2Gx5Q==}
peerDependencies: peerDependencies:
@@ -1748,6 +1776,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-dismissable-layer@1.1.3':
resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-dropdown-menu@2.1.2': '@radix-ui/react-dropdown-menu@2.1.2':
resolution: {integrity: sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==} resolution: {integrity: sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==}
peerDependencies: peerDependencies:
@@ -1783,6 +1824,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-focus-scope@1.1.1':
resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-id@1.1.0': '@radix-ui/react-id@1.1.0':
resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==}
peerDependencies: peerDependencies:
@@ -1805,6 +1859,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-popover@1.1.4':
resolution: {integrity: sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-popper@1.2.0': '@radix-ui/react-popper@1.2.0':
resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==} resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==}
peerDependencies: peerDependencies:
@@ -1818,6 +1885,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-popper@1.2.1':
resolution: {integrity: sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-portal@1.1.2': '@radix-ui/react-portal@1.1.2':
resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==} resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==}
peerDependencies: peerDependencies:
@@ -1831,6 +1911,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-portal@1.1.3':
resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-presence@1.1.1': '@radix-ui/react-presence@1.1.1':
resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==} resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==}
peerDependencies: peerDependencies:
@@ -1844,6 +1937,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-presence@1.1.2':
resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-primitive@2.0.0': '@radix-ui/react-primitive@2.0.0':
resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==}
peerDependencies: peerDependencies:
@@ -1857,6 +1963,19 @@ packages:
'@types/react-dom': '@types/react-dom':
optional: true optional: true
'@radix-ui/react-primitive@2.0.1':
resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-roving-focus@1.1.0': '@radix-ui/react-roving-focus@1.1.0':
resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==} resolution: {integrity: sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==}
peerDependencies: peerDependencies:
@@ -1892,6 +2011,15 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
'@radix-ui/react-slot@1.1.1':
resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-switch@1.1.1': '@radix-ui/react-switch@1.1.1':
resolution: {integrity: sha512-diPqDDoBcZPSicYoMWdWx+bCPuTRH4QSp9J+65IvtdS0Kuzt67bI6n32vCj8q6NZmYW/ah+2orOtMwcX5eQwIg==} resolution: {integrity: sha512-diPqDDoBcZPSicYoMWdWx+bCPuTRH4QSp9J+65IvtdS0Kuzt67bI6n32vCj8q6NZmYW/ah+2orOtMwcX5eQwIg==}
peerDependencies: peerDependencies:
@@ -4985,6 +5113,16 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
peerDependenciesMeta:
'@types/react':
optional: true
react-remove-scroll@2.6.0: react-remove-scroll@2.6.0:
resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==} resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -4995,6 +5133,16 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
react-remove-scroll@2.6.2:
resolution: {integrity: sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
react-resizable-panels@2.1.7: react-resizable-panels@2.1.7:
resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==} resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==}
peerDependencies: peerDependencies:
@@ -5024,6 +5172,16 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
react-style-singleton@2.2.3:
resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
react-toastify@10.0.6: react-toastify@10.0.6:
resolution: {integrity: sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==} resolution: {integrity: sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==}
peerDependencies: peerDependencies:
@@ -5804,6 +5962,16 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
use-callback-ref@1.3.3:
resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==}
engines: {node: '>=10'}
peerDependencies:
'@types/react': '*'
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
use-sidecar@1.1.2: use-sidecar@1.1.2:
resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -7649,6 +7817,8 @@ snapshots:
'@radix-ui/primitive@1.1.0': {} '@radix-ui/primitive@1.1.0': {}
'@radix-ui/primitive@1.1.1': {}
'@radix-ui/react-arrow@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-arrow@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -7658,6 +7828,15 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-arrow@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-collection@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1) '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1)
@@ -7676,6 +7855,12 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@radix-ui/react-compose-refs@1.1.1(@types/react@18.3.12)(react@18.3.1)':
dependencies:
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.12
'@radix-ui/react-context-menu@2.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-context-menu@2.2.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/primitive': 1.1.0 '@radix-ui/primitive': 1.1.0
@@ -7743,6 +7928,19 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.1
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.3.12)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-dropdown-menu@2.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-dropdown-menu@2.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/primitive': 1.1.0 '@radix-ui/primitive': 1.1.0
@@ -7775,6 +7973,17 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-focus-scope@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-id@1.1.0(@types/react@18.3.12)(react@18.3.1)': '@radix-ui/react-id@1.1.0(@types/react@18.3.12)(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1) '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1)
@@ -7808,6 +8017,29 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-popover@1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/primitive': 1.1.1
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-focus-guards': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-id': 1.1.0(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-popper': 1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-portal': 1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-presence': 1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1)
aria-hidden: 1.2.4
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
react-remove-scroll: 2.6.2(@types/react@18.3.12)(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-popper@1.2.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-popper@1.2.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -7826,6 +8058,24 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-popper@1.2.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@floating-ui/react-dom': 2.1.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-arrow': 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-use-rect': 1.1.0(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/rect': 1.1.0
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-portal@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-portal@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -7836,6 +8086,16 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-portal@1.1.3(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-primitive': 2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-presence@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-presence@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1) '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1)
@@ -7846,6 +8106,16 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-presence@1.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1)
'@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.3.12)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/react-slot': 1.1.0(@types/react@18.3.12)(react@18.3.1) '@radix-ui/react-slot': 1.1.0(@types/react@18.3.12)(react@18.3.1)
@@ -7855,6 +8125,15 @@ snapshots:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@types/react-dom': 18.3.1 '@types/react-dom': 18.3.1
'@radix-ui/react-primitive@2.0.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies:
'@radix-ui/react-slot': 1.1.1(@types/react@18.3.12)(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
'@types/react-dom': 18.3.1
'@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-roving-focus@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/primitive': 1.1.0 '@radix-ui/primitive': 1.1.0
@@ -7888,6 +8167,13 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 18.3.12 '@types/react': 18.3.12
'@radix-ui/react-slot@1.1.1(@types/react@18.3.12)(react@18.3.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.1(@types/react@18.3.12)(react@18.3.1)
react: 18.3.1
optionalDependencies:
'@types/react': 18.3.12
'@radix-ui/react-switch@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': '@radix-ui/react-switch@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
dependencies: dependencies:
'@radix-ui/primitive': 1.1.0 '@radix-ui/primitive': 1.1.0
@@ -11850,6 +12136,14 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 18.3.12 '@types/react': 18.3.12
react-remove-scroll-bar@2.3.8(@types/react@18.3.12)(react@18.3.1):
dependencies:
react: 18.3.1
react-style-singleton: 2.2.3(@types/react@18.3.12)(react@18.3.1)
tslib: 2.8.1
optionalDependencies:
'@types/react': 18.3.12
react-remove-scroll@2.6.0(@types/react@18.3.12)(react@18.3.1): react-remove-scroll@2.6.0(@types/react@18.3.12)(react@18.3.1):
dependencies: dependencies:
react: 18.3.1 react: 18.3.1
@@ -11861,6 +12155,17 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 18.3.12 '@types/react': 18.3.12
react-remove-scroll@2.6.2(@types/react@18.3.12)(react@18.3.1):
dependencies:
react: 18.3.1
react-remove-scroll-bar: 2.3.8(@types/react@18.3.12)(react@18.3.1)
react-style-singleton: 2.2.1(@types/react@18.3.12)(react@18.3.1)
tslib: 2.8.1
use-callback-ref: 1.3.3(@types/react@18.3.12)(react@18.3.1)
use-sidecar: 1.1.2(@types/react@18.3.12)(react@18.3.1)
optionalDependencies:
'@types/react': 18.3.12
react-resizable-panels@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1): react-resizable-panels@2.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
react: 18.3.1 react: 18.3.1
@@ -11887,6 +12192,14 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 18.3.12 '@types/react': 18.3.12
react-style-singleton@2.2.3(@types/react@18.3.12)(react@18.3.1):
dependencies:
get-nonce: 1.0.1
react: 18.3.1
tslib: 2.8.1
optionalDependencies:
'@types/react': 18.3.12
react-toastify@10.0.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1): react-toastify@10.0.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies: dependencies:
clsx: 2.1.1 clsx: 2.1.1
@@ -12737,6 +13050,13 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/react': 18.3.12 '@types/react': 18.3.12
use-callback-ref@1.3.3(@types/react@18.3.12)(react@18.3.1):
dependencies:
react: 18.3.1
tslib: 2.8.1
optionalDependencies:
'@types/react': 18.3.12
use-sidecar@1.1.2(@types/react@18.3.12)(react@18.3.1): use-sidecar@1.1.2(@types/react@18.3.12)(react@18.3.1):
dependencies: dependencies:
detect-node-es: 1.1.0 detect-node-es: 1.1.0