diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml new file mode 100644 index 00000000..2f194cdf --- /dev/null +++ b/.github/workflows/commit.yaml @@ -0,0 +1,32 @@ +name: Update Commit Hash File + +on: + push: + branches: + - main + +permissions: + contents: write + +jobs: + update-commit: + runs-on: ubuntu-latest + + steps: + - name: Checkout the code + uses: actions/checkout@v3 + + - name: Get the latest commit hash + run: echo "COMMIT_HASH=$(git rev-parse HEAD)" >> $GITHUB_ENV + + - name: Update commit file + run: | + echo "{ \"commit\": \"$COMMIT_HASH\" }" > app/commit.json + + - name: Commit and push the update + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add app/commit.json + git commit -m "chore: update commit hash to $COMMIT_HASH" + git push \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit index 05fe9ee6..1aab67dc 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -5,15 +5,21 @@ echo "🔍 Running pre-commit hook to check the code looks good... 🔍" export NVM_DIR="$HOME/.nvm" [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # Load nvm if you're using i +echo "Running typecheck..." +which pnpm + if ! pnpm typecheck; then - echo "❌ Type checking failed! Please review TypeScript types." - echo "Once you're done, don't forget to add your changes to the commit! 🚀" - exit 1 + echo "❌ Type checking failed! Please review TypeScript types." + echo "Once you're done, don't forget to add your changes to the commit! 🚀" + echo "Typecheck exit code: $?" + exit 1 fi +echo "Running lint..." if ! pnpm lint; then echo "❌ Linting failed! 'pnpm lint:fix' will help you fix the easy ones." echo "Once you're done, don't forget to add your beautification to the commit! 🤩" + echo "lint exit code: $?" exit 1 fi diff --git a/README.md b/README.md index 33f861fa..3b1d2827 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ [![Bolt.new: AI-Powered Full-Stack Web Development in the Browser](./public/social_preview_index.jpg)](https://bolt.new) -# Bolt.new Fork by Cole Medin - oTToDev +# Bolt.diy (Previously oTToDev) -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. +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. -Check the [oTToDev Docs](https://coleam00.github.io/bolt.new-any-llm/) for more information. +Check the [Bolt.diy Docs](https://stackblitz-labs.github.io/bolt.diy/) for more information. This documentation is still being updated after the transfer. -## Join the community for oTToDev! +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! + +## Join the community for Bolt.diy! https://thinktank.ottomator.ai @@ -41,6 +43,7 @@ https://thinktank.ottomator.ai - ✅ Mobile friendly (@qwikode) - ✅ Better prompt enhancing (@SujalXplores) - ✅ Attach images to prompts (@atrokhym) +- ✅ Detect package.json and commands to auto install and run preview for folder and git import (@wonderwhy-er) - ⬜ **HIGH PRIORITY** - Prevent Bolt from rewriting files as often (file locking and diffs) - ⬜ **HIGH PRIORITY** - Better prompting for smaller LLMs (code window sometimes doesn't start) - ⬜ **HIGH PRIORITY** - Run agents in the backend as opposed to a single model call @@ -55,7 +58,7 @@ https://thinktank.ottomator.ai ## Bolt.new: AI-Powered Full-Stack Web Development in the Browser -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) +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) ## What Makes Bolt.new Different @@ -95,7 +98,7 @@ If you see usr/local/bin in the output then you're good to go. 3. Clone the repository (if you haven't already) by opening a Terminal window (or CMD with admin permissions) and then typing in this: ``` -git clone https://github.com/coleam00/bolt.new-any-llm.git +git clone https://github.com/stackblitz-labs/bolt.diy.git ``` 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 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. -## How do I contribute to oTToDev? +## How do I contribute to Bolt.diy? -[Please check out our dedicated page for contributing to oTToDev here!](CONTRIBUTING.md) +[Please check out our dedicated page for contributing to Bolt.diy here!](CONTRIBUTING.md) -## What are the future plans for oTToDev? +## What are the future plans for Bolt.diy? [Check out our Roadmap here!](https://roadmap.sh/r/ottodev-roadmap-2ovzo) @@ -236,4 +239,4 @@ Lot more updates to this roadmap coming soon! ## FAQ -[Please check out our dedicated page for FAQ's related to oTToDev here!](FAQ.md) +[Please check out our dedicated page for FAQ's related to Bolt.diy here!](FAQ.md) diff --git a/app/commit.json b/app/commit.json new file mode 100644 index 00000000..208eefff --- /dev/null +++ b/app/commit.json @@ -0,0 +1 @@ +{ "commit": "154935cdeb054d2cc22dfb0c7e6cf084f02b95d0" } diff --git a/app/components/chat/Artifact.tsx b/app/components/chat/Artifact.tsx index 989b92b2..5f0c9910 100644 --- a/app/components/chat/Artifact.tsx +++ b/app/components/chat/Artifact.tsx @@ -52,7 +52,7 @@ export const Artifact = memo(({ messageId }: ArtifactProps) => { if (actions.length !== 0 && artifact.type === 'bundled') { const finished = !actions.find((action) => action.status !== 'complete'); - if (finished != allActionFinished) { + if (allActionFinished !== finished) { setAllActionFinished(finished); } } diff --git a/app/components/chat/BaseChat.module.scss b/app/components/chat/BaseChat.module.scss index cf530a11..4908e34e 100644 --- a/app/components/chat/BaseChat.module.scss +++ b/app/components/chat/BaseChat.module.scss @@ -18,82 +18,6 @@ opacity: 1; } -.RayContainer { - --gradient-opacity: 0.85; - --ray-gradient: radial-gradient(rgba(83, 196, 255, var(--gradient-opacity)) 0%, rgba(43, 166, 255, 0) 100%); - transition: opacity 0.25s linear; - position: fixed; - inset: 0; - pointer-events: none; - user-select: none; -} - -.LightRayOne { - width: 480px; - height: 680px; - transform: rotate(80deg); - top: -540px; - left: 250px; - filter: blur(110px); - position: absolute; - border-radius: 100%; - background: var(--ray-gradient); -} - -.LightRayTwo { - width: 110px; - height: 400px; - transform: rotate(-20deg); - top: -280px; - left: 350px; - mix-blend-mode: overlay; - opacity: 0.6; - filter: blur(60px); - position: absolute; - border-radius: 100%; - background: var(--ray-gradient); -} - -.LightRayThree { - width: 400px; - height: 370px; - top: -350px; - left: 200px; - mix-blend-mode: overlay; - opacity: 0.6; - filter: blur(21px); - position: absolute; - border-radius: 100%; - background: var(--ray-gradient); -} - -.LightRayFour { - position: absolute; - width: 330px; - height: 370px; - top: -330px; - left: 50px; - mix-blend-mode: overlay; - opacity: 0.5; - filter: blur(21px); - border-radius: 100%; - background: var(--ray-gradient); -} - -.LightRayFive { - position: absolute; - width: 110px; - height: 400px; - transform: rotate(-40deg); - top: -280px; - left: -10px; - mix-blend-mode: overlay; - opacity: 0.8; - filter: blur(60px); - border-radius: 100%; - background: var(--ray-gradient); -} - .PromptEffectContainer { --prompt-container-offset: 50px; --prompt-line-stroke-width: 1px; diff --git a/app/components/chat/BaseChat.tsx b/app/components/chat/BaseChat.tsx index 8c7589a6..0d8933b1 100644 --- a/app/components/chat/BaseChat.tsx +++ b/app/components/chat/BaseChat.tsx @@ -21,6 +21,7 @@ import type { ProviderInfo } from '~/utils/types'; import { ExportChatButton } from '~/components/chat/chatExportAndImport/ExportChatButton'; import { ImportButtons } from '~/components/chat/chatExportAndImport/ImportButtons'; import { ExamplePrompts } from '~/components/chat/ExamplePrompts'; +import GitCloneButton from './GitCloneButton'; import FilePreview from './FilePreview'; import { ModelSelector } from '~/components/chat/ModelSelector'; @@ -87,14 +88,68 @@ export const BaseChat = React.forwardRef( ref, ) => { const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200; - const [apiKeys, setApiKeys] = useState>({}); + const [apiKeys, setApiKeys] = useState>(() => { + const savedKeys = Cookies.get('apiKeys'); + + if (savedKeys) { + try { + return JSON.parse(savedKeys); + } catch (error) { + console.error('Failed to parse API keys from cookies:', error); + return {}; + } + } + + return {}; + }); const [modelList, setModelList] = useState(MODEL_LIST); const [isModelSettingsCollapsed, setIsModelSettingsCollapsed] = useState(false); const [isListening, setIsListening] = useState(false); const [recognition, setRecognition] = useState(null); const [transcript, setTranscript] = useState(''); - console.log(transcript); + // Load enabled providers from cookies + const [enabledProviders, setEnabledProviders] = useState(() => { + const savedProviders = Cookies.get('providers'); + + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + return PROVIDER_LIST.filter((p) => parsedProviders[p.name]); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + return PROVIDER_LIST; + } + } + + return PROVIDER_LIST; + }); + + // Update enabled providers when cookies change + useEffect(() => { + const updateProvidersFromCookies = () => { + const savedProviders = Cookies.get('providers'); + + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + setEnabledProviders(PROVIDER_LIST.filter((p) => parsedProviders[p.name])); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + } + } + }; + + updateProvidersFromCookies(); + + const interval = setInterval(updateProvidersFromCookies, 1000); + + return () => clearInterval(interval); + }, [PROVIDER_LIST]); + + useEffect(() => { + console.log(transcript); + }, [transcript]); useEffect(() => { // Load API keys from cookies on component mount try { @@ -183,23 +238,6 @@ export const BaseChat = React.forwardRef( } }; - const updateApiKey = (provider: string, key: string) => { - try { - const updatedApiKeys = { ...apiKeys, [provider]: key }; - setApiKeys(updatedApiKeys); - - // Save updated API keys to cookies with 30 day expiry and secure settings - Cookies.set('apiKeys', JSON.stringify(updatedApiKeys), { - expires: 30, // 30 days - secure: true, // Only send over HTTPS - sameSite: 'strict', // Protect against CSRF - path: '/', // Accessible across the site - }); - } catch (error) { - console.error('Error saving API keys to cookies:', error); - } - }; - const handleFileUpload = () => { const input = document.createElement('input'); input.type = 'file'; @@ -255,19 +293,9 @@ export const BaseChat = React.forwardRef( const baseChat = (
-
-
-
-
-
-
-
{() => }
@@ -317,15 +345,15 @@ export const BaseChat = React.forwardRef( gradientUnits="userSpaceOnUse" gradientTransform="rotate(-45)" > - - - - + + + + - - + + @@ -333,21 +361,6 @@ export const BaseChat = React.forwardRef(
-
- -
-
( providerList={PROVIDER_LIST} apiKeys={apiKeys} /> - {provider && ( + {enabledProviders.length > 0 && provider && ( updateApiKey(provider.name, key)} + setApiKey={(key) => { + const newApiKeys = { ...apiKeys, [provider.name]: key }; + setApiKeys(newApiKeys); + Cookies.set('apiKeys', JSON.stringify(newApiKeys)); + }} /> )}
@@ -451,6 +468,7 @@ export const BaseChat = React.forwardRef( 0 || isStreaming || uploadedFiles.length > 0} isStreaming={isStreaming} + disabled={enabledProviders.length === 0} onClick={(event) => { if (isStreaming) { handleStop?.(); @@ -501,6 +519,20 @@ export const BaseChat = React.forwardRef( disabled={isStreaming} /> {chatStarted && {() => }} + setIsModelSettingsCollapsed(!isModelSettingsCollapsed)} + disabled={enabledProviders.length === 0} + > +
+ {isModelSettingsCollapsed ? {model} : } +
{input.length > 3 ? (
@@ -513,7 +545,12 @@ export const BaseChat = React.forwardRef(
- {!chatStarted && ImportButtons(importChat)} + {!chatStarted && ( +
+ {ImportButtons(importChat)} + +
+ )} {!chatStarted && ExamplePrompts((event, messageInput) => { if (isStreaming) { diff --git a/app/components/chat/GitCloneButton.tsx b/app/components/chat/GitCloneButton.tsx new file mode 100644 index 00000000..7b7c9f7f --- /dev/null +++ b/app/components/chat/GitCloneButton.tsx @@ -0,0 +1,115 @@ +import ignore from 'ignore'; +import { useGit } from '~/lib/hooks/useGit'; +import type { Message } from 'ai'; +import WithTooltip from '~/components/ui/Tooltip'; +import { detectProjectCommands, createCommandsMessage } from '~/utils/projectCommands'; +import { generateId } from '~/utils/fileUtils'; + +const IGNORE_PATTERNS = [ + 'node_modules/**', + '.git/**', + '.github/**', + '.vscode/**', + '**/*.jpg', + '**/*.jpeg', + '**/*.png', + 'dist/**', + 'build/**', + '.next/**', + 'coverage/**', + '.cache/**', + '.vscode/**', + '.idea/**', + '**/*.log', + '**/.DS_Store', + '**/npm-debug.log*', + '**/yarn-debug.log*', + '**/yarn-error.log*', + '**/*lock.json', + '**/*lock.yaml', +]; + +const ig = ignore().add(IGNORE_PATTERNS); + +interface GitCloneButtonProps { + className?: string; + importChat?: (description: string, messages: Message[]) => Promise; +} + +export default function GitCloneButton({ importChat }: GitCloneButtonProps) { + const { ready, gitClone } = useGit(); + const onClick = async (_e: any) => { + if (!ready) { + return; + } + + const repoUrl = prompt('Enter the Git url'); + + if (repoUrl) { + const { workdir, data } = await gitClone(repoUrl); + + if (importChat) { + const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath)); + console.log(filePaths); + + const textDecoder = new TextDecoder('utf-8'); + + // Convert files to common format for command detection + const fileContents = filePaths + .map((filePath) => { + const { data: content, encoding } = data[filePath]; + return { + path: filePath, + content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '', + }; + }) + .filter((f) => f.content); + + // Detect and create commands message + const commands = await detectProjectCommands(fileContents); + const commandsMessage = createCommandsMessage(commands); + + // Create files message + const filesMessage: Message = { + role: 'assistant', + content: `Cloning the repo ${repoUrl} into ${workdir} + +${fileContents + .map( + (file) => + ` +${file.content} +`, + ) + .join('\n')} +`, + id: generateId(), + createdAt: new Date(), + }; + + const messages = [filesMessage]; + + if (commandsMessage) { + messages.push(commandsMessage); + } + + await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages); + } + } + }; + + return ( + + + + ); +} diff --git a/app/components/chat/ImportFolderButton.tsx b/app/components/chat/ImportFolderButton.tsx index 3da78c1c..6cbfcacf 100644 --- a/app/components/chat/ImportFolderButton.tsx +++ b/app/components/chat/ImportFolderButton.tsx @@ -1,102 +1,75 @@ -import React from 'react'; +import React, { useState } from 'react'; import type { Message } from 'ai'; import { toast } from 'react-toastify'; -import ignore from 'ignore'; +import { MAX_FILES, isBinaryFile, shouldIncludeFile } from '~/utils/fileUtils'; +import { createChatFromFolder } from '~/utils/folderImport'; interface ImportFolderButtonProps { className?: string; importChat?: (description: string, messages: Message[]) => Promise; } -// Common patterns to ignore, similar to .gitignore -const IGNORE_PATTERNS = [ - 'node_modules/**', - '.git/**', - 'dist/**', - 'build/**', - '.next/**', - 'coverage/**', - '.cache/**', - '.vscode/**', - '.idea/**', - '**/*.log', - '**/.DS_Store', - '**/npm-debug.log*', - '**/yarn-debug.log*', - '**/yarn-error.log*', -]; - -const ig = ignore().add(IGNORE_PATTERNS); -const generateId = () => Math.random().toString(36).substring(2, 15); - -const isBinaryFile = async (file: File): Promise => { - const chunkSize = 1024; // Read the first 1 KB of the file - const buffer = new Uint8Array(await file.slice(0, chunkSize).arrayBuffer()); - - for (let i = 0; i < buffer.length; i++) { - const byte = buffer[i]; - - if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) { - return true; // Found a binary character - } - } - - return false; -}; - export const ImportFolderButton: React.FC = ({ className, importChat }) => { - const shouldIncludeFile = (path: string): boolean => { - return !ig.ignores(path); - }; + const [isLoading, setIsLoading] = useState(false); - const createChatFromFolder = async (files: File[], binaryFiles: string[]) => { - const fileArtifacts = await Promise.all( - files.map(async (file) => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); + const handleFileChange = async (e: React.ChangeEvent) => { + const allFiles = Array.from(e.target.files || []); - reader.onload = () => { - const content = reader.result as string; - const relativePath = file.webkitRelativePath.split('/').slice(1).join('/'); - resolve( - ` -${content} -`, - ); - }; - reader.onerror = reject; - reader.readAsText(file); - }); - }), - ); + if (allFiles.length > MAX_FILES) { + toast.error( + `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.`, + ); + return; + } - const binaryFilesMessage = - binaryFiles.length > 0 - ? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}` - : ''; + const folderName = allFiles[0]?.webkitRelativePath.split('/')[0] || 'Unknown Folder'; + setIsLoading(true); - const message: Message = { - role: 'assistant', - content: `I'll help you set up these files.${binaryFilesMessage} + const loadingToast = toast.loading(`Importing ${folderName}...`); - -${fileArtifacts.join('\n\n')} -`, - id: generateId(), - createdAt: new Date(), - }; + try { + const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath)); - const userMessage: Message = { - role: 'user', - id: generateId(), - content: 'Import my files', - createdAt: new Date(), - }; + if (filteredFiles.length === 0) { + toast.error('No files found in the selected folder'); + return; + } - const description = `Folder Import: ${files[0].webkitRelativePath.split('/')[0]}`; + const fileChecks = await Promise.all( + filteredFiles.map(async (file) => ({ + file, + isBinary: await isBinaryFile(file), + })), + ); - if (importChat) { - await importChat(description, [userMessage, message]); + const textFiles = fileChecks.filter((f) => !f.isBinary).map((f) => f.file); + const binaryFilePaths = fileChecks + .filter((f) => f.isBinary) + .map((f) => f.file.webkitRelativePath.split('/').slice(1).join('/')); + + if (textFiles.length === 0) { + toast.error('No text files found in the selected folder'); + return; + } + + if (binaryFilePaths.length > 0) { + toast.info(`Skipping ${binaryFilePaths.length} binary files`); + } + + const messages = await createChatFromFolder(textFiles, binaryFilePaths, folderName); + + if (importChat) { + await importChat(folderName, [...messages]); + } + + toast.success('Folder imported successfully'); + } catch (error) { + console.error('Failed to import folder:', error); + toast.error('Failed to import folder'); + } finally { + setIsLoading(false); + toast.dismiss(loadingToast); + e.target.value = ''; // Reset file input } }; @@ -108,46 +81,8 @@ ${fileArtifacts.join('\n\n')} className="hidden" webkitdirectory="" directory="" - onChange={async (e) => { - const allFiles = Array.from(e.target.files || []); - const filteredFiles = allFiles.filter((file) => shouldIncludeFile(file.webkitRelativePath)); - - if (filteredFiles.length === 0) { - toast.error('No files found in the selected folder'); - return; - } - - try { - const fileChecks = await Promise.all( - filteredFiles.map(async (file) => ({ - file, - isBinary: await isBinaryFile(file), - })), - ); - - const textFiles = fileChecks.filter((f) => !f.isBinary).map((f) => f.file); - const binaryFilePaths = fileChecks - .filter((f) => f.isBinary) - .map((f) => f.file.webkitRelativePath.split('/').slice(1).join('/')); - - if (textFiles.length === 0) { - toast.error('No text files found in the selected folder'); - return; - } - - if (binaryFilePaths.length > 0) { - toast.info(`Skipping ${binaryFilePaths.length} binary files`); - } - - await createChatFromFolder(textFiles, binaryFilePaths); - } catch (error) { - console.error('Failed to import folder:', error); - toast.error('Failed to import folder'); - } - - e.target.value = ''; // Reset file input - }} - {...({} as any)} // if removed webkitdirectory will throw errors as unknow attribute + onChange={handleFileChange} + {...({} as any)} /> ); diff --git a/app/components/chat/ModelSelector.tsx b/app/components/chat/ModelSelector.tsx index 1bc7a669..bd41eb4d 100644 --- a/app/components/chat/ModelSelector.tsx +++ b/app/components/chat/ModelSelector.tsx @@ -1,5 +1,7 @@ import type { ProviderInfo } from '~/types/model'; import type { ModelInfo } from '~/utils/types'; +import { useEffect, useState } from 'react'; +import Cookies from 'js-cookie'; interface ModelSelectorProps { model?: string; @@ -19,12 +21,79 @@ export const ModelSelector = ({ modelList, providerList, }: ModelSelectorProps) => { + // Load enabled providers from cookies + const [enabledProviders, setEnabledProviders] = useState(() => { + const savedProviders = Cookies.get('providers'); + + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + return providerList.filter((p) => parsedProviders[p.name]); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + return providerList; + } + } + + return providerList; + }); + + // Update enabled providers when cookies change + useEffect(() => { + // Function to update providers from cookies + const updateProvidersFromCookies = () => { + const savedProviders = Cookies.get('providers'); + + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + const newEnabledProviders = providerList.filter((p) => parsedProviders[p.name]); + setEnabledProviders(newEnabledProviders); + + // If current provider is disabled, switch to first enabled provider + if (provider && !parsedProviders[provider.name] && newEnabledProviders.length > 0) { + const firstEnabledProvider = newEnabledProviders[0]; + setProvider?.(firstEnabledProvider); + + // Also update the model to the first available one for the new provider + const firstModel = modelList.find((m) => m.provider === firstEnabledProvider.name); + + if (firstModel) { + setModel?.(firstModel.name); + } + } + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + } + } + }; + + // Initial update + updateProvidersFromCookies(); + + // Set up an interval to check for cookie changes + const interval = setInterval(updateProvidersFromCookies, 1000); + + return () => clearInterval(interval); + }, [providerList, provider, setProvider, modelList, setModel]); + + if (enabledProviders.length === 0) { + return ( +
+

+ No providers are currently enabled. Please enable at least one provider in the settings to start using the + chat. +

+
+ ); + } + return (
module.vue()); + }, + }), LanguageDescription.of({ name: 'TS', extensions: ['ts'], diff --git a/app/components/git/GitUrlImport.client.tsx b/app/components/git/GitUrlImport.client.tsx new file mode 100644 index 00000000..cbdeaa5c --- /dev/null +++ b/app/components/git/GitUrlImport.client.tsx @@ -0,0 +1,117 @@ +import { useSearchParams } from '@remix-run/react'; +import { generateId, type Message } from 'ai'; +import ignore from 'ignore'; +import { useEffect, useState } from 'react'; +import { ClientOnly } from 'remix-utils/client-only'; +import { BaseChat } from '~/components/chat/BaseChat'; +import { Chat } from '~/components/chat/Chat.client'; +import { useGit } from '~/lib/hooks/useGit'; +import { useChatHistory } from '~/lib/persistence'; +import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands'; + +const IGNORE_PATTERNS = [ + 'node_modules/**', + '.git/**', + '.github/**', + '.vscode/**', + '**/*.jpg', + '**/*.jpeg', + '**/*.png', + 'dist/**', + 'build/**', + '.next/**', + 'coverage/**', + '.cache/**', + '.vscode/**', + '.idea/**', + '**/*.log', + '**/.DS_Store', + '**/npm-debug.log*', + '**/yarn-debug.log*', + '**/yarn-error.log*', + '**/*lock.json', + '**/*lock.yaml', +]; + +export function GitUrlImport() { + const [searchParams] = useSearchParams(); + const { ready: historyReady, importChat } = useChatHistory(); + const { ready: gitReady, gitClone } = useGit(); + const [imported, setImported] = useState(false); + + const importRepo = async (repoUrl?: string) => { + if (!gitReady && !historyReady) { + return; + } + + if (repoUrl) { + const ig = ignore().add(IGNORE_PATTERNS); + const { workdir, data } = await gitClone(repoUrl); + + if (importChat) { + const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath)); + + const textDecoder = new TextDecoder('utf-8'); + + // Convert files to common format for command detection + const fileContents = filePaths + .map((filePath) => { + const { data: content, encoding } = data[filePath]; + return { + path: filePath, + content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '', + }; + }) + .filter((f) => f.content); + + // Detect and create commands message + const commands = await detectProjectCommands(fileContents); + const commandsMessage = createCommandsMessage(commands); + + // Create files message + const filesMessage: Message = { + role: 'assistant', + content: `Cloning the repo ${repoUrl} into ${workdir} + +${fileContents + .map( + (file) => + ` +${file.content} +`, + ) + .join('\n')} +`, + id: generateId(), + createdAt: new Date(), + }; + + const messages = [filesMessage]; + + if (commandsMessage) { + messages.push(commandsMessage); + } + + await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages); + } + } + }; + + useEffect(() => { + if (!historyReady || !gitReady || imported) { + return; + } + + const url = searchParams.get('url'); + + if (!url) { + window.location.href = '/'; + return; + } + + importRepo(url); + setImported(true); + }, [searchParams, historyReady, gitReady, imported]); + + return }>{() => }; +} diff --git a/app/components/header/Header.tsx b/app/components/header/Header.tsx index 8b2e81f7..ce46702a 100644 --- a/app/components/header/Header.tsx +++ b/app/components/header/Header.tsx @@ -10,18 +10,17 @@ export function Header() { return (
{chat.started && ( // Display ChatDescription and HeaderActionButtons only when the chat has started. diff --git a/app/components/settings/Settings.module.scss b/app/components/settings/Settings.module.scss new file mode 100644 index 00000000..6da82882 --- /dev/null +++ b/app/components/settings/Settings.module.scss @@ -0,0 +1,63 @@ +.settings-tabs { + button { + width: 100%; + display: flex; + align-items: center; + gap: 0.5rem; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + text-align: left; + font-size: 0.875rem; + transition: all 0.2s; + margin-bottom: 0.5rem; + + &.active { + background: var(--bolt-elements-button-primary-background); + color: var(--bolt-elements-textPrimary); + } + + &:not(.active) { + background: var(--bolt-elements-bg-depth-3); + color: var(--bolt-elements-textPrimary); + + &:hover { + background: var(--bolt-elements-button-primary-backgroundHover); + } + } + } +} + +.settings-button { + background-color: var(--bolt-elements-button-primary-background); + color: var(--bolt-elements-textPrimary); + border-radius: 0.5rem; + padding: 0.5rem 1rem; + transition: background-color 0.2s; + + &:hover { + background-color: var(--bolt-elements-button-primary-backgroundHover); + } +} + +.settings-danger-area { + background-color: transparent; + color: var(--bolt-elements-textPrimary); + border-radius: 0.5rem; + padding: 1rem; + margin-bottom: 1rem; + border-style: solid; + border-color: var(--bolt-elements-button-danger-backgroundHover) ; + border-width: thin; + + button { + background-color: var(--bolt-elements-button-danger-background); + color: var(--bolt-elements-button-danger-text); + border-radius: 0.5rem; + padding: 0.5rem 1rem; + transition: background-color 0.2s; + + &:hover { + background-color: var(--bolt-elements-button-danger-backgroundHover); + } + } +} \ No newline at end of file diff --git a/app/components/settings/SettingsWindow.tsx b/app/components/settings/SettingsWindow.tsx new file mode 100644 index 00000000..1c727115 --- /dev/null +++ b/app/components/settings/SettingsWindow.tsx @@ -0,0 +1,483 @@ +import * as RadixDialog from '@radix-ui/react-dialog'; +import { motion } from 'framer-motion'; +import { useState } from 'react'; +import { classNames } from '~/utils/classNames'; +import { DialogTitle, dialogVariants, dialogBackdropVariants } from '~/components/ui/Dialog'; +import { IconButton } from '~/components/ui/IconButton'; +import { providersList } from '~/lib/stores/settings'; +import { db, getAll, deleteById } from '~/lib/persistence'; +import { toast } from 'react-toastify'; +import { useNavigate } from '@remix-run/react'; +import commit from '~/commit.json'; +import Cookies from 'js-cookie'; +import styles from './Settings.module.scss'; +import { Switch } from '~/components/ui/Switch'; + +interface SettingsProps { + open: boolean; + onClose: () => void; +} + +type TabType = 'chat-history' | 'providers' | 'features' | 'debug' | 'connection'; + +// Providers that support base URL configuration +const URL_CONFIGURABLE_PROVIDERS = ['Ollama', 'LMStudio', 'OpenAILike']; + +export const SettingsWindow = ({ open, onClose }: SettingsProps) => { + const navigate = useNavigate(); + const [activeTab, setActiveTab] = useState('chat-history'); + const [isDebugEnabled, setIsDebugEnabled] = useState(() => { + const savedDebugState = Cookies.get('isDebugEnabled'); + return savedDebugState === 'true'; + }); + const [searchTerm, setSearchTerm] = useState(''); + const [isDeleting, setIsDeleting] = useState(false); + const [githubUsername, setGithubUsername] = useState(Cookies.get('githubUsername') || ''); + const [githubToken, setGithubToken] = useState(Cookies.get('githubToken') || ''); + const [isLocalModelsEnabled, setIsLocalModelsEnabled] = useState(() => { + const savedLocalModelsState = Cookies.get('isLocalModelsEnabled'); + return savedLocalModelsState === 'true'; + }); + + // Load base URLs from cookies + const [baseUrls, setBaseUrls] = useState(() => { + const savedUrls = Cookies.get('providerBaseUrls'); + + if (savedUrls) { + try { + return JSON.parse(savedUrls); + } catch (error) { + console.error('Failed to parse base URLs from cookies:', error); + return { + Ollama: 'http://localhost:11434', + LMStudio: 'http://localhost:1234', + OpenAILike: '', + }; + } + } + + return { + Ollama: 'http://localhost:11434', + LMStudio: 'http://localhost:1234', + OpenAILike: '', + }; + }); + + const handleBaseUrlChange = (provider: string, url: string) => { + setBaseUrls((prev: Record) => { + const newUrls = { ...prev, [provider]: url }; + Cookies.set('providerBaseUrls', JSON.stringify(newUrls)); + + return newUrls; + }); + }; + + const tabs: { id: TabType; label: string; icon: string }[] = [ + { id: 'chat-history', label: 'Chat History', icon: 'i-ph:book' }, + { id: 'providers', label: 'Providers', icon: 'i-ph:key' }, + { id: 'features', label: 'Features', icon: 'i-ph:star' }, + { id: 'connection', label: 'Connection', icon: 'i-ph:link' }, + ...(isDebugEnabled ? [{ id: 'debug' as TabType, label: 'Debug Tab', icon: 'i-ph:bug' }] : []), + ]; + + // Load providers from cookies on mount + const [providers, setProviders] = useState(() => { + const savedProviders = Cookies.get('providers'); + + if (savedProviders) { + try { + const parsedProviders = JSON.parse(savedProviders); + + // Merge saved enabled states with the base provider list + return providersList.map((provider) => ({ + ...provider, + isEnabled: parsedProviders[provider.name] || false, + })); + } catch (error) { + console.error('Failed to parse providers from cookies:', error); + } + } + + return providersList; + }); + + const handleToggleProvider = (providerName: string, enabled: boolean) => { + setProviders((prevProviders) => { + const newProviders = prevProviders.map((provider) => + provider.name === providerName ? { ...provider, isEnabled: enabled } : provider, + ); + + // Save to cookies + const enabledStates = newProviders.reduce( + (acc, provider) => ({ + ...acc, + [provider.name]: provider.isEnabled, + }), + {}, + ); + Cookies.set('providers', JSON.stringify(enabledStates)); + + return newProviders; + }); + }; + + const filteredProviders = providers + .filter((provider) => { + const isLocalModelProvider = ['OpenAILike', 'LMStudio', 'Ollama'].includes(provider.name); + return isLocalModelsEnabled || !isLocalModelProvider; + }) + .filter((provider) => provider.name.toLowerCase().includes(searchTerm.toLowerCase())) + .sort((a, b) => a.name.localeCompare(b.name)); + + const handleCopyToClipboard = () => { + const debugInfo = { + OS: navigator.platform, + Browser: navigator.userAgent, + ActiveFeatures: providers.filter((provider) => provider.isEnabled).map((provider) => provider.name), + BaseURLs: { + Ollama: process.env.REACT_APP_OLLAMA_URL, + OpenAI: process.env.REACT_APP_OPENAI_URL, + LMStudio: process.env.REACT_APP_LM_STUDIO_URL, + }, + Version: versionHash, + }; + navigator.clipboard.writeText(JSON.stringify(debugInfo, null, 2)).then(() => { + alert('Debug information copied to clipboard!'); + }); + }; + + const downloadAsJson = (data: any, filename: string) => { + const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + }; + + const handleDeleteAllChats = async () => { + if (!db) { + toast.error('Database is not available'); + return; + } + + try { + setIsDeleting(true); + + const allChats = await getAll(db); + + // Delete all chats one by one + await Promise.all(allChats.map((chat) => deleteById(db!, chat.id))); + + toast.success('All chats deleted successfully'); + navigate('/', { replace: true }); + } catch (error) { + toast.error('Failed to delete chats'); + console.error(error); + } finally { + setIsDeleting(false); + } + }; + + const handleExportAllChats = async () => { + if (!db) { + toast.error('Database is not available'); + return; + } + + try { + const allChats = await getAll(db); + const exportData = { + chats: allChats, + exportDate: new Date().toISOString(), + }; + + downloadAsJson(exportData, `all-chats-${new Date().toISOString()}.json`); + toast.success('Chats exported successfully'); + } catch (error) { + toast.error('Failed to export chats'); + console.error(error); + } + }; + + const versionHash = commit.commit; // Get the version hash from commit.json + + const handleSaveConnection = () => { + Cookies.set('githubUsername', githubUsername); + Cookies.set('githubToken', githubToken); + toast.success('GitHub credentials saved successfully!'); + }; + + const handleToggleDebug = (enabled: boolean) => { + setIsDebugEnabled(enabled); + Cookies.set('isDebugEnabled', String(enabled)); + }; + + const handleToggleLocalModels = (enabled: boolean) => { + setIsLocalModelsEnabled(enabled); + Cookies.set('isLocalModelsEnabled', String(enabled)); + }; + + return ( + + + + + + + +
+
+ + Settings + + {tabs.map((tab) => ( + + ))} +
+ + + +
+
+ {activeTab === 'chat-history' && ( +
+

Chat History

+ + +
+

Danger Area

+

This action cannot be undone!

+ +
+
+ )} + {activeTab === 'providers' && ( +
+
+ setSearchTerm(e.target.value)} + 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" + /> +
+ {filteredProviders.map((provider) => ( +
+
+ {provider.name} + handleToggleProvider(provider.name, enabled)} + /> +
+ {/* Base URL input for configurable providers */} + {URL_CONFIGURABLE_PROVIDERS.includes(provider.name) && provider.isEnabled && ( +
+ + handleBaseUrlChange(provider.name, e.target.value)} + placeholder={`Enter ${provider.name} base URL`} + 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" + /> +
+ )} +
+ ))} +
+ )} + {activeTab === 'features' && ( +
+
+

Optional Features

+
+ Debug Info + +
+
+ +
+

+ Experimental Features +

+

+ Disclaimer: Experimental features may be unstable and are subject to change. +

+
+ Enable Local Models + +
+
+
+ )} + {activeTab === 'debug' && isDebugEnabled && ( +
+

Debug Tab

+ + +

System Information

+

OS: {navigator.platform}

+

Browser: {navigator.userAgent}

+ +

Active Features

+
    + {providers + .filter((provider) => provider.isEnabled) + .map((provider) => ( +
  • + {provider.name} +
  • + ))} +
+ +

Base URLs

+
    +
  • Ollama: {process.env.REACT_APP_OLLAMA_URL}
  • +
  • OpenAI: {process.env.REACT_APP_OPENAI_URL}
  • +
  • + LM Studio: {process.env.REACT_APP_LM_STUDIO_URL} +
  • +
+ +

Version Information

+

Version Hash: {versionHash}

+
+ )} + {activeTab === 'connection' && ( +
+

GitHub Connection

+
+
+ + setGithubUsername(e.target.value)} + 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" + /> +
+
+ + setGithubToken(e.target.value)} + 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" + /> +
+
+
+ +
+
+ )} +
+
+
+ + + + + + + + ); +}; diff --git a/app/components/sidebar/Menu.client.tsx b/app/components/sidebar/Menu.client.tsx index b2af5e15..38ffb546 100644 --- a/app/components/sidebar/Menu.client.tsx +++ b/app/components/sidebar/Menu.client.tsx @@ -3,6 +3,8 @@ import { useCallback, useEffect, useRef, useState } from 'react'; import { toast } from 'react-toastify'; import { Dialog, DialogButton, DialogDescription, DialogRoot, DialogTitle } from '~/components/ui/Dialog'; import { ThemeSwitch } from '~/components/ui/ThemeSwitch'; +import { SettingsWindow } from '~/components/settings/SettingsWindow'; +import { SettingsButton } from '~/components/ui/SettingsButton'; import { db, deleteById, getAll, chatId, type ChatHistoryItem, useChatHistory } from '~/lib/persistence'; import { cubicEasingFn } from '~/utils/easings'; import { logger } from '~/utils/logger'; @@ -39,6 +41,7 @@ export const Menu = () => { const [list, setList] = useState([]); const [open, setOpen] = useState(false); const [dialogContent, setDialogContent] = useState(null); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); const { filteredItems: filteredList, handleSearchChange } = useSearchFilter({ items: list, @@ -200,10 +203,12 @@ export const Menu = () => {
-
- +
+ setIsSettingsOpen(true)} /> +
+ setIsSettingsOpen(false)} /> ); }; diff --git a/app/components/ui/BackgroundRays/index.tsx b/app/components/ui/BackgroundRays/index.tsx new file mode 100644 index 00000000..ac4ed86d --- /dev/null +++ b/app/components/ui/BackgroundRays/index.tsx @@ -0,0 +1,18 @@ +import styles from './styles.module.scss'; + +const BackgroundRays = () => { + return ( +
+
+
+
+
+
+
+
+
+
+ ); +}; + +export default BackgroundRays; diff --git a/app/components/ui/BackgroundRays/styles.module.scss b/app/components/ui/BackgroundRays/styles.module.scss new file mode 100644 index 00000000..bac4c8aa --- /dev/null +++ b/app/components/ui/BackgroundRays/styles.module.scss @@ -0,0 +1,246 @@ +.rayContainer { + // Theme-specific colors + --ray-color-primary: color-mix(in srgb, var(--primary-color), transparent 30%); + --ray-color-secondary: color-mix(in srgb, var(--secondary-color), transparent 30%); + --ray-color-accent: color-mix(in srgb, var(--accent-color), transparent 30%); + + // Theme-specific gradients + --ray-gradient-primary: radial-gradient(var(--ray-color-primary) 0%, transparent 70%); + --ray-gradient-secondary: radial-gradient(var(--ray-color-secondary) 0%, transparent 70%); + --ray-gradient-accent: radial-gradient(var(--ray-color-accent) 0%, transparent 70%); + + position: fixed; + inset: 0; + overflow: hidden; + animation: fadeIn 1.5s ease-out; + pointer-events: none; + z-index: 0; + // background-color: transparent; + + :global(html[data-theme='dark']) & { + mix-blend-mode: screen; + } + + :global(html[data-theme='light']) & { + mix-blend-mode: multiply; + } +} + +.lightRay { + position: absolute; + border-radius: 100%; + + :global(html[data-theme='dark']) & { + mix-blend-mode: screen; + } + + :global(html[data-theme='light']) & { + mix-blend-mode: multiply; + opacity: 0.4; + } +} + +.ray1 { + width: 600px; + height: 800px; + background: var(--ray-gradient-primary); + transform: rotate(65deg); + top: -500px; + left: -100px; + filter: blur(80px); + opacity: 0.6; + animation: float1 15s infinite ease-in-out; +} + +.ray2 { + width: 400px; + height: 600px; + background: var(--ray-gradient-secondary); + transform: rotate(-30deg); + top: -300px; + left: 200px; + filter: blur(60px); + opacity: 0.6; + animation: float2 18s infinite ease-in-out; +} + +.ray3 { + width: 500px; + height: 400px; + background: var(--ray-gradient-accent); + top: -320px; + left: 500px; + filter: blur(65px); + opacity: 0.5; + animation: float3 20s infinite ease-in-out; +} + +.ray4 { + width: 400px; + height: 450px; + background: var(--ray-gradient-secondary); + top: -350px; + left: 800px; + filter: blur(55px); + opacity: 0.55; + animation: float4 17s infinite ease-in-out; +} + +.ray5 { + width: 350px; + height: 500px; + background: var(--ray-gradient-primary); + transform: rotate(-45deg); + top: -250px; + left: 1000px; + filter: blur(45px); + opacity: 0.6; + animation: float5 16s infinite ease-in-out; +} + +.ray6 { + width: 300px; + height: 700px; + background: var(--ray-gradient-accent); + transform: rotate(75deg); + top: -400px; + left: 600px; + filter: blur(75px); + opacity: 0.45; + animation: float6 19s infinite ease-in-out; +} + +.ray7 { + width: 450px; + height: 600px; + background: var(--ray-gradient-primary); + transform: rotate(45deg); + top: -450px; + left: 350px; + filter: blur(65px); + opacity: 0.55; + animation: float7 21s infinite ease-in-out; +} + +.ray8 { + width: 380px; + height: 550px; + background: var(--ray-gradient-secondary); + transform: rotate(-60deg); + top: -380px; + left: 750px; + filter: blur(58px); + opacity: 0.6; + animation: float8 14s infinite ease-in-out; +} + +@keyframes float1 { + 0%, + 100% { + transform: rotate(65deg) translate(0, 0); + } + 25% { + transform: rotate(70deg) translate(30px, 20px); + } + 50% { + transform: rotate(60deg) translate(-20px, 40px); + } + 75% { + transform: rotate(68deg) translate(-40px, 10px); + } +} + +@keyframes float2 { + 0%, + 100% { + transform: rotate(-30deg) scale(1); + } + 33% { + transform: rotate(-25deg) scale(1.1); + } + 66% { + transform: rotate(-35deg) scale(0.95); + } +} + +@keyframes float3 { + 0%, + 100% { + transform: translate(0, 0) rotate(0deg); + } + 25% { + transform: translate(40px, 20px) rotate(5deg); + } + 75% { + transform: translate(-30px, 40px) rotate(-5deg); + } +} + +@keyframes float4 { + 0%, + 100% { + transform: scale(1) rotate(0deg); + } + 50% { + transform: scale(1.15) rotate(10deg); + } +} + +@keyframes float5 { + 0%, + 100% { + transform: rotate(-45deg) translate(0, 0); + } + 33% { + transform: rotate(-40deg) translate(25px, -20px); + } + 66% { + transform: rotate(-50deg) translate(-25px, 20px); + } +} + +@keyframes float6 { + 0%, + 100% { + transform: rotate(75deg) scale(1); + filter: blur(75px); + } + 50% { + transform: rotate(85deg) scale(1.1); + filter: blur(65px); + } +} + +@keyframes float7 { + 0%, + 100% { + transform: rotate(45deg) translate(0, 0); + opacity: 0.55; + } + 50% { + transform: rotate(40deg) translate(-30px, 30px); + opacity: 0.65; + } +} + +@keyframes float8 { + 0%, + 100% { + transform: rotate(-60deg) scale(1); + } + 25% { + transform: rotate(-55deg) scale(1.05); + } + 75% { + transform: rotate(-65deg) scale(0.95); + } +} + +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} diff --git a/app/components/ui/SettingsButton.tsx b/app/components/ui/SettingsButton.tsx new file mode 100644 index 00000000..dc26ddb6 --- /dev/null +++ b/app/components/ui/SettingsButton.tsx @@ -0,0 +1,17 @@ +import { memo } from 'react'; +import { IconButton } from '~/components/ui/IconButton'; +interface SettingsButtonProps { + onClick: () => void; +} + +export const SettingsButton = memo(({ onClick }: SettingsButtonProps) => { + return ( + + ); +}); diff --git a/app/components/ui/Switch.tsx b/app/components/ui/Switch.tsx new file mode 100644 index 00000000..55d2745b --- /dev/null +++ b/app/components/ui/Switch.tsx @@ -0,0 +1,37 @@ +import { memo } from 'react'; +import * as SwitchPrimitive from '@radix-ui/react-switch'; +import { classNames } from '~/utils/classNames'; + +interface SwitchProps { + className?: string; + checked?: boolean; + onCheckedChange?: (event: boolean) => void; +} + +export const Switch = memo(({ className, onCheckedChange, checked }: SwitchProps) => { + return ( + onCheckedChange?.(e)} + > + + + ); +}); diff --git a/app/components/workbench/Workbench.client.tsx b/app/components/workbench/Workbench.client.tsx index fb2f49e9..0e34b599 100644 --- a/app/components/workbench/Workbench.client.tsx +++ b/app/components/workbench/Workbench.client.tsx @@ -17,6 +17,7 @@ import { renderLogger } from '~/utils/logger'; import { EditorPanel } from './EditorPanel'; import { Preview } from './Preview'; import useViewport from '~/lib/hooks'; +import Cookies from 'js-cookie'; interface WorkspaceProps { chatStarted?: boolean; @@ -180,21 +181,22 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) => return; } - const githubUsername = prompt('Please enter your GitHub username:'); + const githubUsername = Cookies.get('githubUsername'); + const githubToken = Cookies.get('githubToken'); - if (!githubUsername) { - alert('GitHub username is required. Push to GitHub cancelled.'); - return; + if (!githubUsername || !githubToken) { + const usernameInput = prompt('Please enter your GitHub username:'); + const tokenInput = prompt('Please enter your GitHub personal access token:'); + + if (!usernameInput || !tokenInput) { + alert('GitHub username and token are required. Push to GitHub cancelled.'); + return; + } + + workbenchStore.pushToGitHub(repoName, usernameInput, tokenInput); + } else { + workbenchStore.pushToGitHub(repoName, githubUsername, githubToken); } - - const githubToken = prompt('Please enter your GitHub personal access token:'); - - if (!githubToken) { - alert('GitHub token is required. Push to GitHub cancelled.'); - return; - } - - workbenchStore.pushToGitHub(repoName, githubUsername, githubToken); }} >
diff --git a/app/lib/hooks/useGit.ts b/app/lib/hooks/useGit.ts new file mode 100644 index 00000000..3c8c61bb --- /dev/null +++ b/app/lib/hooks/useGit.ts @@ -0,0 +1,287 @@ +import type { WebContainer } from '@webcontainer/api'; +import { useCallback, useEffect, useRef, useState, type MutableRefObject } from 'react'; +import { webcontainer as webcontainerPromise } from '~/lib/webcontainer'; +import git, { type GitAuth, type PromiseFsClient } from 'isomorphic-git'; +import http from 'isomorphic-git/http/web'; +import Cookies from 'js-cookie'; +import { toast } from 'react-toastify'; + +const lookupSavedPassword = (url: string) => { + const domain = url.split('/')[2]; + const gitCreds = Cookies.get(`git:${domain}`); + + if (!gitCreds) { + return null; + } + + try { + const { username, password } = JSON.parse(gitCreds || '{}'); + return { username, password }; + } catch (error) { + console.log(`Failed to parse Git Cookie ${error}`); + return null; + } +}; + +const saveGitAuth = (url: string, auth: GitAuth) => { + const domain = url.split('/')[2]; + Cookies.set(`git:${domain}`, JSON.stringify(auth)); +}; + +export function useGit() { + const [ready, setReady] = useState(false); + const [webcontainer, setWebcontainer] = useState(); + const [fs, setFs] = useState(); + const fileData = useRef>({}); + useEffect(() => { + webcontainerPromise.then((container) => { + fileData.current = {}; + setWebcontainer(container); + setFs(getFs(container, fileData)); + setReady(true); + }); + }, []); + + const gitClone = useCallback( + async (url: string) => { + if (!webcontainer || !fs || !ready) { + throw 'Webcontainer not initialized'; + } + + fileData.current = {}; + await git.clone({ + fs, + http, + dir: webcontainer.workdir, + url, + depth: 1, + singleBranch: true, + corsProxy: 'https://cors.isomorphic-git.org', + onAuth: (url) => { + // let domain=url.split("/")[2] + + let auth = lookupSavedPassword(url); + + if (auth) { + return auth; + } + + if (confirm('This repo is password protected. Ready to enter a username & password?')) { + auth = { + username: prompt('Enter username'), + password: prompt('Enter password'), + }; + return auth; + } else { + return { cancel: true }; + } + }, + onAuthFailure: (url, _auth) => { + toast.error(`Error Authenticating with ${url.split('/')[2]}`); + }, + onAuthSuccess: (url, auth) => { + saveGitAuth(url, auth); + }, + }); + + const data: Record = {}; + + for (const [key, value] of Object.entries(fileData.current)) { + data[key] = value; + } + + return { workdir: webcontainer.workdir, data }; + }, + [webcontainer], + ); + + return { ready, gitClone }; +} + +const getFs = ( + webcontainer: WebContainer, + record: MutableRefObject>, +) => ({ + promises: { + readFile: async (path: string, options: any) => { + const encoding = options.encoding; + const relativePath = pathUtils.relative(webcontainer.workdir, path); + console.log('readFile', relativePath, encoding); + + return await webcontainer.fs.readFile(relativePath, encoding); + }, + writeFile: async (path: string, data: any, options: any) => { + const encoding = options.encoding; + const relativePath = pathUtils.relative(webcontainer.workdir, path); + console.log('writeFile', { relativePath, data, encoding }); + + if (record.current) { + record.current[relativePath] = { data, encoding }; + } + + return await webcontainer.fs.writeFile(relativePath, data, { ...options, encoding }); + }, + mkdir: async (path: string, options: any) => { + const relativePath = pathUtils.relative(webcontainer.workdir, path); + console.log('mkdir', relativePath, options); + + return await webcontainer.fs.mkdir(relativePath, { ...options, recursive: true }); + }, + readdir: async (path: string, options: any) => { + const relativePath = pathUtils.relative(webcontainer.workdir, path); + console.log('readdir', relativePath, options); + + return await webcontainer.fs.readdir(relativePath, options); + }, + rm: async (path: string, options: any) => { + const relativePath = pathUtils.relative(webcontainer.workdir, path); + console.log('rm', relativePath, options); + + return await webcontainer.fs.rm(relativePath, { ...(options || {}) }); + }, + rmdir: async (path: string, options: any) => { + const relativePath = pathUtils.relative(webcontainer.workdir, path); + console.log('rmdir', relativePath, options); + + return await webcontainer.fs.rm(relativePath, { recursive: true, ...options }); + }, + + // Mock implementations for missing functions + unlink: async (path: string) => { + // unlink is just removing a single file + const relativePath = pathUtils.relative(webcontainer.workdir, path); + return await webcontainer.fs.rm(relativePath, { recursive: false }); + }, + + stat: async (path: string) => { + try { + const relativePath = pathUtils.relative(webcontainer.workdir, path); + const resp = await webcontainer.fs.readdir(pathUtils.dirname(relativePath), { withFileTypes: true }); + const name = pathUtils.basename(relativePath); + const fileInfo = resp.find((x) => x.name == name); + + if (!fileInfo) { + throw new Error(`ENOENT: no such file or directory, stat '${path}'`); + } + + return { + isFile: () => fileInfo.isFile(), + isDirectory: () => fileInfo.isDirectory(), + isSymbolicLink: () => false, + size: 1, + mode: 0o666, // Default permissions + mtimeMs: Date.now(), + uid: 1000, + gid: 1000, + }; + } catch (error: any) { + console.log(error?.message); + + const err = new Error(`ENOENT: no such file or directory, stat '${path}'`) as NodeJS.ErrnoException; + err.code = 'ENOENT'; + err.errno = -2; + err.syscall = 'stat'; + err.path = path; + throw err; + } + }, + + lstat: async (path: string) => { + /* + * For basic usage, lstat can return the same as stat + * since we're not handling symbolic links + */ + return await getFs(webcontainer, record).promises.stat(path); + }, + + readlink: async (path: string) => { + /* + * Since WebContainer doesn't support symlinks, + * we'll throw a "not a symbolic link" error + */ + throw new Error(`EINVAL: invalid argument, readlink '${path}'`); + }, + + symlink: async (target: string, path: string) => { + /* + * Since WebContainer doesn't support symlinks, + * we'll throw a "operation not supported" error + */ + throw new Error(`EPERM: operation not permitted, symlink '${target}' -> '${path}'`); + }, + + chmod: async (_path: string, _mode: number) => { + /* + * WebContainer doesn't support changing permissions, + * but we can pretend it succeeded for compatibility + */ + return await Promise.resolve(); + }, + }, +}); + +const pathUtils = { + dirname: (path: string) => { + // Handle empty or just filename cases + if (!path || !path.includes('/')) { + return '.'; + } + + // Remove trailing slashes + path = path.replace(/\/+$/, ''); + + // Get directory part + return path.split('/').slice(0, -1).join('/') || '/'; + }, + + basename: (path: string, ext?: string) => { + // Remove trailing slashes + path = path.replace(/\/+$/, ''); + + // Get the last part of the path + const base = path.split('/').pop() || ''; + + // If extension is provided, remove it from the result + if (ext && base.endsWith(ext)) { + return base.slice(0, -ext.length); + } + + return base; + }, + relative: (from: string, to: string): string => { + // Handle empty inputs + if (!from || !to) { + return '.'; + } + + // Normalize paths by removing trailing slashes and splitting + const normalizePathParts = (p: string) => p.replace(/\/+$/, '').split('/').filter(Boolean); + + const fromParts = normalizePathParts(from); + const toParts = normalizePathParts(to); + + // Find common parts at the start of both paths + let commonLength = 0; + const minLength = Math.min(fromParts.length, toParts.length); + + for (let i = 0; i < minLength; i++) { + if (fromParts[i] !== toParts[i]) { + break; + } + + commonLength++; + } + + // Calculate the number of "../" needed + const upCount = fromParts.length - commonLength; + + // Get the remaining path parts we need to append + const remainingPath = toParts.slice(commonLength); + + // Construct the relative path + const relativeParts = [...Array(upCount).fill('..'), ...remainingPath]; + + // Handle empty result case + return relativeParts.length === 0 ? '.' : relativeParts.join('/'); + }, +}; diff --git a/app/lib/stores/settings.ts b/app/lib/stores/settings.ts index 5e48bfe4..7106cfb6 100644 --- a/app/lib/stores/settings.ts +++ b/app/lib/stores/settings.ts @@ -15,10 +15,33 @@ export interface Shortcuts { toggleTerminal: Shortcut; } +export interface Provider { + name: string; + isEnabled: boolean; +} + export interface Settings { shortcuts: Shortcuts; + providers: Provider[]; } +export const providersList: Provider[] = [ + { name: 'Groq', isEnabled: false }, + { name: 'HuggingFace', isEnabled: false }, + { name: 'OpenAI', isEnabled: false }, + { name: 'Anthropic', isEnabled: false }, + { name: 'OpenRouter', isEnabled: false }, + { name: 'Google', isEnabled: false }, + { name: 'Ollama', isEnabled: false }, + { name: 'OpenAILike', isEnabled: false }, + { name: 'Together', isEnabled: false }, + { name: 'Deepseek', isEnabled: false }, + { name: 'Mistral', isEnabled: false }, + { name: 'Cohere', isEnabled: false }, + { name: 'LMStudio', isEnabled: false }, + { name: 'xAI', isEnabled: false }, +]; + export const shortcutsStore = map({ toggleTerminal: { key: 'j', @@ -29,6 +52,7 @@ export const shortcutsStore = map({ export const settingsStore = map({ shortcuts: shortcutsStore.get(), + providers: providersList, }); shortcutsStore.subscribe((shortcuts) => { diff --git a/app/lib/stores/workbench.ts b/app/lib/stores/workbench.ts index 90c3bd93..068cc826 100644 --- a/app/lib/stores/workbench.ts +++ b/app/lib/stores/workbench.ts @@ -15,6 +15,7 @@ import { Octokit, type RestEndpointMethodTypes } from '@octokit/rest'; import * as nodePath from 'node:path'; import { extractRelativePath } from '~/utils/diff'; import { description } from '~/lib/persistence'; +import Cookies from 'js-cookie'; export interface ArtifactState { id: string; @@ -402,15 +403,14 @@ export class WorkbenchStore { return syncedFiles; } - async pushToGitHub(repoName: string, githubUsername: string, ghToken: string) { + async pushToGitHub(repoName: string, githubUsername?: string, ghToken?: string) { try { - // Get the GitHub auth token from environment variables - const githubToken = ghToken; + // Use cookies if username and token are not provided + const githubToken = ghToken || Cookies.get('githubToken'); + const owner = githubUsername || Cookies.get('githubUsername'); - const owner = githubUsername; - - if (!githubToken) { - throw new Error('GitHub token is not set in environment variables'); + if (!githubToken || !owner) { + throw new Error('GitHub token or username is not set in cookies or provided.'); } // Initialize Octokit with the auth token @@ -507,7 +507,8 @@ export class WorkbenchStore { alert(`Repository created and code pushed: ${repo.html_url}`); } catch (error) { - console.error('Error pushing to GitHub:', error instanceof Error ? error.message : String(error)); + console.error('Error pushing to GitHub:', error); + throw error; // Rethrow the error for further handling } } } diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx index 86d73409..dc1c8358 100644 --- a/app/routes/_index.tsx +++ b/app/routes/_index.tsx @@ -3,6 +3,7 @@ import { ClientOnly } from 'remix-utils/client-only'; import { BaseChat } from '~/components/chat/BaseChat'; import { Chat } from '~/components/chat/Chat.client'; import { Header } from '~/components/header/Header'; +import BackgroundRays from '~/components/ui/BackgroundRays'; export const meta: MetaFunction = () => { return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }]; @@ -12,7 +13,8 @@ export const loader = () => json({}); export default function Index() { return ( -
+
+
}>{() => }
diff --git a/app/routes/git.tsx b/app/routes/git.tsx new file mode 100644 index 00000000..aa1689a4 --- /dev/null +++ b/app/routes/git.tsx @@ -0,0 +1,23 @@ +import type { LoaderFunctionArgs } from '@remix-run/cloudflare'; +import { json, type MetaFunction } from '@remix-run/cloudflare'; +import { ClientOnly } from 'remix-utils/client-only'; +import { BaseChat } from '~/components/chat/BaseChat'; +import { GitUrlImport } from '~/components/git/GitUrlImport.client'; +import { Header } from '~/components/header/Header'; + +export const meta: MetaFunction = () => { + return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }]; +}; + +export async function loader(args: LoaderFunctionArgs) { + return json({ url: args.params.url }); +} + +export default function Index() { + return ( +
+
+ }>{() => } +
+ ); +} diff --git a/app/styles/index.scss b/app/styles/index.scss index 36ebac2c..91a4cf84 100644 --- a/app/styles/index.scss +++ b/app/styles/index.scss @@ -12,3 +12,13 @@ body { height: 100%; width: 100%; } + +:root { + --gradient-opacity: 0.8; + --primary-color: rgba(158, 117, 240, var(--gradient-opacity)); + --secondary-color: rgba(138, 43, 226, var(--gradient-opacity)); + --accent-color: rgba(128, 59, 239, var(--gradient-opacity)); + // --primary-color: rgba(147, 112, 219, var(--gradient-opacity)); + // --secondary-color: rgba(138, 43, 226, var(--gradient-opacity)); + // --accent-color: rgba(180, 170, 220, var(--gradient-opacity)); +} diff --git a/app/types/model.ts b/app/types/model.ts index 29bff2e0..c6c58d75 100644 --- a/app/types/model.ts +++ b/app/types/model.ts @@ -7,4 +7,5 @@ export type ProviderInfo = { getApiKeyLink?: string; labelForGetApiKey?: string; icon?: string; + isEnabled?: boolean; }; diff --git a/app/utils/constants.ts b/app/utils/constants.ts index 26db65a9..0cd08081 100644 --- a/app/utils/constants.ts +++ b/app/utils/constants.ts @@ -1,6 +1,7 @@ import Cookies from 'js-cookie'; import type { ModelInfo, OllamaApiResponse, OllamaModel } from './types'; import type { ProviderInfo } from '~/types/model'; +import { createScopedLogger } from './logger'; export const WORK_DIR_NAME = 'project'; export const WORK_DIR = `/home/${WORK_DIR_NAME}`; @@ -10,6 +11,8 @@ export const PROVIDER_REGEX = /\[Provider: (.*?)\]\n\n/; export const DEFAULT_MODEL = 'claude-3-5-sonnet-latest'; export const PROMPT_COOKIE_KEY = 'cachedPrompt'; +const logger = createScopedLogger('Constants'); + const PROVIDER_LIST: ProviderInfo[] = [ { name: 'Anthropic', @@ -127,7 +130,7 @@ const PROVIDER_LIST: ProviderInfo[] = [ { name: 'gemini-1.5-flash-8b', label: 'Gemini 1.5 Flash-8b', provider: 'Google', maxTokenAllowed: 8192 }, { name: 'gemini-1.5-pro-latest', label: 'Gemini 1.5 Pro', provider: 'Google', maxTokenAllowed: 8192 }, { name: 'gemini-1.5-pro-002', label: 'Gemini 1.5 Pro-002', provider: 'Google', maxTokenAllowed: 8192 }, - { name: 'gemini-exp-1121', label: 'Gemini exp-1121', provider: 'Google', maxTokenAllowed: 8192 }, + { name: 'gemini-exp-1206', label: 'Gemini exp-1206', provider: 'Google', maxTokenAllowed: 8192 }, ], getApiKeyLink: 'https://aistudio.google.com/app/apikey', }, @@ -383,8 +386,8 @@ async function getOllamaModels(): Promise { provider: 'Ollama', maxTokenAllowed: 8000, })); - } catch (e) { - console.error('Error getting Ollama models:', e); + } catch (e: any) { + logger.warn('Failed to get Ollama models: ', e.message || ''); return []; } } @@ -471,8 +474,8 @@ async function getLMStudioModels(): Promise { label: model.id, provider: 'LMStudio', })); - } catch (e) { - console.error('Error getting LMStudio models:', e); + } catch (e: any) { + logger.warn('Failed to get LMStudio models: ', e.message || ''); return []; } } @@ -491,7 +494,7 @@ async function initializeModelList(): Promise { } } } catch (error: any) { - console.warn(`Failed to fetch apikeys from cookies:${error?.message}`); + logger.warn(`Failed to fetch apikeys from cookies: ${error?.message}`); } MODEL_LIST = [ ...( diff --git a/app/utils/fileUtils.ts b/app/utils/fileUtils.ts new file mode 100644 index 00000000..fcf2a017 --- /dev/null +++ b/app/utils/fileUtils.ts @@ -0,0 +1,105 @@ +import ignore from 'ignore'; + +// Common patterns to ignore, similar to .gitignore +export const IGNORE_PATTERNS = [ + 'node_modules/**', + '.git/**', + 'dist/**', + 'build/**', + '.next/**', + 'coverage/**', + '.cache/**', + '.vscode/**', + '.idea/**', + '**/*.log', + '**/.DS_Store', + '**/npm-debug.log*', + '**/yarn-debug.log*', + '**/yarn-error.log*', +]; + +export const MAX_FILES = 1000; +export const ig = ignore().add(IGNORE_PATTERNS); + +export const generateId = () => Math.random().toString(36).substring(2, 15); + +export const isBinaryFile = async (file: File): Promise => { + const chunkSize = 1024; + const buffer = new Uint8Array(await file.slice(0, chunkSize).arrayBuffer()); + + for (let i = 0; i < buffer.length; i++) { + const byte = buffer[i]; + + if (byte === 0 || (byte < 32 && byte !== 9 && byte !== 10 && byte !== 13)) { + return true; + } + } + + return false; +}; + +export const shouldIncludeFile = (path: string): boolean => { + return !ig.ignores(path); +}; + +const readPackageJson = async (files: File[]): Promise<{ scripts?: Record } | null> => { + const packageJsonFile = files.find((f) => f.webkitRelativePath.endsWith('package.json')); + + if (!packageJsonFile) { + return null; + } + + try { + const content = await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result as string); + reader.onerror = reject; + reader.readAsText(packageJsonFile); + }); + + return JSON.parse(content); + } catch (error) { + console.error('Error reading package.json:', error); + return null; + } +}; + +export const detectProjectType = async ( + files: File[], +): Promise<{ type: string; setupCommand: string; followupMessage: string }> => { + const hasFile = (name: string) => files.some((f) => f.webkitRelativePath.endsWith(name)); + + if (hasFile('package.json')) { + const packageJson = await readPackageJson(files); + const scripts = packageJson?.scripts || {}; + + // Check for preferred commands in priority order + const preferredCommands = ['dev', 'start', 'preview']; + const availableCommand = preferredCommands.find((cmd) => scripts[cmd]); + + if (availableCommand) { + return { + type: 'Node.js', + setupCommand: `npm install && npm run ${availableCommand}`, + followupMessage: `Found "${availableCommand}" script in package.json. Running "npm run ${availableCommand}" after installation.`, + }; + } + + return { + type: 'Node.js', + setupCommand: 'npm install', + followupMessage: + 'Would you like me to inspect package.json to determine the available scripts for running this project?', + }; + } + + if (hasFile('index.html')) { + return { + type: 'Static', + setupCommand: 'npx --yes serve', + followupMessage: '', + }; + } + + return { type: '', setupCommand: '', followupMessage: '' }; +}; diff --git a/app/utils/folderImport.ts b/app/utils/folderImport.ts new file mode 100644 index 00000000..759df100 --- /dev/null +++ b/app/utils/folderImport.ts @@ -0,0 +1,68 @@ +import type { Message } from 'ai'; +import { generateId } from './fileUtils'; +import { detectProjectCommands, createCommandsMessage } from './projectCommands'; + +export const createChatFromFolder = async ( + files: File[], + binaryFiles: string[], + folderName: string, +): Promise => { + const fileArtifacts = await Promise.all( + files.map(async (file) => { + return new Promise<{ content: string; path: string }>((resolve, reject) => { + const reader = new FileReader(); + + reader.onload = () => { + const content = reader.result as string; + const relativePath = file.webkitRelativePath.split('/').slice(1).join('/'); + resolve({ + content, + path: relativePath, + }); + }; + reader.onerror = reject; + reader.readAsText(file); + }); + }), + ); + + const commands = await detectProjectCommands(fileArtifacts); + const commandsMessage = createCommandsMessage(commands); + + const binaryFilesMessage = + binaryFiles.length > 0 + ? `\n\nSkipped ${binaryFiles.length} binary files:\n${binaryFiles.map((f) => `- ${f}`).join('\n')}` + : ''; + + const filesMessage: Message = { + role: 'assistant', + content: `I've imported the contents of the "${folderName}" folder.${binaryFilesMessage} + + +${fileArtifacts + .map( + (file) => ` +${file.content} +`, + ) + .join('\n\n')} +`, + id: generateId(), + createdAt: new Date(), + }; + + const userMessage: Message = { + role: 'user', + id: generateId(), + content: `Import the "${folderName}" folder`, + createdAt: new Date(), + }; + + const messages = [userMessage, filesMessage]; + + if (commandsMessage) { + messages.push(commandsMessage); + } + + return messages; +}; diff --git a/app/utils/projectCommands.ts b/app/utils/projectCommands.ts new file mode 100644 index 00000000..050663ae --- /dev/null +++ b/app/utils/projectCommands.ts @@ -0,0 +1,80 @@ +import type { Message } from 'ai'; +import { generateId } from './fileUtils'; + +export interface ProjectCommands { + type: string; + setupCommand: string; + followupMessage: string; +} + +interface FileContent { + content: string; + path: string; +} + +export async function detectProjectCommands(files: FileContent[]): Promise { + const hasFile = (name: string) => files.some((f) => f.path.endsWith(name)); + + if (hasFile('package.json')) { + const packageJsonFile = files.find((f) => f.path.endsWith('package.json')); + + if (!packageJsonFile) { + return { type: '', setupCommand: '', followupMessage: '' }; + } + + try { + const packageJson = JSON.parse(packageJsonFile.content); + const scripts = packageJson?.scripts || {}; + + // Check for preferred commands in priority order + const preferredCommands = ['dev', 'start', 'preview']; + const availableCommand = preferredCommands.find((cmd) => scripts[cmd]); + + if (availableCommand) { + return { + type: 'Node.js', + setupCommand: `npm install && npm run ${availableCommand}`, + followupMessage: `Found "${availableCommand}" script in package.json. Running "npm run ${availableCommand}" after installation.`, + }; + } + + return { + type: 'Node.js', + setupCommand: 'npm install', + followupMessage: + 'Would you like me to inspect package.json to determine the available scripts for running this project?', + }; + } catch (error) { + console.error('Error parsing package.json:', error); + return { type: '', setupCommand: '', followupMessage: '' }; + } + } + + if (hasFile('index.html')) { + return { + type: 'Static', + setupCommand: 'npx --yes serve', + followupMessage: '', + }; + } + + return { type: '', setupCommand: '', followupMessage: '' }; +} + +export function createCommandsMessage(commands: ProjectCommands): Message | null { + if (!commands.setupCommand) { + return null; + } + + return { + role: 'assistant', + content: ` + + +${commands.setupCommand} + +${commands.followupMessage ? `\n\n${commands.followupMessage}` : ''}`, + id: generateId(), + createdAt: new Date(), + }; +} diff --git a/docs/docs/FAQ.md b/docs/docs/FAQ.md index 27711e08..8e57502e 100644 --- a/docs/docs/FAQ.md +++ b/docs/docs/FAQ.md @@ -1,52 +1,81 @@ -# FAQ +# Frequently Asked Questions (FAQ) -### How do I get the best results with oTToDev? +## How do I get the best results with oTToDev? -- **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. +- **Be specific about your stack**: + 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. -- **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. +- **Use the enhance prompt icon**: + Before sending your prompt, click the *enhance* icon to let the AI refine your prompt. You can edit the suggested improvements before submitting. -- **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. +- **Scaffold the basics first, then add features**: + Ensure the foundational structure of your application is in place before introducing advanced functionality. This helps oTToDev establish a solid base to build on. -- **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. +- **Batch simple instructions**: + Combine simple tasks into a single prompt to save time and reduce API credit consumption. For example: + *"Change the color scheme, add mobile responsiveness, and restart the dev server."* -### How do I contribute to oTToDev? +--- -[Please check out our dedicated page for contributing to oTToDev here!](CONTRIBUTING.md) +## How do I contribute to oTToDev? -### Do you plan on merging oTToDev back into the official Bolt.new repo? +Check out our [Contribution Guide](CONTRIBUTING.md) for more details on how to get involved! -More news coming on this coming early next month - stay tuned! +--- -### What are the future plans for oTToDev? +## Do you plan on merging oTToDev back into the official Bolt.new repo? -[Check out our Roadmap here!](https://roadmap.sh/r/ottodev-roadmap-2ovzo) +Stay tuned! We’ll share updates on this early next month. -Lot more updates to this roadmap coming soon! +--- -### Why are there so many open issues/pull requests? +## What are the future plans for oTToDev? -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 -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. -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 -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! +Visit our [Roadmap](https://roadmap.sh/r/ottodev-roadmap-2ovzo) for the latest updates. +New features and improvements are on the way! -### How do local LLMs fair compared to larger models like Claude 3.5 Sonnet for oTToDev/Bolt.new? +--- -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! +## Why are there so many open issues/pull requests? -### I'm getting the error: "There was an error processing this request" +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! -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. +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. -### I'm getting the error: "x-api-key header missing" +--- -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! +## How do local LLMs compare to larger models like Claude 3.5 Sonnet for oTToDev/Bolt.new? -### I'm getting a blank preview when oTToDev runs my app! +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. -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. +--- -### Everything works but the results are bad +## Common Errors and Troubleshooting -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! \ No newline at end of file +### **"There was an error processing this request"** +This generic error message means something went wrong. Check both: +- The terminal (if you started the app with Docker or `pnpm`). +- The developer console in your browser (press `F12` or right-click > *Inspect*, then go to the *Console* tab). + +--- + +### **"x-api-key header missing"** +This error is sometimes resolved by restarting the Docker container. +If that doesn’t work, try switching from Docker to `pnpm` or vice versa. We’re actively investigating this issue. + +--- + +### **Blank preview when running the app** +A blank preview often occurs due to hallucinated bad code or incorrect commands. +To troubleshoot: +- Check the developer console for errors. +- Remember, previews are core functionality, so the app isn’t broken! We’re working on making these errors more transparent. + +--- + +### **"Everything works, but the results are bad"** +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. + +--- + +Got more questions? Feel free to reach out or open an issue in our GitHub repo! \ No newline at end of file diff --git a/docs/docs/index.md b/docs/docs/index.md index 5c12a005..d9c953ed 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -148,31 +148,6 @@ sudo npm install -g pnpm pnpm run dev ``` -## Super Important Note on Running Ollama Models - -Ollama models by default only have 2048 tokens for their context window. Even for large models that can easily handle way more. -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 -to use where you specify a larger context window. Luckily it's super easy to do that. - -All you have to do is: - -- Create a file called "Modelfile" (no file extension) anywhere on your computer -- Put in the two lines: - -``` -FROM [Ollama model ID such as qwen2.5-coder:7b] -PARAMETER num_ctx 32768 -``` - -- Run the command: - -``` -ollama create -f Modelfile [your new model ID, can be whatever you want (example: qwen2.5-coder-extra-ctx:7b)] -``` - -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. -You'll see this new model in the list of Ollama models along with all the others you pulled! - ## Adding New LLMs: 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. diff --git a/package.json b/package.json index 8dd1fbb4..15f4d991 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@codemirror/lang-markdown": "^6.3.1", "@codemirror/lang-python": "^6.1.6", "@codemirror/lang-sass": "^6.0.2", + "@codemirror/lang-vue": "^0.1.3", "@codemirror/lang-wast": "^6.0.2", "@codemirror/language": "^6.10.6", "@codemirror/search": "^6.5.8", @@ -58,6 +59,8 @@ "@openrouter/ai-sdk-provider": "^0.0.5", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-switch": "^1.1.1", "@radix-ui/react-tooltip": "^1.1.4", "@remix-run/cloudflare": "^2.15.0", "@remix-run/cloudflare-pages": "^2.15.0", @@ -75,13 +78,13 @@ "framer-motion": "^11.12.0", "ignore": "^6.0.2", "isbot": "^4.4.0", + "isomorphic-git": "^1.27.2", "istextorbinary": "^9.5.0", "jose": "^5.9.6", "js-cookie": "^3.0.5", "jszip": "^3.10.1", "nanostores": "^0.10.3", "ollama-ai-provider": "^0.15.2", - "pnpm": "^9.14.4", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hotkeys-hook": "^4.6.1", @@ -110,6 +113,7 @@ "husky": "9.1.7", "is-ci": "^3.0.1", "node-fetch": "^3.3.2", + "pnpm": "^9.14.4", "prettier": "^3.4.1", "sass-embedded": "^1.81.0", "typescript": "^5.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1bf0c536..e355d04e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -56,6 +56,9 @@ importers: '@codemirror/lang-sass': specifier: ^6.0.2 version: 6.0.2(@codemirror/view@6.35.0) + '@codemirror/lang-vue': + specifier: ^0.1.3 + version: 0.1.3 '@codemirror/lang-wast': specifier: ^6.0.2 version: 6.0.2 @@ -98,6 +101,12 @@ importers: '@radix-ui/react-dropdown-menu': specifier: ^2.1.2 version: 2.1.2(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-separator': + specifier: ^1.1.0 + version: 1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-switch': + specifier: ^1.1.1 + version: 1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-tooltip': specifier: ^1.1.4 version: 1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -130,7 +139,7 @@ importers: version: 5.5.0 ai: specifier: ^3.4.33 - version: 3.4.33(react@18.3.1)(sswr@2.1.0(svelte@4.2.18))(svelte@4.2.18)(vue@3.4.30(typescript@5.7.2))(zod@3.23.8) + version: 3.4.33(react@18.3.1)(sswr@2.1.0(svelte@5.4.0))(svelte@5.4.0)(vue@3.5.13(typescript@5.7.2))(zod@3.23.8) date-fns: specifier: ^3.6.0 version: 3.6.0 @@ -149,6 +158,9 @@ importers: isbot: specifier: ^4.4.0 version: 4.4.0 + isomorphic-git: + specifier: ^1.27.2 + version: 1.27.2 istextorbinary: specifier: ^9.5.0 version: 9.5.0 @@ -167,9 +179,6 @@ importers: ollama-ai-provider: specifier: ^0.15.2 version: 0.15.2(zod@3.23.8) - pnpm: - specifier: ^9.14.4 - version: 9.14.4 react: specifier: ^18.3.1 version: 18.3.1 @@ -218,7 +227,7 @@ importers: version: 4.20241127.0 '@remix-run/dev': specifier: ^2.15.0 - version: 2.15.0(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6))(wrangler@3.91.0(@cloudflare/workers-types@4.20241127.0)) + version: 2.15.0(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@types/node@22.10.1)(sass-embedded@1.81.0)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0))(wrangler@3.91.0(@cloudflare/workers-types@4.20241127.0)) '@types/diff': specifier: ^5.2.3 version: 5.2.3 @@ -249,6 +258,9 @@ importers: node-fetch: specifier: ^3.3.2 version: 3.3.2 + pnpm: + specifier: ^9.14.4 + version: 9.14.4 prettier: specifier: ^3.4.1 version: 3.4.1 @@ -263,22 +275,22 @@ importers: version: 11.0.5 unocss: specifier: ^0.61.9 - version: 0.61.9(postcss@8.4.49)(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) + version: 0.61.9(postcss@8.4.49)(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) vite: specifier: ^5.4.11 - version: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + version: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) vite-plugin-node-polyfills: specifier: ^0.22.0 - version: 0.22.0(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) + version: 0.22.0(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) vite-plugin-optimize-css-modules: specifier: ^1.1.0 - version: 1.1.0(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) + version: 1.1.0(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) vite-tsconfig-paths: specifier: ^4.3.2 - version: 4.3.2(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) + version: 4.3.2(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) vitest: specifier: ^2.1.7 - version: 2.1.7(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + version: 2.1.8(@types/node@22.10.1)(sass-embedded@1.81.0) wrangler: specifier: ^3.91.0 version: 3.91.0(@cloudflare/workers-types@4.20241127.0) @@ -666,6 +678,9 @@ packages: '@codemirror/lang-sass@6.0.2': resolution: {integrity: sha512-l/bdzIABvnTo1nzdY6U+kPAC51czYQcOErfzQ9zSm9D8GmNPD0WTW8st/CJwBTPLO8jlrbyvlSEcN20dc4iL0Q==} + '@codemirror/lang-vue@0.1.3': + resolution: {integrity: sha512-QSKdtYTDRhEHCfo5zOShzxCmqKJvgGrZwDQSdbvCRJ5pRLWBS7pD/8e/tH44aVQT6FKm0t6RVNoSUWHOI5vNug==} + '@codemirror/lang-wast@6.0.2': resolution: {integrity: sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==} @@ -1375,8 +1390,8 @@ packages: '@lezer/html@1.3.10': resolution: {integrity: sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==} - '@lezer/javascript@1.4.19': - resolution: {integrity: sha512-j44kbR1QL26l6dMunZ1uhKBFteVGLVCBGNUD2sUaMnic+rbTviVuoK0CD1l9FTW31EueWvFFswCKMH7Z+M3JRA==} + '@lezer/javascript@1.4.20': + resolution: {integrity: sha512-Qhl3x+hVPnZkylv+BS//zx77KR4GLxM4PiL02r/D1Zoa4WLQI1A0cHuOr6k0FOTTSCPNNfeNANax0I5DWcXBYw==} '@lezer/json@1.0.2': resolution: {integrity: sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==} @@ -1717,6 +1732,19 @@ packages: '@types/react-dom': optional: true + '@radix-ui/react-separator@1.1.0': + resolution: {integrity: sha512-3uBAs+egzvJBDZAzvb/n4NxxOYpnspmWxO2u5NbZ8Y6FM/NdrGSF9bop3Cf6F6C71z1rTSn8KV0Fo2ZVd79lGA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.1.0': resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} peerDependencies: @@ -1726,6 +1754,19 @@ packages: '@types/react': optional: true + '@radix-ui/react-switch@1.1.1': + resolution: {integrity: sha512-diPqDDoBcZPSicYoMWdWx+bCPuTRH4QSp9J+65IvtdS0Kuzt67bI6n32vCj8q6NZmYW/ah+2orOtMwcX5eQwIg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-tooltip@1.1.4': resolution: {integrity: sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==} peerDependencies: @@ -1775,6 +1816,15 @@ packages: '@types/react': optional: true + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@radix-ui/react-use-rect@1.1.0': resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} peerDependencies: @@ -2102,8 +2152,8 @@ packages: '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} - '@typescript-eslint/eslint-plugin@8.16.0': - resolution: {integrity: sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==} + '@typescript-eslint/eslint-plugin@8.17.0': + resolution: {integrity: sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -2113,8 +2163,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.16.0': - resolution: {integrity: sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==} + '@typescript-eslint/parser@8.17.0': + resolution: {integrity: sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2123,12 +2173,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@8.16.0': - resolution: {integrity: sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==} + '@typescript-eslint/scope-manager@8.17.0': + resolution: {integrity: sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.16.0': - resolution: {integrity: sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==} + '@typescript-eslint/type-utils@8.17.0': + resolution: {integrity: sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2137,12 +2187,12 @@ packages: typescript: optional: true - '@typescript-eslint/types@8.16.0': - resolution: {integrity: sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==} + '@typescript-eslint/types@8.17.0': + resolution: {integrity: sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.16.0': - resolution: {integrity: sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==} + '@typescript-eslint/typescript-estree@8.17.0': + resolution: {integrity: sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -2150,8 +2200,8 @@ packages: typescript: optional: true - '@typescript-eslint/utils@8.16.0': - resolution: {integrity: sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==} + '@typescript-eslint/utils@8.17.0': + resolution: {integrity: sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2160,8 +2210,8 @@ packages: typescript: optional: true - '@typescript-eslint/visitor-keys@8.16.0': - resolution: {integrity: sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==} + '@typescript-eslint/visitor-keys@8.17.0': + resolution: {integrity: sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@uiw/codemirror-theme-vscode@4.23.6': @@ -2275,11 +2325,11 @@ packages: '@vanilla-extract/private@1.0.6': resolution: {integrity: sha512-ytsG/JLweEjw7DBuZ/0JCN4WAQgM9erfSTdS1NQY778hFQSZ6cfCDEZZ0sgVm4k54uNz6ImKB33AYvSR//fjxw==} - '@vitest/expect@2.1.7': - resolution: {integrity: sha512-folWk4qQDEedgUyvaZw94LIJuNLoDtY+rhKhhNy0csdwifn/pQz8EWVRnyrW3j0wMpy+xwJT8WiwiYxk+i+s7w==} + '@vitest/expect@2.1.8': + resolution: {integrity: sha512-8ytZ/fFHq2g4PJVAtDX57mayemKgDR6X3Oa2Foro+EygiOJHUXhCqBAAKQYYajZpFoIfvBCF1j6R6IYRSIUFuw==} - '@vitest/mocker@2.1.7': - resolution: {integrity: sha512-nKMTnuJrarFH+7llWxeLmYRldIwTY3OM1DzdytHj0f2+fah6Cyk4XbswhjOiTCnAvXsZAEoo1OaD6rneSSU+3Q==} + '@vitest/mocker@2.1.8': + resolution: {integrity: sha512-7guJ/47I6uqfttp33mgo6ga5Gr1VnL58rcqYKyShoRK9ebu8T5Rs6HN3s1NABiBeVTdWNrwUMcHH54uXZBN4zA==} peerDependencies: msw: ^2.4.9 vite: ^5.0.0 @@ -2289,49 +2339,49 @@ packages: vite: optional: true - '@vitest/pretty-format@2.1.7': - resolution: {integrity: sha512-HoqRIyfQlXPrRDB43h0lC8eHPUDPwFweMaD6t+psOvwClCC+oZZim6wPMjuoMnRdiFxXqbybg/QbuewgTwK1vA==} + '@vitest/pretty-format@2.1.8': + resolution: {integrity: sha512-9HiSZ9zpqNLKlbIDRWOnAWqgcA7xu+8YxXSekhr0Ykab7PAYFkhkwoqVArPOtJhPmYeE2YHgKZlj3CP36z2AJQ==} - '@vitest/runner@2.1.7': - resolution: {integrity: sha512-MrDNpXUIXksR57qipYh068SOX4N1hVw6oVILlTlfeTyA1rp0asuljyp15IZwKqhjpWLObFj+tiNrOM4R8UnSqg==} + '@vitest/runner@2.1.8': + resolution: {integrity: sha512-17ub8vQstRnRlIU5k50bG+QOMLHRhYPAna5tw8tYbj+jzjcspnwnwtPtiOlkuKC4+ixDPTuLZiqiWWQ2PSXHVg==} - '@vitest/snapshot@2.1.7': - resolution: {integrity: sha512-OioIxV/xS393DKdlkRNhmtY0K37qVdCv8w1M2SlLTBSX+fNK6zgcd01VlT1nXdbKVDaB8Zb6BOfQYYoGeGTEGg==} + '@vitest/snapshot@2.1.8': + resolution: {integrity: sha512-20T7xRFbmnkfcmgVEz+z3AU/3b0cEzZOt/zmnvZEctg64/QZbSDJEVm9fLnnlSi74KibmRsO9/Qabi+t0vCRPg==} - '@vitest/spy@2.1.7': - resolution: {integrity: sha512-e5pzIaIC0LBrb/j1FaF7HXlPJLGtltiAkwXTMqNEHALJc7USSLEwziJ+aIWTmjsWNg89zazg37h7oZITnublsQ==} + '@vitest/spy@2.1.8': + resolution: {integrity: sha512-5swjf2q95gXeYPevtW0BLk6H8+bPlMb4Vw/9Em4hFxDcaOxS+e0LOX4yqNxoHzMR2akEB2xfpnWUzkZokmgWDg==} - '@vitest/utils@2.1.7': - resolution: {integrity: sha512-7gUdvIzCCuIrMZu0WHTvDJo8C1NsUtOqmwmcS3bRHUcfHemj29wmkzLVNuWQD7WHoBD/+I7WIgrnzt7kxR54ow==} + '@vitest/utils@2.1.8': + resolution: {integrity: sha512-dwSoui6djdwbfFmIgbIjX2ZhIoG7Ex/+xpxyiEgIGzjliY8xGkcpITKTlp6B4MgtGkF2ilvm97cPM96XZaAgcA==} - '@vue/compiler-core@3.4.30': - resolution: {integrity: sha512-ZL8y4Xxdh8O6PSwfdZ1IpQ24PjTAieOz3jXb/MDTfDtANcKBMxg1KLm6OX2jofsaQGYfIVzd3BAG22i56/cF1w==} + '@vue/compiler-core@3.5.13': + resolution: {integrity: sha512-oOdAkwqUfW1WqpwSYJce06wvt6HljgY3fGeM9NcVA1HaYOij3mZG9Rkysn0OHuyUAGMbEbARIpsG+LPVlBJ5/Q==} - '@vue/compiler-dom@3.4.30': - resolution: {integrity: sha512-+16Sd8lYr5j/owCbr9dowcNfrHd+pz+w2/b5Lt26Oz/kB90C9yNbxQ3bYOvt7rI2bxk0nqda39hVcwDFw85c2Q==} + '@vue/compiler-dom@3.5.13': + resolution: {integrity: sha512-ZOJ46sMOKUjO3e94wPdCzQ6P1Lx/vhp2RSvfaab88Ajexs0AHeV0uasYhi99WPaogmBlRHNRuly8xV75cNTMDA==} - '@vue/compiler-sfc@3.4.30': - resolution: {integrity: sha512-8vElKklHn/UY8+FgUFlQrYAPbtiSB2zcgeRKW7HkpSRn/JjMRmZvuOtwDx036D1aqKNSTtXkWRfqx53Qb+HmMg==} + '@vue/compiler-sfc@3.5.13': + resolution: {integrity: sha512-6VdaljMpD82w6c2749Zhf5T9u5uLBWKnVue6XWxprDobftnletJ8+oel7sexFfM3qIxNmVE7LSFGTpv6obNyaQ==} - '@vue/compiler-ssr@3.4.30': - resolution: {integrity: sha512-ZJ56YZGXJDd6jky4mmM0rNaNP6kIbQu9LTKZDhcpddGe/3QIalB1WHHmZ6iZfFNyj5mSypTa4+qDJa5VIuxMSg==} + '@vue/compiler-ssr@3.5.13': + resolution: {integrity: sha512-wMH6vrYHxQl/IybKJagqbquvxpWCuVYpoUJfCqFZwa/JY1GdATAQ+TgVtgrwwMZ0D07QhA99rs/EAAWfvG6KpA==} - '@vue/reactivity@3.4.30': - resolution: {integrity: sha512-bVJurnCe3LS0JII8PPoAA63Zd2MBzcKrEzwdQl92eHCcxtIbxD2fhNwJpa+KkM3Y/A4T5FUnmdhgKwOf6BfbcA==} + '@vue/reactivity@3.5.13': + resolution: {integrity: sha512-NaCwtw8o48B9I6L1zl2p41OHo/2Z4wqYGGIK1Khu5T7yxrn+ATOixn/Udn2m+6kZKB/J7cuT9DbWWhRxqixACg==} - '@vue/runtime-core@3.4.30': - resolution: {integrity: sha512-qaFEbnNpGz+tlnkaualomogzN8vBLkgzK55uuWjYXbYn039eOBZrWxyXWq/7qh9Bz2FPifZqGjVDl/FXiq9L2g==} + '@vue/runtime-core@3.5.13': + resolution: {integrity: sha512-Fj4YRQ3Az0WTZw1sFe+QDb0aXCerigEpw418pw1HBUKFtnQHWzwojaukAs2X/c9DQz4MQ4bsXTGlcpGxU/RCIw==} - '@vue/runtime-dom@3.4.30': - resolution: {integrity: sha512-tV6B4YiZRj5QsaJgw2THCy5C1H+2UeywO9tqgWEc21tn85qHEERndHN/CxlyXvSBFrpmlexCIdnqPuR9RM9thw==} + '@vue/runtime-dom@3.5.13': + resolution: {integrity: sha512-dLaj94s93NYLqjLiyFzVs9X6dWhTdAlEAciC3Moq7gzAc13VJUdCnjjRurNM6uTLFATRHexHCTu/Xp3eW6yoog==} - '@vue/server-renderer@3.4.30': - resolution: {integrity: sha512-TBD3eqR1DeDc0cMrXS/vEs/PWzq1uXxnvjoqQuDGFIEHFIwuDTX/KWAQKIBjyMWLFHEeTDGYVsYci85z2UbTDg==} + '@vue/server-renderer@3.5.13': + resolution: {integrity: sha512-wAi4IRJV/2SAW3htkTlB+dHeRmpTiVIK1OGLWV1yeStVSebSQQOwGwIq0D3ZIoBj2C2qpgz5+vX9iEBkTdk5YA==} peerDependencies: - vue: 3.4.30 + vue: 3.5.13 - '@vue/shared@3.4.30': - resolution: {integrity: sha512-CLg+f8RQCHQnKvuHY9adMsMaQOcqclh6Z5V9TaoMgy0ut0tz848joZ7/CYFFyF/yZ5i2yaw7Fn498C+CNZVHIg==} + '@vue/shared@3.5.13': + resolution: {integrity: sha512-/hnE/qP5ZoGpol0a5mDi45bOd7t3tjYJBjsgCsivow7D48cJeV5l05RD82lPqi7gRiphZM37rnhW1l6ZoCNNnQ==} '@web3-storage/multipart-parser@1.0.0': resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} @@ -2368,6 +2418,11 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + acorn-typescript@1.4.13: + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + acorn-walk@8.3.4: resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} engines: {node: '>=0.4.0'} @@ -2459,6 +2514,9 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + async-lock@1.4.1: + resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} + available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} @@ -2648,6 +2706,9 @@ packages: resolution: {integrity: sha512-3Ek9H3X6pj5TgenXYtNWdaBon1tgYCaebd+XPg0keyjEbEfkD4KkmAxkQ/i1vYvxdcT5nscLBfq9VJRmCBcFSw==} engines: {node: '>= 0.10'} + clean-git-ref@2.0.1: + resolution: {integrity: sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -2671,9 +2732,6 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - code-red@1.0.4: - resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -2743,6 +2801,11 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + create-ecdh@4.0.4: resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} @@ -2819,6 +2882,10 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + dedent@1.5.3: resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} peerDependencies: @@ -2882,6 +2949,9 @@ packages: diff-match-patch@1.0.5: resolution: {integrity: sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==} + diff3@0.0.3: + resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==} + diff@5.2.0: resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} engines: {node: '>=0.3.1'} @@ -2913,8 +2983,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.67: - resolution: {integrity: sha512-nz88NNBsD7kQSAGGJyp8hS6xSPtWwqNogA0mjtc2nUYeEf3nURK9qpV18TuBdDmEDgVWotS8Wkzf+V52dSQ/LQ==} + electron-to-chromium@1.5.68: + resolution: {integrity: sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -3063,6 +3133,9 @@ packages: jiti: optional: true + esm-env@1.2.1: + resolution: {integrity: sha512-U9JedYYjCnadUlXk7e1Kr+aENQhtUaoaV9+gZm1T8LC/YBAPJx3NSPIAurFOC0U5vrdSevnUJS2/wUVxGwPhng==} + espree@10.3.0: resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -3075,6 +3148,9 @@ packages: resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} engines: {node: '>=0.10'} + esrap@1.2.3: + resolution: {integrity: sha512-ZlQmCCK+n7SGoqo7DnfKaP1sJZa49P01/dXzmjCASSo04p72w8EksT2NMK8CEX8DhKsfJXANioIw8VyHNsBfvQ==} + esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} @@ -3367,8 +3443,8 @@ packages: resolution: {integrity: sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==} engines: {node: '>= 0.4'} - has-symbols@1.0.3: - resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} has-tostringtag@1.0.2: @@ -3472,9 +3548,6 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - immutable@4.3.7: - resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} - immutable@5.0.3: resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} @@ -3613,6 +3686,11 @@ packages: isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + isomorphic-git@1.27.2: + resolution: {integrity: sha512-nCiz+ieOkWb5kDJSSckDTiMjTcgkxqH2xuiQmw1Y6O/spwx4d6TKYSfGCd4f71HGvUYcRSUGqJEI+3uN6UQlOw==} + engines: {node: '>=12'} + hasBin: true + isomorphic-timers-promises@1.0.1: resolution: {integrity: sha512-u4sej9B1LPSxTGKB/HiuzvEQnXH0ECYkSVQU39koSwmFAxhlEAFl9RdTvLv4TOTQUgBS5O3O5fwUxk6byBZ+IQ==} engines: {node: '>=10'} @@ -4093,6 +4171,10 @@ packages: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + miniflare@3.20241106.1: resolution: {integrity: sha512-dM3RBlJE8rUFxnqlPCaFCq0E7qQqEQvKbYX7W/APGCK+rLcyLmEBzC4GQR/niXdNM/oV6gdg9AA50ghnn2ALuw==} engines: {node: '>=16.13'} @@ -4114,6 +4196,9 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minimisted@2.0.1: + resolution: {integrity: sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==} + minipass-collect@1.0.2: resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} engines: {node: '>= 8'} @@ -4417,6 +4502,10 @@ packages: engines: {node: '>=0.10'} hasBin: true + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + pkg-dir@5.0.0: resolution: {integrity: sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==} engines: {node: '>=10'} @@ -4786,8 +4875,8 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} - resolve.exports@2.0.2: - resolution: {integrity: sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==} + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} resolve@1.22.8: @@ -4968,11 +5057,6 @@ packages: engines: {node: '>=16.0.0'} hasBin: true - sass@1.77.6: - resolution: {integrity: sha512-ByXE1oLD79GVq9Ht1PeHWCPMPB8XHpBuz1r85oByKHjZY6qV6rWnQovQzXJXuQ/XyE1Oj3iPk3lo28uzaRA2/Q==} - engines: {node: '>=14.0.0'} - hasBin: true - scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -5042,6 +5126,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + sirv@2.0.4: resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==} engines: {node: '>= 10'} @@ -5179,9 +5269,9 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svelte@4.2.18: - resolution: {integrity: sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==} - engines: {node: '>=16'} + svelte@5.4.0: + resolution: {integrity: sha512-2I/mjD8cXDpKfdfUK+T6yo/OzugMXIm8lhyJUFM5F/gICMYnkl3C/+4cOSpia8TqpDsi6Qfm5+fdmBNMNmaf2g==} + engines: {node: '>=18'} swr@2.2.5: resolution: {integrity: sha512-QtxqyclFeAsxEUeZIYmsaQ0UjimSq1RZ9Un7I68/0ClKK/U3LoyQunwkQfJZr2fc22DfIXLNDc2wFyTEikCUpg==} @@ -5315,16 +5405,16 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} - type-fest@4.29.1: - resolution: {integrity: sha512-Y1zUveI92UYM/vo1EFlQSsNf74+hfKH+7saZJslF0Fw92FRaiTAnHPIvo9d7SLxXt/gAYqA4RXyDTioMQCCp0A==} + type-fest@4.30.0: + resolution: {integrity: sha512-G6zXWS1dLj6eagy6sVhOMQiLtJdxQBHIA9Z6HFUNLOlr6MFOgzV8wvmidtPONfPtEUv0uZsy77XJNzTAfwPDaA==} engines: {node: '>=16'} type-is@1.6.18: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} - typescript-eslint@8.16.0: - resolution: {integrity: sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==} + typescript-eslint@8.17.0: + resolution: {integrity: sha512-409VXvFd/f1br1DCbuKNFqQpXICoTB+V51afcwG1pn1a3Cp92MqAUges3YjwEdQ0cMUoCIodjVDAYzyD8h3SYA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -5533,9 +5623,9 @@ packages: engines: {node: ^18.0.0 || >=20.0.0} hasBin: true - vite-node@2.1.7: - resolution: {integrity: sha512-b/5MxSWd0ftWt1B1LHfzCw0ASzaxHztUwP0rcsBhkDSGy9ZDEDieSIjFG3I78nI9dUN0eSeD6LtuKPZGjwwpZQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vite-node@2.1.8: + resolution: {integrity: sha512-uPAwSr57kYjAUux+8E2j0q0Fxpn8M9VoyfGiRI8Kfktz9NcYMCenwY5RnZxnF1WTu3TGiYipirIzacLL3VVGFg==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true vite-plugin-node-polyfills@0.22.0: @@ -5587,15 +5677,15 @@ packages: terser: optional: true - vitest@2.1.7: - resolution: {integrity: sha512-wzJ7Wri44ufkzTZbI1lHsdHfiGdFRmnJ9qIudDQ6tknjJeHhF5QgNSSjk7KRZUU535qEiEXFJ7tSHqyzyIv0jQ==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + vitest@2.1.8: + resolution: {integrity: sha512-1vBKTZskHw/aosXqQUlVWWlGUxSJR8YtiyZDJAFeW2kPAeX6S3Sool0mjspO+kXLuxVWlEDDowBAeqeAQefqLQ==} + engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' - '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 - '@vitest/browser': 2.1.7 - '@vitest/ui': 2.1.7 + '@types/node': ^18.0.0 || >=20.0.0 + '@vitest/browser': 2.1.8 + '@vitest/ui': 2.1.8 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -5615,8 +5705,8 @@ packages: vm-browserify@1.1.2: resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} - vue@3.4.30: - resolution: {integrity: sha512-NcxtKCwkdf1zPsr7Y8+QlDBCGqxvjLXF2EX+yi76rV5rrz90Y6gK1cq0olIhdWGgrlhs9ElHuhi9t3+W5sG5Xw==} + vue@3.5.13: + resolution: {integrity: sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ==} peerDependencies: typescript: '*' peerDependenciesMeta: @@ -5737,6 +5827,9 @@ packages: youch@3.3.4: resolution: {integrity: sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==} + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + zod-to-json-schema@3.23.5: resolution: {integrity: sha512-5wlSS0bXfF/BrL4jPAbz9da5hDlDptdEppYfe+x4eIJ7jioqKG9uUxOwPzqof09u/XeVdrgFu29lZi+8XNDJtA==} peerDependencies: @@ -5863,13 +5956,13 @@ snapshots: transitivePeerDependencies: - zod - '@ai-sdk/svelte@0.0.57(svelte@4.2.18)(zod@3.23.8)': + '@ai-sdk/svelte@0.0.57(svelte@5.4.0)(zod@3.23.8)': dependencies: '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) - sswr: 2.1.0(svelte@4.2.18) + sswr: 2.1.0(svelte@5.4.0) optionalDependencies: - svelte: 4.2.18 + svelte: 5.4.0 transitivePeerDependencies: - zod @@ -5883,13 +5976,13 @@ snapshots: optionalDependencies: zod: 3.23.8 - '@ai-sdk/vue@0.0.59(vue@3.4.30(typescript@5.7.2))(zod@3.23.8)': + '@ai-sdk/vue@0.0.59(vue@3.5.13(typescript@5.7.2))(zod@3.23.8)': dependencies: '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) - swrv: 1.0.4(vue@3.4.30(typescript@5.7.2)) + swrv: 1.0.4(vue@3.5.13(typescript@5.7.2)) optionalDependencies: - vue: 3.4.30(typescript@5.7.2) + vue: 3.5.13(typescript@5.7.2) transitivePeerDependencies: - zod @@ -6109,16 +6202,16 @@ snapshots: '@blitz/eslint-plugin@0.1.0(@types/eslint@8.56.10)(jiti@1.21.6)(prettier@3.4.1)(typescript@5.7.2)': dependencies: '@stylistic/eslint-plugin-ts': 2.11.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/eslint-plugin': 8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/parser': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/eslint-plugin': 8.17.0(@typescript-eslint/parser@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/parser': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/utils': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) common-tags: 1.8.2 eslint: 9.16.0(jiti@1.21.6) eslint-config-prettier: 9.1.0(eslint@9.16.0(jiti@1.21.6)) eslint-plugin-jsonc: 2.18.2(eslint@9.16.0(jiti@1.21.6)) eslint-plugin-prettier: 5.2.1(@types/eslint@8.56.10)(eslint-config-prettier@9.1.0(eslint@9.16.0(jiti@1.21.6)))(eslint@9.16.0(jiti@1.21.6))(prettier@3.4.1) globals: 15.13.0 - typescript-eslint: 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + typescript-eslint: 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) transitivePeerDependencies: - '@eslint/json' - '@types/eslint' @@ -6208,7 +6301,7 @@ snapshots: '@codemirror/state': 6.4.1 '@codemirror/view': 6.35.0 '@lezer/common': 1.2.3 - '@lezer/javascript': 1.4.19 + '@lezer/javascript': 1.4.20 '@codemirror/lang-json@6.0.1': dependencies: @@ -6245,6 +6338,15 @@ snapshots: transitivePeerDependencies: - '@codemirror/view' + '@codemirror/lang-vue@0.1.3': + dependencies: + '@codemirror/lang-html': 6.4.9 + '@codemirror/lang-javascript': 6.2.2 + '@codemirror/language': 6.10.6 + '@lezer/common': 1.2.3 + '@lezer/highlight': 1.2.1 + '@lezer/lr': 1.4.2 + '@codemirror/lang-wast@6.0.2': dependencies: '@codemirror/language': 6.10.6 @@ -6720,7 +6822,7 @@ snapshots: '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 - '@lezer/javascript@1.4.19': + '@lezer/javascript@1.4.20': dependencies: '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 @@ -7112,6 +7214,15 @@ snapshots: '@types/react': 18.3.12 '@types/react-dom': 18.3.1 + '@radix-ui/react-separator@1.1.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + '@radix-ui/react-slot@1.1.0(@types/react@18.3.12)(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1) @@ -7119,6 +7230,21 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 + '@radix-ui/react-switch@1.1.1(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-context': 1.1.1(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.3.12)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.3.12)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.12 + '@types/react-dom': 18.3.1 + '@radix-ui/react-tooltip@1.1.4(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.0 @@ -7165,6 +7291,12 @@ snapshots: optionalDependencies: '@types/react': 18.3.12 + '@radix-ui/react-use-previous@1.1.0(@types/react@18.3.12)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.12 + '@radix-ui/react-use-rect@1.1.0(@types/react@18.3.12)(react@18.3.1)': dependencies: '@radix-ui/rect': 1.1.0 @@ -7205,7 +7337,7 @@ snapshots: optionalDependencies: typescript: 5.7.2 - '@remix-run/dev@2.15.0(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6))(wrangler@3.91.0(@cloudflare/workers-types@4.20241127.0))': + '@remix-run/dev@2.15.0(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@types/node@22.10.1)(sass-embedded@1.81.0)(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0))(wrangler@3.91.0(@cloudflare/workers-types@4.20241127.0))': dependencies: '@babel/core': 7.26.0 '@babel/generator': 7.26.2 @@ -7222,7 +7354,7 @@ snapshots: '@remix-run/router': 1.21.0 '@remix-run/server-runtime': 2.15.0(typescript@5.7.2) '@types/mdx': 2.0.13 - '@vanilla-extract/integration': 6.5.0(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + '@vanilla-extract/integration': 6.5.0(@types/node@22.10.1)(sass-embedded@1.81.0) arg: 5.0.2 cacache: 17.1.4 chalk: 4.1.2 @@ -7261,11 +7393,11 @@ snapshots: tar-fs: 2.1.1 tsconfig-paths: 4.2.0 valibot: 0.41.0(typescript@5.7.2) - vite-node: 1.6.0(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite-node: 1.6.0(@types/node@22.10.1)(sass-embedded@1.81.0) ws: 7.5.10 optionalDependencies: typescript: 5.7.2 - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) wrangler: 3.91.0(@cloudflare/workers-types@4.20241127.0) transitivePeerDependencies: - '@types/node' @@ -7448,7 +7580,7 @@ snapshots: '@stylistic/eslint-plugin-ts@2.11.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': dependencies: - '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/utils': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) eslint: 9.16.0(jiti@1.21.6) eslint-visitor-keys: 4.2.0 espree: 10.3.0 @@ -7533,14 +7665,14 @@ snapshots: '@types/unist@3.0.3': {} - '@typescript-eslint/eslint-plugin@8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': + '@typescript-eslint/eslint-plugin@8.17.0(@typescript-eslint/parser@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/scope-manager': 8.16.0 - '@typescript-eslint/type-utils': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.16.0 + '@typescript-eslint/parser': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.17.0 + '@typescript-eslint/type-utils': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/utils': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.17.0 eslint: 9.16.0(jiti@1.21.6) graphemer: 1.4.0 ignore: 5.3.2 @@ -7551,12 +7683,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': + '@typescript-eslint/parser@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': dependencies: - '@typescript-eslint/scope-manager': 8.16.0 - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.7.2) - '@typescript-eslint/visitor-keys': 8.16.0 + '@typescript-eslint/scope-manager': 8.17.0 + '@typescript-eslint/types': 8.17.0 + '@typescript-eslint/typescript-estree': 8.17.0(typescript@5.7.2) + '@typescript-eslint/visitor-keys': 8.17.0 debug: 4.3.7 eslint: 9.16.0(jiti@1.21.6) optionalDependencies: @@ -7564,15 +7696,15 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.16.0': + '@typescript-eslint/scope-manager@8.17.0': dependencies: - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/visitor-keys': 8.16.0 + '@typescript-eslint/types': 8.17.0 + '@typescript-eslint/visitor-keys': 8.17.0 - '@typescript-eslint/type-utils@8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': + '@typescript-eslint/type-utils@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': dependencies: - '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.7.2) - '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/typescript-estree': 8.17.0(typescript@5.7.2) + '@typescript-eslint/utils': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) debug: 4.3.7 eslint: 9.16.0(jiti@1.21.6) ts-api-utils: 1.4.3(typescript@5.7.2) @@ -7581,12 +7713,12 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.16.0': {} + '@typescript-eslint/types@8.17.0': {} - '@typescript-eslint/typescript-estree@8.16.0(typescript@5.7.2)': + '@typescript-eslint/typescript-estree@8.17.0(typescript@5.7.2)': dependencies: - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/visitor-keys': 8.16.0 + '@typescript-eslint/types': 8.17.0 + '@typescript-eslint/visitor-keys': 8.17.0 debug: 4.3.7 fast-glob: 3.3.2 is-glob: 4.0.3 @@ -7598,21 +7730,21 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': + '@typescript-eslint/utils@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.16.0(jiti@1.21.6)) - '@typescript-eslint/scope-manager': 8.16.0 - '@typescript-eslint/types': 8.16.0 - '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.7.2) + '@typescript-eslint/scope-manager': 8.17.0 + '@typescript-eslint/types': 8.17.0 + '@typescript-eslint/typescript-estree': 8.17.0(typescript@5.7.2) eslint: 9.16.0(jiti@1.21.6) optionalDependencies: typescript: 5.7.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.16.0': + '@typescript-eslint/visitor-keys@8.17.0': dependencies: - '@typescript-eslint/types': 8.16.0 + '@typescript-eslint/types': 8.17.0 eslint-visitor-keys: 4.2.0 '@uiw/codemirror-theme-vscode@4.23.6(@codemirror/language@6.10.6)(@codemirror/state@6.4.1)(@codemirror/view@6.35.0)': @@ -7631,13 +7763,13 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@unocss/astro@0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6))': + '@unocss/astro@0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0))': dependencies: '@unocss/core': 0.61.9 '@unocss/reset': 0.61.9 - '@unocss/vite': 0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) + '@unocss/vite': 0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) optionalDependencies: - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - rollup - supports-color @@ -7774,7 +7906,7 @@ snapshots: dependencies: '@unocss/core': 0.61.9 - '@unocss/vite@0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6))': + '@unocss/vite@0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0))': dependencies: '@ampproject/remapping': 2.3.0 '@rollup/pluginutils': 5.1.3(rollup@4.28.0) @@ -7786,7 +7918,7 @@ snapshots: chokidar: 3.6.0 fast-glob: 3.3.2 magic-string: 0.30.14 - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - rollup - supports-color @@ -7814,7 +7946,7 @@ snapshots: transitivePeerDependencies: - babel-plugin-macros - '@vanilla-extract/integration@6.5.0(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)': + '@vanilla-extract/integration@6.5.0(@types/node@22.10.1)(sass-embedded@1.81.0)': dependencies: '@babel/core': 7.26.0 '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.0) @@ -7827,8 +7959,8 @@ snapshots: lodash: 4.17.21 mlly: 1.7.3 outdent: 0.8.0 - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) - vite-node: 1.6.0(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) + vite-node: 1.6.0(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -7843,99 +7975,99 @@ snapshots: '@vanilla-extract/private@1.0.6': {} - '@vitest/expect@2.1.7': + '@vitest/expect@2.1.8': dependencies: - '@vitest/spy': 2.1.7 - '@vitest/utils': 2.1.7 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 chai: 5.1.2 tinyrainbow: 1.2.0 - '@vitest/mocker@2.1.7(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6))': + '@vitest/mocker@2.1.8(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0))': dependencies: - '@vitest/spy': 2.1.7 + '@vitest/spy': 2.1.8 estree-walker: 3.0.3 magic-string: 0.30.14 optionalDependencies: - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) - '@vitest/pretty-format@2.1.7': + '@vitest/pretty-format@2.1.8': dependencies: tinyrainbow: 1.2.0 - '@vitest/runner@2.1.7': + '@vitest/runner@2.1.8': dependencies: - '@vitest/utils': 2.1.7 + '@vitest/utils': 2.1.8 pathe: 1.1.2 - '@vitest/snapshot@2.1.7': + '@vitest/snapshot@2.1.8': dependencies: - '@vitest/pretty-format': 2.1.7 + '@vitest/pretty-format': 2.1.8 magic-string: 0.30.14 pathe: 1.1.2 - '@vitest/spy@2.1.7': + '@vitest/spy@2.1.8': dependencies: tinyspy: 3.0.2 - '@vitest/utils@2.1.7': + '@vitest/utils@2.1.8': dependencies: - '@vitest/pretty-format': 2.1.7 + '@vitest/pretty-format': 2.1.8 loupe: 3.1.2 tinyrainbow: 1.2.0 - '@vue/compiler-core@3.4.30': + '@vue/compiler-core@3.5.13': dependencies: '@babel/parser': 7.26.2 - '@vue/shared': 3.4.30 + '@vue/shared': 3.5.13 entities: 4.5.0 estree-walker: 2.0.2 source-map-js: 1.2.1 - '@vue/compiler-dom@3.4.30': + '@vue/compiler-dom@3.5.13': dependencies: - '@vue/compiler-core': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/compiler-core': 3.5.13 + '@vue/shared': 3.5.13 - '@vue/compiler-sfc@3.4.30': + '@vue/compiler-sfc@3.5.13': dependencies: '@babel/parser': 7.26.2 - '@vue/compiler-core': 3.4.30 - '@vue/compiler-dom': 3.4.30 - '@vue/compiler-ssr': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/compiler-core': 3.5.13 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 estree-walker: 2.0.2 magic-string: 0.30.14 postcss: 8.4.49 source-map-js: 1.2.1 - '@vue/compiler-ssr@3.4.30': + '@vue/compiler-ssr@3.5.13': dependencies: - '@vue/compiler-dom': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/compiler-dom': 3.5.13 + '@vue/shared': 3.5.13 - '@vue/reactivity@3.4.30': + '@vue/reactivity@3.5.13': dependencies: - '@vue/shared': 3.4.30 + '@vue/shared': 3.5.13 - '@vue/runtime-core@3.4.30': + '@vue/runtime-core@3.5.13': dependencies: - '@vue/reactivity': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/reactivity': 3.5.13 + '@vue/shared': 3.5.13 - '@vue/runtime-dom@3.4.30': + '@vue/runtime-dom@3.5.13': dependencies: - '@vue/reactivity': 3.4.30 - '@vue/runtime-core': 3.4.30 - '@vue/shared': 3.4.30 + '@vue/reactivity': 3.5.13 + '@vue/runtime-core': 3.5.13 + '@vue/shared': 3.5.13 csstype: 3.1.3 - '@vue/server-renderer@3.4.30(vue@3.4.30(typescript@5.7.2))': + '@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.7.2))': dependencies: - '@vue/compiler-ssr': 3.4.30 - '@vue/shared': 3.4.30 - vue: 3.4.30(typescript@5.7.2) + '@vue/compiler-ssr': 3.5.13 + '@vue/shared': 3.5.13 + vue: 3.5.13(typescript@5.7.2) - '@vue/shared@3.4.30': {} + '@vue/shared@3.5.13': {} '@web3-storage/multipart-parser@1.0.0': {} @@ -7967,6 +8099,10 @@ snapshots: dependencies: acorn: 8.14.0 + acorn-typescript@1.4.13(acorn@8.14.0): + dependencies: + acorn: 8.14.0 + acorn-walk@8.3.4: dependencies: acorn: 8.14.0 @@ -7978,15 +8114,15 @@ snapshots: clean-stack: 2.2.0 indent-string: 4.0.0 - ai@3.4.33(react@18.3.1)(sswr@2.1.0(svelte@4.2.18))(svelte@4.2.18)(vue@3.4.30(typescript@5.7.2))(zod@3.23.8): + ai@3.4.33(react@18.3.1)(sswr@2.1.0(svelte@5.4.0))(svelte@5.4.0)(vue@3.5.13(typescript@5.7.2))(zod@3.23.8): dependencies: '@ai-sdk/provider': 0.0.26 '@ai-sdk/provider-utils': 1.0.22(zod@3.23.8) '@ai-sdk/react': 0.0.70(react@18.3.1)(zod@3.23.8) '@ai-sdk/solid': 0.0.54(zod@3.23.8) - '@ai-sdk/svelte': 0.0.57(svelte@4.2.18)(zod@3.23.8) + '@ai-sdk/svelte': 0.0.57(svelte@5.4.0)(zod@3.23.8) '@ai-sdk/ui-utils': 0.0.50(zod@3.23.8) - '@ai-sdk/vue': 0.0.59(vue@3.4.30(typescript@5.7.2))(zod@3.23.8) + '@ai-sdk/vue': 0.0.59(vue@3.5.13(typescript@5.7.2))(zod@3.23.8) '@opentelemetry/api': 1.9.0 eventsource-parser: 1.1.2 json-schema: 0.4.0 @@ -7995,8 +8131,8 @@ snapshots: zod-to-json-schema: 3.23.5(zod@3.23.8) optionalDependencies: react: 18.3.1 - sswr: 2.1.0(svelte@4.2.18) - svelte: 4.2.18 + sswr: 2.1.0(svelte@5.4.0) + svelte: 5.4.0 zod: 3.23.8 transitivePeerDependencies: - solid-js @@ -8058,6 +8194,8 @@ snapshots: astring@1.9.0: {} + async-lock@1.4.1: {} + available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 @@ -8178,7 +8316,7 @@ snapshots: browserslist@4.24.2: dependencies: caniuse-lite: 1.0.30001685 - electron-to-chromium: 1.5.67 + electron-to-chromium: 1.5.68 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) @@ -8292,6 +8430,8 @@ snapshots: inherits: 2.0.4 safe-buffer: 5.2.1 + clean-git-ref@2.0.1: {} + clean-stack@2.2.0: {} cli-cursor@3.1.0: @@ -8306,14 +8446,6 @@ snapshots: clsx@2.1.1: {} - code-red@1.0.4: - dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 - '@types/estree': 1.0.6 - acorn: 8.14.0 - estree-walker: 3.0.3 - periscopic: 3.1.0 - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -8358,6 +8490,8 @@ snapshots: core-util-is@1.0.3: {} + crc-32@1.2.2: {} + create-ecdh@4.0.4: dependencies: bn.js: 4.12.1 @@ -8438,6 +8572,10 @@ snapshots: dependencies: character-entities: 2.0.2 + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + dedent@1.5.3: {} deep-eql@5.0.2: {} @@ -8487,6 +8625,8 @@ snapshots: diff-match-patch@1.0.5: {} + diff3@0.0.3: {} + diff@5.2.0: {} diffie-hellman@5.0.3: @@ -8516,7 +8656,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.67: {} + electron-to-chromium@1.5.68: {} elliptic@6.6.1: dependencies: @@ -8559,7 +8699,7 @@ snapshots: '@jspm/core': 2.0.1 esbuild: 0.17.6 local-pkg: 0.5.1 - resolve.exports: 2.0.2 + resolve.exports: 2.0.3 esbuild@0.17.19: optionalDependencies: @@ -8761,6 +8901,8 @@ snapshots: transitivePeerDependencies: - supports-color + esm-env@1.2.1: {} + espree@10.3.0: dependencies: acorn: 8.14.0 @@ -8777,6 +8919,11 @@ snapshots: dependencies: estraverse: 5.3.0 + esrap@1.2.3: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.6 + esrecurse@4.3.0: dependencies: estraverse: 5.3.0 @@ -9019,7 +9166,7 @@ snapshots: es-errors: 1.3.0 function-bind: 1.1.2 has-proto: 1.1.0 - has-symbols: 1.0.3 + has-symbols: 1.1.0 hasown: 2.0.2 get-nonce@1.0.1: {} @@ -9095,11 +9242,11 @@ snapshots: dependencies: call-bind: 1.0.7 - has-symbols@1.0.3: {} + has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 hash-base@3.0.5: dependencies: @@ -9274,9 +9421,6 @@ snapshots: immediate@3.0.6: {} - immutable@4.3.7: - optional: true - immutable@5.0.3: {} import-fresh@3.3.0: @@ -9391,6 +9535,21 @@ snapshots: isexe@2.0.0: {} + isomorphic-git@1.27.2: + dependencies: + async-lock: 1.4.1 + clean-git-ref: 2.0.1 + crc-32: 1.2.2 + diff3: 0.0.3 + ignore: 5.3.2 + minimisted: 2.0.1 + pako: 1.0.11 + path-browserify: 1.0.1 + pify: 4.0.1 + readable-stream: 3.6.2 + sha.js: 2.4.11 + simple-get: 4.0.1 + isomorphic-timers-promises@1.0.1: {} istextorbinary@9.5.0: @@ -10248,6 +10407,8 @@ snapshots: mimic-fn@2.1.0: {} + mimic-response@3.1.0: {} + miniflare@3.20241106.1: dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -10281,6 +10442,10 @@ snapshots: minimist@1.2.8: {} + minimisted@2.0.1: + dependencies: + minimist: 1.2.8 + minipass-collect@1.0.2: dependencies: minipass: 3.3.6 @@ -10431,7 +10596,7 @@ snapshots: dependencies: call-bind: 1.0.7 define-properties: 1.2.1 - has-symbols: 1.0.3 + has-symbols: 1.1.0 object-keys: 1.1.1 ofetch@1.4.1: @@ -10598,6 +10763,8 @@ snapshots: pidtree@0.6.0: {} + pify@4.0.1: {} + pkg-dir@5.0.0: dependencies: find-up: 5.0.0 @@ -10973,7 +11140,7 @@ snapshots: remix-utils@7.7.0(@remix-run/cloudflare@2.15.0(@cloudflare/workers-types@4.20241127.0)(typescript@5.7.2))(@remix-run/node@2.15.0(typescript@5.7.2))(@remix-run/react@2.15.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@5.7.2))(@remix-run/router@1.21.0)(react@18.3.1)(zod@3.23.8): dependencies: - type-fest: 4.29.1 + type-fest: 4.30.0 optionalDependencies: '@remix-run/cloudflare': 2.15.0(@cloudflare/workers-types@4.20241127.0)(typescript@5.7.2) '@remix-run/node': 2.15.0(typescript@5.7.2) @@ -10988,7 +11155,7 @@ snapshots: resolve-pkg-maps@1.0.0: {} - resolve.exports@2.0.2: {} + resolve.exports@2.0.3: {} resolve@1.22.8: dependencies: @@ -11158,13 +11325,6 @@ snapshots: sass-embedded-win32-ia32: 1.81.0 sass-embedded-win32-x64: 1.81.0 - sass@1.77.6: - dependencies: - chokidar: 3.6.0 - immutable: 4.3.7 - source-map-js: 1.2.1 - optional: true - scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -11255,6 +11415,14 @@ snapshots: signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + sirv@2.0.4: dependencies: '@polka/url': 1.0.0-next.28 @@ -11294,9 +11462,9 @@ snapshots: dependencies: minipass: 7.1.2 - sswr@2.1.0(svelte@4.2.18): + sswr@2.1.0(svelte@5.4.0): dependencies: - svelte: 4.2.18 + svelte: 5.4.0 swrev: 4.0.0 stackback@0.0.2: {} @@ -11389,22 +11557,21 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte@4.2.18: + svelte@5.4.0: dependencies: '@ampproject/remapping': 2.3.0 '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 '@types/estree': 1.0.6 acorn: 8.14.0 + acorn-typescript: 1.4.13(acorn@8.14.0) aria-query: 5.3.2 axobject-query: 4.1.0 - code-red: 1.0.4 - css-tree: 2.3.1 - estree-walker: 3.0.3 + esm-env: 1.2.1 + esrap: 1.2.3 is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.14 - periscopic: 3.1.0 + zimmerframe: 1.1.2 swr@2.2.5(react@18.3.1): dependencies: @@ -11414,9 +11581,9 @@ snapshots: swrev@4.0.0: {} - swrv@1.0.4(vue@3.4.30(typescript@5.7.2)): + swrv@1.0.4(vue@3.5.13(typescript@5.7.2)): dependencies: - vue: 3.4.30(typescript@5.7.2) + vue: 3.5.13(typescript@5.7.2) sync-child-process@1.0.2: dependencies: @@ -11527,18 +11694,18 @@ snapshots: dependencies: prelude-ls: 1.2.1 - type-fest@4.29.1: {} + type-fest@4.30.0: {} type-is@1.6.18: dependencies: media-typer: 0.3.0 mime-types: 2.1.35 - typescript-eslint@8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2): + typescript-eslint@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2): dependencies: - '@typescript-eslint/eslint-plugin': 8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/parser': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) - '@typescript-eslint/utils': 8.16.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/eslint-plugin': 8.17.0(@typescript-eslint/parser@8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2))(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/parser': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) + '@typescript-eslint/utils': 8.17.0(eslint@9.16.0(jiti@1.21.6))(typescript@5.7.2) eslint: 9.16.0(jiti@1.21.6) optionalDependencies: typescript: 5.7.2 @@ -11661,9 +11828,9 @@ snapshots: universalify@2.0.1: {} - unocss@0.61.9(postcss@8.4.49)(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)): + unocss@0.61.9(postcss@8.4.49)(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)): dependencies: - '@unocss/astro': 0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) + '@unocss/astro': 0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) '@unocss/cli': 0.61.9(rollup@4.28.0) '@unocss/core': 0.61.9 '@unocss/extractor-arbitrary-variants': 0.61.9 @@ -11682,9 +11849,9 @@ snapshots: '@unocss/transformer-compile-class': 0.61.9 '@unocss/transformer-directives': 0.61.9 '@unocss/transformer-variant-group': 0.61.9 - '@unocss/vite': 0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) + '@unocss/vite': 0.61.9(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) optionalDependencies: - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - postcss - rollup @@ -11789,13 +11956,13 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.2 - vite-node@1.6.0(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6): + vite-node@1.6.0(@types/node@22.10.1)(sass-embedded@1.81.0): dependencies: cac: 6.7.14 debug: 4.3.7 pathe: 1.1.2 picocolors: 1.1.1 - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - '@types/node' - less @@ -11807,13 +11974,13 @@ snapshots: - supports-color - terser - vite-node@2.1.7(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6): + vite-node@2.1.8(@types/node@22.10.1)(sass-embedded@1.81.0): dependencies: cac: 6.7.14 debug: 4.3.7 es-module-lexer: 1.5.4 pathe: 1.1.2 - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - '@types/node' - less @@ -11825,30 +11992,30 @@ snapshots: - supports-color - terser - vite-plugin-node-polyfills@0.22.0(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)): + vite-plugin-node-polyfills@0.22.0(rollup@4.28.0)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)): dependencies: '@rollup/plugin-inject': 5.0.5(rollup@4.28.0) node-stdlib-browser: 1.3.0 - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - rollup - vite-plugin-optimize-css-modules@1.1.0(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)): + vite-plugin-optimize-css-modules@1.1.0(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)): dependencies: - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) - vite-tsconfig-paths@4.3.2(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)): + vite-tsconfig-paths@4.3.2(typescript@5.7.2)(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)): dependencies: debug: 4.3.7 globrex: 0.1.2 tsconfck: 3.1.4(typescript@5.7.2) optionalDependencies: - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) transitivePeerDependencies: - supports-color - typescript - vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6): + vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0): dependencies: esbuild: 0.21.5 postcss: 8.4.49 @@ -11856,18 +12023,17 @@ snapshots: optionalDependencies: '@types/node': 22.10.1 fsevents: 2.3.3 - sass: 1.77.6 sass-embedded: 1.81.0 - vitest@2.1.7(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6): + vitest@2.1.8(@types/node@22.10.1)(sass-embedded@1.81.0): dependencies: - '@vitest/expect': 2.1.7 - '@vitest/mocker': 2.1.7(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6)) - '@vitest/pretty-format': 2.1.7 - '@vitest/runner': 2.1.7 - '@vitest/snapshot': 2.1.7 - '@vitest/spy': 2.1.7 - '@vitest/utils': 2.1.7 + '@vitest/expect': 2.1.8 + '@vitest/mocker': 2.1.8(vite@5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)) + '@vitest/pretty-format': 2.1.8 + '@vitest/runner': 2.1.8 + '@vitest/snapshot': 2.1.8 + '@vitest/spy': 2.1.8 + '@vitest/utils': 2.1.8 chai: 5.1.2 debug: 4.3.7 expect-type: 1.1.0 @@ -11878,8 +12044,8 @@ snapshots: tinyexec: 0.3.1 tinypool: 1.0.2 tinyrainbow: 1.2.0 - vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) - vite-node: 2.1.7(@types/node@22.10.1)(sass-embedded@1.81.0)(sass@1.77.6) + vite: 5.4.11(@types/node@22.10.1)(sass-embedded@1.81.0) + vite-node: 2.1.8(@types/node@22.10.1)(sass-embedded@1.81.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.10.1 @@ -11896,13 +12062,13 @@ snapshots: vm-browserify@1.1.2: {} - vue@3.4.30(typescript@5.7.2): + vue@3.5.13(typescript@5.7.2): dependencies: - '@vue/compiler-dom': 3.4.30 - '@vue/compiler-sfc': 3.4.30 - '@vue/runtime-dom': 3.4.30 - '@vue/server-renderer': 3.4.30(vue@3.4.30(typescript@5.7.2)) - '@vue/shared': 3.4.30 + '@vue/compiler-dom': 3.5.13 + '@vue/compiler-sfc': 3.5.13 + '@vue/runtime-dom': 3.5.13 + '@vue/server-renderer': 3.5.13(vue@3.5.13(typescript@5.7.2)) + '@vue/shared': 3.5.13 optionalDependencies: typescript: 5.7.2 @@ -11968,7 +12134,7 @@ snapshots: nanoid: 3.3.8 path-to-regexp: 6.3.0 resolve: 1.22.8 - resolve.exports: 2.0.2 + resolve.exports: 2.0.3 selfsigned: 2.4.1 source-map: 0.6.1 unenv: unenv-nightly@2.0.0-20241121-161142-806b5c0 @@ -12018,6 +12184,8 @@ snapshots: mustache: 4.2.0 stacktracey: 2.1.8 + zimmerframe@1.1.2: {} + zod-to-json-schema@3.23.5(zod@3.23.8): dependencies: zod: 3.23.8 diff --git a/public/favicon.svg b/public/favicon.svg index c68d62fd..f8d2d72c 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,4 +1,4 @@ - + diff --git a/public/logo-dark-styled.png b/public/logo-dark-styled.png new file mode 100644 index 00000000..d410fe6e Binary files /dev/null and b/public/logo-dark-styled.png differ diff --git a/public/logo-dark.png b/public/logo-dark.png new file mode 100644 index 00000000..377fda33 Binary files /dev/null and b/public/logo-dark.png differ diff --git a/public/logo-light-styled.png b/public/logo-light-styled.png new file mode 100644 index 00000000..ef0af665 Binary files /dev/null and b/public/logo-light-styled.png differ diff --git a/public/logo-light.png b/public/logo-light.png new file mode 100644 index 00000000..6b4513ef Binary files /dev/null and b/public/logo-light.png differ diff --git a/public/logo.svg b/public/logo.svg index 58d6874c..d3ae1ba3 100644 --- a/public/logo.svg +++ b/public/logo.svg @@ -1 +1,15 @@ - + + + + + + + + + + + + + + + diff --git a/public/social_preview_index.jpg b/public/social_preview_index.jpg index 55952e32..2f226cb4 100644 Binary files a/public/social_preview_index.jpg and b/public/social_preview_index.jpg differ diff --git a/uno.config.ts b/uno.config.ts index 503e1af6..24019911 100644 --- a/uno.config.ts +++ b/uno.config.ts @@ -35,17 +35,17 @@ const BASE_COLORS = { 950: '#0A0A0A', }, accent: { - 50: '#EEF9FF', - 100: '#D8F1FF', - 200: '#BAE7FF', - 300: '#8ADAFF', - 400: '#53C4FF', - 500: '#2BA6FF', - 600: '#1488FC', - 700: '#0D6FE8', - 800: '#1259BB', - 900: '#154E93', - 950: '#122F59', + 50: '#F8F5FF', + 100: '#F0EBFF', + 200: '#E1D6FF', + 300: '#CEBEFF', + 400: '#B69EFF', + 500: '#9C7DFF', + 600: '#8A5FFF', + 700: '#7645E8', + 800: '#6234BB', + 900: '#502D93', + 950: '#2D1959', }, green: { 50: '#F0FDF4',