codacus commited on
Commit
3c7b125
·
2 Parent(s): dfbbea1 527dc05

Merge branch 'main' into context-optimization

Browse files
Files changed (46) hide show
  1. .github/workflows/commit.yaml +32 -0
  2. .husky/pre-commit +9 -3
  3. README.md +13 -10
  4. app/commit.json +1 -0
  5. app/components/chat/Artifact.tsx +1 -1
  6. app/components/chat/BaseChat.module.scss +0 -76
  7. app/components/chat/BaseChat.tsx +91 -54
  8. app/components/chat/GitCloneButton.tsx +115 -0
  9. app/components/chat/ImportFolderButton.tsx +65 -129
  10. app/components/chat/ModelSelector.tsx +73 -4
  11. app/components/chat/SendButton.client.tsx +8 -3
  12. app/components/chat/chatExportAndImport/ImportButtons.tsx +1 -1
  13. app/components/editor/codemirror/languages.ts +7 -0
  14. app/components/git/GitUrlImport.client.tsx +117 -0
  15. app/components/header/Header.tsx +7 -8
  16. app/components/settings/Settings.module.scss +63 -0
  17. app/components/settings/SettingsWindow.tsx +483 -0
  18. app/components/sidebar/Menu.client.tsx +7 -2
  19. app/components/ui/BackgroundRays/index.tsx +18 -0
  20. app/components/ui/BackgroundRays/styles.module.scss +246 -0
  21. app/components/ui/SettingsButton.tsx +17 -0
  22. app/components/ui/Switch.tsx +37 -0
  23. app/components/workbench/Workbench.client.tsx +13 -11
  24. app/lib/hooks/useGit.ts +287 -0
  25. app/lib/stores/settings.ts +24 -0
  26. app/lib/stores/workbench.ts +9 -8
  27. app/routes/_index.tsx +3 -1
  28. app/routes/git.tsx +23 -0
  29. app/styles/index.scss +10 -0
  30. app/types/model.ts +1 -0
  31. app/utils/constants.ts +9 -6
  32. app/utils/fileUtils.ts +105 -0
  33. app/utils/folderImport.ts +68 -0
  34. app/utils/projectCommands.ts +80 -0
  35. docs/docs/FAQ.md +57 -28
  36. docs/docs/index.md +0 -25
  37. package.json +5 -1
  38. pnpm-lock.yaml +438 -270
  39. public/favicon.svg +1 -1
  40. public/logo-dark-styled.png +0 -0
  41. public/logo-dark.png +0 -0
  42. public/logo-light-styled.png +0 -0
  43. public/logo-light.png +0 -0
  44. public/logo.svg +15 -1
  45. public/social_preview_index.jpg +0 -0
  46. uno.config.ts +11 -11
.github/workflows/commit.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Update Commit Hash File
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ update-commit:
13
+ runs-on: ubuntu-latest
14
+
15
+ steps:
16
+ - name: Checkout the code
17
+ uses: actions/checkout@v3
18
+
19
+ - name: Get the latest commit hash
20
+ run: echo "COMMIT_HASH=$(git rev-parse HEAD)" >> $GITHUB_ENV
21
+
22
+ - name: Update commit file
23
+ run: |
24
+ echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json
25
+
26
+ - name: Commit and push the update
27
+ run: |
28
+ git config --global user.name "github-actions[bot]"
29
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
30
+ git add app/commit.json
31
+ git commit -m "chore: update commit hash to $COMMIT_HASH"
32
+ git push
.husky/pre-commit CHANGED
@@ -5,15 +5,21 @@ echo "🔍 Running pre-commit hook to check the code looks good... 🔍"
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
  if ! pnpm typecheck; then
9
- echo "❌ Type checking failed! Please review TypeScript types."
10
- echo "Once you're done, don't forget to add your changes to the commit! 🚀"
11
- exit 1
 
12
  fi
13
 
 
14
  if ! pnpm lint; then
15
  echo "❌ Linting failed! 'pnpm lint:fix' will help you fix the easy ones."
16
  echo "Once you're done, don't forget to add your beautification to the commit! 🤩"
 
17
  exit 1
18
  fi
19
 
 
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
 
README.md CHANGED
@@ -1,12 +1,14 @@
1
  [![Bolt.new: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.new)
2
 
3
- # Bolt.new Fork by Cole Medin - oTToDev
4
 
5
- This fork of Bolt.new (oTToDev) allows you to choose the LLM that you use for each prompt! Currently, you can use OpenAI, Anthropic, Ollama, OpenRouter, Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, or Groq models - and it is easily extended to use any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models.
6
 
7
- Check the [oTToDev Docs](https://coleam00.github.io/bolt.new-any-llm/) for more information.
8
 
9
- ## Join the community for oTToDev!
 
 
10
 
11
  https://thinktank.ottomator.ai
12
 
@@ -41,6 +43,7 @@ https://thinktank.ottomator.ai
41
  - ✅ Mobile friendly (@qwikode)
42
  - ✅ Better prompt enhancing (@SujalXplores)
43
  - ✅ Attach images to prompts (@atrokhym)
 
44
  - ⬜ **HIGH PRIORITY** - Prevent Bolt from rewriting files as often (file locking and diffs)
45
  - ⬜ **HIGH PRIORITY** - Better prompting for smaller LLMs (code window sometimes doesn't start)
46
  - ⬜ **HIGH PRIORITY** - Run agents in the backend as opposed to a single model call
@@ -55,7 +58,7 @@ https://thinktank.ottomator.ai
55
 
56
  ## Bolt.new: AI-Powered Full-Stack Web Development in the Browser
57
 
58
- Bolt.new 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)
59
 
60
  ## What Makes Bolt.new Different
61
 
@@ -95,7 +98,7 @@ If you see usr/local/bin in the output then you're good to go.
95
  3. Clone the repository (if you haven't already) by opening a Terminal window (or CMD with admin permissions) and then typing in this:
96
 
97
  ```
98
- git clone https://github.com/coleam00/bolt.new-any-llm.git
99
  ```
100
 
101
  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.
@@ -224,11 +227,11 @@ pnpm run dev
224
 
225
  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.
226
 
227
- ## How do I contribute to oTToDev?
228
 
229
- [Please check out our dedicated page for contributing to oTToDev here!](CONTRIBUTING.md)
230
 
231
- ## What are the future plans for oTToDev?
232
 
233
  [Check out our Roadmap here!](https://roadmap.sh/r/ottodev-roadmap-2ovzo)
234
 
@@ -236,4 +239,4 @@ Lot more updates to this roadmap coming soon!
236
 
237
  ## FAQ
238
 
239
- [Please check out our dedicated page for FAQ's related to oTToDev here!](FAQ.md)
 
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
 
5
+ Welcome to Bolt.diy, the official open source version of Bolt.new (previously known as oTToDev and Bolt.new ANY LLM), which allows you to choose the LLM that you use for each prompt! Currently, you can use OpenAI, Anthropic, Ollama, OpenRouter, Gemini, LMStudio, Mistral, xAI, HuggingFace, DeepSeek, or Groq models - and it is easily extended to use any other model supported by the Vercel AI SDK! See the instructions below for running this locally and extending it to include more models.
6
 
7
+ Check the [Bolt.diy Docs](https://stackblitz-labs.github.io/bolt.diy/) for more information. This documentation is still being updated after the transfer.
8
 
9
+ Bolt.diy was originally started by [Cole Medin](https://www.youtube.com/@ColeMedin) but has quickly grown into a massive community effort to build the BEST open source AI coding assistant!
10
+
11
+ ## Join the community for Bolt.diy!
12
 
13
  https://thinktank.ottomator.ai
14
 
 
43
  - ✅ Mobile friendly (@qwikode)
44
  - ✅ Better prompt enhancing (@SujalXplores)
45
  - ✅ Attach images to prompts (@atrokhym)
46
+ - ✅ Detect package.json and commands to auto install and run preview for folder and git import (@wonderwhy-er)
47
  - ⬜ **HIGH PRIORITY** - Prevent Bolt from rewriting files as often (file locking and diffs)
48
  - ⬜ **HIGH PRIORITY** - Better prompting for smaller LLMs (code window sometimes doesn't start)
49
  - ⬜ **HIGH PRIORITY** - Run agents in the backend as opposed to a single model call
 
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
 
 
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.
 
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
 
232
+ [Please check out our dedicated page for contributing to Bolt.diy here!](CONTRIBUTING.md)
233
 
234
+ ## What are the future plans for Bolt.diy?
235
 
236
  [Check out our Roadmap here!](https://roadmap.sh/r/ottodev-roadmap-2ovzo)
237
 
 
239
 
240
  ## FAQ
241
 
242
+ [Please check out our dedicated page for FAQ's related to Bolt.diy here!](FAQ.md)
app/commit.json ADDED
@@ -0,0 +1 @@
 
 
1
+ { "commit": "154935cdeb054d2cc22dfb0c7e6cf084f02b95d0" }
app/components/chat/Artifact.tsx CHANGED
@@ -52,7 +52,7 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => {
52
  if (actions.length !== 0 && artifact.type === 'bundled') {
53
  const finished = !actions.find((action) => action.status !== 'complete');
54
 
55
- if (finished != allActionFinished) {
56
  setAllActionFinished(finished);
57
  }
58
  }
 
52
  if (actions.length !== 0 && artifact.type === 'bundled') {
53
  const finished = !actions.find((action) => action.status !== 'complete');
54
 
55
+ if (allActionFinished !== finished) {
56
  setAllActionFinished(finished);
57
  }
58
  }
app/components/chat/BaseChat.module.scss CHANGED
@@ -18,82 +18,6 @@
18
  opacity: 1;
19
  }
20
 
21
- .RayContainer {
22
- --gradient-opacity: 0.85;
23
- --ray-gradient: radial-gradient(rgba(83, 196, 255, var(--gradient-opacity)) 0%, rgba(43, 166, 255, 0) 100%);
24
- transition: opacity 0.25s linear;
25
- position: fixed;
26
- inset: 0;
27
- pointer-events: none;
28
- user-select: none;
29
- }
30
-
31
- .LightRayOne {
32
- width: 480px;
33
- height: 680px;
34
- transform: rotate(80deg);
35
- top: -540px;
36
- left: 250px;
37
- filter: blur(110px);
38
- position: absolute;
39
- border-radius: 100%;
40
- background: var(--ray-gradient);
41
- }
42
-
43
- .LightRayTwo {
44
- width: 110px;
45
- height: 400px;
46
- transform: rotate(-20deg);
47
- top: -280px;
48
- left: 350px;
49
- mix-blend-mode: overlay;
50
- opacity: 0.6;
51
- filter: blur(60px);
52
- position: absolute;
53
- border-radius: 100%;
54
- background: var(--ray-gradient);
55
- }
56
-
57
- .LightRayThree {
58
- width: 400px;
59
- height: 370px;
60
- top: -350px;
61
- left: 200px;
62
- mix-blend-mode: overlay;
63
- opacity: 0.6;
64
- filter: blur(21px);
65
- position: absolute;
66
- border-radius: 100%;
67
- background: var(--ray-gradient);
68
- }
69
-
70
- .LightRayFour {
71
- position: absolute;
72
- width: 330px;
73
- height: 370px;
74
- top: -330px;
75
- left: 50px;
76
- mix-blend-mode: overlay;
77
- opacity: 0.5;
78
- filter: blur(21px);
79
- border-radius: 100%;
80
- background: var(--ray-gradient);
81
- }
82
-
83
- .LightRayFive {
84
- position: absolute;
85
- width: 110px;
86
- height: 400px;
87
- transform: rotate(-40deg);
88
- top: -280px;
89
- left: -10px;
90
- mix-blend-mode: overlay;
91
- opacity: 0.8;
92
- filter: blur(60px);
93
- border-radius: 100%;
94
- background: var(--ray-gradient);
95
- }
96
-
97
  .PromptEffectContainer {
98
  --prompt-container-offset: 50px;
99
  --prompt-line-stroke-width: 1px;
 
18
  opacity: 1;
19
  }
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  .PromptEffectContainer {
22
  --prompt-container-offset: 50px;
23
  --prompt-line-stroke-width: 1px;
app/components/chat/BaseChat.tsx CHANGED
@@ -21,6 +21,7 @@ import type { ProviderInfo } from '~/utils/types';
21
  import { ExportChatButton } from '~/components/chat/chatExportAndImport/ExportChatButton';
22
  import { ImportButtons } from '~/components/chat/chatExportAndImport/ImportButtons';
23
  import { ExamplePrompts } from '~/components/chat/ExamplePrompts';
 
24
 
25
  import FilePreview from './FilePreview';
26
  import { ModelSelector } from '~/components/chat/ModelSelector';
@@ -87,14 +88,68 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
87
  ref,
88
  ) => {
89
  const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200;
90
- const [apiKeys, setApiKeys] = useState<Record<string, string>>({});
 
 
 
 
 
 
 
 
 
 
 
 
 
91
  const [modelList, setModelList] = useState(MODEL_LIST);
92
  const [isModelSettingsCollapsed, setIsModelSettingsCollapsed] = useState(false);
93
  const [isListening, setIsListening] = useState(false);
94
  const [recognition, setRecognition] = useState<SpeechRecognition | null>(null);
95
  const [transcript, setTranscript] = useState('');
96
 
97
- console.log(transcript);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  useEffect(() => {
99
  // Load API keys from cookies on component mount
100
  try {
@@ -183,23 +238,6 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
183
  }
184
  };
185
 
186
- const updateApiKey = (provider: string, key: string) => {
187
- try {
188
- const updatedApiKeys = { ...apiKeys, [provider]: key };
189
- setApiKeys(updatedApiKeys);
190
-
191
- // Save updated API keys to cookies with 30 day expiry and secure settings
192
- Cookies.set('apiKeys', JSON.stringify(updatedApiKeys), {
193
- expires: 30, // 30 days
194
- secure: true, // Only send over HTTPS
195
- sameSite: 'strict', // Protect against CSRF
196
- path: '/', // Accessible across the site
197
- });
198
- } catch (error) {
199
- console.error('Error saving API keys to cookies:', error);
200
- }
201
- };
202
-
203
  const handleFileUpload = () => {
204
  const input = document.createElement('input');
205
  input.type = 'file';
@@ -255,19 +293,9 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
255
  const baseChat = (
256
  <div
257
  ref={ref}
258
- className={classNames(
259
- styles.BaseChat,
260
- 'relative flex flex-col lg:flex-row h-full w-full overflow-hidden bg-bolt-elements-background-depth-1',
261
- )}
262
  data-chat-visible={showChat}
263
  >
264
- <div className={classNames(styles.RayContainer)}>
265
- <div className={classNames(styles.LightRayOne)}></div>
266
- <div className={classNames(styles.LightRayTwo)}></div>
267
- <div className={classNames(styles.LightRayThree)}></div>
268
- <div className={classNames(styles.LightRayFour)}></div>
269
- <div className={classNames(styles.LightRayFive)}></div>
270
- </div>
271
  <ClientOnly>{() => <Menu />}</ClientOnly>
272
  <div ref={scrollRef} className="flex flex-col lg:flex-row overflow-y-auto w-full h-full">
273
  <div className={classNames(styles.Chat, 'flex flex-col flex-grow lg:min-w-[var(--chat-min-width)] h-full')}>
@@ -317,15 +345,15 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
317
  gradientUnits="userSpaceOnUse"
318
  gradientTransform="rotate(-45)"
319
  >
320
- <stop offset="0%" stopColor="#1488fc" stopOpacity="0%"></stop>
321
- <stop offset="40%" stopColor="#1488fc" stopOpacity="80%"></stop>
322
- <stop offset="50%" stopColor="#1488fc" stopOpacity="80%"></stop>
323
- <stop offset="100%" stopColor="#1488fc" stopOpacity="0%"></stop>
324
  </linearGradient>
325
  <linearGradient id="shine-gradient">
326
  <stop offset="0%" stopColor="white" stopOpacity="0%"></stop>
327
- <stop offset="40%" stopColor="#8adaff" stopOpacity="80%"></stop>
328
- <stop offset="50%" stopColor="#8adaff" stopOpacity="80%"></stop>
329
  <stop offset="100%" stopColor="white" stopOpacity="0%"></stop>
330
  </linearGradient>
331
  </defs>
@@ -333,21 +361,6 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
333
  <rect className={classNames(styles.PromptShine)} x="48" y="24" width="70" height="1"></rect>
334
  </svg>
335
  <div>
336
- <div className="flex justify-between items-center mb-2">
337
- <button
338
- onClick={() => setIsModelSettingsCollapsed(!isModelSettingsCollapsed)}
339
- className={classNames('flex items-center gap-2 p-2 rounded-lg transition-all', {
340
- 'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent':
341
- isModelSettingsCollapsed,
342
- 'bg-bolt-elements-item-backgroundDefault text-bolt-elements-item-contentDefault':
343
- !isModelSettingsCollapsed,
344
- })}
345
- >
346
- <div className={`i-ph:caret-${isModelSettingsCollapsed ? 'right' : 'down'} text-lg`} />
347
- <span>Model Settings</span>
348
- </button>
349
- </div>
350
-
351
  <div className={isModelSettingsCollapsed ? 'hidden' : ''}>
352
  <ModelSelector
353
  key={provider?.name + ':' + modelList.length}
@@ -359,11 +372,15 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
359
  providerList={PROVIDER_LIST}
360
  apiKeys={apiKeys}
361
  />
362
- {provider && (
363
  <APIKeyManager
364
  provider={provider}
365
  apiKey={apiKeys[provider.name] || ''}
366
- setApiKey={(key) => updateApiKey(provider.name, key)}
 
 
 
 
367
  />
368
  )}
369
  </div>
@@ -451,6 +468,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
451
  <SendButton
452
  show={input.length > 0 || isStreaming || uploadedFiles.length > 0}
453
  isStreaming={isStreaming}
 
454
  onClick={(event) => {
455
  if (isStreaming) {
456
  handleStop?.();
@@ -501,6 +519,20 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
501
  disabled={isStreaming}
502
  />
503
  {chatStarted && <ClientOnly>{() => <ExportChatButton exportChat={exportChat} />}</ClientOnly>}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
504
  </div>
505
  {input.length > 3 ? (
506
  <div className="text-xs text-bolt-elements-textTertiary">
@@ -513,7 +545,12 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
513
  </div>
514
  </div>
515
  </div>
516
- {!chatStarted && ImportButtons(importChat)}
 
 
 
 
 
517
  {!chatStarted &&
518
  ExamplePrompts((event, messageInput) => {
519
  if (isStreaming) {
 
21
  import { ExportChatButton } from '~/components/chat/chatExportAndImport/ExportChatButton';
22
  import { ImportButtons } from '~/components/chat/chatExportAndImport/ImportButtons';
23
  import { ExamplePrompts } from '~/components/chat/ExamplePrompts';
24
+ import GitCloneButton from './GitCloneButton';
25
 
26
  import FilePreview from './FilePreview';
27
  import { ModelSelector } from '~/components/chat/ModelSelector';
 
88
  ref,
89
  ) => {
90
  const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200;
91
+ const [apiKeys, setApiKeys] = useState<Record<string, string>>(() => {
92
+ const savedKeys = Cookies.get('apiKeys');
93
+
94
+ if (savedKeys) {
95
+ try {
96
+ return JSON.parse(savedKeys);
97
+ } catch (error) {
98
+ console.error('Failed to parse API keys from cookies:', error);
99
+ return {};
100
+ }
101
+ }
102
+
103
+ return {};
104
+ });
105
  const [modelList, setModelList] = useState(MODEL_LIST);
106
  const [isModelSettingsCollapsed, setIsModelSettingsCollapsed] = useState(false);
107
  const [isListening, setIsListening] = useState(false);
108
  const [recognition, setRecognition] = useState<SpeechRecognition | null>(null);
109
  const [transcript, setTranscript] = useState('');
110
 
111
+ // Load enabled providers from cookies
112
+ const [enabledProviders, setEnabledProviders] = useState(() => {
113
+ const savedProviders = Cookies.get('providers');
114
+
115
+ if (savedProviders) {
116
+ try {
117
+ const parsedProviders = JSON.parse(savedProviders);
118
+ return PROVIDER_LIST.filter((p) => parsedProviders[p.name]);
119
+ } catch (error) {
120
+ console.error('Failed to parse providers from cookies:', error);
121
+ return PROVIDER_LIST;
122
+ }
123
+ }
124
+
125
+ return PROVIDER_LIST;
126
+ });
127
+
128
+ // Update enabled providers when cookies change
129
+ useEffect(() => {
130
+ const updateProvidersFromCookies = () => {
131
+ const savedProviders = Cookies.get('providers');
132
+
133
+ if (savedProviders) {
134
+ try {
135
+ const parsedProviders = JSON.parse(savedProviders);
136
+ setEnabledProviders(PROVIDER_LIST.filter((p) => parsedProviders[p.name]));
137
+ } catch (error) {
138
+ console.error('Failed to parse providers from cookies:', error);
139
+ }
140
+ }
141
+ };
142
+
143
+ updateProvidersFromCookies();
144
+
145
+ const interval = setInterval(updateProvidersFromCookies, 1000);
146
+
147
+ return () => clearInterval(interval);
148
+ }, [PROVIDER_LIST]);
149
+
150
+ useEffect(() => {
151
+ console.log(transcript);
152
+ }, [transcript]);
153
  useEffect(() => {
154
  // Load API keys from cookies on component mount
155
  try {
 
238
  }
239
  };
240
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
241
  const handleFileUpload = () => {
242
  const input = document.createElement('input');
243
  input.type = 'file';
 
293
  const baseChat = (
294
  <div
295
  ref={ref}
296
+ className={classNames(styles.BaseChat, 'relative flex h-full w-full overflow-hidden')}
 
 
 
297
  data-chat-visible={showChat}
298
  >
 
 
 
 
 
 
 
299
  <ClientOnly>{() => <Menu />}</ClientOnly>
300
  <div ref={scrollRef} className="flex flex-col lg:flex-row overflow-y-auto w-full h-full">
301
  <div className={classNames(styles.Chat, 'flex flex-col flex-grow lg:min-w-[var(--chat-min-width)] h-full')}>
 
345
  gradientUnits="userSpaceOnUse"
346
  gradientTransform="rotate(-45)"
347
  >
348
+ <stop offset="0%" stopColor="#b44aff" stopOpacity="0%"></stop>
349
+ <stop offset="40%" stopColor="#b44aff" stopOpacity="80%"></stop>
350
+ <stop offset="50%" stopColor="#b44aff" stopOpacity="80%"></stop>
351
+ <stop offset="100%" stopColor="#b44aff" stopOpacity="0%"></stop>
352
  </linearGradient>
353
  <linearGradient id="shine-gradient">
354
  <stop offset="0%" stopColor="white" stopOpacity="0%"></stop>
355
+ <stop offset="40%" stopColor="#ffffff" stopOpacity="80%"></stop>
356
+ <stop offset="50%" stopColor="#ffffff" stopOpacity="80%"></stop>
357
  <stop offset="100%" stopColor="white" stopOpacity="0%"></stop>
358
  </linearGradient>
359
  </defs>
 
361
  <rect className={classNames(styles.PromptShine)} x="48" y="24" width="70" height="1"></rect>
362
  </svg>
363
  <div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  <div className={isModelSettingsCollapsed ? 'hidden' : ''}>
365
  <ModelSelector
366
  key={provider?.name + ':' + modelList.length}
 
372
  providerList={PROVIDER_LIST}
373
  apiKeys={apiKeys}
374
  />
375
+ {enabledProviders.length > 0 && provider && (
376
  <APIKeyManager
377
  provider={provider}
378
  apiKey={apiKeys[provider.name] || ''}
379
+ setApiKey={(key) => {
380
+ const newApiKeys = { ...apiKeys, [provider.name]: key };
381
+ setApiKeys(newApiKeys);
382
+ Cookies.set('apiKeys', JSON.stringify(newApiKeys));
383
+ }}
384
  />
385
  )}
386
  </div>
 
468
  <SendButton
469
  show={input.length > 0 || isStreaming || uploadedFiles.length > 0}
470
  isStreaming={isStreaming}
471
+ disabled={enabledProviders.length === 0}
472
  onClick={(event) => {
473
  if (isStreaming) {
474
  handleStop?.();
 
519
  disabled={isStreaming}
520
  />
521
  {chatStarted && <ClientOnly>{() => <ExportChatButton exportChat={exportChat} />}</ClientOnly>}
522
+ <IconButton
523
+ title="Model Settings"
524
+ className={classNames('transition-all flex items-center gap-1', {
525
+ 'bg-bolt-elements-item-backgroundAccent text-bolt-elements-item-contentAccent':
526
+ isModelSettingsCollapsed,
527
+ 'bg-bolt-elements-item-backgroundDefault text-bolt-elements-item-contentDefault':
528
+ !isModelSettingsCollapsed,
529
+ })}
530
+ onClick={() => setIsModelSettingsCollapsed(!isModelSettingsCollapsed)}
531
+ disabled={enabledProviders.length === 0}
532
+ >
533
+ <div className={`i-ph:caret-${isModelSettingsCollapsed ? 'right' : 'down'} text-lg`} />
534
+ {isModelSettingsCollapsed ? <span className="text-xs">{model}</span> : <span />}
535
+ </IconButton>
536
  </div>
537
  {input.length > 3 ? (
538
  <div className="text-xs text-bolt-elements-textTertiary">
 
545
  </div>
546
  </div>
547
  </div>
548
+ {!chatStarted && (
549
+ <div className="flex justify-center gap-2">
550
+ {ImportButtons(importChat)}
551
+ <GitCloneButton importChat={importChat} />
552
+ </div>
553
+ )}
554
  {!chatStarted &&
555
  ExamplePrompts((event, messageInput) => {
556
  if (isStreaming) {
app/components/chat/GitCloneButton.tsx ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
8
+ const IGNORE_PATTERNS = [
9
+ 'node_modules/**',
10
+ '.git/**',
11
+ '.github/**',
12
+ '.vscode/**',
13
+ '**/*.jpg',
14
+ '**/*.jpeg',
15
+ '**/*.png',
16
+ 'dist/**',
17
+ 'build/**',
18
+ '.next/**',
19
+ 'coverage/**',
20
+ '.cache/**',
21
+ '.vscode/**',
22
+ '.idea/**',
23
+ '**/*.log',
24
+ '**/.DS_Store',
25
+ '**/npm-debug.log*',
26
+ '**/yarn-debug.log*',
27
+ '**/yarn-error.log*',
28
+ '**/*lock.json',
29
+ '**/*lock.yaml',
30
+ ];
31
+
32
+ const ig = ignore().add(IGNORE_PATTERNS);
33
+
34
+ interface GitCloneButtonProps {
35
+ className?: string;
36
+ importChat?: (description: string, messages: Message[]) => Promise<void>;
37
+ }
38
+
39
+ export default function GitCloneButton({ importChat }: GitCloneButtonProps) {
40
+ const { ready, gitClone } = useGit();
41
+ const onClick = async (_e: any) => {
42
+ if (!ready) {
43
+ return;
44
+ }
45
+
46
+ const repoUrl = prompt('Enter the Git url');
47
+
48
+ if (repoUrl) {
49
+ const { workdir, data } = await gitClone(repoUrl);
50
+
51
+ if (importChat) {
52
+ const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
53
+ console.log(filePaths);
54
+
55
+ const textDecoder = new TextDecoder('utf-8');
56
+
57
+ // Convert files to common format for command detection
58
+ const fileContents = filePaths
59
+ .map((filePath) => {
60
+ const { data: content, encoding } = data[filePath];
61
+ return {
62
+ path: filePath,
63
+ content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
64
+ };
65
+ })
66
+ .filter((f) => f.content);
67
+
68
+ // Detect and create commands message
69
+ const commands = await detectProjectCommands(fileContents);
70
+ const commandsMessage = createCommandsMessage(commands);
71
+
72
+ // Create files message
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) =>
80
+ `<boltAction type="file" filePath="${file.path}">
81
+ ${file.content}
82
+ </boltAction>`,
83
+ )
84
+ .join('\n')}
85
+ </boltArtifact>`,
86
+ id: generateId(),
87
+ createdAt: new Date(),
88
+ };
89
+
90
+ const messages = [filesMessage];
91
+
92
+ if (commandsMessage) {
93
+ messages.push(commandsMessage);
94
+ }
95
+
96
+ await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
97
+ }
98
+ }
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
+ }
app/components/chat/ImportFolderButton.tsx CHANGED
@@ -1,102 +1,75 @@
1
- import React from 'react';
2
  import type { Message } from 'ai';
3
  import { toast } from 'react-toastify';
4
- import ignore from 'ignore';
 
5
 
6
  interface ImportFolderButtonProps {
7
  className?: string;
8
  importChat?: (description: string, messages: Message[]) => Promise<void>;
9
  }
10
 
11
- // Common patterns to ignore, similar to .gitignore
12
- const IGNORE_PATTERNS = [
13
- 'node_modules/**',
14
- '.git/**',
15
- 'dist/**',
16
- 'build/**',
17
- '.next/**',
18
- 'coverage/**',
19
- '.cache/**',
20
- '.vscode/**',
21
- '.idea/**',
22
- '**/*.log',
23
- '**/.DS_Store',
24
- '**/npm-debug.log*',
25
- '**/yarn-debug.log*',
26
- '**/yarn-error.log*',
27
- ];
28
-
29
- const ig = ignore().add(IGNORE_PATTERNS);
30
- const generateId = () => Math.random().toString(36).substring(2, 15);
31
-
32
- const isBinaryFile = async (file: File): Promise<boolean> => {
33
- const chunkSize = 1024; // Read the first 1 KB of the file
34
- const buffer = new Uint8Array(await file.slice(0, chunkSize).arrayBuffer());
35
-
36
- for (let i = 0; i < buffer.length; i++) {
37
- const byte = buffer[i];
38
-
39
- if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) {
40
- return true; // Found a binary character
41
- }
42
- }
43
 
44
- return false;
45
- };
46
 
47
- export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ className, importChat }) => {
48
- const shouldIncludeFile = (path: string): boolean => {
49
- return !ig.ignores(path);
50
- };
 
 
51
 
52
- const createChatFromFolder = async (files: File[], binaryFiles: string[]) => {
53
- const fileArtifacts = await Promise.all(
54
- files.map(async (file) => {
55
- return new Promise<string>((resolve, reject) => {
56
- const reader = new FileReader();
57
-
58
- reader.onload = () => {
59
- const content = reader.result as string;
60
- const relativePath = file.webkitRelativePath.split('/').slice(1).join('/');
61
- resolve(
62
- `<boltAction type="file" filePath="${relativePath}">
63
- ${content}
64
- </boltAction>`,
65
- );
66
- };
67
- reader.onerror = reject;
68
- reader.readAsText(file);
69
- });
70
- }),
71
- );
72
-
73
- const binaryFilesMessage =
74
- binaryFiles.length > 0
75
- ? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}`
76
- : '';
77
-
78
- const message: Message = {
79
- role: 'assistant',
80
- content: `I'll help you set up these files.${binaryFilesMessage}
81
-
82
- <boltArtifact id="imported-files" title="Imported Files" type="bundled">
83
- ${fileArtifacts.join('\n\n')}
84
- </boltArtifact>`,
85
- id: generateId(),
86
- createdAt: new Date(),
87
- };
88
-
89
- const userMessage: Message = {
90
- role: 'user',
91
- id: generateId(),
92
- content: 'Import my files',
93
- createdAt: new Date(),
94
- };
95
-
96
- const description = `Folder Import: ${files[0].webkitRelativePath.split('/')[0]}`;
97
-
98
- if (importChat) {
99
- await importChat(description, [userMessage, message]);
100
  }
101
  };
102
 
@@ -108,46 +81,8 @@ ${fileArtifacts.join('\n\n')}
108
  className="hidden"
109
  webkitdirectory=""
110
  directory=""
111
- onChange={async (e) => {
112
- const allFiles = Array.from(e.target.files || []);
113
- const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath));
114
-
115
- if (filteredFiles.length === 0) {
116
- toast.error('No files found in the selected folder');
117
- return;
118
- }
119
-
120
- try {
121
- const fileChecks = await Promise.all(
122
- filteredFiles.map(async (file) => ({
123
- file,
124
- isBinary: await isBinaryFile(file),
125
- })),
126
- );
127
-
128
- const textFiles = fileChecks.filter((f) => !f.isBinary).map((f) => f.file);
129
- const binaryFilePaths = fileChecks
130
- .filter((f) => f.isBinary)
131
- .map((f) => f.file.webkitRelativePath.split('/').slice(1).join('/'));
132
-
133
- if (textFiles.length === 0) {
134
- toast.error('No text files found in the selected folder');
135
- return;
136
- }
137
-
138
- if (binaryFilePaths.length > 0) {
139
- toast.info(`Skipping ${binaryFilePaths.length} binary files`);
140
- }
141
-
142
- await createChatFromFolder(textFiles, binaryFilePaths);
143
- } catch (error) {
144
- console.error('Failed to import folder:', error);
145
- toast.error('Failed to import folder');
146
- }
147
-
148
- e.target.value = ''; // Reset file input
149
- }}
150
- {...({} as any)} // if removed webkitdirectory will throw errors as unknow attribute
151
  />
152
  <button
153
  onClick={() => {
@@ -155,9 +90,10 @@ ${fileArtifacts.join('\n\n')}
155
  input?.click();
156
  }}
157
  className={className}
 
158
  >
159
  <div className="i-ph:upload-simple" />
160
- Import Folder
161
  </button>
162
  </>
163
  );
 
1
+ import React, { useState } from 'react';
2
  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;
9
  importChat?: (description: string, messages: Message[]) => Promise<void>;
10
  }
11
 
12
+ export const ImportFolderButton: React.FC<ImportFolderButtonProps> = ({ className, importChat }) => {
13
+ const [isLoading, setIsLoading] = useState(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
+ const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
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
 
25
+ const folderName = allFiles[0]?.webkitRelativePath.split('/')[0] || 'Unknown Folder';
26
+ setIsLoading(true);
27
+
28
+ const loadingToast = toast.loading(`Importing ${folderName}...`);
29
+
30
+ try {
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
+
38
+ const fileChecks = await Promise.all(
39
+ filteredFiles.map(async (file) => ({
40
+ file,
41
+ isBinary: await isBinaryFile(file),
42
+ })),
43
+ );
44
+
45
+ const textFiles = fileChecks.filter((f) => !f.isBinary).map((f) => f.file);
46
+ const binaryFilePaths = fileChecks
47
+ .filter((f) => f.isBinary)
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
+
59
+ const messages = await createChatFromFolder(textFiles, binaryFilePaths, folderName);
60
+
61
+ if (importChat) {
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 {
70
+ setIsLoading(false);
71
+ toast.dismiss(loadingToast);
72
+ e.target.value = ''; // Reset file input
73
  }
74
  };
75
 
 
81
  className="hidden"
82
  webkitdirectory=""
83
  directory=""
84
+ onChange={handleFileChange}
85
+ {...({} as any)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  />
87
  <button
88
  onClick={() => {
 
90
  input?.click();
91
  }}
92
  className={className}
93
+ disabled={isLoading}
94
  >
95
  <div className="i-ph:upload-simple" />
96
+ {isLoading ? 'Importing...' : 'Import Folder'}
97
  </button>
98
  </>
99
  );
app/components/chat/ModelSelector.tsx CHANGED
@@ -1,5 +1,7 @@
1
  import type { ProviderInfo } from '~/types/model';
2
  import type { ModelInfo } from '~/utils/types';
 
 
3
 
4
  interface ModelSelectorProps {
5
  model?: string;
@@ -19,12 +21,79 @@ export const ModelSelector = ({
19
  modelList,
20
  providerList,
21
  }: ModelSelectorProps) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  return (
23
  <div className="mb-2 flex gap-2 flex-col sm:flex-row">
24
  <select
25
  value={provider?.name ?? ''}
26
  onChange={(e) => {
27
- const newProvider = providerList.find((p: ProviderInfo) => p.name === e.target.value);
28
 
29
  if (newProvider && setProvider) {
30
  setProvider(newProvider);
@@ -38,7 +107,7 @@ export const ModelSelector = ({
38
  }}
39
  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"
40
  >
41
- {providerList.map((provider: ProviderInfo) => (
42
  <option key={provider.name} value={provider.name}>
43
  {provider.name}
44
  </option>
@@ -52,8 +121,8 @@ export const ModelSelector = ({
52
  >
53
  {[...modelList]
54
  .filter((e) => e.provider == provider?.name && e.name)
55
- .map((modelOption) => (
56
- <option key={modelOption.name} value={modelOption.name}>
57
  {modelOption.label}
58
  </option>
59
  ))}
 
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;
 
21
  modelList,
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">
84
+ No providers are currently enabled. Please enable at least one provider in the settings to start using the
85
+ chat.
86
+ </p>
87
+ </div>
88
+ );
89
+ }
90
+
91
  return (
92
  <div className="mb-2 flex gap-2 flex-col sm:flex-row">
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
  }}
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>
 
121
  >
122
  {[...modelList]
123
  .filter((e) => e.provider == provider?.name && e.name)
124
+ .map((modelOption, index) => (
125
+ <option key={index} value={modelOption.name}>
126
  {modelOption.label}
127
  </option>
128
  ))}
app/components/chat/SendButton.client.tsx CHANGED
@@ -3,25 +3,30 @@ import { AnimatePresence, cubicBezier, motion } from 'framer-motion';
3
  interface SendButtonProps {
4
  show: boolean;
5
  isStreaming?: boolean;
 
6
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
7
  onImagesSelected?: (images: File[]) => void;
8
  }
9
 
10
  const customEasingFn = cubicBezier(0.4, 0, 0.2, 1);
11
 
12
- export const SendButton = ({ show, isStreaming, onClick }: SendButtonProps) => {
13
  return (
14
  <AnimatePresence>
15
  {show ? (
16
  <motion.button
17
- className="absolute flex justify-center items-center top-[18px] right-[22px] p-1 bg-accent-500 hover:brightness-94 color-white rounded-md w-[34px] h-[34px] transition-theme"
18
  transition={{ ease: customEasingFn, duration: 0.17 }}
19
  initial={{ opacity: 0, y: 10 }}
20
  animate={{ opacity: 1, y: 0 }}
21
  exit={{ opacity: 0, y: 10 }}
 
22
  onClick={(event) => {
23
  event.preventDefault();
24
- onClick?.(event);
 
 
 
25
  }}
26
  >
27
  <div className="text-lg">
 
3
  interface SendButtonProps {
4
  show: boolean;
5
  isStreaming?: boolean;
6
+ disabled?: boolean;
7
  onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
8
  onImagesSelected?: (images: File[]) => void;
9
  }
10
 
11
  const customEasingFn = cubicBezier(0.4, 0, 0.2, 1);
12
 
13
+ export const SendButton = ({ show, isStreaming, disabled, onClick }: SendButtonProps) => {
14
  return (
15
  <AnimatePresence>
16
  {show ? (
17
  <motion.button
18
+ className="absolute flex justify-center items-center top-[18px] right-[22px] p-1 bg-accent-500 hover:brightness-94 color-white rounded-md w-[34px] h-[34px] transition-theme disabled:opacity-50 disabled:cursor-not-allowed"
19
  transition={{ ease: customEasingFn, duration: 0.17 }}
20
  initial={{ opacity: 0, y: 10 }}
21
  animate={{ opacity: 1, y: 0 }}
22
  exit={{ opacity: 0, y: 10 }}
23
+ disabled={disabled}
24
  onClick={(event) => {
25
  event.preventDefault();
26
+
27
+ if (!disabled) {
28
+ onClick?.(event);
29
+ }
30
  }}
31
  >
32
  <div className="text-lg">
app/components/chat/chatExportAndImport/ImportButtons.tsx CHANGED
@@ -5,7 +5,7 @@ import { ImportFolderButton } from '~/components/chat/ImportFolderButton';
5
 
6
  export function ImportButtons(importChat: ((description: string, messages: Message[]) => Promise<void>) | undefined) {
7
  return (
8
- <div className="flex flex-col items-center justify-center flex-1 p-4">
9
  <input
10
  type="file"
11
  id="chat-import"
 
5
 
6
  export function ImportButtons(importChat: ((description: string, messages: Message[]) => Promise<void>) | undefined) {
7
  return (
8
+ <div className="flex flex-col items-center justify-center w-auto">
9
  <input
10
  type="file"
11
  id="chat-import"
app/components/editor/codemirror/languages.ts CHANGED
@@ -1,6 +1,13 @@
1
  import { LanguageDescription } from '@codemirror/language';
2
 
3
  export const supportedLanguages = [
 
 
 
 
 
 
 
4
  LanguageDescription.of({
5
  name: 'TS',
6
  extensions: ['ts'],
 
1
  import { LanguageDescription } from '@codemirror/language';
2
 
3
  export const supportedLanguages = [
4
+ LanguageDescription.of({
5
+ name: 'VUE',
6
+ extensions: ['vue'],
7
+ async load() {
8
+ return import('@codemirror/lang-vue').then((module) => module.vue());
9
+ },
10
+ }),
11
  LanguageDescription.of({
12
  name: 'TS',
13
  extensions: ['ts'],
app/components/git/GitUrlImport.client.tsx ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useSearchParams } from '@remix-run/react';
2
+ import { generateId, type Message } from 'ai';
3
+ import ignore from 'ignore';
4
+ import { useEffect, useState } from 'react';
5
+ import { ClientOnly } from 'remix-utils/client-only';
6
+ import { BaseChat } from '~/components/chat/BaseChat';
7
+ import { Chat } from '~/components/chat/Chat.client';
8
+ import { useGit } from '~/lib/hooks/useGit';
9
+ import { useChatHistory } from '~/lib/persistence';
10
+ import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands';
11
+
12
+ const IGNORE_PATTERNS = [
13
+ 'node_modules/**',
14
+ '.git/**',
15
+ '.github/**',
16
+ '.vscode/**',
17
+ '**/*.jpg',
18
+ '**/*.jpeg',
19
+ '**/*.png',
20
+ 'dist/**',
21
+ 'build/**',
22
+ '.next/**',
23
+ 'coverage/**',
24
+ '.cache/**',
25
+ '.vscode/**',
26
+ '.idea/**',
27
+ '**/*.log',
28
+ '**/.DS_Store',
29
+ '**/npm-debug.log*',
30
+ '**/yarn-debug.log*',
31
+ '**/yarn-error.log*',
32
+ '**/*lock.json',
33
+ '**/*lock.yaml',
34
+ ];
35
+
36
+ export function GitUrlImport() {
37
+ const [searchParams] = useSearchParams();
38
+ const { ready: historyReady, importChat } = useChatHistory();
39
+ const { ready: gitReady, gitClone } = useGit();
40
+ const [imported, setImported] = useState(false);
41
+
42
+ const importRepo = async (repoUrl?: string) => {
43
+ if (!gitReady && !historyReady) {
44
+ return;
45
+ }
46
+
47
+ if (repoUrl) {
48
+ const ig = ignore().add(IGNORE_PATTERNS);
49
+ const { workdir, data } = await gitClone(repoUrl);
50
+
51
+ if (importChat) {
52
+ const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
53
+
54
+ const textDecoder = new TextDecoder('utf-8');
55
+
56
+ // Convert files to common format for command detection
57
+ const fileContents = filePaths
58
+ .map((filePath) => {
59
+ const { data: content, encoding } = data[filePath];
60
+ return {
61
+ path: filePath,
62
+ content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
63
+ };
64
+ })
65
+ .filter((f) => f.content);
66
+
67
+ // Detect and create commands message
68
+ const commands = await detectProjectCommands(fileContents);
69
+ const commandsMessage = createCommandsMessage(commands);
70
+
71
+ // Create files message
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) =>
79
+ `<boltAction type="file" filePath="${file.path}">
80
+ ${file.content}
81
+ </boltAction>`,
82
+ )
83
+ .join('\n')}
84
+ </boltArtifact>`,
85
+ id: generateId(),
86
+ createdAt: new Date(),
87
+ };
88
+
89
+ const messages = [filesMessage];
90
+
91
+ if (commandsMessage) {
92
+ messages.push(commandsMessage);
93
+ }
94
+
95
+ await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
96
+ }
97
+ }
98
+ };
99
+
100
+ useEffect(() => {
101
+ if (!historyReady || !gitReady || imported) {
102
+ return;
103
+ }
104
+
105
+ const url = searchParams.get('url');
106
+
107
+ if (!url) {
108
+ window.location.href = '/';
109
+ return;
110
+ }
111
+
112
+ importRepo(url);
113
+ setImported(true);
114
+ }, [searchParams, historyReady, gitReady, imported]);
115
+
116
+ return <ClientOnly fallback={<BaseChat />}>{() => <Chat />}</ClientOnly>;
117
+ }
app/components/header/Header.tsx CHANGED
@@ -10,18 +10,17 @@ export function Header() {
10
 
11
  return (
12
  <header
13
- className={classNames(
14
- 'flex items-center bg-bolt-elements-background-depth-1 p-5 border-b h-[var(--header-height)]',
15
- {
16
- 'border-transparent': !chat.started,
17
- 'border-bolt-elements-borderColor': chat.started,
18
- },
19
- )}
20
  >
21
  <div className="flex items-center gap-2 z-logo text-bolt-elements-textPrimary cursor-pointer">
22
  <div className="i-ph:sidebar-simple-duotone text-xl" />
23
  <a href="/" className="text-2xl font-semibold text-accent flex items-center">
24
- <span className="i-bolt:logo-text?mask w-[46px] inline-block" />
 
 
25
  </a>
26
  </div>
27
  {chat.started && ( // Display ChatDescription and HeaderActionButtons only when the chat has started.
 
10
 
11
  return (
12
  <header
13
+ className={classNames('flex items-center p-5 border-b h-[var(--header-height)]', {
14
+ 'border-transparent': !chat.started,
15
+ 'border-bolt-elements-borderColor': chat.started,
16
+ })}
 
 
 
17
  >
18
  <div className="flex items-center gap-2 z-logo text-bolt-elements-textPrimary cursor-pointer">
19
  <div className="i-ph:sidebar-simple-duotone text-xl" />
20
  <a href="/" className="text-2xl font-semibold text-accent flex items-center">
21
+ {/* <span className="i-bolt:logo-text?mask w-[46px] inline-block" /> */}
22
+ <img src="/logo-light-styled.png" alt="logo" className="w-[90px] inline-block dark:hidden" />
23
+ <img src="/logo-dark-styled.png" alt="logo" className="w-[90px] inline-block hidden dark:block" />
24
  </a>
25
  </div>
26
  {chat.started && ( // Display ChatDescription and HeaderActionButtons only when the chat has started.
app/components/settings/Settings.module.scss ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .settings-tabs {
2
+ button {
3
+ width: 100%;
4
+ display: flex;
5
+ align-items: center;
6
+ gap: 0.5rem;
7
+ padding: 0.75rem 1rem;
8
+ border-radius: 0.5rem;
9
+ text-align: left;
10
+ font-size: 0.875rem;
11
+ transition: all 0.2s;
12
+ margin-bottom: 0.5rem;
13
+
14
+ &.active {
15
+ background: var(--bolt-elements-button-primary-background);
16
+ color: var(--bolt-elements-textPrimary);
17
+ }
18
+
19
+ &:not(.active) {
20
+ background: var(--bolt-elements-bg-depth-3);
21
+ color: var(--bolt-elements-textPrimary);
22
+
23
+ &:hover {
24
+ background: var(--bolt-elements-button-primary-backgroundHover);
25
+ }
26
+ }
27
+ }
28
+ }
29
+
30
+ .settings-button {
31
+ background-color: var(--bolt-elements-button-primary-background);
32
+ color: var(--bolt-elements-textPrimary);
33
+ border-radius: 0.5rem;
34
+ padding: 0.5rem 1rem;
35
+ transition: background-color 0.2s;
36
+
37
+ &:hover {
38
+ background-color: var(--bolt-elements-button-primary-backgroundHover);
39
+ }
40
+ }
41
+
42
+ .settings-danger-area {
43
+ background-color: transparent;
44
+ color: var(--bolt-elements-textPrimary);
45
+ border-radius: 0.5rem;
46
+ padding: 1rem;
47
+ margin-bottom: 1rem;
48
+ border-style: solid;
49
+ border-color: var(--bolt-elements-button-danger-backgroundHover) ;
50
+ border-width: thin;
51
+
52
+ button {
53
+ background-color: var(--bolt-elements-button-danger-background);
54
+ color: var(--bolt-elements-button-danger-text);
55
+ border-radius: 0.5rem;
56
+ padding: 0.5rem 1rem;
57
+ transition: background-color 0.2s;
58
+
59
+ &:hover {
60
+ background-color: var(--bolt-elements-button-danger-backgroundHover);
61
+ }
62
+ }
63
+ }
app/components/settings/SettingsWindow.tsx ADDED
@@ -0,0 +1,483 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as RadixDialog from '@radix-ui/react-dialog';
2
+ import { motion } from 'framer-motion';
3
+ import { useState } from 'react';
4
+ import { classNames } from '~/utils/classNames';
5
+ import { DialogTitle, dialogVariants, dialogBackdropVariants } from '~/components/ui/Dialog';
6
+ import { IconButton } from '~/components/ui/IconButton';
7
+ import { providersList } from '~/lib/stores/settings';
8
+ import { db, getAll, deleteById } from '~/lib/persistence';
9
+ import { toast } from 'react-toastify';
10
+ import { useNavigate } from '@remix-run/react';
11
+ import commit from '~/commit.json';
12
+ import Cookies from 'js-cookie';
13
+ import styles from './Settings.module.scss';
14
+ import { Switch } from '~/components/ui/Switch';
15
+
16
+ interface SettingsProps {
17
+ open: boolean;
18
+ onClose: () => void;
19
+ }
20
+
21
+ type TabType = 'chat-history' | 'providers' | 'features' | 'debug' | 'connection';
22
+
23
+ // Providers that support base URL configuration
24
+ const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike'];
25
+
26
+ export const SettingsWindow = ({ open, onClose }: SettingsProps) => {
27
+ const navigate = useNavigate();
28
+ const [activeTab, setActiveTab] = useState<TabType>('chat-history');
29
+ const [isDebugEnabled, setIsDebugEnabled] = useState(() => {
30
+ const savedDebugState = Cookies.get('isDebugEnabled');
31
+ return savedDebugState === 'true';
32
+ });
33
+ const [searchTerm, setSearchTerm] = useState('');
34
+ const [isDeleting, setIsDeleting] = useState(false);
35
+ const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || '');
36
+ const [githubToken, setGithubToken] = useState(Cookies.get('githubToken') || '');
37
+ const [isLocalModelsEnabled, setIsLocalModelsEnabled] = useState(() => {
38
+ const savedLocalModelsState = Cookies.get('isLocalModelsEnabled');
39
+ return savedLocalModelsState === 'true';
40
+ });
41
+
42
+ // Load base URLs from cookies
43
+ const [baseUrls, setBaseUrls] = useState(() => {
44
+ const savedUrls = Cookies.get('providerBaseUrls');
45
+
46
+ if (savedUrls) {
47
+ try {
48
+ return JSON.parse(savedUrls);
49
+ } catch (error) {
50
+ console.error('Failed to parse base URLs from cookies:', error);
51
+ return {
52
+ Ollama: 'http://localhost:11434',
53
+ LMStudio: 'http://localhost:1234',
54
+ OpenAILike: '',
55
+ };
56
+ }
57
+ }
58
+
59
+ return {
60
+ Ollama: 'http://localhost:11434',
61
+ LMStudio: 'http://localhost:1234',
62
+ OpenAILike: '',
63
+ };
64
+ });
65
+
66
+ const handleBaseUrlChange = (provider: string, url: string) => {
67
+ setBaseUrls((prev: Record<string, string>) => {
68
+ const newUrls = { ...prev, [provider]: url };
69
+ Cookies.set('providerBaseUrls', JSON.stringify(newUrls));
70
+
71
+ return newUrls;
72
+ });
73
+ };
74
+
75
+ const tabs: { id: TabType; label: string; icon: string }[] = [
76
+ { id: 'chat-history', label: 'Chat History', icon: 'i-ph:book' },
77
+ { id: 'providers', label: 'Providers', icon: 'i-ph:key' },
78
+ { id: 'features', label: 'Features', icon: 'i-ph:star' },
79
+ { id: 'connection', label: 'Connection', icon: 'i-ph:link' },
80
+ ...(isDebugEnabled ? [{ id: 'debug' as TabType, label: 'Debug Tab', icon: 'i-ph:bug' }] : []),
81
+ ];
82
+
83
+ // Load providers from cookies on mount
84
+ const [providers, setProviders] = useState(() => {
85
+ const savedProviders = Cookies.get('providers');
86
+
87
+ if (savedProviders) {
88
+ try {
89
+ const parsedProviders = JSON.parse(savedProviders);
90
+
91
+ // Merge saved enabled states with the base provider list
92
+ return providersList.map((provider) => ({
93
+ ...provider,
94
+ isEnabled: parsedProviders[provider.name] || false,
95
+ }));
96
+ } catch (error) {
97
+ console.error('Failed to parse providers from cookies:', error);
98
+ }
99
+ }
100
+
101
+ return providersList;
102
+ });
103
+
104
+ const handleToggleProvider = (providerName: string, enabled: boolean) => {
105
+ setProviders((prevProviders) => {
106
+ const newProviders = prevProviders.map((provider) =>
107
+ provider.name === providerName ? { ...provider, isEnabled: enabled } : provider,
108
+ );
109
+
110
+ // Save to cookies
111
+ const enabledStates = newProviders.reduce(
112
+ (acc, provider) => ({
113
+ ...acc,
114
+ [provider.name]: provider.isEnabled,
115
+ }),
116
+ {},
117
+ );
118
+ Cookies.set('providers', JSON.stringify(enabledStates));
119
+
120
+ return newProviders;
121
+ });
122
+ };
123
+
124
+ const filteredProviders = providers
125
+ .filter((provider) => {
126
+ const isLocalModelProvider = ['OpenAILike', 'LMStudio', 'Ollama'].includes(provider.name);
127
+ return isLocalModelsEnabled || !isLocalModelProvider;
128
+ })
129
+ .filter((provider) => provider.name.toLowerCase().includes(searchTerm.toLowerCase()))
130
+ .sort((a, b) => a.name.localeCompare(b.name));
131
+
132
+ const handleCopyToClipboard = () => {
133
+ const debugInfo = {
134
+ OS: navigator.platform,
135
+ Browser: navigator.userAgent,
136
+ ActiveFeatures: providers.filter((provider) => provider.isEnabled).map((provider) => provider.name),
137
+ BaseURLs: {
138
+ Ollama: process.env.REACT_APP_OLLAMA_URL,
139
+ OpenAI: process.env.REACT_APP_OPENAI_URL,
140
+ LMStudio: process.env.REACT_APP_LM_STUDIO_URL,
141
+ },
142
+ Version: versionHash,
143
+ };
144
+ navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => {
145
+ alert('Debug information copied to clipboard!');
146
+ });
147
+ };
148
+
149
+ const downloadAsJson = (data: any, filename: string) => {
150
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
151
+ const url = URL.createObjectURL(blob);
152
+ const link = document.createElement('a');
153
+ link.href = url;
154
+ link.download = filename;
155
+ document.body.appendChild(link);
156
+ link.click();
157
+ document.body.removeChild(link);
158
+ URL.revokeObjectURL(url);
159
+ };
160
+
161
+ const handleDeleteAllChats = async () => {
162
+ if (!db) {
163
+ toast.error('Database is not available');
164
+ return;
165
+ }
166
+
167
+ try {
168
+ setIsDeleting(true);
169
+
170
+ const allChats = await getAll(db);
171
+
172
+ // Delete all chats one by one
173
+ await Promise.all(allChats.map((chat) => deleteById(db!, chat.id)));
174
+
175
+ toast.success('All chats deleted successfully');
176
+ navigate('/', { replace: true });
177
+ } catch (error) {
178
+ toast.error('Failed to delete chats');
179
+ console.error(error);
180
+ } finally {
181
+ setIsDeleting(false);
182
+ }
183
+ };
184
+
185
+ const handleExportAllChats = async () => {
186
+ if (!db) {
187
+ toast.error('Database is not available');
188
+ return;
189
+ }
190
+
191
+ try {
192
+ const allChats = await getAll(db);
193
+ const exportData = {
194
+ chats: allChats,
195
+ exportDate: new Date().toISOString(),
196
+ };
197
+
198
+ downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`);
199
+ toast.success('Chats exported successfully');
200
+ } catch (error) {
201
+ toast.error('Failed to export chats');
202
+ console.error(error);
203
+ }
204
+ };
205
+
206
+ const versionHash = commit.commit; // Get the version hash from commit.json
207
+
208
+ const handleSaveConnection = () => {
209
+ Cookies.set('githubUsername', githubUsername);
210
+ Cookies.set('githubToken', githubToken);
211
+ toast.success('GitHub credentials saved successfully!');
212
+ };
213
+
214
+ const handleToggleDebug = (enabled: boolean) => {
215
+ setIsDebugEnabled(enabled);
216
+ Cookies.set('isDebugEnabled', String(enabled));
217
+ };
218
+
219
+ const handleToggleLocalModels = (enabled: boolean) => {
220
+ setIsLocalModelsEnabled(enabled);
221
+ Cookies.set('isLocalModelsEnabled', String(enabled));
222
+ };
223
+
224
+ return (
225
+ <RadixDialog.Root open={open}>
226
+ <RadixDialog.Portal>
227
+ <RadixDialog.Overlay asChild onClick={onClose}>
228
+ <motion.div
229
+ className="bg-black/50 fixed inset-0 z-max backdrop-blur-sm"
230
+ initial="closed"
231
+ animate="open"
232
+ exit="closed"
233
+ variants={dialogBackdropVariants}
234
+ />
235
+ </RadixDialog.Overlay>
236
+ <RadixDialog.Content asChild>
237
+ <motion.div
238
+ className="fixed top-[50%] left-[50%] z-max h-[85vh] w-[90vw] max-w-[900px] translate-x-[-50%] translate-y-[-50%] border border-bolt-elements-borderColor rounded-lg shadow-lg focus:outline-none overflow-hidden"
239
+ initial="closed"
240
+ animate="open"
241
+ exit="closed"
242
+ variants={dialogVariants}
243
+ >
244
+ <div className="flex h-full">
245
+ <div
246
+ className={classNames(
247
+ 'w-48 border-r border-bolt-elements-borderColor bg-bolt-elements-background-depth-1 p-4 flex flex-col justify-between',
248
+ styles['settings-tabs'],
249
+ )}
250
+ >
251
+ <DialogTitle className="flex-shrink-0 text-lg font-semibold text-bolt-elements-textPrimary mb-2">
252
+ Settings
253
+ </DialogTitle>
254
+ {tabs.map((tab) => (
255
+ <button
256
+ key={tab.id}
257
+ onClick={() => setActiveTab(tab.id)}
258
+ className={classNames(activeTab === tab.id ? styles.active : '')}
259
+ >
260
+ <div className={tab.icon} />
261
+ {tab.label}
262
+ </button>
263
+ ))}
264
+ <div className="mt-auto flex flex-col gap-2">
265
+ <a
266
+ href="https://github.com/stackblitz-labs/bolt.diy"
267
+ target="_blank"
268
+ rel="noopener noreferrer"
269
+ className={classNames(styles['settings-button'], 'flex items-center gap-2')}
270
+ >
271
+ <div className="i-ph:github-logo" />
272
+ GitHub
273
+ </a>
274
+ <a
275
+ href="https://coleam00.github.io/bolt.new-any-llm"
276
+ target="_blank"
277
+ rel="noopener noreferrer"
278
+ className={classNames(styles['settings-button'], 'flex items-center gap-2')}
279
+ >
280
+ <div className="i-ph:book" />
281
+ Docs
282
+ </a>
283
+ </div>
284
+ </div>
285
+
286
+ <div className="flex-1 flex flex-col p-8 pt-10 bg-bolt-elements-background-depth-2">
287
+ <div className="flex-1 overflow-y-auto">
288
+ {activeTab === 'chat-history' && (
289
+ <div className="p-4">
290
+ <h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Chat History</h3>
291
+ <button
292
+ onClick={handleExportAllChats}
293
+ className={classNames(
294
+ 'bg-bolt-elements-button-primary-background',
295
+ 'rounded-lg px-4 py-2 mb-4 transition-colors duration-200',
296
+ 'hover:bg-bolt-elements-button-primary-backgroundHover',
297
+ 'text-bolt-elements-button-primary-text',
298
+ )}
299
+ >
300
+ Export All Chats
301
+ </button>
302
+
303
+ <div
304
+ className={classNames(
305
+ 'text-bolt-elements-textPrimary rounded-lg py-4 mb-4',
306
+ styles['settings-danger-area'],
307
+ )}
308
+ >
309
+ <h4 className="font-semibold">Danger Area</h4>
310
+ <p className="mb-2">This action cannot be undone!</p>
311
+ <button
312
+ onClick={handleDeleteAllChats}
313
+ disabled={isDeleting}
314
+ className={classNames(
315
+ 'bg-bolt-elements-button-danger-background',
316
+ 'rounded-lg px-4 py-2 transition-colors duration-200',
317
+ isDeleting
318
+ ? 'opacity-50 cursor-not-allowed'
319
+ : 'hover:bg-bolt-elements-button-danger-backgroundHover',
320
+ 'text-bolt-elements-button-danger-text',
321
+ )}
322
+ >
323
+ {isDeleting ? 'Deleting...' : 'Delete All Chats'}
324
+ </button>
325
+ </div>
326
+ </div>
327
+ )}
328
+ {activeTab === 'providers' && (
329
+ <div className="p-4">
330
+ <div className="flex mb-4">
331
+ <input
332
+ type="text"
333
+ placeholder="Search providers..."
334
+ value={searchTerm}
335
+ onChange={(e) => setSearchTerm(e.target.value)}
336
+ 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"
337
+ />
338
+ </div>
339
+ {filteredProviders.map((provider) => (
340
+ <div
341
+ key={provider.name}
342
+ className="flex flex-col mb-2 provider-item hover:bg-bolt-elements-bg-depth-3 p-4 rounded-lg border border-bolt-elements-borderColor "
343
+ >
344
+ <div className="flex items-center justify-between mb-2">
345
+ <span className="text-bolt-elements-textPrimary">{provider.name}</span>
346
+ <Switch
347
+ className="ml-auto"
348
+ checked={provider.isEnabled}
349
+ onCheckedChange={(enabled) => handleToggleProvider(provider.name, enabled)}
350
+ />
351
+ </div>
352
+ {/* Base URL input for configurable providers */}
353
+ {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.isEnabled && (
354
+ <div className="mt-2">
355
+ <label className="block text-sm text-bolt-elements-textSecondary mb-1">Base URL:</label>
356
+ <input
357
+ type="text"
358
+ value={baseUrls[provider.name]}
359
+ onChange={(e) => handleBaseUrlChange(provider.name, e.target.value)}
360
+ placeholder={`Enter ${provider.name} base URL`}
361
+ 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"
362
+ />
363
+ </div>
364
+ )}
365
+ </div>
366
+ ))}
367
+ </div>
368
+ )}
369
+ {activeTab === 'features' && (
370
+ <div className="p-4 bg-bolt-elements-bg-depth-2 border border-bolt-elements-borderColor rounded-lg mb-4">
371
+ <div className="mb-6">
372
+ <h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Optional Features</h3>
373
+ <div className="flex items-center justify-between mb-2">
374
+ <span className="text-bolt-elements-textPrimary">Debug Info</span>
375
+ <Switch className="ml-auto" checked={isDebugEnabled} onCheckedChange={handleToggleDebug} />
376
+ </div>
377
+ </div>
378
+
379
+ <div className="mb-6 border-t border-bolt-elements-borderColor pt-4">
380
+ <h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">
381
+ Experimental Features
382
+ </h3>
383
+ <p className="text-sm text-bolt-elements-textSecondary mb-4">
384
+ Disclaimer: Experimental features may be unstable and are subject to change.
385
+ </p>
386
+ <div className="flex items-center justify-between mb-2">
387
+ <span className="text-bolt-elements-textPrimary">Enable Local Models</span>
388
+ <Switch
389
+ className="ml-auto"
390
+ checked={isLocalModelsEnabled}
391
+ onCheckedChange={handleToggleLocalModels}
392
+ />
393
+ </div>
394
+ </div>
395
+ </div>
396
+ )}
397
+ {activeTab === 'debug' && isDebugEnabled && (
398
+ <div className="p-4">
399
+ <h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">Debug Tab</h3>
400
+ <button
401
+ onClick={handleCopyToClipboard}
402
+ className="bg-blue-500 text-white rounded-lg px-4 py-2 hover:bg-blue-600 mb-4 transition-colors duration-200"
403
+ >
404
+ Copy to Clipboard
405
+ </button>
406
+
407
+ <h4 className="text-md font-medium text-bolt-elements-textPrimary">System Information</h4>
408
+ <p className="text-bolt-elements-textSecondary">OS: {navigator.platform}</p>
409
+ <p className="text-bolt-elements-textSecondary">Browser: {navigator.userAgent}</p>
410
+
411
+ <h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Active Features</h4>
412
+ <ul>
413
+ {providers
414
+ .filter((provider) => provider.isEnabled)
415
+ .map((provider) => (
416
+ <li key={provider.name} className="text-bolt-elements-textSecondary">
417
+ {provider.name}
418
+ </li>
419
+ ))}
420
+ </ul>
421
+
422
+ <h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Base URLs</h4>
423
+ <ul>
424
+ <li className="text-bolt-elements-textSecondary">Ollama: {process.env.REACT_APP_OLLAMA_URL}</li>
425
+ <li className="text-bolt-elements-textSecondary">OpenAI: {process.env.REACT_APP_OPENAI_URL}</li>
426
+ <li className="text-bolt-elements-textSecondary">
427
+ LM Studio: {process.env.REACT_APP_LM_STUDIO_URL}
428
+ </li>
429
+ </ul>
430
+
431
+ <h4 className="text-md font-medium text-bolt-elements-textPrimary mt-4">Version Information</h4>
432
+ <p className="text-bolt-elements-textSecondary">Version Hash: {versionHash}</p>
433
+ </div>
434
+ )}
435
+ {activeTab === 'connection' && (
436
+ <div className="p-4 mb-4 border border-bolt-elements-borderColor rounded-lg bg-bolt-elements-background-depth-3">
437
+ <h3 className="text-lg font-medium text-bolt-elements-textPrimary mb-4">GitHub Connection</h3>
438
+ <div className="flex mb-4">
439
+ <div className="flex-1 mr-2">
440
+ <label className="block text-sm text-bolt-elements-textSecondary mb-1">
441
+ GitHub Username:
442
+ </label>
443
+ <input
444
+ type="text"
445
+ value={githubUsername}
446
+ onChange={(e) => setGithubUsername(e.target.value)}
447
+ 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"
448
+ />
449
+ </div>
450
+ <div className="flex-1">
451
+ <label className="block text-sm text-bolt-elements-textSecondary mb-1">
452
+ Personal Access Token:
453
+ </label>
454
+ <input
455
+ type="password"
456
+ value={githubToken}
457
+ onChange={(e) => setGithubToken(e.target.value)}
458
+ 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"
459
+ />
460
+ </div>
461
+ </div>
462
+ <div className="flex mb-4">
463
+ <button
464
+ onClick={handleSaveConnection}
465
+ className="bg-bolt-elements-button-primary-background rounded-lg px-4 py-2 mr-2 transition-colors duration-200 hover:bg-bolt-elements-button-primary-backgroundHover text-bolt-elements-button-primary-text"
466
+ >
467
+ Save Connection
468
+ </button>
469
+ </div>
470
+ </div>
471
+ )}
472
+ </div>
473
+ </div>
474
+ </div>
475
+ <RadixDialog.Close asChild onClick={onClose}>
476
+ <IconButton icon="i-ph:x" className="absolute top-[10px] right-[10px]" />
477
+ </RadixDialog.Close>
478
+ </motion.div>
479
+ </RadixDialog.Content>
480
+ </RadixDialog.Portal>
481
+ </RadixDialog.Root>
482
+ );
483
+ };
app/components/sidebar/Menu.client.tsx CHANGED
@@ -3,6 +3,8 @@ import { useCallback, useEffect, useRef, useState } from 'react';
3
  import { toast } from 'react-toastify';
4
  import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
5
  import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
 
 
6
  import { db, deleteById, getAll, chatId, type ChatHistoryItem, useChatHistory } from '~/lib/persistence';
7
  import { cubicEasingFn } from '~/utils/easings';
8
  import { logger } from '~/utils/logger';
@@ -39,6 +41,7 @@ export const Menu = () => {
39
  const [list, setList] = useState<ChatHistoryItem[]>([]);
40
  const [open, setOpen] = useState(false);
41
  const [dialogContent, setDialogContent] = useState<DialogContent>(null);
 
42
 
43
  const { filteredItems: filteredList, handleSearchChange } = useSearchFilter({
44
  items: list,
@@ -200,10 +203,12 @@ export const Menu = () => {
200
  </Dialog>
201
  </DialogRoot>
202
  </div>
203
- <div className="flex items-center border-t border-bolt-elements-borderColor p-4">
204
- <ThemeSwitch className="ml-auto" />
 
205
  </div>
206
  </div>
 
207
  </motion.div>
208
  );
209
  };
 
3
  import { toast } from 'react-toastify';
4
  import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog';
5
  import { ThemeSwitch } from '~/components/ui/ThemeSwitch';
6
+ import { SettingsWindow } from '~/components/settings/SettingsWindow';
7
+ import { SettingsButton } from '~/components/ui/SettingsButton';
8
  import { db, deleteById, getAll, chatId, type ChatHistoryItem, useChatHistory } from '~/lib/persistence';
9
  import { cubicEasingFn } from '~/utils/easings';
10
  import { logger } from '~/utils/logger';
 
41
  const [list, setList] = useState<ChatHistoryItem[]>([]);
42
  const [open, setOpen] = useState(false);
43
  const [dialogContent, setDialogContent] = useState<DialogContent>(null);
44
+ const [isSettingsOpen, setIsSettingsOpen] = useState(false);
45
 
46
  const { filteredItems: filteredList, handleSearchChange } = useSearchFilter({
47
  items: list,
 
203
  </Dialog>
204
  </DialogRoot>
205
  </div>
206
+ <div className="flex items-center justify-between border-t border-bolt-elements-borderColor p-4">
207
+ <SettingsButton onClick={() => setIsSettingsOpen(true)} />
208
+ <ThemeSwitch />
209
  </div>
210
  </div>
211
+ <SettingsWindow open={isSettingsOpen} onClose={() => setIsSettingsOpen(false)} />
212
  </motion.div>
213
  );
214
  };
app/components/ui/BackgroundRays/index.tsx ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import styles from './styles.module.scss';
2
+
3
+ const BackgroundRays = () => {
4
+ return (
5
+ <div className={`${styles.rayContainer} `}>
6
+ <div className={`${styles.lightRay} ${styles.ray1}`}></div>
7
+ <div className={`${styles.lightRay} ${styles.ray2}`}></div>
8
+ <div className={`${styles.lightRay} ${styles.ray3}`}></div>
9
+ <div className={`${styles.lightRay} ${styles.ray4}`}></div>
10
+ <div className={`${styles.lightRay} ${styles.ray5}`}></div>
11
+ <div className={`${styles.lightRay} ${styles.ray6}`}></div>
12
+ <div className={`${styles.lightRay} ${styles.ray7}`}></div>
13
+ <div className={`${styles.lightRay} ${styles.ray8}`}></div>
14
+ </div>
15
+ );
16
+ };
17
+
18
+ export default BackgroundRays;
app/components/ui/BackgroundRays/styles.module.scss ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .rayContainer {
2
+ // Theme-specific colors
3
+ --ray-color-primary: color-mix(in srgb, var(--primary-color), transparent 30%);
4
+ --ray-color-secondary: color-mix(in srgb, var(--secondary-color), transparent 30%);
5
+ --ray-color-accent: color-mix(in srgb, var(--accent-color), transparent 30%);
6
+
7
+ // Theme-specific gradients
8
+ --ray-gradient-primary: radial-gradient(var(--ray-color-primary) 0%, transparent 70%);
9
+ --ray-gradient-secondary: radial-gradient(var(--ray-color-secondary) 0%, transparent 70%);
10
+ --ray-gradient-accent: radial-gradient(var(--ray-color-accent) 0%, transparent 70%);
11
+
12
+ position: fixed;
13
+ inset: 0;
14
+ overflow: hidden;
15
+ animation: fadeIn 1.5s ease-out;
16
+ pointer-events: none;
17
+ z-index: 0;
18
+ // background-color: transparent;
19
+
20
+ :global(html[data-theme='dark']) & {
21
+ mix-blend-mode: screen;
22
+ }
23
+
24
+ :global(html[data-theme='light']) & {
25
+ mix-blend-mode: multiply;
26
+ }
27
+ }
28
+
29
+ .lightRay {
30
+ position: absolute;
31
+ border-radius: 100%;
32
+
33
+ :global(html[data-theme='dark']) & {
34
+ mix-blend-mode: screen;
35
+ }
36
+
37
+ :global(html[data-theme='light']) & {
38
+ mix-blend-mode: multiply;
39
+ opacity: 0.4;
40
+ }
41
+ }
42
+
43
+ .ray1 {
44
+ width: 600px;
45
+ height: 800px;
46
+ background: var(--ray-gradient-primary);
47
+ transform: rotate(65deg);
48
+ top: -500px;
49
+ left: -100px;
50
+ filter: blur(80px);
51
+ opacity: 0.6;
52
+ animation: float1 15s infinite ease-in-out;
53
+ }
54
+
55
+ .ray2 {
56
+ width: 400px;
57
+ height: 600px;
58
+ background: var(--ray-gradient-secondary);
59
+ transform: rotate(-30deg);
60
+ top: -300px;
61
+ left: 200px;
62
+ filter: blur(60px);
63
+ opacity: 0.6;
64
+ animation: float2 18s infinite ease-in-out;
65
+ }
66
+
67
+ .ray3 {
68
+ width: 500px;
69
+ height: 400px;
70
+ background: var(--ray-gradient-accent);
71
+ top: -320px;
72
+ left: 500px;
73
+ filter: blur(65px);
74
+ opacity: 0.5;
75
+ animation: float3 20s infinite ease-in-out;
76
+ }
77
+
78
+ .ray4 {
79
+ width: 400px;
80
+ height: 450px;
81
+ background: var(--ray-gradient-secondary);
82
+ top: -350px;
83
+ left: 800px;
84
+ filter: blur(55px);
85
+ opacity: 0.55;
86
+ animation: float4 17s infinite ease-in-out;
87
+ }
88
+
89
+ .ray5 {
90
+ width: 350px;
91
+ height: 500px;
92
+ background: var(--ray-gradient-primary);
93
+ transform: rotate(-45deg);
94
+ top: -250px;
95
+ left: 1000px;
96
+ filter: blur(45px);
97
+ opacity: 0.6;
98
+ animation: float5 16s infinite ease-in-out;
99
+ }
100
+
101
+ .ray6 {
102
+ width: 300px;
103
+ height: 700px;
104
+ background: var(--ray-gradient-accent);
105
+ transform: rotate(75deg);
106
+ top: -400px;
107
+ left: 600px;
108
+ filter: blur(75px);
109
+ opacity: 0.45;
110
+ animation: float6 19s infinite ease-in-out;
111
+ }
112
+
113
+ .ray7 {
114
+ width: 450px;
115
+ height: 600px;
116
+ background: var(--ray-gradient-primary);
117
+ transform: rotate(45deg);
118
+ top: -450px;
119
+ left: 350px;
120
+ filter: blur(65px);
121
+ opacity: 0.55;
122
+ animation: float7 21s infinite ease-in-out;
123
+ }
124
+
125
+ .ray8 {
126
+ width: 380px;
127
+ height: 550px;
128
+ background: var(--ray-gradient-secondary);
129
+ transform: rotate(-60deg);
130
+ top: -380px;
131
+ left: 750px;
132
+ filter: blur(58px);
133
+ opacity: 0.6;
134
+ animation: float8 14s infinite ease-in-out;
135
+ }
136
+
137
+ @keyframes float1 {
138
+ 0%,
139
+ 100% {
140
+ transform: rotate(65deg) translate(0, 0);
141
+ }
142
+ 25% {
143
+ transform: rotate(70deg) translate(30px, 20px);
144
+ }
145
+ 50% {
146
+ transform: rotate(60deg) translate(-20px, 40px);
147
+ }
148
+ 75% {
149
+ transform: rotate(68deg) translate(-40px, 10px);
150
+ }
151
+ }
152
+
153
+ @keyframes float2 {
154
+ 0%,
155
+ 100% {
156
+ transform: rotate(-30deg) scale(1);
157
+ }
158
+ 33% {
159
+ transform: rotate(-25deg) scale(1.1);
160
+ }
161
+ 66% {
162
+ transform: rotate(-35deg) scale(0.95);
163
+ }
164
+ }
165
+
166
+ @keyframes float3 {
167
+ 0%,
168
+ 100% {
169
+ transform: translate(0, 0) rotate(0deg);
170
+ }
171
+ 25% {
172
+ transform: translate(40px, 20px) rotate(5deg);
173
+ }
174
+ 75% {
175
+ transform: translate(-30px, 40px) rotate(-5deg);
176
+ }
177
+ }
178
+
179
+ @keyframes float4 {
180
+ 0%,
181
+ 100% {
182
+ transform: scale(1) rotate(0deg);
183
+ }
184
+ 50% {
185
+ transform: scale(1.15) rotate(10deg);
186
+ }
187
+ }
188
+
189
+ @keyframes float5 {
190
+ 0%,
191
+ 100% {
192
+ transform: rotate(-45deg) translate(0, 0);
193
+ }
194
+ 33% {
195
+ transform: rotate(-40deg) translate(25px, -20px);
196
+ }
197
+ 66% {
198
+ transform: rotate(-50deg) translate(-25px, 20px);
199
+ }
200
+ }
201
+
202
+ @keyframes float6 {
203
+ 0%,
204
+ 100% {
205
+ transform: rotate(75deg) scale(1);
206
+ filter: blur(75px);
207
+ }
208
+ 50% {
209
+ transform: rotate(85deg) scale(1.1);
210
+ filter: blur(65px);
211
+ }
212
+ }
213
+
214
+ @keyframes float7 {
215
+ 0%,
216
+ 100% {
217
+ transform: rotate(45deg) translate(0, 0);
218
+ opacity: 0.55;
219
+ }
220
+ 50% {
221
+ transform: rotate(40deg) translate(-30px, 30px);
222
+ opacity: 0.65;
223
+ }
224
+ }
225
+
226
+ @keyframes float8 {
227
+ 0%,
228
+ 100% {
229
+ transform: rotate(-60deg) scale(1);
230
+ }
231
+ 25% {
232
+ transform: rotate(-55deg) scale(1.05);
233
+ }
234
+ 75% {
235
+ transform: rotate(-65deg) scale(0.95);
236
+ }
237
+ }
238
+
239
+ @keyframes fadeIn {
240
+ from {
241
+ opacity: 0;
242
+ }
243
+ to {
244
+ opacity: 1;
245
+ }
246
+ }
app/components/ui/SettingsButton.tsx ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { memo } from 'react';
2
+ import { IconButton } from '~/components/ui/IconButton';
3
+ interface SettingsButtonProps {
4
+ onClick: () => void;
5
+ }
6
+
7
+ export const SettingsButton = memo(({ onClick }: SettingsButtonProps) => {
8
+ return (
9
+ <IconButton
10
+ onClick={onClick}
11
+ icon="i-ph:gear"
12
+ size="xl"
13
+ title="Settings"
14
+ className="text-[#666] hover:text-bolt-elements-textPrimary hover:bg-bolt-elements-item-backgroundActive/10 transition-colors"
15
+ />
16
+ );
17
+ });
app/components/ui/Switch.tsx ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { memo } from 'react';
2
+ import * as SwitchPrimitive from '@radix-ui/react-switch';
3
+ import { classNames } from '~/utils/classNames';
4
+
5
+ interface SwitchProps {
6
+ className?: string;
7
+ checked?: boolean;
8
+ onCheckedChange?: (event: boolean) => void;
9
+ }
10
+
11
+ export const Switch = memo(({ className, onCheckedChange, checked }: SwitchProps) => {
12
+ return (
13
+ <SwitchPrimitive.Root
14
+ className={classNames(
15
+ 'relative h-6 w-11 cursor-pointer rounded-full bg-bolt-elements-button-primary-background',
16
+ 'transition-colors duration-200 ease-in-out',
17
+ 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2',
18
+ 'disabled:cursor-not-allowed disabled:opacity-50',
19
+ 'data-[state=checked]:bg-bolt-elements-item-contentAccent',
20
+ className,
21
+ )}
22
+ checked={checked}
23
+ onCheckedChange={(e) => onCheckedChange?.(e)}
24
+ >
25
+ <SwitchPrimitive.Thumb
26
+ className={classNames(
27
+ 'block h-5 w-5 rounded-full bg-white',
28
+ 'shadow-lg shadow-black/20',
29
+ 'transition-transform duration-200 ease-in-out',
30
+ 'translate-x-0.5',
31
+ 'data-[state=checked]:translate-x-[1.375rem]',
32
+ 'will-change-transform',
33
+ )}
34
+ />
35
+ </SwitchPrimitive.Root>
36
+ );
37
+ });
app/components/workbench/Workbench.client.tsx CHANGED
@@ -17,6 +17,7 @@ import { renderLogger } from '~/utils/logger';
17
  import { EditorPanel } from './EditorPanel';
18
  import { Preview } from './Preview';
19
  import useViewport from '~/lib/hooks';
 
20
 
21
  interface WorkspaceProps {
22
  chatStarted?: boolean;
@@ -180,21 +181,22 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
180
  return;
181
  }
182
 
183
- const githubUsername = prompt('Please enter your GitHub username:');
 
184
 
185
- if (!githubUsername) {
186
- alert('GitHub username is required. Push to GitHub cancelled.');
187
- return;
188
- }
189
 
190
- const githubToken = prompt('Please enter your GitHub personal access token:');
 
 
 
191
 
192
- if (!githubToken) {
193
- alert('GitHub token is required. Push to GitHub cancelled.');
194
- return;
195
  }
196
-
197
- workbenchStore.pushToGitHub(repoName, githubUsername, githubToken);
198
  }}
199
  >
200
  <div className="i-ph:github-logo" />
 
17
  import { EditorPanel } from './EditorPanel';
18
  import { Preview } from './Preview';
19
  import useViewport from '~/lib/hooks';
20
+ import Cookies from 'js-cookie';
21
 
22
  interface WorkspaceProps {
23
  chatStarted?: boolean;
 
181
  return;
182
  }
183
 
184
+ const githubUsername = Cookies.get('githubUsername');
185
+ const githubToken = Cookies.get('githubToken');
186
 
187
+ if (!githubUsername || !githubToken) {
188
+ const usernameInput = prompt('Please enter your GitHub username:');
189
+ const tokenInput = prompt('Please enter your GitHub personal access token:');
 
190
 
191
+ if (!usernameInput || !tokenInput) {
192
+ alert('GitHub username and token are required. Push to GitHub cancelled.');
193
+ return;
194
+ }
195
 
196
+ workbenchStore.pushToGitHub(repoName, usernameInput, tokenInput);
197
+ } else {
198
+ workbenchStore.pushToGitHub(repoName, githubUsername, githubToken);
199
  }
 
 
200
  }}
201
  >
202
  <div className="i-ph:github-logo" />
app/lib/hooks/useGit.ts ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { WebContainer } from '@webcontainer/api';
2
+ import { useCallback, useEffect, useRef, useState, type MutableRefObject } from 'react';
3
+ import { webcontainer as webcontainerPromise } from '~/lib/webcontainer';
4
+ import git, { type GitAuth, type PromiseFsClient } from 'isomorphic-git';
5
+ import http from 'isomorphic-git/http/web';
6
+ import Cookies from 'js-cookie';
7
+ import { toast } from 'react-toastify';
8
+
9
+ const lookupSavedPassword = (url: string) => {
10
+ const domain = url.split('/')[2];
11
+ const gitCreds = Cookies.get(`git:${domain}`);
12
+
13
+ if (!gitCreds) {
14
+ return null;
15
+ }
16
+
17
+ try {
18
+ const { username, password } = JSON.parse(gitCreds || '{}');
19
+ return { username, password };
20
+ } catch (error) {
21
+ console.log(`Failed to parse Git Cookie ${error}`);
22
+ return null;
23
+ }
24
+ };
25
+
26
+ const saveGitAuth = (url: string, auth: GitAuth) => {
27
+ const domain = url.split('/')[2];
28
+ Cookies.set(`git:${domain}`, JSON.stringify(auth));
29
+ };
30
+
31
+ export function useGit() {
32
+ const [ready, setReady] = useState(false);
33
+ const [webcontainer, setWebcontainer] = useState<WebContainer>();
34
+ const [fs, setFs] = useState<PromiseFsClient>();
35
+ const fileData = useRef<Record<string, { data: any; encoding?: string }>>({});
36
+ useEffect(() => {
37
+ webcontainerPromise.then((container) => {
38
+ fileData.current = {};
39
+ setWebcontainer(container);
40
+ setFs(getFs(container, fileData));
41
+ setReady(true);
42
+ });
43
+ }, []);
44
+
45
+ const gitClone = useCallback(
46
+ async (url: string) => {
47
+ if (!webcontainer || !fs || !ready) {
48
+ throw 'Webcontainer not initialized';
49
+ }
50
+
51
+ fileData.current = {};
52
+ await git.clone({
53
+ fs,
54
+ http,
55
+ dir: webcontainer.workdir,
56
+ url,
57
+ depth: 1,
58
+ singleBranch: true,
59
+ corsProxy: 'https://cors.isomorphic-git.org',
60
+ onAuth: (url) => {
61
+ // let domain=url.split("/")[2]
62
+
63
+ let auth = lookupSavedPassword(url);
64
+
65
+ if (auth) {
66
+ return auth;
67
+ }
68
+
69
+ if (confirm('This repo is password protected. Ready to enter a username & password?')) {
70
+ auth = {
71
+ username: prompt('Enter username'),
72
+ password: prompt('Enter password'),
73
+ };
74
+ return auth;
75
+ } else {
76
+ return { cancel: true };
77
+ }
78
+ },
79
+ onAuthFailure: (url, _auth) => {
80
+ toast.error(`Error Authenticating with ${url.split('/')[2]}`);
81
+ },
82
+ onAuthSuccess: (url, auth) => {
83
+ saveGitAuth(url, auth);
84
+ },
85
+ });
86
+
87
+ const data: Record<string, { data: any; encoding?: string }> = {};
88
+
89
+ for (const [key, value] of Object.entries(fileData.current)) {
90
+ data[key] = value;
91
+ }
92
+
93
+ return { workdir: webcontainer.workdir, data };
94
+ },
95
+ [webcontainer],
96
+ );
97
+
98
+ return { ready, gitClone };
99
+ }
100
+
101
+ const getFs = (
102
+ webcontainer: WebContainer,
103
+ record: MutableRefObject<Record<string, { data: any; encoding?: string }>>,
104
+ ) => ({
105
+ promises: {
106
+ readFile: async (path: string, options: any) => {
107
+ const encoding = options.encoding;
108
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
109
+ console.log('readFile', relativePath, encoding);
110
+
111
+ return await webcontainer.fs.readFile(relativePath, encoding);
112
+ },
113
+ writeFile: async (path: string, data: any, options: any) => {
114
+ const encoding = options.encoding;
115
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
116
+ console.log('writeFile', { relativePath, data, encoding });
117
+
118
+ if (record.current) {
119
+ record.current[relativePath] = { data, encoding };
120
+ }
121
+
122
+ return await webcontainer.fs.writeFile(relativePath, data, { ...options, encoding });
123
+ },
124
+ mkdir: async (path: string, options: any) => {
125
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
126
+ console.log('mkdir', relativePath, options);
127
+
128
+ return await webcontainer.fs.mkdir(relativePath, { ...options, recursive: true });
129
+ },
130
+ readdir: async (path: string, options: any) => {
131
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
132
+ console.log('readdir', relativePath, options);
133
+
134
+ return await webcontainer.fs.readdir(relativePath, options);
135
+ },
136
+ rm: async (path: string, options: any) => {
137
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
138
+ console.log('rm', relativePath, options);
139
+
140
+ return await webcontainer.fs.rm(relativePath, { ...(options || {}) });
141
+ },
142
+ rmdir: async (path: string, options: any) => {
143
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
144
+ console.log('rmdir', relativePath, options);
145
+
146
+ return await webcontainer.fs.rm(relativePath, { recursive: true, ...options });
147
+ },
148
+
149
+ // Mock implementations for missing functions
150
+ unlink: async (path: string) => {
151
+ // unlink is just removing a single file
152
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
153
+ return await webcontainer.fs.rm(relativePath, { recursive: false });
154
+ },
155
+
156
+ stat: async (path: string) => {
157
+ try {
158
+ const relativePath = pathUtils.relative(webcontainer.workdir, path);
159
+ const resp = await webcontainer.fs.readdir(pathUtils.dirname(relativePath), { withFileTypes: true });
160
+ const name = pathUtils.basename(relativePath);
161
+ const fileInfo = resp.find((x) => x.name == name);
162
+
163
+ if (!fileInfo) {
164
+ throw new Error(`ENOENT: no such file or directory, stat '${path}'`);
165
+ }
166
+
167
+ return {
168
+ isFile: () => fileInfo.isFile(),
169
+ isDirectory: () => fileInfo.isDirectory(),
170
+ isSymbolicLink: () => false,
171
+ size: 1,
172
+ mode: 0o666, // Default permissions
173
+ mtimeMs: Date.now(),
174
+ uid: 1000,
175
+ gid: 1000,
176
+ };
177
+ } catch (error: any) {
178
+ console.log(error?.message);
179
+
180
+ const err = new Error(`ENOENT: no such file or directory, stat '${path}'`) as NodeJS.ErrnoException;
181
+ err.code = 'ENOENT';
182
+ err.errno = -2;
183
+ err.syscall = 'stat';
184
+ err.path = path;
185
+ throw err;
186
+ }
187
+ },
188
+
189
+ lstat: async (path: string) => {
190
+ /*
191
+ * For basic usage, lstat can return the same as stat
192
+ * since we're not handling symbolic links
193
+ */
194
+ return await getFs(webcontainer, record).promises.stat(path);
195
+ },
196
+
197
+ readlink: async (path: string) => {
198
+ /*
199
+ * Since WebContainer doesn't support symlinks,
200
+ * we'll throw a "not a symbolic link" error
201
+ */
202
+ throw new Error(`EINVAL: invalid argument, readlink '${path}'`);
203
+ },
204
+
205
+ symlink: async (target: string, path: string) => {
206
+ /*
207
+ * Since WebContainer doesn't support symlinks,
208
+ * we'll throw a "operation not supported" error
209
+ */
210
+ throw new Error(`EPERM: operation not permitted, symlink '${target}' -> '${path}'`);
211
+ },
212
+
213
+ chmod: async (_path: string, _mode: number) => {
214
+ /*
215
+ * WebContainer doesn't support changing permissions,
216
+ * but we can pretend it succeeded for compatibility
217
+ */
218
+ return await Promise.resolve();
219
+ },
220
+ },
221
+ });
222
+
223
+ const pathUtils = {
224
+ dirname: (path: string) => {
225
+ // Handle empty or just filename cases
226
+ if (!path || !path.includes('/')) {
227
+ return '.';
228
+ }
229
+
230
+ // Remove trailing slashes
231
+ path = path.replace(/\/+$/, '');
232
+
233
+ // Get directory part
234
+ return path.split('/').slice(0, -1).join('/') || '/';
235
+ },
236
+
237
+ basename: (path: string, ext?: string) => {
238
+ // Remove trailing slashes
239
+ path = path.replace(/\/+$/, '');
240
+
241
+ // Get the last part of the path
242
+ const base = path.split('/').pop() || '';
243
+
244
+ // If extension is provided, remove it from the result
245
+ if (ext && base.endsWith(ext)) {
246
+ return base.slice(0, -ext.length);
247
+ }
248
+
249
+ return base;
250
+ },
251
+ relative: (from: string, to: string): string => {
252
+ // Handle empty inputs
253
+ if (!from || !to) {
254
+ return '.';
255
+ }
256
+
257
+ // Normalize paths by removing trailing slashes and splitting
258
+ const normalizePathParts = (p: string) => p.replace(/\/+$/, '').split('/').filter(Boolean);
259
+
260
+ const fromParts = normalizePathParts(from);
261
+ const toParts = normalizePathParts(to);
262
+
263
+ // Find common parts at the start of both paths
264
+ let commonLength = 0;
265
+ const minLength = Math.min(fromParts.length, toParts.length);
266
+
267
+ for (let i = 0; i < minLength; i++) {
268
+ if (fromParts[i] !== toParts[i]) {
269
+ break;
270
+ }
271
+
272
+ commonLength++;
273
+ }
274
+
275
+ // Calculate the number of "../" needed
276
+ const upCount = fromParts.length - commonLength;
277
+
278
+ // Get the remaining path parts we need to append
279
+ const remainingPath = toParts.slice(commonLength);
280
+
281
+ // Construct the relative path
282
+ const relativeParts = [...Array(upCount).fill('..'), ...remainingPath];
283
+
284
+ // Handle empty result case
285
+ return relativeParts.length === 0 ? '.' : relativeParts.join('/');
286
+ },
287
+ };
app/lib/stores/settings.ts CHANGED
@@ -15,10 +15,33 @@ export interface Shortcuts {
15
  toggleTerminal: Shortcut;
16
  }
17
 
 
 
 
 
 
18
  export interface Settings {
19
  shortcuts: Shortcuts;
 
20
  }
21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
  export const shortcutsStore = map<Shortcuts>({
23
  toggleTerminal: {
24
  key: 'j',
@@ -29,6 +52,7 @@ export const shortcutsStore = map<Shortcuts>({
29
 
30
  export const settingsStore = map<Settings>({
31
  shortcuts: shortcutsStore.get(),
 
32
  });
33
 
34
  shortcutsStore.subscribe((shortcuts) => {
 
15
  toggleTerminal: Shortcut;
16
  }
17
 
18
+ export interface Provider {
19
+ name: string;
20
+ isEnabled: boolean;
21
+ }
22
+
23
  export interface Settings {
24
  shortcuts: Shortcuts;
25
+ providers: Provider[];
26
  }
27
 
28
+ export const providersList: Provider[] = [
29
+ { name: 'Groq', isEnabled: false },
30
+ { name: 'HuggingFace', isEnabled: false },
31
+ { name: 'OpenAI', isEnabled: false },
32
+ { name: 'Anthropic', isEnabled: false },
33
+ { name: 'OpenRouter', isEnabled: false },
34
+ { name: 'Google', isEnabled: false },
35
+ { name: 'Ollama', isEnabled: false },
36
+ { name: 'OpenAILike', isEnabled: false },
37
+ { name: 'Together', isEnabled: false },
38
+ { name: 'Deepseek', isEnabled: false },
39
+ { name: 'Mistral', isEnabled: false },
40
+ { name: 'Cohere', isEnabled: false },
41
+ { name: 'LMStudio', isEnabled: false },
42
+ { name: 'xAI', isEnabled: false },
43
+ ];
44
+
45
  export const shortcutsStore = map<Shortcuts>({
46
  toggleTerminal: {
47
  key: 'j',
 
52
 
53
  export const settingsStore = map<Settings>({
54
  shortcuts: shortcutsStore.get(),
55
+ providers: providersList,
56
  });
57
 
58
  shortcutsStore.subscribe((shortcuts) => {
app/lib/stores/workbench.ts CHANGED
@@ -15,6 +15,7 @@ import { Octokit, type RestEndpointMethodTypes } from '@octokit/rest';
15
  import * as nodePath from 'node:path';
16
  import { extractRelativePath } from '~/utils/diff';
17
  import { description } from '~/lib/persistence';
 
18
 
19
  export interface ArtifactState {
20
  id: string;
@@ -402,15 +403,14 @@ export class WorkbenchStore {
402
  return syncedFiles;
403
  }
404
 
405
- async pushToGitHub(repoName: string, githubUsername: string, ghToken: string) {
406
  try {
407
- // Get the GitHub auth token from environment variables
408
- const githubToken = ghToken;
 
409
 
410
- const owner = githubUsername;
411
-
412
- if (!githubToken) {
413
- throw new Error('GitHub token is not set in environment variables');
414
  }
415
 
416
  // Initialize Octokit with the auth token
@@ -507,7 +507,8 @@ export class WorkbenchStore {
507
 
508
  alert(`Repository created and code pushed: ${repo.html_url}`);
509
  } catch (error) {
510
- console.error('Error pushing to GitHub:', error instanceof Error ? error.message : String(error));
 
511
  }
512
  }
513
  }
 
15
  import * as nodePath from 'node:path';
16
  import { extractRelativePath } from '~/utils/diff';
17
  import { description } from '~/lib/persistence';
18
+ import Cookies from 'js-cookie';
19
 
20
  export interface ArtifactState {
21
  id: string;
 
403
  return syncedFiles;
404
  }
405
 
406
+ async pushToGitHub(repoName: string, githubUsername?: string, ghToken?: string) {
407
  try {
408
+ // Use cookies if username and token are not provided
409
+ const githubToken = ghToken || Cookies.get('githubToken');
410
+ const owner = githubUsername || Cookies.get('githubUsername');
411
 
412
+ if (!githubToken || !owner) {
413
+ throw new Error('GitHub token or username is not set in cookies or provided.');
 
 
414
  }
415
 
416
  // Initialize Octokit with the auth token
 
507
 
508
  alert(`Repository created and code pushed: ${repo.html_url}`);
509
  } catch (error) {
510
+ console.error('Error pushing to GitHub:', error);
511
+ throw error; // Rethrow the error for further handling
512
  }
513
  }
514
  }
app/routes/_index.tsx CHANGED
@@ -3,6 +3,7 @@ import { ClientOnly } from 'remix-utils/client-only';
3
  import { BaseChat } from '~/components/chat/BaseChat';
4
  import { Chat } from '~/components/chat/Chat.client';
5
  import { Header } from '~/components/header/Header';
 
6
 
7
  export const meta: MetaFunction = () => {
8
  return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }];
@@ -12,7 +13,8 @@ export const loader = () => json({});
12
 
13
  export default function Index() {
14
  return (
15
- <div className="flex flex-col h-full w-full">
 
16
  <Header />
17
  <ClientOnly fallback={<BaseChat />}>{() => <Chat />}</ClientOnly>
18
  </div>
 
3
  import { BaseChat } from '~/components/chat/BaseChat';
4
  import { Chat } from '~/components/chat/Chat.client';
5
  import { Header } from '~/components/header/Header';
6
+ import BackgroundRays from '~/components/ui/BackgroundRays';
7
 
8
  export const meta: MetaFunction = () => {
9
  return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }];
 
13
 
14
  export default function Index() {
15
  return (
16
+ <div className="flex flex-col h-full w-full bg-bolt-elements-background-depth-1">
17
+ <BackgroundRays />
18
  <Header />
19
  <ClientOnly fallback={<BaseChat />}>{() => <Chat />}</ClientOnly>
20
  </div>
app/routes/git.tsx ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { LoaderFunctionArgs } from '@remix-run/cloudflare';
2
+ import { json, type MetaFunction } from '@remix-run/cloudflare';
3
+ import { ClientOnly } from 'remix-utils/client-only';
4
+ import { BaseChat } from '~/components/chat/BaseChat';
5
+ import { GitUrlImport } from '~/components/git/GitUrlImport.client';
6
+ import { Header } from '~/components/header/Header';
7
+
8
+ export const meta: MetaFunction = () => {
9
+ return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }];
10
+ };
11
+
12
+ export async function loader(args: LoaderFunctionArgs) {
13
+ return json({ url: args.params.url });
14
+ }
15
+
16
+ export default function Index() {
17
+ return (
18
+ <div className="flex flex-col h-full w-full">
19
+ <Header />
20
+ <ClientOnly fallback={<BaseChat />}>{() => <GitUrlImport />}</ClientOnly>
21
+ </div>
22
+ );
23
+ }
app/styles/index.scss CHANGED
@@ -12,3 +12,13 @@ body {
12
  height: 100%;
13
  width: 100%;
14
  }
 
 
 
 
 
 
 
 
 
 
 
12
  height: 100%;
13
  width: 100%;
14
  }
15
+
16
+ :root {
17
+ --gradient-opacity: 0.8;
18
+ --primary-color: rgba(158, 117, 240, var(--gradient-opacity));
19
+ --secondary-color: rgba(138, 43, 226, var(--gradient-opacity));
20
+ --accent-color: rgba(128, 59, 239, var(--gradient-opacity));
21
+ // --primary-color: rgba(147, 112, 219, var(--gradient-opacity));
22
+ // --secondary-color: rgba(138, 43, 226, var(--gradient-opacity));
23
+ // --accent-color: rgba(180, 170, 220, var(--gradient-opacity));
24
+ }
app/types/model.ts CHANGED
@@ -7,4 +7,5 @@ export type ProviderInfo = {
7
  getApiKeyLink?: string;
8
  labelForGetApiKey?: string;
9
  icon?: string;
 
10
  };
 
7
  getApiKeyLink?: string;
8
  labelForGetApiKey?: string;
9
  icon?: string;
10
+ isEnabled?: boolean;
11
  };
app/utils/constants.ts CHANGED
@@ -1,6 +1,7 @@
1
  import Cookies from 'js-cookie';
2
  import type { ModelInfo, OllamaApiResponse, OllamaModel } from './types';
3
  import type { ProviderInfo } from '~/types/model';
 
4
 
5
  export const WORK_DIR_NAME = 'project';
6
  export const WORK_DIR = `/home/${WORK_DIR_NAME}`;
@@ -10,6 +11,8 @@ export const PROVIDER_REGEX = /\[Provider: (.*?)\]\n\n/;
10
  export const DEFAULT_MODEL = 'claude-3-5-sonnet-latest';
11
  export const PROMPT_COOKIE_KEY = 'cachedPrompt';
12
 
 
 
13
  const PROVIDER_LIST: ProviderInfo[] = [
14
  {
15
  name: 'Anthropic',
@@ -127,7 +130,7 @@ const PROVIDER_LIST: ProviderInfo[] = [
127
  { name: 'gemini-1.5-flash-8b', label: 'Gemini 1.5 Flash-8b', provider: 'Google', maxTokenAllowed: 8192 },
128
  { name: 'gemini-1.5-pro-latest', label: 'Gemini 1.5 Pro', provider: 'Google', maxTokenAllowed: 8192 },
129
  { name: 'gemini-1.5-pro-002', label: 'Gemini 1.5 Pro-002', provider: 'Google', maxTokenAllowed: 8192 },
130
- { name: 'gemini-exp-1121', label: 'Gemini exp-1121', provider: 'Google', maxTokenAllowed: 8192 },
131
  ],
132
  getApiKeyLink: 'https://aistudio.google.com/app/apikey',
133
  },
@@ -383,8 +386,8 @@ async function getOllamaModels(): Promise<ModelInfo[]> {
383
  provider: 'Ollama',
384
  maxTokenAllowed: 8000,
385
  }));
386
- } catch (e) {
387
- console.error('Error getting Ollama models:', e);
388
  return [];
389
  }
390
  }
@@ -471,8 +474,8 @@ async function getLMStudioModels(): Promise<ModelInfo[]> {
471
  label: model.id,
472
  provider: 'LMStudio',
473
  }));
474
- } catch (e) {
475
- console.error('Error getting LMStudio models:', e);
476
  return [];
477
  }
478
  }
@@ -491,7 +494,7 @@ async function initializeModelList(): Promise<ModelInfo[]> {
491
  }
492
  }
493
  } catch (error: any) {
494
- console.warn(`Failed to fetch apikeys from cookies:${error?.message}`);
495
  }
496
  MODEL_LIST = [
497
  ...(
 
1
  import Cookies from 'js-cookie';
2
  import type { ModelInfo, OllamaApiResponse, OllamaModel } from './types';
3
  import type { ProviderInfo } 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}`;
 
11
  export const DEFAULT_MODEL = 'claude-3-5-sonnet-latest';
12
  export const PROMPT_COOKIE_KEY = 'cachedPrompt';
13
 
14
+ const logger = createScopedLogger('Constants');
15
+
16
  const PROVIDER_LIST: ProviderInfo[] = [
17
  {
18
  name: 'Anthropic',
 
130
  { name: 'gemini-1.5-flash-8b', label: 'Gemini 1.5 Flash-8b', provider: 'Google', maxTokenAllowed: 8192 },
131
  { name: 'gemini-1.5-pro-latest', label: 'Gemini 1.5 Pro', provider: 'Google', maxTokenAllowed: 8192 },
132
  { name: 'gemini-1.5-pro-002', label: 'Gemini 1.5 Pro-002', provider: 'Google', maxTokenAllowed: 8192 },
133
+ { name: 'gemini-exp-1206', label: 'Gemini exp-1206', provider: 'Google', maxTokenAllowed: 8192 },
134
  ],
135
  getApiKeyLink: 'https://aistudio.google.com/app/apikey',
136
  },
 
386
  provider: 'Ollama',
387
  maxTokenAllowed: 8000,
388
  }));
389
+ } catch (e: any) {
390
+ logger.warn('Failed to get Ollama models: ', e.message || '');
391
  return [];
392
  }
393
  }
 
474
  label: model.id,
475
  provider: 'LMStudio',
476
  }));
477
+ } catch (e: any) {
478
+ logger.warn('Failed to get LMStudio models: ', e.message || '');
479
  return [];
480
  }
481
  }
 
494
  }
495
  }
496
  } catch (error: any) {
497
+ logger.warn(`Failed to fetch apikeys from cookies: ${error?.message}`);
498
  }
499
  MODEL_LIST = [
500
  ...(
app/utils/fileUtils.ts ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ignore from 'ignore';
2
+
3
+ // Common patterns to ignore, similar to .gitignore
4
+ export const IGNORE_PATTERNS = [
5
+ 'node_modules/**',
6
+ '.git/**',
7
+ 'dist/**',
8
+ 'build/**',
9
+ '.next/**',
10
+ 'coverage/**',
11
+ '.cache/**',
12
+ '.vscode/**',
13
+ '.idea/**',
14
+ '**/*.log',
15
+ '**/.DS_Store',
16
+ '**/npm-debug.log*',
17
+ '**/yarn-debug.log*',
18
+ '**/yarn-error.log*',
19
+ ];
20
+
21
+ export const MAX_FILES = 1000;
22
+ export const ig = ignore().add(IGNORE_PATTERNS);
23
+
24
+ export const generateId = () => Math.random().toString(36).substring(2, 15);
25
+
26
+ export const isBinaryFile = async (file: File): Promise<boolean> => {
27
+ const chunkSize = 1024;
28
+ const buffer = new Uint8Array(await file.slice(0, chunkSize).arrayBuffer());
29
+
30
+ for (let i = 0; i < buffer.length; i++) {
31
+ const byte = buffer[i];
32
+
33
+ if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) {
34
+ return true;
35
+ }
36
+ }
37
+
38
+ return false;
39
+ };
40
+
41
+ export const shouldIncludeFile = (path: string): boolean => {
42
+ return !ig.ignores(path);
43
+ };
44
+
45
+ const readPackageJson = async (files: File[]): Promise<{ scripts?: Record<string, string> } | null> => {
46
+ const packageJsonFile = files.find((f) => f.webkitRelativePath.endsWith('package.json'));
47
+
48
+ if (!packageJsonFile) {
49
+ return null;
50
+ }
51
+
52
+ try {
53
+ const content = await new Promise<string>((resolve, reject) => {
54
+ const reader = new FileReader();
55
+ reader.onload = () => resolve(reader.result as string);
56
+ reader.onerror = reject;
57
+ reader.readAsText(packageJsonFile);
58
+ });
59
+
60
+ return JSON.parse(content);
61
+ } catch (error) {
62
+ console.error('Error reading package.json:', error);
63
+ return null;
64
+ }
65
+ };
66
+
67
+ export const detectProjectType = async (
68
+ files: File[],
69
+ ): Promise<{ type: string; setupCommand: string; followupMessage: string }> => {
70
+ const hasFile = (name: string) => files.some((f) => f.webkitRelativePath.endsWith(name));
71
+
72
+ if (hasFile('package.json')) {
73
+ const packageJson = await readPackageJson(files);
74
+ const scripts = packageJson?.scripts || {};
75
+
76
+ // Check for preferred commands in priority order
77
+ const preferredCommands = ['dev', 'start', 'preview'];
78
+ const availableCommand = preferredCommands.find((cmd) => scripts[cmd]);
79
+
80
+ if (availableCommand) {
81
+ return {
82
+ type: 'Node.js',
83
+ setupCommand: `npm install && npm run ${availableCommand}`,
84
+ followupMessage: `Found "${availableCommand}" script in package.json. Running "npm run ${availableCommand}" after installation.`,
85
+ };
86
+ }
87
+
88
+ return {
89
+ type: 'Node.js',
90
+ setupCommand: 'npm install',
91
+ followupMessage:
92
+ 'Would you like me to inspect package.json to determine the available scripts for running this project?',
93
+ };
94
+ }
95
+
96
+ if (hasFile('index.html')) {
97
+ return {
98
+ type: 'Static',
99
+ setupCommand: 'npx --yes serve',
100
+ followupMessage: '',
101
+ };
102
+ }
103
+
104
+ return { type: '', setupCommand: '', followupMessage: '' };
105
+ };
app/utils/folderImport.ts ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Message } from 'ai';
2
+ import { generateId } from './fileUtils';
3
+ import { detectProjectCommands, createCommandsMessage } from './projectCommands';
4
+
5
+ export const createChatFromFolder = async (
6
+ files: File[],
7
+ binaryFiles: string[],
8
+ folderName: string,
9
+ ): Promise<Message[]> => {
10
+ const fileArtifacts = await Promise.all(
11
+ files.map(async (file) => {
12
+ return new Promise<{ content: string; path: string }>((resolve, reject) => {
13
+ const reader = new FileReader();
14
+
15
+ reader.onload = () => {
16
+ const content = reader.result as string;
17
+ const relativePath = file.webkitRelativePath.split('/').slice(1).join('/');
18
+ resolve({
19
+ content,
20
+ path: relativePath,
21
+ });
22
+ };
23
+ reader.onerror = reject;
24
+ reader.readAsText(file);
25
+ });
26
+ }),
27
+ );
28
+
29
+ const commands = await detectProjectCommands(fileArtifacts);
30
+ const commandsMessage = createCommandsMessage(commands);
31
+
32
+ const binaryFilesMessage =
33
+ binaryFiles.length > 0
34
+ ? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}`
35
+ : '';
36
+
37
+ const filesMessage: Message = {
38
+ role: 'assistant',
39
+ content: `I've imported the contents of the "${folderName}" folder.${binaryFilesMessage}
40
+
41
+ <boltArtifact id="imported-files" title="Imported Files">
42
+ ${fileArtifacts
43
+ .map(
44
+ (file) => `<boltAction type="file" filePath="${file.path}">
45
+ ${file.content}
46
+ </boltAction>`,
47
+ )
48
+ .join('\n\n')}
49
+ </boltArtifact>`,
50
+ id: generateId(),
51
+ createdAt: new Date(),
52
+ };
53
+
54
+ const userMessage: Message = {
55
+ role: 'user',
56
+ id: generateId(),
57
+ content: `Import the "${folderName}" folder`,
58
+ createdAt: new Date(),
59
+ };
60
+
61
+ const messages = [userMessage, filesMessage];
62
+
63
+ if (commandsMessage) {
64
+ messages.push(commandsMessage);
65
+ }
66
+
67
+ return messages;
68
+ };
app/utils/projectCommands.ts ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import type { Message } from 'ai';
2
+ import { generateId } from './fileUtils';
3
+
4
+ export interface ProjectCommands {
5
+ type: string;
6
+ setupCommand: string;
7
+ followupMessage: string;
8
+ }
9
+
10
+ interface FileContent {
11
+ content: string;
12
+ path: string;
13
+ }
14
+
15
+ export async function detectProjectCommands(files: FileContent[]): Promise<ProjectCommands> {
16
+ const hasFile = (name: string) => files.some((f) => f.path.endsWith(name));
17
+
18
+ if (hasFile('package.json')) {
19
+ const packageJsonFile = files.find((f) => f.path.endsWith('package.json'));
20
+
21
+ if (!packageJsonFile) {
22
+ return { type: '', setupCommand: '', followupMessage: '' };
23
+ }
24
+
25
+ try {
26
+ const packageJson = JSON.parse(packageJsonFile.content);
27
+ const scripts = packageJson?.scripts || {};
28
+
29
+ // Check for preferred commands in priority order
30
+ const preferredCommands = ['dev', 'start', 'preview'];
31
+ const availableCommand = preferredCommands.find((cmd) => scripts[cmd]);
32
+
33
+ if (availableCommand) {
34
+ return {
35
+ type: 'Node.js',
36
+ setupCommand: `npm install && npm run ${availableCommand}`,
37
+ followupMessage: `Found "${availableCommand}" script in package.json. Running "npm run ${availableCommand}" after installation.`,
38
+ };
39
+ }
40
+
41
+ return {
42
+ type: 'Node.js',
43
+ setupCommand: 'npm install',
44
+ followupMessage:
45
+ 'Would you like me to inspect package.json to determine the available scripts for running this project?',
46
+ };
47
+ } catch (error) {
48
+ console.error('Error parsing package.json:', error);
49
+ return { type: '', setupCommand: '', followupMessage: '' };
50
+ }
51
+ }
52
+
53
+ if (hasFile('index.html')) {
54
+ return {
55
+ type: 'Static',
56
+ setupCommand: 'npx --yes serve',
57
+ followupMessage: '',
58
+ };
59
+ }
60
+
61
+ return { type: '', setupCommand: '', followupMessage: '' };
62
+ }
63
+
64
+ export function createCommandsMessage(commands: ProjectCommands): Message | null {
65
+ if (!commands.setupCommand) {
66
+ return null;
67
+ }
68
+
69
+ return {
70
+ role: 'assistant',
71
+ content: `
72
+ <boltArtifact id="project-setup" title="Project Setup">
73
+ <boltAction type="shell">
74
+ ${commands.setupCommand}
75
+ </boltAction>
76
+ </boltArtifact>${commands.followupMessage ? `\n\n${commands.followupMessage}` : ''}`,
77
+ id: generateId(),
78
+ createdAt: new Date(),
79
+ };
80
+ }
docs/docs/FAQ.md CHANGED
@@ -1,52 +1,81 @@
1
- # FAQ
2
 
3
- ### How do I get the best results with oTToDev?
4
 
5
- - **Be specific about your stack**: If you want to use specific frameworks or libraries (like Astro, Tailwind, ShadCN, or any other popular JavaScript framework), mention them in your initial prompt to ensure Bolt scaffolds the project accordingly.
 
6
 
7
- - **Use the enhance prompt icon**: Before sending your prompt, try clicking the 'enhance' icon to have the AI model help you refine your prompt, then edit the results before submitting.
 
8
 
9
- - **Scaffold the basics first, then add features**: Make sure the basic structure of your application is in place before diving into more advanced functionality. This helps oTToDev understand the foundation of your project and ensure everything is wired up right before building out more advanced functionality.
 
10
 
11
- - **Batch simple instructions**: Save time by combining simple instructions into one message. For example, you can ask oTToDev to change the color scheme, add mobile responsiveness, and restart the dev server, all in one go saving you time and reducing API credit consumption significantly.
 
 
12
 
13
- ### How do I contribute to oTToDev?
14
 
15
- [Please check out our dedicated page for contributing to oTToDev here!](CONTRIBUTING.md)
16
 
17
- ### Do you plan on merging oTToDev back into the official Bolt.new repo?
18
 
19
- More news coming on this coming early next month - stay tuned!
20
 
21
- ### What are the future plans for oTToDev?
22
 
23
- [Check out our Roadmap here!](https://roadmap.sh/r/ottodev-roadmap-2ovzo)
24
 
25
- Lot more updates to this roadmap coming soon!
26
 
27
- ### Why are there so many open issues/pull requests?
28
 
29
- oTToDev was started simply to showcase how to edit an open source project and to do something cool with local LLMs on my (@ColeMedin) YouTube channel! However, it quickly
30
- grew into a massive community project that I am working hard to keep up with the demand of by forming a team of maintainers and getting as many people involved as I can.
31
- That effort is going well and all of our maintainers are ABSOLUTE rockstars, but it still takes time to organize everything so we can efficiently get through all
32
- the issues and PRs. But rest assured, we are working hard and even working on some partnerships behind the scenes to really help this project take off!
33
 
34
- ### How do local LLMs fair compared to larger models like Claude 3.5 Sonnet for oTToDev/Bolt.new?
35
 
36
- As much as the gap is quickly closing between open source and massive close source models, you’re still going to get the best results with the very large models like GPT-4o, Claude 3.5 Sonnet, and DeepSeek Coder V2 236b. This is one of the big tasks we have at hand - figuring out how to prompt better, use agents, and improve the platform as a whole to make it work better for even the smaller local LLMs!
37
 
38
- ### I'm getting the error: "There was an error processing this request"
39
 
40
- If you see this error within oTToDev, that is just the application telling you there is a problem at a high level, and this could mean a number of different things. To find the actual error, please check BOTH the terminal where you started the application (with Docker or pnpm) and the developer console in the browser. For most browsers, you can access the developer console by pressing F12 or right clicking anywhere in the browser and selecting “Inspect”. Then go to the “console” tab in the top right.
41
 
42
- ### I'm getting the error: "x-api-key header missing"
43
 
44
- We have seen this error a couple times and for some reason just restarting the Docker container has fixed it. This seems to be Ollama specific. Another thing to try is try to run oTToDev with Docker or pnpm, whichever you didn’t run first. We are still on the hunt for why this happens once and a while!
45
 
46
- ### I'm getting a blank preview when oTToDev runs my app!
47
 
48
- We promise you that we are constantly testing new PRs coming into oTToDev and the preview is core functionality, so the application is not broken! When you get a blank preview or don’t get a preview, this is generally because the LLM hallucinated bad code or incorrect commands. We are working on making this more transparent so it is obvious. Sometimes the error will appear in developer console too so check that as well.
49
 
50
- ### Everything works but the results are bad
51
 
52
- This goes to the point above about how local LLMs are getting very powerful but you still are going to see better (sometimes much better) results with the largest LLMs like GPT-4o, Claude 3.5 Sonnet, and DeepSeek Coder V2 236b. If you are using smaller LLMs like Qwen-2.5-Coder, consider it more experimental and educational at this point. It can build smaller applications really well, which is super impressive for a local LLM, but for larger scale applications you want to use the larger LLMs still!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Frequently Asked Questions (FAQ)
2
 
3
+ ## How do I get the best results with oTToDev?
4
 
5
+ - **Be specific about your stack**:
6
+ Mention the frameworks or libraries you want to use (e.g., Astro, Tailwind, ShadCN) in your initial prompt. This ensures that oTToDev scaffolds the project according to your preferences.
7
 
8
+ - **Use the enhance prompt icon**:
9
+ Before sending your prompt, click the *enhance* icon to let the AI refine your prompt. You can edit the suggested improvements before submitting.
10
 
11
+ - **Scaffold the basics first, then add features**:
12
+ Ensure the foundational structure of your application is in place before introducing advanced functionality. This helps oTToDev establish a solid base to build on.
13
 
14
+ - **Batch simple instructions**:
15
+ Combine simple tasks into a single prompt to save time and reduce API credit consumption. For example:
16
+ *"Change the color scheme, add mobile responsiveness, and restart the dev server."*
17
 
18
+ ---
19
 
20
+ ## How do I contribute to oTToDev?
21
 
22
+ Check out our [Contribution Guide](CONTRIBUTING.md) for more details on how to get involved!
23
 
24
+ ---
25
 
26
+ ## Do you plan on merging oTToDev back into the official Bolt.new repo?
27
 
28
+ Stay tuned! We’ll share updates on this early next month.
29
 
30
+ ---
31
 
32
+ ## What are the future plans for oTToDev?
33
 
34
+ Visit our [Roadmap](https://roadmap.sh/r/ottodev-roadmap-2ovzo) for the latest updates.
35
+ New features and improvements are on the way!
 
 
36
 
37
+ ---
38
 
39
+ ## Why are there so many open issues/pull requests?
40
 
41
+ oTToDev began as a small showcase project on @ColeMedin's YouTube channel to explore editing open-source projects with local LLMs. However, it quickly grew into a massive community effort!
42
 
43
+ We’re forming a team of maintainers to manage demand and streamline issue resolution. The maintainers are rockstars, and we’re also exploring partnerships to help the project thrive.
44
 
45
+ ---
46
 
47
+ ## How do local LLMs compare to larger models like Claude 3.5 Sonnet for oTToDev/Bolt.new?
48
 
49
+ While local LLMs are improving rapidly, larger models like GPT-4o, Claude 3.5 Sonnet, and DeepSeek Coder V2 236b still offer the best results for complex applications. Our ongoing focus is to improve prompts, agents, and the platform to better support smaller local LLMs.
50
 
51
+ ---
52
 
53
+ ## Common Errors and Troubleshooting
54
 
55
+ ### **"There was an error processing this request"**
56
+ This generic error message means something went wrong. Check both:
57
+ - The terminal (if you started the app with Docker or `pnpm`).
58
+ - The developer console in your browser (press `F12` or right-click > *Inspect*, then go to the *Console* tab).
59
+
60
+ ---
61
+
62
+ ### **"x-api-key header missing"**
63
+ This error is sometimes resolved by restarting the Docker container.
64
+ If that doesn’t work, try switching from Docker to `pnpm` or vice versa. We’re actively investigating this issue.
65
+
66
+ ---
67
+
68
+ ### **Blank preview when running the app**
69
+ A blank preview often occurs due to hallucinated bad code or incorrect commands.
70
+ To troubleshoot:
71
+ - Check the developer console for errors.
72
+ - Remember, previews are core functionality, so the app isn’t broken! We’re working on making these errors more transparent.
73
+
74
+ ---
75
+
76
+ ### **"Everything works, but the results are bad"**
77
+ Local LLMs like Qwen-2.5-Coder are powerful for small applications but still experimental for larger projects. For better results, consider using larger models like GPT-4o, Claude 3.5 Sonnet, or DeepSeek Coder V2 236b.
78
+
79
+ ---
80
+
81
+ Got more questions? Feel free to reach out or open an issue in our GitHub repo!
docs/docs/index.md CHANGED
@@ -148,31 +148,6 @@ sudo npm install -g pnpm
148
  pnpm run dev
149
  ```
150
 
151
- ## Super Important Note on Running Ollama Models
152
-
153
- Ollama models by default only have 2048 tokens for their context window. Even for large models that can easily handle way more.
154
- This is not a large enough window to handle the Bolt.new/oTToDev prompt! You have to create a version of any model you want
155
- to use where you specify a larger context window. Luckily it's super easy to do that.
156
-
157
- All you have to do is:
158
-
159
- - Create a file called "Modelfile" (no file extension) anywhere on your computer
160
- - Put in the two lines:
161
-
162
- ```
163
- FROM [Ollama model ID such as qwen2.5-coder:7b]
164
- PARAMETER num_ctx 32768
165
- ```
166
-
167
- - Run the command:
168
-
169
- ```
170
- ollama create -f Modelfile [your new model ID, can be whatever you want (example: qwen2.5-coder-extra-ctx:7b)]
171
- ```
172
-
173
- Now you have a new Ollama model that isn't heavily limited in the context length like Ollama models are by default for some reason.
174
- You'll see this new model in the list of Ollama models along with all the others you pulled!
175
-
176
  ## Adding New LLMs:
177
 
178
  To make new LLMs available to use in this version of Bolt.new, head on over to `app/utils/constants.ts` and find the constant MODEL_LIST. Each element in this array is an object that has the model ID for the name (get this from the provider's API documentation), a label for the frontend model dropdown, and the provider.
 
148
  pnpm run dev
149
  ```
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  ## Adding New LLMs:
152
 
153
  To make new LLMs available to use in this version of Bolt.new, head on over to `app/utils/constants.ts` and find the constant MODEL_LIST. Each element in this array is an object that has the model ID for the name (get this from the provider's API documentation), a label for the frontend model dropdown, and the provider.
package.json CHANGED
@@ -44,6 +44,7 @@
44
  "@codemirror/lang-markdown": "^6.3.1",
45
  "@codemirror/lang-python": "^6.1.6",
46
  "@codemirror/lang-sass": "^6.0.2",
 
47
  "@codemirror/lang-wast": "^6.0.2",
48
  "@codemirror/language": "^6.10.6",
49
  "@codemirror/search": "^6.5.8",
@@ -58,6 +59,8 @@
58
  "@openrouter/ai-sdk-provider": "^0.0.5",
59
  "@radix-ui/react-dialog": "^1.1.2",
60
  "@radix-ui/react-dropdown-menu": "^2.1.2",
 
 
61
  "@radix-ui/react-tooltip": "^1.1.4",
62
  "@remix-run/cloudflare": "^2.15.0",
63
  "@remix-run/cloudflare-pages": "^2.15.0",
@@ -75,13 +78,13 @@
75
  "framer-motion": "^11.12.0",
76
  "ignore": "^6.0.2",
77
  "isbot": "^4.4.0",
 
78
  "istextorbinary": "^9.5.0",
79
  "jose": "^5.9.6",
80
  "js-cookie": "^3.0.5",
81
  "jszip": "^3.10.1",
82
  "nanostores": "^0.10.3",
83
  "ollama-ai-provider": "^0.15.2",
84
- "pnpm": "^9.14.4",
85
  "react": "^18.3.1",
86
  "react-dom": "^18.3.1",
87
  "react-hotkeys-hook": "^4.6.1",
@@ -110,6 +113,7 @@
110
  "husky": "9.1.7",
111
  "is-ci": "^3.0.1",
112
  "node-fetch": "^3.3.2",
 
113
  "prettier": "^3.4.1",
114
  "sass-embedded": "^1.81.0",
115
  "typescript": "^5.7.2",
 
44
  "@codemirror/lang-markdown": "^6.3.1",
45
  "@codemirror/lang-python": "^6.1.6",
46
  "@codemirror/lang-sass": "^6.0.2",
47
+ "@codemirror/lang-vue": "^0.1.3",
48
  "@codemirror/lang-wast": "^6.0.2",
49
  "@codemirror/language": "^6.10.6",
50
  "@codemirror/search": "^6.5.8",
 
59
  "@openrouter/ai-sdk-provider": "^0.0.5",
60
  "@radix-ui/react-dialog": "^1.1.2",
61
  "@radix-ui/react-dropdown-menu": "^2.1.2",
62
+ "@radix-ui/react-separator": "^1.1.0",
63
+ "@radix-ui/react-switch": "^1.1.1",
64
  "@radix-ui/react-tooltip": "^1.1.4",
65
  "@remix-run/cloudflare": "^2.15.0",
66
  "@remix-run/cloudflare-pages": "^2.15.0",
 
78
  "framer-motion": "^11.12.0",
79
  "ignore": "^6.0.2",
80
  "isbot": "^4.4.0",
81
+ "isomorphic-git": "^1.27.2",
82
  "istextorbinary": "^9.5.0",
83
  "jose": "^5.9.6",
84
  "js-cookie": "^3.0.5",
85
  "jszip": "^3.10.1",
86
  "nanostores": "^0.10.3",
87
  "ollama-ai-provider": "^0.15.2",
 
88
  "react": "^18.3.1",
89
  "react-dom": "^18.3.1",
90
  "react-hotkeys-hook": "^4.6.1",
 
113
  "husky": "9.1.7",
114
  "is-ci": "^3.0.1",
115
  "node-fetch": "^3.3.2",
116
+ "pnpm": "^9.14.4",
117
  "prettier": "^3.4.1",
118
  "sass-embedded": "^1.81.0",
119
  "typescript": "^5.7.2",
pnpm-lock.yaml CHANGED
@@ -56,6 +56,9 @@ importers:
56
  '@codemirror/lang-sass':
57
  specifier: ^6.0.2
58
  version: 6.0.2(@codemirror/[email protected])
 
 
 
59
  '@codemirror/lang-wast':
60
  specifier: ^6.0.2
61
  version: 6.0.2
@@ -98,6 +101,12 @@ importers:
98
  '@radix-ui/react-dropdown-menu':
99
  specifier: ^2.1.2
100
 
 
 
 
 
 
101
  '@radix-ui/react-tooltip':
102
  specifier: ^1.1.4
103
@@ -130,7 +139,7 @@ importers:
130
  version: 5.5.0
131
  ai:
132
  specifier: ^3.4.33
133
- version: 3.4.33([email protected])([email protected](svelte@4.2.18))(svelte@4.2.18)(vue@3.4.30([email protected]))([email protected])
134
  date-fns:
135
  specifier: ^3.6.0
136
  version: 3.6.0
@@ -149,6 +158,9 @@ importers:
149
  isbot:
150
  specifier: ^4.4.0
151
  version: 4.4.0
 
 
 
152
  istextorbinary:
153
  specifier: ^9.5.0
154
  version: 9.5.0
@@ -167,9 +179,6 @@ importers:
167
  ollama-ai-provider:
168
  specifier: ^0.15.2
169
  version: 0.15.2([email protected])
170
- pnpm:
171
- specifier: ^9.14.4
172
- version: 9.14.4
173
  react:
174
  specifier: ^18.3.1
175
  version: 18.3.1
@@ -218,7 +227,7 @@ importers:
218
  version: 4.20241127.0
219
  '@remix-run/dev':
220
  specifier: ^2.15.0
221
222
  '@types/diff':
223
  specifier: ^5.2.3
224
  version: 5.2.3
@@ -249,6 +258,9 @@ importers:
249
  node-fetch:
250
  specifier: ^3.3.2
251
  version: 3.3.2
 
 
 
252
  prettier:
253
  specifier: ^3.4.1
254
  version: 3.4.1
@@ -263,22 +275,22 @@ importers:
263
  version: 11.0.5
264
  unocss:
265
  specifier: ^0.61.9
266
267
  vite:
268
  specifier: ^5.4.11
269
270
  vite-plugin-node-polyfills:
271
  specifier: ^0.22.0
272
273
  vite-plugin-optimize-css-modules:
274
  specifier: ^1.1.0
275
276
  vite-tsconfig-paths:
277
  specifier: ^4.3.2
278
279
  vitest:
280
  specifier: ^2.1.7
281
282
  wrangler:
283
  specifier: ^3.91.0
284
  version: 3.91.0(@cloudflare/[email protected])
@@ -666,6 +678,9 @@ packages:
666
  '@codemirror/[email protected]':
667
  resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==}
668
 
 
 
 
669
  '@codemirror/[email protected]':
670
  resolution: {integrity: sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==}
671
 
@@ -1375,8 +1390,8 @@ packages:
1375
  '@lezer/[email protected]':
1376
  resolution: {integrity: sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==}
1377
 
1378
- '@lezer/[email protected].19':
1379
- resolution: {integrity: sha512-j44kbR1QL26l6dMunZ1uhKBFteVGLVCBGNUD2sUaMnic+rbTviVuoK0CD1l9FTW31EueWvFFswCKMH7Z+M3JRA==}
1380
 
1381
  '@lezer/[email protected]':
1382
  resolution: {integrity: sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==}
@@ -1717,6 +1732,19 @@ packages:
1717
  '@types/react-dom':
1718
  optional: true
1719
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1720
  '@radix-ui/[email protected]':
1721
  resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
1722
  peerDependencies:
@@ -1726,6 +1754,19 @@ packages:
1726
  '@types/react':
1727
  optional: true
1728
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1729
  '@radix-ui/[email protected]':
1730
  resolution: {integrity: sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==}
1731
  peerDependencies:
@@ -1775,6 +1816,15 @@ packages:
1775
  '@types/react':
1776
  optional: true
1777
 
 
 
 
 
 
 
 
 
 
1778
  '@radix-ui/[email protected]':
1779
  resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
1780
  peerDependencies:
@@ -2102,8 +2152,8 @@ packages:
2102
  '@types/[email protected]':
2103
  resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
2104
 
2105
- '@typescript-eslint/eslint-plugin@8.16.0':
2106
- resolution: {integrity: sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==}
2107
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2108
  peerDependencies:
2109
  '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
@@ -2113,8 +2163,8 @@ packages:
2113
  typescript:
2114
  optional: true
2115
 
2116
- '@typescript-eslint/parser@8.16.0':
2117
- resolution: {integrity: sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==}
2118
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2119
  peerDependencies:
2120
  eslint: ^8.57.0 || ^9.0.0
@@ -2123,12 +2173,12 @@ packages:
2123
  typescript:
2124
  optional: true
2125
 
2126
- '@typescript-eslint/scope-manager@8.16.0':
2127
- resolution: {integrity: sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==}
2128
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2129
 
2130
- '@typescript-eslint/type-utils@8.16.0':
2131
- resolution: {integrity: sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==}
2132
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2133
  peerDependencies:
2134
  eslint: ^8.57.0 || ^9.0.0
@@ -2137,12 +2187,12 @@ packages:
2137
  typescript:
2138
  optional: true
2139
 
2140
- '@typescript-eslint/types@8.16.0':
2141
- resolution: {integrity: sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==}
2142
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2143
 
2144
- '@typescript-eslint/typescript-estree@8.16.0':
2145
- resolution: {integrity: sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==}
2146
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2147
  peerDependencies:
2148
  typescript: '*'
@@ -2150,8 +2200,8 @@ packages:
2150
  typescript:
2151
  optional: true
2152
 
2153
- '@typescript-eslint/utils@8.16.0':
2154
- resolution: {integrity: sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==}
2155
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2156
  peerDependencies:
2157
  eslint: ^8.57.0 || ^9.0.0
@@ -2160,8 +2210,8 @@ packages:
2160
  typescript:
2161
  optional: true
2162
 
2163
- '@typescript-eslint/visitor-keys@8.16.0':
2164
- resolution: {integrity: sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==}
2165
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2166
 
2167
  '@uiw/[email protected]':
@@ -2275,11 +2325,11 @@ packages:
2275
  '@vanilla-extract/[email protected]':
2276
  resolution: {integrity: sha512-ytsG/JLweEjw7DBuZ/0JCN4WAQgM9erfSTdS1NQY778hFQSZ6cfCDEZZ0sgVm4k54uNz6ImKB33AYvSR//fjxw==}
2277
 
2278
- '@vitest/[email protected].7':
2279
- resolution: {integrity: sha512-folWk4qQDEedgUyvaZw94LIJuNLoDtY+rhKhhNy0csdwifn/pQz8EWVRnyrW3j0wMpy+xwJT8WiwiYxk+i+s7w==}
2280
 
2281
- '@vitest/[email protected].7':
2282
- resolution: {integrity: sha512-nKMTnuJrarFH+7llWxeLmYRldIwTY3OM1DzdytHj0f2+fah6Cyk4XbswhjOiTCnAvXsZAEoo1OaD6rneSSU+3Q==}
2283
  peerDependencies:
2284
  msw: ^2.4.9
2285
  vite: ^5.0.0
@@ -2289,49 +2339,49 @@ packages:
2289
  vite:
2290
  optional: true
2291
 
2292
- '@vitest/[email protected].7':
2293
- resolution: {integrity: sha512-HoqRIyfQlXPrRDB43h0lC8eHPUDPwFweMaD6t+psOvwClCC+oZZim6wPMjuoMnRdiFxXqbybg/QbuewgTwK1vA==}
2294
 
2295
- '@vitest/[email protected].7':
2296
- resolution: {integrity: sha512-MrDNpXUIXksR57qipYh068SOX4N1hVw6oVILlTlfeTyA1rp0asuljyp15IZwKqhjpWLObFj+tiNrOM4R8UnSqg==}
2297
 
2298
- '@vitest/[email protected].7':
2299
- resolution: {integrity: sha512-OioIxV/xS393DKdlkRNhmtY0K37qVdCv8w1M2SlLTBSX+fNK6zgcd01VlT1nXdbKVDaB8Zb6BOfQYYoGeGTEGg==}
2300
 
2301
- '@vitest/[email protected].7':
2302
- resolution: {integrity: sha512-e5pzIaIC0LBrb/j1FaF7HXlPJLGtltiAkwXTMqNEHALJc7USSLEwziJ+aIWTmjsWNg89zazg37h7oZITnublsQ==}
2303
 
2304
- '@vitest/[email protected].7':
2305
- resolution: {integrity: sha512-7gUdvIzCCuIrMZu0WHTvDJo8C1NsUtOqmwmcS3bRHUcfHemj29wmkzLVNuWQD7WHoBD/+I7WIgrnzt7kxR54ow==}
2306
 
2307
- '@vue/compiler-core@3.4.30':
2308
- resolution: {integrity: sha512-ZL8y4Xxdh8O6PSwfdZ1IpQ24PjTAieOz3jXb/MDTfDtANcKBMxg1KLm6OX2jofsaQGYfIVzd3BAG22i56/cF1w==}
2309
 
2310
- '@vue/compiler-dom@3.4.30':
2311
- resolution: {integrity: sha512-+16Sd8lYr5j/owCbr9dowcNfrHd+pz+w2/b5Lt26Oz/kB90C9yNbxQ3bYOvt7rI2bxk0nqda39hVcwDFw85c2Q==}
2312
 
2313
- '@vue/compiler-sfc@3.4.30':
2314
- resolution: {integrity: sha512-8vElKklHn/UY8+FgUFlQrYAPbtiSB2zcgeRKW7HkpSRn/JjMRmZvuOtwDx036D1aqKNSTtXkWRfqx53Qb+HmMg==}
2315
 
2316
- '@vue/compiler-ssr@3.4.30':
2317
- resolution: {integrity: sha512-ZJ56YZGXJDd6jky4mmM0rNaNP6kIbQu9LTKZDhcpddGe/3QIalB1WHHmZ6iZfFNyj5mSypTa4+qDJa5VIuxMSg==}
2318
 
2319
- '@vue/reactivity@3.4.30':
2320
- resolution: {integrity: sha512-bVJurnCe3LS0JII8PPoAA63Zd2MBzcKrEzwdQl92eHCcxtIbxD2fhNwJpa+KkM3Y/A4T5FUnmdhgKwOf6BfbcA==}
2321
 
2322
- '@vue/runtime-core@3.4.30':
2323
- resolution: {integrity: sha512-qaFEbnNpGz+tlnkaualomogzN8vBLkgzK55uuWjYXbYn039eOBZrWxyXWq/7qh9Bz2FPifZqGjVDl/FXiq9L2g==}
2324
 
2325
- '@vue/runtime-dom@3.4.30':
2326
- resolution: {integrity: sha512-tV6B4YiZRj5QsaJgw2THCy5C1H+2UeywO9tqgWEc21tn85qHEERndHN/CxlyXvSBFrpmlexCIdnqPuR9RM9thw==}
2327
 
2328
- '@vue/server-renderer@3.4.30':
2329
- resolution: {integrity: sha512-TBD3eqR1DeDc0cMrXS/vEs/PWzq1uXxnvjoqQuDGFIEHFIwuDTX/KWAQKIBjyMWLFHEeTDGYVsYci85z2UbTDg==}
2330
  peerDependencies:
2331
- vue: 3.4.30
2332
 
2333
- '@vue/shared@3.4.30':
2334
- resolution: {integrity: sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==}
2335
 
2336
  '@web3-storage/[email protected]':
2337
  resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==}
@@ -2368,6 +2418,11 @@ packages:
2368
  peerDependencies:
2369
  acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
2370
 
 
 
 
 
 
2371
2372
  resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
2373
  engines: {node: '>=0.4.0'}
@@ -2459,6 +2514,9 @@ packages:
2459
  resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
2460
  hasBin: true
2461
 
 
 
 
2462
2463
  resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
2464
  engines: {node: '>= 0.4'}
@@ -2648,6 +2706,9 @@ packages:
2648
  resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==}
2649
  engines: {node: '>= 0.10'}
2650
 
 
 
 
2651
2652
  resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
2653
  engines: {node: '>=6'}
@@ -2671,9 +2732,6 @@ packages:
2671
  resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
2672
  engines: {node: '>=6'}
2673
 
2674
2675
- resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==}
2676
-
2677
2678
  resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
2679
  engines: {node: '>=7.0.0'}
@@ -2743,6 +2801,11 @@ packages:
2743
2744
  resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
2745
 
 
 
 
 
 
2746
2747
  resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
2748
 
@@ -2819,6 +2882,10 @@ packages:
2819
2820
  resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
2821
 
 
 
 
 
2822
2823
  resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==}
2824
  peerDependencies:
@@ -2882,6 +2949,9 @@ packages:
2882
2883
  resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
2884
 
 
 
 
2885
2886
  resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
2887
  engines: {node: '>=0.3.1'}
@@ -2913,8 +2983,8 @@ packages:
2913
2914
  resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
2915
 
2916
2917
- resolution: {integrity: sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==}
2918
 
2919
2920
  resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==}
@@ -3063,6 +3133,9 @@ packages:
3063
  jiti:
3064
  optional: true
3065
 
 
 
 
3066
3067
  resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
3068
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -3075,6 +3148,9 @@ packages:
3075
  resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
3076
  engines: {node: '>=0.10'}
3077
 
 
 
 
3078
3079
  resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
3080
  engines: {node: '>=4.0'}
@@ -3367,8 +3443,8 @@ packages:
3367
  resolution: {integrity: sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==}
3368
  engines: {node: '>= 0.4'}
3369
 
3370
3371
- resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
3372
  engines: {node: '>= 0.4'}
3373
 
3374
@@ -3472,9 +3548,6 @@ packages:
3472
3473
  resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
3474
 
3475
3476
- resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==}
3477
-
3478
3479
  resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
3480
 
@@ -3613,6 +3686,11 @@ packages:
3613
3614
  resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
3615
 
 
 
 
 
 
3616
3617
  resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==}
3618
  engines: {node: '>=10'}
@@ -4093,6 +4171,10 @@ packages:
4093
  resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
4094
  engines: {node: '>=6'}
4095
 
 
 
 
 
4096
4097
  resolution: {integrity: sha512-dM3RBlJE8rUFxnqlPCaFCq0E7qQqEQvKbYX7W/APGCK+rLcyLmEBzC4GQR/niXdNM/oV6gdg9AA50ghnn2ALuw==}
4098
  engines: {node: '>=16.13'}
@@ -4114,6 +4196,9 @@ packages:
4114
4115
  resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
4116
 
 
 
 
4117
4118
  resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
4119
  engines: {node: '>= 8'}
@@ -4417,6 +4502,10 @@ packages:
4417
  engines: {node: '>=0.10'}
4418
  hasBin: true
4419
 
 
 
 
 
4420
4421
  resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==}
4422
  engines: {node: '>=10'}
@@ -4786,8 +4875,8 @@ packages:
4786
4787
  resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
4788
 
4789
4790
- resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==}
4791
  engines: {node: '>=10'}
4792
 
4793
@@ -4968,11 +5057,6 @@ packages:
4968
  engines: {node: '>=16.0.0'}
4969
  hasBin: true
4970
 
4971
4972
- resolution: {integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==}
4973
- engines: {node: '>=14.0.0'}
4974
- hasBin: true
4975
-
4976
4977
  resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
4978
 
@@ -5042,6 +5126,12 @@ packages:
5042
  resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
5043
  engines: {node: '>=14'}
5044
 
 
 
 
 
 
 
5045
5046
  resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
5047
  engines: {node: '>= 10'}
@@ -5179,9 +5269,9 @@ packages:
5179
  resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
5180
  engines: {node: '>= 0.4'}
5181
 
5182
- svelte@4.2.18:
5183
- resolution: {integrity: sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==}
5184
- engines: {node: '>=16'}
5185
 
5186
5187
  resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==}
@@ -5315,16 +5405,16 @@ packages:
5315
  resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
5316
  engines: {node: '>= 0.8.0'}
5317
 
5318
- type-fest@4.29.1:
5319
- resolution: {integrity: sha512-Y1zUveI92UYM/vo1EFlQSsNf74+hfKH+7saZJslF0Fw92FRaiTAnHPIvo9d7SLxXt/gAYqA4RXyDTioMQCCp0A==}
5320
  engines: {node: '>=16'}
5321
 
5322
5323
  resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
5324
  engines: {node: '>= 0.6'}
5325
 
5326
- typescript-eslint@8.16.0:
5327
- resolution: {integrity: sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==}
5328
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
5329
  peerDependencies:
5330
  eslint: ^8.57.0 || ^9.0.0
@@ -5533,9 +5623,9 @@ packages:
5533
  engines: {node: ^18.0.0 || >=20.0.0}
5534
  hasBin: true
5535
 
5536
5537
- resolution: {integrity: sha512-b/5MxSWd0ftWt1B1LHfzCw0ASzaxHztUwP0rcsBhkDSGy9ZDEDieSIjFG3I78nI9dUN0eSeD6LtuKPZGjwwpZQ==}
5538
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
5539
  hasBin: true
5540
 
5541
@@ -5587,15 +5677,15 @@ packages:
5587
  terser:
5588
  optional: true
5589
 
5590
5591
- resolution: {integrity: sha512-wzJ7Wri44ufkzTZbI1lHsdHfiGdFRmnJ9qIudDQ6tknjJeHhF5QgNSSjk7KRZUU535qEiEXFJ7tSHqyzyIv0jQ==}
5592
- engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
5593
  hasBin: true
5594
  peerDependencies:
5595
  '@edge-runtime/vm': '*'
5596
- '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0
5597
- '@vitest/browser': 2.1.7
5598
- '@vitest/ui': 2.1.7
5599
  happy-dom: '*'
5600
  jsdom: '*'
5601
  peerDependenciesMeta:
@@ -5615,8 +5705,8 @@ packages:
5615
5616
  resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
5617
 
5618
- vue@3.4.30:
5619
- resolution: {integrity: sha512-NcxtKCwkdf1zPsr7Y8+QlDBCGqxvjLXF2EX+yi76rV5rrz90Y6gK1cq0olIhdWGgrlhs9ElHuhi9t3+W5sG5Xw==}
5620
  peerDependencies:
5621
  typescript: '*'
5622
  peerDependenciesMeta:
@@ -5737,6 +5827,9 @@ packages:
5737
5738
  resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==}
5739
 
 
 
 
5740
5741
  resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==}
5742
  peerDependencies:
@@ -5863,13 +5956,13 @@ snapshots:
5863
  transitivePeerDependencies:
5864
  - zod
5865
 
5866
- '@ai-sdk/[email protected](svelte@4.2.18)([email protected])':
5867
  dependencies:
5868
  '@ai-sdk/provider-utils': 1.0.22([email protected])
5869
  '@ai-sdk/ui-utils': 0.0.50([email protected])
5870
- sswr: 2.1.0(svelte@4.2.18)
5871
  optionalDependencies:
5872
- svelte: 4.2.18
5873
  transitivePeerDependencies:
5874
  - zod
5875
 
@@ -5883,13 +5976,13 @@ snapshots:
5883
  optionalDependencies:
5884
  zod: 3.23.8
5885
 
5886
5887
  dependencies:
5888
  '@ai-sdk/provider-utils': 1.0.22([email protected])
5889
  '@ai-sdk/ui-utils': 0.0.50([email protected])
5890
- swrv: 1.0.4(vue@3.4.30([email protected]))
5891
  optionalDependencies:
5892
- vue: 3.4.30([email protected])
5893
  transitivePeerDependencies:
5894
  - zod
5895
 
@@ -6109,16 +6202,16 @@ snapshots:
6109
6110
  dependencies:
6111
  '@stylistic/eslint-plugin-ts': 2.11.0([email protected]([email protected]))([email protected])
6112
- '@typescript-eslint/eslint-plugin': 8.16.0(@typescript-eslint/parser@8.16.0([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
6113
- '@typescript-eslint/parser': 8.16.0([email protected]([email protected]))([email protected])
6114
- '@typescript-eslint/utils': 8.16.0([email protected]([email protected]))([email protected])
6115
  common-tags: 1.8.2
6116
  eslint: 9.16.0([email protected])
6117
  eslint-config-prettier: 9.1.0([email protected]([email protected]))
6118
  eslint-plugin-jsonc: 2.18.2([email protected]([email protected]))
6119
6120
  globals: 15.13.0
6121
6122
  transitivePeerDependencies:
6123
  - '@eslint/json'
6124
  - '@types/eslint'
@@ -6208,7 +6301,7 @@ snapshots:
6208
  '@codemirror/state': 6.4.1
6209
  '@codemirror/view': 6.35.0
6210
  '@lezer/common': 1.2.3
6211
- '@lezer/javascript': 1.4.19
6212
 
6213
  '@codemirror/[email protected]':
6214
  dependencies:
@@ -6245,6 +6338,15 @@ snapshots:
6245
  transitivePeerDependencies:
6246
  - '@codemirror/view'
6247
 
 
 
 
 
 
 
 
 
 
6248
  '@codemirror/[email protected]':
6249
  dependencies:
6250
  '@codemirror/language': 6.10.6
@@ -6720,7 +6822,7 @@ snapshots:
6720
  '@lezer/highlight': 1.2.1
6721
  '@lezer/lr': 1.4.2
6722
 
6723
- '@lezer/[email protected].19':
6724
  dependencies:
6725
  '@lezer/common': 1.2.3
6726
  '@lezer/highlight': 1.2.1
@@ -7112,6 +7214,15 @@ snapshots:
7112
  '@types/react': 18.3.12
7113
  '@types/react-dom': 18.3.1
7114
 
 
 
 
 
 
 
 
 
 
7115
7116
  dependencies:
7117
  '@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
@@ -7119,6 +7230,21 @@ snapshots:
7119
  optionalDependencies:
7120
  '@types/react': 18.3.12
7121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7122
7123
  dependencies:
7124
  '@radix-ui/primitive': 1.1.0
@@ -7165,6 +7291,12 @@ snapshots:
7165
  optionalDependencies:
7166
  '@types/react': 18.3.12
7167
 
 
 
 
 
 
 
7168
7169
  dependencies:
7170
  '@radix-ui/rect': 1.1.0
@@ -7205,7 +7337,7 @@ snapshots:
7205
  optionalDependencies:
7206
  typescript: 5.7.2
7207
 
7208
7209
  dependencies:
7210
  '@babel/core': 7.26.0
7211
  '@babel/generator': 7.26.2
@@ -7222,7 +7354,7 @@ snapshots:
7222
  '@remix-run/router': 1.21.0
7223
  '@remix-run/server-runtime': 2.15.0([email protected])
7224
  '@types/mdx': 2.0.13
7225
- '@vanilla-extract/integration': 6.5.0(@types/[email protected])([email protected])([email protected])
7226
  arg: 5.0.2
7227
  cacache: 17.1.4
7228
  chalk: 4.1.2
@@ -7261,11 +7393,11 @@ snapshots:
7261
  tar-fs: 2.1.1
7262
  tsconfig-paths: 4.2.0
7263
  valibot: 0.41.0([email protected])
7264
7265
  ws: 7.5.10
7266
  optionalDependencies:
7267
  typescript: 5.7.2
7268
7269
  wrangler: 3.91.0(@cloudflare/[email protected])
7270
  transitivePeerDependencies:
7271
  - '@types/node'
@@ -7448,7 +7580,7 @@ snapshots:
7448
 
7449
7450
  dependencies:
7451
- '@typescript-eslint/utils': 8.16.0([email protected]([email protected]))([email protected])
7452
  eslint: 9.16.0([email protected])
7453
  eslint-visitor-keys: 4.2.0
7454
  espree: 10.3.0
@@ -7533,14 +7665,14 @@ snapshots:
7533
 
7534
  '@types/[email protected]': {}
7535
 
7536
- '@typescript-eslint/eslint-plugin@8.16.0(@typescript-eslint/parser@8.16.0([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])':
7537
  dependencies:
7538
  '@eslint-community/regexpp': 4.12.1
7539
- '@typescript-eslint/parser': 8.16.0([email protected]([email protected]))([email protected])
7540
- '@typescript-eslint/scope-manager': 8.16.0
7541
- '@typescript-eslint/type-utils': 8.16.0([email protected]([email protected]))([email protected])
7542
- '@typescript-eslint/utils': 8.16.0([email protected]([email protected]))([email protected])
7543
- '@typescript-eslint/visitor-keys': 8.16.0
7544
  eslint: 9.16.0([email protected])
7545
  graphemer: 1.4.0
7546
  ignore: 5.3.2
@@ -7551,12 +7683,12 @@ snapshots:
7551
  transitivePeerDependencies:
7552
  - supports-color
7553
 
7554
- '@typescript-eslint/parser@8.16.0([email protected]([email protected]))([email protected])':
7555
  dependencies:
7556
- '@typescript-eslint/scope-manager': 8.16.0
7557
- '@typescript-eslint/types': 8.16.0
7558
- '@typescript-eslint/typescript-estree': 8.16.0([email protected])
7559
- '@typescript-eslint/visitor-keys': 8.16.0
7560
  debug: 4.3.7
7561
  eslint: 9.16.0([email protected])
7562
  optionalDependencies:
@@ -7564,15 +7696,15 @@ snapshots:
7564
  transitivePeerDependencies:
7565
  - supports-color
7566
 
7567
- '@typescript-eslint/scope-manager@8.16.0':
7568
  dependencies:
7569
- '@typescript-eslint/types': 8.16.0
7570
- '@typescript-eslint/visitor-keys': 8.16.0
7571
 
7572
- '@typescript-eslint/type-utils@8.16.0([email protected]([email protected]))([email protected])':
7573
  dependencies:
7574
- '@typescript-eslint/typescript-estree': 8.16.0([email protected])
7575
- '@typescript-eslint/utils': 8.16.0([email protected]([email protected]))([email protected])
7576
  debug: 4.3.7
7577
  eslint: 9.16.0([email protected])
7578
  ts-api-utils: 1.4.3([email protected])
@@ -7581,12 +7713,12 @@ snapshots:
7581
  transitivePeerDependencies:
7582
  - supports-color
7583
 
7584
- '@typescript-eslint/types@8.16.0': {}
7585
 
7586
- '@typescript-eslint/typescript-estree@8.16.0([email protected])':
7587
  dependencies:
7588
- '@typescript-eslint/types': 8.16.0
7589
- '@typescript-eslint/visitor-keys': 8.16.0
7590
  debug: 4.3.7
7591
  fast-glob: 3.3.2
7592
  is-glob: 4.0.3
@@ -7598,21 +7730,21 @@ snapshots:
7598
  transitivePeerDependencies:
7599
  - supports-color
7600
 
7601
- '@typescript-eslint/utils@8.16.0([email protected]([email protected]))([email protected])':
7602
  dependencies:
7603
  '@eslint-community/eslint-utils': 4.4.1([email protected]([email protected]))
7604
- '@typescript-eslint/scope-manager': 8.16.0
7605
- '@typescript-eslint/types': 8.16.0
7606
- '@typescript-eslint/typescript-estree': 8.16.0([email protected])
7607
  eslint: 9.16.0([email protected])
7608
  optionalDependencies:
7609
  typescript: 5.7.2
7610
  transitivePeerDependencies:
7611
  - supports-color
7612
 
7613
- '@typescript-eslint/visitor-keys@8.16.0':
7614
  dependencies:
7615
- '@typescript-eslint/types': 8.16.0
7616
  eslint-visitor-keys: 4.2.0
7617
 
7618
  '@uiw/[email protected](@codemirror/[email protected])(@codemirror/[email protected])(@codemirror/[email protected])':
@@ -7631,13 +7763,13 @@ snapshots:
7631
 
7632
  '@ungap/[email protected]': {}
7633
 
7634
7635
  dependencies:
7636
  '@unocss/core': 0.61.9
7637
  '@unocss/reset': 0.61.9
7638
7639
  optionalDependencies:
7640
7641
  transitivePeerDependencies:
7642
  - rollup
7643
  - supports-color
@@ -7774,7 +7906,7 @@ snapshots:
7774
  dependencies:
7775
  '@unocss/core': 0.61.9
7776
 
7777
7778
  dependencies:
7779
  '@ampproject/remapping': 2.3.0
7780
  '@rollup/pluginutils': 5.1.3([email protected])
@@ -7786,7 +7918,7 @@ snapshots:
7786
  chokidar: 3.6.0
7787
  fast-glob: 3.3.2
7788
  magic-string: 0.30.14
7789
7790
  transitivePeerDependencies:
7791
  - rollup
7792
  - supports-color
@@ -7814,7 +7946,7 @@ snapshots:
7814
  transitivePeerDependencies:
7815
  - babel-plugin-macros
7816
 
7817
7818
  dependencies:
7819
  '@babel/core': 7.26.0
7820
  '@babel/plugin-syntax-typescript': 7.25.9(@babel/[email protected])
@@ -7827,8 +7959,8 @@ snapshots:
7827
  lodash: 4.17.21
7828
  mlly: 1.7.3
7829
  outdent: 0.8.0
7830
7831
7832
  transitivePeerDependencies:
7833
  - '@types/node'
7834
  - babel-plugin-macros
@@ -7843,99 +7975,99 @@ snapshots:
7843
 
7844
  '@vanilla-extract/[email protected]': {}
7845
 
7846
- '@vitest/[email protected].7':
7847
  dependencies:
7848
- '@vitest/spy': 2.1.7
7849
- '@vitest/utils': 2.1.7
7850
  chai: 5.1.2
7851
  tinyrainbow: 1.2.0
7852
 
7853
7854
  dependencies:
7855
- '@vitest/spy': 2.1.7
7856
  estree-walker: 3.0.3
7857
  magic-string: 0.30.14
7858
  optionalDependencies:
7859
7860
 
7861
- '@vitest/[email protected].7':
7862
  dependencies:
7863
  tinyrainbow: 1.2.0
7864
 
7865
- '@vitest/[email protected].7':
7866
  dependencies:
7867
- '@vitest/utils': 2.1.7
7868
  pathe: 1.1.2
7869
 
7870
- '@vitest/[email protected].7':
7871
  dependencies:
7872
- '@vitest/pretty-format': 2.1.7
7873
  magic-string: 0.30.14
7874
  pathe: 1.1.2
7875
 
7876
- '@vitest/[email protected].7':
7877
  dependencies:
7878
  tinyspy: 3.0.2
7879
 
7880
- '@vitest/[email protected].7':
7881
  dependencies:
7882
- '@vitest/pretty-format': 2.1.7
7883
  loupe: 3.1.2
7884
  tinyrainbow: 1.2.0
7885
 
7886
- '@vue/compiler-core@3.4.30':
7887
  dependencies:
7888
  '@babel/parser': 7.26.2
7889
- '@vue/shared': 3.4.30
7890
  entities: 4.5.0
7891
  estree-walker: 2.0.2
7892
  source-map-js: 1.2.1
7893
 
7894
- '@vue/compiler-dom@3.4.30':
7895
  dependencies:
7896
- '@vue/compiler-core': 3.4.30
7897
- '@vue/shared': 3.4.30
7898
 
7899
- '@vue/compiler-sfc@3.4.30':
7900
  dependencies:
7901
  '@babel/parser': 7.26.2
7902
- '@vue/compiler-core': 3.4.30
7903
- '@vue/compiler-dom': 3.4.30
7904
- '@vue/compiler-ssr': 3.4.30
7905
- '@vue/shared': 3.4.30
7906
  estree-walker: 2.0.2
7907
  magic-string: 0.30.14
7908
  postcss: 8.4.49
7909
  source-map-js: 1.2.1
7910
 
7911
- '@vue/compiler-ssr@3.4.30':
7912
  dependencies:
7913
- '@vue/compiler-dom': 3.4.30
7914
- '@vue/shared': 3.4.30
7915
 
7916
- '@vue/reactivity@3.4.30':
7917
  dependencies:
7918
- '@vue/shared': 3.4.30
7919
 
7920
- '@vue/runtime-core@3.4.30':
7921
  dependencies:
7922
- '@vue/reactivity': 3.4.30
7923
- '@vue/shared': 3.4.30
7924
 
7925
- '@vue/runtime-dom@3.4.30':
7926
  dependencies:
7927
- '@vue/reactivity': 3.4.30
7928
- '@vue/runtime-core': 3.4.30
7929
- '@vue/shared': 3.4.30
7930
  csstype: 3.1.3
7931
 
7932
- '@vue/server-renderer@3.4.30(vue@3.4.30([email protected]))':
7933
  dependencies:
7934
- '@vue/compiler-ssr': 3.4.30
7935
- '@vue/shared': 3.4.30
7936
- vue: 3.4.30([email protected])
7937
 
7938
- '@vue/shared@3.4.30': {}
7939
 
7940
  '@web3-storage/[email protected]': {}
7941
 
@@ -7967,6 +8099,10 @@ snapshots:
7967
  dependencies:
7968
  acorn: 8.14.0
7969
 
 
 
 
 
7970
7971
  dependencies:
7972
  acorn: 8.14.0
@@ -7978,15 +8114,15 @@ snapshots:
7978
  clean-stack: 2.2.0
7979
  indent-string: 4.0.0
7980
 
7981
7982
  dependencies:
7983
  '@ai-sdk/provider': 0.0.26
7984
  '@ai-sdk/provider-utils': 1.0.22([email protected])
7985
  '@ai-sdk/react': 0.0.70([email protected])([email protected])
7986
  '@ai-sdk/solid': 0.0.54([email protected])
7987
- '@ai-sdk/svelte': 0.0.57(svelte@4.2.18)([email protected])
7988
  '@ai-sdk/ui-utils': 0.0.50([email protected])
7989
- '@ai-sdk/vue': 0.0.59(vue@3.4.30([email protected]))([email protected])
7990
  '@opentelemetry/api': 1.9.0
7991
  eventsource-parser: 1.1.2
7992
  json-schema: 0.4.0
@@ -7995,8 +8131,8 @@ snapshots:
7995
  zod-to-json-schema: 3.23.5([email protected])
7996
  optionalDependencies:
7997
  react: 18.3.1
7998
- sswr: 2.1.0(svelte@4.2.18)
7999
- svelte: 4.2.18
8000
  zod: 3.23.8
8001
  transitivePeerDependencies:
8002
  - solid-js
@@ -8058,6 +8194,8 @@ snapshots:
8058
 
8059
8060
 
 
 
8061
8062
  dependencies:
8063
  possible-typed-array-names: 1.0.0
@@ -8178,7 +8316,7 @@ snapshots:
8178
8179
  dependencies:
8180
  caniuse-lite: 1.0.30001685
8181
- electron-to-chromium: 1.5.67
8182
  node-releases: 2.0.18
8183
  update-browserslist-db: 1.1.1([email protected])
8184
 
@@ -8292,6 +8430,8 @@ snapshots:
8292
  inherits: 2.0.4
8293
  safe-buffer: 5.2.1
8294
 
 
 
8295
8296
 
8297
@@ -8306,14 +8446,6 @@ snapshots:
8306
 
8307
8308
 
8309
8310
- dependencies:
8311
- '@jridgewell/sourcemap-codec': 1.5.0
8312
- '@types/estree': 1.0.6
8313
- acorn: 8.14.0
8314
- estree-walker: 3.0.3
8315
- periscopic: 3.1.0
8316
-
8317
8318
  dependencies:
8319
  color-name: 1.1.4
@@ -8358,6 +8490,8 @@ snapshots:
8358
 
8359
8360
 
 
 
8361
8362
  dependencies:
8363
  bn.js: 4.12.1
@@ -8438,6 +8572,10 @@ snapshots:
8438
  dependencies:
8439
  character-entities: 2.0.2
8440
 
 
 
 
 
8441
8442
 
8443
@@ -8487,6 +8625,8 @@ snapshots:
8487
 
8488
8489
 
 
 
8490
8491
 
8492
@@ -8516,7 +8656,7 @@ snapshots:
8516
 
8517
8518
 
8519
8520
 
8521
8522
  dependencies:
@@ -8559,7 +8699,7 @@ snapshots:
8559
  '@jspm/core': 2.0.1
8560
  esbuild: 0.17.6
8561
  local-pkg: 0.5.1
8562
- resolve.exports: 2.0.2
8563
 
8564
8565
  optionalDependencies:
@@ -8761,6 +8901,8 @@ snapshots:
8761
  transitivePeerDependencies:
8762
  - supports-color
8763
 
 
 
8764
8765
  dependencies:
8766
  acorn: 8.14.0
@@ -8777,6 +8919,11 @@ snapshots:
8777
  dependencies:
8778
  estraverse: 5.3.0
8779
 
 
 
 
 
 
8780
8781
  dependencies:
8782
  estraverse: 5.3.0
@@ -9019,7 +9166,7 @@ snapshots:
9019
  es-errors: 1.3.0
9020
  function-bind: 1.1.2
9021
  has-proto: 1.1.0
9022
- has-symbols: 1.0.3
9023
  hasown: 2.0.2
9024
 
9025
@@ -9095,11 +9242,11 @@ snapshots:
9095
  dependencies:
9096
  call-bind: 1.0.7
9097
 
9098
9099
 
9100
9101
  dependencies:
9102
- has-symbols: 1.0.3
9103
 
9104
9105
  dependencies:
@@ -9274,9 +9421,6 @@ snapshots:
9274
 
9275
9276
 
9277
9278
- optional: true
9279
-
9280
9281
 
9282
@@ -9391,6 +9535,21 @@ snapshots:
9391
 
9392
9393
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9394
9395
 
9396
@@ -10248,6 +10407,8 @@ snapshots:
10248
 
10249
10250
 
 
 
10251
10252
  dependencies:
10253
  '@cspotcode/source-map-support': 0.8.1
@@ -10281,6 +10442,10 @@ snapshots:
10281
 
10282
10283
 
 
 
 
 
10284
10285
  dependencies:
10286
  minipass: 3.3.6
@@ -10431,7 +10596,7 @@ snapshots:
10431
  dependencies:
10432
  call-bind: 1.0.7
10433
  define-properties: 1.2.1
10434
- has-symbols: 1.0.3
10435
  object-keys: 1.1.1
10436
 
10437
@@ -10598,6 +10763,8 @@ snapshots:
10598
 
10599
10600
 
 
 
10601
10602
  dependencies:
10603
  find-up: 5.0.0
@@ -10973,7 +11140,7 @@ snapshots:
10973
 
10974
10975
  dependencies:
10976
- type-fest: 4.29.1
10977
  optionalDependencies:
10978
  '@remix-run/cloudflare': 2.15.0(@cloudflare/[email protected])([email protected])
10979
  '@remix-run/node': 2.15.0([email protected])
@@ -10988,7 +11155,7 @@ snapshots:
10988
 
10989
10990
 
10991
10992
 
10993
10994
  dependencies:
@@ -11158,13 +11325,6 @@ snapshots:
11158
  sass-embedded-win32-ia32: 1.81.0
11159
  sass-embedded-win32-x64: 1.81.0
11160
 
11161
11162
- dependencies:
11163
- chokidar: 3.6.0
11164
- immutable: 4.3.7
11165
- source-map-js: 1.2.1
11166
- optional: true
11167
-
11168
11169
  dependencies:
11170
  loose-envify: 1.4.0
@@ -11255,6 +11415,14 @@ snapshots:
11255
 
11256
11257
 
 
 
 
 
 
 
 
 
11258
11259
  dependencies:
11260
  '@polka/url': 1.0.0-next.28
@@ -11294,9 +11462,9 @@ snapshots:
11294
  dependencies:
11295
  minipass: 7.1.2
11296
 
11297
- [email protected](svelte@4.2.18):
11298
  dependencies:
11299
- svelte: 4.2.18
11300
  swrev: 4.0.0
11301
 
11302
@@ -11389,22 +11557,21 @@ snapshots:
11389
 
11390
11391
 
11392
- svelte@4.2.18:
11393
  dependencies:
11394
  '@ampproject/remapping': 2.3.0
11395
  '@jridgewell/sourcemap-codec': 1.5.0
11396
- '@jridgewell/trace-mapping': 0.3.25
11397
  '@types/estree': 1.0.6
11398
  acorn: 8.14.0
 
11399
  aria-query: 5.3.2
11400
  axobject-query: 4.1.0
11401
- code-red: 1.0.4
11402
- css-tree: 2.3.1
11403
- estree-walker: 3.0.3
11404
  is-reference: 3.0.3
11405
  locate-character: 3.0.0
11406
  magic-string: 0.30.14
11407
- periscopic: 3.1.0
11408
 
11409
11410
  dependencies:
@@ -11414,9 +11581,9 @@ snapshots:
11414
 
11415
11416
 
11417
11418
  dependencies:
11419
- vue: 3.4.30([email protected])
11420
 
11421
11422
  dependencies:
@@ -11527,18 +11694,18 @@ snapshots:
11527
  dependencies:
11528
  prelude-ls: 1.2.1
11529
 
11530
- type-fest@4.29.1: {}
11531
 
11532
11533
  dependencies:
11534
  media-typer: 0.3.0
11535
  mime-types: 2.1.35
11536
 
11537
11538
  dependencies:
11539
- '@typescript-eslint/eslint-plugin': 8.16.0(@typescript-eslint/parser@8.16.0([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
11540
- '@typescript-eslint/parser': 8.16.0([email protected]([email protected]))([email protected])
11541
- '@typescript-eslint/utils': 8.16.0([email protected]([email protected]))([email protected])
11542
  eslint: 9.16.0([email protected])
11543
  optionalDependencies:
11544
  typescript: 5.7.2
@@ -11661,9 +11828,9 @@ snapshots:
11661
 
11662
11663
 
11664
11665
  dependencies:
11666
11667
  '@unocss/cli': 0.61.9([email protected])
11668
  '@unocss/core': 0.61.9
11669
  '@unocss/extractor-arbitrary-variants': 0.61.9
@@ -11682,9 +11849,9 @@ snapshots:
11682
  '@unocss/transformer-compile-class': 0.61.9
11683
  '@unocss/transformer-directives': 0.61.9
11684
  '@unocss/transformer-variant-group': 0.61.9
11685
11686
  optionalDependencies:
11687
11688
  transitivePeerDependencies:
11689
  - postcss
11690
  - rollup
@@ -11789,13 +11956,13 @@ snapshots:
11789
  '@types/unist': 3.0.3
11790
  vfile-message: 4.0.2
11791
 
11792
11793
  dependencies:
11794
  cac: 6.7.14
11795
  debug: 4.3.7
11796
  pathe: 1.1.2
11797
  picocolors: 1.1.1
11798
11799
  transitivePeerDependencies:
11800
  - '@types/node'
11801
  - less
@@ -11807,13 +11974,13 @@ snapshots:
11807
  - supports-color
11808
  - terser
11809
 
11810
11811
  dependencies:
11812
  cac: 6.7.14
11813
  debug: 4.3.7
11814
  es-module-lexer: 1.5.4
11815
  pathe: 1.1.2
11816
11817
  transitivePeerDependencies:
11818
  - '@types/node'
11819
  - less
@@ -11825,30 +11992,30 @@ snapshots:
11825
  - supports-color
11826
  - terser
11827
 
11828
11829
  dependencies:
11830
  '@rollup/plugin-inject': 5.0.5([email protected])
11831
  node-stdlib-browser: 1.3.0
11832
11833
  transitivePeerDependencies:
11834
  - rollup
11835
 
11836
11837
  dependencies:
11838
11839
 
11840
11841
  dependencies:
11842
  debug: 4.3.7
11843
  globrex: 0.1.2
11844
  tsconfck: 3.1.4([email protected])
11845
  optionalDependencies:
11846
11847
  transitivePeerDependencies:
11848
  - supports-color
11849
  - typescript
11850
 
11851
11852
  dependencies:
11853
  esbuild: 0.21.5
11854
  postcss: 8.4.49
@@ -11856,18 +12023,17 @@ snapshots:
11856
  optionalDependencies:
11857
  '@types/node': 22.10.1
11858
  fsevents: 2.3.3
11859
- sass: 1.77.6
11860
  sass-embedded: 1.81.0
11861
 
11862
11863
  dependencies:
11864
- '@vitest/expect': 2.1.7
11865
11866
- '@vitest/pretty-format': 2.1.7
11867
- '@vitest/runner': 2.1.7
11868
- '@vitest/snapshot': 2.1.7
11869
- '@vitest/spy': 2.1.7
11870
- '@vitest/utils': 2.1.7
11871
  chai: 5.1.2
11872
  debug: 4.3.7
11873
  expect-type: 1.1.0
@@ -11878,8 +12044,8 @@ snapshots:
11878
  tinyexec: 0.3.1
11879
  tinypool: 1.0.2
11880
  tinyrainbow: 1.2.0
11881
11882
11883
  why-is-node-running: 2.3.0
11884
  optionalDependencies:
11885
  '@types/node': 22.10.1
@@ -11896,13 +12062,13 @@ snapshots:
11896
 
11897
11898
 
11899
- vue@3.4.30([email protected]):
11900
  dependencies:
11901
- '@vue/compiler-dom': 3.4.30
11902
- '@vue/compiler-sfc': 3.4.30
11903
- '@vue/runtime-dom': 3.4.30
11904
- '@vue/server-renderer': 3.4.30(vue@3.4.30([email protected]))
11905
- '@vue/shared': 3.4.30
11906
  optionalDependencies:
11907
  typescript: 5.7.2
11908
 
@@ -11968,7 +12134,7 @@ snapshots:
11968
  nanoid: 3.3.8
11969
  path-to-regexp: 6.3.0
11970
  resolve: 1.22.8
11971
- resolve.exports: 2.0.2
11972
  selfsigned: 2.4.1
11973
  source-map: 0.6.1
11974
  unenv: [email protected]
@@ -12018,6 +12184,8 @@ snapshots:
12018
  mustache: 4.2.0
12019
  stacktracey: 2.1.8
12020
 
 
 
12021
12022
  dependencies:
12023
  zod: 3.23.8
 
56
  '@codemirror/lang-sass':
57
  specifier: ^6.0.2
58
  version: 6.0.2(@codemirror/[email protected])
59
+ '@codemirror/lang-vue':
60
+ specifier: ^0.1.3
61
+ version: 0.1.3
62
  '@codemirror/lang-wast':
63
  specifier: ^6.0.2
64
  version: 6.0.2
 
101
  '@radix-ui/react-dropdown-menu':
102
  specifier: ^2.1.2
103
104
+ '@radix-ui/react-separator':
105
+ specifier: ^1.1.0
106
107
+ '@radix-ui/react-switch':
108
+ specifier: ^1.1.1
109
110
  '@radix-ui/react-tooltip':
111
  specifier: ^1.1.4
112
 
139
  version: 5.5.0
140
  ai:
141
  specifier: ^3.4.33
142
+ version: 3.4.33([email protected])([email protected](svelte@5.4.0))(svelte@5.4.0)(vue@3.5.13([email protected]))([email protected])
143
  date-fns:
144
  specifier: ^3.6.0
145
  version: 3.6.0
 
158
  isbot:
159
  specifier: ^4.4.0
160
  version: 4.4.0
161
+ isomorphic-git:
162
+ specifier: ^1.27.2
163
+ version: 1.27.2
164
  istextorbinary:
165
  specifier: ^9.5.0
166
  version: 9.5.0
 
179
  ollama-ai-provider:
180
  specifier: ^0.15.2
181
  version: 0.15.2([email protected])
 
 
 
182
  react:
183
  specifier: ^18.3.1
184
  version: 18.3.1
 
227
  version: 4.20241127.0
228
  '@remix-run/dev':
229
  specifier: ^2.15.0
230
231
  '@types/diff':
232
  specifier: ^5.2.3
233
  version: 5.2.3
 
258
  node-fetch:
259
  specifier: ^3.3.2
260
  version: 3.3.2
261
+ pnpm:
262
+ specifier: ^9.14.4
263
+ version: 9.14.4
264
  prettier:
265
  specifier: ^3.4.1
266
  version: 3.4.1
 
275
  version: 11.0.5
276
  unocss:
277
  specifier: ^0.61.9
278
279
  vite:
280
  specifier: ^5.4.11
281
+ version: 5.4.11(@types/[email protected])([email protected])
282
  vite-plugin-node-polyfills:
283
  specifier: ^0.22.0
284
285
  vite-plugin-optimize-css-modules:
286
  specifier: ^1.1.0
287
288
  vite-tsconfig-paths:
289
  specifier: ^4.3.2
290
291
  vitest:
292
  specifier: ^2.1.7
293
+ version: 2.1.8(@types/[email protected])([email protected])
294
  wrangler:
295
  specifier: ^3.91.0
296
  version: 3.91.0(@cloudflare/[email protected])
 
678
  '@codemirror/[email protected]':
679
  resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==}
680
 
681
+ '@codemirror/[email protected]':
682
+ resolution: {integrity: sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==}
683
+
684
  '@codemirror/[email protected]':
685
  resolution: {integrity: sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==}
686
 
 
1390
  '@lezer/[email protected]':
1391
  resolution: {integrity: sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==}
1392
 
1393
+ '@lezer/[email protected].20':
1394
+ resolution: {integrity: sha512-Qhl3x+hVPnZkylv+BS//zx77KR4GLxM4PiL02r/D1Zoa4WLQI1A0cHuOr6k0FOTTSCPNNfeNANax0I5DWcXBYw==}
1395
 
1396
  '@lezer/[email protected]':
1397
  resolution: {integrity: sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==}
 
1732
  '@types/react-dom':
1733
  optional: true
1734
 
1735
+ '@radix-ui/[email protected]':
1736
+ resolution: {integrity: sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==}
1737
+ peerDependencies:
1738
+ '@types/react': '*'
1739
+ '@types/react-dom': '*'
1740
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
1741
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
1742
+ peerDependenciesMeta:
1743
+ '@types/react':
1744
+ optional: true
1745
+ '@types/react-dom':
1746
+ optional: true
1747
+
1748
  '@radix-ui/[email protected]':
1749
  resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==}
1750
  peerDependencies:
 
1754
  '@types/react':
1755
  optional: true
1756
 
1757
+ '@radix-ui/[email protected]':
1758
+ resolution: {integrity: sha512-diPqDDoBcZPSicYoMWdWx+bCPuTRH4QSp9J+65IvtdS0Kuzt67bI6n32vCj8q6NZmYW/ah+2orOtMwcX5eQwIg==}
1759
+ peerDependencies:
1760
+ '@types/react': '*'
1761
+ '@types/react-dom': '*'
1762
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
1763
+ react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
1764
+ peerDependenciesMeta:
1765
+ '@types/react':
1766
+ optional: true
1767
+ '@types/react-dom':
1768
+ optional: true
1769
+
1770
  '@radix-ui/[email protected]':
1771
  resolution: {integrity: sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==}
1772
  peerDependencies:
 
1816
  '@types/react':
1817
  optional: true
1818
 
1819
+ '@radix-ui/[email protected]':
1820
+ resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==}
1821
+ peerDependencies:
1822
+ '@types/react': '*'
1823
+ react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
1824
+ peerDependenciesMeta:
1825
+ '@types/react':
1826
+ optional: true
1827
+
1828
  '@radix-ui/[email protected]':
1829
  resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==}
1830
  peerDependencies:
 
2152
  '@types/[email protected]':
2153
  resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
2154
 
2155
+ '@typescript-eslint/eslint-plugin@8.17.0':
2156
+ resolution: {integrity: sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w==}
2157
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2158
  peerDependencies:
2159
  '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0
 
2163
  typescript:
2164
  optional: true
2165
 
2166
+ '@typescript-eslint/parser@8.17.0':
2167
+ resolution: {integrity: sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg==}
2168
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2169
  peerDependencies:
2170
  eslint: ^8.57.0 || ^9.0.0
 
2173
  typescript:
2174
  optional: true
2175
 
2176
+ '@typescript-eslint/scope-manager@8.17.0':
2177
+ resolution: {integrity: sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==}
2178
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2179
 
2180
+ '@typescript-eslint/type-utils@8.17.0':
2181
+ resolution: {integrity: sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw==}
2182
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2183
  peerDependencies:
2184
  eslint: ^8.57.0 || ^9.0.0
 
2187
  typescript:
2188
  optional: true
2189
 
2190
+ '@typescript-eslint/types@8.17.0':
2191
+ resolution: {integrity: sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==}
2192
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2193
 
2194
+ '@typescript-eslint/typescript-estree@8.17.0':
2195
+ resolution: {integrity: sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==}
2196
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2197
  peerDependencies:
2198
  typescript: '*'
 
2200
  typescript:
2201
  optional: true
2202
 
2203
+ '@typescript-eslint/utils@8.17.0':
2204
+ resolution: {integrity: sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==}
2205
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2206
  peerDependencies:
2207
  eslint: ^8.57.0 || ^9.0.0
 
2210
  typescript:
2211
  optional: true
2212
 
2213
+ '@typescript-eslint/visitor-keys@8.17.0':
2214
+ resolution: {integrity: sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==}
2215
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
2216
 
2217
  '@uiw/[email protected]':
 
2325
  '@vanilla-extract/[email protected]':
2326
  resolution: {integrity: sha512-ytsG/JLweEjw7DBuZ/0JCN4WAQgM9erfSTdS1NQY778hFQSZ6cfCDEZZ0sgVm4k54uNz6ImKB33AYvSR//fjxw==}
2327
 
2328
+ '@vitest/[email protected].8':
2329
+ resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==}
2330
 
2331
+ '@vitest/[email protected].8':
2332
+ resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==}
2333
  peerDependencies:
2334
  msw: ^2.4.9
2335
  vite: ^5.0.0
 
2339
  vite:
2340
  optional: true
2341
 
2342
+ '@vitest/[email protected].8':
2343
+ resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==}
2344
 
2345
+ '@vitest/[email protected].8':
2346
+ resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==}
2347
 
2348
+ '@vitest/[email protected].8':
2349
+ resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==}
2350
 
2351
+ '@vitest/[email protected].8':
2352
+ resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==}
2353
 
2354
+ '@vitest/[email protected].8':
2355
+ resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==}
2356
 
2357
+ '@vue/compiler-core@3.5.13':
2358
+ resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==}
2359
 
2360
+ '@vue/compiler-dom@3.5.13':
2361
+ resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==}
2362
 
2363
+ '@vue/compiler-sfc@3.5.13':
2364
+ resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==}
2365
 
2366
+ '@vue/compiler-ssr@3.5.13':
2367
+ resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==}
2368
 
2369
+ '@vue/reactivity@3.5.13':
2370
+ resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==}
2371
 
2372
+ '@vue/runtime-core@3.5.13':
2373
+ resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==}
2374
 
2375
+ '@vue/runtime-dom@3.5.13':
2376
+ resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==}
2377
 
2378
+ '@vue/server-renderer@3.5.13':
2379
+ resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==}
2380
  peerDependencies:
2381
+ vue: 3.5.13
2382
 
2383
+ '@vue/shared@3.5.13':
2384
+ resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==}
2385
 
2386
  '@web3-storage/[email protected]':
2387
  resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==}
 
2418
  peerDependencies:
2419
  acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
2420
 
2421
2422
+ resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==}
2423
+ peerDependencies:
2424
+ acorn: '>=8.9.0'
2425
+
2426
2427
  resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==}
2428
  engines: {node: '>=0.4.0'}
 
2514
  resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==}
2515
  hasBin: true
2516
 
2517
2518
+ resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==}
2519
+
2520
2521
  resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
2522
  engines: {node: '>= 0.4'}
 
2706
  resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==}
2707
  engines: {node: '>= 0.10'}
2708
 
2709
2710
+ resolution: {integrity: sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==}
2711
+
2712
2713
  resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
2714
  engines: {node: '>=6'}
 
2732
  resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
2733
  engines: {node: '>=6'}
2734
 
 
 
 
2735
2736
  resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
2737
  engines: {node: '>=7.0.0'}
 
2801
2802
  resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
2803
 
2804
2805
+ resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
2806
+ engines: {node: '>=0.8'}
2807
+ hasBin: true
2808
+
2809
2810
  resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==}
2811
 
 
2882
2883
  resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
2884
 
2885
2886
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
2887
+ engines: {node: '>=10'}
2888
+
2889
2890
  resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==}
2891
  peerDependencies:
 
2949
2950
  resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==}
2951
 
2952
2953
+ resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==}
2954
+
2955
2956
  resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
2957
  engines: {node: '>=0.3.1'}
 
2983
2984
  resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
2985
 
2986
2987
+ resolution: {integrity: sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==}
2988
 
2989
2990
  resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==}
 
3133
  jiti:
3134
  optional: true
3135
 
3136
3137
+ resolution: {integrity: sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==}
3138
+
3139
3140
  resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
3141
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
 
3148
  resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
3149
  engines: {node: '>=0.10'}
3150
 
3151
3152
+ resolution: {integrity: sha512-ZlQmCCK+n7SGoqo7DnfKaP1sJZa49P01/dXzmjCASSo04p72w8EksT2NMK8CEX8DhKsfJXANioIw8VyHNsBfvQ==}
3153
+
3154
3155
  resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
3156
  engines: {node: '>=4.0'}
 
3443
  resolution: {integrity: sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==}
3444
  engines: {node: '>= 0.4'}
3445
 
3446
+ has-symbols@1.1.0:
3447
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
3448
  engines: {node: '>= 0.4'}
3449
 
3450
 
3548
3549
  resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
3550
 
 
 
 
3551
3552
  resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==}
3553
 
 
3686
3687
  resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
3688
 
3689
3690
+ resolution: {integrity: sha512-nCiz+ieOkWb5kDJSSckDTiMjTcgkxqH2xuiQmw1Y6O/spwx4d6TKYSfGCd4f71HGvUYcRSUGqJEI+3uN6UQlOw==}
3691
+ engines: {node: '>=12'}
3692
+ hasBin: true
3693
+
3694
3695
  resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==}
3696
  engines: {node: '>=10'}
 
4171
  resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
4172
  engines: {node: '>=6'}
4173
 
4174
4175
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
4176
+ engines: {node: '>=10'}
4177
+
4178
4179
  resolution: {integrity: sha512-dM3RBlJE8rUFxnqlPCaFCq0E7qQqEQvKbYX7W/APGCK+rLcyLmEBzC4GQR/niXdNM/oV6gdg9AA50ghnn2ALuw==}
4180
  engines: {node: '>=16.13'}
 
4196
4197
  resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
4198
 
4199
4200
+ resolution: {integrity: sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==}
4201
+
4202
4203
  resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
4204
  engines: {node: '>= 8'}
 
4502
  engines: {node: '>=0.10'}
4503
  hasBin: true
4504
 
4505
4506
+ resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
4507
+ engines: {node: '>=6'}
4508
+
4509
4510
  resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==}
4511
  engines: {node: '>=10'}
 
4875
4876
  resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
4877
 
4878
4879
+ resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==}
4880
  engines: {node: '>=10'}
4881
 
4882
 
5057
  engines: {node: '>=16.0.0'}
5058
  hasBin: true
5059
 
 
 
 
 
 
5060
5061
  resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
5062
 
 
5126
  resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
5127
  engines: {node: '>=14'}
5128
 
5129
5130
+ resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
5131
+
5132
5133
+ resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
5134
+
5135
5136
  resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==}
5137
  engines: {node: '>= 10'}
 
5269
  resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
5270
  engines: {node: '>= 0.4'}
5271
 
5272
+ svelte@5.4.0:
5273
+ resolution: {integrity: sha512-2I/mjD8cXDpKfdfUK+T6yo/OzugMXIm8lhyJUFM5F/gICMYnkl3C/+4cOSpia8TqpDsi6Qfm5+fdmBNMNmaf2g==}
5274
+ engines: {node: '>=18'}
5275
 
5276
5277
  resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==}
 
5405
  resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
5406
  engines: {node: '>= 0.8.0'}
5407
 
5408
+ type-fest@4.30.0:
5409
+ resolution: {integrity: sha512-G6zXWS1dLj6eagy6sVhOMQiLtJdxQBHIA9Z6HFUNLOlr6MFOgzV8wvmidtPONfPtEUv0uZsy77XJNzTAfwPDaA==}
5410
  engines: {node: '>=16'}
5411
 
5412
5413
  resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
5414
  engines: {node: '>= 0.6'}
5415
 
5416
+ typescript-eslint@8.17.0:
5417
+ resolution: {integrity: sha512-409VXvFd/f1br1DCbuKNFqQpXICoTB+V51afcwG1pn1a3Cp92MqAUges3YjwEdQ0cMUoCIodjVDAYzyD8h3SYA==}
5418
  engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
5419
  peerDependencies:
5420
  eslint: ^8.57.0 || ^9.0.0
 
5623
  engines: {node: ^18.0.0 || >=20.0.0}
5624
  hasBin: true
5625
 
5626
5627
+ resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==}
5628
+ engines: {node: ^18.0.0 || >=20.0.0}
5629
  hasBin: true
5630
 
5631
 
5677
  terser:
5678
  optional: true
5679
 
5680
5681
+ resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==}
5682
+ engines: {node: ^18.0.0 || >=20.0.0}
5683
  hasBin: true
5684
  peerDependencies:
5685
  '@edge-runtime/vm': '*'
5686
+ '@types/node': ^18.0.0 || >=20.0.0
5687
+ '@vitest/browser': 2.1.8
5688
+ '@vitest/ui': 2.1.8
5689
  happy-dom: '*'
5690
  jsdom: '*'
5691
  peerDependenciesMeta:
 
5705
5706
  resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==}
5707
 
5708
+ vue@3.5.13:
5709
+ resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==}
5710
  peerDependencies:
5711
  typescript: '*'
5712
  peerDependenciesMeta:
 
5827
5828
  resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==}
5829
 
5830
5831
+ resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==}
5832
+
5833
5834
  resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==}
5835
  peerDependencies:
 
5956
  transitivePeerDependencies:
5957
  - zod
5958
 
5959
+ '@ai-sdk/[email protected](svelte@5.4.0)([email protected])':
5960
  dependencies:
5961
  '@ai-sdk/provider-utils': 1.0.22([email protected])
5962
  '@ai-sdk/ui-utils': 0.0.50([email protected])
5963
+ sswr: 2.1.0(svelte@5.4.0)
5964
  optionalDependencies:
5965
+ svelte: 5.4.0
5966
  transitivePeerDependencies:
5967
  - zod
5968
 
 
5976
  optionalDependencies:
5977
  zod: 3.23.8
5978
 
5979
5980
  dependencies:
5981
  '@ai-sdk/provider-utils': 1.0.22([email protected])
5982
  '@ai-sdk/ui-utils': 0.0.50([email protected])
5983
+ swrv: 1.0.4(vue@3.5.13([email protected]))
5984
  optionalDependencies:
5985
+ vue: 3.5.13([email protected])
5986
  transitivePeerDependencies:
5987
  - zod
5988
 
 
6202
6203
  dependencies:
6204
  '@stylistic/eslint-plugin-ts': 2.11.0([email protected]([email protected]))([email protected])
6205
+ '@typescript-eslint/eslint-plugin': 8.17.0(@typescript-eslint/parser@8.17.0([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
6206
+ '@typescript-eslint/parser': 8.17.0([email protected]([email protected]))([email protected])
6207
+ '@typescript-eslint/utils': 8.17.0([email protected]([email protected]))([email protected])
6208
  common-tags: 1.8.2
6209
  eslint: 9.16.0([email protected])
6210
  eslint-config-prettier: 9.1.0([email protected]([email protected]))
6211
  eslint-plugin-jsonc: 2.18.2([email protected]([email protected]))
6212
6213
  globals: 15.13.0
6214
6215
  transitivePeerDependencies:
6216
  - '@eslint/json'
6217
  - '@types/eslint'
 
6301
  '@codemirror/state': 6.4.1
6302
  '@codemirror/view': 6.35.0
6303
  '@lezer/common': 1.2.3
6304
+ '@lezer/javascript': 1.4.20
6305
 
6306
  '@codemirror/[email protected]':
6307
  dependencies:
 
6338
  transitivePeerDependencies:
6339
  - '@codemirror/view'
6340
 
6341
+ '@codemirror/[email protected]':
6342
+ dependencies:
6343
+ '@codemirror/lang-html': 6.4.9
6344
+ '@codemirror/lang-javascript': 6.2.2
6345
+ '@codemirror/language': 6.10.6
6346
+ '@lezer/common': 1.2.3
6347
+ '@lezer/highlight': 1.2.1
6348
+ '@lezer/lr': 1.4.2
6349
+
6350
  '@codemirror/[email protected]':
6351
  dependencies:
6352
  '@codemirror/language': 6.10.6
 
6822
  '@lezer/highlight': 1.2.1
6823
  '@lezer/lr': 1.4.2
6824
 
6825
+ '@lezer/[email protected].20':
6826
  dependencies:
6827
  '@lezer/common': 1.2.3
6828
  '@lezer/highlight': 1.2.1
 
7214
  '@types/react': 18.3.12
7215
  '@types/react-dom': 18.3.1
7216
 
7217
7218
+ dependencies:
7219
+ '@radix-ui/react-primitive': 2.0.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
7220
+ react: 18.3.1
7221
+ react-dom: 18.3.1([email protected])
7222
+ optionalDependencies:
7223
+ '@types/react': 18.3.12
7224
+ '@types/react-dom': 18.3.1
7225
+
7226
7227
  dependencies:
7228
  '@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
 
7230
  optionalDependencies:
7231
  '@types/react': 18.3.12
7232
 
7233
7234
+ dependencies:
7235
+ '@radix-ui/primitive': 1.1.0
7236
+ '@radix-ui/react-compose-refs': 1.1.0(@types/[email protected])([email protected])
7237
+ '@radix-ui/react-context': 1.1.1(@types/[email protected])([email protected])
7238
+ '@radix-ui/react-primitive': 2.0.0(@types/[email protected])(@types/[email protected])([email protected]([email protected]))([email protected])
7239
+ '@radix-ui/react-use-controllable-state': 1.1.0(@types/[email protected])([email protected])
7240
+ '@radix-ui/react-use-previous': 1.1.0(@types/[email protected])([email protected])
7241
+ '@radix-ui/react-use-size': 1.1.0(@types/[email protected])([email protected])
7242
+ react: 18.3.1
7243
+ react-dom: 18.3.1([email protected])
7244
+ optionalDependencies:
7245
+ '@types/react': 18.3.12
7246
+ '@types/react-dom': 18.3.1
7247
+
7248
7249
  dependencies:
7250
  '@radix-ui/primitive': 1.1.0
 
7291
  optionalDependencies:
7292
  '@types/react': 18.3.12
7293
 
7294
7295
+ dependencies:
7296
+ react: 18.3.1
7297
+ optionalDependencies:
7298
+ '@types/react': 18.3.12
7299
+
7300
7301
  dependencies:
7302
  '@radix-ui/rect': 1.1.0
 
7337
  optionalDependencies:
7338
  typescript: 5.7.2
7339
 
7340
7341
  dependencies:
7342
  '@babel/core': 7.26.0
7343
  '@babel/generator': 7.26.2
 
7354
  '@remix-run/router': 1.21.0
7355
  '@remix-run/server-runtime': 2.15.0([email protected])
7356
  '@types/mdx': 2.0.13
7357
+ '@vanilla-extract/integration': 6.5.0(@types/[email protected])([email protected])
7358
  arg: 5.0.2
7359
  cacache: 17.1.4
7360
  chalk: 4.1.2
 
7393
  tar-fs: 2.1.1
7394
  tsconfig-paths: 4.2.0
7395
  valibot: 0.41.0([email protected])
7396
+ vite-node: 1.6.0(@types/[email protected])([email protected])
7397
  ws: 7.5.10
7398
  optionalDependencies:
7399
  typescript: 5.7.2
7400
+ vite: 5.4.11(@types/[email protected])([email protected])
7401
  wrangler: 3.91.0(@cloudflare/[email protected])
7402
  transitivePeerDependencies:
7403
  - '@types/node'
 
7580
 
7581
7582
  dependencies:
7583
+ '@typescript-eslint/utils': 8.17.0([email protected]([email protected]))([email protected])
7584
  eslint: 9.16.0([email protected])
7585
  eslint-visitor-keys: 4.2.0
7586
  espree: 10.3.0
 
7665
 
7666
  '@types/[email protected]': {}
7667
 
7668
+ '@typescript-eslint/eslint-plugin@8.17.0(@typescript-eslint/parser@8.17.0([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])':
7669
  dependencies:
7670
  '@eslint-community/regexpp': 4.12.1
7671
+ '@typescript-eslint/parser': 8.17.0([email protected]([email protected]))([email protected])
7672
+ '@typescript-eslint/scope-manager': 8.17.0
7673
+ '@typescript-eslint/type-utils': 8.17.0([email protected]([email protected]))([email protected])
7674
+ '@typescript-eslint/utils': 8.17.0([email protected]([email protected]))([email protected])
7675
+ '@typescript-eslint/visitor-keys': 8.17.0
7676
  eslint: 9.16.0([email protected])
7677
  graphemer: 1.4.0
7678
  ignore: 5.3.2
 
7683
  transitivePeerDependencies:
7684
  - supports-color
7685
 
7686
+ '@typescript-eslint/parser@8.17.0([email protected]([email protected]))([email protected])':
7687
  dependencies:
7688
+ '@typescript-eslint/scope-manager': 8.17.0
7689
+ '@typescript-eslint/types': 8.17.0
7690
+ '@typescript-eslint/typescript-estree': 8.17.0([email protected])
7691
+ '@typescript-eslint/visitor-keys': 8.17.0
7692
  debug: 4.3.7
7693
  eslint: 9.16.0([email protected])
7694
  optionalDependencies:
 
7696
  transitivePeerDependencies:
7697
  - supports-color
7698
 
7699
+ '@typescript-eslint/scope-manager@8.17.0':
7700
  dependencies:
7701
+ '@typescript-eslint/types': 8.17.0
7702
+ '@typescript-eslint/visitor-keys': 8.17.0
7703
 
7704
+ '@typescript-eslint/type-utils@8.17.0([email protected]([email protected]))([email protected])':
7705
  dependencies:
7706
+ '@typescript-eslint/typescript-estree': 8.17.0([email protected])
7707
+ '@typescript-eslint/utils': 8.17.0([email protected]([email protected]))([email protected])
7708
  debug: 4.3.7
7709
  eslint: 9.16.0([email protected])
7710
  ts-api-utils: 1.4.3([email protected])
 
7713
  transitivePeerDependencies:
7714
  - supports-color
7715
 
7716
+ '@typescript-eslint/types@8.17.0': {}
7717
 
7718
+ '@typescript-eslint/typescript-estree@8.17.0([email protected])':
7719
  dependencies:
7720
+ '@typescript-eslint/types': 8.17.0
7721
+ '@typescript-eslint/visitor-keys': 8.17.0
7722
  debug: 4.3.7
7723
  fast-glob: 3.3.2
7724
  is-glob: 4.0.3
 
7730
  transitivePeerDependencies:
7731
  - supports-color
7732
 
7733
+ '@typescript-eslint/utils@8.17.0([email protected]([email protected]))([email protected])':
7734
  dependencies:
7735
  '@eslint-community/eslint-utils': 4.4.1([email protected]([email protected]))
7736
+ '@typescript-eslint/scope-manager': 8.17.0
7737
+ '@typescript-eslint/types': 8.17.0
7738
+ '@typescript-eslint/typescript-estree': 8.17.0([email protected])
7739
  eslint: 9.16.0([email protected])
7740
  optionalDependencies:
7741
  typescript: 5.7.2
7742
  transitivePeerDependencies:
7743
  - supports-color
7744
 
7745
+ '@typescript-eslint/visitor-keys@8.17.0':
7746
  dependencies:
7747
+ '@typescript-eslint/types': 8.17.0
7748
  eslint-visitor-keys: 4.2.0
7749
 
7750
  '@uiw/[email protected](@codemirror/[email protected])(@codemirror/[email protected])(@codemirror/[email protected])':
 
7763
 
7764
  '@ungap/[email protected]': {}
7765
 
7766
7767
  dependencies:
7768
  '@unocss/core': 0.61.9
7769
  '@unocss/reset': 0.61.9
7770
7771
  optionalDependencies:
7772
+ vite: 5.4.11(@types/[email protected])([email protected])
7773
  transitivePeerDependencies:
7774
  - rollup
7775
  - supports-color
 
7906
  dependencies:
7907
  '@unocss/core': 0.61.9
7908
 
7909
7910
  dependencies:
7911
  '@ampproject/remapping': 2.3.0
7912
  '@rollup/pluginutils': 5.1.3([email protected])
 
7918
  chokidar: 3.6.0
7919
  fast-glob: 3.3.2
7920
  magic-string: 0.30.14
7921
+ vite: 5.4.11(@types/[email protected])([email protected])
7922
  transitivePeerDependencies:
7923
  - rollup
7924
  - supports-color
 
7946
  transitivePeerDependencies:
7947
  - babel-plugin-macros
7948
 
7949
7950
  dependencies:
7951
  '@babel/core': 7.26.0
7952
  '@babel/plugin-syntax-typescript': 7.25.9(@babel/[email protected])
 
7959
  lodash: 4.17.21
7960
  mlly: 1.7.3
7961
  outdent: 0.8.0
7962
+ vite: 5.4.11(@types/[email protected])([email protected])
7963
+ vite-node: 1.6.0(@types/[email protected])([email protected])
7964
  transitivePeerDependencies:
7965
  - '@types/node'
7966
  - babel-plugin-macros
 
7975
 
7976
  '@vanilla-extract/[email protected]': {}
7977
 
7978
+ '@vitest/[email protected].8':
7979
  dependencies:
7980
+ '@vitest/spy': 2.1.8
7981
+ '@vitest/utils': 2.1.8
7982
  chai: 5.1.2
7983
  tinyrainbow: 1.2.0
7984
 
7985
7986
  dependencies:
7987
+ '@vitest/spy': 2.1.8
7988
  estree-walker: 3.0.3
7989
  magic-string: 0.30.14
7990
  optionalDependencies:
7991
+ vite: 5.4.11(@types/[email protected])([email protected])
7992
 
7993
+ '@vitest/[email protected].8':
7994
  dependencies:
7995
  tinyrainbow: 1.2.0
7996
 
7997
+ '@vitest/[email protected].8':
7998
  dependencies:
7999
+ '@vitest/utils': 2.1.8
8000
  pathe: 1.1.2
8001
 
8002
+ '@vitest/[email protected].8':
8003
  dependencies:
8004
+ '@vitest/pretty-format': 2.1.8
8005
  magic-string: 0.30.14
8006
  pathe: 1.1.2
8007
 
8008
+ '@vitest/[email protected].8':
8009
  dependencies:
8010
  tinyspy: 3.0.2
8011
 
8012
+ '@vitest/[email protected].8':
8013
  dependencies:
8014
+ '@vitest/pretty-format': 2.1.8
8015
  loupe: 3.1.2
8016
  tinyrainbow: 1.2.0
8017
 
8018
+ '@vue/compiler-core@3.5.13':
8019
  dependencies:
8020
  '@babel/parser': 7.26.2
8021
+ '@vue/shared': 3.5.13
8022
  entities: 4.5.0
8023
  estree-walker: 2.0.2
8024
  source-map-js: 1.2.1
8025
 
8026
+ '@vue/compiler-dom@3.5.13':
8027
  dependencies:
8028
+ '@vue/compiler-core': 3.5.13
8029
+ '@vue/shared': 3.5.13
8030
 
8031
+ '@vue/compiler-sfc@3.5.13':
8032
  dependencies:
8033
  '@babel/parser': 7.26.2
8034
+ '@vue/compiler-core': 3.5.13
8035
+ '@vue/compiler-dom': 3.5.13
8036
+ '@vue/compiler-ssr': 3.5.13
8037
+ '@vue/shared': 3.5.13
8038
  estree-walker: 2.0.2
8039
  magic-string: 0.30.14
8040
  postcss: 8.4.49
8041
  source-map-js: 1.2.1
8042
 
8043
+ '@vue/compiler-ssr@3.5.13':
8044
  dependencies:
8045
+ '@vue/compiler-dom': 3.5.13
8046
+ '@vue/shared': 3.5.13
8047
 
8048
+ '@vue/reactivity@3.5.13':
8049
  dependencies:
8050
+ '@vue/shared': 3.5.13
8051
 
8052
+ '@vue/runtime-core@3.5.13':
8053
  dependencies:
8054
+ '@vue/reactivity': 3.5.13
8055
+ '@vue/shared': 3.5.13
8056
 
8057
+ '@vue/runtime-dom@3.5.13':
8058
  dependencies:
8059
+ '@vue/reactivity': 3.5.13
8060
+ '@vue/runtime-core': 3.5.13
8061
+ '@vue/shared': 3.5.13
8062
  csstype: 3.1.3
8063
 
8064
+ '@vue/server-renderer@3.5.13(vue@3.5.13([email protected]))':
8065
  dependencies:
8066
+ '@vue/compiler-ssr': 3.5.13
8067
+ '@vue/shared': 3.5.13
8068
+ vue: 3.5.13([email protected])
8069
 
8070
+ '@vue/shared@3.5.13': {}
8071
 
8072
  '@web3-storage/[email protected]': {}
8073
 
 
8099
  dependencies:
8100
  acorn: 8.14.0
8101
 
8102
8103
+ dependencies:
8104
+ acorn: 8.14.0
8105
+
8106
8107
  dependencies:
8108
  acorn: 8.14.0
 
8114
  clean-stack: 2.2.0
8115
  indent-string: 4.0.0
8116
 
8117
8118
  dependencies:
8119
  '@ai-sdk/provider': 0.0.26
8120
  '@ai-sdk/provider-utils': 1.0.22([email protected])
8121
  '@ai-sdk/react': 0.0.70([email protected])([email protected])
8122
  '@ai-sdk/solid': 0.0.54([email protected])
8123
+ '@ai-sdk/svelte': 0.0.57(svelte@5.4.0)([email protected])
8124
  '@ai-sdk/ui-utils': 0.0.50([email protected])
8125
+ '@ai-sdk/vue': 0.0.59(vue@3.5.13([email protected]))([email protected])
8126
  '@opentelemetry/api': 1.9.0
8127
  eventsource-parser: 1.1.2
8128
  json-schema: 0.4.0
 
8131
  zod-to-json-schema: 3.23.5([email protected])
8132
  optionalDependencies:
8133
  react: 18.3.1
8134
+ sswr: 2.1.0(svelte@5.4.0)
8135
+ svelte: 5.4.0
8136
  zod: 3.23.8
8137
  transitivePeerDependencies:
8138
  - solid-js
 
8194
 
8195
8196
 
8197
8198
+
8199
8200
  dependencies:
8201
  possible-typed-array-names: 1.0.0
 
8316
8317
  dependencies:
8318
  caniuse-lite: 1.0.30001685
8319
+ electron-to-chromium: 1.5.68
8320
  node-releases: 2.0.18
8321
  update-browserslist-db: 1.1.1([email protected])
8322
 
 
8430
  inherits: 2.0.4
8431
  safe-buffer: 5.2.1
8432
 
8433
8434
+
8435
8436
 
8437
 
8446
 
8447
8448
 
 
 
 
 
 
 
 
 
8449
8450
  dependencies:
8451
  color-name: 1.1.4
 
8490
 
8491
8492
 
8493
8494
+
8495
8496
  dependencies:
8497
  bn.js: 4.12.1
 
8572
  dependencies:
8573
  character-entities: 2.0.2
8574
 
8575
8576
+ dependencies:
8577
+ mimic-response: 3.1.0
8578
+
8579
8580
 
8581
 
8625
 
8626
8627
 
8628
8629
+
8630
8631
 
8632
 
8656
 
8657
8658
 
8659
8660
 
8661
8662
  dependencies:
 
8699
  '@jspm/core': 2.0.1
8700
  esbuild: 0.17.6
8701
  local-pkg: 0.5.1
8702
+ resolve.exports: 2.0.3
8703
 
8704
8705
  optionalDependencies:
 
8901
  transitivePeerDependencies:
8902
  - supports-color
8903
 
8904
8905
+
8906
8907
  dependencies:
8908
  acorn: 8.14.0
 
8919
  dependencies:
8920
  estraverse: 5.3.0
8921
 
8922
8923
+ dependencies:
8924
+ '@jridgewell/sourcemap-codec': 1.5.0
8925
+ '@types/estree': 1.0.6
8926
+
8927
8928
  dependencies:
8929
  estraverse: 5.3.0
 
9166
  es-errors: 1.3.0
9167
  function-bind: 1.1.2
9168
  has-proto: 1.1.0
9169
+ has-symbols: 1.1.0
9170
  hasown: 2.0.2
9171
 
9172
 
9242
  dependencies:
9243
  call-bind: 1.0.7
9244
 
9245
+ has-symbols@1.1.0: {}
9246
 
9247
9248
  dependencies:
9249
+ has-symbols: 1.1.0
9250
 
9251
9252
  dependencies:
 
9421
 
9422
9423
 
 
 
 
9424
9425
 
9426
 
9535
 
9536
9537
 
9538
9539
+ dependencies:
9540
+ async-lock: 1.4.1
9541
+ clean-git-ref: 2.0.1
9542
+ crc-32: 1.2.2
9543
+ diff3: 0.0.3
9544
+ ignore: 5.3.2
9545
+ minimisted: 2.0.1
9546
+ pako: 1.0.11
9547
+ path-browserify: 1.0.1
9548
+ pify: 4.0.1
9549
+ readable-stream: 3.6.2
9550
+ sha.js: 2.4.11
9551
+ simple-get: 4.0.1
9552
+
9553
9554
 
9555
 
10407
 
10408
10409
 
10410
10411
+
10412
10413
  dependencies:
10414
  '@cspotcode/source-map-support': 0.8.1
 
10442
 
10443
10444
 
10445
10446
+ dependencies:
10447
+ minimist: 1.2.8
10448
+
10449
10450
  dependencies:
10451
  minipass: 3.3.6
 
10596
  dependencies:
10597
  call-bind: 1.0.7
10598
  define-properties: 1.2.1
10599
+ has-symbols: 1.1.0
10600
  object-keys: 1.1.1
10601
 
10602
 
10763
 
10764
10765
 
10766
10767
+
10768
10769
  dependencies:
10770
  find-up: 5.0.0
 
11140
 
11141
11142
  dependencies:
11143
+ type-fest: 4.30.0
11144
  optionalDependencies:
11145
  '@remix-run/cloudflare': 2.15.0(@cloudflare/[email protected])([email protected])
11146
  '@remix-run/node': 2.15.0([email protected])
 
11155
 
11156
11157
 
11158
11159
 
11160
11161
  dependencies:
 
11325
  sass-embedded-win32-ia32: 1.81.0
11326
  sass-embedded-win32-x64: 1.81.0
11327
 
 
 
 
 
 
 
 
11328
11329
  dependencies:
11330
  loose-envify: 1.4.0
 
11415
 
11416
11417
 
11418
11419
+
11420
11421
+ dependencies:
11422
+ decompress-response: 6.0.0
11423
+ once: 1.4.0
11424
+ simple-concat: 1.0.1
11425
+
11426
11427
  dependencies:
11428
  '@polka/url': 1.0.0-next.28
 
11462
  dependencies:
11463
  minipass: 7.1.2
11464
 
11465
+ [email protected](svelte@5.4.0):
11466
  dependencies:
11467
+ svelte: 5.4.0
11468
  swrev: 4.0.0
11469
 
11470
 
11557
 
11558
11559
 
11560
+ svelte@5.4.0:
11561
  dependencies:
11562
  '@ampproject/remapping': 2.3.0
11563
  '@jridgewell/sourcemap-codec': 1.5.0
 
11564
  '@types/estree': 1.0.6
11565
  acorn: 8.14.0
11566
+ acorn-typescript: 1.4.13([email protected])
11567
  aria-query: 5.3.2
11568
  axobject-query: 4.1.0
11569
+ esm-env: 1.2.1
11570
+ esrap: 1.2.3
 
11571
  is-reference: 3.0.3
11572
  locate-character: 3.0.0
11573
  magic-string: 0.30.14
11574
+ zimmerframe: 1.1.2
11575
 
11576
11577
  dependencies:
 
11581
 
11582
11583
 
11584
11585
  dependencies:
11586
+ vue: 3.5.13([email protected])
11587
 
11588
11589
  dependencies:
 
11694
  dependencies:
11695
  prelude-ls: 1.2.1
11696
 
11697
+ type-fest@4.30.0: {}
11698
 
11699
11700
  dependencies:
11701
  media-typer: 0.3.0
11702
  mime-types: 2.1.35
11703
 
11704
11705
  dependencies:
11706
+ '@typescript-eslint/eslint-plugin': 8.17.0(@typescript-eslint/parser@8.17.0([email protected]([email protected]))([email protected]))([email protected]([email protected]))([email protected])
11707
+ '@typescript-eslint/parser': 8.17.0([email protected]([email protected]))([email protected])
11708
+ '@typescript-eslint/utils': 8.17.0([email protected]([email protected]))([email protected])
11709
  eslint: 9.16.0([email protected])
11710
  optionalDependencies:
11711
  typescript: 5.7.2
 
11828
 
11829
11830
 
11831
11832
  dependencies:
11833
11834
  '@unocss/cli': 0.61.9([email protected])
11835
  '@unocss/core': 0.61.9
11836
  '@unocss/extractor-arbitrary-variants': 0.61.9
 
11849
  '@unocss/transformer-compile-class': 0.61.9
11850
  '@unocss/transformer-directives': 0.61.9
11851
  '@unocss/transformer-variant-group': 0.61.9
11852
11853
  optionalDependencies:
11854
+ vite: 5.4.11(@types/[email protected])([email protected])
11855
  transitivePeerDependencies:
11856
  - postcss
11857
  - rollup
 
11956
  '@types/unist': 3.0.3
11957
  vfile-message: 4.0.2
11958
 
11959
11960
  dependencies:
11961
  cac: 6.7.14
11962
  debug: 4.3.7
11963
  pathe: 1.1.2
11964
  picocolors: 1.1.1
11965
+ vite: 5.4.11(@types/[email protected])([email protected])
11966
  transitivePeerDependencies:
11967
  - '@types/node'
11968
  - less
 
11974
  - supports-color
11975
  - terser
11976
 
11977
11978
  dependencies:
11979
  cac: 6.7.14
11980
  debug: 4.3.7
11981
  es-module-lexer: 1.5.4
11982
  pathe: 1.1.2
11983
+ vite: 5.4.11(@types/[email protected])([email protected])
11984
  transitivePeerDependencies:
11985
  - '@types/node'
11986
  - less
 
11992
  - supports-color
11993
  - terser
11994
 
11995
11996
  dependencies:
11997
  '@rollup/plugin-inject': 5.0.5([email protected])
11998
  node-stdlib-browser: 1.3.0
11999
+ vite: 5.4.11(@types/[email protected])([email protected])
12000
  transitivePeerDependencies:
12001
  - rollup
12002
 
12003
12004
  dependencies:
12005
+ vite: 5.4.11(@types/[email protected])([email protected])
12006
 
12007
12008
  dependencies:
12009
  debug: 4.3.7
12010
  globrex: 0.1.2
12011
  tsconfck: 3.1.4([email protected])
12012
  optionalDependencies:
12013
+ vite: 5.4.11(@types/[email protected])([email protected])
12014
  transitivePeerDependencies:
12015
  - supports-color
12016
  - typescript
12017
 
12018
12019
  dependencies:
12020
  esbuild: 0.21.5
12021
  postcss: 8.4.49
 
12023
  optionalDependencies:
12024
  '@types/node': 22.10.1
12025
  fsevents: 2.3.3
 
12026
  sass-embedded: 1.81.0
12027
 
12028
12029
  dependencies:
12030
+ '@vitest/expect': 2.1.8
12031
+ '@vitest/mocker': 2.1.8([email protected](@types/[email protected])([email protected]))
12032
+ '@vitest/pretty-format': 2.1.8
12033
+ '@vitest/runner': 2.1.8
12034
+ '@vitest/snapshot': 2.1.8
12035
+ '@vitest/spy': 2.1.8
12036
+ '@vitest/utils': 2.1.8
12037
  chai: 5.1.2
12038
  debug: 4.3.7
12039
  expect-type: 1.1.0
 
12044
  tinyexec: 0.3.1
12045
  tinypool: 1.0.2
12046
  tinyrainbow: 1.2.0
12047
+ vite: 5.4.11(@types/[email protected])([email protected])
12048
+ vite-node: 2.1.8(@types/[email protected])([email protected])
12049
  why-is-node-running: 2.3.0
12050
  optionalDependencies:
12051
  '@types/node': 22.10.1
 
12062
 
12063
12064
 
12065
+ vue@3.5.13([email protected]):
12066
  dependencies:
12067
+ '@vue/compiler-dom': 3.5.13
12068
+ '@vue/compiler-sfc': 3.5.13
12069
+ '@vue/runtime-dom': 3.5.13
12070
+ '@vue/server-renderer': 3.5.13(vue@3.5.13([email protected]))
12071
+ '@vue/shared': 3.5.13
12072
  optionalDependencies:
12073
  typescript: 5.7.2
12074
 
 
12134
  nanoid: 3.3.8
12135
  path-to-regexp: 6.3.0
12136
  resolve: 1.22.8
12137
+ resolve.exports: 2.0.3
12138
  selfsigned: 2.4.1
12139
  source-map: 0.6.1
12140
  unenv: [email protected]
 
12184
  mustache: 4.2.0
12185
  stacktracey: 2.1.8
12186
 
12187
12188
+
12189
12190
  dependencies:
12191
  zod: 3.23.8
public/favicon.svg CHANGED
public/logo-dark-styled.png ADDED
public/logo-dark.png ADDED
public/logo-light-styled.png ADDED
public/logo-light.png ADDED
public/logo.svg CHANGED
public/social_preview_index.jpg CHANGED
uno.config.ts CHANGED
@@ -35,17 +35,17 @@ const BASE_COLORS = {
35
  950: '#0A0A0A',
36
  },
37
  accent: {
38
- 50: '#EEF9FF',
39
- 100: '#D8F1FF',
40
- 200: '#BAE7FF',
41
- 300: '#8ADAFF',
42
- 400: '#53C4FF',
43
- 500: '#2BA6FF',
44
- 600: '#1488FC',
45
- 700: '#0D6FE8',
46
- 800: '#1259BB',
47
- 900: '#154E93',
48
- 950: '#122F59',
49
  },
50
  green: {
51
  50: '#F0FDF4',
 
35
  950: '#0A0A0A',
36
  },
37
  accent: {
38
+ 50: '#F8F5FF',
39
+ 100: '#F0EBFF',
40
+ 200: '#E1D6FF',
41
+ 300: '#CEBEFF',
42
+ 400: '#B69EFF',
43
+ 500: '#9C7DFF',
44
+ 600: '#8A5FFF',
45
+ 700: '#7645E8',
46
+ 800: '#6234BB',
47
+ 900: '#502D93',
48
+ 950: '#2D1959',
49
  },
50
  green: {
51
  50: '#F0FDF4',