diff --git a/.gitignore b/.gitignore
index 39aee32..7b377b1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,13 @@
.aider*
+# Project-related files
+
# Run logs
backend/run_logs/*
-.env
\ No newline at end of file
+# Weird Docker setup related files
+backend/backend/*
+
+# Env vars
+frontend/.env.local
+.env
diff --git a/README.md b/README.md
index 97bc183..393e027 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# screenshot-to-code
-This simple app converts a screenshot to HTML/Tailwind CSS. It uses GPT-4 Vision to generate the code and DALL-E 3 to generate similar-looking images.
+This simple app converts a screenshot to code (HTML/Tailwind CSS, or React or Vue or Bootstrap). It uses GPT-4 Vision to generate the code and DALL-E 3 to generate similar-looking images. You can now also enter a URL to clone a live website!
https://github.com/abi/screenshot-to-code/assets/23818/6cebadae-2fe3-4986-ac6a-8fb9db030045
@@ -8,15 +8,18 @@ See the [Examples](#examples) section below for more demos.
## 🚀 Try It Out!
-🆕 [Try it here](https://picoapps.xyz/free-tools/screenshot-to-code) (bring your own OpenAI key - **your key must have access to GPT-4 Vision. See [FAQ](#%EF%B8%8F-faqs) section below for details**). Or see [Getting Started](#-getting-started) below for local install instructions.
+🆕 [Try it here](https://screenshottocode.com) (bring your own OpenAI key - **your key must have access to GPT-4 Vision. See [FAQ](#%EF%B8%8F-faqs) section below for details**). Or see [Getting Started](#-getting-started) below for local install instructions.
## 🌟 Recent Updates
+- Nov 28 - 🔥 🔥 🔥 Get output code in React or Bootstrap or TailwindCSS
+- Nov 23 - Send in a screenshot of the current replicated version (sometimes improves quality of subsequent generations)
+- Nov 21 - Edit code in the code editor and preview changes live thanks to [@clean99](https://github.com/clean99)
- Nov 20 - Paste in a URL to screenshot and clone (requires [ScreenshotOne free API key](https://screenshotone.com?via=screenshot-to-code))
-- Nov 19 - Support for dark/light code editor theme - thanks https://github.com/kachbit
+- Nov 19 - Support for dark/light code editor theme - thanks [@kachbit](https://github.com/kachbit)
- Nov 16 - Added a setting to disable DALL-E image generation if you don't need that
- Nov 16 - View code directly within the app
-- Nov 15 - 🔥 You can now instruct the AI to update the code as you wish. It is helpful if the AI messed up some styles or missed a section.
+- Nov 15 - You can now instruct the AI to update the code as you wish. It is helpful if the AI messed up some styles or missed a section.
## 🛠 Getting Started
@@ -44,6 +47,12 @@ Open http://localhost:5173 to use the app.
If you prefer to run the backend on a different port, update VITE_WS_BACKEND_URL in `frontend/.env.local`
+For debugging purposes, if you don't want to waste GPT4-Vision credits, you can run the backend in mock mode (which streams a pre-recorded response):
+
+```bash
+MOCK=true poetry run uvicorn main:app --reload --port 7001
+```
+
## Docker
If you have Docker installed on your system, in the root directory, run:
@@ -58,19 +67,17 @@ The app will be up and running at http://localhost:5173. Note that you can't dev
## 🙋♂️ FAQs
- **I'm running into an error when setting up the backend. How can I fix it?** [Try this](https://github.com/abi/screenshot-to-code/issues/3#issuecomment-1814777959). If that still doesn't work, open an issue.
-- **How do I get an OpenAI API key that has the GPT4 Vision model available?** Create an OpenAI account. And then, you need to buy at least $1 worth of credit on the [Billing dashboard](https://platform.openai.com/account/billing/overview).
+- **How do I get an OpenAI API key?** See https://github.com/abi/screenshot-to-code/blob/main/Troubleshooting.md
- **How can I provide feedback?** For feedback, feature requests and bug reports, open an issue or ping me on [Twitter](https://twitter.com/_abi_).
## 📚 Examples
**NYTimes**
-| Original | Replica |
-| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
+| Original | Replica |
+| --------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| | |
-
-
**Instagram page (with not Taylor Swift pics)**
https://github.com/abi/screenshot-to-code/assets/23818/503eb86a-356e-4dfc-926a-dabdb1ac7ba1
@@ -81,4 +88,6 @@ https://github.com/abi/screenshot-to-code/assets/23818/3fec0f77-44e8-4fb3-a769-a
## 🌍 Hosted Version
-🆕 [Try it here](https://picoapps.xyz/free-tools/screenshot-to-code) (bring your own OpenAI key - **your key must have access to GPT-4 Vision. See [FAQ](#%EF%B8%8F-faqs) section for details**). Or see [Getting Started](#-getting-started) for local install instructions.
+🆕 [Try it here](https://screenshottocode.com) (bring your own OpenAI key - **your key must have access to GPT-4 Vision. See [FAQ](#%EF%B8%8F-faqs) section for details**). Or see [Getting Started](#-getting-started) for local install instructions.
+
+[](https://www.buymeacoffee.com/abiraja)
diff --git a/Troubleshooting.md b/Troubleshooting.md
new file mode 100644
index 0000000..20fa815
--- /dev/null
+++ b/Troubleshooting.md
@@ -0,0 +1,17 @@
+### Getting an OpenAI API key with GPT4-Vision model access
+
+You don't need a ChatGPT Pro account. Screenshot to code uses API keys from your OpenAI developer account. In order to get access to the GPT4 Vision model, log into your OpenAI account and then, follow these instructions:
+
+1. Open [OpenAI Dashboard](https://platform.openai.com/)
+1. Go to Settings > Billing
+1. Click at the Add payment details
+
+4. You have to buy some credits. The minimum is $5.
+
+5. Go to Settings > Limits and check at the bottom of the page, your current tier has to be "Tier 1" to have GPT4 access
+
+6. Go to Screenshot to code and paste it in the Settings dialog under OpenAI key (gear icon). Your key is only stored in your browser. Never stored on our servers.
+
+Some users have also reported that it can take upto 30 minutes after your credit purchase for the GPT4 vision model to be activated.
+
+If you've followed these steps, and it still doesn't work, feel free to open a Github issue.
diff --git a/backend/README.md b/backend/README.md
new file mode 100644
index 0000000..7c1cad2
--- /dev/null
+++ b/backend/README.md
@@ -0,0 +1,3 @@
+Run tests
+
+pytest test_prompts.py
diff --git a/backend/access_token.py b/backend/access_token.py
new file mode 100644
index 0000000..e61ef12
--- /dev/null
+++ b/backend/access_token.py
@@ -0,0 +1,27 @@
+import json
+import os
+import httpx
+
+
+async def validate_access_token(access_code: str):
+ async with httpx.AsyncClient() as client:
+ url = (
+ "https://backend.buildpicoapps.com/screenshot_to_code/validate_access_token"
+ )
+ data = json.dumps(
+ {
+ "access_code": access_code,
+ "secret": os.environ.get("PICO_BACKEND_SECRET"),
+ }
+ )
+ headers = {"Content-Type": "application/json"}
+
+ response = await client.post(url, content=data, headers=headers)
+ response_data = response.json()
+
+ if response_data["success"]:
+ print("Access token is valid.")
+ return True
+ else:
+ print(f"Access token validation failed: {response_data['failure_reason']}")
+ return False
diff --git a/backend/main.py b/backend/main.py
index bd161c8..fd2e7e2 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -1,6 +1,5 @@
# Load environment variables first
from dotenv import load_dotenv
-from pydantic import BaseModel
load_dotenv()
@@ -16,6 +15,7 @@ from mock import mock_completion
from image_generation import create_alt_url_mapping, generate_images
from prompts import assemble_prompt
from routes import screenshot
+from access_token import validate_access_token
app = FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
@@ -33,7 +33,8 @@ app.add_middleware(
# Useful for debugging purposes when you don't want to waste GPT4-Vision credits
# Setting to True will stream a mock response instead of calling the OpenAI API
-SHOULD_MOCK_AI_RESPONSE = False
+# TODO: Should only be set to true when value is 'True', not any abitrary truthy value
+SHOULD_MOCK_AI_RESPONSE = bool(os.environ.get("MOCK", False))
app.include_router(screenshot.router)
@@ -59,27 +60,46 @@ def write_logs(prompt_messages, completion):
@app.websocket("/generate-code")
-async def stream_code_test(websocket: WebSocket):
+async def stream_code(websocket: WebSocket):
await websocket.accept()
+ print("Incoming websocket connection...")
+
params = await websocket.receive_json()
+ print("Received params")
+
+ # Read the output settings from the request. Fall back to default if not provided.
+ output_settings = {"css": "tailwind", "js": "vanilla"}
+ if params["outputSettings"] and params["outputSettings"]["css"]:
+ output_settings["css"] = params["outputSettings"]["css"]
+ if params["outputSettings"] and params["outputSettings"]["js"]:
+ output_settings["js"] = params["outputSettings"]["js"]
+ print("Using output settings:", output_settings)
+
# Get the OpenAI API key from the request. Fall back to environment variable if not provided.
# If neither is provided, we throw an error.
- if params["openAiApiKey"]:
- openai_api_key = params["openAiApiKey"]
- print("Using OpenAI API key from client-side settings dialog")
+ openai_api_key = None
+ if "accessCode" in params and params["accessCode"]:
+ print("Access code - using platform API key")
+ if await validate_access_token(params["accessCode"]):
+ openai_api_key = os.environ.get("PLATFORM_OPENAI_API_KEY")
+ else:
+ await websocket.send_json(
+ {
+ "type": "error",
+ "value": "Invalid access code or you're out of credits. Please try again.",
+ }
+ )
+ return
else:
- openai_api_key = os.environ.get("OPENAI_API_KEY")
- if openai_api_key:
- print("Using OpenAI API key from environment variable")
- if params["openAiBaseURL"]:
- openai_base_url = params["openAiBaseURL"]
- print("Using OpenAI Base URL from client-side settings dialog")
- else:
- openai_base_url = os.environ.get("OPENAI_BASE_URL")
- if openai_base_url:
- print("Using OpenAI Base URL from environment variable")
+ if params["openAiApiKey"]:
+ openai_api_key = params["openAiApiKey"]
+ print("Using OpenAI API key from client-side settings dialog")
+ else:
+ openai_api_key = os.environ.get("OPENAI_API_KEY")
+ if openai_api_key:
+ print("Using OpenAI API key from environment variable")
if not openai_api_key:
print("OpenAI API key not found")
@@ -90,12 +110,21 @@ async def stream_code_test(websocket: WebSocket):
}
)
return
- # openai_base_url="https://flag.smarttrot.com/v1"
+
+ # Get the OpenAI Base URL from the request. Fall back to environment variable if not provided.
+ openai_base_url = None
+ if params["openAiBaseURL"]:
+ openai_base_url = params["openAiBaseURL"]
+ print("Using OpenAI Base URL from client-side settings dialog")
+ else:
+ openai_base_url = os.environ.get("OPENAI_BASE_URL")
+ if openai_base_url:
+ print("Using OpenAI Base URL from environment variable")
+
if not openai_base_url:
- openai_base_url = None
print("Using Offical OpenAI Base URL")
-
+ # Get the image generation flag from the request. Fall back to True if not provided.
should_generate_images = (
params["isImageGenerationEnabled"]
if "isImageGenerationEnabled" in params
@@ -108,7 +137,12 @@ async def stream_code_test(websocket: WebSocket):
async def process_chunk(content):
await websocket.send_json({"type": "chunk", "value": content})
- prompt_messages = assemble_prompt(params["image"])
+ if params.get("resultImage") and params["resultImage"]:
+ prompt_messages = assemble_prompt(
+ params["image"], output_settings, params["resultImage"]
+ )
+ else:
+ prompt_messages = assemble_prompt(params["image"], output_settings)
# Image cache for updates so that we don't have to regenerate images
image_cache = {}
@@ -129,7 +163,7 @@ async def stream_code_test(websocket: WebSocket):
completion = await stream_openai_response(
prompt_messages,
api_key=openai_api_key,
- base_url = openai_base_url,
+ base_url=openai_base_url,
callback=lambda x: process_chunk(x),
)
@@ -142,7 +176,10 @@ async def stream_code_test(websocket: WebSocket):
{"type": "status", "value": "Generating images..."}
)
updated_html = await generate_images(
- completion, api_key=openai_api_key, base_url=openai_base_url, image_cache=image_cache
+ completion,
+ api_key=openai_api_key,
+ base_url=openai_base_url,
+ image_cache=image_cache,
)
else:
updated_html = completion
diff --git a/backend/prompts.py b/backend/prompts.py
index 3761404..f01eb7e 100644
--- a/backend/prompts.py
+++ b/backend/prompts.py
@@ -1,9 +1,9 @@
-SYSTEM_PROMPT = """
+TAILWIND_SYSTEM_PROMPT = """
You are an expert Tailwind developer
You take screenshots of a reference web page from the user, and then build single page apps
using Tailwind, HTML and JS.
-You might also be given a screenshot of a web page that you have already built, and asked to
-update it to look more like the reference image.
+You might also be given a screenshot(The second image) of a web page that you have already built, and asked to
+update it to look more like the reference image(The first image).
- Make sure the app looks exactly like the screenshot.
- Pay close attention to background color, text color, font size, font family,
@@ -23,25 +23,105 @@ Return only the full code in tags.
Do not include markdown "```" or "```html" at the start or end.
"""
+BOOTSTRAP_SYSTEM_PROMPT = """
+You are an expert Bootstrap developer
+You take screenshots of a reference web page from the user, and then build single page apps
+using Bootstrap, HTML and JS.
+You might also be given a screenshot(The second image) of a web page that you have already built, and asked to
+update it to look more like the reference image(The first image).
+
+- Make sure the app looks exactly like the screenshot.
+- Pay close attention to background color, text color, font size, font family,
+padding, margin, border, etc. Match the colors and sizes exactly.
+- Use the exact text from the screenshot.
+- Do not add comments in the code such as "" and "" in place of writing the full code. WRITE THE FULL CODE.
+- Repeat elements as needed to match the screenshot. For example, if there are 15 items, the code should have 15 items. DO NOT LEAVE comments like "" or bad things will happen.
+- For images, use placeholder images from https://placehold.co and include a detailed description of the image in the alt text so that an image generation AI can generate the image later.
+
+In terms of libraries,
+
+- Use this script to include Bootstrap:
+- You can use Google Fonts
+- Font Awesome for icons:
+
+Return only the full code in tags.
+Do not include markdown "```" or "```html" at the start or end.
+"""
+
+REACT_TAILWIND_SYSTEM_PROMPT = """
+You are an expert React/Tailwind developer
+You take screenshots of a reference web page from the user, and then build single page apps
+using React and Tailwind CSS.
+You might also be given a screenshot(The second image) of a web page that you have already built, and asked to
+update it to look more like the reference image(The first image).
+
+- Make sure the app looks exactly like the screenshot.
+- Pay close attention to background color, text color, font size, font family,
+padding, margin, border, etc. Match the colors and sizes exactly.
+- Use the exact text from the screenshot.
+- Do not add comments in the code such as "" and "" in place of writing the full code. WRITE THE FULL CODE.
+- Repeat elements as needed to match the screenshot. For example, if there are 15 items, the code should have 15 items. DO NOT LEAVE comments like "" or bad things will happen.
+- For images, use placeholder images from https://placehold.co and include a detailed description of the image in the alt text so that an image generation AI can generate the image later.
+
+In terms of libraries,
+
+- Use these script to include React so that it can run on a standalone page:
+
+
+
+- Use this script to include Tailwind:
+- You can use Google Fonts
+- Font Awesome for icons:
+
+Return only the full code in tags.
+Do not include markdown "```" or "```html" at the start or end.
+"""
+
USER_PROMPT = """
Generate code for a web page that looks exactly like this.
"""
-def assemble_prompt(image_data_url):
- return [
- {"role": "system", "content": SYSTEM_PROMPT},
+def assemble_prompt(image_data_url, output_settings: dict, result_image_data_url=None):
+ # Set the system prompt based on the output settings
+ chosen_prompt_name = "tailwind"
+ system_content = TAILWIND_SYSTEM_PROMPT
+ if output_settings["css"] == "bootstrap":
+ chosen_prompt_name = "bootstrap"
+ system_content = BOOTSTRAP_SYSTEM_PROMPT
+ if output_settings["js"] == "react":
+ chosen_prompt_name = "react-tailwind"
+ system_content = REACT_TAILWIND_SYSTEM_PROMPT
+
+ print("Using system prompt:", chosen_prompt_name)
+
+ user_content = [
{
- "role": "user",
- "content": [
- {
- "type": "image_url",
- "image_url": {"url": image_data_url, "detail": "high"},
- },
- {
- "type": "text",
- "text": USER_PROMPT,
- },
- ],
+ "type": "image_url",
+ "image_url": {"url": image_data_url, "detail": "high"},
+ },
+ {
+ "type": "text",
+ "text": USER_PROMPT,
+ },
+ ]
+
+ # Include the result image if it exists
+ if result_image_data_url:
+ user_content.insert(
+ 1,
+ {
+ "type": "image_url",
+ "image_url": {"url": result_image_data_url, "detail": "high"},
+ },
+ )
+ return [
+ {
+ "role": "system",
+ "content": system_content,
+ },
+ {
+ "role": "user",
+ "content": user_content,
},
]
diff --git a/backend/test_prompts.py b/backend/test_prompts.py
new file mode 100644
index 0000000..2eaaaf4
--- /dev/null
+++ b/backend/test_prompts.py
@@ -0,0 +1,97 @@
+from prompts import assemble_prompt
+
+TAILWIND_SYSTEM_PROMPT = """
+You are an expert Tailwind developer
+You take screenshots of a reference web page from the user, and then build single page apps
+using Tailwind, HTML and JS.
+You might also be given a screenshot(The second image) of a web page that you have already built, and asked to
+update it to look more like the reference image(The first image).
+
+- Make sure the app looks exactly like the screenshot.
+- Pay close attention to background color, text color, font size, font family,
+padding, margin, border, etc. Match the colors and sizes exactly.
+- Use the exact text from the screenshot.
+- Do not add comments in the code such as "" and "" in place of writing the full code. WRITE THE FULL CODE.
+- Repeat elements as needed to match the screenshot. For example, if there are 15 items, the code should have 15 items. DO NOT LEAVE comments like "" or bad things will happen.
+- For images, use placeholder images from https://placehold.co and include a detailed description of the image in the alt text so that an image generation AI can generate the image later.
+
+In terms of libraries,
+
+- Use this script to include Tailwind:
+- You can use Google Fonts
+- Font Awesome for icons:
+
+Return only the full code in tags.
+Do not include markdown "```" or "```html" at the start or end.
+"""
+
+BOOTSTRAP_SYSTEM_PROMPT = """
+You are an expert Bootstrap developer
+You take screenshots of a reference web page from the user, and then build single page apps
+using Bootstrap, HTML and JS.
+You might also be given a screenshot(The second image) of a web page that you have already built, and asked to
+update it to look more like the reference image(The first image).
+
+- Make sure the app looks exactly like the screenshot.
+- Pay close attention to background color, text color, font size, font family,
+padding, margin, border, etc. Match the colors and sizes exactly.
+- Use the exact text from the screenshot.
+- Do not add comments in the code such as "" and "" in place of writing the full code. WRITE THE FULL CODE.
+- Repeat elements as needed to match the screenshot. For example, if there are 15 items, the code should have 15 items. DO NOT LEAVE comments like "" or bad things will happen.
+- For images, use placeholder images from https://placehold.co and include a detailed description of the image in the alt text so that an image generation AI can generate the image later.
+
+In terms of libraries,
+
+- Use this script to include Bootstrap:
+- You can use Google Fonts
+- Font Awesome for icons:
+
+Return only the full code in tags.
+Do not include markdown "```" or "```html" at the start or end.
+"""
+
+REACT_TAILWIND_SYSTEM_PROMPT = """
+You are an expert React/Tailwind developer
+You take screenshots of a reference web page from the user, and then build single page apps
+using React and Tailwind CSS.
+You might also be given a screenshot(The second image) of a web page that you have already built, and asked to
+update it to look more like the reference image(The first image).
+
+- Make sure the app looks exactly like the screenshot.
+- Pay close attention to background color, text color, font size, font family,
+padding, margin, border, etc. Match the colors and sizes exactly.
+- Use the exact text from the screenshot.
+- Do not add comments in the code such as "" and "" in place of writing the full code. WRITE THE FULL CODE.
+- Repeat elements as needed to match the screenshot. For example, if there are 15 items, the code should have 15 items. DO NOT LEAVE comments like "" or bad things will happen.
+- For images, use placeholder images from https://placehold.co and include a detailed description of the image in the alt text so that an image generation AI can generate the image later.
+
+In terms of libraries,
+
+- Use these script to include React so that it can run on a standalone page:
+
+
+
+- Use this script to include Tailwind:
+- You can use Google Fonts
+- Font Awesome for icons:
+
+Return only the full code in tags.
+Do not include markdown "```" or "```html" at the start or end.
+"""
+
+
+def test_prompts():
+ tailwind_prompt = assemble_prompt(
+ "image_data_url", {"css": "tailwind", "js": "vanilla"}, "result_image_data_url"
+ )
+ assert tailwind_prompt[0]["content"] == TAILWIND_SYSTEM_PROMPT
+
+ bootstrap_prompt = assemble_prompt(
+ "image_data_url", {"css": "bootstrap", "js": "vanilla"}, "result_image_data_url"
+ )
+ assert bootstrap_prompt[0]["content"] == BOOTSTRAP_SYSTEM_PROMPT
+
+ react_tailwind_prompt = assemble_prompt(
+ "image_data_url", {"css": "tailwind", "js": "react"}, "result_image_data_url"
+ )
+ assert react_tailwind_prompt[0]["content"] == REACT_TAILWIND_SYSTEM_PROMPT
diff --git a/frontend/.gitignore b/frontend/.gitignore
index a547bf3..17ceca3 100644
--- a/frontend/.gitignore
+++ b/frontend/.gitignore
@@ -22,3 +22,6 @@ dist-ssr
*.njsproj
*.sln
*.sw?
+
+# Env files
+.env*
diff --git a/frontend/index.html b/frontend/index.html
index 0d85bb4..2a7fa0e 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -17,7 +17,38 @@
rel="stylesheet"
/>
+
+ <%- injectHead %>
+
Screenshot to Code
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/package.json b/frontend/package.json
index 7a34ab2..476a72d 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -5,15 +5,22 @@
"type": "module",
"scripts": {
"dev": "vite",
+ "dev-hosted": "vite --mode prod",
"build": "tsc && vite build",
+ "build-hosted": "tsc && vite build --mode prod",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview"
},
"dependencies": {
"@codemirror/lang-html": "^6.4.6",
+ "@radix-ui/react-accordion": "^1.1.2",
+ "@radix-ui/react-alert-dialog": "^1.0.5",
+ "@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-popover": "^1.0.7",
+ "@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-switch": "^1.0.3",
@@ -22,6 +29,8 @@
"classnames": "^2.3.2",
"clsx": "^2.0.0",
"codemirror": "^6.0.1",
+ "copy-to-clipboard": "^3.3.3",
+ "html2canvas": "^1.4.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
@@ -46,7 +55,8 @@
"postcss": "^8.4.31",
"tailwindcss": "^3.3.5",
"typescript": "^5.0.2",
- "vite": "^4.4.5"
+ "vite": "^4.4.5",
+ "vite-plugin-html": "^3.2.0"
},
"engines": {
"node": ">=14.18.0"
diff --git a/frontend/public/brand/twitter-summary-card.png b/frontend/public/brand/twitter-summary-card.png
new file mode 100644
index 0000000..6fcedbf
Binary files /dev/null and b/frontend/public/brand/twitter-summary-card.png differ
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 7e89041..ed416da 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,4 +1,4 @@
-import { useState } from "react";
+import { useRef, useState } from "react";
import ImageUpload from "./components/ImageUpload";
import CodePreview from "./components/CodePreview";
import Preview from "./components/Preview";
@@ -12,23 +12,33 @@ import {
FaMobile,
FaUndo,
} from "react-icons/fa";
+
+import { Switch } from "./components/ui/switch";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs";
-import CodeMirror from "./components/CodeMirror";
import SettingsDialog from "./components/SettingsDialog";
-import { Settings } from "./types";
+import {
+ Settings,
+ EditorTheme,
+ AppState,
+ CSSOption,
+ OutputSettings,
+ JSFrameworkOption,
+} from "./types";
import { IS_RUNNING_ON_CLOUD } from "./config";
import { PicoBadge } from "./components/PicoBadge";
import { OnboardingNote } from "./components/OnboardingNote";
import { usePersistedState } from "./hooks/usePersistedState";
import { UrlInputSection } from "./components/UrlInputSection";
import TermsOfServiceDialog from "./components/TermsOfServiceDialog";
+import html2canvas from "html2canvas";
+import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants";
+import CodeTab from "./components/CodeTab";
+import OutputSettingsSection from "./components/OutputSettingsSection";
function App() {
- const [appState, setAppState] = useState<"INITIAL" | "CODING" | "CODE_READY">(
- "INITIAL"
- );
+ const [appState, setAppState] = useState(AppState.INITIAL);
const [generatedCode, setGeneratedCode] = useState("");
const [referenceImages, setReferenceImages] = useState([]);
const [executionConsole, setExecutionConsole] = useState([]);
@@ -40,10 +50,33 @@ function App() {
openAiBaseURL: null,
screenshotOneApiKey: null,
isImageGenerationEnabled: true,
- editorTheme: "cobalt",
+ editorTheme: EditorTheme.COBALT,
+ isTermOfServiceAccepted: false,
+ accessCode: null,
},
"setting"
);
+ const [outputSettings, setOutputSettings] = useState({
+ css: CSSOption.TAILWIND,
+ js: JSFrameworkOption.NO_FRAMEWORK,
+ });
+ const [shouldIncludeResultImage, setShouldIncludeResultImage] =
+ useState(false);
+
+ const wsRef = useRef(null);
+
+ const takeScreenshot = async (): Promise => {
+ const iframeElement = document.querySelector(
+ "#preview-desktop"
+ ) as HTMLIFrameElement;
+ if (!iframeElement?.contentWindow?.document.body) {
+ return "";
+ }
+
+ const canvas = await html2canvas(iframeElement.contentWindow.document.body);
+ const png = canvas.toDataURL("image/png");
+ return png;
+ };
const downloadCode = () => {
// Create a blob from the generated code
@@ -63,31 +96,41 @@ function App() {
};
const reset = () => {
- setAppState("INITIAL");
+ setAppState(AppState.INITIAL);
setGeneratedCode("");
setReferenceImages([]);
setExecutionConsole([]);
setHistory([]);
};
+ const stop = () => {
+ wsRef.current?.close?.(USER_CLOSE_WEB_SOCKET_CODE);
+ // make sure stop can correct the state even if the websocket is already closed
+ setAppState(AppState.CODE_READY);
+ };
+
function doGenerateCode(params: CodeGenerationParams) {
setExecutionConsole([]);
- setAppState("CODING");
+ setAppState(AppState.CODING);
// Merge settings with params
- const updatedParams = { ...params, ...settings };
+ const updatedParams = { ...params, ...settings, outputSettings };
generateCode(
+ wsRef,
updatedParams,
(token) => setGeneratedCode((prev) => prev + token),
(code) => setGeneratedCode(code),
(line) => setExecutionConsole((prev) => [...prev, line]),
- () => setAppState("CODE_READY")
+ () => setAppState(AppState.CODE_READY)
);
}
// Initial version creation
function doCreate(referenceImages: string[]) {
+ // Reset any existing state
+ reset();
+
setReferenceImages(referenceImages);
if (referenceImages.length > 0) {
doGenerateCode({
@@ -98,53 +141,88 @@ function App() {
}
// Subsequent updates
- function doUpdate() {
+ async function doUpdate() {
const updatedHistory = [...history, generatedCode, updateInstruction];
-
- doGenerateCode({
- generationType: "update",
- image: referenceImages[0],
- history: updatedHistory,
- });
+ if (shouldIncludeResultImage) {
+ const resultImage = await takeScreenshot();
+ doGenerateCode({
+ generationType: "update",
+ image: referenceImages[0],
+ resultImage: resultImage,
+ history: updatedHistory,
+ });
+ } else {
+ doGenerateCode({
+ generationType: "update",
+ image: referenceImages[0],
+ history: updatedHistory,
+ });
+ }
setHistory(updatedHistory);
setGeneratedCode("");
setUpdateInstruction("");
}
- return (
-
- {IS_RUNNING_ON_CLOUD &&
}
- {IS_RUNNING_ON_CLOUD &&
}
+ const handleTermDialogOpenChange = (open: boolean) => {
+ setSettings((s) => ({
+ ...s,
+ isTermOfServiceAccepted: !open,
+ }));
+ };
-
-
-
+ return (
+
+ {IS_RUNNING_ON_CLOUD &&
}
+ {IS_RUNNING_ON_CLOUD && (
+
+ )}
+
+
+
Screenshot to Code
- {appState === "INITIAL" && (
-
- Drag & drop a screenshot to get started.
-
- )}
- {IS_RUNNING_ON_CLOUD && !settings.openAiApiKey &&
}
+
- {(appState === "CODING" || appState === "CODE_READY") && (
+ {IS_RUNNING_ON_CLOUD &&
+ !(settings.openAiApiKey || settings.accessCode) && (
+
+ )}
+
+ {(appState === AppState.CODING ||
+ appState === AppState.CODE_READY) && (
<>
{/* Show code preview only when coding */}
- {appState === "CODING" && (
+ {appState === AppState.CODING && (
{executionConsole.slice(-1)[0]}
+
+
+ Stop
+
+
)}
- {appState === "CODE_READY" && (
+ {appState === AppState.CODE_READY && (
Download
Reset
@@ -177,7 +270,7 @@ function App() {
- {appState === "INITIAL" && (
+ {appState === AppState.INITIAL && (
)}
- {(appState === "CODING" || appState === "CODE_READY") && (
+ {(appState === AppState.CODING || appState === AppState.CODE_READY) && (
@@ -244,9 +337,10 @@ function App() {
-
diff --git a/frontend/src/components/CodeMirror.tsx b/frontend/src/components/CodeMirror.tsx
index 21008bc..c1dba0e 100644
--- a/frontend/src/components/CodeMirror.tsx
+++ b/frontend/src/components/CodeMirror.tsx
@@ -1,6 +1,6 @@
-import { useRef, useEffect } from "react";
+import { useRef, useEffect, useMemo } from "react";
import { EditorState } from "@codemirror/state";
-import { EditorView, keymap, lineNumbers } from "@codemirror/view";
+import { EditorView, keymap, lineNumbers, ViewUpdate } from "@codemirror/view";
import { espresso, cobalt } from "thememirror";
import {
defaultKeymap,
@@ -11,24 +11,20 @@ import {
} from "@codemirror/commands";
import { bracketMatching } from "@codemirror/language";
import { html } from "@codemirror/lang-html";
+import { EditorTheme } from "@/types";
interface Props {
code: string;
- editorTheme: string;
+ editorTheme: EditorTheme;
+ onCodeChange: (code: string) => void;
}
-function CodeMirror({ code, editorTheme }: Props) {
+function CodeMirror({ code, editorTheme, onCodeChange }: Props) {
const ref = useRef
(null);
const view = useRef(null);
-
- useEffect(() => {
- let selectedTheme = cobalt;
- if (editorTheme === "espresso") {
- selectedTheme = espresso;
- }
- view.current = new EditorView({
- state: EditorState.create({
- doc: code,
+ const editorState = useMemo(
+ () =>
+ EditorState.create({
extensions: [
history(),
keymap.of([
@@ -40,10 +36,21 @@ function CodeMirror({ code, editorTheme }: Props) {
lineNumbers(),
bracketMatching(),
html(),
- selectedTheme,
+ editorTheme === EditorTheme.ESPRESSO ? espresso : cobalt,
EditorView.lineWrapping,
+ EditorView.updateListener.of((update: ViewUpdate) => {
+ if (update.docChanged) {
+ const updatedCode = update.state.doc.toString();
+ onCodeChange(updatedCode);
+ }
+ }),
],
}),
+ [editorTheme]
+ );
+ useEffect(() => {
+ view.current = new EditorView({
+ state: editorState,
parent: ref.current as Element,
});
@@ -53,7 +60,7 @@ function CodeMirror({ code, editorTheme }: Props) {
view.current = null;
}
};
- }, [code, editorTheme]);
+ }, []);
useEffect(() => {
if (view.current && view.current.state.doc.toString() !== code) {
@@ -64,7 +71,10 @@ function CodeMirror({ code, editorTheme }: Props) {
}, [code]);
return (
-
+
);
}
diff --git a/frontend/src/components/CodeTab.tsx b/frontend/src/components/CodeTab.tsx
new file mode 100644
index 0000000..d10f649
--- /dev/null
+++ b/frontend/src/components/CodeTab.tsx
@@ -0,0 +1,87 @@
+import { FaCopy } from "react-icons/fa";
+import CodeMirror from "./CodeMirror";
+import { Button } from "./ui/button";
+import { Settings } from "../types";
+import copy from "copy-to-clipboard";
+import { useCallback } from "react";
+import toast from "react-hot-toast";
+
+interface Props {
+ code: string;
+ setCode: React.Dispatch>;
+ settings: Settings;
+}
+
+function CodeTab({ code, setCode, settings }: Props) {
+ const copyCode = useCallback(() => {
+ copy(code);
+ toast.success("Copied to clipboard");
+ }, [code]);
+
+ const doOpenInCodepenio = useCallback(async () => {
+ // TODO: Update CSS and JS external links depending on the framework being used
+ const data = {
+ html: code,
+ editors: "100", // 1: Open HTML, 0: Close CSS, 0: Close JS
+ layout: "left",
+ css_external:
+ "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" +
+ (code.includes("
+
+
+ Copy Code
+
+
+ Open in{" "}
+
+
+
+
+
+ );
+}
+
+export default CodeTab;
diff --git a/frontend/src/components/ImageUpload.tsx b/frontend/src/components/ImageUpload.tsx
index 4a46d99..6cd45b7 100644
--- a/frontend/src/components/ImageUpload.tsx
+++ b/frontend/src/components/ImageUpload.tsx
@@ -106,7 +106,9 @@ function ImageUpload({ setReferenceImages }: Props) {
// Convert images to data URLs and set the prompt images state
Promise.all(files.map((file) => fileToDataURL(file)))
.then((dataUrls) => {
- setReferenceImages(dataUrls.map((dataUrl) => dataUrl as string));
+ if (dataUrls.length > 0) {
+ setReferenceImages(dataUrls.map((dataUrl) => dataUrl as string));
+ }
})
.catch((error) => {
// TODO: Display error to user
@@ -140,7 +142,11 @@ function ImageUpload({ setReferenceImages }: Props) {
{/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
-
Drop a screenshot here, paste from clipboard, or click to select
+
+ Drag & drop a screenshot here,
+ or paste from clipboard,
+ or click to upload
+
);
diff --git a/frontend/src/components/OnboardingNote.tsx b/frontend/src/components/OnboardingNote.tsx
index 8cc6f11..acf919f 100644
--- a/frontend/src/components/OnboardingNote.tsx
+++ b/frontend/src/components/OnboardingNote.tsx
@@ -1,20 +1,25 @@
export function OnboardingNote() {
return (
-
- Please add your OpenAI API key (must have GPT4 vision access) in the
- settings dialog (gear icon above).
-
-
- How do you get an OpenAI API key that has the GPT4 Vision model available?
- Create an OpenAI account. And then, you need to buy at least $1 worth of
- credit on the Billing dashboard.
-
+
);
diff --git a/frontend/src/components/OutputSettingsSection.tsx b/frontend/src/components/OutputSettingsSection.tsx
new file mode 100644
index 0000000..cc927a5
--- /dev/null
+++ b/frontend/src/components/OutputSettingsSection.tsx
@@ -0,0 +1,201 @@
+import {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectTrigger,
+} from "./ui/select";
+import { CSSOption, JSFrameworkOption, OutputSettings } from "../types";
+import toast from "react-hot-toast";
+import { Label } from "@radix-ui/react-label";
+import { Button } from "./ui/button";
+import { Popover, PopoverTrigger, PopoverContent } from "./ui/popover";
+
+function displayCSSOption(option: CSSOption) {
+ switch (option) {
+ case CSSOption.TAILWIND:
+ return "Tailwind";
+ case CSSOption.BOOTSTRAP:
+ return "Bootstrap";
+ default:
+ return option;
+ }
+}
+
+function displayJSOption(option: JSFrameworkOption) {
+ switch (option) {
+ case JSFrameworkOption.REACT:
+ return "React";
+ case JSFrameworkOption.NO_FRAMEWORK:
+ return "No Framework";
+ default:
+ return option;
+ }
+}
+
+function convertStringToCSSOption(option: string) {
+ switch (option) {
+ case "tailwind":
+ return CSSOption.TAILWIND;
+ case "bootstrap":
+ return CSSOption.BOOTSTRAP;
+ default:
+ throw new Error(`Unknown CSS option: ${option}`);
+ }
+}
+
+function generateDisplayString(settings: OutputSettings) {
+ if (
+ settings.js === JSFrameworkOption.REACT &&
+ settings.css === CSSOption.TAILWIND
+ ) {
+ return (
+
+ Generating React +{" "}
+ Tailwind code
+
+ );
+ } else if (
+ settings.js === JSFrameworkOption.NO_FRAMEWORK &&
+ settings.css === CSSOption.TAILWIND
+ ) {
+ return (
+
+ Generating HTML +{" "}
+ Tailwind code
+
+ );
+ } else if (
+ settings.js === JSFrameworkOption.NO_FRAMEWORK &&
+ settings.css === CSSOption.BOOTSTRAP
+ ) {
+ return (
+
+ Generating HTML +{" "}
+ Bootstrap code
+
+ );
+ }
+}
+
+interface Props {
+ outputSettings: OutputSettings;
+ setOutputSettings: React.Dispatch
>;
+ shouldDisableUpdates?: boolean;
+}
+
+function OutputSettingsSection({
+ outputSettings,
+ setOutputSettings,
+ shouldDisableUpdates = false,
+}: Props) {
+ const onCSSValueChange = (value: string) => {
+ setOutputSettings((prev) => {
+ if (prev.js === JSFrameworkOption.REACT) {
+ if (value !== CSSOption.TAILWIND) {
+ toast.error(
+ 'React only supports Tailwind CSS. Change JS framework to "No Framework" to use Bootstrap.'
+ );
+ }
+ return {
+ css: CSSOption.TAILWIND,
+ js: JSFrameworkOption.REACT,
+ };
+ } else {
+ return {
+ ...prev,
+ css: convertStringToCSSOption(value),
+ };
+ }
+ });
+ };
+
+ const onJsFrameworkChange = (value: string) => {
+ if (value === JSFrameworkOption.REACT) {
+ setOutputSettings(() => ({
+ css: CSSOption.TAILWIND,
+ js: value as JSFrameworkOption,
+ }));
+ } else {
+ setOutputSettings((prev) => ({
+ ...prev,
+ js: value as JSFrameworkOption,
+ }));
+ }
+ };
+
+ return (
+
+ {generateDisplayString(outputSettings)}{" "}
+ {!shouldDisableUpdates && (
+
+
+ Customize
+
+
+
+
+
Code Settings
+
+ Customize your code output
+
+
+
+
+ JS
+
+
+ {displayJSOption(outputSettings.js)}
+
+
+
+
+ No Framework
+
+
+ React
+
+
+
+
+
+
+ CSS
+
+
+ {displayCSSOption(outputSettings.css)}
+
+
+
+
+ Tailwind
+
+
+ Bootstrap
+
+
+
+
+
+
+
+
+
+ )}
+
+ );
+}
+
+export default OutputSettingsSection;
diff --git a/frontend/src/components/PicoBadge.tsx b/frontend/src/components/PicoBadge.tsx
index 077a99d..70a4f20 100644
--- a/frontend/src/components/PicoBadge.tsx
+++ b/frontend/src/components/PicoBadge.tsx
@@ -1,12 +1,39 @@
-export function PicoBadge() {
+import { Settings } from "../types";
+
+export function PicoBadge({ settings }: { settings: Settings }) {
return (
-
-
-
+
+ feature requests?
+
+
+ {!settings.accessCode && (
+
+
+ an open source project by Pico
+
+
+ )}
+ {settings.accessCode && (
+
+
+ email support
+
+
+ )}
+ >
);
}
diff --git a/frontend/src/components/Preview.tsx b/frontend/src/components/Preview.tsx
index de0134f..24f33c0 100644
--- a/frontend/src/components/Preview.tsx
+++ b/frontend/src/components/Preview.tsx
@@ -1,3 +1,4 @@
+import { useEffect, useRef } from 'react';
import classNames from "classnames";
import useThrottle from "../hooks/useThrottle";
@@ -8,12 +9,23 @@ interface Props {
function Preview({ code, device }: Props) {
const throttledCode = useThrottle(code, 200);
+ const iframeRef = useRef(null);
+
+ useEffect(() => {
+ const iframe = iframeRef.current;
+ if (iframe && iframe.contentDocument) {
+ iframe.contentDocument.open();
+ iframe.contentDocument.write(throttledCode);
+ iframe.contentDocument.close();
+ }
+ }, [throttledCode]);
return (
);
}
diff --git a/frontend/src/components/ui/accordion.tsx b/frontend/src/components/ui/accordion.tsx
new file mode 100644
index 0000000..7e84c32
--- /dev/null
+++ b/frontend/src/components/ui/accordion.tsx
@@ -0,0 +1,55 @@
+import * as React from "react"
+import * as AccordionPrimitive from "@radix-ui/react-accordion"
+import { ChevronDownIcon } from "@radix-ui/react-icons"
+
+import { cn } from "@/lib/utils"
+
+const Accordion = AccordionPrimitive.Root
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AccordionItem.displayName = "AccordionItem"
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ svg]:rotate-180",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+))
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+ {children}
+
+))
+AccordionContent.displayName = AccordionPrimitive.Content.displayName
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
diff --git a/frontend/src/components/ui/alert-dialog.tsx b/frontend/src/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000..cc49f39
--- /dev/null
+++ b/frontend/src/components/ui/alert-dialog.tsx
@@ -0,0 +1,139 @@
+import * as React from "react"
+import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
+
+import { cn } from "@/lib/utils"
+import { buttonVariants } from "@/components/ui/button"
+
+const AlertDialog = AlertDialogPrimitive.Root
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger
+
+const AlertDialogPortal = AlertDialogPrimitive.Portal
+
+const AlertDialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName
+
+const AlertDialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+))
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName
+
+const AlertDialogHeader = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogHeader.displayName = "AlertDialogHeader"
+
+const AlertDialogFooter = ({
+ className,
+ ...props
+}: React.HTMLAttributes) => (
+
+)
+AlertDialogFooter.displayName = "AlertDialogFooter"
+
+const AlertDialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName
+
+const AlertDialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogDescription.displayName =
+ AlertDialogPrimitive.Description.displayName
+
+const AlertDialogAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName
+
+const AlertDialogCancel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel,
+}
diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx
new file mode 100644
index 0000000..e87d62b
--- /dev/null
+++ b/frontend/src/components/ui/badge.tsx
@@ -0,0 +1,36 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const badgeVariants = cva(
+ "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ destructive:
+ "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
+ outline: "text-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+export interface BadgeProps
+ extends React.HTMLAttributes,
+ VariantProps {}
+
+function Badge({ className, variant, ...props }: BadgeProps) {
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/frontend/src/components/ui/checkbox.tsx b/frontend/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..f831532
--- /dev/null
+++ b/frontend/src/components/ui/checkbox.tsx
@@ -0,0 +1,28 @@
+import * as React from "react"
+import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
+import { CheckIcon } from "@radix-ui/react-icons"
+
+import { cn } from "@/lib/utils"
+
+const Checkbox = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+))
+Checkbox.displayName = CheckboxPrimitive.Root.displayName
+
+export { Checkbox }
diff --git a/frontend/src/components/ui/popover.tsx b/frontend/src/components/ui/popover.tsx
new file mode 100644
index 0000000..bbba7e0
--- /dev/null
+++ b/frontend/src/components/ui/popover.tsx
@@ -0,0 +1,29 @@
+import * as React from "react"
+import * as PopoverPrimitive from "@radix-ui/react-popover"
+
+import { cn } from "@/lib/utils"
+
+const Popover = PopoverPrimitive.Root
+
+const PopoverTrigger = PopoverPrimitive.Trigger
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+PopoverContent.displayName = PopoverPrimitive.Content.displayName
+
+export { Popover, PopoverTrigger, PopoverContent }
diff --git a/frontend/src/components/ui/select.tsx b/frontend/src/components/ui/select.tsx
index 6d4d943..1dcfce0 100644
--- a/frontend/src/components/ui/select.tsx
+++ b/frontend/src/components/ui/select.tsx
@@ -1,24 +1,162 @@
-import * as React from "react";
-import { cn } from "@/lib/utils";
+import * as React from "react"
+import {
+ CaretSortIcon,
+ CheckIcon,
+ ChevronDownIcon,
+ ChevronUpIcon,
+} from "@radix-ui/react-icons"
+import * as SelectPrimitive from "@radix-ui/react-select"
-export interface SelectProps
- extends React.SelectHTMLAttributes {}
+import { cn } from "@/lib/utils"
-const Select = React.forwardRef(
- ({ className, ...props }, ref) => {
- return (
- ,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+ span]:line-clamp-1",
+ className
+ )}
+ {...props}
+ >
+ {children}
+
+
+
+
+))
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+))
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName
+
+const SelectContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, position = "popper", ...props }, ref) => (
+
+
+
+
- );
- }
-);
+ >
+ {children}
+
+
+
+
+))
+SelectContent.displayName = SelectPrimitive.Content.displayName
-Select.displayName = "Select";
+const SelectLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectLabel.displayName = SelectPrimitive.Label.displayName
-export { Select };
+const SelectItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+
+
+ {children}
+
+))
+SelectItem.displayName = SelectPrimitive.Item.displayName
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+}
\ No newline at end of file
diff --git a/frontend/src/config.ts b/frontend/src/config.ts
index 7a6251c..4f0de2f 100644
--- a/frontend/src/config.ts
+++ b/frontend/src/config.ts
@@ -7,3 +7,6 @@ export const WS_BACKEND_URL =
export const HTTP_BACKEND_URL =
import.meta.env.VITE_HTTP_BACKEND_URL || "http://127.0.0.1:7001";
+
+export const PICO_BACKEND_FORM_SECRET =
+ import.meta.env.VITE_PICO_BACKEND_FORM_SECRET || null;
diff --git a/frontend/src/constants.ts b/frontend/src/constants.ts
new file mode 100644
index 0000000..a5da189
--- /dev/null
+++ b/frontend/src/constants.ts
@@ -0,0 +1 @@
+export const USER_CLOSE_WEB_SOCKET_CODE = 4333;
diff --git a/frontend/src/generateCode.ts b/frontend/src/generateCode.ts
index 6fea92d..8a05fe7 100644
--- a/frontend/src/generateCode.ts
+++ b/frontend/src/generateCode.ts
@@ -1,17 +1,22 @@
import toast from "react-hot-toast";
import { WS_BACKEND_URL } from "./config";
+import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants";
const ERROR_MESSAGE =
- "Error generating code. Check the Developer Console for details. Feel free to open a Github issue";
+ "Error generating code. Check the Developer Console AND the backend logs for details. Feel free to open a Github issue.";
+
+const STOP_MESSAGE = "Code generation stopped";
export interface CodeGenerationParams {
generationType: "create" | "update";
image: string;
+ resultImage?: string;
history?: string[];
// isImageGenerationEnabled: boolean; // TODO: Merge with Settings type in types.ts
}
export function generateCode(
+ wsRef: React.MutableRefObject,
params: CodeGenerationParams,
onChange: (chunk: string) => void,
onSetCode: (code: string) => void,
@@ -22,6 +27,7 @@ export function generateCode(
console.log("Connecting to backend @ ", wsUrl);
const ws = new WebSocket(wsUrl);
+ wsRef.current = ws;
ws.addEventListener("open", () => {
ws.send(JSON.stringify(params));
@@ -40,15 +46,15 @@ export function generateCode(
toast.error(response.value);
}
});
-
ws.addEventListener("close", (event) => {
console.log("Connection closed", event.code, event.reason);
- if (event.code != 1000) {
+ if (event.code === USER_CLOSE_WEB_SOCKET_CODE) {
+ toast.success(STOP_MESSAGE);
+ } else if (event.code !== 1000) {
console.error("WebSocket error code", event);
toast.error(ERROR_MESSAGE);
- } else {
- onComplete();
}
+ onComplete();
});
ws.addEventListener("error", (error) => {
diff --git a/frontend/src/index.css b/frontend/src/index.css
index ab05eb6..9d965f3 100644
--- a/frontend/src/index.css
+++ b/frontend/src/index.css
@@ -61,9 +61,21 @@
--radius: 0.5rem;
}
+
+ body.dark {
+ background-color: black;
+ }
+
+ div[role="presentation"].dark {
+ background-color: #09090b !important;
+ }
+
+ iframe {
+ background-color: white !important;
+ }
.dark {
- --background: 222.2 84% 4.9%;
+ --background: 222.2 0% 0%;
--foreground: 210 40% 98%;
--card: 222.2 84% 4.9%;
diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts
index ec79801..deaefb5 100644
--- a/frontend/src/lib/utils.ts
+++ b/frontend/src/lib/utils.ts
@@ -1,6 +1,10 @@
-import { type ClassValue, clsx } from "clsx"
-import { twMerge } from "tailwind-merge"
-
+import { type ClassValue, clsx } from "clsx";
+import { twMerge } from "tailwind-merge";
+
export function cn(...inputs: ClassValue[]) {
- return twMerge(clsx(inputs))
+ return twMerge(clsx(inputs));
+}
+
+export function capitalize(str: string) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
}
diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx
index 6daa7bf..2e771a7 100644
--- a/frontend/src/main.tsx
+++ b/frontend/src/main.tsx
@@ -7,6 +7,6 @@ import { Toaster } from "react-hot-toast";
ReactDOM.createRoot(document.getElementById("root")!).render(
-
+
);
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 0fc532f..137f770 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -1,7 +1,36 @@
+export enum EditorTheme {
+ ESPRESSO = "espresso",
+ COBALT = "cobalt",
+}
+
+export enum CSSOption {
+ TAILWIND = "tailwind",
+ BOOTSTRAP = "bootstrap",
+}
+
+export enum JSFrameworkOption {
+ NO_FRAMEWORK = "vanilla",
+ REACT = "react",
+ VUE = "vue",
+}
+
+export interface OutputSettings {
+ css: CSSOption;
+ js: JSFrameworkOption;
+}
+
export interface Settings {
openAiApiKey: string | null;
openAiBaseURL: string | null;
screenshotOneApiKey: string | null;
isImageGenerationEnabled: boolean;
- editorTheme: string;
+ editorTheme: EditorTheme;
+ isTermOfServiceAccepted: boolean; // Only relevant for hosted version
+ accessCode: string | null; // Only relevant for hosted version
+}
+
+export enum AppState {
+ INITIAL = "INITIAL",
+ CODING = "CODING",
+ CODE_READY = "CODE_READY",
}
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index 3e7414d..f9b5353 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -1,15 +1,31 @@
import path from "path";
-import { defineConfig } from "vite";
+import { defineConfig, loadEnv } from "vite";
import checker from "vite-plugin-checker";
import react from "@vitejs/plugin-react";
+import { createHtmlPlugin } from "vite-plugin-html";
// https://vitejs.dev/config/
-export default defineConfig({
- base: process.env.VITE_IS_DEPLOYED ? "/free-tools/screenshot-to-code/" : "",
- plugins: [react(), checker({ typescript: true })],
- resolve: {
- alias: {
- "@": path.resolve(__dirname, "./src"),
+export default ({ mode }) => {
+ process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
+ return defineConfig({
+ base: "",
+ plugins: [
+ react(),
+ checker({ typescript: true }),
+ createHtmlPlugin({
+ inject: {
+ data: {
+ injectHead: process.env.VITE_IS_DEPLOYED
+ ? ''
+ : "",
+ },
+ },
+ }),
+ ],
+ resolve: {
+ alias: {
+ "@": path.resolve(__dirname, "./src"),
+ },
},
- },
-});
+ });
+};
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 2109c60..16ffdbe 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -470,6 +470,33 @@
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz"
integrity sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==
+"@floating-ui/core@^1.4.2":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@floating-ui/core/-/core-1.5.0.tgz#5c05c60d5ae2d05101c3021c1a2a350ddc027f8c"
+ integrity sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==
+ dependencies:
+ "@floating-ui/utils" "^0.1.3"
+
+"@floating-ui/dom@^1.5.1":
+ version "1.5.3"
+ resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.5.3.tgz#54e50efcb432c06c23cd33de2b575102005436fa"
+ integrity sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==
+ dependencies:
+ "@floating-ui/core" "^1.4.2"
+ "@floating-ui/utils" "^0.1.3"
+
+"@floating-ui/react-dom@^2.0.0":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.0.4.tgz#b076fafbdfeb881e1d86ae748b7ff95150e9f3ec"
+ integrity sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==
+ dependencies:
+ "@floating-ui/dom" "^1.5.1"
+
+"@floating-ui/utils@^0.1.3":
+ version "0.1.6"
+ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.1.6.tgz#22958c042e10b67463997bd6ea7115fe28cbcaf9"
+ integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==
+
"@humanwhocodes/config-array@^0.11.13":
version "0.11.13"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz"
@@ -508,6 +535,14 @@
resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz"
integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==
+"@jridgewell/source-map@^0.3.3":
+ version "0.3.5"
+ resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91"
+ integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==
+ dependencies:
+ "@jridgewell/gen-mapping" "^0.3.0"
+ "@jridgewell/trace-mapping" "^0.3.9"
+
"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
version "1.4.15"
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
@@ -586,6 +621,13 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
+"@radix-ui/number@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.0.1.tgz#644161a3557f46ed38a042acf4a770e826021674"
+ integrity sha512-T5gIdVO2mmPW3NNhjNgEP3cqMXjXL9UbO0BzWcXfvdBs+BohbQxvd/K5hSVKmn9/lbTdsQVKbUcP5WLCwvUbBg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
"@radix-ui/primitive@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
@@ -593,6 +635,73 @@
dependencies:
"@babel/runtime" "^7.13.10"
+"@radix-ui/react-accordion@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.1.2.tgz#738441f7343e5142273cdef94d12054c3287966f"
+ integrity sha512-fDG7jcoNKVjSK6yfmuAs0EnPDro0WMXIhMtXdTBWqEioVW206ku+4Lw07e+13lUkFkpoEQ2PdeMIAGpdqEAmDg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-collapsible" "1.0.3"
+ "@radix-ui/react-collection" "1.0.3"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-direction" "1.0.1"
+ "@radix-ui/react-id" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+
+"@radix-ui/react-alert-dialog@^1.0.5":
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz#70dd529cbf1e4bff386814d3776901fcaa131b8c"
+ integrity sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-dialog" "1.0.5"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-slot" "1.0.2"
+
+"@radix-ui/react-arrow@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d"
+ integrity sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-primitive" "1.0.3"
+
+"@radix-ui/react-checkbox@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b"
+ integrity sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-presence" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+ "@radix-ui/react-use-previous" "1.0.1"
+ "@radix-ui/react-use-size" "1.0.1"
+
+"@radix-ui/react-collapsible@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.0.3.tgz#df0e22e7a025439f13f62d4e4a9e92c4a0df5b81"
+ integrity sha512-UBmVDkmR6IvDsloHVN+3rtx4Mi5TFvylYXpluuv0f37dtaz3H99bp8No0LGXRigVpl3UAT4l9j6bIchh42S/Gg==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-id" "1.0.1"
+ "@radix-ui/react-presence" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+ "@radix-ui/react-use-layout-effect" "1.0.1"
+
"@radix-ui/react-collection@1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
@@ -618,7 +727,7 @@
dependencies:
"@babel/runtime" "^7.13.10"
-"@radix-ui/react-dialog@^1.0.5":
+"@radix-ui/react-dialog@1.0.5", "@radix-ui/react-dialog@^1.0.5":
version "1.0.5"
resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.5.tgz#71657b1b116de6c7a0b03242d7d43e01062c7300"
integrity sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==
@@ -696,6 +805,45 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-primitive" "1.0.3"
+"@radix-ui/react-popover@^1.0.7":
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popover/-/react-popover-1.0.7.tgz#23eb7e3327330cb75ec7b4092d685398c1654e3c"
+ integrity sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-dismissable-layer" "1.0.5"
+ "@radix-ui/react-focus-guards" "1.0.1"
+ "@radix-ui/react-focus-scope" "1.0.4"
+ "@radix-ui/react-id" "1.0.1"
+ "@radix-ui/react-popper" "1.1.3"
+ "@radix-ui/react-portal" "1.0.4"
+ "@radix-ui/react-presence" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-slot" "1.0.2"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+ aria-hidden "^1.1.1"
+ react-remove-scroll "2.5.5"
+
+"@radix-ui/react-popper@1.1.3":
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.3.tgz#24c03f527e7ac348fabf18c89795d85d21b00b42"
+ integrity sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@floating-ui/react-dom" "^2.0.0"
+ "@radix-ui/react-arrow" "1.0.3"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-use-callback-ref" "1.0.1"
+ "@radix-ui/react-use-layout-effect" "1.0.1"
+ "@radix-ui/react-use-rect" "1.0.1"
+ "@radix-ui/react-use-size" "1.0.1"
+ "@radix-ui/rect" "1.0.1"
+
"@radix-ui/react-portal@1.0.4":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.4.tgz#df4bfd353db3b1e84e639e9c63a5f2565fb00e15"
@@ -737,6 +885,34 @@
"@radix-ui/react-use-callback-ref" "1.0.1"
"@radix-ui/react-use-controllable-state" "1.0.1"
+"@radix-ui/react-select@^2.0.0":
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-2.0.0.tgz#a3511792a51a7018d6559357323a7f52e0e38887"
+ integrity sha512-RH5b7af4oHtkcHS7pG6Sgv5rk5Wxa7XI8W5gvB1N/yiuDGZxko1ynvOiVhFM7Cis2A8zxF9bTOUVbRDzPepe6w==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/number" "1.0.1"
+ "@radix-ui/primitive" "1.0.1"
+ "@radix-ui/react-collection" "1.0.3"
+ "@radix-ui/react-compose-refs" "1.0.1"
+ "@radix-ui/react-context" "1.0.1"
+ "@radix-ui/react-direction" "1.0.1"
+ "@radix-ui/react-dismissable-layer" "1.0.5"
+ "@radix-ui/react-focus-guards" "1.0.1"
+ "@radix-ui/react-focus-scope" "1.0.4"
+ "@radix-ui/react-id" "1.0.1"
+ "@radix-ui/react-popper" "1.1.3"
+ "@radix-ui/react-portal" "1.0.4"
+ "@radix-ui/react-primitive" "1.0.3"
+ "@radix-ui/react-slot" "1.0.2"
+ "@radix-ui/react-use-callback-ref" "1.0.1"
+ "@radix-ui/react-use-controllable-state" "1.0.1"
+ "@radix-ui/react-use-layout-effect" "1.0.1"
+ "@radix-ui/react-use-previous" "1.0.1"
+ "@radix-ui/react-visually-hidden" "1.0.3"
+ aria-hidden "^1.1.1"
+ react-remove-scroll "2.5.5"
+
"@radix-ui/react-separator@^1.0.3":
version "1.0.3"
resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.3.tgz#be5a931a543d5726336b112f465f58585c04c8aa"
@@ -819,6 +995,14 @@
dependencies:
"@babel/runtime" "^7.13.10"
+"@radix-ui/react-use-rect@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-use-rect/-/react-use-rect-1.0.1.tgz#fde50b3bb9fd08f4a1cd204572e5943c244fcec2"
+ integrity sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/rect" "1.0.1"
+
"@radix-ui/react-use-size@1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
@@ -827,6 +1011,29 @@
"@babel/runtime" "^7.13.10"
"@radix-ui/react-use-layout-effect" "1.0.1"
+"@radix-ui/react-visually-hidden@1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.0.3.tgz#51aed9dd0fe5abcad7dee2a234ad36106a6984ac"
+ integrity sha512-D4w41yN5YRKtu464TLnByKzMDG/JlMPHtfZgQAu9v6mNakUqGUI9vUrfQKz8NK41VMm/xbZbh76NUTVtIYqOMA==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+ "@radix-ui/react-primitive" "1.0.3"
+
+"@radix-ui/rect@1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@radix-ui/rect/-/rect-1.0.1.tgz#bf8e7d947671996da2e30f4904ece343bc4a883f"
+ integrity sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==
+ dependencies:
+ "@babel/runtime" "^7.13.10"
+
+"@rollup/pluginutils@^4.2.0":
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
+ integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==
+ dependencies:
+ estree-walker "^2.0.1"
+ picomatch "^2.2.2"
+
"@types/babel__core@^7.20.3":
version "7.20.4"
resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz"
@@ -1009,7 +1216,7 @@ acorn-jsx@^5.3.2:
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz"
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
-acorn@^8.9.0:
+acorn@^8.8.2, acorn@^8.9.0:
version "8.11.2"
resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz"
integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
@@ -1085,6 +1292,11 @@ array-union@^2.1.0:
resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz"
integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
+async@^3.2.3:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66"
+ integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==
+
attr-accept@^2.2.2:
version "2.2.2"
resolved "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz"
@@ -1107,11 +1319,21 @@ balanced-match@^1.0.0:
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+base64-arraybuffer@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
+ integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
+
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+boolbase@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
+ integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
+
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
@@ -1120,6 +1342,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz"
@@ -1137,11 +1366,24 @@ browserslist@^4.21.10, browserslist@^4.21.9:
node-releases "^2.0.13"
update-browserslist-db "^1.0.13"
+buffer-from@^1.0.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
+ integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
+
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+camel-case@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a"
+ integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==
+ dependencies:
+ pascal-case "^3.1.2"
+ tslib "^2.0.3"
+
camelcase-css@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz"
@@ -1161,7 +1403,7 @@ chalk@^2.4.2:
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
-chalk@^4.0.0, chalk@^4.1.1:
+chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.1:
version "4.1.2"
resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz"
integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
@@ -1196,6 +1438,13 @@ classnames@^2.3.2:
resolved "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz"
integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==
+clean-css@^5.2.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.2.tgz#70ecc7d4d4114921f5d298349ff86a31a9975224"
+ integrity sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==
+ dependencies:
+ source-map "~0.6.0"
+
clsx@2.0.0, clsx@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b"
@@ -1238,12 +1487,22 @@ color-name@~1.1.4:
resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+colorette@^2.0.16:
+ version "2.0.20"
+ resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
+ integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
+
+commander@^2.20.0:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
commander@^4.0.0:
version "4.1.1"
resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
-commander@^8.0.0:
+commander@^8.0.0, commander@^8.3.0:
version "8.3.0"
resolved "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==
@@ -1253,11 +1512,28 @@ concat-map@0.0.1:
resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
+connect-history-api-fallback@^1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc"
+ integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==
+
+consola@^2.15.3:
+ version "2.15.3"
+ resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550"
+ integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==
+
convert-source-map@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz"
integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==
+copy-to-clipboard@^3.3.3:
+ version "3.3.3"
+ resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0"
+ integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==
+ dependencies:
+ toggle-selection "^1.0.6"
+
crelt@^1.0.5:
version "1.0.6"
resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.6.tgz#7cc898ea74e190fb6ef9dae57f8f81cf7302df72"
@@ -1272,6 +1548,29 @@ cross-spawn@^7.0.2:
shebang-command "^2.0.0"
which "^2.0.1"
+css-line-break@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz#bfef660dfa6f5397ea54116bb3cb4873edbc4fa0"
+ integrity sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==
+ dependencies:
+ utrie "^1.0.2"
+
+css-select@^4.2.1:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b"
+ integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==
+ dependencies:
+ boolbase "^1.0.0"
+ css-what "^6.0.1"
+ domhandler "^4.3.1"
+ domutils "^2.8.0"
+ nth-check "^2.0.1"
+
+css-what@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
+ integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
+
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
@@ -1323,11 +1622,71 @@ doctrine@^3.0.0:
dependencies:
esutils "^2.0.2"
+dom-serializer@^1.0.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
+ integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
+ dependencies:
+ domelementtype "^2.0.1"
+ domhandler "^4.2.0"
+ entities "^2.0.0"
+
+domelementtype@^2.0.1, domelementtype@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
+ integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
+
+domhandler@^4.2.0, domhandler@^4.3.1:
+ version "4.3.1"
+ resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
+ integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
+ dependencies:
+ domelementtype "^2.2.0"
+
+domutils@^2.8.0:
+ version "2.8.0"
+ resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
+ integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
+ dependencies:
+ dom-serializer "^1.0.1"
+ domelementtype "^2.2.0"
+ domhandler "^4.2.0"
+
+dot-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751"
+ integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==
+ dependencies:
+ no-case "^3.0.4"
+ tslib "^2.0.3"
+
+dotenv-expand@^8.0.2:
+ version "8.0.3"
+ resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-8.0.3.tgz#29016757455bcc748469c83a19b36aaf2b83dd6e"
+ integrity sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==
+
+dotenv@^16.0.0:
+ version "16.3.1"
+ resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
+ integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
+
+ejs@^3.1.6:
+ version "3.1.9"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.9.tgz#03c9e8777fe12686a9effcef22303ca3d8eeb361"
+ integrity sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==
+ dependencies:
+ jake "^10.8.5"
+
electron-to-chromium@^1.4.535:
version "1.4.582"
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.582.tgz"
integrity sha512-89o0MGoocwYbzqUUjc+VNpeOFSOK9nIdC5wY4N+PVUarUK0MtjyTjks75AZS2bW4Kl8MdewdFsWaH0jLy+JNoA==
+entities@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
+ integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
+
esbuild@^0.18.10:
version "0.18.20"
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.18.20.tgz"
@@ -1466,6 +1825,11 @@ estraverse@^5.1.0, estraverse@^5.2.0:
resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz"
integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+estree-walker@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+ integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
esutils@^2.0.2:
version "2.0.3"
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
@@ -1476,7 +1840,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.0:
+fast-glob@^3.2.11, fast-glob@^3.2.7, fast-glob@^3.2.9, fast-glob@^3.3.0:
version "3.3.2"
resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz"
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
@@ -1518,6 +1882,13 @@ file-selector@^0.6.0:
dependencies:
tslib "^2.4.0"
+filelist@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5"
+ integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==
+ dependencies:
+ minimatch "^5.0.1"
+
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz"
@@ -1552,6 +1923,15 @@ fraction.js@^4.3.6:
resolved "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz"
integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==
+fs-extra@^10.0.1:
+ version "10.1.0"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf"
+ integrity sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
fs-extra@^11.1.0:
version "11.1.1"
resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz"
@@ -1680,6 +2060,32 @@ hasown@^2.0.0:
dependencies:
function-bind "^1.1.2"
+he@1.2.0, he@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+ integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+html-minifier-terser@^6.1.0:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#bfc818934cc07918f6b3669f5774ecdfd48f32ab"
+ integrity sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==
+ dependencies:
+ camel-case "^4.1.2"
+ clean-css "^5.2.2"
+ commander "^8.3.0"
+ he "^1.2.0"
+ param-case "^3.0.4"
+ relateurl "^0.2.7"
+ terser "^5.10.0"
+
+html2canvas@^1.4.1:
+ version "1.4.1"
+ resolved "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz#7cef1888311b5011d507794a066041b14669a543"
+ integrity sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==
+ dependencies:
+ css-line-break "^2.1.0"
+ text-segmentation "^1.0.3"
+
ignore@^5.2.0, ignore@^5.2.4:
version "5.2.4"
resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz"
@@ -1759,6 +2165,16 @@ isexe@^2.0.0:
resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz"
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
+jake@^10.8.5:
+ version "10.8.7"
+ resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.7.tgz#63a32821177940c33f356e0ba44ff9d34e1c7d8f"
+ integrity sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==
+ dependencies:
+ async "^3.2.3"
+ chalk "^4.0.2"
+ filelist "^1.0.4"
+ minimatch "^3.1.2"
+
jiti@^1.19.1:
version "1.21.0"
resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz"
@@ -1864,6 +2280,13 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
dependencies:
js-tokens "^3.0.0 || ^4.0.0"
+lower-case@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28"
+ integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==
+ dependencies:
+ tslib "^2.0.3"
+
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz"
@@ -1898,6 +2321,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
+minimatch@^5.0.1:
+ version "5.1.6"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+ integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
+ dependencies:
+ brace-expansion "^2.0.1"
+
ms@2.1.2:
version "2.1.2"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
@@ -1922,6 +2352,22 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
+no-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d"
+ integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==
+ dependencies:
+ lower-case "^2.0.2"
+ tslib "^2.0.3"
+
+node-html-parser@^5.3.3:
+ version "5.4.2"
+ resolved "https://registry.yarnpkg.com/node-html-parser/-/node-html-parser-5.4.2.tgz#93e004038c17af80226c942336990a0eaed8136a"
+ integrity sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==
+ dependencies:
+ css-select "^4.2.1"
+ he "1.2.0"
+
node-releases@^2.0.13:
version "2.0.13"
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz"
@@ -1944,6 +2390,13 @@ npm-run-path@^4.0.1:
dependencies:
path-key "^3.0.0"
+nth-check@^2.0.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d"
+ integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==
+ dependencies:
+ boolbase "^1.0.0"
+
object-assign@^4.0.1, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
@@ -1987,6 +2440,14 @@ p-locate@^5.0.0:
dependencies:
p-limit "^3.0.2"
+param-case@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5"
+ integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==
+ dependencies:
+ dot-case "^3.0.4"
+ tslib "^2.0.3"
+
parent-module@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz"
@@ -1994,6 +2455,14 @@ parent-module@^1.0.0:
dependencies:
callsites "^3.0.0"
+pascal-case@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb"
+ integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==
+ dependencies:
+ no-case "^3.0.4"
+ tslib "^2.0.3"
+
path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz"
@@ -2019,12 +2488,17 @@ path-type@^4.0.0:
resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
+pathe@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/pathe/-/pathe-0.2.0.tgz#30fd7bbe0a0d91f0e60bae621f5d19e9e225c339"
+ integrity sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==
+
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
@@ -2209,6 +2683,11 @@ regenerator-runtime@^0.14.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
+relateurl@^0.2.7:
+ version "0.2.7"
+ resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+ integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==
+
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
@@ -2290,6 +2769,19 @@ source-map-js@^1.0.2:
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+source-map-support@~0.5.20:
+ version "0.5.21"
+ resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
+ integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
+ dependencies:
+ buffer-from "^1.0.0"
+ source-map "^0.6.0"
+
+source-map@^0.6.0, source-map@~0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
@@ -2379,6 +2871,23 @@ tailwindcss@^3.3.5:
resolve "^1.22.2"
sucrase "^3.32.0"
+terser@^5.10.0:
+ version "5.24.0"
+ resolved "https://registry.yarnpkg.com/terser/-/terser-5.24.0.tgz#4ae50302977bca4831ccc7b4fef63a3c04228364"
+ integrity sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==
+ dependencies:
+ "@jridgewell/source-map" "^0.3.3"
+ acorn "^8.8.2"
+ commander "^2.20.0"
+ source-map-support "~0.5.20"
+
+text-segmentation@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz#52a388159efffe746b24a63ba311b6ac9f2d7943"
+ integrity sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==
+ dependencies:
+ utrie "^1.0.2"
+
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
@@ -2420,6 +2929,11 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
+toggle-selection@^1.0.6:
+ version "1.0.6"
+ resolved "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
+ integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==
+
ts-api-utils@^1.0.1:
version "1.0.3"
resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz"
@@ -2430,7 +2944,7 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
-tslib@^2.0.0, tslib@^2.1.0, tslib@^2.4.0:
+tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.4.0:
version "2.6.2"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@@ -2502,6 +3016,13 @@ util-deprecate@^1.0.2:
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
+utrie@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz#d42fe44de9bc0119c25de7f564a6ed1b2c87a645"
+ integrity sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==
+ dependencies:
+ base64-arraybuffer "^1.0.2"
+
vite-plugin-checker@^0.6.2:
version "0.6.2"
resolved "https://registry.npmjs.org/vite-plugin-checker/-/vite-plugin-checker-0.6.2.tgz"
@@ -2525,6 +3046,24 @@ vite-plugin-checker@^0.6.2:
vscode-languageserver-textdocument "^1.0.1"
vscode-uri "^3.0.2"
+vite-plugin-html@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/vite-plugin-html/-/vite-plugin-html-3.2.0.tgz#0d4df9900642a321a139f1c25c05195ba9d0ec79"
+ integrity sha512-2VLCeDiHmV/BqqNn5h2V+4280KRgQzCFN47cst3WiNK848klESPQnzuC3okH5XHtgwHH/6s1Ho/YV6yIO0pgoQ==
+ dependencies:
+ "@rollup/pluginutils" "^4.2.0"
+ colorette "^2.0.16"
+ connect-history-api-fallback "^1.6.0"
+ consola "^2.15.3"
+ dotenv "^16.0.0"
+ dotenv-expand "^8.0.2"
+ ejs "^3.1.6"
+ fast-glob "^3.2.11"
+ fs-extra "^10.0.1"
+ html-minifier-terser "^6.1.0"
+ node-html-parser "^5.3.3"
+ pathe "^0.2.0"
+
vite@^4.4.5:
version "4.5.0"
resolved "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz"
diff --git a/sweep.yaml b/sweep.yaml
new file mode 100644
index 0000000..ada0e25
--- /dev/null
+++ b/sweep.yaml
@@ -0,0 +1,42 @@
+# Sweep AI turns bugs & feature requests into code changes (https://sweep.dev)
+# For details on our config file, check out our docs at https://docs.sweep.dev/usage/config
+
+# This setting contains a list of rules that Sweep will check for. If any of these rules are broken in a new commit, Sweep will create an pull request to fix the broken rule.
+rules:
+ - "All docstrings and comments should be up to date."
+['All new business logic should have corresponding unit tests.', 'Refactor large functions to be more modular.', 'Add docstrings to all functions and file headers.']
+
+# This is the branch that Sweep will develop from and make pull requests to. Most people use 'main' or 'master' but some users also use 'dev' or 'staging'.
+branch: 'main'
+
+# By default Sweep will read the logs and outputs from your existing Github Actions. To disable this, set this to false.
+gha_enabled: True
+
+# This is the description of your project. It will be used by sweep when creating PRs. You can tell Sweep what's unique about your project, what frameworks you use, or anything else you want.
+#
+# Example:
+#
+# description: sweepai/sweep is a python project. The main api endpoints are in sweepai/api.py. Write code that adheres to PEP8.
+description: ''
+
+# This sets whether to create pull requests as drafts. If this is set to True, then all pull requests will be created as drafts and GitHub Actions will not be triggered.
+draft: False
+
+# This is a list of directories that Sweep will not be able to edit.
+blocked_dirs: []
+
+# This is a list of documentation links that Sweep will use to help it understand your code. You can add links to documentation for any packages you use here.
+#
+# Example:
+#
+# docs:
+# - PyGitHub: ["https://pygithub.readthedocs.io/en/latest/", "We use pygithub to interact with the GitHub API"]
+docs: []
+
+# Sandbox executes commands in a sandboxed environment to validate code changes after every edit to guarantee pristine code. For more details, see the [Sandbox](./sandbox) page.
+sandbox:
+ install:
+ - trunk init
+ check:
+ - trunk fmt {file_path} || return 0
+ - trunk check --fix --print-failures {file_path}