Sam Denty
commited on
fix: remove monorepo
Browse filesThis view is limited to 50 files because it contains too many changes. Β
See raw diff
- .github/workflows/ci.yaml +0 -1
- README.md +53 -24
- {packages/bolt/app β app}/components/chat/Artifact.tsx +0 -0
- {packages/bolt/app β app}/components/chat/AssistantMessage.tsx +0 -0
- {packages/bolt/app β app}/components/chat/BaseChat.module.scss +0 -0
- {packages/bolt/app β app}/components/chat/BaseChat.tsx +0 -0
- {packages/bolt/app β app}/components/chat/Chat.client.tsx +0 -13
- {packages/bolt/app β app}/components/chat/CodeBlock.module.scss +0 -0
- {packages/bolt/app β app}/components/chat/CodeBlock.tsx +0 -0
- {packages/bolt/app β app}/components/chat/Markdown.module.scss +0 -0
- {packages/bolt/app β app}/components/chat/Markdown.tsx +0 -0
- {packages/bolt/app β app}/components/chat/Messages.client.tsx +0 -0
- {packages/bolt/app β app}/components/chat/SendButton.client.tsx +0 -0
- {packages/bolt/app β app}/components/chat/UserMessage.tsx +0 -0
- {packages/bolt/app β app}/components/editor/codemirror/BinaryContent.tsx +0 -0
- {packages/bolt/app β app}/components/editor/codemirror/CodeMirrorEditor.tsx +0 -0
- {packages/bolt/app β app}/components/editor/codemirror/cm-theme.ts +0 -0
- {packages/bolt/app β app}/components/editor/codemirror/indent.ts +0 -0
- {packages/bolt/app β app}/components/editor/codemirror/languages.ts +0 -0
- {packages/bolt/app β app}/components/header/Header.tsx +0 -0
- {packages/bolt/app β app}/components/header/HeaderActionButtons.client.tsx +0 -4
- {packages/bolt/app β app}/components/sidebar/HistoryItem.tsx +0 -0
- {packages/bolt/app β app}/components/sidebar/Menu.client.tsx +0 -0
- {packages/bolt/app β app}/components/sidebar/date-binning.ts +0 -0
- {packages/bolt/app β app}/components/ui/Dialog.tsx +0 -0
- {packages/bolt/app β app}/components/ui/IconButton.tsx +0 -0
- {packages/bolt/app β app}/components/ui/LoadingDots.tsx +0 -0
- {packages/bolt/app β app}/components/ui/PanelHeader.tsx +0 -0
- {packages/bolt/app β app}/components/ui/PanelHeaderButton.tsx +0 -0
- {packages/bolt/app β app}/components/ui/Slider.tsx +0 -0
- {packages/bolt/app β app}/components/ui/ThemeSwitch.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/EditorPanel.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/FileBreadcrumb.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/FileTree.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/PortDropdown.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/Preview.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/Workbench.client.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/terminal/Terminal.tsx +0 -0
- {packages/bolt/app β app}/components/workbench/terminal/theme.ts +0 -0
- {packages/bolt/app β app}/entry.client.tsx +0 -0
- {packages/bolt/app β app}/entry.server.tsx +0 -0
- {packages/bolt/app β app}/lib/.server/auth.ts +0 -0
- {packages/bolt/app β app}/lib/.server/llm/api-key.ts +0 -0
- {packages/bolt/app β app}/lib/.server/llm/constants.ts +0 -0
- {packages/bolt/app β app}/lib/.server/llm/model.ts +0 -0
- {packages/bolt/app β app}/lib/.server/llm/prompts.ts +0 -0
- {packages/bolt/app β app}/lib/.server/llm/stream-text.ts +0 -0
- {packages/bolt/app β app}/lib/.server/llm/switchable-stream.ts +0 -0
- {packages/bolt/app β app}/lib/.server/sessions.ts +0 -0
- {packages/bolt/app β app}/lib/analytics.ts +0 -67
.github/workflows/ci.yaml
CHANGED
@@ -45,7 +45,6 @@ jobs:
|
|
45 |
with:
|
46 |
wranglerVersion: '* -w'
|
47 |
packageManager: pnpm
|
48 |
-
workingDirectory: 'packages/bolt'
|
49 |
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
50 |
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
51 |
command: pages deploy
|
|
|
45 |
with:
|
46 |
wranglerVersion: '* -w'
|
47 |
packageManager: pnpm
|
|
|
48 |
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
49 |
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
50 |
command: pages deploy
|
README.md
CHANGED
@@ -1,55 +1,84 @@
|
|
1 |
-
# Bolt
|
2 |
|
3 |
-
|
4 |
|
5 |
-
##
|
6 |
|
7 |
-
|
8 |
-
|
9 |
-
- [`bolt`](packages/bolt): The main package containing the UI interface for Bolt as well as the server components.
|
10 |
-
|
11 |
-
As the project grows, additional packages may be added to this workspace.
|
12 |
-
|
13 |
-
## Getting Started
|
14 |
-
|
15 |
-
### Prerequisites
|
16 |
|
17 |
- Node.js (v20.15.1)
|
18 |
- pnpm (v9.4.0)
|
19 |
|
20 |
-
|
21 |
|
22 |
-
1. Clone the repository:
|
23 |
|
24 |
```bash
|
25 |
git clone https://github.com/stackblitz/bolt.git
|
26 |
-
cd bolt
|
27 |
```
|
28 |
|
29 |
2. Install dependencies:
|
30 |
|
31 |
```bash
|
32 |
-
pnpm
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
```
|
34 |
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
|
37 |
```bash
|
38 |
-
|
39 |
```
|
40 |
|
41 |
-
|
42 |
|
43 |
-
|
44 |
|
45 |
-
|
46 |
|
47 |
```bash
|
48 |
-
|
49 |
```
|
50 |
|
51 |
-
|
|
|
|
|
52 |
|
53 |
```bash
|
54 |
-
pnpm run
|
55 |
```
|
|
|
|
|
|
1 |
+
# Bolt
|
2 |
|
3 |
+
Bolt is an AI assistant developed by StackBlitz. This package contains the UI interface for Bolt as well as the server components, built using [Remix Run](https://remix.run/).
|
4 |
|
5 |
+
## Prerequisites
|
6 |
|
7 |
+
Before you begin, ensure you have the following installed:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
- Node.js (v20.15.1)
|
10 |
- pnpm (v9.4.0)
|
11 |
|
12 |
+
## Setup
|
13 |
|
14 |
+
1. Clone the repository (if you haven't already):
|
15 |
|
16 |
```bash
|
17 |
git clone https://github.com/stackblitz/bolt.git
|
|
|
18 |
```
|
19 |
|
20 |
2. Install dependencies:
|
21 |
|
22 |
```bash
|
23 |
+
pnpm install
|
24 |
+
```
|
25 |
+
|
26 |
+
3. Create a `.env.local` file in the root directory and add your Anthropic API key:
|
27 |
+
|
28 |
+
```
|
29 |
+
ANTHROPIC_API_KEY=XXX
|
30 |
+
```
|
31 |
+
|
32 |
+
Optionally, you an set the debug level or disable authentication:
|
33 |
+
|
34 |
+
```
|
35 |
+
VITE_LOG_LEVEL=debug
|
36 |
+
VITE_DISABLE_AUTH=1
|
37 |
```
|
38 |
|
39 |
+
If you want to run authentication against a local StackBlitz instance, add:
|
40 |
+
|
41 |
+
```
|
42 |
+
VITE_CLIENT_ORIGIN=https://local.stackblitz.com:3000
|
43 |
+
```
|
44 |
+
|
45 |
+
**Important**: Never commit your `.env.local` file to version control. It's already included in .gitignore.
|
46 |
+
|
47 |
+
## Available Scripts
|
48 |
+
|
49 |
+
- `pnpm run dev`: Starts the development server.
|
50 |
+
- `pnpm run build`: Builds the project.
|
51 |
+
- `pnpm run start`: Runs the built application locally using Wrangler Pages. This script uses `bindings.sh` to set up necessary bindings so you don't have to duplicate environment variables.
|
52 |
+
- `pnpm run preview`: Builds the project and then starts it locally, useful for testing the production build. Note, HTTP streaming currently doesn't work as expected with `wrangler pages dev`.
|
53 |
+
- `pnpm test:` Runs the test suite using Vitest.
|
54 |
+
- `pnpm run typecheck`: Runs TypeScript type checking.
|
55 |
+
- `pnpm run typegen`: Generates TypeScript types using Wrangler.
|
56 |
+
- `pnpm run deploy`: Builds the project and deploys it to Cloudflare Pages.
|
57 |
+
|
58 |
+
## Development
|
59 |
+
|
60 |
+
To start the development server:
|
61 |
|
62 |
```bash
|
63 |
+
pnpm run dev
|
64 |
```
|
65 |
|
66 |
+
This will start the Remix Vite development server.
|
67 |
|
68 |
+
## Testing
|
69 |
|
70 |
+
Run the test suite with:
|
71 |
|
72 |
```bash
|
73 |
+
pnpm test
|
74 |
```
|
75 |
|
76 |
+
## Deployment
|
77 |
+
|
78 |
+
To deploy the application to Cloudflare Pages:
|
79 |
|
80 |
```bash
|
81 |
+
pnpm run deploy
|
82 |
```
|
83 |
+
|
84 |
+
Make sure you have the necessary permissions and Wrangler is correctly configured for your Cloudflare account.
|
{packages/bolt/app β app}/components/chat/Artifact.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/AssistantMessage.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/BaseChat.module.scss
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/BaseChat.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/Chat.client.tsx
RENAMED
@@ -4,7 +4,6 @@ import { useChat } from 'ai/react';
|
|
4 |
import { useAnimate } from 'framer-motion';
|
5 |
import { memo, useEffect, useRef, useState } from 'react';
|
6 |
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
7 |
-
import { AnalyticsAction, AnalyticsTrackEvent, sendAnalyticsEvent } from '~/lib/analytics';
|
8 |
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
|
9 |
import { useChatHistory } from '~/lib/persistence';
|
10 |
import { chatStore } from '~/lib/stores/chat';
|
@@ -195,18 +194,6 @@ export const ChatImpl = memo(({ initialMessages, storeMessageHistory }: ChatProp
|
|
195 |
resetEnhancer();
|
196 |
|
197 |
textareaRef.current?.blur();
|
198 |
-
|
199 |
-
const event = messages.length === 0 ? AnalyticsTrackEvent.ChatCreated : AnalyticsTrackEvent.MessageSent;
|
200 |
-
|
201 |
-
sendAnalyticsEvent({
|
202 |
-
action: AnalyticsAction.Track,
|
203 |
-
payload: {
|
204 |
-
event,
|
205 |
-
properties: {
|
206 |
-
message: _input,
|
207 |
-
},
|
208 |
-
},
|
209 |
-
});
|
210 |
};
|
211 |
|
212 |
const [messageRef, scrollRef] = useSnapScroll();
|
|
|
4 |
import { useAnimate } from 'framer-motion';
|
5 |
import { memo, useEffect, useRef, useState } from 'react';
|
6 |
import { cssTransition, toast, ToastContainer } from 'react-toastify';
|
|
|
7 |
import { useMessageParser, usePromptEnhancer, useShortcuts, useSnapScroll } from '~/lib/hooks';
|
8 |
import { useChatHistory } from '~/lib/persistence';
|
9 |
import { chatStore } from '~/lib/stores/chat';
|
|
|
194 |
resetEnhancer();
|
195 |
|
196 |
textareaRef.current?.blur();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
197 |
};
|
198 |
|
199 |
const [messageRef, scrollRef] = useSnapScroll();
|
{packages/bolt/app β app}/components/chat/CodeBlock.module.scss
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/CodeBlock.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/Markdown.module.scss
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/Markdown.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/Messages.client.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/SendButton.client.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/chat/UserMessage.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/editor/codemirror/BinaryContent.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/editor/codemirror/CodeMirrorEditor.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/editor/codemirror/cm-theme.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/components/editor/codemirror/indent.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/components/editor/codemirror/languages.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/components/header/Header.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/header/HeaderActionButtons.client.tsx
RENAMED
@@ -2,7 +2,6 @@ import { useStore } from '@nanostores/react';
|
|
2 |
import { chatStore } from '~/lib/stores/chat';
|
3 |
import { workbenchStore } from '~/lib/stores/workbench';
|
4 |
import { classNames } from '~/utils/classNames';
|
5 |
-
import { OpenStackBlitz } from './OpenStackBlitz.client';
|
6 |
|
7 |
interface HeaderActionButtonsProps {}
|
8 |
|
@@ -40,9 +39,6 @@ export function HeaderActionButtons({}: HeaderActionButtonsProps) {
|
|
40 |
<div className="i-ph:code-bold" />
|
41 |
</Button>
|
42 |
</div>
|
43 |
-
<div className="flex ml-2">
|
44 |
-
<OpenStackBlitz />
|
45 |
-
</div>
|
46 |
</div>
|
47 |
);
|
48 |
}
|
|
|
2 |
import { chatStore } from '~/lib/stores/chat';
|
3 |
import { workbenchStore } from '~/lib/stores/workbench';
|
4 |
import { classNames } from '~/utils/classNames';
|
|
|
5 |
|
6 |
interface HeaderActionButtonsProps {}
|
7 |
|
|
|
39 |
<div className="i-ph:code-bold" />
|
40 |
</Button>
|
41 |
</div>
|
|
|
|
|
|
|
42 |
</div>
|
43 |
);
|
44 |
}
|
{packages/bolt/app β app}/components/sidebar/HistoryItem.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/sidebar/Menu.client.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/sidebar/date-binning.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/components/ui/Dialog.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/ui/IconButton.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/ui/LoadingDots.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/ui/PanelHeader.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/ui/PanelHeaderButton.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/ui/Slider.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/ui/ThemeSwitch.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/EditorPanel.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/FileBreadcrumb.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/FileTree.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/PortDropdown.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/Preview.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/Workbench.client.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/terminal/Terminal.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/components/workbench/terminal/theme.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/entry.client.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/entry.server.tsx
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/auth.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/llm/api-key.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/llm/constants.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/llm/model.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/llm/prompts.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/llm/stream-text.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/llm/switchable-stream.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/.server/sessions.ts
RENAMED
File without changes
|
{packages/bolt/app β app}/lib/analytics.ts
RENAMED
@@ -1,7 +1,5 @@
|
|
1 |
-
import { Analytics, type IdentifyParams, type PageParams, type TrackParams } from '@segment/analytics-node';
|
2 |
import { CLIENT_ORIGIN } from '~/lib/constants';
|
3 |
import { request as doRequest } from '~/lib/fetch';
|
4 |
-
import { logger } from '~/utils/logger';
|
5 |
|
6 |
export interface Identity {
|
7 |
userId?: string | null;
|
@@ -18,20 +16,6 @@ export enum AnalyticsTrackEvent {
|
|
18 |
ChatCreated = `${MESSAGE_PREFIX} Chat Created`,
|
19 |
}
|
20 |
|
21 |
-
export enum AnalyticsAction {
|
22 |
-
Identify = 'identify',
|
23 |
-
Page = 'page',
|
24 |
-
Track = 'track',
|
25 |
-
}
|
26 |
-
|
27 |
-
// we can omit the user ID since it's retrieved from the user's session
|
28 |
-
type OmitUserId<T> = Omit<T, 'userId'>;
|
29 |
-
|
30 |
-
export type AnalyticsEvent =
|
31 |
-
| { action: AnalyticsAction.Identify; payload: OmitUserId<IdentifyParams> }
|
32 |
-
| { action: AnalyticsAction.Page; payload: OmitUserId<PageParams> }
|
33 |
-
| { action: AnalyticsAction.Track; payload: OmitUserId<TrackParams> };
|
34 |
-
|
35 |
export async function identifyUser(access: string): Promise<Identity | undefined> {
|
36 |
const response = await doRequest(`${CLIENT_ORIGIN}/api/identify`, {
|
37 |
method: 'GET',
|
@@ -52,54 +36,3 @@ export async function identifyUser(access: string): Promise<Identity | undefined
|
|
52 |
|
53 |
return Object.fromEntries(stringified) as Identity;
|
54 |
}
|
55 |
-
|
56 |
-
// send an analytics event from the client
|
57 |
-
export async function sendAnalyticsEvent(event: AnalyticsEvent) {
|
58 |
-
// don't send analytics events when in dev mode
|
59 |
-
if (import.meta.env.DEV) {
|
60 |
-
return;
|
61 |
-
}
|
62 |
-
|
63 |
-
const request = await fetch('/api/analytics', {
|
64 |
-
method: 'POST',
|
65 |
-
headers: {
|
66 |
-
'Content-Type': 'application/json',
|
67 |
-
},
|
68 |
-
body: JSON.stringify(event),
|
69 |
-
});
|
70 |
-
|
71 |
-
if (!request.ok) {
|
72 |
-
logger.error(`Error handling Segment Analytics action: ${event.action}`);
|
73 |
-
}
|
74 |
-
}
|
75 |
-
|
76 |
-
// send an analytics event from the server
|
77 |
-
export async function sendEventInternal(identity: Identity, { action, payload }: AnalyticsEvent) {
|
78 |
-
const { userId, segmentWriteKey: writeKey } = identity;
|
79 |
-
|
80 |
-
if (!userId || !writeKey) {
|
81 |
-
logger.warn('Missing user ID or write key when logging analytics');
|
82 |
-
return { success: false as const, error: 'missing-data' };
|
83 |
-
}
|
84 |
-
|
85 |
-
const analytics = new Analytics({ flushAt: 1, writeKey }).on('error', logger.error);
|
86 |
-
|
87 |
-
try {
|
88 |
-
await new Promise((resolve, reject) => {
|
89 |
-
if (action === AnalyticsAction.Identify) {
|
90 |
-
analytics.identify({ ...payload, userId }, resolve);
|
91 |
-
} else if (action === AnalyticsAction.Page) {
|
92 |
-
analytics.page({ ...payload, userId }, resolve);
|
93 |
-
} else if (action === AnalyticsAction.Track) {
|
94 |
-
analytics.track({ ...payload, userId }, resolve);
|
95 |
-
} else {
|
96 |
-
reject();
|
97 |
-
}
|
98 |
-
});
|
99 |
-
} catch {
|
100 |
-
logger.error(`Error handling Segment Analytics action: ${action}`);
|
101 |
-
return { success: false as const, error: 'invalid-action' };
|
102 |
-
}
|
103 |
-
|
104 |
-
return { success: true as const };
|
105 |
-
}
|
|
|
|
|
1 |
import { CLIENT_ORIGIN } from '~/lib/constants';
|
2 |
import { request as doRequest } from '~/lib/fetch';
|
|
|
3 |
|
4 |
export interface Identity {
|
5 |
userId?: string | null;
|
|
|
16 |
ChatCreated = `${MESSAGE_PREFIX} Chat Created`,
|
17 |
}
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
export async function identifyUser(access: string): Promise<Identity | undefined> {
|
20 |
const response = await doRequest(`${CLIENT_ORIGIN}/api/identify`, {
|
21 |
method: 'GET',
|
|
|
36 |
|
37 |
return Object.fromEntries(stringified) as Identity;
|
38 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|