From 6927c07451d188cb1f985294a53f591fbcd98407 Mon Sep 17 00:00:00 2001 From: Dominic Elm Date: Wed, 10 Jul 2024 18:44:39 +0200 Subject: [PATCH] feat: initial commit --- .github/workflows/ci.yaml | 35 + .github/workflows/semantic-pr.yaml | 32 + .gitignore | 23 + .husky/pre-commit | 1 + .prettierignore | 2 + .prettierrc | 8 + .tool-versions | 2 + .vscode/launch.json | 16 + README.md | 49 + eslint.config.mjs | 26 + package.json | 40 + packages/bolt/.gitignore | 8 + packages/bolt/README.md | 78 + packages/bolt/app/components/Header.tsx | 12 + .../bolt/app/components/chat/Artifact.tsx | 29 + .../app/components/chat/AssistantMessage.tsx | 14 + .../bolt/app/components/chat/BaseChat.tsx | 140 + .../bolt/app/components/chat/Chat.client.tsx | 123 + .../app/components/chat/CodeBlock.module.scss | 10 + .../bolt/app/components/chat/CodeBlock.tsx | 82 + .../app/components/chat/Markdown.module.scss | 136 + .../bolt/app/components/chat/Markdown.tsx | 66 + .../bolt/app/components/chat/Messages.tsx | 55 + .../app/components/chat/SendButton.client.tsx | 30 + .../bolt/app/components/chat/UserMessage.tsx | 13 + .../bolt/app/components/ui/IconButton.tsx | 70 + .../components/workspace/WorkspacePanel.tsx | 3 + packages/bolt/app/entry.client.tsx | 12 + packages/bolt/app/entry.server.tsx | 34 + packages/bolt/app/lib/.server/llm/api-key.ts | 9 + packages/bolt/app/lib/.server/llm/model.ts | 9 + packages/bolt/app/lib/.server/llm/prompts.ts | 185 + packages/bolt/app/lib/hooks/index.ts | 2 + .../bolt/app/lib/hooks/useMessageParser.ts | 57 + .../bolt/app/lib/hooks/usePromptEnhancer.ts | 71 + .../app/lib/runtime/message-parser.spec.ts | 84 + .../bolt/app/lib/runtime/message-parser.ts | 172 + packages/bolt/app/lib/stores/workspace.ts | 42 + packages/bolt/app/lib/webcontainer/index.ts | 31 + packages/bolt/app/root.tsx | 51 + packages/bolt/app/routes/_index.tsx | 18 + packages/bolt/app/routes/api.chat.ts | 43 + packages/bolt/app/routes/api.enhancer.ts | 65 + packages/bolt/app/styles/index.scss | 31 + packages/bolt/app/styles/variables.scss | 21 + packages/bolt/app/utils/classNames.ts | 61 + packages/bolt/app/utils/logger.ts | 87 + packages/bolt/app/utils/markdown.ts | 6 + packages/bolt/app/utils/promises.ts | 19 + packages/bolt/app/utils/stripIndent.ts | 23 + packages/bolt/bindings.sh | 16 + packages/bolt/functions/[[path]].ts | 9 + packages/bolt/icons/logo.svg | 4 + packages/bolt/icons/stars.svg | 1 + packages/bolt/load-context.ts | 9 + packages/bolt/package.json | 59 + packages/bolt/public/favicon.svg | 4 + packages/bolt/tsconfig.json | 33 + packages/bolt/uno.config.ts | 134 + packages/bolt/vite.config.ts | 21 + packages/bolt/worker-configuration.d.ts | 3 + packages/bolt/wrangler.toml | 5 + pnpm-lock.yaml | 9794 +++++++++++++++++ pnpm-workspace.yaml | 2 + 64 files changed, 12330 insertions(+) create mode 100644 .github/workflows/ci.yaml create mode 100644 .github/workflows/semantic-pr.yaml create mode 100644 .gitignore create mode 100644 .husky/pre-commit create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .tool-versions create mode 100644 .vscode/launch.json create mode 100644 README.md create mode 100644 eslint.config.mjs create mode 100644 package.json create mode 100644 packages/bolt/.gitignore create mode 100644 packages/bolt/README.md create mode 100644 packages/bolt/app/components/Header.tsx create mode 100644 packages/bolt/app/components/chat/Artifact.tsx create mode 100644 packages/bolt/app/components/chat/AssistantMessage.tsx create mode 100644 packages/bolt/app/components/chat/BaseChat.tsx create mode 100644 packages/bolt/app/components/chat/Chat.client.tsx create mode 100644 packages/bolt/app/components/chat/CodeBlock.module.scss create mode 100644 packages/bolt/app/components/chat/CodeBlock.tsx create mode 100644 packages/bolt/app/components/chat/Markdown.module.scss create mode 100644 packages/bolt/app/components/chat/Markdown.tsx create mode 100644 packages/bolt/app/components/chat/Messages.tsx create mode 100644 packages/bolt/app/components/chat/SendButton.client.tsx create mode 100644 packages/bolt/app/components/chat/UserMessage.tsx create mode 100644 packages/bolt/app/components/ui/IconButton.tsx create mode 100644 packages/bolt/app/components/workspace/WorkspacePanel.tsx create mode 100644 packages/bolt/app/entry.client.tsx create mode 100644 packages/bolt/app/entry.server.tsx create mode 100644 packages/bolt/app/lib/.server/llm/api-key.ts create mode 100644 packages/bolt/app/lib/.server/llm/model.ts create mode 100644 packages/bolt/app/lib/.server/llm/prompts.ts create mode 100644 packages/bolt/app/lib/hooks/index.ts create mode 100644 packages/bolt/app/lib/hooks/useMessageParser.ts create mode 100644 packages/bolt/app/lib/hooks/usePromptEnhancer.ts create mode 100644 packages/bolt/app/lib/runtime/message-parser.spec.ts create mode 100644 packages/bolt/app/lib/runtime/message-parser.ts create mode 100644 packages/bolt/app/lib/stores/workspace.ts create mode 100644 packages/bolt/app/lib/webcontainer/index.ts create mode 100644 packages/bolt/app/root.tsx create mode 100644 packages/bolt/app/routes/_index.tsx create mode 100644 packages/bolt/app/routes/api.chat.ts create mode 100644 packages/bolt/app/routes/api.enhancer.ts create mode 100644 packages/bolt/app/styles/index.scss create mode 100644 packages/bolt/app/styles/variables.scss create mode 100644 packages/bolt/app/utils/classNames.ts create mode 100644 packages/bolt/app/utils/logger.ts create mode 100644 packages/bolt/app/utils/markdown.ts create mode 100644 packages/bolt/app/utils/promises.ts create mode 100644 packages/bolt/app/utils/stripIndent.ts create mode 100755 packages/bolt/bindings.sh create mode 100644 packages/bolt/functions/[[path]].ts create mode 100644 packages/bolt/icons/logo.svg create mode 100644 packages/bolt/icons/stars.svg create mode 100644 packages/bolt/load-context.ts create mode 100644 packages/bolt/package.json create mode 100644 packages/bolt/public/favicon.svg create mode 100644 packages/bolt/tsconfig.json create mode 100644 packages/bolt/uno.config.ts create mode 100644 packages/bolt/vite.config.ts create mode 100644 packages/bolt/worker-configuration.d.ts create mode 100644 packages/bolt/wrangler.toml create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..e1ee8e2 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,35 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [18.20.3] + steps: + - name: Setup + uses: pnpm/action-setup@v4 + with: + version: 9.4.0 + + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: pnpm install + + - name: Run type check + run: pnpm run typecheck + + - name: Run ESLint + run: pnpm run lint + + - name: Run tests + run: pnpm run test diff --git a/.github/workflows/semantic-pr.yaml b/.github/workflows/semantic-pr.yaml new file mode 100644 index 0000000..503b045 --- /dev/null +++ b/.github/workflows/semantic-pr.yaml @@ -0,0 +1,32 @@ +name: Semantic Pull Request +on: + pull_request_target: + types: [opened, reopened, edited, synchronize] +permissions: + pull-requests: read +jobs: + main: + name: Validate PR Title + runs-on: ubuntu-latest + steps: + # https://github.com/amannn/action-semantic-pull-request/releases/tag/v5.5.3 + - uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + subjectPattern: ^(?![A-Z]).+$ + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + didn't match the configured pattern. Please ensure that the subject + doesn't start with an uppercase character. + types: | + fix + feat + chore + build + ci + perf + docs + refactor + revert + test diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c3ed3e6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,23 @@ +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +.vscode/* +!.vscode/launch.json +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..72c4429 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1 @@ +npm test diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..3a08d6e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +pnpm-lock.yaml +.astro diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..8d3dfb0 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,8 @@ +{ + "printWidth": 120, + "singleQuote": true, + "useTabs": false, + "tabWidth": 2, + "semi": true, + "bracketSpacing": true +} diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..c08a31d --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +nodejs 18.20.3 +pnpm 9.4.0 diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..feaf418 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Debug Current Test File", + "autoAttachChildProcesses": true, + "skipFiles": ["/**", "**/node_modules/**"], + "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", + "args": ["run", "${relativeFile}"], + "smartStep": true, + "console": "integratedTerminal" + } + ] +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..80421fa --- /dev/null +++ b/README.md @@ -0,0 +1,49 @@ +# Bolt Monorepo + +Welcome to the Bolt monorepo! This repository contains the codebase for Bolt, an AI assistant developed by StackBlitz. + +## Repository Structure + +Currently, this monorepo contains a single package: + +- [`bolt`](packages/bolt): The main package containing the UI interface for Bolt as well as the server components. + +As the project grows, additional packages may be added to this workspace. + +## Getting Started + +### Prerequisites + +- Node.js (v18.20.3) +- pnpm (v9.4.0) + +### Installation + +1. Clone the repository: + +```bash +git clone https://github.com/stackblitz/bolt.git +cd bolt +``` + +2. Install dependencies: + +```bash +pnpm i +``` + +### Development + +To start developing the Bolt UI: + +1. Navigate to the bolt package: + +```bash +cd packages/bolt +``` + +2. Start the development server: + +```bash +pnpm run dev +``` diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..c904562 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,26 @@ +import blitzPlugin from '@blitz/eslint-plugin'; +import { getNamingConventionRule } from '@blitz/eslint-plugin/dist/configs/typescript.js'; + +export default [ + { + ignores: ['**/dist', '**/node_modules'], + }, + ...blitzPlugin.configs.recommended(), + { + rules: { + '@blitz/catch-error-name': 'off', + }, + }, + { + files: ['**/*.tsx'], + rules: { + ...getNamingConventionRule({}, true), + }, + }, + { + files: ['**/*.d.ts'], + rules: { + '@typescript-eslint/no-empty-object-type': 'off', + }, + }, +]; diff --git a/package.json b/package.json new file mode 100644 index 0000000..a34537f --- /dev/null +++ b/package.json @@ -0,0 +1,40 @@ +{ + "private": true, + "license": "MIT", + "packageManager": "pnpm@9.4.0", + "scripts": { + "playground:dev": "pnpm run --filter=playground dev", + "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint .", + "test": "pnpm run -r test", + "typecheck": "pnpm run -r typecheck", + "prepare": "husky" + }, + "lint-staged": {}, + "commitlint": { + "extends": [ + "@commitlint/config-conventional" + ] + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged", + "commit-msg": "commitlint --edit $1" + } + }, + "engines": { + "node": ">=18.18.0", + "pnpm": "9.4.0" + }, + "devDependencies": { + "@blitz/eslint-plugin": "0.1.0", + "@commitlint/config-conventional": "^19.2.2", + "commitlint": "^19.3.0", + "husky": "^9.0.11", + "is-ci": "^3.0.1", + "prettier": "^3.3.2", + "vitest": "^2.0.1" + }, + "resolutions": { + "@typescript-eslint/utils": "^8.0.0-alpha.30" + } +} diff --git a/packages/bolt/.gitignore b/packages/bolt/.gitignore new file mode 100644 index 0000000..970cd5b --- /dev/null +++ b/packages/bolt/.gitignore @@ -0,0 +1,8 @@ +node_modules + +/.cache +/build +.env +*.vars +.wrangler +_worker.bundle diff --git a/packages/bolt/README.md b/packages/bolt/README.md new file mode 100644 index 0000000..4251b95 --- /dev/null +++ b/packages/bolt/README.md @@ -0,0 +1,78 @@ +# Bolt + +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/). + +## Prerequisites + +Before you begin, ensure you have the following installed: + +- Node.js (v18.20.3) +- pnpm (v9.4.0) + +## Setup + +1. Clone the repository (if you haven't already): + +```bash +git clone https://github.com/stackblitz/bolt.git +cd bolt +``` + +2. Install dependencies: + +```bash +pnpm install +``` + +3. Create a `.env.local` file in the root of the bolt package directory and add your Anthropic API key: + +``` +ANTHROPIC_API_KEY=XXX +``` + +Optionally, you an set the debug level: + +``` +VITE_LOG_LEVEL=debug +``` + +**Important**: Never commit your `.env.local` file to version control. It's already included in .gitignore. + +## Available Scripts + +- `pnpm run dev`: Starts the development server. +- `pnpm run build`: Builds the project. +- `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. +- `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`. +- `pnpm test:` Runs the test suite using Vitest. +- `pnpm run typecheck`: Runs TypeScript type checking. +- `pnpm run typegen`: Generates TypeScript types using Wrangler. +- `pnpm run deploy`: Builds the project and deploys it to Cloudflare Pages. + +## Development + +To start the development server: + +```bash +pnpm run dev +``` + +This will start the Remix Vite development server. + +## Testing + +Run the test suite with: + +```bash +pnpm test +``` + +## Deployment + +To deploy the application to Cloudflare Pages: + +```bash +pnpm run deploy +``` + +Make sure you have the necessary permissions and Wrangler is correctly configured for your Cloudflare account. diff --git a/packages/bolt/app/components/Header.tsx b/packages/bolt/app/components/Header.tsx new file mode 100644 index 0000000..a51eed6 --- /dev/null +++ b/packages/bolt/app/components/Header.tsx @@ -0,0 +1,12 @@ +import { IconButton } from './ui/IconButton'; + +export function Header() { + return ( +
+
+
Bolt
+
+ +
+ ); +} diff --git a/packages/bolt/app/components/chat/Artifact.tsx b/packages/bolt/app/components/chat/Artifact.tsx new file mode 100644 index 0000000..525b074 --- /dev/null +++ b/packages/bolt/app/components/chat/Artifact.tsx @@ -0,0 +1,29 @@ +import { useStore } from '@nanostores/react'; +import { workspaceStore } from '~/lib/stores/workspace'; + +interface ArtifactProps { + messageId: string; + onClick?: () => void; +} + +export function Artifact({ messageId, onClick }: ArtifactProps) { + const artifacts = useStore(workspaceStore.artifacts); + + const artifact = artifacts[messageId]; + + return ( + + ); +} diff --git a/packages/bolt/app/components/chat/AssistantMessage.tsx b/packages/bolt/app/components/chat/AssistantMessage.tsx new file mode 100644 index 0000000..c30d811 --- /dev/null +++ b/packages/bolt/app/components/chat/AssistantMessage.tsx @@ -0,0 +1,14 @@ +import { memo } from 'react'; +import { Markdown } from './Markdown'; + +interface AssistantMessageProps { + content: string; +} + +export const AssistantMessage = memo(({ content }: AssistantMessageProps) => { + return ( +
+ {content} +
+ ); +}); diff --git a/packages/bolt/app/components/chat/BaseChat.tsx b/packages/bolt/app/components/chat/BaseChat.tsx new file mode 100644 index 0000000..f72e2a0 --- /dev/null +++ b/packages/bolt/app/components/chat/BaseChat.tsx @@ -0,0 +1,140 @@ +import type { LegacyRef } from 'react'; +import React from 'react'; +import { ClientOnly } from 'remix-utils/client-only'; +import { IconButton } from '~/components/ui/IconButton'; +import { classNames } from '~/utils/classNames'; +import { SendButton } from './SendButton.client'; + +interface BaseChatProps { + textareaRef?: LegacyRef | undefined; + messagesSlot?: React.ReactNode; + workspaceSlot?: React.ReactNode; + chatStarted?: boolean; + enhancingPrompt?: boolean; + promptEnhanced?: boolean; + input?: string; + sendMessage?: () => void; + handleInputChange?: (event: React.ChangeEvent) => void; + enhancePrompt?: () => void; +} + +const EXAMPLES = [{ text: 'Example' }, { text: 'Example' }, { text: 'Example' }, { text: 'Example' }]; + +const TEXTAREA_MIN_HEIGHT = 72; + +export const BaseChat = React.forwardRef( + ( + { + textareaRef, + chatStarted = false, + enhancingPrompt = false, + promptEnhanced = false, + messagesSlot, + workspaceSlot, + input = '', + sendMessage, + handleInputChange, + enhancePrompt, + }, + ref, + ) => { + const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; + + return ( +
+
+
+ {!chatStarted && ( +
+

Where ideas begin.

+

Bring ideas to life in seconds or get help on existing projects.

+
+ {EXAMPLES.map((suggestion, index) => ( + + ))} +
+
+ )} + {messagesSlot} +
+
+
+