codacus commited on
Commit
02d0be5
Β·
unverified Β·
2 Parent(s): 97b886a 81d2c01

Merge branch 'main' into streaming-fixed

Browse files
This view is limited to 50 files because it contains too many changes. Β  See raw diff
Files changed (50) hide show
  1. .github/ISSUE_TEMPLATE/config.yml +8 -0
  2. .github/workflows/commit.yaml +1 -0
  3. .github/workflows/pr-release-validation.yaml +31 -0
  4. .github/workflows/update-stable.yml +210 -0
  5. .gitignore +3 -0
  6. .husky/pre-commit +27 -10
  7. README.md +116 -125
  8. app/commit.json +1 -1
  9. app/components/chat/BaseChat.tsx +2 -2
  10. app/components/chat/Chat.client.tsx +2 -0
  11. app/components/chat/GitCloneButton.tsx +9 -14
  12. app/components/chat/ImportFolderButton.tsx +23 -0
  13. app/components/chat/ModelSelector.tsx +16 -51
  14. app/components/chat/chatExportAndImport/ImportButtons.tsx +0 -1
  15. app/components/settings/SettingsWindow.tsx +13 -3
  16. app/components/settings/chat-history/ChatHistoryTab.tsx +11 -3
  17. app/components/settings/connections/ConnectionsTab.tsx +6 -0
  18. app/components/settings/debug/DebugTab.tsx +472 -47
  19. app/components/settings/event-logs/EventLogsTab.tsx +219 -0
  20. app/components/settings/features/FeaturesTab.tsx +5 -1
  21. app/components/settings/providers/ProvidersTab.tsx +22 -5
  22. app/components/sidebar/Menu.client.tsx +22 -4
  23. app/lib/.server/llm/prompts.ts +5 -5
  24. app/lib/.server/llm/stream-text.ts +90 -2
  25. app/lib/hooks/useMessageParser.ts +2 -2
  26. app/lib/hooks/useSettings.tsx +34 -6
  27. app/lib/persistence/useChatHistory.ts +4 -0
  28. app/lib/stores/logs.ts +149 -0
  29. app/lib/stores/settings.ts +3 -1
  30. app/lib/stores/theme.ts +2 -3
  31. app/lib/stores/workbench.ts +1 -0
  32. app/root.tsx +18 -1
  33. app/routes/api.chat.ts +18 -4
  34. app/types/global.d.ts +8 -0
  35. app/utils/constants.ts +6 -10
  36. changelog.md +808 -0
  37. docs/mkdocs.yml +8 -0
  38. package.json +2 -1
  39. pre-start.cjs +10 -0
  40. public/icons/Anthropic.svg +4 -0
  41. public/icons/Cohere.svg +4 -0
  42. public/icons/Deepseek.svg +5 -0
  43. public/icons/Google.svg +4 -0
  44. public/icons/Groq.svg +4 -0
  45. public/icons/HuggingFace.svg +4 -0
  46. public/icons/LMStudio.svg +5 -0
  47. public/icons/Mistral.svg +4 -0
  48. public/icons/Ollama.svg +4 -0
  49. public/icons/OpenAI.svg +4 -0
  50. public/icons/OpenAILike.svg +4 -0
.github/ISSUE_TEMPLATE/config.yml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Bolt.new related issues
4
+ url: https://github.com/stackblitz/bolt.new/issues/new/choose
5
+ about: Report issues related to Bolt.new (not Bolt.diy)
6
+ - name: Chat
7
+ url: https://thinktank.ottomator.ai
8
+ about: Ask questions and discuss with other Bolt.diy users.
.github/workflows/commit.yaml CHANGED
@@ -10,6 +10,7 @@ permissions:
10
 
11
  jobs:
12
  update-commit:
 
13
  runs-on: ubuntu-latest
14
 
15
  steps:
 
10
 
11
  jobs:
12
  update-commit:
13
+ if: contains(github.event.head_commit.message, '#release') != true
14
  runs-on: ubuntu-latest
15
 
16
  steps:
.github/workflows/pr-release-validation.yaml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: PR Validation
2
+
3
+ on:
4
+ pull_request:
5
+ types: [opened, synchronize, reopened, labeled, unlabeled]
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ validate:
11
+ runs-on: ubuntu-latest
12
+
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+
16
+ - name: Validate PR Labels
17
+ run: |
18
+ if [[ "${{ contains(github.event.pull_request.labels.*.name, 'stable-release') }}" == "true" ]]; then
19
+ echo "βœ“ PR has stable-release label"
20
+
21
+ # Check version bump labels
22
+ if [[ "${{ contains(github.event.pull_request.labels.*.name, 'major') }}" == "true" ]]; then
23
+ echo "βœ“ Major version bump requested"
24
+ elif [[ "${{ contains(github.event.pull_request.labels.*.name, 'minor') }}" == "true" ]]; then
25
+ echo "βœ“ Minor version bump requested"
26
+ else
27
+ echo "βœ“ Patch version bump will be applied"
28
+ fi
29
+ else
30
+ echo "This PR doesn't have the stable-release label. No release will be created."
31
+ fi
.github/workflows/update-stable.yml ADDED
@@ -0,0 +1,210 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Update Stable Branch
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ update-commit:
13
+ if: contains(github.event.head_commit.message, '#release')
14
+ runs-on: ubuntu-latest
15
+
16
+ steps:
17
+ - name: Checkout the code
18
+ uses: actions/checkout@v3
19
+
20
+ - name: Get the latest commit hash
21
+ run: echo "COMMIT_HASH=$(git rev-parse HEAD)" >> $GITHUB_ENV
22
+
23
+ - name: Update commit file
24
+ run: |
25
+ echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json
26
+
27
+ - name: Commit and push the update
28
+ run: |
29
+ git config --global user.name "github-actions[bot]"
30
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
31
+ git add app/commit.json
32
+ git commit -m "chore: update commit hash to $COMMIT_HASH"
33
+ git push
34
+ prepare-release:
35
+ needs: update-commit
36
+ if: contains(github.event.head_commit.message, '#release')
37
+ runs-on: ubuntu-latest
38
+
39
+ steps:
40
+ - uses: actions/checkout@v4
41
+ with:
42
+ fetch-depth: 0
43
+
44
+ - name: Configure Git
45
+ run: |
46
+ git config --global user.name 'github-actions[bot]'
47
+ git config --global user.email 'github-actions[bot]@users.noreply.github.com'
48
+
49
+ - name: Setup Node.js
50
+ uses: actions/setup-node@v4
51
+ with:
52
+ node-version: '20'
53
+
54
+ - name: Install pnpm
55
+ uses: pnpm/action-setup@v2
56
+ with:
57
+ version: latest
58
+ run_install: false
59
+
60
+ - name: Get pnpm store directory
61
+ id: pnpm-cache
62
+ shell: bash
63
+ run: |
64
+ echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
65
+
66
+ - name: Setup pnpm cache
67
+ uses: actions/cache@v4
68
+ with:
69
+ path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
70
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
71
+ restore-keys: |
72
+ ${{ runner.os }}-pnpm-store-
73
+
74
+ - name: Get Current Version
75
+ id: current_version
76
+ run: |
77
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
78
+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
79
+
80
+ - name: Install semver
81
+ run: pnpm add -g semver
82
+
83
+ - name: Determine Version Bump
84
+ id: version_bump
85
+ run: |
86
+ COMMIT_MSG="${{ github.event.head_commit.message }}"
87
+ if [[ $COMMIT_MSG =~ "#release:major" ]]; then
88
+ echo "bump=major" >> $GITHUB_OUTPUT
89
+ elif [[ $COMMIT_MSG =~ "#release:minor" ]]; then
90
+ echo "bump=minor" >> $GITHUB_OUTPUT
91
+ else
92
+ echo "bump=patch" >> $GITHUB_OUTPUT
93
+ fi
94
+
95
+ - name: Bump Version
96
+ id: bump_version
97
+ run: |
98
+ NEW_VERSION=$(semver -i ${{ steps.version_bump.outputs.bump }} ${{ steps.current_version.outputs.version }})
99
+ echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
100
+
101
+ - name: Update Package.json
102
+ run: |
103
+ NEW_VERSION=${{ steps.bump_version.outputs.new_version }}
104
+ pnpm version $NEW_VERSION --no-git-tag-version --allow-same-version
105
+
106
+ - name: Generate Changelog
107
+ id: changelog
108
+ run: |
109
+ # Get the latest tag
110
+ LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
111
+
112
+ # Start changelog file
113
+ echo "# Release v${{ steps.bump_version.outputs.new_version }}" > changelog.md
114
+ echo "" >> changelog.md
115
+
116
+ if [ -z "$LATEST_TAG" ]; then
117
+ echo "### πŸŽ‰ First Release" >> changelog.md
118
+ echo "" >> changelog.md
119
+ COMPARE_BASE="$(git rev-list --max-parents=0 HEAD)"
120
+ else
121
+ echo "### πŸ”„ Changes since $LATEST_TAG" >> changelog.md
122
+ echo "" >> changelog.md
123
+ COMPARE_BASE="$LATEST_TAG"
124
+ fi
125
+
126
+ # Function to extract conventional commit type
127
+ get_commit_type() {
128
+ if [[ $1 =~ ^feat:|^feature: ]]; then echo "✨ Features";
129
+ elif [[ $1 =~ ^fix: ]]; then echo "πŸ› Bug Fixes";
130
+ elif [[ $1 =~ ^docs: ]]; then echo "πŸ“š Documentation";
131
+ elif [[ $1 =~ ^style: ]]; then echo "πŸ’Ž Styles";
132
+ elif [[ $1 =~ ^refactor: ]]; then echo "♻️ Code Refactoring";
133
+ elif [[ $1 =~ ^perf: ]]; then echo "⚑️ Performance Improvements";
134
+ elif [[ $1 =~ ^test: ]]; then echo "βœ… Tests";
135
+ elif [[ $1 =~ ^build: ]]; then echo "πŸ› οΈ Build System";
136
+ elif [[ $1 =~ ^ci: ]]; then echo "βš™οΈ CI";
137
+ elif [[ $1 =~ ^chore: ]]; then echo "πŸ”§ Chores";
138
+ else echo "πŸ” Other Changes";
139
+ fi
140
+ }
141
+
142
+ # Generate categorized changelog
143
+ declare -A CATEGORIES
144
+ declare -A COMMITS_BY_CATEGORY
145
+
146
+ # Get commits since last tag or all commits if no tag exists
147
+ while IFS= read -r commit_line; do
148
+ HASH=$(echo "$commit_line" | cut -d'|' -f1)
149
+ MSG=$(echo "$commit_line" | cut -d'|' -f2)
150
+ PR_NUM=$(echo "$commit_line" | cut -d'|' -f3)
151
+
152
+ CATEGORY=$(get_commit_type "$MSG")
153
+ CATEGORIES["$CATEGORY"]=1
154
+
155
+ # Format commit message with PR link if available
156
+ if [ -n "$PR_NUM" ]; then
157
+ COMMITS_BY_CATEGORY["$CATEGORY"]+="- ${MSG#*: } ([#$PR_NUM](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/pull/$PR_NUM))"$'\n'
158
+ else
159
+ COMMITS_BY_CATEGORY["$CATEGORY"]+="- ${MSG#*: }"$'\n'
160
+ fi
161
+ done < <(git log "${COMPARE_BASE}..HEAD" --pretty=format:"%H|%s|%(trailers:key=PR-Number,valueonly)" --reverse)
162
+
163
+ # Write categorized commits to changelog
164
+ for category in "✨ Features" "πŸ› Bug Fixes" "πŸ“š Documentation" "πŸ’Ž Styles" "♻️ Code Refactoring" "⚑️ Performance Improvements" "βœ… Tests" "πŸ› οΈ Build System" "βš™οΈ CI" "πŸ”§ Chores" "πŸ” Other Changes"; do
165
+ if [ -n "${COMMITS_BY_CATEGORY[$category]}" ]; then
166
+ echo "#### $category" >> changelog.md
167
+ echo "" >> changelog.md
168
+ echo "${COMMITS_BY_CATEGORY[$category]}" >> changelog.md
169
+ echo "" >> changelog.md
170
+ fi
171
+ done
172
+
173
+ # Add compare link if not first release
174
+ if [ -n "$LATEST_TAG" ]; then
175
+ echo "**Full Changelog**: [\`$LATEST_TAG..v${{ steps.bump_version.outputs.new_version }}\`](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/compare/$LATEST_TAG...v${{ steps.bump_version.outputs.new_version }})" >> changelog.md
176
+ fi
177
+
178
+ # Save changelog content for the release
179
+ CHANGELOG_CONTENT=$(cat changelog.md)
180
+ echo "content<<EOF" >> $GITHUB_OUTPUT
181
+ echo "$CHANGELOG_CONTENT" >> $GITHUB_OUTPUT
182
+ echo "EOF" >> $GITHUB_OUTPUT
183
+
184
+ - name: Commit and Tag Release
185
+ run: |
186
+ git pull
187
+ git add package.json pnpm-lock.yaml changelog.md
188
+ git commit -m "chore: release version ${{ steps.bump_version.outputs.new_version }}"
189
+ git tag "v${{ steps.bump_version.outputs.new_version }}"
190
+ git push
191
+ git push --tags
192
+
193
+ - name: Update Stable Branch
194
+ run: |
195
+ if ! git checkout stable 2>/dev/null; then
196
+ echo "Creating new stable branch..."
197
+ git checkout -b stable
198
+ fi
199
+ git merge main --no-ff -m "chore: release version ${{ steps.bump_version.outputs.new_version }}"
200
+ git push --set-upstream origin stable --force
201
+
202
+ - name: Create GitHub Release
203
+ env:
204
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
205
+ run: |
206
+ VERSION="v${{ steps.bump_version.outputs.new_version }}"
207
+ gh release create "$VERSION" \
208
+ --title "Release $VERSION" \
209
+ --notes "${{ steps.changelog.outputs.content }}" \
210
+ --target stable
.gitignore CHANGED
@@ -37,3 +37,6 @@ modelfiles
37
 
38
  # docs ignore
39
  site
 
 
 
 
37
 
38
  # docs ignore
39
  site
40
+
41
+ # commit file ignore
42
+ app/commit.json
.husky/pre-commit CHANGED
@@ -2,25 +2,42 @@
2
 
3
  echo "πŸ” Running pre-commit hook to check the code looks good... πŸ”"
4
 
 
5
  export NVM_DIR="$HOME/.nvm"
6
- [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # Load nvm if you're using i
7
 
8
- echo "Running typecheck..."
9
- which pnpm
 
 
 
 
10
 
 
 
11
  if ! pnpm typecheck; then
12
- echo "❌ Type checking failed! Please review TypeScript types."
13
- echo "Once you're done, don't forget to add your changes to the commit! πŸš€"
14
- echo "Typecheck exit code: $?"
15
- exit 1
16
  fi
17
 
 
18
  echo "Running lint..."
19
  if ! pnpm lint; then
20
- echo "❌ Linting failed! 'pnpm lint:fix' will help you fix the easy ones."
21
  echo "Once you're done, don't forget to add your beautification to the commit! 🀩"
22
- echo "lint exit code: $?"
23
  exit 1
24
  fi
25
 
26
- echo "πŸ‘ All good! Committing changes..."
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
  echo "πŸ” Running pre-commit hook to check the code looks good... πŸ”"
4
 
5
+ # Load NVM if available (useful for managing Node.js versions)
6
  export NVM_DIR="$HOME/.nvm"
7
+ [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
8
 
9
+ # Ensure `pnpm` is available
10
+ echo "Checking if pnpm is available..."
11
+ if ! command -v pnpm >/dev/null 2>&1; then
12
+ echo "❌ pnpm not found! Please ensure pnpm is installed and available in PATH."
13
+ exit 1
14
+ fi
15
 
16
+ # Run typecheck
17
+ echo "Running typecheck..."
18
  if ! pnpm typecheck; then
19
+ echo "❌ Type checking failed! Please review TypeScript types."
20
+ echo "Once you're done, don't forget to add your changes to the commit! πŸš€"
21
+ exit 1
 
22
  fi
23
 
24
+ # Run lint
25
  echo "Running lint..."
26
  if ! pnpm lint; then
27
+ echo "❌ Linting failed! Run 'pnpm lint:fix' to fix the easy issues."
28
  echo "Once you're done, don't forget to add your beautification to the commit! 🀩"
 
29
  exit 1
30
  fi
31
 
32
+ # Update commit.json with the latest commit hash
33
+ echo "Updating commit.json with the latest commit hash..."
34
+ COMMIT_HASH=$(git rev-parse HEAD)
35
+ if [ $? -ne 0 ]; then
36
+ echo "❌ Failed to get commit hash. Ensure you are in a git repository."
37
+ exit 1
38
+ fi
39
+
40
+ echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json
41
+ git add app/commit.json
42
+
43
+ echo "πŸ‘ All checks passed! Committing changes..."
README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Bolt.new: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.new)
2
 
3
  # Bolt.diy (Previously oTToDev)
4
 
@@ -56,176 +56,167 @@ https://thinktank.ottomator.ai
56
  - ⬜ Perplexity Integration
57
  - ⬜ Vertex AI Integration
58
 
59
- ## Bolt.new: AI-Powered Full-Stack Web Development in the Browser
60
 
61
- Bolt.new (and by extension Bolt.diy) is an AI-powered web development agent that allows you to prompt, run, edit, and deploy full-stack applications directly from your browserβ€”no local setup required. If you're here to build your own AI-powered web dev agent using the Bolt open source codebase, [click here to get started!](./CONTRIBUTING.md)
 
 
 
 
 
 
62
 
63
- ## What Makes Bolt.new Different
64
 
65
- Claude, v0, etc are incredible- but you can't install packages, run backends, or edit code. That’s where Bolt.new stands out:
66
 
67
- - **Full-Stack in the Browser**: Bolt.new integrates cutting-edge AI models with an in-browser development environment powered by **StackBlitz’s WebContainers**. This allows you to:
68
- - Install and run npm tools and libraries (like Vite, Next.js, and more)
69
- - Run Node.js servers
70
- - Interact with third-party APIs
71
- - Deploy to production from chat
72
- - Share your work via a URL
73
 
74
- - **AI with Environment Control**: Unlike traditional dev environments where the AI can only assist in code generation, Bolt.new gives AI models **complete control** over the entire environment including the filesystem, node server, package manager, terminal, and browser console. This empowers AI agents to handle the whole app lifecycleβ€”from creation to deployment.
 
75
 
76
- Whether you’re an experienced developer, a PM, or a designer, Bolt.new allows you to easily build production-grade full-stack applications.
 
 
 
 
 
 
77
 
78
- For developers interested in building their own AI-powered development tools with WebContainers, check out the open-source Bolt codebase in this repo!
79
 
80
- ## Setup
81
 
82
- Many of you are new users to installing software from Github. If you have any installation troubles reach out and submit an "issue" using the links above, or feel free to enhance this documentation by forking, editing the instructions, and doing a pull request.
 
 
83
 
84
- 1. Install Git from https://git-scm.com/downloads
85
 
86
- 2. Install Node.js from https://nodejs.org/en/download/
87
 
88
- Pay attention to the installer notes after completion.
 
89
 
90
- On all operating systems, the path to Node.js should automatically be added to your system path. But you can check your path if you want to be sure. On Windows, you can search for "edit the system environment variables" in your system, select "Environment Variables..." once you are in the system properties, and then check for a path to Node in your "Path" system variable. On a Mac or Linux machine, it will tell you to check if /usr/local/bin is in your $PATH. To determine if usr/local/bin is included in $PATHΒ open your Terminal and run:
 
 
 
 
91
 
92
- ```
93
- echo $PATHΒ .
94
- ```
95
 
96
- If you see usr/local/bin in the output then you're good to go.
97
 
98
- 3. Clone the repository (if you haven't already) by opening a Terminal window (or CMD with admin permissions) and then typing in this:
 
 
99
 
100
- ```
101
- git clone https://github.com/stackblitz-labs/bolt.diy.git
102
- ```
 
103
 
104
- 3. Rename .env.example to .env.local and add your LLM API keys. You will find this file on a Mac at "[your name]/bold.new-any-llm/.env.example". For Windows and Linux the path will be similar.
105
 
106
- ![image](https://github.com/user-attachments/assets/7e6a532c-2268-401f-8310-e8d20c731328)
107
 
108
- If you can't see the file indicated above, its likely you can't view hidden files. On Mac, open a Terminal window and enter this command below. On Windows, you will see the hidden files option in File Explorer Settings. A quick Google search will help you if you are stuck here.
109
 
110
- ```
111
- defaults write com.apple.finder AppleShowAllFiles YES
112
- ```
113
 
114
- **NOTE**: you only have to set the ones you want to use and Ollama doesn't need an API key because it runs locally on your computer:
 
 
 
 
 
 
 
115
 
116
- Get your GROQ API Key here: https://console.groq.com/keys
 
 
 
 
117
 
118
- Get your Open AI API Key by following these instructions: https://help.openai.com/en/articles/4936850-where-do-i-find-my-openai-api-key
119
 
120
- Get your Anthropic API Key in your account settings: https://console.anthropic.com/settings/keys
 
121
 
122
- ```
123
- GROQ_API_KEY=XXX
124
- OPENAI_API_KEY=XXX
125
- ANTHROPIC_API_KEY=XXX
126
- ```
127
 
128
- Optionally, you can set the debug level:
129
 
130
- ```
131
- VITE_LOG_LEVEL=debug
132
- ```
 
 
133
 
134
- And if using Ollama set the DEFAULT_NUM_CTX, the example below uses 8K context and ollama running on localhost port 11434:
 
 
 
 
135
 
136
- ```
137
- OLLAMA_API_BASE_URL=http://localhost:11434
138
- DEFAULT_NUM_CTX=8192
139
- ```
 
 
140
 
141
- **Important**: Never commit your `.env.local` file to version control. It's already included in .gitignore.
142
 
143
- ## Run with Docker
144
 
145
- Prerequisites:
146
 
147
- Git and Node.js as mentioned above, as well as Docker: https://www.docker.com/
148
 
149
- ### 1a. Using Helper Scripts
 
150
 
151
- NPM scripts are provided for convenient building:
 
152
 
153
- ```bash
154
- # Development build
155
- npm run dockerbuild
156
 
157
- # Production build
158
- npm run dockerbuild:prod
159
- ```
160
 
161
- ### 1b. Direct Docker Build Commands (alternative to using NPM scripts)
 
 
162
 
163
- You can use Docker's target feature to specify the build environment instead of using NPM scripts if you wish:
 
164
 
165
- ```bash
166
- # Development build
167
- docker build . --target bolt-ai-development
168
 
169
- # Production build
170
- docker build . --target bolt-ai-production
171
- ```
172
 
173
- ### 2. Docker Compose with Profiles to Run the Container
174
 
175
- Use Docker Compose profiles to manage different environments:
176
 
177
- ```bash
178
- # Development environment
179
- docker-compose --profile development up
180
 
181
- # Production environment
182
- docker-compose --profile production up
183
- ```
184
-
185
- When you run the Docker Compose command with the development profile, any changes you
186
- make on your machine to the code will automatically be reflected in the site running
187
- on the container (i.e. hot reloading still applies!).
188
-
189
- ## Run Without Docker
190
-
191
- 1. Install dependencies using Terminal (or CMD in Windows with admin permissions):
192
-
193
- ```
194
- pnpm install
195
- ```
196
-
197
- If you get an error saying "command not found: pnpm" or similar, then that means pnpm isn't installed. You can install it via this:
198
-
199
- ```
200
- sudo npm install -g pnpm
201
- ```
202
-
203
- 2. Start the application with the command:
204
-
205
- ```bash
206
- pnpm run dev
207
- ```
208
- ## Available Scripts
209
-
210
- - `pnpm run dev`: Starts the development server.
211
- - `pnpm run build`: Builds the project.
212
- - `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.
213
- - `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`.
214
- - `pnpm test`: Runs the test suite using Vitest.
215
- - `pnpm run typecheck`: Runs TypeScript type checking.
216
- - `pnpm run typegen`: Generates TypeScript types using Wrangler.
217
- - `pnpm run deploy`: Builds the project and deploys it to Cloudflare Pages.
218
- - `pnpm run lint:fix`: Runs the linter and automatically fixes issues according to your ESLint configuration.
219
-
220
- ## Development
221
-
222
- To start the development server:
223
-
224
- ```bash
225
- pnpm run dev
226
- ```
227
-
228
- This will start the Remix Vite development server. You will need Google Chrome Canary to run this locally if you use Chrome! It's an easy install and a good browser for web development anyway.
229
 
230
  ## How do I contribute to Bolt.diy?
231
 
 
1
+ [![Bolt.diy: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.diy)
2
 
3
  # Bolt.diy (Previously oTToDev)
4
 
 
56
  - ⬜ Perplexity Integration
57
  - ⬜ Vertex AI Integration
58
 
59
+ ## Bolt.diy Features
60
 
61
+ - **AI-powered full-stack web development** directly in your browser.
62
+ - **Support for multiple LLMs** with an extensible architecture to integrate additional models.
63
+ - **Attach images to prompts** for better contextual understanding.
64
+ - **Integrated terminal** to view output of LLM-run commands.
65
+ - **Revert code to earlier versions** for easier debugging and quicker changes.
66
+ - **Download projects as ZIP** for easy portability.
67
+ - **Integration-ready Docker support** for a hassle-free setup.
68
 
69
+ ## Setup Bolt.diy
70
 
71
+ If you're new to installing software from GitHub, don't worry! If you encounter any issues, feel free to submit an "issue" using the provided links or improve this documentation by forking the repository, editing the instructions, and submitting a pull request. The following instruction will help you get the stable branch up and running on your local machine in no time.
72
 
73
+ ### Prerequisites
 
 
 
 
 
74
 
75
+ 1. **Install Git**: [Download Git](https://git-scm.com/downloads)
76
+ 2. **Install Node.js**: [Download Node.js](https://nodejs.org/en/download/)
77
 
78
+ - After installation, the Node.js path is usually added to your system automatically. To verify:
79
+ - **Windows**: Search for "Edit the system environment variables," click "Environment Variables," and check if `Node.js` is in the `Path` variable.
80
+ - **Mac/Linux**: Open a terminal and run:
81
+ ```bash
82
+ echo $PATH
83
+ ```
84
+ Look for `/usr/local/bin` in the output.
85
 
86
+ ### Clone the Repository
87
 
88
+ Clone the repository using Git:
89
 
90
+ ```bash
91
+ git clone -b stable https://github.com/stackblitz-labs/bolt.diy
92
+ ```
93
 
94
+ ### (Optional) Configure Environment Variables
95
 
96
+ Most environment variables can be configured directly through the settings menu of the application. However, if you need to manually configure them:
97
 
98
+ 1. Rename `.env.example` to `.env.local`.
99
+ 2. Add your LLM API keys. For example:
100
 
101
+ ```env
102
+ GROQ_API_KEY=YOUR_GROQ_API_KEY
103
+ OPENAI_API_KEY=YOUR_OPENAI_API_KEY
104
+ ANTHROPIC_API_KEY=YOUR_ANTHROPIC_API_KEY
105
+ ```
106
 
107
+ **Note**: Ollama does not require an API key as it runs locally.
 
 
108
 
109
+ 3. Optionally, set additional configurations:
110
 
111
+ ```env
112
+ # Debugging
113
+ VITE_LOG_LEVEL=debug
114
 
115
+ # Ollama settings (example: 8K context, localhost port 11434)
116
+ OLLAMA_API_BASE_URL=http://localhost:11434
117
+ DEFAULT_NUM_CTX=8192
118
+ ```
119
 
120
+ **Important**: Do not commit your `.env.local` file to version control. This file is already included in `.gitignore`.
121
 
122
+ ---
123
 
124
+ ## Run the Application
125
 
126
+ ### Option 1: Without Docker
 
 
127
 
128
+ 1. **Install Dependencies**:
129
+ ```bash
130
+ pnpm install
131
+ ```
132
+ If `pnpm` is not installed, install it using:
133
+ ```bash
134
+ sudo npm install -g pnpm
135
+ ```
136
 
137
+ 2. **Start the Application**:
138
+ ```bash
139
+ pnpm run dev
140
+ ```
141
+ This will start the Remix Vite development server. You will need Google Chrome Canary to run this locally if you use Chrome! It's an easy install and a good browser for web development anyway.
142
 
143
+ ### Option 2: With Docker
144
 
145
+ #### Prerequisites
146
+ - Ensure Git, Node.js, and Docker are installed: [Download Docker](https://www.docker.com/)
147
 
148
+ #### Steps
 
 
 
 
149
 
150
+ 1. **Build the Docker Image**:
151
 
152
+ Use the provided NPM scripts:
153
+ ```bash
154
+ npm run dockerbuild # Development build
155
+ npm run dockerbuild:prod # Production build
156
+ ```
157
 
158
+ Alternatively, use Docker commands directly:
159
+ ```bash
160
+ docker build . --target bolt-ai-development # Development build
161
+ docker build . --target bolt-ai-production # Production build
162
+ ```
163
 
164
+ 2. **Run the Container**:
165
+ Use Docker Compose profiles to manage environments:
166
+ ```bash
167
+ docker-compose --profile development up # Development
168
+ docker-compose --profile production up # Production
169
+ ```
170
 
171
+ - With the development profile, changes to your code will automatically reflect in the running container (hot reloading).
172
 
173
+ ---
174
 
175
+ ### Update Your Local Version to the Latest
176
 
177
+ To keep your local version of Bolt.diy up to date with the latest changes, follow these steps for your operating system:
178
 
179
+ #### 1. **Navigate to your project folder**
180
+ Navigate to the directory where you cloned the repository and open a terminal:
181
 
182
+ #### 2. **Fetch the Latest Changes**
183
+ Use Git to pull the latest changes from the main repository:
184
 
185
+ ```bash
186
+ git pull origin main
187
+ ```
188
 
189
+ #### 3. **Update Dependencies**
190
+ After pulling the latest changes, update the project dependencies by running the following command:
 
191
 
192
+ ```bash
193
+ pnpm install
194
+ ```
195
 
196
+ #### 4. **Run the Application**
197
+ Once the updates are complete, you can start the application again with:
198
 
199
+ ```bash
200
+ pnpm run dev
201
+ ```
202
 
203
+ This ensures that you're running the latest version of Bolt.diy and can take advantage of all the newest features and bug fixes.
 
 
204
 
205
+ ---
206
 
207
+ ## Available Scripts
208
 
209
+ Here are the available commands for managing the application:
 
 
210
 
211
+ - `pnpm run dev`: Start the development server.
212
+ - `pnpm run build`: Build the project.
213
+ - `pnpm run start`: Run the built application locally (uses Wrangler Pages).
214
+ - `pnpm run preview`: Build and start the application locally for production testing.
215
+ - `pnpm test`: Run the test suite using Vitest.
216
+ - `pnpm run typecheck`: Perform TypeScript type checking.
217
+ - `pnpm run typegen`: Generate TypeScript types using Wrangler.
218
+ - `pnpm run deploy`: Build and deploy the project to Cloudflare Pages.
219
+ - `pnpm lint:fix`: Run the linter and automatically fix issues.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
220
 
221
  ## How do I contribute to Bolt.diy?
222
 
app/commit.json CHANGED
@@ -1 +1 @@
1
- { "commit": "8f3b4cd08249d26b14397e66241b9d099d3eb205" }
 
1
+ { "commit": "6a5ed21c0fed92a8c842b683bf9a430901e6bb05" }
app/components/chat/BaseChat.tsx CHANGED
@@ -283,7 +283,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
283
  <div ref={scrollRef} className="flex flex-col lg:flex-row overflow-y-auto w-full h-full">
284
  <div className={classNames(styles.Chat, 'flex flex-col flex-grow lg:min-w-[var(--chat-min-width)] h-full')}>
285
  {!chatStarted && (
286
- <div id="intro" className="mt-[26vh] max-w-chat mx-auto text-center px-4 lg:px-0">
287
  <h1 className="text-3xl lg:text-6xl font-bold text-bolt-elements-textPrimary mb-4 animate-fade-in">
288
  Where ideas begin
289
  </h1>
@@ -384,7 +384,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
384
  <textarea
385
  ref={textareaRef}
386
  className={classNames(
387
- 'w-full pl-4 pt-4 pr-16 focus:outline-none resize-none text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary bg-transparent text-sm',
388
  'transition-all duration-200',
389
  'hover:border-bolt-elements-focus',
390
  )}
 
283
  <div ref={scrollRef} className="flex flex-col lg:flex-row overflow-y-auto w-full h-full">
284
  <div className={classNames(styles.Chat, 'flex flex-col flex-grow lg:min-w-[var(--chat-min-width)] h-full')}>
285
  {!chatStarted && (
286
+ <div id="intro" className="mt-[16vh] max-w-chat mx-auto text-center px-4 lg:px-0">
287
  <h1 className="text-3xl lg:text-6xl font-bold text-bolt-elements-textPrimary mb-4 animate-fade-in">
288
  Where ideas begin
289
  </h1>
 
384
  <textarea
385
  ref={textareaRef}
386
  className={classNames(
387
+ 'w-full pl-4 pt-4 pr-16 outline-none resize-none text-bolt-elements-textPrimary placeholder-bolt-elements-textTertiary bg-transparent text-sm',
388
  'transition-all duration-200',
389
  'hover:border-bolt-elements-focus',
390
  )}
app/components/chat/Chat.client.tsx CHANGED
@@ -92,6 +92,7 @@ export const ChatImpl = memo(
92
  const [chatStarted, setChatStarted] = useState(initialMessages.length > 0);
93
  const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); // Move here
94
  const [imageDataList, setImageDataList] = useState<string[]>([]); // Move here
 
95
  const { activeProviders } = useSettings();
96
 
97
  const [model, setModel] = useState(() => {
@@ -113,6 +114,7 @@ export const ChatImpl = memo(
113
  api: '/api/chat',
114
  body: {
115
  apiKeys,
 
116
  },
117
  onError: (error) => {
118
  logger.error('Request failed\n\n', error);
 
92
  const [chatStarted, setChatStarted] = useState(initialMessages.length > 0);
93
  const [uploadedFiles, setUploadedFiles] = useState<File[]>([]); // Move here
94
  const [imageDataList, setImageDataList] = useState<string[]>([]); // Move here
95
+ const files = useStore(workbenchStore.files);
96
  const { activeProviders } = useSettings();
97
 
98
  const [model, setModel] = useState(() => {
 
114
  api: '/api/chat',
115
  body: {
116
  apiKeys,
117
+ files,
118
  },
119
  onError: (error) => {
120
  logger.error('Request failed\n\n', error);
app/components/chat/GitCloneButton.tsx CHANGED
@@ -1,7 +1,6 @@
1
  import ignore from 'ignore';
2
  import { useGit } from '~/lib/hooks/useGit';
3
  import type { Message } from 'ai';
4
- import WithTooltip from '~/components/ui/Tooltip';
5
  import { detectProjectCommands, createCommandsMessage } from '~/utils/projectCommands';
6
  import { generateId } from '~/utils/fileUtils';
7
 
@@ -73,7 +72,7 @@ export default function GitCloneButton({ importChat }: GitCloneButtonProps) {
73
  const filesMessage: Message = {
74
  role: 'assistant',
75
  content: `Cloning the repo ${repoUrl} into ${workdir}
76
- <boltArtifact id="imported-files" title="Git Cloned Files" type="bundled">
77
  ${fileContents
78
  .map(
79
  (file) =>
@@ -99,17 +98,13 @@ ${file.content}
99
  };
100
 
101
  return (
102
- <WithTooltip tooltip="Clone A Git Repo">
103
- <button
104
- onClick={(e) => {
105
- onClick(e);
106
- }}
107
- title="Clone A Git Repo"
108
- className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
109
- >
110
- <span className="i-ph:git-branch" />
111
- Clone A Git Repo
112
- </button>
113
- </WithTooltip>
114
  );
115
  }
 
1
  import ignore from 'ignore';
2
  import { useGit } from '~/lib/hooks/useGit';
3
  import type { Message } from 'ai';
 
4
  import { detectProjectCommands, createCommandsMessage } from '~/utils/projectCommands';
5
  import { generateId } from '~/utils/fileUtils';
6
 
 
72
  const filesMessage: Message = {
73
  role: 'assistant',
74
  content: `Cloning the repo ${repoUrl} into ${workdir}
75
+ <boltArtifact id="imported-files" title="Git Cloned Files" type="bundled">
76
  ${fileContents
77
  .map(
78
  (file) =>
 
98
  };
99
 
100
  return (
101
+ <button
102
+ onClick={onClick}
103
+ title="Clone a Git Repo"
104
+ className="px-4 py-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary hover:bg-bolt-elements-background-depth-3 transition-all flex items-center gap-2"
105
+ >
106
+ <span className="i-ph:git-branch" />
107
+ Clone a Git Repo
108
+ </button>
 
 
 
 
109
  );
110
  }
app/components/chat/ImportFolderButton.tsx CHANGED
@@ -3,6 +3,7 @@ import type { Message } from 'ai';
3
  import { toast } from 'react-toastify';
4
  import { MAX_FILES, isBinaryFile, shouldIncludeFile } from '~/utils/fileUtils';
5
  import { createChatFromFolder } from '~/utils/folderImport';
 
6
 
7
  interface ImportFolderButtonProps {
8
  className?: string;
@@ -16,9 +17,15 @@ export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ classNam
16
  const allFiles = Array.from(e.target.files || []);
17
 
18
  if (allFiles.length > MAX_FILES) {
 
 
 
 
 
19
  toast.error(
20
  `This folder contains ${allFiles.length.toLocaleString()} files. This product is not yet optimized for very large projects. Please select a folder with fewer than ${MAX_FILES.toLocaleString()} files.`,
21
  );
 
22
  return;
23
  }
24
 
@@ -31,7 +38,10 @@ export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ classNam
31
  const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath));
32
 
33
  if (filteredFiles.length === 0) {
 
 
34
  toast.error('No files found in the selected folder');
 
35
  return;
36
  }
37
 
@@ -48,11 +58,18 @@ export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ classNam
48
  .map((f) => f.file.webkitRelativePath.split('/').slice(1).join('/'));
49
 
50
  if (textFiles.length === 0) {
 
 
51
  toast.error('No text files found in the selected folder');
 
52
  return;
53
  }
54
 
55
  if (binaryFilePaths.length > 0) {
 
 
 
 
56
  toast.info(`Skipping ${binaryFilePaths.length} binary files`);
57
  }
58
 
@@ -62,8 +79,14 @@ export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ classNam
62
  await importChat(folderName, [...messages]);
63
  }
64
 
 
 
 
 
 
65
  toast.success('Folder imported successfully');
66
  } catch (error) {
 
67
  console.error('Failed to import folder:', error);
68
  toast.error('Failed to import folder');
69
  } finally {
 
3
  import { toast } from 'react-toastify';
4
  import { MAX_FILES, isBinaryFile, shouldIncludeFile } from '~/utils/fileUtils';
5
  import { createChatFromFolder } from '~/utils/folderImport';
6
+ import { logStore } from '~/lib/stores/logs'; // Assuming logStore is imported from this location
7
 
8
  interface ImportFolderButtonProps {
9
  className?: string;
 
17
  const allFiles = Array.from(e.target.files || []);
18
 
19
  if (allFiles.length > MAX_FILES) {
20
+ const error = new Error(`Too many files: ${allFiles.length}`);
21
+ logStore.logError('File import failed - too many files', error, {
22
+ fileCount: allFiles.length,
23
+ maxFiles: MAX_FILES,
24
+ });
25
  toast.error(
26
  `This folder contains ${allFiles.length.toLocaleString()} files. This product is not yet optimized for very large projects. Please select a folder with fewer than ${MAX_FILES.toLocaleString()} files.`,
27
  );
28
+
29
  return;
30
  }
31
 
 
38
  const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath));
39
 
40
  if (filteredFiles.length === 0) {
41
+ const error = new Error('No valid files found');
42
+ logStore.logError('File import failed - no valid files', error, { folderName });
43
  toast.error('No files found in the selected folder');
44
+
45
  return;
46
  }
47
 
 
58
  .map((f) => f.file.webkitRelativePath.split('/').slice(1).join('/'));
59
 
60
  if (textFiles.length === 0) {
61
+ const error = new Error('No text files found');
62
+ logStore.logError('File import failed - no text files', error, { folderName });
63
  toast.error('No text files found in the selected folder');
64
+
65
  return;
66
  }
67
 
68
  if (binaryFilePaths.length > 0) {
69
+ logStore.logWarning(`Skipping binary files during import`, {
70
+ folderName,
71
+ binaryCount: binaryFilePaths.length,
72
+ });
73
  toast.info(`Skipping ${binaryFilePaths.length} binary files`);
74
  }
75
 
 
79
  await importChat(folderName, [...messages]);
80
  }
81
 
82
+ logStore.logSystem('Folder imported successfully', {
83
+ folderName,
84
+ textFileCount: textFiles.length,
85
+ binaryFileCount: binaryFilePaths.length,
86
+ });
87
  toast.success('Folder imported successfully');
88
  } catch (error) {
89
+ logStore.logError('Failed to import folder', error, { folderName });
90
  console.error('Failed to import folder:', error);
91
  toast.error('Failed to import folder');
92
  } finally {
app/components/chat/ModelSelector.tsx CHANGED
@@ -1,7 +1,6 @@
1
  import type { ProviderInfo } from '~/types/model';
2
  import type { ModelInfo } from '~/utils/types';
3
- import { useEffect, useState } from 'react';
4
- import Cookies from 'js-cookie';
5
 
6
  interface ModelSelectorProps {
7
  model?: string;
@@ -22,62 +21,28 @@ export const ModelSelector = ({
22
  providerList,
23
  }: ModelSelectorProps) => {
24
  // Load enabled providers from cookies
25
- const [enabledProviders, setEnabledProviders] = useState(() => {
26
- const savedProviders = Cookies.get('providers');
27
-
28
- if (savedProviders) {
29
- try {
30
- const parsedProviders = JSON.parse(savedProviders);
31
- return providerList.filter((p) => parsedProviders[p.name]);
32
- } catch (error) {
33
- console.error('Failed to parse providers from cookies:', error);
34
- return providerList;
35
- }
36
- }
37
-
38
- return providerList;
39
- });
40
 
41
  // Update enabled providers when cookies change
42
  useEffect(() => {
43
- // Function to update providers from cookies
44
- const updateProvidersFromCookies = () => {
45
- const savedProviders = Cookies.get('providers');
46
-
47
- if (savedProviders) {
48
- try {
49
- const parsedProviders = JSON.parse(savedProviders);
50
- const newEnabledProviders = providerList.filter((p) => parsedProviders[p.name]);
51
- setEnabledProviders(newEnabledProviders);
52
 
53
- // If current provider is disabled, switch to first enabled provider
54
- if (provider && !parsedProviders[provider.name] && newEnabledProviders.length > 0) {
55
- const firstEnabledProvider = newEnabledProviders[0];
56
- setProvider?.(firstEnabledProvider);
57
 
58
- // Also update the model to the first available one for the new provider
59
- const firstModel = modelList.find((m) => m.provider === firstEnabledProvider.name);
60
 
61
- if (firstModel) {
62
- setModel?.(firstModel.name);
63
- }
64
- }
65
- } catch (error) {
66
- console.error('Failed to parse providers from cookies:', error);
67
- }
68
  }
69
- };
70
-
71
- // Initial update
72
- updateProvidersFromCookies();
73
-
74
- // Set up an interval to check for cookie changes
75
- const interval = setInterval(updateProvidersFromCookies, 1000);
76
-
77
- return () => clearInterval(interval);
78
  }, [providerList, provider, setProvider, modelList, setModel]);
79
 
80
- if (enabledProviders.length === 0) {
81
  return (
82
  <div className="mb-2 p-4 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary">
83
  <p className="text-center">
@@ -93,7 +58,7 @@ export const ModelSelector = ({
93
  <select
94
  value={provider?.name ?? ''}
95
  onChange={(e) => {
96
- const newProvider = enabledProviders.find((p: ProviderInfo) => p.name === e.target.value);
97
 
98
  if (newProvider && setProvider) {
99
  setProvider(newProvider);
@@ -107,7 +72,7 @@ export const ModelSelector = ({
107
  }}
108
  className="flex-1 p-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus transition-all"
109
  >
110
- {enabledProviders.map((provider: ProviderInfo) => (
111
  <option key={provider.name} value={provider.name}>
112
  {provider.name}
113
  </option>
 
1
  import type { ProviderInfo } from '~/types/model';
2
  import type { ModelInfo } from '~/utils/types';
3
+ import { useEffect } from 'react';
 
4
 
5
  interface ModelSelectorProps {
6
  model?: string;
 
21
  providerList,
22
  }: ModelSelectorProps) => {
23
  // Load enabled providers from cookies
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  // Update enabled providers when cookies change
26
  useEffect(() => {
27
+ // If current provider is disabled, switch to first enabled provider
28
+ if (providerList.length == 0) {
29
+ return;
30
+ }
 
 
 
 
 
31
 
32
+ if (provider && !providerList.map((p) => p.name).includes(provider.name)) {
33
+ const firstEnabledProvider = providerList[0];
34
+ setProvider?.(firstEnabledProvider);
 
35
 
36
+ // Also update the model to the first available one for the new provider
37
+ const firstModel = modelList.find((m) => m.provider === firstEnabledProvider.name);
38
 
39
+ if (firstModel) {
40
+ setModel?.(firstModel.name);
 
 
 
 
 
41
  }
42
+ }
 
 
 
 
 
 
 
 
43
  }, [providerList, provider, setProvider, modelList, setModel]);
44
 
45
+ if (providerList.length === 0) {
46
  return (
47
  <div className="mb-2 p-4 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary">
48
  <p className="text-center">
 
58
  <select
59
  value={provider?.name ?? ''}
60
  onChange={(e) => {
61
+ const newProvider = providerList.find((p: ProviderInfo) => p.name === e.target.value);
62
 
63
  if (newProvider && setProvider) {
64
  setProvider(newProvider);
 
72
  }}
73
  className="flex-1 p-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus transition-all"
74
  >
75
+ {providerList.map((provider: ProviderInfo) => (
76
  <option key={provider.name} value={provider.name}>
77
  {provider.name}
78
  </option>
app/components/chat/chatExportAndImport/ImportButtons.tsx CHANGED
@@ -1,6 +1,5 @@
1
  import type { Message } from 'ai';
2
  import { toast } from 'react-toastify';
3
- import React from 'react';
4
  import { ImportFolderButton } from '~/components/chat/ImportFolderButton';
5
 
6
  export function ImportButtons(importChat: ((description: string, messages: Message[]) => Promise<void>) | undefined) {
 
1
  import type { Message } from 'ai';
2
  import { toast } from 'react-toastify';
 
3
  import { ImportFolderButton } from '~/components/chat/ImportFolderButton';
4
 
5
  export function ImportButtons(importChat: ((description: string, messages: Message[]) => Promise<void>) | undefined) {
app/components/settings/SettingsWindow.tsx CHANGED
@@ -10,6 +10,7 @@ import ProvidersTab from './providers/ProvidersTab';
10
  import { useSettings } from '~/lib/hooks/useSettings';
11
  import FeaturesTab from './features/FeaturesTab';
12
  import DebugTab from './debug/DebugTab';
 
13
  import ConnectionsTab from './connections/ConnectionsTab';
14
 
15
  interface SettingsProps {
@@ -17,11 +18,10 @@ interface SettingsProps {
17
  onClose: () => void;
18
  }
19
 
20
- type TabType = 'chat-history' | 'providers' | 'features' | 'debug' | 'connection';
21
 
22
- // Providers that support base URL configuration
23
  export const SettingsWindow = ({ open, onClose }: SettingsProps) => {
24
- const { debug } = useSettings();
25
  const [activeTab, setActiveTab] = useState<TabType>('chat-history');
26
 
27
  const tabs: { id: TabType; label: string; icon: string; component?: ReactElement }[] = [
@@ -39,6 +39,16 @@ export const SettingsWindow = ({ open, onClose }: SettingsProps) => {
39
  },
40
  ]
41
  : []),
 
 
 
 
 
 
 
 
 
 
42
  ];
43
 
44
  return (
 
10
  import { useSettings } from '~/lib/hooks/useSettings';
11
  import FeaturesTab from './features/FeaturesTab';
12
  import DebugTab from './debug/DebugTab';
13
+ import EventLogsTab from './event-logs/EventLogsTab';
14
  import ConnectionsTab from './connections/ConnectionsTab';
15
 
16
  interface SettingsProps {
 
18
  onClose: () => void;
19
  }
20
 
21
+ type TabType = 'chat-history' | 'providers' | 'features' | 'debug' | 'event-logs' | 'connection';
22
 
 
23
  export const SettingsWindow = ({ open, onClose }: SettingsProps) => {
24
+ const { debug, eventLogs } = useSettings();
25
  const [activeTab, setActiveTab] = useState<TabType>('chat-history');
26
 
27
  const tabs: { id: TabType; label: string; icon: string; component?: ReactElement }[] = [
 
39
  },
40
  ]
41
  : []),
42
+ ...(eventLogs
43
+ ? [
44
+ {
45
+ id: 'event-logs' as TabType,
46
+ label: 'Event Logs',
47
+ icon: 'i-ph:list-bullets',
48
+ component: <EventLogsTab />,
49
+ },
50
+ ]
51
+ : []),
52
  ];
53
 
54
  return (
app/components/settings/chat-history/ChatHistoryTab.tsx CHANGED
@@ -4,6 +4,7 @@ import { toast } from 'react-toastify';
4
  import { db, deleteById, getAll } from '~/lib/persistence';
5
  import { classNames } from '~/utils/classNames';
6
  import styles from '~/components/settings/Settings.module.scss';
 
7
 
8
  export default function ChatHistoryTab() {
9
  const navigate = useNavigate();
@@ -22,7 +23,10 @@ export default function ChatHistoryTab() {
22
 
23
  const handleDeleteAllChats = async () => {
24
  if (!db) {
 
 
25
  toast.error('Database is not available');
 
26
  return;
27
  }
28
 
@@ -30,13 +34,12 @@ export default function ChatHistoryTab() {
30
  setIsDeleting(true);
31
 
32
  const allChats = await getAll(db);
33
-
34
- // Delete all chats one by one
35
  await Promise.all(allChats.map((chat) => deleteById(db!, chat.id)));
36
-
37
  toast.success('All chats deleted successfully');
38
  navigate('/', { replace: true });
39
  } catch (error) {
 
40
  toast.error('Failed to delete chats');
41
  console.error(error);
42
  } finally {
@@ -46,7 +49,10 @@ export default function ChatHistoryTab() {
46
 
47
  const handleExportAllChats = async () => {
48
  if (!db) {
 
 
49
  toast.error('Database is not available');
 
50
  return;
51
  }
52
 
@@ -58,8 +64,10 @@ export default function ChatHistoryTab() {
58
  };
59
 
60
  downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`);
 
61
  toast.success('Chats exported successfully');
62
  } catch (error) {
 
63
  toast.error('Failed to export chats');
64
  console.error(error);
65
  }
 
4
  import { db, deleteById, getAll } from '~/lib/persistence';
5
  import { classNames } from '~/utils/classNames';
6
  import styles from '~/components/settings/Settings.module.scss';
7
+ import { logStore } from '~/lib/stores/logs'; // Import logStore for event logging
8
 
9
  export default function ChatHistoryTab() {
10
  const navigate = useNavigate();
 
23
 
24
  const handleDeleteAllChats = async () => {
25
  if (!db) {
26
+ const error = new Error('Database is not available');
27
+ logStore.logError('Failed to delete chats - DB unavailable', error);
28
  toast.error('Database is not available');
29
+
30
  return;
31
  }
32
 
 
34
  setIsDeleting(true);
35
 
36
  const allChats = await getAll(db);
 
 
37
  await Promise.all(allChats.map((chat) => deleteById(db!, chat.id)));
38
+ logStore.logSystem('All chats deleted successfully', { count: allChats.length });
39
  toast.success('All chats deleted successfully');
40
  navigate('/', { replace: true });
41
  } catch (error) {
42
+ logStore.logError('Failed to delete chats', error);
43
  toast.error('Failed to delete chats');
44
  console.error(error);
45
  } finally {
 
49
 
50
  const handleExportAllChats = async () => {
51
  if (!db) {
52
+ const error = new Error('Database is not available');
53
+ logStore.logError('Failed to export chats - DB unavailable', error);
54
  toast.error('Database is not available');
55
+
56
  return;
57
  }
58
 
 
64
  };
65
 
66
  downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`);
67
+ logStore.logSystem('Chats exported successfully', { count: allChats.length });
68
  toast.success('Chats exported successfully');
69
  } catch (error) {
70
+ logStore.logError('Failed to export chats', error);
71
  toast.error('Failed to export chats');
72
  console.error(error);
73
  }
app/components/settings/connections/ConnectionsTab.tsx CHANGED
@@ -1,6 +1,7 @@
1
  import React, { useState } from 'react';
2
  import { toast } from 'react-toastify';
3
  import Cookies from 'js-cookie';
 
4
 
5
  export default function ConnectionsTab() {
6
  const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || '');
@@ -9,7 +10,12 @@ export default function ConnectionsTab() {
9
  const handleSaveConnection = () => {
10
  Cookies.set('githubUsername', githubUsername);
11
  Cookies.set('githubToken', githubToken);
 
 
 
 
12
  toast.success('GitHub credentials saved successfully!');
 
13
  };
14
 
15
  return (
 
1
  import React, { useState } from 'react';
2
  import { toast } from 'react-toastify';
3
  import Cookies from 'js-cookie';
4
+ import { logStore } from '~/lib/stores/logs';
5
 
6
  export default function ConnectionsTab() {
7
  const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || '');
 
10
  const handleSaveConnection = () => {
11
  Cookies.set('githubUsername', githubUsername);
12
  Cookies.set('githubToken', githubToken);
13
+ logStore.logSystem('GitHub connection settings updated', {
14
+ username: githubUsername,
15
+ hasToken: !!githubToken,
16
+ });
17
  toast.success('GitHub credentials saved successfully!');
18
+ Cookies.set('git:github.com', JSON.stringify({ username: githubToken, password: 'x-oauth-basic' }));
19
  };
20
 
21
  return (
app/components/settings/debug/DebugTab.tsx CHANGED
@@ -2,68 +2,493 @@ import React, { useCallback, useEffect, useState } from 'react';
2
  import { useSettings } from '~/lib/hooks/useSettings';
3
  import commit from '~/commit.json';
4
 
5
- const versionHash = commit.commit; // Get the version hash from commit.json
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  export default function DebugTab() {
8
  const { providers } = useSettings();
9
- const [activeProviders, setActiveProviders] = useState<string[]>([]);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  useEffect(() => {
11
- setActiveProviders(
12
- Object.entries(providers)
13
- .filter(([_key, provider]) => provider.settings.enabled)
14
- .map(([_key, provider]) => provider.name),
15
- );
16
  }, [providers]);
17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  const handleCopyToClipboard = useCallback(() => {
19
  const debugInfo = {
20
- OS: navigator.platform,
21
- Browser: navigator.userAgent,
22
- ActiveFeatures: activeProviders,
23
- BaseURLs: {
24
- Ollama: process.env.REACT_APP_OLLAMA_URL,
25
- OpenAI: process.env.REACT_APP_OPENAI_URL,
26
- LMStudio: process.env.REACT_APP_LM_STUDIO_URL,
27
- },
 
 
 
28
  Version: versionHash,
 
29
  };
30
  navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => {
31
  alert('Debug information copied to clipboard!');
32
  });
33
- }, [providers]);
34
 
35
  return (
36
- <div className="p-4">
37
- <h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Debug Tab</h3>
38
- <button
39
- onClick={handleCopyToClipboard}
40
- className="bg-blue-500 text-white rounded-lg px-4 py-2 hover:bg-blue-600 mb-4 transition-colors duration-200"
41
- >
42
- Copy to Clipboard
43
- </button>
44
-
45
- <h4 className="text-md font-medium text-bolt-elements-textPrimary">System Information</h4>
46
- <p className="text-bolt-elements-textSecondary">OS: {navigator.platform}</p>
47
- <p className="text-bolt-elements-textSecondary">Browser: {navigator.userAgent}</p>
48
-
49
- <h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Active Features</h4>
50
- <ul>
51
- {activeProviders.map((name) => (
52
- <li key={name} className="text-bolt-elements-textSecondary">
53
- {name}
54
- </li>
55
- ))}
56
- </ul>
57
-
58
- <h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Base URLs</h4>
59
- <ul>
60
- <li className="text-bolt-elements-textSecondary">Ollama: {process.env.REACT_APP_OLLAMA_URL}</li>
61
- <li className="text-bolt-elements-textSecondary">OpenAI: {process.env.REACT_APP_OPENAI_URL}</li>
62
- <li className="text-bolt-elements-textSecondary">LM Studio: {process.env.REACT_APP_LM_STUDIO_URL}</li>
63
- </ul>
64
-
65
- <h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Version Information</h4>
66
- <p className="text-bolt-elements-textSecondary">Version Hash: {versionHash}</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  </div>
68
  );
69
  }
 
2
  import { useSettings } from '~/lib/hooks/useSettings';
3
  import commit from '~/commit.json';
4
 
5
+ interface ProviderStatus {
6
+ name: string;
7
+ enabled: boolean;
8
+ isLocal: boolean;
9
+ isRunning: boolean | null;
10
+ error?: string;
11
+ lastChecked: Date;
12
+ responseTime?: number;
13
+ url: string | null;
14
+ }
15
+
16
+ interface SystemInfo {
17
+ os: string;
18
+ browser: string;
19
+ screen: string;
20
+ language: string;
21
+ timezone: string;
22
+ memory: string;
23
+ cores: number;
24
+ }
25
+
26
+ interface IProviderConfig {
27
+ name: string;
28
+ settings: {
29
+ enabled: boolean;
30
+ };
31
+ }
32
+
33
+ const LOCAL_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike'];
34
+ const versionHash = commit.commit;
35
+ const GITHUB_URLS = {
36
+ original: 'https://api.github.com/repos/stackblitz-labs/bolt.diy/commits/main',
37
+ fork: 'https://api.github.com/repos/Stijnus/bolt.new-any-llm/commits/main',
38
+ };
39
+
40
+ function getSystemInfo(): SystemInfo {
41
+ const formatBytes = (bytes: number): string => {
42
+ if (bytes === 0) {
43
+ return '0 Bytes';
44
+ }
45
+
46
+ const k = 1024;
47
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
48
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
49
+
50
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
51
+ };
52
+
53
+ return {
54
+ os: navigator.platform,
55
+ browser: navigator.userAgent.split(' ').slice(-1)[0],
56
+ screen: `${window.screen.width}x${window.screen.height}`,
57
+ language: navigator.language,
58
+ timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
59
+ memory: formatBytes(performance?.memory?.jsHeapSizeLimit || 0),
60
+ cores: navigator.hardwareConcurrency || 0,
61
+ };
62
+ }
63
+
64
+ const checkProviderStatus = async (url: string | null, providerName: string): Promise<ProviderStatus> => {
65
+ if (!url) {
66
+ console.log(`[Debug] No URL provided for ${providerName}`);
67
+ return {
68
+ name: providerName,
69
+ enabled: false,
70
+ isLocal: true,
71
+ isRunning: false,
72
+ error: 'No URL configured',
73
+ lastChecked: new Date(),
74
+ url: null,
75
+ };
76
+ }
77
+
78
+ console.log(`[Debug] Checking status for ${providerName} at ${url}`);
79
+
80
+ const startTime = performance.now();
81
+
82
+ try {
83
+ if (providerName.toLowerCase() === 'ollama') {
84
+ // Special check for Ollama root endpoint
85
+ try {
86
+ console.log(`[Debug] Checking Ollama root endpoint: ${url}`);
87
+
88
+ const controller = new AbortController();
89
+ const timeoutId = setTimeout(() => controller.abort(), 5000); // 5 second timeout
90
+
91
+ const response = await fetch(url, {
92
+ signal: controller.signal,
93
+ headers: {
94
+ Accept: 'text/plain,application/json',
95
+ },
96
+ });
97
+ clearTimeout(timeoutId);
98
+
99
+ const text = await response.text();
100
+ console.log(`[Debug] Ollama root response:`, text);
101
+
102
+ if (text.includes('Ollama is running')) {
103
+ console.log(`[Debug] Ollama running confirmed via root endpoint`);
104
+ return {
105
+ name: providerName,
106
+ enabled: false,
107
+ isLocal: true,
108
+ isRunning: true,
109
+ lastChecked: new Date(),
110
+ responseTime: performance.now() - startTime,
111
+ url,
112
+ };
113
+ }
114
+ } catch (error) {
115
+ console.log(`[Debug] Ollama root check failed:`, error);
116
+
117
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
118
+
119
+ if (errorMessage.includes('aborted')) {
120
+ return {
121
+ name: providerName,
122
+ enabled: false,
123
+ isLocal: true,
124
+ isRunning: false,
125
+ error: 'Connection timeout',
126
+ lastChecked: new Date(),
127
+ responseTime: performance.now() - startTime,
128
+ url,
129
+ };
130
+ }
131
+ }
132
+ }
133
+
134
+ // Try different endpoints based on provider
135
+ const checkUrls = [`${url}/api/health`, `${url}/v1/models`];
136
+ console.log(`[Debug] Checking additional endpoints:`, checkUrls);
137
+
138
+ const results = await Promise.all(
139
+ checkUrls.map(async (checkUrl) => {
140
+ try {
141
+ console.log(`[Debug] Trying endpoint: ${checkUrl}`);
142
+
143
+ const controller = new AbortController();
144
+ const timeoutId = setTimeout(() => controller.abort(), 5000);
145
+
146
+ const response = await fetch(checkUrl, {
147
+ signal: controller.signal,
148
+ headers: {
149
+ Accept: 'application/json',
150
+ },
151
+ });
152
+ clearTimeout(timeoutId);
153
+
154
+ const ok = response.ok;
155
+ console.log(`[Debug] Endpoint ${checkUrl} response:`, ok);
156
+
157
+ if (ok) {
158
+ try {
159
+ const data = await response.json();
160
+ console.log(`[Debug] Endpoint ${checkUrl} data:`, data);
161
+ } catch {
162
+ console.log(`[Debug] Could not parse JSON from ${checkUrl}`);
163
+ }
164
+ }
165
+
166
+ return ok;
167
+ } catch (error) {
168
+ console.log(`[Debug] Endpoint ${checkUrl} failed:`, error);
169
+ return false;
170
+ }
171
+ }),
172
+ );
173
+
174
+ const isRunning = results.some((result) => result);
175
+ console.log(`[Debug] Final status for ${providerName}:`, isRunning);
176
+
177
+ return {
178
+ name: providerName,
179
+ enabled: false,
180
+ isLocal: true,
181
+ isRunning,
182
+ lastChecked: new Date(),
183
+ responseTime: performance.now() - startTime,
184
+ url,
185
+ };
186
+ } catch (error) {
187
+ console.log(`[Debug] Provider check failed for ${providerName}:`, error);
188
+ return {
189
+ name: providerName,
190
+ enabled: false,
191
+ isLocal: true,
192
+ isRunning: false,
193
+ error: error instanceof Error ? error.message : 'Unknown error',
194
+ lastChecked: new Date(),
195
+ responseTime: performance.now() - startTime,
196
+ url,
197
+ };
198
+ }
199
+ };
200
 
201
  export default function DebugTab() {
202
  const { providers } = useSettings();
203
+ const [activeProviders, setActiveProviders] = useState<ProviderStatus[]>([]);
204
+ const [updateMessage, setUpdateMessage] = useState<string>('');
205
+ const [systemInfo] = useState<SystemInfo>(getSystemInfo());
206
+ const [isCheckingUpdate, setIsCheckingUpdate] = useState(false);
207
+
208
+ const updateProviderStatuses = async () => {
209
+ if (!providers) {
210
+ return;
211
+ }
212
+
213
+ try {
214
+ const entries = Object.entries(providers) as [string, IProviderConfig][];
215
+ const statuses = entries
216
+ .filter(([, provider]) => LOCAL_PROVIDERS.includes(provider.name))
217
+ .map(async ([, provider]) => {
218
+ const envVarName =
219
+ provider.name.toLowerCase() === 'ollama'
220
+ ? 'OLLAMA_API_BASE_URL'
221
+ : provider.name.toLowerCase() === 'lmstudio'
222
+ ? 'LMSTUDIO_API_BASE_URL'
223
+ : `REACT_APP_${provider.name.toUpperCase()}_URL`;
224
+
225
+ // Access environment variables through import.meta.env
226
+ const url = import.meta.env[envVarName] || null;
227
+ console.log(`[Debug] Using URL for ${provider.name}:`, url, `(from ${envVarName})`);
228
+
229
+ const status = await checkProviderStatus(url, provider.name);
230
+
231
+ return {
232
+ ...status,
233
+ enabled: provider.settings.enabled ?? false,
234
+ };
235
+ });
236
+
237
+ Promise.all(statuses).then(setActiveProviders);
238
+ } catch (error) {
239
+ console.error('[Debug] Failed to update provider statuses:', error);
240
+ }
241
+ };
242
+
243
  useEffect(() => {
244
+ updateProviderStatuses();
245
+
246
+ const interval = setInterval(updateProviderStatuses, 30000);
247
+
248
+ return () => clearInterval(interval);
249
  }, [providers]);
250
 
251
+ const handleCheckForUpdate = useCallback(async () => {
252
+ if (isCheckingUpdate) {
253
+ return;
254
+ }
255
+
256
+ try {
257
+ setIsCheckingUpdate(true);
258
+ setUpdateMessage('Checking for updates...');
259
+
260
+ const [originalResponse, forkResponse] = await Promise.all([
261
+ fetch(GITHUB_URLS.original),
262
+ fetch(GITHUB_URLS.fork),
263
+ ]);
264
+
265
+ if (!originalResponse.ok || !forkResponse.ok) {
266
+ throw new Error('Failed to fetch repository information');
267
+ }
268
+
269
+ const [originalData, forkData] = await Promise.all([
270
+ originalResponse.json() as Promise<{ sha: string }>,
271
+ forkResponse.json() as Promise<{ sha: string }>,
272
+ ]);
273
+
274
+ const originalCommitHash = originalData.sha;
275
+ const forkCommitHash = forkData.sha;
276
+ const isForked = versionHash === forkCommitHash && forkCommitHash !== originalCommitHash;
277
+
278
+ if (originalCommitHash !== versionHash) {
279
+ setUpdateMessage(
280
+ `Update available from original repository!\n` +
281
+ `Current: ${versionHash.slice(0, 7)}${isForked ? ' (forked)' : ''}\n` +
282
+ `Latest: ${originalCommitHash.slice(0, 7)}`,
283
+ );
284
+ } else {
285
+ setUpdateMessage('You are on the latest version from the original repository');
286
+ }
287
+ } catch (error) {
288
+ setUpdateMessage('Failed to check for updates');
289
+ console.error('[Debug] Failed to check for updates:', error);
290
+ } finally {
291
+ setIsCheckingUpdate(false);
292
+ }
293
+ }, [isCheckingUpdate]);
294
+
295
  const handleCopyToClipboard = useCallback(() => {
296
  const debugInfo = {
297
+ System: systemInfo,
298
+ Providers: activeProviders.map((provider) => ({
299
+ name: provider.name,
300
+ enabled: provider.enabled,
301
+ isLocal: provider.isLocal,
302
+ running: provider.isRunning,
303
+ error: provider.error,
304
+ lastChecked: provider.lastChecked,
305
+ responseTime: provider.responseTime,
306
+ url: provider.url,
307
+ })),
308
  Version: versionHash,
309
+ Timestamp: new Date().toISOString(),
310
  };
311
  navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => {
312
  alert('Debug information copied to clipboard!');
313
  });
314
+ }, [activeProviders, systemInfo]);
315
 
316
  return (
317
+ <div className="p-4 space-y-6">
318
+ <div className="flex items-center justify-between">
319
+ <h3 className="text-lg font-medium text-bolt-elements-textPrimary">Debug Information</h3>
320
+ <div className="flex gap-2">
321
+ <button
322
+ onClick={handleCopyToClipboard}
323
+ className="bg-bolt-elements-button-primary-background rounded-lg px-4 py-2 transition-colors duration-200 hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-button-primary-text"
324
+ >
325
+ Copy Debug Info
326
+ </button>
327
+ <button
328
+ onClick={handleCheckForUpdate}
329
+ disabled={isCheckingUpdate}
330
+ className={`bg-bolt-elements-button-primary-background rounded-lg px-4 py-2 transition-colors duration-200
331
+ ${!isCheckingUpdate ? 'hover:bg-bolt-elements-button-primary-backgroundHover' : 'opacity-75 cursor-not-allowed'}
332
+ text-bolt-elements-button-primary-text`}
333
+ >
334
+ {isCheckingUpdate ? 'Checking...' : 'Check for Updates'}
335
+ </button>
336
+ </div>
337
+ </div>
338
+
339
+ {updateMessage && (
340
+ <div
341
+ className={`bg-bolt-elements-surface rounded-lg p-3 ${
342
+ updateMessage.includes('Update available') ? 'border-l-4 border-yellow-400' : ''
343
+ }`}
344
+ >
345
+ <p className="text-bolt-elements-textSecondary whitespace-pre-line">{updateMessage}</p>
346
+ {updateMessage.includes('Update available') && (
347
+ <div className="mt-3 text-sm">
348
+ <p className="font-medium text-bolt-elements-textPrimary">To update:</p>
349
+ <ol className="list-decimal ml-4 mt-1 text-bolt-elements-textSecondary">
350
+ <li>
351
+ Pull the latest changes:{' '}
352
+ <code className="bg-bolt-elements-surface-hover px-1 rounded">git pull upstream main</code>
353
+ </li>
354
+ <li>
355
+ Install any new dependencies:{' '}
356
+ <code className="bg-bolt-elements-surface-hover px-1 rounded">pnpm install</code>
357
+ </li>
358
+ <li>Restart the application</li>
359
+ </ol>
360
+ </div>
361
+ )}
362
+ </div>
363
+ )}
364
+
365
+ <section className="space-y-4">
366
+ <div>
367
+ <h4 className="text-md font-medium text-bolt-elements-textPrimary mb-2">System Information</h4>
368
+ <div className="bg-bolt-elements-surface rounded-lg p-4">
369
+ <div className="grid grid-cols-2 md:grid-cols-3 gap-4">
370
+ <div>
371
+ <p className="text-xs text-bolt-elements-textSecondary">Operating System</p>
372
+ <p className="text-sm font-medium text-bolt-elements-textPrimary">{systemInfo.os}</p>
373
+ </div>
374
+ <div>
375
+ <p className="text-xs text-bolt-elements-textSecondary">Browser</p>
376
+ <p className="text-sm font-medium text-bolt-elements-textPrimary">{systemInfo.browser}</p>
377
+ </div>
378
+ <div>
379
+ <p className="text-xs text-bolt-elements-textSecondary">Screen Resolution</p>
380
+ <p className="text-sm font-medium text-bolt-elements-textPrimary">{systemInfo.screen}</p>
381
+ </div>
382
+ <div>
383
+ <p className="text-xs text-bolt-elements-textSecondary">Language</p>
384
+ <p className="text-sm font-medium text-bolt-elements-textPrimary">{systemInfo.language}</p>
385
+ </div>
386
+ <div>
387
+ <p className="text-xs text-bolt-elements-textSecondary">Timezone</p>
388
+ <p className="text-sm font-medium text-bolt-elements-textPrimary">{systemInfo.timezone}</p>
389
+ </div>
390
+ <div>
391
+ <p className="text-xs text-bolt-elements-textSecondary">CPU Cores</p>
392
+ <p className="text-sm font-medium text-bolt-elements-textPrimary">{systemInfo.cores}</p>
393
+ </div>
394
+ </div>
395
+ <div className="mt-3 pt-3 border-t border-bolt-elements-surface-hover">
396
+ <p className="text-xs text-bolt-elements-textSecondary">Version</p>
397
+ <p className="text-sm font-medium text-bolt-elements-textPrimary font-mono">
398
+ {versionHash.slice(0, 7)}
399
+ <span className="ml-2 text-xs text-bolt-elements-textSecondary">
400
+ ({new Date().toLocaleDateString()})
401
+ </span>
402
+ </p>
403
+ </div>
404
+ </div>
405
+ </div>
406
+
407
+ <div>
408
+ <h4 className="text-md font-medium text-bolt-elements-textPrimary mb-2">Local LLM Status</h4>
409
+ <div className="bg-bolt-elements-surface rounded-lg">
410
+ <div className="grid grid-cols-1 divide-y">
411
+ {activeProviders.map((provider) => (
412
+ <div key={provider.name} className="p-3 flex flex-col space-y-2">
413
+ <div className="flex items-center justify-between">
414
+ <div className="flex items-center gap-3">
415
+ <div className="flex-shrink-0">
416
+ <div
417
+ className={`w-2 h-2 rounded-full ${
418
+ !provider.enabled ? 'bg-gray-300' : provider.isRunning ? 'bg-green-400' : 'bg-red-400'
419
+ }`}
420
+ />
421
+ </div>
422
+ <div>
423
+ <p className="text-sm font-medium text-bolt-elements-textPrimary">{provider.name}</p>
424
+ {provider.url && (
425
+ <p className="text-xs text-bolt-elements-textSecondary truncate max-w-[300px]">
426
+ {provider.url}
427
+ </p>
428
+ )}
429
+ </div>
430
+ </div>
431
+ <div className="flex items-center gap-2">
432
+ <span
433
+ className={`px-2 py-0.5 text-xs rounded-full ${
434
+ provider.enabled ? 'bg-green-100 text-green-800' : 'bg-gray-100 text-gray-800'
435
+ }`}
436
+ >
437
+ {provider.enabled ? 'Enabled' : 'Disabled'}
438
+ </span>
439
+ {provider.enabled && (
440
+ <span
441
+ className={`px-2 py-0.5 text-xs rounded-full ${
442
+ provider.isRunning ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'
443
+ }`}
444
+ >
445
+ {provider.isRunning ? 'Running' : 'Not Running'}
446
+ </span>
447
+ )}
448
+ </div>
449
+ </div>
450
+
451
+ <div className="pl-5 flex flex-col space-y-1 text-xs">
452
+ {/* Status Details */}
453
+ <div className="flex flex-wrap gap-2">
454
+ <span className="text-bolt-elements-textSecondary">
455
+ Last checked: {new Date(provider.lastChecked).toLocaleTimeString()}
456
+ </span>
457
+ {provider.responseTime && (
458
+ <span className="text-bolt-elements-textSecondary">
459
+ Response time: {Math.round(provider.responseTime)}ms
460
+ </span>
461
+ )}
462
+ </div>
463
+
464
+ {/* Error Message */}
465
+ {provider.error && (
466
+ <div className="mt-1 text-red-600 bg-red-50 rounded-md p-2">
467
+ <span className="font-medium">Error:</span> {provider.error}
468
+ </div>
469
+ )}
470
+
471
+ {/* Connection Info */}
472
+ {provider.url && (
473
+ <div className="text-bolt-elements-textSecondary">
474
+ <span className="font-medium">Endpoints checked:</span>
475
+ <ul className="list-disc list-inside pl-2 mt-1">
476
+ <li>{provider.url} (root)</li>
477
+ <li>{provider.url}/api/health</li>
478
+ <li>{provider.url}/v1/models</li>
479
+ </ul>
480
+ </div>
481
+ )}
482
+ </div>
483
+ </div>
484
+ ))}
485
+ {activeProviders.length === 0 && (
486
+ <div className="p-4 text-center text-bolt-elements-textSecondary">No local LLMs configured</div>
487
+ )}
488
+ </div>
489
+ </div>
490
+ </div>
491
+ </section>
492
  </div>
493
  );
494
  }
app/components/settings/event-logs/EventLogsTab.tsx ADDED
@@ -0,0 +1,219 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useCallback, useEffect, useState, useMemo } from 'react';
2
+ import { useSettings } from '~/lib/hooks/useSettings';
3
+ import { toast } from 'react-toastify';
4
+ import { Switch } from '~/components/ui/Switch';
5
+ import { logStore, type LogEntry } from '~/lib/stores/logs';
6
+ import { useStore } from '@nanostores/react';
7
+ import { classNames } from '~/utils/classNames';
8
+
9
+ export default function EventLogsTab() {
10
+ const {} = useSettings();
11
+ const showLogs = useStore(logStore.showLogs);
12
+ const [logLevel, setLogLevel] = useState<LogEntry['level'] | 'all'>('info');
13
+ const [autoScroll, setAutoScroll] = useState(true);
14
+ const [searchQuery, setSearchQuery] = useState('');
15
+ const [, forceUpdate] = useState({});
16
+
17
+ const filteredLogs = useMemo(() => {
18
+ const logs = logStore.getLogs();
19
+ return logs.filter((log) => {
20
+ const matchesLevel = !logLevel || log.level === logLevel || logLevel === 'all';
21
+ const matchesSearch =
22
+ !searchQuery ||
23
+ log.message?.toLowerCase().includes(searchQuery.toLowerCase()) ||
24
+ JSON.stringify(log.details)?.toLowerCase()?.includes(searchQuery?.toLowerCase());
25
+
26
+ return matchesLevel && matchesSearch;
27
+ });
28
+ }, [logLevel, searchQuery]);
29
+
30
+ // Effect to initialize showLogs
31
+ useEffect(() => {
32
+ logStore.showLogs.set(true);
33
+ }, []);
34
+
35
+ useEffect(() => {
36
+ // System info logs
37
+ logStore.logSystem('Application initialized', {
38
+ version: process.env.NEXT_PUBLIC_APP_VERSION,
39
+ environment: process.env.NODE_ENV,
40
+ });
41
+
42
+ // Debug logs for system state
43
+ logStore.logDebug('System configuration loaded', {
44
+ runtime: 'Next.js',
45
+ features: ['AI Chat', 'Event Logging'],
46
+ });
47
+
48
+ // Warning logs for potential issues
49
+ logStore.logWarning('Resource usage threshold approaching', {
50
+ memoryUsage: '75%',
51
+ cpuLoad: '60%',
52
+ });
53
+
54
+ // Error logs with detailed context
55
+ logStore.logError('API connection failed', new Error('Connection timeout'), {
56
+ endpoint: '/api/chat',
57
+ retryCount: 3,
58
+ lastAttempt: new Date().toISOString(),
59
+ });
60
+ }, []);
61
+
62
+ useEffect(() => {
63
+ const container = document.querySelector('.logs-container');
64
+
65
+ if (container && autoScroll) {
66
+ container.scrollTop = container.scrollHeight;
67
+ }
68
+ }, [filteredLogs, autoScroll]);
69
+
70
+ const handleClearLogs = useCallback(() => {
71
+ if (confirm('Are you sure you want to clear all logs?')) {
72
+ logStore.clearLogs();
73
+ toast.success('Logs cleared successfully');
74
+ forceUpdate({}); // Force a re-render after clearing logs
75
+ }
76
+ }, []);
77
+
78
+ const handleExportLogs = useCallback(() => {
79
+ try {
80
+ const logText = logStore
81
+ .getLogs()
82
+ .map(
83
+ (log) =>
84
+ `[${log.level.toUpperCase()}] ${log.timestamp} - ${log.message}${
85
+ log.details ? '\nDetails: ' + JSON.stringify(log.details, null, 2) : ''
86
+ }`,
87
+ )
88
+ .join('\n\n');
89
+
90
+ const blob = new Blob([logText], { type: 'text/plain' });
91
+ const url = URL.createObjectURL(blob);
92
+ const a = document.createElement('a');
93
+ a.href = url;
94
+ a.download = `event-logs-${new Date().toISOString()}.txt`;
95
+ document.body.appendChild(a);
96
+ a.click();
97
+ document.body.removeChild(a);
98
+ URL.revokeObjectURL(url);
99
+ toast.success('Logs exported successfully');
100
+ } catch (error) {
101
+ toast.error('Failed to export logs');
102
+ console.error('Export error:', error);
103
+ }
104
+ }, []);
105
+
106
+ const getLevelColor = (level: LogEntry['level']) => {
107
+ switch (level) {
108
+ case 'info':
109
+ return 'text-blue-500';
110
+ case 'warning':
111
+ return 'text-yellow-500';
112
+ case 'error':
113
+ return 'text-red-500';
114
+ case 'debug':
115
+ return 'text-gray-500';
116
+ default:
117
+ return 'text-bolt-elements-textPrimary';
118
+ }
119
+ };
120
+
121
+ return (
122
+ <div className="p-4 h-full flex flex-col">
123
+ <div className="flex flex-col space-y-4 mb-4">
124
+ {/* Title and Toggles Row */}
125
+ <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
126
+ <h3 className="text-lg font-medium text-bolt-elements-textPrimary">Event Logs</h3>
127
+ <div className="flex flex-wrap items-center gap-4">
128
+ <div className="flex items-center space-x-2">
129
+ <span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Show Actions</span>
130
+ <Switch checked={showLogs} onCheckedChange={(checked) => logStore.showLogs.set(checked)} />
131
+ </div>
132
+ <div className="flex items-center space-x-2">
133
+ <span className="text-sm text-bolt-elements-textSecondary whitespace-nowrap">Auto-scroll</span>
134
+ <Switch checked={autoScroll} onCheckedChange={setAutoScroll} />
135
+ </div>
136
+ </div>
137
+ </div>
138
+
139
+ {/* Controls Row */}
140
+ <div className="flex flex-wrap items-center gap-2">
141
+ <select
142
+ value={logLevel}
143
+ onChange={(e) => setLogLevel(e.target.value as LogEntry['level'])}
144
+ className="flex-1 p-2 rounded-lg border border-bolt-elements-borderColor bg-bolt-elements-prompt-background text-bolt-elements-textPrimary focus:outline-none focus:ring-2 focus:ring-bolt-elements-focus transition-all lg:max-w-[20%] text-sm min-w-[100px]"
145
+ >
146
+ <option value="all">All</option>
147
+ <option value="info">Info</option>
148
+ <option value="warning">Warning</option>
149
+ <option value="error">Error</option>
150
+ <option value="debug">Debug</option>
151
+ </select>
152
+ <div className="flex-1 min-w-[200px]">
153
+ <input
154
+ type="text"
155
+ placeholder="Search logs..."
156
+ value={searchQuery}
157
+ onChange={(e) => setSearchQuery(e.target.value)}
158
+ className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"
159
+ />
160
+ </div>
161
+ {showLogs && (
162
+ <div className="flex items-center gap-2 flex-nowrap">
163
+ <button
164
+ onClick={handleExportLogs}
165
+ className={classNames(
166
+ 'bg-bolt-elements-button-primary-background',
167
+ 'rounded-lg px-4 py-2 transition-colors duration-200',
168
+ 'hover:bg-bolt-elements-button-primary-backgroundHover',
169
+ 'text-bolt-elements-button-primary-text',
170
+ )}
171
+ >
172
+ Export Logs
173
+ </button>
174
+ <button
175
+ onClick={handleClearLogs}
176
+ className={classNames(
177
+ 'bg-bolt-elements-button-danger-background',
178
+ 'rounded-lg px-4 py-2 transition-colors duration-200',
179
+ 'hover:bg-bolt-elements-button-danger-backgroundHover',
180
+ 'text-bolt-elements-button-danger-text',
181
+ )}
182
+ >
183
+ Clear Logs
184
+ </button>
185
+ </div>
186
+ )}
187
+ </div>
188
+ </div>
189
+
190
+ <div className="bg-bolt-elements-bg-depth-1 rounded-lg p-4 h-[calc(100vh - 250px)] min-h-[400px] overflow-y-auto logs-container overflow-y-auto">
191
+ {filteredLogs.length === 0 ? (
192
+ <div className="text-center text-bolt-elements-textSecondary py-8">No logs found</div>
193
+ ) : (
194
+ filteredLogs.map((log, index) => (
195
+ <div
196
+ key={index}
197
+ className="text-sm mb-3 font-mono border-b border-bolt-elements-borderColor pb-2 last:border-0"
198
+ >
199
+ <div className="flex items-start space-x-2 flex-wrap">
200
+ <span className={`font-bold ${getLevelColor(log.level)} whitespace-nowrap`}>
201
+ [{log.level.toUpperCase()}]
202
+ </span>
203
+ <span className="text-bolt-elements-textSecondary whitespace-nowrap">
204
+ {new Date(log.timestamp).toLocaleString()}
205
+ </span>
206
+ <span className="text-bolt-elements-textPrimary break-all">{log.message}</span>
207
+ </div>
208
+ {log.details && (
209
+ <pre className="mt-2 text-xs text-bolt-elements-textSecondary overflow-x-auto whitespace-pre-wrap break-all">
210
+ {JSON.stringify(log.details, null, 2)}
211
+ </pre>
212
+ )}
213
+ </div>
214
+ ))
215
+ )}
216
+ </div>
217
+ </div>
218
+ );
219
+ }
app/components/settings/features/FeaturesTab.tsx CHANGED
@@ -3,7 +3,7 @@ import { Switch } from '~/components/ui/Switch';
3
  import { useSettings } from '~/lib/hooks/useSettings';
4
 
5
  export default function FeaturesTab() {
6
- const { debug, enableDebugMode, isLocalModel, enableLocalModels } = useSettings();
7
  return (
8
  <div className="p-4 bg-bolt-elements-bg-depth-2 border border-bolt-elements-borderColor rounded-lg mb-4">
9
  <div className="mb-6">
@@ -12,6 +12,10 @@ export default function FeaturesTab() {
12
  <span className="text-bolt-elements-textPrimary">Debug Info</span>
13
  <Switch className="ml-auto" checked={debug} onCheckedChange={enableDebugMode} />
14
  </div>
 
 
 
 
15
  </div>
16
 
17
  <div className="mb-6 border-t border-bolt-elements-borderColor pt-4">
 
3
  import { useSettings } from '~/lib/hooks/useSettings';
4
 
5
  export default function FeaturesTab() {
6
+ const { debug, enableDebugMode, isLocalModel, enableLocalModels, eventLogs, enableEventLogs } = useSettings();
7
  return (
8
  <div className="p-4 bg-bolt-elements-bg-depth-2 border border-bolt-elements-borderColor rounded-lg mb-4">
9
  <div className="mb-6">
 
12
  <span className="text-bolt-elements-textPrimary">Debug Info</span>
13
  <Switch className="ml-auto" checked={debug} onCheckedChange={enableDebugMode} />
14
  </div>
15
+ <div className="flex items-center justify-between mb-2">
16
+ <span className="text-bolt-elements-textPrimary">Event Logs</span>
17
+ <Switch className="ml-auto" checked={eventLogs} onCheckedChange={enableEventLogs} />
18
+ </div>
19
  </div>
20
 
21
  <div className="mb-6 border-t border-bolt-elements-borderColor pt-4">
app/components/settings/providers/ProvidersTab.tsx CHANGED
@@ -3,6 +3,7 @@ import { Switch } from '~/components/ui/Switch';
3
  import { useSettings } from '~/lib/hooks/useSettings';
4
  import { LOCAL_PROVIDERS, URL_CONFIGURABLE_PROVIDERS } from '~/lib/stores/settings';
5
  import type { IProviderConfig } from '~/types/model';
 
6
 
7
  export default function ProvidersTab() {
8
  const { providers, updateProviderSettings, isLocalModel } = useSettings();
@@ -49,11 +50,22 @@ export default function ProvidersTab() {
49
  className="flex flex-col mb-2 provider-item hover:bg-bolt-elements-bg-depth-3 p-4 rounded-lg border border-bolt-elements-borderColor "
50
  >
51
  <div className="flex items-center justify-between mb-2">
52
- <span className="text-bolt-elements-textPrimary">{provider.name}</span>
 
 
 
53
  <Switch
54
  className="ml-auto"
55
  checked={provider.settings.enabled}
56
- onCheckedChange={(enabled) => updateProviderSettings(provider.name, { ...provider.settings, enabled })}
 
 
 
 
 
 
 
 
57
  />
58
  </div>
59
  {/* Base URL input for configurable providers */}
@@ -63,9 +75,14 @@ export default function ProvidersTab() {
63
  <input
64
  type="text"
65
  value={provider.settings.baseUrl || ''}
66
- onChange={(e) =>
67
- updateProviderSettings(provider.name, { ...provider.settings, baseUrl: e.target.value })
68
- }
 
 
 
 
 
69
  placeholder={`Enter ${provider.name} base URL`}
70
  className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"
71
  />
 
3
  import { useSettings } from '~/lib/hooks/useSettings';
4
  import { LOCAL_PROVIDERS, URL_CONFIGURABLE_PROVIDERS } from '~/lib/stores/settings';
5
  import type { IProviderConfig } from '~/types/model';
6
+ import { logStore } from '~/lib/stores/logs';
7
 
8
  export default function ProvidersTab() {
9
  const { providers, updateProviderSettings, isLocalModel } = useSettings();
 
50
  className="flex flex-col mb-2 provider-item hover:bg-bolt-elements-bg-depth-3 p-4 rounded-lg border border-bolt-elements-borderColor "
51
  >
52
  <div className="flex items-center justify-between mb-2">
53
+ <div className="flex items-center gap-2">
54
+ <img src={`/icons/${provider.name}.svg`} alt={`${provider.name} icon`} className="w-6 h-6 dark:invert" />
55
+ <span className="text-bolt-elements-textPrimary">{provider.name}</span>
56
+ </div>
57
  <Switch
58
  className="ml-auto"
59
  checked={provider.settings.enabled}
60
+ onCheckedChange={(enabled) => {
61
+ updateProviderSettings(provider.name, { ...provider.settings, enabled });
62
+
63
+ if (enabled) {
64
+ logStore.logProvider(`Provider ${provider.name} enabled`, { provider: provider.name });
65
+ } else {
66
+ logStore.logProvider(`Provider ${provider.name} disabled`, { provider: provider.name });
67
+ }
68
+ }}
69
  />
70
  </div>
71
  {/* Base URL input for configurable providers */}
 
75
  <input
76
  type="text"
77
  value={provider.settings.baseUrl || ''}
78
+ onChange={(e) => {
79
+ const newBaseUrl = e.target.value;
80
+ updateProviderSettings(provider.name, { ...provider.settings, baseUrl: newBaseUrl });
81
+ logStore.logProvider(`Base URL updated for ${provider.name}`, {
82
+ provider: provider.name,
83
+ baseUrl: newBaseUrl,
84
+ });
85
+ }}
86
  placeholder={`Enter ${provider.name} base URL`}
87
  className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"
88
  />
app/components/sidebar/Menu.client.tsx CHANGED
@@ -35,6 +35,25 @@ const menuVariants = {
35
 
36
  type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null;
37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  export const Menu = () => {
39
  const { duplicateCurrentChat, exportChat } = useChatHistory();
40
  const menuRef = useRef<HTMLDivElement>(null);
@@ -126,18 +145,17 @@ export const Menu = () => {
126
  variants={menuVariants}
127
  className="flex selection-accent flex-col side-menu fixed top-0 w-[350px] h-full bg-bolt-elements-background-depth-2 border-r rounded-r-3xl border-bolt-elements-borderColor z-sidebar shadow-xl shadow-bolt-elements-sidebar-dropdownShadow text-sm"
128
  >
129
- <div className="flex items-center h-[var(--header-height)]">{/* Placeholder */}</div>
 
130
  <div className="flex-1 flex flex-col h-full w-full overflow-hidden">
131
  <div className="p-4 select-none">
132
  <a
133
  href="/"
134
- className="flex gap-2 items-center bg-bolt-elements-sidebar-buttonBackgroundDefault text-bolt-elements-sidebar-buttonText hover:bg-bolt-elements-sidebar-buttonBackgroundHover rounded-md p-2 transition-theme"
135
  >
136
  <span className="inline-block i-bolt:chat scale-110" />
137
  Start new chat
138
  </a>
139
- </div>
140
- <div className="pl-4 pr-4 my-2">
141
  <div className="relative w-full">
142
  <input
143
  className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"
 
35
 
36
  type DialogContent = { type: 'delete'; item: ChatHistoryItem } | null;
37
 
38
+ function CurrentDateTime() {
39
+ const [dateTime, setDateTime] = useState(new Date());
40
+
41
+ useEffect(() => {
42
+ const timer = setInterval(() => {
43
+ setDateTime(new Date());
44
+ }, 60000); // Update every minute
45
+
46
+ return () => clearInterval(timer);
47
+ }, []);
48
+
49
+ return (
50
+ <div className="flex items-center gap-2 px-4 py-3 font-bold text-gray-700 dark:text-gray-300 border-b border-bolt-elements-borderColor">
51
+ <div className="h-4 w-4 i-ph:clock-thin" />
52
+ {dateTime.toLocaleDateString()} {dateTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
53
+ </div>
54
+ );
55
+ }
56
+
57
  export const Menu = () => {
58
  const { duplicateCurrentChat, exportChat } = useChatHistory();
59
  const menuRef = useRef<HTMLDivElement>(null);
 
145
  variants={menuVariants}
146
  className="flex selection-accent flex-col side-menu fixed top-0 w-[350px] h-full bg-bolt-elements-background-depth-2 border-r rounded-r-3xl border-bolt-elements-borderColor z-sidebar shadow-xl shadow-bolt-elements-sidebar-dropdownShadow text-sm"
147
  >
148
+ <div className="h-[60px]" /> {/* Spacer for top margin */}
149
+ <CurrentDateTime />
150
  <div className="flex-1 flex flex-col h-full w-full overflow-hidden">
151
  <div className="p-4 select-none">
152
  <a
153
  href="/"
154
+ className="flex gap-2 items-center bg-bolt-elements-sidebar-buttonBackgroundDefault text-bolt-elements-sidebar-buttonText hover:bg-bolt-elements-sidebar-buttonBackgroundHover rounded-md p-2 transition-theme mb-4"
155
  >
156
  <span className="inline-block i-bolt:chat scale-110" />
157
  Start new chat
158
  </a>
 
 
159
  <div className="relative w-full">
160
  <input
161
  className="w-full bg-white dark:bg-bolt-elements-background-depth-4 relative px-2 py-1.5 rounded-md focus:outline-none placeholder-bolt-elements-textTertiary text-bolt-elements-textPrimary dark:text-bolt-elements-textPrimary border border-bolt-elements-borderColor"
app/lib/.server/llm/prompts.ts CHANGED
@@ -174,14 +174,14 @@ You are Bolt, an expert AI assistant and exceptional senior software developer w
174
 
175
  - When Using \`npx\`, ALWAYS provide the \`--yes\` flag.
176
  - When running multiple shell commands, use \`&&\` to run them sequentially.
177
- - ULTRA IMPORTANT: Do NOT re-run a dev command with shell action use dev action to run dev commands
178
 
179
  - file: For writing new files or updating existing files. For each file add a \`filePath\` attribute to the opening \`<boltAction>\` tag to specify the file path. The content of the file artifact is the file contents. All file paths MUST BE relative to the current working directory.
180
 
181
- - start: For starting development server.
182
- - Use to start application if not already started or NEW dependencies added
183
- - Only use this action when you need to run a dev server or start the application
184
- - ULTRA IMORTANT: do NOT re-run a dev server if files updated, existing dev server can autometically detect changes and executes the file changes
185
 
186
 
187
  9. The order of the actions is VERY IMPORTANT. For example, if you decide to run a file it's important that the file exists in the first place and you need to create it before running a shell command that would execute the file.
 
174
 
175
  - When Using \`npx\`, ALWAYS provide the \`--yes\` flag.
176
  - When running multiple shell commands, use \`&&\` to run them sequentially.
177
+ - ULTRA IMPORTANT: Do NOT run a dev command with shell action use start action to run dev commands
178
 
179
  - file: For writing new files or updating existing files. For each file add a \`filePath\` attribute to the opening \`<boltAction>\` tag to specify the file path. The content of the file artifact is the file contents. All file paths MUST BE relative to the current working directory.
180
 
181
+ - start: For starting a development server.
182
+ - Use to start application if it hasn’t been started yet or when NEW dependencies have been added.
183
+ - Only use this action when you need to run a dev server or start the application
184
+ - ULTRA IMPORTANT: do NOT re-run a dev server if files are updated. The existing dev server can automatically detect changes and executes the file changes
185
 
186
 
187
  9. The order of the actions is VERY IMPORTANT. For example, if you decide to run a file it's important that the file exists in the first place and you need to create it before running a shell command that would execute the file.
app/lib/.server/llm/stream-text.ts CHANGED
@@ -3,6 +3,7 @@ import { getModel } from '~/lib/.server/llm/model';
3
  import { MAX_TOKENS } from './constants';
4
  import { getSystemPrompt } from './prompts';
5
  import { DEFAULT_MODEL, DEFAULT_PROVIDER, getModelList, MODEL_REGEX, PROVIDER_REGEX } from '~/utils/constants';
 
6
  import type { IProviderSetting } from '~/types/model';
7
 
8
  interface ToolResult<Name extends string, Args, Result> {
@@ -23,6 +24,78 @@ export type Messages = Message[];
23
 
24
  export type StreamingOptions = Omit<Parameters<typeof _streamText>[0], 'model'>;
25
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
26
  function extractPropertiesFromMessage(message: Message): { model: string; provider: string; content: string } {
27
  const textContent = Array.isArray(message.content)
28
  ? message.content.find((item) => item.type === 'text')?.text || ''
@@ -64,9 +137,10 @@ export async function streamText(props: {
64
  env: Env;
65
  options?: StreamingOptions;
66
  apiKeys?: Record<string, string>;
 
67
  providerSettings?: Record<string, IProviderSetting>;
68
  }) {
69
- const { messages, env, options, apiKeys, providerSettings } = props;
70
  let currentModel = DEFAULT_MODEL;
71
  let currentProvider = DEFAULT_PROVIDER.name;
72
  const MODEL_LIST = await getModelList(apiKeys || {}, providerSettings);
@@ -80,6 +154,12 @@ export async function streamText(props: {
80
 
81
  currentProvider = provider;
82
 
 
 
 
 
 
 
83
  return { ...message, content };
84
  }
85
 
@@ -90,9 +170,17 @@ export async function streamText(props: {
90
 
91
  const dynamicMaxTokens = modelDetails && modelDetails.maxTokenAllowed ? modelDetails.maxTokenAllowed : MAX_TOKENS;
92
 
 
 
 
 
 
 
 
 
93
  return _streamText({
94
  model: getModel(currentProvider, currentModel, env, apiKeys, providerSettings) as any,
95
- system: getSystemPrompt(),
96
  maxTokens: dynamicMaxTokens,
97
  messages: convertToCoreMessages(processedMessages as any),
98
  ...options,
 
3
  import { MAX_TOKENS } from './constants';
4
  import { getSystemPrompt } from './prompts';
5
  import { DEFAULT_MODEL, DEFAULT_PROVIDER, getModelList, MODEL_REGEX, PROVIDER_REGEX } from '~/utils/constants';
6
+ import ignore from 'ignore';
7
  import type { IProviderSetting } from '~/types/model';
8
 
9
  interface ToolResult<Name extends string, Args, Result> {
 
24
 
25
  export type StreamingOptions = Omit<Parameters<typeof _streamText>[0], 'model'>;
26
 
27
+ export interface File {
28
+ type: 'file';
29
+ content: string;
30
+ isBinary: boolean;
31
+ }
32
+
33
+ export interface Folder {
34
+ type: 'folder';
35
+ }
36
+
37
+ type Dirent = File | Folder;
38
+
39
+ export type FileMap = Record<string, Dirent | undefined>;
40
+
41
+ export function simplifyBoltActions(input: string): string {
42
+ // Using regex to match boltAction tags that have type="file"
43
+ const regex = /(<boltAction[^>]*type="file"[^>]*>)([\s\S]*?)(<\/boltAction>)/g;
44
+
45
+ // Replace each matching occurrence
46
+ return input.replace(regex, (_0, openingTag, _2, closingTag) => {
47
+ return `${openingTag}\n ...\n ${closingTag}`;
48
+ });
49
+ }
50
+
51
+ // Common patterns to ignore, similar to .gitignore
52
+ const IGNORE_PATTERNS = [
53
+ 'node_modules/**',
54
+ '.git/**',
55
+ 'dist/**',
56
+ 'build/**',
57
+ '.next/**',
58
+ 'coverage/**',
59
+ '.cache/**',
60
+ '.vscode/**',
61
+ '.idea/**',
62
+ '**/*.log',
63
+ '**/.DS_Store',
64
+ '**/npm-debug.log*',
65
+ '**/yarn-debug.log*',
66
+ '**/yarn-error.log*',
67
+ '**/*lock.json',
68
+ '**/*lock.yml',
69
+ ];
70
+ const ig = ignore().add(IGNORE_PATTERNS);
71
+
72
+ function createFilesContext(files: FileMap) {
73
+ let filePaths = Object.keys(files);
74
+ filePaths = filePaths.filter((x) => {
75
+ const relPath = x.replace('/home/project/', '');
76
+ return !ig.ignores(relPath);
77
+ });
78
+
79
+ const fileContexts = filePaths
80
+ .filter((x) => files[x] && files[x].type == 'file')
81
+ .map((path) => {
82
+ const dirent = files[path];
83
+
84
+ if (!dirent || dirent.type == 'folder') {
85
+ return '';
86
+ }
87
+
88
+ const codeWithLinesNumbers = dirent.content
89
+ .split('\n')
90
+ .map((v, i) => `${i + 1}|${v}`)
91
+ .join('\n');
92
+
93
+ return `<file path="${path}">\n${codeWithLinesNumbers}\n</file>`;
94
+ });
95
+
96
+ 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>`;
97
+ }
98
+
99
  function extractPropertiesFromMessage(message: Message): { model: string; provider: string; content: string } {
100
  const textContent = Array.isArray(message.content)
101
  ? message.content.find((item) => item.type === 'text')?.text || ''
 
137
  env: Env;
138
  options?: StreamingOptions;
139
  apiKeys?: Record<string, string>;
140
+ files?: FileMap;
141
  providerSettings?: Record<string, IProviderSetting>;
142
  }) {
143
+ const { messages, env, options, apiKeys, files, providerSettings } = props;
144
  let currentModel = DEFAULT_MODEL;
145
  let currentProvider = DEFAULT_PROVIDER.name;
146
  const MODEL_LIST = await getModelList(apiKeys || {}, providerSettings);
 
154
 
155
  currentProvider = provider;
156
 
157
+ return { ...message, content };
158
+ } else if (message.role == 'assistant') {
159
+ const content = message.content;
160
+
161
+ // content = simplifyBoltActions(content);
162
+
163
  return { ...message, content };
164
  }
165
 
 
170
 
171
  const dynamicMaxTokens = modelDetails && modelDetails.maxTokenAllowed ? modelDetails.maxTokenAllowed : MAX_TOKENS;
172
 
173
+ let systemPrompt = getSystemPrompt();
174
+ let codeContext = '';
175
+
176
+ if (files) {
177
+ codeContext = createFilesContext(files);
178
+ systemPrompt = `${systemPrompt}\n\n ${codeContext}`;
179
+ }
180
+
181
  return _streamText({
182
  model: getModel(currentProvider, currentModel, env, apiKeys, providerSettings) as any,
183
+ system: systemPrompt,
184
  maxTokens: dynamicMaxTokens,
185
  messages: convertToCoreMessages(processedMessages as any),
186
  ...options,
app/lib/hooks/useMessageParser.ts CHANGED
@@ -23,14 +23,14 @@ const messageParser = new StreamingMessageParser({
23
  logger.trace('onActionOpen', data.action);
24
 
25
  // we only add shell actions when when the close tag got parsed because only then we have the content
26
- if (data.action.type !== 'shell') {
27
  workbenchStore.addAction(data);
28
  }
29
  },
30
  onActionClose: (data) => {
31
  logger.trace('onActionClose', data.action);
32
 
33
- if (data.action.type === 'shell') {
34
  workbenchStore.addAction(data);
35
  }
36
 
 
23
  logger.trace('onActionOpen', data.action);
24
 
25
  // we only add shell actions when when the close tag got parsed because only then we have the content
26
+ if (data.action.type === 'file') {
27
  workbenchStore.addAction(data);
28
  }
29
  },
30
  onActionClose: (data) => {
31
  logger.trace('onActionClose', data.action);
32
 
33
+ if (data.action.type !== 'file') {
34
  workbenchStore.addAction(data);
35
  }
36
 
app/lib/hooks/useSettings.tsx CHANGED
@@ -1,12 +1,20 @@
1
  import { useStore } from '@nanostores/react';
2
- import { isDebugMode, isLocalModelsEnabled, LOCAL_PROVIDERS, providersStore } from '~/lib/stores/settings';
 
 
 
 
 
 
3
  import { useCallback, useEffect, useState } from 'react';
4
  import Cookies from 'js-cookie';
5
  import type { IProviderSetting, ProviderInfo } from '~/types/model';
 
6
 
7
  export function useSettings() {
8
  const providers = useStore(providersStore);
9
  const debug = useStore(isDebugMode);
 
10
  const isLocalModel = useStore(isLocalModelsEnabled);
11
  const [activeProviders, setActiveProviders] = useState<ProviderInfo[]>([]);
12
 
@@ -23,7 +31,7 @@ export function useSettings() {
23
  ...currentProvider,
24
  settings: {
25
  ...parsedProviders[provider],
26
- enabled: parsedProviders[provider].enabled || true,
27
  },
28
  });
29
  });
@@ -39,6 +47,13 @@ export function useSettings() {
39
  isDebugMode.set(savedDebugMode === 'true');
40
  }
41
 
 
 
 
 
 
 
 
42
  // load local models from cookies
43
  const savedLocalModels = Cookies.get('isLocalModelsEnabled');
44
 
@@ -70,18 +85,29 @@ export function useSettings() {
70
  }, [providers, isLocalModel]);
71
 
72
  // helper function to update settings
73
- const updateProviderSettings = useCallback((provider: string, config: IProviderSetting) => {
74
- const settings = providers[provider].settings;
75
- providersStore.setKey(provider, { ...providers[provider], settings: { ...settings, ...config } });
76
- }, []);
 
 
 
77
 
78
  const enableDebugMode = useCallback((enabled: boolean) => {
79
  isDebugMode.set(enabled);
 
80
  Cookies.set('isDebugEnabled', String(enabled));
81
  }, []);
82
 
 
 
 
 
 
 
83
  const enableLocalModels = useCallback((enabled: boolean) => {
84
  isLocalModelsEnabled.set(enabled);
 
85
  Cookies.set('isLocalModelsEnabled', String(enabled));
86
  }, []);
87
 
@@ -91,6 +117,8 @@ export function useSettings() {
91
  updateProviderSettings,
92
  debug,
93
  enableDebugMode,
 
 
94
  isLocalModel,
95
  enableLocalModels,
96
  };
 
1
  import { useStore } from '@nanostores/react';
2
+ import {
3
+ isDebugMode,
4
+ isEventLogsEnabled,
5
+ isLocalModelsEnabled,
6
+ LOCAL_PROVIDERS,
7
+ providersStore,
8
+ } from '~/lib/stores/settings';
9
  import { useCallback, useEffect, useState } from 'react';
10
  import Cookies from 'js-cookie';
11
  import type { IProviderSetting, ProviderInfo } from '~/types/model';
12
+ import { logStore } from '~/lib/stores/logs'; // assuming logStore is imported from this location
13
 
14
  export function useSettings() {
15
  const providers = useStore(providersStore);
16
  const debug = useStore(isDebugMode);
17
+ const eventLogs = useStore(isEventLogsEnabled);
18
  const isLocalModel = useStore(isLocalModelsEnabled);
19
  const [activeProviders, setActiveProviders] = useState<ProviderInfo[]>([]);
20
 
 
31
  ...currentProvider,
32
  settings: {
33
  ...parsedProviders[provider],
34
+ enabled: parsedProviders[provider].enabled ?? true,
35
  },
36
  });
37
  });
 
47
  isDebugMode.set(savedDebugMode === 'true');
48
  }
49
 
50
+ // load event logs from cookies
51
+ const savedEventLogs = Cookies.get('isEventLogsEnabled');
52
+
53
+ if (savedEventLogs) {
54
+ isEventLogsEnabled.set(savedEventLogs === 'true');
55
+ }
56
+
57
  // load local models from cookies
58
  const savedLocalModels = Cookies.get('isLocalModelsEnabled');
59
 
 
85
  }, [providers, isLocalModel]);
86
 
87
  // helper function to update settings
88
+ const updateProviderSettings = useCallback(
89
+ (provider: string, config: IProviderSetting) => {
90
+ const settings = providers[provider].settings;
91
+ providersStore.setKey(provider, { ...providers[provider], settings: { ...settings, ...config } });
92
+ },
93
+ [providers],
94
+ );
95
 
96
  const enableDebugMode = useCallback((enabled: boolean) => {
97
  isDebugMode.set(enabled);
98
+ logStore.logSystem(`Debug mode ${enabled ? 'enabled' : 'disabled'}`);
99
  Cookies.set('isDebugEnabled', String(enabled));
100
  }, []);
101
 
102
+ const enableEventLogs = useCallback((enabled: boolean) => {
103
+ isEventLogsEnabled.set(enabled);
104
+ logStore.logSystem(`Event logs ${enabled ? 'enabled' : 'disabled'}`);
105
+ Cookies.set('isEventLogsEnabled', String(enabled));
106
+ }, []);
107
+
108
  const enableLocalModels = useCallback((enabled: boolean) => {
109
  isLocalModelsEnabled.set(enabled);
110
+ logStore.logSystem(`Local models ${enabled ? 'enabled' : 'disabled'}`);
111
  Cookies.set('isLocalModelsEnabled', String(enabled));
112
  }, []);
113
 
 
117
  updateProviderSettings,
118
  debug,
119
  enableDebugMode,
120
+ eventLogs,
121
+ enableEventLogs,
122
  isLocalModel,
123
  enableLocalModels,
124
  };
app/lib/persistence/useChatHistory.ts CHANGED
@@ -4,6 +4,7 @@ import { atom } from 'nanostores';
4
  import type { Message } from 'ai';
5
  import { toast } from 'react-toastify';
6
  import { workbenchStore } from '~/lib/stores/workbench';
 
7
  import {
8
  getMessages,
9
  getNextId,
@@ -43,6 +44,8 @@ export function useChatHistory() {
43
  setReady(true);
44
 
45
  if (persistenceEnabled) {
 
 
46
  toast.error('Chat persistence is unavailable');
47
  }
48
 
@@ -69,6 +72,7 @@ export function useChatHistory() {
69
  setReady(true);
70
  })
71
  .catch((error) => {
 
72
  toast.error(error.message);
73
  });
74
  }
 
4
  import type { Message } from 'ai';
5
  import { toast } from 'react-toastify';
6
  import { workbenchStore } from '~/lib/stores/workbench';
7
+ import { logStore } from '~/lib/stores/logs'; // Import logStore
8
  import {
9
  getMessages,
10
  getNextId,
 
44
  setReady(true);
45
 
46
  if (persistenceEnabled) {
47
+ const error = new Error('Chat persistence is unavailable');
48
+ logStore.logError('Chat persistence initialization failed', error);
49
  toast.error('Chat persistence is unavailable');
50
  }
51
 
 
72
  setReady(true);
73
  })
74
  .catch((error) => {
75
+ logStore.logError('Failed to load chat messages', error);
76
  toast.error(error.message);
77
  });
78
  }
app/lib/stores/logs.ts ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { atom, map } from 'nanostores';
2
+ import Cookies from 'js-cookie';
3
+ import { createScopedLogger } from '~/utils/logger';
4
+
5
+ const logger = createScopedLogger('LogStore');
6
+
7
+ export interface LogEntry {
8
+ id: string;
9
+ timestamp: string;
10
+ level: 'info' | 'warning' | 'error' | 'debug';
11
+ message: string;
12
+ details?: Record<string, any>;
13
+ category: 'system' | 'provider' | 'user' | 'error';
14
+ }
15
+
16
+ const MAX_LOGS = 1000; // Maximum number of logs to keep in memory
17
+
18
+ class LogStore {
19
+ private _logs = map<Record<string, LogEntry>>({});
20
+ showLogs = atom(true);
21
+
22
+ constructor() {
23
+ // Load saved logs from cookies on initialization
24
+ this._loadLogs();
25
+ }
26
+
27
+ private _loadLogs() {
28
+ const savedLogs = Cookies.get('eventLogs');
29
+
30
+ if (savedLogs) {
31
+ try {
32
+ const parsedLogs = JSON.parse(savedLogs);
33
+ this._logs.set(parsedLogs);
34
+ } catch (error) {
35
+ logger.error('Failed to parse logs from cookies:', error);
36
+ }
37
+ }
38
+ }
39
+
40
+ private _saveLogs() {
41
+ const currentLogs = this._logs.get();
42
+ Cookies.set('eventLogs', JSON.stringify(currentLogs));
43
+ }
44
+
45
+ private _generateId(): string {
46
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
47
+ }
48
+
49
+ private _trimLogs() {
50
+ const currentLogs = Object.entries(this._logs.get());
51
+
52
+ if (currentLogs.length > MAX_LOGS) {
53
+ const sortedLogs = currentLogs.sort(
54
+ ([, a], [, b]) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
55
+ );
56
+ const newLogs = Object.fromEntries(sortedLogs.slice(0, MAX_LOGS));
57
+ this._logs.set(newLogs);
58
+ }
59
+ }
60
+
61
+ addLog(
62
+ message: string,
63
+ level: LogEntry['level'] = 'info',
64
+ category: LogEntry['category'] = 'system',
65
+ details?: Record<string, any>,
66
+ ) {
67
+ const id = this._generateId();
68
+ const entry: LogEntry = {
69
+ id,
70
+ timestamp: new Date().toISOString(),
71
+ level,
72
+ message,
73
+ details,
74
+ category,
75
+ };
76
+
77
+ this._logs.setKey(id, entry);
78
+ this._trimLogs();
79
+ this._saveLogs();
80
+
81
+ return id;
82
+ }
83
+
84
+ // System events
85
+ logSystem(message: string, details?: Record<string, any>) {
86
+ return this.addLog(message, 'info', 'system', details);
87
+ }
88
+
89
+ // Provider events
90
+ logProvider(message: string, details?: Record<string, any>) {
91
+ return this.addLog(message, 'info', 'provider', details);
92
+ }
93
+
94
+ // User actions
95
+ logUserAction(message: string, details?: Record<string, any>) {
96
+ return this.addLog(message, 'info', 'user', details);
97
+ }
98
+
99
+ // Error events
100
+ logError(message: string, error?: Error | unknown, details?: Record<string, any>) {
101
+ const errorDetails = {
102
+ ...(details || {}),
103
+ error:
104
+ error instanceof Error
105
+ ? {
106
+ message: error.message,
107
+ stack: error.stack,
108
+ }
109
+ : error,
110
+ };
111
+ return this.addLog(message, 'error', 'error', errorDetails);
112
+ }
113
+
114
+ // Warning events
115
+ logWarning(message: string, details?: Record<string, any>) {
116
+ return this.addLog(message, 'warning', 'system', details);
117
+ }
118
+
119
+ // Debug events
120
+ logDebug(message: string, details?: Record<string, any>) {
121
+ return this.addLog(message, 'debug', 'system', details);
122
+ }
123
+
124
+ clearLogs() {
125
+ this._logs.set({});
126
+ this._saveLogs();
127
+ }
128
+
129
+ getLogs() {
130
+ return Object.values(this._logs.get()).sort(
131
+ (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime(),
132
+ );
133
+ }
134
+
135
+ getFilteredLogs(level?: LogEntry['level'], category?: LogEntry['category'], searchQuery?: string) {
136
+ return this.getLogs().filter((log) => {
137
+ const matchesLevel = !level || level === 'debug' || log.level === level;
138
+ const matchesCategory = !category || log.category === category;
139
+ const matchesSearch =
140
+ !searchQuery ||
141
+ log.message.toLowerCase().includes(searchQuery.toLowerCase()) ||
142
+ JSON.stringify(log.details).toLowerCase().includes(searchQuery.toLowerCase());
143
+
144
+ return matchesLevel && matchesCategory && matchesSearch;
145
+ });
146
+ }
147
+ }
148
+
149
+ export const logStore = new LogStore();
app/lib/stores/settings.ts CHANGED
@@ -35,7 +35,7 @@ PROVIDER_LIST.forEach((provider) => {
35
  initialProviderSettings[provider.name] = {
36
  ...provider,
37
  settings: {
38
- enabled: false,
39
  },
40
  };
41
  });
@@ -43,4 +43,6 @@ export const providersStore = map<ProviderSetting>(initialProviderSettings);
43
 
44
  export const isDebugMode = atom(false);
45
 
 
 
46
  export const isLocalModelsEnabled = atom(true);
 
35
  initialProviderSettings[provider.name] = {
36
  ...provider,
37
  settings: {
38
+ enabled: true,
39
  },
40
  };
41
  });
 
43
 
44
  export const isDebugMode = atom(false);
45
 
46
+ export const isEventLogsEnabled = atom(false);
47
+
48
  export const isLocalModelsEnabled = atom(true);
app/lib/stores/theme.ts CHANGED
@@ -1,4 +1,5 @@
1
  import { atom } from 'nanostores';
 
2
 
3
  export type Theme = 'dark' | 'light';
4
 
@@ -26,10 +27,8 @@ function initStore() {
26
  export function toggleTheme() {
27
  const currentTheme = themeStore.get();
28
  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
29
-
30
  themeStore.set(newTheme);
31
-
32
  localStorage.setItem(kTheme, newTheme);
33
-
34
  document.querySelector('html')?.setAttribute('data-theme', newTheme);
35
  }
 
1
  import { atom } from 'nanostores';
2
+ import { logStore } from './logs';
3
 
4
  export type Theme = 'dark' | 'light';
5
 
 
27
  export function toggleTheme() {
28
  const currentTheme = themeStore.get();
29
  const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
 
30
  themeStore.set(newTheme);
31
+ logStore.logSystem(`Theme changed to ${newTheme} mode`);
32
  localStorage.setItem(kTheme, newTheme);
 
33
  document.querySelector('html')?.setAttribute('data-theme', newTheme);
34
  }
app/lib/stores/workbench.ts CHANGED
@@ -297,6 +297,7 @@ export class WorkbenchStore {
297
 
298
  const action = artifact.runner.actions.get()[data.actionId];
299
 
 
300
  if (!action || action.executed) {
301
  return;
302
  }
 
297
 
298
  const action = artifact.runner.actions.get()[data.actionId];
299
 
300
+
301
  if (!action || action.executed) {
302
  return;
303
  }
app/root.tsx CHANGED
@@ -78,6 +78,23 @@ export function Layout({ children }: { children: React.ReactNode }) {
78
  );
79
  }
80
 
 
 
81
  export default function App() {
82
- return <Outlet />;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  }
 
78
  );
79
  }
80
 
81
+ import { logStore } from './lib/stores/logs';
82
+
83
  export default function App() {
84
+ const theme = useStore(themeStore);
85
+
86
+ useEffect(() => {
87
+ logStore.logSystem('Application initialized', {
88
+ theme,
89
+ platform: navigator.platform,
90
+ userAgent: navigator.userAgent,
91
+ timestamp: new Date().toISOString(),
92
+ });
93
+ }, []);
94
+
95
+ return (
96
+ <Layout>
97
+ <Outlet />
98
+ </Layout>
99
+ );
100
  }
app/routes/api.chat.ts CHANGED
@@ -30,9 +30,9 @@ function parseCookies(cookieHeader: string) {
30
  }
31
 
32
  async function chatAction({ context, request }: ActionFunctionArgs) {
33
- const { messages } = await request.json<{
34
  messages: Messages;
35
- model: string;
36
  }>();
37
 
38
  const cookieHeader = request.headers.get('Cookie');
@@ -64,13 +64,27 @@ async function chatAction({ context, request }: ActionFunctionArgs) {
64
  messages.push({ role: 'assistant', content });
65
  messages.push({ role: 'user', content: CONTINUE_PROMPT });
66
 
67
- const result = await streamText({ messages, env: context.cloudflare.env, options, apiKeys, providerSettings });
 
 
 
 
 
 
 
68
 
69
  return stream.switchSource(result.toAIStream());
70
  },
71
  };
72
 
73
- const result = await streamText({ messages, env: context.cloudflare.env, options, apiKeys, providerSettings });
 
 
 
 
 
 
 
74
 
75
  stream.switchSource(result.toAIStream());
76
 
 
30
  }
31
 
32
  async function chatAction({ context, request }: ActionFunctionArgs) {
33
+ const { messages, files } = await request.json<{
34
  messages: Messages;
35
+ files: any;
36
  }>();
37
 
38
  const cookieHeader = request.headers.get('Cookie');
 
64
  messages.push({ role: 'assistant', content });
65
  messages.push({ role: 'user', content: CONTINUE_PROMPT });
66
 
67
+ const result = await streamText({
68
+ messages,
69
+ env: context.cloudflare.env,
70
+ options,
71
+ apiKeys,
72
+ files,
73
+ providerSettings,
74
+ });
75
 
76
  return stream.switchSource(result.toAIStream());
77
  },
78
  };
79
 
80
+ const result = await streamText({
81
+ messages,
82
+ env: context.cloudflare.env,
83
+ options,
84
+ apiKeys,
85
+ files,
86
+ providerSettings,
87
+ });
88
 
89
  stream.switchSource(result.toAIStream());
90
 
app/types/global.d.ts CHANGED
@@ -3,3 +3,11 @@ interface Window {
3
  webkitSpeechRecognition: typeof SpeechRecognition;
4
  SpeechRecognition: typeof SpeechRecognition;
5
  }
 
 
 
 
 
 
 
 
 
3
  webkitSpeechRecognition: typeof SpeechRecognition;
4
  SpeechRecognition: typeof SpeechRecognition;
5
  }
6
+
7
+ interface Performance {
8
+ memory?: {
9
+ jsHeapSizeLimit: number;
10
+ totalJSHeapSize: number;
11
+ usedJSHeapSize: number;
12
+ };
13
+ }
app/utils/constants.ts CHANGED
@@ -2,6 +2,7 @@ import Cookies from 'js-cookie';
2
  import type { ModelInfo, OllamaApiResponse, OllamaModel } from './types';
3
  import type { ProviderInfo, IProviderSetting } from '~/types/model';
4
  import { createScopedLogger } from './logger';
 
5
 
6
  export const WORK_DIR_NAME = 'project';
7
  export const WORK_DIR = `/home/${WORK_DIR_NAME}`;
@@ -373,12 +374,6 @@ const getOllamaBaseUrl = (settings?: IProviderSetting) => {
373
  };
374
 
375
  async function getOllamaModels(apiKeys?: Record<string, string>, settings?: IProviderSetting): Promise<ModelInfo[]> {
376
- /*
377
- * if (typeof window === 'undefined') {
378
- * return [];
379
- * }
380
- */
381
-
382
  try {
383
  const baseUrl = getOllamaBaseUrl(settings);
384
  const response = await fetch(`${baseUrl}/api/tags`);
@@ -391,7 +386,9 @@ async function getOllamaModels(apiKeys?: Record<string, string>, settings?: IPro
391
  maxTokenAllowed: 8000,
392
  }));
393
  } catch (e: any) {
 
394
  logger.warn('Failed to get Ollama models: ', e.message || '');
 
395
  return [];
396
  }
397
  }
@@ -465,10 +462,6 @@ async function getOpenRouterModels(): Promise<ModelInfo[]> {
465
  }
466
 
467
  async function getLMStudioModels(_apiKeys?: Record<string, string>, settings?: IProviderSetting): Promise<ModelInfo[]> {
468
- if (typeof window === 'undefined') {
469
- return [];
470
- }
471
-
472
  try {
473
  const baseUrl = settings?.baseUrl || import.meta.env.LMSTUDIO_API_BASE_URL || 'http://localhost:1234';
474
  const response = await fetch(`${baseUrl}/v1/models`);
@@ -480,7 +473,9 @@ async function getLMStudioModels(_apiKeys?: Record<string, string>, settings?: I
480
  provider: 'LMStudio',
481
  }));
482
  } catch (e: any) {
 
483
  logger.warn('Failed to get LMStudio models: ', e.message || '');
 
484
  return [];
485
  }
486
  }
@@ -499,6 +494,7 @@ async function initializeModelList(providerSettings?: Record<string, IProviderSe
499
  }
500
  }
501
  } catch (error: any) {
 
502
  logger.warn(`Failed to fetch apikeys from cookies: ${error?.message}`);
503
  }
504
  MODEL_LIST = [
 
2
  import type { ModelInfo, OllamaApiResponse, OllamaModel } from './types';
3
  import type { ProviderInfo, IProviderSetting } from '~/types/model';
4
  import { createScopedLogger } from './logger';
5
+ import { logStore } from '~/lib/stores/logs';
6
 
7
  export const WORK_DIR_NAME = 'project';
8
  export const WORK_DIR = `/home/${WORK_DIR_NAME}`;
 
374
  };
375
 
376
  async function getOllamaModels(apiKeys?: Record<string, string>, settings?: IProviderSetting): Promise<ModelInfo[]> {
 
 
 
 
 
 
377
  try {
378
  const baseUrl = getOllamaBaseUrl(settings);
379
  const response = await fetch(`${baseUrl}/api/tags`);
 
386
  maxTokenAllowed: 8000,
387
  }));
388
  } catch (e: any) {
389
+ logStore.logError('Failed to get Ollama models', e, { baseUrl: settings?.baseUrl });
390
  logger.warn('Failed to get Ollama models: ', e.message || '');
391
+
392
  return [];
393
  }
394
  }
 
462
  }
463
 
464
  async function getLMStudioModels(_apiKeys?: Record<string, string>, settings?: IProviderSetting): Promise<ModelInfo[]> {
 
 
 
 
465
  try {
466
  const baseUrl = settings?.baseUrl || import.meta.env.LMSTUDIO_API_BASE_URL || 'http://localhost:1234';
467
  const response = await fetch(`${baseUrl}/v1/models`);
 
473
  provider: 'LMStudio',
474
  }));
475
  } catch (e: any) {
476
+ logStore.logError('Failed to get LMStudio models', e, { baseUrl: settings?.baseUrl });
477
  logger.warn('Failed to get LMStudio models: ', e.message || '');
478
+
479
  return [];
480
  }
481
  }
 
494
  }
495
  }
496
  } catch (error: any) {
497
+ logStore.logError('Failed to fetch API keys from cookies', error);
498
  logger.warn(`Failed to fetch apikeys from cookies: ${error?.message}`);
499
  }
500
  MODEL_LIST = [
changelog.md ADDED
@@ -0,0 +1,808 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Release v0.0.1
2
+
3
+ ### πŸŽ‰ First Release
4
+
5
+ #### ✨ Features
6
+
7
+ - add login
8
+ - use tailwind-compat
9
+ - refactor layout and introduce workspace panel and fix some bugs
10
+ - add first version of workbench, increase token limit, improve system prompt
11
+ - improve prompt, add ability to abort streaming, improve message parser
12
+ - add support for message continuation (#1)
13
+ - chat autoscroll (#6)
14
+ - add simple api error handling (#9)
15
+ - initial persistence (#3)
16
+ - submit file changes to the llm (#11)
17
+ - add 'Open in StackBlitz' button to header (#10)
18
+ - add terminal and simple shortcut system (#16)
19
+ - use artifact id in urls, store metadata in history (#15)
20
+ - oauth-based login (#7)
21
+ - allow to disable auth during development (#21)
22
+ - allow to open up to three terminals (#22)
23
+ - tweak ui for redirect screen (#23)
24
+ - initial chat history ui (#25)
25
+ - add ability to change preview URL (#26)
26
+ - implement light and dark theme (#30)
27
+ - add basic analytics (#29)
28
+ - send analytics event for token usage (#37)
29
+ - add dropdown to select preview port (#17)
30
+ - add file tree breadcrumb (#40)
31
+ - rework ux for deleting chats (#46)
32
+ - navigate away when deleting current chat (#44)
33
+ - add avatar (#47)
34
+ - sanitize user messages (#42)
35
+ - remove authentication (#1)
36
+ - add readme image (#4)
37
+ - add readme image (#4)
38
+ - added sync files to selected local folder function is created. Yarn package manager fixes, styling fixes. Sass module fix. Added Claude model for open router.
39
+ - add ability to enter API keys in the UI
40
+ - added bolt dedicated shell
41
+ - hyperlinked on "Start application" actionto switch to preview in workbench
42
+ - add custom unique filename when doanload as zip
43
+ - add Together AI integration and provider implementation guide
44
+ - better prompt enhancement
45
+ - prompt caching
46
+ - search chats
47
+ - Connections Tabs
48
+
49
+
50
+ #### πŸ› Bug Fixes
51
+
52
+ - buttons after switching to tailwind-compat reset
53
+ - update system prompt
54
+ - do not use path mapping for worker function
55
+ - make file tree scrollable (#14)
56
+ - always parse all assistant messages (#13)
57
+ - issue with generating a new url id every time (#18)
58
+ - use jose for cloudflare compatibility (#20)
59
+ - typo in example prompt
60
+ - adjust system prompt (#32)
61
+ - update dependencies to fix type validation error (#33)
62
+ - user avatar (#51)
63
+ - remove monorepo
64
+ - add issue templates (#2)
65
+ - update repo name
66
+ - rename template
67
+ - rename template
68
+ - add license
69
+ - update README.md (#3)
70
+ - typo
71
+ - remove duplicated bug_report template
72
+ - update links
73
+ - add screen recordings section to bug_report.yml
74
+ - typo
75
+ - remove duplicated bug_report template
76
+ - update links
77
+ - add screen recordings section to bug_report.yml
78
+ - remove logout button (#130)
79
+ - typo in README.md (#117)
80
+ - typo in README.md (#151)
81
+ - typos in CONTRIBUTING.md (#165)
82
+ - don't always show scrollbars (#548)
83
+ - don't always show scrollbars (#548)
84
+ - working
85
+ - Resolved
86
+ - adds missing -t for dockerbuild:prod command in package.json
87
+ - bug #245
88
+ - added scroll fix for file browser
89
+ - global execution queue added
90
+ - enhance prompt "Invalid or missing provider" bad request error
91
+ - prettier issue
92
+ - silent eslint issues
93
+ - add browser environment check for local API calls
94
+ - sidebar scroll always showing up
95
+ - tooltip UI
96
+ - typo in docker-compose.yaml
97
+ - updated ci
98
+ - Added some minor UI fix
99
+ - artifact loop fix
100
+ - clean up
101
+ - small bug
102
+ - correction
103
+ - grammar/typos in system prompt
104
+ - grammar
105
+ - re-capitalize "NEW"
106
+ - dev command
107
+
108
+
109
+ #### πŸ“š Documentation
110
+
111
+ - fix typo in CONTRIBUTING.md (#158)
112
+ - fix typos in README.md (#164)
113
+ - docs added to readme
114
+ - add link to bolt.new issue tracker
115
+ - added socials
116
+
117
+
118
+ #### ♻️ Code Refactoring
119
+
120
+ - workbench store and move logic into action runner (#4)
121
+ - improve history item hover states and interactions
122
+ - settinge menu refactored with useSettings hook
123
+
124
+
125
+ #### βš™οΈ CI
126
+
127
+ - use correct versions (#2)
128
+ - deploy to cloudflare (#19)
129
+ - remove deployment workflow
130
+
131
+
132
+ #### πŸ”§ Chores
133
+
134
+ - make sure that husky hooks are executed
135
+ - update readme
136
+ - update gitignore
137
+ - disable css shorthand to avoid conflicts
138
+ - better clarify readme (#41)
139
+ - update readme
140
+ - create bug report template
141
+ - update readme (#3)
142
+ - update readme
143
+ - create MAIN-FOLDER-README.md
144
+ - update MAIN-FOLDER-README.md
145
+ - rename README.md to CONTRIBUTING.md
146
+ - rename MAIN-FOLDER-README.md to README.md
147
+ - update readme
148
+ - update contributing guide
149
+ - update contributing guide
150
+ - update readme
151
+ - update readme (#7)
152
+ - Add environment variables for OpenAI API Like integration
153
+ - Update environment variable names for OpenAI Like integration
154
+ - Update environment variable names for OpenAI Like integration
155
+ - cleanup logging
156
+ - reverted pnpm package version to match ghaction
157
+ - reverted pnpm lock
158
+ - recreated the lock file
159
+ - ui fix
160
+ - fixed lock file
161
+ - update commit hash to 31e7b48e057d12008a9790810433179bf88b9a32
162
+ - update commit hash to 0a9f04fe3d6001efb863eee7bd2210b5a889e04e
163
+ - update commit hash to 95e38e020cc8a4d865172187fc25c94b39806275
164
+ - update commit hash to 5b6b26bc9ce287e6e351ca443ad0f411d1371a7f
165
+ - update commit hash to 67f63aaf31f406379daa97708d6a1a9f8ac41d43
166
+ - update commit hash to 33d87a1b0eaf5ec36232bb54b3ba9e44e228024d
167
+ - update commit hash to db8c65ec2ba2f28382cb5e792a3f7495fb9a8e03
168
+ - update commit hash to d9ae9d5afbd0310a976fc4c9aee4b9256edef79a
169
+ - update commit hash to 7269c8246f7e89d29a4dd7b446617d66be2bb8da
170
+ - update commit hash to 9758e6c2a00bb9104f4338f67a5757945c69bfa1
171
+ - update commit hash to ac2f42d2d1398f218ec430dd8ba5667011f9d452
172
+ - update commit hash to b4978ca8193afa277f6df0d80e5fbdf787a3524a
173
+ - update commit hash to 5aeb52ae01aee1bc98605f41a0c747ef26dc8739
174
+ - update commit hash to eddf5603c3865536f96774fc3358cf24760fb613
175
+ - update commit hash to 225042bf5ffbf34868cf28ea1091c35a63f76599
176
+ - update commit hash to 1466b6e8777932ce0ab26199126c912373532637
177
+ - update commit hash to 46ad914d1869a7ebb37c67ee68aa7e65333e462f
178
+ - update commit hash to 61a6e133783565ac33fd3e1100a1484debad7c0d
179
+ - update commit hash to 3c71e4e1a1ea6179f0550d3f7628a2f6a75db286
180
+ - update commit hash to 1d5ad998b911dcf7deb3fa34516f73ee46901d1e
181
+ - update commit hash to fa526a643b3529dad86574af5c7ded33388901a2
182
+ - update commit hash to 7d202a4cc737183b29531dcb6336bdb77d899974
183
+ - update commit hash to 62bc87b6f31f5db69cde4874db02739ce8df9ded
184
+ - update commit hash to 154935cdeb054d2cc22dfb0c7e6cf084f02b95d0
185
+ - update commit hash to 7d482ace3d20d62d73107777a51c4ccc375c5969
186
+ - update commit hash to ab08f52aa0b13350cdfe0d0136b668af5e1cd108
187
+ - update commit hash to fd2c17c384a69ab5e7a40113342caa7de405b944
188
+ - update commit hash to c8a7ed9eb02a3626a6e1d591545102765bf762cb
189
+ - update commit hash to f682216515e6e594c6a55cf4520eb67d63939b60
190
+ - update commit hash to 8f3b4cd08249d26b14397e66241b9d099d3eb205
191
+ - update commit hash to 5e1936f5de539324f840305bd94a22260c339511
192
+ - update commit hash to f6329c28c6941fd5c6457a10c209b4b66402e8d5
193
+ - update commit hash to 0b9fd89c7089e98cfc2c17b6fd6ed7cdd6517f1a
194
+ - update commit hash to e7859a34ae64dfac73bbf6fb9e243dc0a7be0a09
195
+ - update commit hash to acd61fea8b6f5c6bbc6d2c7906ac88a6c6aaee5a
196
+ - update commit hash to 4b36601061652ec2ec3cb1f1d5c7cc5649690bbb
197
+ - update commit hash to b0c2f69dca041736f3dd7a8d48df3b5c44fe0948
198
+ - update commit hash to fb1ec72b505a0da0f03a6f1282845844afd7d61c
199
+ - update commit hash to 91ec049b72fcf42d807eb0aa1c8caa01611a4e1d
200
+ - fix workflow permission
201
+ - update commit hash to cbad04f035f017a4797768c75e180f10920c0e17
202
+ - update commit hash to 3f706702b2486e72efe1602e710ccef6c387c82a
203
+ - versioning workflow fix
204
+ - update commit hash to 212ab4a020245c96e3d126c9ef5522d4e9db1edf
205
+ - update commit hash to 0969aacb3533b94887cd63883b30c7fb91d2a957
206
+ - added workflow permission
207
+ - update commit hash to 5c1b4de26a861113ac727b521dfaae07b5f6856b
208
+ - update commit hash to b4104962b7c33202f004bcd05ed75d29c641f014
209
+ - adding workflow
210
+ - update commit hash to 6cb536a9a32e04b4ebc1f3788d6fae06c5bce5ac
211
+
212
+
213
+ #### πŸ” Other Changes
214
+
215
+ - add file tree and hook up editor
216
+ - sync file changes back to webcontainer (#5)
217
+ - enforce consistent import paths (#8)
218
+ - remove settings button
219
+ - add slider to switch between code or preview (#12)
220
+ - adjust system prompt (#24)
221
+ - style sidebar and landing page (#27)
222
+ - hidden file patterns (#31)
223
+ - show tooltip when the editor is read-only (#34)
224
+ - allow to minimize chat (#35)
225
+ - correctly sort file tree (#36)
226
+ - encrypt data and fix renewal (#38)
227
+ - disable eslint
228
+ - Create bug_report.yml
229
+ - Update README.md
230
+ - Update README.md
231
+ - Update README.md
232
+ - Update README.md
233
+ - Update README.md
234
+ - Create MAIN-FOLDER-README.md
235
+ - Update MAIN-FOLDER-README.md
236
+ - Update MAIN-FOLDER-README.md
237
+ - Update MAIN-FOLDER-README.md
238
+ - Rename README.md to CONTRIBUTING.md
239
+ - Rename MAIN-FOLDER-README.md to README.md
240
+ - Update README.md
241
+ - Update CONTRIBUTING.md
242
+ - Update CONTRIBUTING.md
243
+ - Update README.md
244
+ - Update README.md (#7)
245
+ - don't render directly in body
246
+ - Add support for docker dev in bolt
247
+ - Update node version and enable host network
248
+ - don't render directly in body
249
+ - Merge branch 'main' into add-docker-support
250
+ - fix hanging shells (#153)
251
+ - show issue page (#157)
252
+ - fix hanging shells (#159)
253
+ - Merge branch 'main' into add-docker-support
254
+ - Update Dockerfile
255
+ - Add corepack to setup pnpm
256
+ - Added the ability to use practically any LLM you can dream of within Bolt.new
257
+ - Added the OpenRouter provider and a few models from OpenRouter (easily extendable to include more!)
258
+ - Add provider filtering on model list
259
+ - Set default provider from constants
260
+ - added Google Generative AI (gemini) integration
261
+ - use correct issues url (#514)
262
+ - let the ollama models be auto generated from ollama api
263
+ - added download code button
264
+ - Merge pull request #1 from ocodo/main
265
+ - Merge pull request #2 from jonathands/main
266
+ - Merge branch 'main' into main
267
+ - Merge pull request #5 from yunatamos/main
268
+ - Merge pull request #6 from fabwaseem/download-code
269
+ - Fixing up codebase after merging pull requests
270
+ - Updated README with new providers and a running list of features to add to the fork
271
+ - Adding together to the list of integration requests
272
+ - added Google Generative AI (gemini) integration
273
+ - Update README.md
274
+ - Update README.md
275
+ - add planning step + organize shell commands
276
+ - Update prompts.ts
277
+ - Update max_tokens in constants.ts
278
+ - More feature requests!!
279
+ - Merge pull request #7 from ocodo/main
280
+ - Docker Additions
281
+ - Added GitHub push functionality
282
+ - Create github-build-push.yml
283
+ - moved action
284
+ - Update github-build-push.yml
285
+ - Update github-build-push.yml
286
+ - Update github-build-push.yml
287
+ - Update github-build-push.yml
288
+ - Update README.md
289
+ - Merge pull request #28 from kofi-bhr/patch-1
290
+ - Merge pull request #8 from yunatamos/patch-1
291
+ - Merge pull request #1 from coleam00/main
292
+ - add mistral models
293
+ - mistral models added
294
+ - removed pixtral
295
+ - Merge branch 'coleam00:main' into main
296
+ - Update types.ts
297
+ - Update constants.ts
298
+ - Merge branch 'main' into add-docker-support
299
+ - Added deepseek models
300
+ - Merge branch 'main' of https://github.com/zenith110/bolt.new-any-llm
301
+ - Added more instructions for newbs
302
+ - Merge pull request #1 from mayurjobanputra/mayurjobanputra-patch-1
303
+ - Update docker-compose.yml
304
+ - Merge branch 'main' from coleam00 into add-docker-support
305
+ - Merge pull request #1 from ZerxZ/main
306
+ - Enabled boh dev and production docker images. Added convenience scripts and deconflicted start and dockerstart scripts
307
+ - updated ollama to use defined base URL for model calls
308
+ - Adding CONTRIBUTING.md specifically for this fork.
309
+ - Merge branch 'main' into main
310
+ - Merge pull request #11 from kofi-bhr/main
311
+ - Merge pull request #12 from fernsdavid25/patch-1
312
+ - Merge pull request #23 from aaronbolton/main
313
+ - Merge pull request #24 from goncaloalves/main
314
+ - Merge branch 'main' into main
315
+ - Merge pull request #30 from muzafferkadir/main
316
+ - Merge pull request #36 from ArulGandhi/main
317
+ - Merge pull request #44 from TarekS93/main
318
+ - Merge branch 'main' into main
319
+ - Merge pull request #51 from zenith110/main
320
+ - Merge branch 'main' into main
321
+ - Merge pull request #60 from ZerxZ/main
322
+ - Merge branch 'main' into main
323
+ - Merge pull request #64 from noobydp/main
324
+ - Cleanup and fixing Ollama models not showing up after merging changes
325
+ - Updating README with finished implementations and reorder the list of priorities
326
+ - Update constants.ts
327
+ - Enhancing Dockerfile to use a staged build, and docker-compose-yaml to use profiles, either 'development' or 'producion'. Adding nixpacks.toml to enable robust coolify support
328
+ - Corrected nixpacks.toml filename
329
+ - Merge pull request #70 from ArulGandhi/main
330
+ - Corrected nixpacks.toml filename
331
+ - Merge branch 'add-docker-support' of github.com:hillct/bolt.new-any-llm into add-docker-support Just a little cleanup... nixpax.toml is no more. Embedding Coolify config in Dockerfile and docker-compose.yaml
332
+ - Adding hints for Coolify config into docker-compose.yaml
333
+ - Adding full suffix o cocker-compose.yaml for ompatibiliy
334
+ - Merge branch 'main' into add-docker-support
335
+ - Corrected oudated docker build convenience script target
336
+ - Merge branch 'coleam00:main' into main
337
+ - main
338
+ - create .dockerignore file
339
+ - Added Docker Deployment documentation to CONTRIBUTING.md
340
+ - LM Studio Integration
341
+ - Remove Package-lock.json
342
+ - Added DEEPSEEK_API_KEY to .env.example
343
+ - Changed mode.ts to add BaseURL. Thanks @alumbs
344
+ - Merge branch 'coleam00:main' into main
345
+ - More feature requests! Will look at pull requests soon
346
+ - Merge pull request #55 from mayurjobanputra/main
347
+ - Merge pull request #71 from hillct/add-docker-support
348
+ - Fixing up Docker Compose to work with hot reloads in development and environment variables
349
+ - Merge pull request #77 from ajshovon/main
350
+ - Fixing up setup + installation instructions in README
351
+ - Small mention of hot reloading even when running in container
352
+ - Fix createGoogleGenerativeAI arguments
353
+ - Instructions on making Ollama models work well
354
+ - Merge branch 'coleam00:main' into main
355
+ - Update README.md changed .env to .env.local
356
+ - Making Ollama work within the Docker container, very important fix
357
+ - Moved provider and setProvider variables to the higher level component so that it can be accessed in sendMessage. Added provider to message queue in sendMessage. Changed streamText to extract both model and provider.
358
+ - Added sanitization for user messages. Use regex defined in constants.ts instead of redefining.
359
+ - Merge branch 'coleam00:main' into main
360
+ - Added support for xAI Grok Beta
361
+ - Added the XAI_API_KEY variable to the .env.example
362
+ - Merge pull request #196 from milutinke/x-ai
363
+ - Added the latest Sonnet 3.5 and Haiku 3.5
364
+ - Set numCtx = 32768 for Ollama models
365
+ - Merge pull request #209 from patrykwegrzyn/main
366
+ - added code streaming to editor while AI is writing code
367
+ - Show which model name and provider is used in user message.
368
+ - Merge branch 'main' into main
369
+ - Merge branch 'main' into new_bolt1
370
+ - Merge pull request #101 from ali00209/new_bolt1
371
+ - feat(bolt-terminal) bolt terminal integrated with the system
372
+ - Merge branch 'main' into respect-provider-choice
373
+ - Merge pull request #188 from TommyHolmberg/respect-provider-choice
374
+ - Fixing merge conflicts in BaseChat.tsx
375
+ - Noting that API key will still work if set in .env file
376
+ - Merge branch 'coleam00:main' into main
377
+ - Merge pull request #178 from albahrani/patch-1
378
+ - Merge branch 'main' into main
379
+ - Merge branch 'main' into claude-new-sonnet-and-haiku
380
+ - Merge pull request #205 from milutinke/claude-new-sonnet-and-haiku
381
+ - Update README.md
382
+ - Delete github-build-push.yml
383
+ - Merge branch 'main' of https://github.com/aaronbolton/bolt.new-any-llm
384
+ - Merge pull request #242 from aaronbolton/main
385
+ - Merge branch 'coleam00:main' into main
386
+ - @wonderwhy-er suggestion fix pr
387
+ - Merge branch 'main' of https://github.com/karrot0/bolt.new-any-llm
388
+ - Merge pull request #104 from karrot0/main
389
+ - Refactor/standartize model providers, add "get provider key" for those who have it for first time users
390
+ - Merge pull request #254 from ali00209/new_bolt5
391
+ - Merge pull request #247 from JNN5/main
392
+ - Bug fixes
393
+ - Merge pull request #228 from thecodacus/feature--bolt-shell
394
+ - Temporarily removing semantic-pr.yaml in order to verify otherwise ready for review PRs.
395
+ - Merge pull request #261 from chrismahoney/fix/remove-ghaction-titlecheck
396
+ - Merge branch 'main' into code-streaming
397
+ - temporary removed lock file
398
+ - recreated the lock file
399
+ - made types optional and, workbench get repo fix
400
+ - type fix
401
+ - Merge pull request #213 from thecodacus/code-streaming
402
+ - Merge remote-tracking branch 'coleam00/main' into addGetKeyLinks
403
+ - Use cookies instead of request body that is stale sometimes
404
+ - Added dynamic openrouter model list
405
+ - Fix google api key bug
406
+ - Various bug fixes around model/provider selection
407
+ - Merge branch 'coleam00:main' into main
408
+ - TypeCheck fix
409
+ - added rey effects for the UI as decorative elements
410
+ - More type fixes
411
+ - One more fix
412
+ - Update README.md
413
+ - Merge pull request #251 from wonderwhy-er/addGetKeyLinks
414
+ - Merge pull request #285 from cardonasMind/patch-1
415
+ - Merge pull request #158 from dmaksimov/main
416
+ - Removing console log of provider info
417
+ - Fix missing key for React.Fragment in Array map listing
418
+ - Merge pull request #296 from chrismahoney/fix/provider-consolelog
419
+ - Merge pull request #304 from thecodacus/fix-filetree-scroll-fix
420
+ - Merge pull request #118 from armfuls/main
421
+ - Add ability to return to older chat message state
422
+ - clean up unnecesary files
423
+ - excluded the action from execution pipeline
424
+ - .gitignore
425
+ - Add ability to duplicate chat in sidebar
426
+ - Huggingface Models Integrated
427
+ - Add windows start command
428
+ - Fix package.json
429
+ - Should not provide hard-coded OLLAMA_API_BASE_URL value in .env.example
430
+ - Merge pull request #321 from chrismahoney/fix/revert-ollamaurl
431
+ - Added tooltips and fork
432
+ - Show revert and fork only on AI messages
433
+ - Fix lost rewind functionality
434
+ - Lock file
435
+ - Created DEFAULT_NUM_CTX VAR with a deafult of 32768
436
+ - [UX] click shortcut in chat to go to source file in workbench
437
+ - revert spaces
438
+ - image-upload
439
+ - add module lucide-react
440
+ - Delete yarn.lock
441
+ - DEFAULT_NUM_CTX additions
442
+ - Merge pull request #314 from ahsan3219/main
443
+ - Merge pull request #305 from wonderwhy-er/Rewind-to-older-message
444
+ - Update the Google Gemini models list
445
+ - Fix the list of names to include the correct model
446
+ - Merge pull request #309 from thecodacus/fix-project-reload-execution-order
447
+ - Revert useless changes
448
+ - Merge pull request #330 from hgosansn/ux-click-open-file-in-chat
449
+ - Merge remote-tracking branch 'upstream/main'
450
+ - changing based on PR review
451
+ - Merge pull request #338 from kekePower/kekePower/update-google-models
452
+ - update comment to reflect the the codeline
453
+ - use a descriptive anique filename when downloading the files to zip
454
+ - Updating README with new features and a link to our community
455
+ - Merge pull request #347 from SujalXplores/fix/enhance-prompt
456
+ - .gitignore
457
+ - Add background for chat window
458
+ - Cohere support added
459
+ - max token is now dynamically handle for each model
460
+ - Merge pull request #350 from wonderwhy-er/Add-background-for-chat-window
461
+ - Merge branch 'coleam00:main' into main
462
+ - console message removed
463
+ - README.md updated
464
+ - flash fix
465
+ - another theme switch fix
466
+ - removed the background color from rays
467
+ - fixes for PR #332
468
+ - .
469
+ - model pickup
470
+ - Update stream-text.ts dynamic model max Token updated
471
+ - Merge pull request #351 from hasanraiyan/main
472
+ - mobile friendly
473
+ - mobile friendly editor scrollable option buttons
474
+ - Added speech to text capability
475
+ - Clear speech to text, listening upon submission
476
+ - Revert constant change
477
+ - Merge pull request #361 from qwikode/feature/mobile-friendly
478
+ - Limit linting to app
479
+ - Lint-fix all files in app
480
+ - Ignore some stackblitz specific linting rules
481
+ - header gradient and textarea border effect
482
+ - remove commented code
483
+ - picking right model
484
+ - Merge branch 'main' into main
485
+ - Merge pull request #328 from aaronbolton/main
486
+ - Merge remote-tracking branch 'upstream/main'
487
+ - merge with upstream
488
+ - Update to Gemini exp-1121
489
+ - Export chat from sidebar
490
+ - Fix linting issues
491
+ - Make tooltip easier to reuse across the app
492
+ - Added export button
493
+ - Merge remote-tracking branch 'upstream/main' into linting
494
+ - Merge pull request #371 from kekePower/update-google-gemini
495
+ - Merge remote-tracking branch 'upstream/main' into linting
496
+ - Lint and fix recent changes from main
497
+ - Added 3 new models to Huggingface
498
+ - Merge pull request #380 from kekePower/update-huggingface-models
499
+ - Merge remote-tracking branch 'upstream/main' into linting
500
+ - adds Husky 🐢 for pre-commit linting
501
+ - Add information about the linting pre-commit to the contributions guideline
502
+ - Merge pull request #367 from mrsimpson/linting
503
+ - Add import, fix export
504
+ - Merge remote-tracking branch 'coleam00/main' into import-export-individual-chats
505
+ - Lint fixes
506
+ - Type fixes
507
+ - Don't fix linting-issues pre-commit
508
+ - Terminal render too many times causing performance freeze
509
+ - Small change to make review easier
510
+ - Couple of bugfixes
511
+ - adding docs
512
+ - updated
513
+ - Merge pull request #372 from wonderwhy-er/import-export-individual-chats
514
+ - Small-cleanup-of-base-chat-component
515
+ - Proof of concept for folder import
516
+ - Merge pull request #412 from wonderwhy-er/Cleanup-extract-import-button
517
+ - work in progress poc git import
518
+ - Created FAQ at bottom of README
519
+ - Added roadmap to README FAQ
520
+ - Added parsing if ignore file and added handling of binary files
521
+ - Merge remote-tracking branch 'coleam00/main' into Import-folder
522
+ - Merge with master fixes
523
+ - Merge pull request #414 from SujalXplores/fix/eslint-issues
524
+ - Merge pull request #378 from mrsimpson/force-local-linting
525
+ - Merge pull request #411 from SujalXplores/fix/prettier-issue
526
+ - Merge pull request #422 from SujalXplores/feat/improve-sidebar
527
+ - Merge pull request #413 from wonderwhy-er/Import-folder
528
+ - Refinement of folder import
529
+ - shell commands failing on app reload
530
+ - artifact actionlist rendering in chat
531
+ - add prompt caching to README
532
+ - upload new files
533
+ - added faq
534
+ - Merge branch 'main' into docs
535
+ - updated name
536
+ - pipeline fix
537
+ - updated CI name
538
+ - reduced the filesize by over 7x, reduced image size to 1200x600
539
+ - Bump the npm_and_yarn group across 1 directory with 9 updates
540
+ - Merge pull request #456 from oTToDev-CE/dependabot/npm_and_yarn/npm_and_yarn-4762c9dd00
541
+ - Merge pull request #455 from oTToDev-CE/image-size
542
+ - fix
543
+ - Merge branch 'docs'
544
+ - Merge pull request #445 from thecodacus/docs
545
+ - Merge pull request #460 from oTToDev-CE/ollama-model-not-respected
546
+ - Merge pull request #440 from SujalXplores/feat/search-chats
547
+ - Merge branch 'coleam00:main' into main
548
+ - Update action-runner.ts
549
+ - Merge pull request #427 from PuneetP16/fix-app-reload
550
+ - merge with upstream/main
551
+ - adjusting spaces for X button in file-preview
552
+ - Merge pull request #488 from thecodacus/github-action-fix-for-docs
553
+ - Updated README Headings and Ollama Section
554
+ - liniting fix
555
+ - Merge pull request #9 from lassecapel/feat-add-custom-project-name
556
+ - Update constants.ts
557
+ - Update docker-compose.yaml
558
+ - added collapsable chat area
559
+ - Update ExamplePrompts.tsx
560
+ - Merge pull request #11 from PuneetP16/fix-artifact-code-block-rendering
561
+ - Merge pull request #10 from SujalXplores/feat/prompt-caching
562
+ - Update BaseChat.module.scss
563
+ - Update BaseChat.tsx
564
+ - Merge pull request #16 from dustinwloring1988/default-prompt-change
565
+ - Merge pull request #17 from dustinwloring1988/collapsible-model-and-provider
566
+ - Merge pull request #18 from dustinwloring1988/pretty-up
567
+ - Merge pull request #20 from dustinwloring1988/readme-heading-ollama-section
568
+ - Merge pull request #15 from dustinwloring1988/artifact-code-block
569
+ - Merge pull request #19 from dustinwloring1988/unique-name-on-download-zip
570
+ - Merge branch 'stable-additions' into linting-fix
571
+ - Merge pull request #21 from dustinwloring1988/linting-fix
572
+ - lint fix
573
+ - fixed path
574
+ - Merge pull request #22 from dustinwloring1988/stable-additions
575
+ - Merge pull request #23 from dustinwloring1988/prompt-caching
576
+ - Merge branch 'dev' into ui-glow
577
+ - Merge pull request #26 from dustinwloring1988/stable-additions
578
+ - Merge pull request #25 from dustinwloring1988/ui-glow
579
+ - small fixes
580
+ - Update ImportFolderButton.tsx
581
+ - last test fix
582
+ - hotfix
583
+ - hotfix for test and lint done
584
+ - updated packages
585
+ - Merge branch 'stable-additions' into stable-plus-ui-glow
586
+ - Merge pull request #491 from dustinwloring1988/stable-additions
587
+ - Updated features being developed in README
588
+ - Merge branch 'main' into stable-plus-ui-glow
589
+ - Merge pull request #493 from dustinwloring1988/stable-plus-ui-glow
590
+ - added example buttons
591
+ - Merge pull request #1 from dustinwloring1988/example-buttons
592
+ - Merge pull request #2 from hgosansn/main
593
+ - Merge pull request #7 from ibrain-one/feature/307-together-ai-integration
594
+ - improved start
595
+ - fixed typo
596
+ - fixed typo
597
+ - Merge pull request #11 from oTToDev-CE/improve-start
598
+ - Update package.json
599
+ - moved faq to its own page
600
+ - Update README.md
601
+ - Update README.md
602
+ - Merge pull request #21 from oTToDev-CE/readme-faq-mod
603
+ - Update README.md
604
+ - Update FAQ.md
605
+ - Update FAQ.md
606
+ - Updated SCSS to use @use instead of @import via sass-migrator
607
+ - Merge pull request #1 from oTToDev-CE/stable-changes
608
+ - pre commit lint
609
+ - Merge pull request #1 from dustinwloring1988/main
610
+ - Merge pull request #2 from calvinvette/main
611
+ - precommit lint
612
+ - new lint rules
613
+ - prompt enhanchment
614
+ - Merge pull request #2 from oTToDev-CE/main
615
+ - Update .env.example
616
+ - lint rules added and fixed
617
+ - Merge pull request #3 from oTToDev-CE/main
618
+ - added last lint rule for this update
619
+ - Merge pull request #4 from oTToDev-CE/main
620
+ - added the v3_lazyRouteDiscovery flag
621
+ - added artifact bundling for custom long artifacts like uploading folder
622
+ - Merge pull request #498 from dustinwloring1988/main
623
+ - Create main.yml
624
+ - Merge pull request #505 from oTToDev-CE/main
625
+ - Update README.md
626
+ - Merge pull request #506 from dustinwloring1988/doc-addition
627
+ - Update and rename main.yml to stale.yml
628
+ - Merge branch 'main' into docs-added-to-readme
629
+ - Merge remote-tracking branch 'origin/main' into bundle-artifact
630
+ - Merge pull request #508 from thecodacus/docs-added-to-readme
631
+ - Update stale.yml
632
+ - Merge remote-tracking branch 'coleam00/main' into Folder-import-refinement
633
+ - adding to display the image in the chat conversation. and paste image too. tnx to @Stijnus
634
+ - Update linting-failed-message in pre-commit
635
+ - Merge pull request #512 from mrsimpson/fix-lint-failed-message
636
+ - merge with upstream
637
+ - together AI Dynamic Models
638
+ - clean up
639
+ - Merge branch 'main' into github-import
640
+ - adding drag and drop images to text area
641
+ - added context to history
642
+ - fixed test cases
643
+ - added nvm for husky
644
+ - Implement chat description editing in sidebar and header, add visual cue for active chat in sidebar
645
+ - added bundled artifact
646
+ - added cookies storage for git
647
+ - updated pnpm.lock
648
+ - skipped images
649
+ - Merge branch 'main' into feat/improve-prompt-enhancement
650
+ - Merge pull request #428 from SujalXplores/feat/improve-prompt-enhancement
651
+ - Merge pull request #471 from sci3ma/patch-1
652
+ - Merge branch 'main' into fix/ui-gradient
653
+ - Merge pull request #368 from qwikode/fix/ui-gradient
654
+ - removed package lock file as this is not needed for pnpm repo, also added nvm support for husky
655
+ - added nvm support for husky
656
+ - Merge branch 'main' into improve-start-command-on-windows
657
+ - Merge pull request #316 from wonderwhy-er/improve-start-command-on-windows
658
+ - Merge pull request #519 from thecodacus/package-lock-removed
659
+ - Update bug_report.yml
660
+ - Merge pull request #520 from oTToDev-CE/doc/issue-template-update
661
+ - some minor fix
662
+ - fixed action-runner linting
663
+ - Merge pull request #483 from PuneetP16/feat/enhance-chat-description-management
664
+ - Merge branch 'main' into github-import
665
+ - Hardcode url for together ai as fallback if not set in env
666
+ - Hardcode url for together ai as fallback if not set in env
667
+ - Merge pull request #332 from atrokhym/main
668
+ - Updating README now that image attaching is merged, changing order of items in README list.
669
+ - Hardcode url for together ai as fallback if not set in env
670
+ - Split code a little bit
671
+ - lock file
672
+ - Merge pull request #537 from wonderwhy-er/Voice-input-with-fixes
673
+ - Added Fullscreen and Resizing to Preview
674
+ - Lint fix
675
+ - Merge pull request #550 from wonderwhy-er/pr-549
676
+ - Merge branch 'main' into together-ai-dynamic-model-list
677
+ - list fix
678
+ - Merge pull request #513 from thecodacus/together-ai-dynamic-model-list
679
+ - Merge pull request #533 from wonderwhy-er/harcode-together-ai-api-url-as-fallback
680
+ - added spinner
681
+ - Merge pull request #504 from thecodacus/bundle-artifact
682
+ - Merge branch 'main' into github-import
683
+ - Update BaseChat.tsx
684
+ - lint fix
685
+ - artifact bugfix
686
+ - Merge pull request #569 from thecodacus/fix-artifact-bug
687
+ - Merge branch 'main' into github-import
688
+ - fix bundling
689
+ - linting
690
+ - added lock file ignore
691
+ - Merge pull request #571 from thecodacus/artifact-bugfix
692
+ - Update to Gemini exp-1206
693
+ - Merge branch 'main' into github-import
694
+ - Added a tabbed setting modal
695
+ - Merge branch 'coleam00:main' into ui/model-dropdown
696
+ - Merge pull request #421 from thecodacus/github-import
697
+ - Merge branch 'main' into Folder-import-refinement
698
+ - add vue support for codemirror
699
+ - Merge branch 'main' into ui-background-rays
700
+ - background rays and changed the theme color to purple
701
+ - updated theme color
702
+ - Merge pull request #580 from oTToDev-CE/feat/add-tabbed-setting-modal
703
+ - Merge branch 'coleam00:main' into ui/model-dropdown
704
+ - fix position issue
705
+ - typecheck fix
706
+ - Merge pull request #565 from oTToDev-CE/ui/model-dropdown
707
+ - import from url
708
+ - Merge branch 'main' into git-import-from-url
709
+ - Small Style Update
710
+ - Update folderImport.ts
711
+ - Update ImportFolderButton.tsx
712
+ - Changed Colors
713
+ - Merge pull request #426 from wonderwhy-er/Folder-import-refinement
714
+ - Reuse automatic setup commands for git import
715
+ - Update readme
716
+ - Merge pull request #589 from wonderwhy-er/Add-command-detection-to-git-import-flow
717
+ - Merge branch 'main' into git-import-from-url
718
+ - added setup command
719
+ - More styling changes
720
+ - update to styles
721
+ - Merge pull request #585 from thecodacus/git-import-from-url
722
+ - Merge pull request #592 from oTToDev-CE/ui/settings-style
723
+ - Merge pull request #581 from mark-when/vue
724
+ - refactor(SettingWindow):Updated Settings Tab Styling
725
+ - updated padding
726
+ - added backdrop blur
727
+ - delete lock file
728
+ - added lockfile back
729
+ - Merge branch 'main' into update-setting-modal-styles
730
+ - added lock file
731
+ - Merge pull request #600 from thecodacus/update-setting-modal-styles
732
+ - Merge pull request #573 from kekePower/update-gemini-models
733
+ - Update docs
734
+ - Merge pull request #605 from dustinwloring1988/doc/removed-ollama-modelfile-section
735
+ - Update FAQ.md
736
+ - Merge pull request #606 from dustinwloring1988/doc/faq-clean-up
737
+ - removed test connection button
738
+ - fixed toggle not displaying in feature tab
739
+ - moved local models to the experimental features
740
+ - Remembers Settings In Features
741
+ - Merge pull request #610 from oTToDev-CE/ui/features-toggle-fix
742
+ - Merge branch 'main' into ui/add-tab-connections
743
+ - fix formatting error on conflict resolve
744
+ - Merge pull request #607 from oTToDev-CE/ui/add-tab-connections
745
+ - remaining changes
746
+ - Merge branch 'main' into ui-background-rays
747
+ - Merge pull request #282 from thecodacus/ui-background-rays
748
+ - added logo
749
+ - Merge pull request #625 from thecodacus/updated-logo-in-header
750
+ - console error fix due to duplicate keys
751
+ - moved log to only print on change, and changed error logs to warnings
752
+ - lint fix
753
+ - some more logs cleanup
754
+ - Replaced images to match new style
755
+ - Add files via upload
756
+ - Merge pull request #628 from thecodacus/console-error-fox-due-to-duplicate-keys-in-model-selector
757
+ - Add files via upload
758
+ - updated to adapth baseurl setup
759
+ - Merge pull request #629 from oTToDev-CE/doc/images-replace
760
+ - Updating name to Bolt.diy in README
761
+ - Updating git clone url in README.
762
+ - Fixing typo.
763
+ - Merge branch 'main' of https://github.com/stackblitz-labs/bolt.diy
764
+ - Update SettingsWindow.tsx
765
+ - Updating documentation link in README.
766
+ - Merge branch 'main' of https://github.com/stackblitz-labs/bolt.diy
767
+ - Merge pull request #635 from Bolt-CE/main
768
+ - updated docs with new name
769
+ - Changed Docs URL
770
+ - Merge pull request #637 from thecodacus/fix-docs
771
+ - fix Title
772
+ - Merge pull request #639 from thecodacus/fix-docs
773
+ - Merge pull request #638 from Bolt-CE/main
774
+ - merge
775
+ - Merge pull request #645 from wonderwhy-er/pr-620
776
+ - Remove other oTToDev mentions
777
+ - Merge pull request #648 from wonderwhy-er/Remove-ottodev-mentions
778
+ - Add gemini flash 2.0
779
+ - Merge pull request #649 from wonderwhy-er/Add-Gemini-2.0-flash
780
+ - Update prompts.ts
781
+ - Merge pull request #654 from Badbird5907/fix/prompt
782
+ - settings bugfix
783
+ - Merge pull request #662 from thecodacus/settings-bugfix
784
+ - Merge pull request #665 from AriPerkkio/docs/issue-template-link
785
+ - added start message for dev server
786
+ - Merge pull request #668 from thecodacus/terminal-start-log-for-dev-server
787
+ - Merge pull request #682 from thecodacus/bug/prestart-script
788
+ - added default value to true
789
+ - Merge pull request #683 from thecodacus/setting-default-value
790
+ - added verioning system and stable branch
791
+ - imporoved version for versioning system
792
+ - Merge pull request #688 from thecodacus/stable-branch-workflow
793
+ - Merge branch 'main' into chore--fix-versioning-workflow
794
+ - updated flow to use pnpm
795
+ - Merge pull request #689 from thecodacus/chore--fix-versioning-workflow
796
+ - fix the creds issue in workflow
797
+ - Merge pull request #690 from thecodacus/update-stable-workflow
798
+ - Merge pull request #691 from thecodacus/workflow-fix
799
+ - Merge pull request #692 from thecodacus/versioning-workflow
800
+ - updated workflow
801
+ - Merge pull request #695 from thecodacus/fix-versioning
802
+ - Merge pull request #696 from thecodacus/fix/workflow-permission
803
+ - Merge branch 'main' into update-socials
804
+ - Merge pull request #697 from thecodacus/update-socials
805
+ - Merge pull request #701 from thecodacus/auto-versioning #release
806
+ - skipping commit version
807
+
808
+
docs/mkdocs.yml CHANGED
@@ -44,6 +44,14 @@ extra:
44
  - icon: fontawesome/brands/discourse
45
  link: https://thinktank.ottomator.ai/
46
  name: Bolt.diy Discourse
 
 
 
 
 
 
 
 
47
 
48
 
49
  markdown_extensions:
 
44
  - icon: fontawesome/brands/discourse
45
  link: https://thinktank.ottomator.ai/
46
  name: Bolt.diy Discourse
47
+ - icon: fontawesome/brands/x-twitter
48
+ link: https://x.com/bolt_diy
49
+ name: Bolt.diy on X
50
+ - icon: fontawesome/brands/bluesky
51
+ link: https://bsky.app/profile/bolt.diy
52
+ name: Bolt.diy on Bluesky
53
+
54
+
55
 
56
 
57
  markdown_extensions:
package.json CHANGED
@@ -5,10 +5,11 @@
5
  "license": "MIT",
6
  "sideEffects": false,
7
  "type": "module",
 
8
  "scripts": {
9
  "deploy": "npm run build && wrangler pages deploy",
10
  "build": "remix vite:build",
11
- "dev": "remix vite:dev",
12
  "test": "vitest --run",
13
  "test:watch": "vitest",
14
  "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint app",
 
5
  "license": "MIT",
6
  "sideEffects": false,
7
  "type": "module",
8
+ "version": "0.0.1",
9
  "scripts": {
10
  "deploy": "npm run build && wrangler pages deploy",
11
  "build": "remix vite:build",
12
+ "dev": "node pre-start.cjs && remix vite:dev",
13
  "test": "vitest --run",
14
  "test:watch": "vitest",
15
  "lint": "eslint --cache --cache-location ./node_modules/.cache/eslint app",
pre-start.cjs ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ const { commit } = require('./app/commit.json');
2
+
3
+ console.log(`
4
+ β˜…β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β˜…
5
+ B O L T . D I Y
6
+ ⚑️ Welcome ⚑️
7
+ β˜…β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β˜…
8
+ `);
9
+ console.log('πŸ“ Current Commit Version:', commit);
10
+ console.log('β˜…β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β˜…');
public/icons/Anthropic.svg ADDED
public/icons/Cohere.svg ADDED
public/icons/Deepseek.svg ADDED
public/icons/Google.svg ADDED
public/icons/Groq.svg ADDED
public/icons/HuggingFace.svg ADDED
public/icons/LMStudio.svg ADDED
public/icons/Mistral.svg ADDED
public/icons/Ollama.svg ADDED
public/icons/OpenAI.svg ADDED
public/icons/OpenAILike.svg ADDED