Merge branch 'main' into pr/65
This commit is contained in:
commit
41b8f91cbc
@ -12,8 +12,9 @@ See the [Examples](#examples) section below for more demos.
|
||||
|
||||
## 🌟 Recent Updates
|
||||
|
||||
- Nov 21 - You can now 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.
|
||||
|
||||
@ -20,7 +20,7 @@ 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 { AppState, Settings } from "./types";
|
||||
import { Settings, EditorTheme, AppState } from "./types";
|
||||
import { IS_RUNNING_ON_CLOUD } from "./config";
|
||||
import { PicoBadge } from "./components/PicoBadge";
|
||||
import { OnboardingNote } from "./components/OnboardingNote";
|
||||
@ -41,7 +41,7 @@ function App() {
|
||||
openAiApiKey: null,
|
||||
screenshotOneApiKey: null,
|
||||
isImageGenerationEnabled: true,
|
||||
editorTheme: "cobalt",
|
||||
editorTheme: EditorTheme.COBALT,
|
||||
termOfServiceAccepted: false,
|
||||
},
|
||||
"setting"
|
||||
@ -135,7 +135,12 @@ function App() {
|
||||
return (
|
||||
<div className="mt-2">
|
||||
{IS_RUNNING_ON_CLOUD && <PicoBadge />}
|
||||
{IS_RUNNING_ON_CLOUD && <TermsOfServiceDialog open={!settings.termOfServiceAccepted} onOpenChange={handleTermDialogOpenChange} />}
|
||||
{IS_RUNNING_ON_CLOUD && (
|
||||
<TermsOfServiceDialog
|
||||
open={!settings.termOfServiceAccepted}
|
||||
onOpenChange={handleTermDialogOpenChange}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="hidden lg:fixed lg:inset-y-0 lg:z-50 lg:flex lg:w-96 lg:flex-col">
|
||||
<div className="flex grow flex-col gap-y-2 overflow-y-auto border-r border-gray-200 bg-white px-6">
|
||||
@ -274,6 +279,7 @@ function App() {
|
||||
<CodeMirror
|
||||
code={generatedCode}
|
||||
editorTheme={settings.editorTheme}
|
||||
onCodeChange={setGeneratedCode}
|
||||
/>
|
||||
<span
|
||||
title="Copy Code"
|
||||
|
||||
@ -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,39 +11,42 @@ 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<HTMLDivElement>(null);
|
||||
const view = useRef<EditorView | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let selectedTheme = cobalt;
|
||||
if (editorTheme === "espresso") {
|
||||
selectedTheme = espresso;
|
||||
}
|
||||
view.current = new EditorView({
|
||||
state: EditorState.create({
|
||||
doc: code,
|
||||
extensions: [
|
||||
history(),
|
||||
keymap.of([
|
||||
...defaultKeymap,
|
||||
indentWithTab,
|
||||
{ key: "Mod-z", run: undo, preventDefault: true },
|
||||
{ key: "Mod-Shift-z", run: redo, preventDefault: true },
|
||||
]),
|
||||
lineNumbers(),
|
||||
bracketMatching(),
|
||||
html(),
|
||||
selectedTheme,
|
||||
EditorView.lineWrapping,
|
||||
],
|
||||
const editorState = useMemo(() => EditorState.create({
|
||||
extensions: [
|
||||
history(),
|
||||
keymap.of([
|
||||
...defaultKeymap,
|
||||
indentWithTab,
|
||||
{ key: "Mod-z", run: undo, preventDefault: true },
|
||||
{ key: "Mod-Shift-z", run: redo, preventDefault: true },
|
||||
]),
|
||||
lineNumbers(),
|
||||
bracketMatching(),
|
||||
html(),
|
||||
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 +56,7 @@ function CodeMirror({ code, editorTheme }: Props) {
|
||||
view.current = null;
|
||||
}
|
||||
};
|
||||
}, [code, editorTheme]);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (view.current && view.current.state.doc.toString() !== code) {
|
||||
@ -69,3 +72,4 @@ function CodeMirror({ code, editorTheme }: Props) {
|
||||
}
|
||||
|
||||
export default CodeMirror;
|
||||
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import classNames from "classnames";
|
||||
import useThrottle from "../hooks/useThrottle";
|
||||
|
||||
@ -8,12 +9,22 @@ interface Props {
|
||||
|
||||
function Preview({ code, device }: Props) {
|
||||
const throttledCode = useThrottle(code, 200);
|
||||
const iframeRef = useRef<HTMLIFrameElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const iframe = iframeRef.current;
|
||||
if (iframe && iframe.contentDocument) {
|
||||
iframe.contentDocument.open();
|
||||
iframe.contentDocument.write(throttledCode);
|
||||
iframe.contentDocument.close();
|
||||
}
|
||||
}, [throttledCode]);
|
||||
|
||||
return (
|
||||
<div className="flex justify-center mx-2">
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
title="Preview"
|
||||
srcDoc={throttledCode}
|
||||
className={classNames(
|
||||
"border-[4px] border-black rounded-[20px] shadow-lg",
|
||||
"transform scale-[0.9] origin-top",
|
||||
@ -26,4 +37,5 @@ function Preview({ code, device }: Props) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default Preview;
|
||||
|
||||
export default Preview;
|
||||
@ -9,7 +9,7 @@ import {
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { FaCog } from "react-icons/fa";
|
||||
import { Settings } from "../types";
|
||||
import { EditorTheme, Settings } from "../types";
|
||||
import { Switch } from "./ui/switch";
|
||||
import { Label } from "./ui/label";
|
||||
import { Input } from "./ui/input";
|
||||
@ -21,7 +21,7 @@ interface Props {
|
||||
}
|
||||
|
||||
function SettingsDialog({ settings, setSettings }: Props) {
|
||||
const handleThemeChange = (theme: string) => {
|
||||
const handleThemeChange = (theme: EditorTheme) => {
|
||||
setSettings((s) => ({
|
||||
...s,
|
||||
editorTheme: theme,
|
||||
@ -110,7 +110,7 @@ function SettingsDialog({ settings, setSettings }: Props) {
|
||||
id="editor-theme"
|
||||
value={settings.editorTheme}
|
||||
onChange={(e: React.ChangeEvent<HTMLSelectElement>) =>
|
||||
handleThemeChange(e.target.value)
|
||||
handleThemeChange(e.target.value as EditorTheme)
|
||||
}
|
||||
>
|
||||
<option value="cobalt">Cobalt</option>
|
||||
|
||||
@ -1,8 +1,13 @@
|
||||
export enum EditorTheme {
|
||||
ESPRESSO = "espresso",
|
||||
COBALT = "cobalt",
|
||||
}
|
||||
|
||||
export interface Settings {
|
||||
openAiApiKey: string | null;
|
||||
screenshotOneApiKey: string | null;
|
||||
isImageGenerationEnabled: boolean;
|
||||
editorTheme: string;
|
||||
editorTheme: EditorTheme;
|
||||
termOfServiceAccepted: boolean;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user