Merge branch 'main' into main
This commit is contained in:
commit
7a3b36621b
@ -1,6 +1,6 @@
|
|||||||
# screenshot-to-code
|
# 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. You can now also enter a URL to clone a live website!
|
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
|
https://github.com/abi/screenshot-to-code/assets/23818/6cebadae-2fe3-4986-ac6a-8fb9db030045
|
||||||
|
|
||||||
@ -8,17 +8,18 @@ See the [Examples](#examples) section below for more demos.
|
|||||||
|
|
||||||
## 🚀 Try It Out!
|
## 🚀 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
|
## 🌟 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 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 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 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 [@kachbit](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 - Added a setting to disable DALL-E image generation if you don't need that
|
||||||
- Nov 16 - View code directly within the app
|
- 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
|
## 🛠 Getting Started
|
||||||
|
|
||||||
@ -87,6 +88,6 @@ https://github.com/abi/screenshot-to-code/assets/23818/3fec0f77-44e8-4fb3-a769-a
|
|||||||
|
|
||||||
## 🌍 Hosted Version
|
## 🌍 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)
|
[](https://www.buymeacoffee.com/abiraja)
|
||||||
|
|||||||
3
backend/README.md
Normal file
3
backend/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Run tests
|
||||||
|
|
||||||
|
pytest test_prompts.py
|
||||||
@ -69,6 +69,14 @@ async def stream_code(websocket: WebSocket):
|
|||||||
|
|
||||||
print("Received params")
|
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.
|
# 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 neither is provided, we throw an error.
|
||||||
if params["openAiApiKey"]:
|
if params["openAiApiKey"]:
|
||||||
@ -102,9 +110,11 @@ async def stream_code(websocket: WebSocket):
|
|||||||
await websocket.send_json({"type": "chunk", "value": content})
|
await websocket.send_json({"type": "chunk", "value": content})
|
||||||
|
|
||||||
if params.get("resultImage") and params["resultImage"]:
|
if params.get("resultImage") and params["resultImage"]:
|
||||||
prompt_messages = assemble_prompt(params["image"], params["resultImage"])
|
prompt_messages = assemble_prompt(
|
||||||
|
params["image"], output_settings, params["resultImage"]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
prompt_messages = assemble_prompt(params["image"])
|
prompt_messages = assemble_prompt(params["image"], output_settings)
|
||||||
|
|
||||||
# Image cache for updates so that we don't have to regenerate images
|
# Image cache for updates so that we don't have to regenerate images
|
||||||
image_cache = {}
|
image_cache = {}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
SYSTEM_PROMPT = """
|
TAILWIND_SYSTEM_PROMPT = """
|
||||||
You are an expert Tailwind developer
|
You are an expert Tailwind developer
|
||||||
You take screenshots of a reference web page from the user, and then build single page apps
|
You take screenshots of a reference web page from the user, and then build single page apps
|
||||||
using Tailwind, HTML and JS.
|
using Tailwind, HTML and JS.
|
||||||
@ -23,12 +23,79 @@ Return only the full code in <html></html> tags.
|
|||||||
Do not include markdown "```" or "```html" at the start or end.
|
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 "<!-- Add other navigation links as needed -->" and "<!-- ... other news items ... -->" 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 "<!-- Repeat for each news item -->" 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: <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||||
|
- You can use Google Fonts
|
||||||
|
- Font Awesome for icons: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"></link>
|
||||||
|
|
||||||
|
Return only the full code in <html></html> 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 "<!-- Add other navigation links as needed -->" and "<!-- ... other news items ... -->" 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 "<!-- Repeat for each news item -->" 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:
|
||||||
|
<script src="https://unpkg.com/react/umd/react.development.js"></script>
|
||||||
|
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
|
||||||
|
<script src="https://unpkg.com/@babel/standalone/babel.js"></script>
|
||||||
|
- Use this script to include Tailwind: <script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
- You can use Google Fonts
|
||||||
|
- Font Awesome for icons: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"></link>
|
||||||
|
|
||||||
|
Return only the full code in <html></html> tags.
|
||||||
|
Do not include markdown "```" or "```html" at the start or end.
|
||||||
|
"""
|
||||||
|
|
||||||
USER_PROMPT = """
|
USER_PROMPT = """
|
||||||
Generate code for a web page that looks exactly like this.
|
Generate code for a web page that looks exactly like this.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def assemble_prompt(image_data_url, result_image_data_url=None):
|
|
||||||
content = [
|
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 = [
|
||||||
{
|
{
|
||||||
"type": "image_url",
|
"type": "image_url",
|
||||||
"image_url": {"url": image_data_url, "detail": "high"},
|
"image_url": {"url": image_data_url, "detail": "high"},
|
||||||
@ -38,15 +105,23 @@ def assemble_prompt(image_data_url, result_image_data_url=None):
|
|||||||
"text": USER_PROMPT,
|
"text": USER_PROMPT,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Include the result image if it exists
|
||||||
if result_image_data_url:
|
if result_image_data_url:
|
||||||
content.insert(1, {
|
user_content.insert(
|
||||||
|
1,
|
||||||
|
{
|
||||||
"type": "image_url",
|
"type": "image_url",
|
||||||
"image_url": {"url": result_image_data_url, "detail": "high"},
|
"image_url": {"url": result_image_data_url, "detail": "high"},
|
||||||
})
|
},
|
||||||
|
)
|
||||||
return [
|
return [
|
||||||
{"role": "system", "content": SYSTEM_PROMPT},
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": system_content,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"role": "user",
|
"role": "user",
|
||||||
"content": content,
|
"content": user_content,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
97
backend/test_prompts.py
Normal file
97
backend/test_prompts.py
Normal file
@ -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 "<!-- Add other navigation links as needed -->" and "<!-- ... other news items ... -->" 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 "<!-- Repeat for each news item -->" 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: <script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
- You can use Google Fonts
|
||||||
|
- Font Awesome for icons: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"></link>
|
||||||
|
|
||||||
|
Return only the full code in <html></html> 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 "<!-- Add other navigation links as needed -->" and "<!-- ... other news items ... -->" 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 "<!-- Repeat for each news item -->" 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: <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||||
|
- You can use Google Fonts
|
||||||
|
- Font Awesome for icons: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"></link>
|
||||||
|
|
||||||
|
Return only the full code in <html></html> 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 "<!-- Add other navigation links as needed -->" and "<!-- ... other news items ... -->" 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 "<!-- Repeat for each news item -->" 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:
|
||||||
|
<script src="https://unpkg.com/react/umd/react.development.js"></script>
|
||||||
|
<script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script>
|
||||||
|
<script src="https://unpkg.com/@babel/standalone/babel.js"></script>
|
||||||
|
- Use this script to include Tailwind: <script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
- You can use Google Fonts
|
||||||
|
- Font Awesome for icons: <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"></link>
|
||||||
|
|
||||||
|
Return only the full code in <html></html> 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
|
||||||
@ -13,11 +13,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@codemirror/lang-html": "^6.4.6",
|
"@codemirror/lang-html": "^6.4.6",
|
||||||
|
"@radix-ui/react-accordion": "^1.1.2",
|
||||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-checkbox": "^1.0.4",
|
"@radix-ui/react-checkbox": "^1.0.4",
|
||||||
"@radix-ui/react-dialog": "^1.0.5",
|
"@radix-ui/react-dialog": "^1.0.5",
|
||||||
"@radix-ui/react-icons": "^1.3.0",
|
"@radix-ui/react-icons": "^1.3.0",
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
|
"@radix-ui/react-select": "^2.0.0",
|
||||||
"@radix-ui/react-separator": "^1.0.3",
|
"@radix-ui/react-separator": "^1.0.3",
|
||||||
"@radix-ui/react-slot": "^1.0.2",
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
"@radix-ui/react-switch": "^1.0.3",
|
"@radix-ui/react-switch": "^1.0.3",
|
||||||
|
|||||||
@ -18,7 +18,14 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Textarea } from "@/components/ui/textarea";
|
import { Textarea } from "@/components/ui/textarea";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "./components/ui/tabs";
|
||||||
import SettingsDialog from "./components/SettingsDialog";
|
import SettingsDialog from "./components/SettingsDialog";
|
||||||
import { Settings, EditorTheme, AppState } from "./types";
|
import {
|
||||||
|
Settings,
|
||||||
|
EditorTheme,
|
||||||
|
AppState,
|
||||||
|
CSSOption,
|
||||||
|
OutputSettings,
|
||||||
|
JSFrameworkOption,
|
||||||
|
} from "./types";
|
||||||
import { IS_RUNNING_ON_CLOUD } from "./config";
|
import { IS_RUNNING_ON_CLOUD } from "./config";
|
||||||
import { PicoBadge } from "./components/PicoBadge";
|
import { PicoBadge } from "./components/PicoBadge";
|
||||||
import { OnboardingNote } from "./components/OnboardingNote";
|
import { OnboardingNote } from "./components/OnboardingNote";
|
||||||
@ -28,6 +35,7 @@ import TermsOfServiceDialog from "./components/TermsOfServiceDialog";
|
|||||||
import html2canvas from "html2canvas";
|
import html2canvas from "html2canvas";
|
||||||
import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants";
|
import { USER_CLOSE_WEB_SOCKET_CODE } from "./constants";
|
||||||
import CodeTab from "./components/CodeTab";
|
import CodeTab from "./components/CodeTab";
|
||||||
|
import OutputSettingsSection from "./components/OutputSettingsSection";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [appState, setAppState] = useState<AppState>(AppState.INITIAL);
|
const [appState, setAppState] = useState<AppState>(AppState.INITIAL);
|
||||||
@ -46,8 +54,13 @@ function App() {
|
|||||||
},
|
},
|
||||||
"setting"
|
"setting"
|
||||||
);
|
);
|
||||||
|
const [outputSettings, setOutputSettings] = useState<OutputSettings>({
|
||||||
|
css: CSSOption.TAILWIND,
|
||||||
|
js: JSFrameworkOption.VANILLA,
|
||||||
|
});
|
||||||
const [shouldIncludeResultImage, setShouldIncludeResultImage] =
|
const [shouldIncludeResultImage, setShouldIncludeResultImage] =
|
||||||
useState<boolean>(false);
|
useState<boolean>(false);
|
||||||
|
|
||||||
const wsRef = useRef<WebSocket>(null);
|
const wsRef = useRef<WebSocket>(null);
|
||||||
|
|
||||||
const takeScreenshot = async (): Promise<string> => {
|
const takeScreenshot = async (): Promise<string> => {
|
||||||
@ -99,7 +112,7 @@ function App() {
|
|||||||
setAppState(AppState.CODING);
|
setAppState(AppState.CODING);
|
||||||
|
|
||||||
// Merge settings with params
|
// Merge settings with params
|
||||||
const updatedParams = { ...params, ...settings };
|
const updatedParams = { ...params, ...settings, outputSettings };
|
||||||
|
|
||||||
generateCode(
|
generateCode(
|
||||||
wsRef,
|
wsRef,
|
||||||
@ -178,6 +191,13 @@ function App() {
|
|||||||
</h2>
|
</h2>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{appState === AppState.INITIAL && (
|
||||||
|
<OutputSettingsSection
|
||||||
|
outputSettings={outputSettings}
|
||||||
|
setOutputSettings={setOutputSettings}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{IS_RUNNING_ON_CLOUD && !settings.openAiApiKey && <OnboardingNote />}
|
{IS_RUNNING_ON_CLOUD && !settings.openAiApiKey && <OnboardingNote />}
|
||||||
|
|
||||||
{(appState === AppState.CODING ||
|
{(appState === AppState.CODING ||
|
||||||
|
|||||||
@ -22,7 +22,9 @@ interface Props {
|
|||||||
function CodeMirror({ code, editorTheme, onCodeChange }: Props) {
|
function CodeMirror({ code, editorTheme, onCodeChange }: Props) {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const view = useRef<EditorView | null>(null);
|
const view = useRef<EditorView | null>(null);
|
||||||
const editorState = useMemo(() => EditorState.create({
|
const editorState = useMemo(
|
||||||
|
() =>
|
||||||
|
EditorState.create({
|
||||||
extensions: [
|
extensions: [
|
||||||
history(),
|
history(),
|
||||||
keymap.of([
|
keymap.of([
|
||||||
@ -43,7 +45,9 @@ function CodeMirror({ code, editorTheme, onCodeChange }: Props) {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
}), [editorTheme]);
|
}),
|
||||||
|
[editorTheme]
|
||||||
|
);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
view.current = new EditorView({
|
view.current = new EditorView({
|
||||||
state: editorState,
|
state: editorState,
|
||||||
@ -67,9 +71,11 @@ function CodeMirror({ code, editorTheme, onCodeChange }: Props) {
|
|||||||
}, [code]);
|
}, [code]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="overflow-x-scroll overflow-y-scroll mx-2 border-[4px] border-black rounded-[20px]" ref={ref} />
|
<div
|
||||||
|
className="overflow-x-scroll overflow-y-scroll mx-2 border-[4px] border-black rounded-[20px]"
|
||||||
|
ref={ref}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CodeMirror;
|
export default CodeMirror;
|
||||||
|
|
||||||
|
|||||||
127
frontend/src/components/OutputSettingsSection.tsx
Normal file
127
frontend/src/components/OutputSettingsSection.tsx
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
} from "./ui/select";
|
||||||
|
import { CSSOption, JSFrameworkOption, OutputSettings } from "../types";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "./ui/accordion";
|
||||||
|
import { capitalize } from "../lib/utils";
|
||||||
|
import toast from "react-hot-toast";
|
||||||
|
|
||||||
|
function displayCSSOption(option: CSSOption) {
|
||||||
|
switch (option) {
|
||||||
|
case CSSOption.TAILWIND:
|
||||||
|
return "Tailwind";
|
||||||
|
case CSSOption.BOOTSTRAP:
|
||||||
|
return "Bootstrap";
|
||||||
|
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}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
outputSettings: OutputSettings;
|
||||||
|
setOutputSettings: React.Dispatch<React.SetStateAction<OutputSettings>>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function OutputSettingsSection({ outputSettings, setOutputSettings }: 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 Vanilla 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 (
|
||||||
|
<Accordion type="single" collapsible className="w-full">
|
||||||
|
<AccordionItem value="item-1">
|
||||||
|
<AccordionTrigger>
|
||||||
|
<div className="flex gap-x-2">Output Settings </div>
|
||||||
|
</AccordionTrigger>
|
||||||
|
<AccordionContent className="gap-y-2 flex flex-col pt-2">
|
||||||
|
<div className="flex justify-between items-center pr-2">
|
||||||
|
<span className="text-sm">CSS</span>
|
||||||
|
<Select value={outputSettings.css} onValueChange={onCSSValueChange}>
|
||||||
|
<SelectTrigger className="w-[180px]">
|
||||||
|
{displayCSSOption(outputSettings.css)}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem value={CSSOption.TAILWIND}>Tailwind</SelectItem>
|
||||||
|
<SelectItem value={CSSOption.BOOTSTRAP}>Bootstrap</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<div className="flex justify-between items-center pr-2">
|
||||||
|
<span className="text-sm">JS Framework</span>
|
||||||
|
<Select
|
||||||
|
value={outputSettings.js}
|
||||||
|
onValueChange={onJsFrameworkChange}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-[180px]">
|
||||||
|
{capitalize(outputSettings.js)}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup>
|
||||||
|
<SelectItem value={JSFrameworkOption.VANILLA}>
|
||||||
|
Vanilla
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value={JSFrameworkOption.REACT}>React</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default OutputSettingsSection;
|
||||||
@ -9,7 +9,7 @@ export function PicoBadge() {
|
|||||||
className="fixed z-50 bottom-16 right-5 rounded-md shadow bg-black
|
className="fixed z-50 bottom-16 right-5 rounded-md shadow bg-black
|
||||||
text-white px-4 text-xs py-3 cursor-pointer"
|
text-white px-4 text-xs py-3 cursor-pointer"
|
||||||
>
|
>
|
||||||
feedback?
|
feature requests?
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<a href="https://picoapps.xyz?ref=screenshot-to-code" target="_blank">
|
<a href="https://picoapps.xyz?ref=screenshot-to-code" target="_blank">
|
||||||
|
|||||||
@ -13,7 +13,8 @@ import { EditorTheme, Settings } from "../types";
|
|||||||
import { Switch } from "./ui/switch";
|
import { Switch } from "./ui/switch";
|
||||||
import { Label } from "./ui/label";
|
import { Label } from "./ui/label";
|
||||||
import { Input } from "./ui/input";
|
import { Input } from "./ui/input";
|
||||||
import { Select } from "./ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger } from "./ui/select";
|
||||||
|
import { capitalize } from "../lib/utils";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
settings: Settings;
|
settings: Settings;
|
||||||
@ -124,14 +125,17 @@ function SettingsDialog({ settings, setSettings }: Props) {
|
|||||||
</Label>
|
</Label>
|
||||||
<div>
|
<div>
|
||||||
<Select // Use the custom Select component here
|
<Select // Use the custom Select component here
|
||||||
id="editor-theme"
|
name="editor-theme"
|
||||||
value={settings.editorTheme}
|
value={settings.editorTheme}
|
||||||
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
|
onValueChange={(value) => handleThemeChange(value as EditorTheme)}
|
||||||
handleThemeChange(e.target.value as EditorTheme)
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<option value="cobalt">Cobalt</option>
|
<SelectTrigger className="w-[180px]">
|
||||||
<option value="espresso">Espresso</option>
|
{capitalize(settings.editorTheme)}
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="cobalt">Cobalt</SelectItem>
|
||||||
|
<SelectItem value="espresso">Espresso</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
55
frontend/src/components/ui/accordion.tsx
Normal file
55
frontend/src/components/ui/accordion.tsx
Normal file
@ -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<typeof AccordionPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn("border-b", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AccordionItem.displayName = "AccordionItem"
|
||||||
|
|
||||||
|
const AccordionTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Header className="flex">
|
||||||
|
<AccordionPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||||
|
</AccordionPrimitive.Trigger>
|
||||||
|
</AccordionPrimitive.Header>
|
||||||
|
))
|
||||||
|
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const AccordionContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||||
|
</AccordionPrimitive.Content>
|
||||||
|
))
|
||||||
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }
|
||||||
36
frontend/src/components/ui/badge.tsx
Normal file
36
frontend/src/components/ui/badge.tsx
Normal file
@ -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<HTMLDivElement>,
|
||||||
|
VariantProps<typeof badgeVariants> {}
|
||||||
|
|
||||||
|
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||||
|
return (
|
||||||
|
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Badge, badgeVariants }
|
||||||
@ -1,24 +1,158 @@
|
|||||||
import * as React from "react";
|
import * as React from "react"
|
||||||
import { cn } from "@/lib/utils";
|
import {
|
||||||
|
CaretSortIcon,
|
||||||
|
CheckIcon,
|
||||||
|
ChevronDownIcon,
|
||||||
|
ChevronUpIcon,
|
||||||
|
} from "@radix-ui/react-icons"
|
||||||
|
import * as SelectPrimitive from "@radix-ui/react-select"
|
||||||
|
|
||||||
export interface SelectProps
|
import { cn } from "@/lib/utils"
|
||||||
extends React.SelectHTMLAttributes<HTMLSelectElement> {}
|
|
||||||
|
|
||||||
const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
|
const Select = SelectPrimitive.Root
|
||||||
({ className, ...props }, ref) => {
|
|
||||||
return (
|
const SelectGroup = SelectPrimitive.Group
|
||||||
<select
|
|
||||||
|
const SelectValue = SelectPrimitive.Value
|
||||||
|
|
||||||
|
const SelectTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"block appearance-none w-full text-sm shadow-sm rounded-md border border-input bg-white py-2 px-3 focus:outline-none focus:border-black dark:bg-background dark:color-white",
|
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<CaretSortIcon className="h-4 w-4 opacity-50" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
))
|
||||||
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const SelectScrollUpButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
))
|
||||||
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
|
||||||
|
|
||||||
|
const SelectScrollDownButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronDownIcon />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
))
|
||||||
|
SelectScrollDownButton.displayName =
|
||||||
|
SelectPrimitive.ScrollDownButton.displayName
|
||||||
|
|
||||||
|
const SelectContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||||
|
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||||
|
position === "popper" &&
|
||||||
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
))
|
||||||
|
SelectContent.displayName = SelectPrimitive.Content.displayName
|
||||||
|
|
||||||
|
const SelectLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn("px-2 py-1.5 text-sm font-semibold", className)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
))
|
||||||
}
|
SelectLabel.displayName = SelectPrimitive.Label.displayName
|
||||||
);
|
|
||||||
|
|
||||||
Select.displayName = "Select";
|
const SelectItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-2 pr-8 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute right-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<CheckIcon className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
))
|
||||||
|
SelectItem.displayName = SelectPrimitive.Item.displayName
|
||||||
|
|
||||||
export { Select };
|
const SelectSeparator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
ref={ref}
|
||||||
|
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectGroup,
|
||||||
|
SelectValue,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectContent,
|
||||||
|
SelectLabel,
|
||||||
|
SelectItem,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
}
|
||||||
|
|||||||
@ -1,6 +1,10 @@
|
|||||||
import { type ClassValue, clsx } from "clsx"
|
import { type ClassValue, clsx } from "clsx";
|
||||||
import { twMerge } from "tailwind-merge"
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
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);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,22 @@ export enum EditorTheme {
|
|||||||
COBALT = "cobalt",
|
COBALT = "cobalt",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum CSSOption {
|
||||||
|
TAILWIND = "tailwind",
|
||||||
|
BOOTSTRAP = "bootstrap",
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum JSFrameworkOption {
|
||||||
|
VANILLA = "vanilla",
|
||||||
|
REACT = "react",
|
||||||
|
VUE = "vue",
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OutputSettings {
|
||||||
|
css: CSSOption;
|
||||||
|
js: JSFrameworkOption;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
openAiApiKey: string | null;
|
openAiApiKey: string | null;
|
||||||
screenshotOneApiKey: string | null;
|
screenshotOneApiKey: string | null;
|
||||||
|
|||||||
@ -8,14 +8,16 @@ import { createHtmlPlugin } from "vite-plugin-html";
|
|||||||
export default ({ mode }) => {
|
export default ({ mode }) => {
|
||||||
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
process.env = { ...process.env, ...loadEnv(mode, process.cwd()) };
|
||||||
return defineConfig({
|
return defineConfig({
|
||||||
base: process.env.VITE_IS_DEPLOYED ? "/free-tools/screenshot-to-code/" : "",
|
base: "",
|
||||||
plugins: [
|
plugins: [
|
||||||
react(),
|
react(),
|
||||||
checker({ typescript: true }),
|
checker({ typescript: true }),
|
||||||
createHtmlPlugin({
|
createHtmlPlugin({
|
||||||
inject: {
|
inject: {
|
||||||
data: {
|
data: {
|
||||||
injectHead: process.env.VITE_INJECT_HEAD,
|
injectHead: process.env.VITE_IS_DEPLOYED
|
||||||
|
? '<script defer="" data-domain="screenshottocode.com" src="https://plausible.io/js/script.js"></script>'
|
||||||
|
: "",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
|||||||
@ -470,6 +470,33 @@
|
|||||||
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz"
|
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz"
|
||||||
integrity sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==
|
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":
|
"@humanwhocodes/config-array@^0.11.13":
|
||||||
version "0.11.13"
|
version "0.11.13"
|
||||||
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz"
|
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz"
|
||||||
@ -594,6 +621,13 @@
|
|||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
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":
|
"@radix-ui/primitive@1.0.1":
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd"
|
||||||
@ -601,6 +635,22 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@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":
|
"@radix-ui/react-alert-dialog@^1.0.5":
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz#70dd529cbf1e4bff386814d3776901fcaa131b8c"
|
||||||
@ -614,6 +664,14 @@
|
|||||||
"@radix-ui/react-primitive" "1.0.3"
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
"@radix-ui/react-slot" "1.0.2"
|
"@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":
|
"@radix-ui/react-checkbox@^1.0.4":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-checkbox/-/react-checkbox-1.0.4.tgz#98f22c38d5010dd6df4c5744cac74087e3275f4b"
|
||||||
@ -629,6 +687,21 @@
|
|||||||
"@radix-ui/react-use-previous" "1.0.1"
|
"@radix-ui/react-use-previous" "1.0.1"
|
||||||
"@radix-ui/react-use-size" "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":
|
"@radix-ui/react-collection@1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159"
|
||||||
@ -732,6 +805,23 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-primitive" "1.0.3"
|
"@radix-ui/react-primitive" "1.0.3"
|
||||||
|
|
||||||
|
"@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":
|
"@radix-ui/react-portal@1.0.4":
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.4.tgz#df4bfd353db3b1e84e639e9c63a5f2565fb00e15"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-portal/-/react-portal-1.0.4.tgz#df4bfd353db3b1e84e639e9c63a5f2565fb00e15"
|
||||||
@ -773,6 +863,34 @@
|
|||||||
"@radix-ui/react-use-callback-ref" "1.0.1"
|
"@radix-ui/react-use-callback-ref" "1.0.1"
|
||||||
"@radix-ui/react-use-controllable-state" "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":
|
"@radix-ui/react-separator@^1.0.3":
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.3.tgz#be5a931a543d5726336b112f465f58585c04c8aa"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-separator/-/react-separator-1.0.3.tgz#be5a931a543d5726336b112f465f58585c04c8aa"
|
||||||
@ -855,6 +973,14 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.13.10"
|
"@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":
|
"@radix-ui/react-use-size@1.0.1":
|
||||||
version "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"
|
resolved "https://registry.yarnpkg.com/@radix-ui/react-use-size/-/react-use-size-1.0.1.tgz#1c5f5fea940a7d7ade77694bb98116fb49f870b2"
|
||||||
@ -863,6 +989,21 @@
|
|||||||
"@babel/runtime" "^7.13.10"
|
"@babel/runtime" "^7.13.10"
|
||||||
"@radix-ui/react-use-layout-effect" "1.0.1"
|
"@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":
|
"@rollup/pluginutils@^4.2.0":
|
||||||
version "4.2.1"
|
version "4.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
|
resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user