add a way to create space in a follow up request
Browse files- app/api/ask/route.ts +47 -6
 - components/editor/ask-ai/index.tsx +3 -7
 - components/editor/index.tsx +15 -6
 - components/editor/preview/index.tsx +2 -1
 - hooks/useAi.ts +21 -14
 - hooks/useEditor.ts +8 -2
 - lib/prompts.ts +16 -12
 
    	
        app/api/ask/route.ts
    CHANGED
    
    | 
         @@ -16,13 +16,15 @@ import { 
     | 
|
| 16 | 
         
             
              SEARCH_START,
         
     | 
| 17 | 
         
             
              UPDATE_PAGE_START,
         
     | 
| 18 | 
         
             
              UPDATE_PAGE_END,
         
     | 
| 
         | 
|
| 19 | 
         
             
            } from "@/lib/prompts";
         
     | 
| 20 | 
         
             
            import MY_TOKEN_KEY from "@/lib/get-cookie-name";
         
     | 
| 21 | 
         
             
            import { Page } from "@/types";
         
     | 
| 22 | 
         
            -
            import { uploadFiles } from "@huggingface/hub";
         
     | 
| 23 | 
         
             
            import { isAuthenticated } from "@/lib/auth";
         
     | 
| 24 | 
         
             
            import { getBestProvider } from "@/lib/best-provider";
         
     | 
| 25 | 
         
             
            import { rewritePrompt } from "@/lib/rewrite-prompt";
         
     | 
| 
         | 
|
| 26 | 
         | 
| 27 | 
         
             
            const ipAddresses = new Map();
         
     | 
| 28 | 
         | 
| 
         @@ -31,7 +33,7 @@ export async function POST(request: NextRequest) { 
     | 
|
| 31 | 
         
             
              const userToken = request.cookies.get(MY_TOKEN_KEY())?.value;
         
     | 
| 32 | 
         | 
| 33 | 
         
             
              const body = await request.json();
         
     | 
| 34 | 
         
            -
              const { prompt, provider, model, redesignMarkdown, enhancedSettings } = body;
         
     | 
| 35 | 
         | 
| 36 | 
         
             
              if (!model || (!prompt && !redesignMarkdown)) {
         
     | 
| 37 | 
         
             
                return NextResponse.json(
         
     | 
| 
         @@ -131,7 +133,7 @@ export async function POST(request: NextRequest) { 
     | 
|
| 131 | 
         
             
                          },
         
     | 
| 132 | 
         
             
                          {
         
     | 
| 133 | 
         
             
                            role: "user",
         
     | 
| 134 | 
         
            -
                            content: `${rewrittenPrompt}${redesignMarkdown ? `\n\nHere is my current design as a markdown:\n\n${redesignMarkdown}\n\nNow, please create a new design based on this markdown. Use the images in the markdown.` : ""}`
         
     | 
| 135 | 
         
             
                          },
         
     | 
| 136 | 
         
             
                        ],
         
     | 
| 137 | 
         
             
                        max_tokens: selectedProvider.max_tokens,
         
     | 
| 
         @@ -220,10 +222,12 @@ export async function PUT(request: NextRequest) { 
     | 
|
| 220 | 
         
             
              const authHeaders = await headers();
         
     | 
| 221 | 
         | 
| 222 | 
         
             
              const body = await request.json();
         
     | 
| 223 | 
         
            -
              const { prompt, previousPrompts, provider, selectedElementHtml, model, pages, files, repoId } =
         
     | 
| 224 | 
         
             
                body;
         
     | 
| 225 | 
         | 
| 226 | 
         
            -
               
     | 
| 
         | 
|
| 
         | 
|
| 227 | 
         
             
                return NextResponse.json(
         
     | 
| 228 | 
         
             
                  { ok: false, error: "Missing required fields" },
         
     | 
| 229 | 
         
             
                  { status: 400 }
         
     | 
| 
         @@ -300,7 +304,7 @@ export async function PUT(request: NextRequest) { 
     | 
|
| 300 | 
         
             
                    messages: [
         
     | 
| 301 | 
         
             
                      {
         
     | 
| 302 | 
         
             
                        role: "system",
         
     | 
| 303 | 
         
            -
                        content: FOLLOW_UP_SYSTEM_PROMPT,
         
     | 
| 304 | 
         
             
                      },
         
     | 
| 305 | 
         
             
                      {
         
     | 
| 306 | 
         
             
                        role: "user",
         
     | 
| 
         @@ -514,6 +518,42 @@ export async function PUT(request: NextRequest) { 
     | 
|
| 514 | 
         
             
                    files.push(file);
         
     | 
| 515 | 
         
             
                  });
         
     | 
| 516 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 517 | 
         
             
                  const response = await uploadFiles({
         
     | 
| 518 | 
         
             
                    repo: {
         
     | 
| 519 | 
         
             
                      type: "space",
         
     | 
| 
         @@ -528,6 +568,7 @@ export async function PUT(request: NextRequest) { 
     | 
|
| 528 | 
         
             
                    ok: true,
         
     | 
| 529 | 
         
             
                    updatedLines,
         
     | 
| 530 | 
         
             
                    pages: updatedPages,
         
     | 
| 
         | 
|
| 531 | 
         
             
                    commit: {
         
     | 
| 532 | 
         
             
                      ...response.commit,
         
     | 
| 533 | 
         
             
                      title: prompt,
         
     | 
| 
         | 
|
| 16 | 
         
             
              SEARCH_START,
         
     | 
| 17 | 
         
             
              UPDATE_PAGE_START,
         
     | 
| 18 | 
         
             
              UPDATE_PAGE_END,
         
     | 
| 19 | 
         
            +
              PROMPT_FOR_PROJECT_NAME,
         
     | 
| 20 | 
         
             
            } from "@/lib/prompts";
         
     | 
| 21 | 
         
             
            import MY_TOKEN_KEY from "@/lib/get-cookie-name";
         
     | 
| 22 | 
         
             
            import { Page } from "@/types";
         
     | 
| 23 | 
         
            +
            import { createRepo, RepoDesignation, uploadFiles } from "@huggingface/hub";
         
     | 
| 24 | 
         
             
            import { isAuthenticated } from "@/lib/auth";
         
     | 
| 25 | 
         
             
            import { getBestProvider } from "@/lib/best-provider";
         
     | 
| 26 | 
         
             
            import { rewritePrompt } from "@/lib/rewrite-prompt";
         
     | 
| 27 | 
         
            +
            import { COLORS } from "@/lib/utils";
         
     | 
| 28 | 
         | 
| 29 | 
         
             
            const ipAddresses = new Map();
         
     | 
| 30 | 
         | 
| 
         | 
|
| 33 | 
         
             
              const userToken = request.cookies.get(MY_TOKEN_KEY())?.value;
         
     | 
| 34 | 
         | 
| 35 | 
         
             
              const body = await request.json();
         
     | 
| 36 | 
         
            +
              const { prompt, provider, model, redesignMarkdown, enhancedSettings, pages } = body;
         
     | 
| 37 | 
         | 
| 38 | 
         
             
              if (!model || (!prompt && !redesignMarkdown)) {
         
     | 
| 39 | 
         
             
                return NextResponse.json(
         
     | 
| 
         | 
|
| 133 | 
         
             
                          },
         
     | 
| 134 | 
         
             
                          {
         
     | 
| 135 | 
         
             
                            role: "user",
         
     | 
| 136 | 
         
            +
                            content: `${rewrittenPrompt}${redesignMarkdown ? `\n\nHere is my current design as a markdown:\n\n${redesignMarkdown}\n\nNow, please create a new design based on this markdown. Use the images in the markdown.` : ""} : ""}`
         
     | 
| 137 | 
         
             
                          },
         
     | 
| 138 | 
         
             
                        ],
         
     | 
| 139 | 
         
             
                        max_tokens: selectedProvider.max_tokens,
         
     | 
| 
         | 
|
| 222 | 
         
             
              const authHeaders = await headers();
         
     | 
| 223 | 
         | 
| 224 | 
         
             
              const body = await request.json();
         
     | 
| 225 | 
         
            +
              const { prompt, previousPrompts, provider, selectedElementHtml, model, pages, files, repoId: repoIdFromBody, isNew, enhancedSettings } =
         
     | 
| 226 | 
         
             
                body;
         
     | 
| 227 | 
         | 
| 228 | 
         
            +
              let repoId = repoIdFromBody;
         
     | 
| 229 | 
         
            +
             
     | 
| 230 | 
         
            +
              if (!prompt || pages.length === 0) {
         
     | 
| 231 | 
         
             
                return NextResponse.json(
         
     | 
| 232 | 
         
             
                  { ok: false, error: "Missing required fields" },
         
     | 
| 233 | 
         
             
                  { status: 400 }
         
     | 
| 
         | 
|
| 304 | 
         
             
                    messages: [
         
     | 
| 305 | 
         
             
                      {
         
     | 
| 306 | 
         
             
                        role: "system",
         
     | 
| 307 | 
         
            +
                        content: FOLLOW_UP_SYSTEM_PROMPT + (isNew ? PROMPT_FOR_PROJECT_NAME : ""),
         
     | 
| 308 | 
         
             
                      },
         
     | 
| 309 | 
         
             
                      {
         
     | 
| 310 | 
         
             
                        role: "user",
         
     | 
| 
         | 
|
| 518 | 
         
             
                    files.push(file);
         
     | 
| 519 | 
         
             
                  });
         
     | 
| 520 | 
         | 
| 521 | 
         
            +
                  if (isNew) {
         
     | 
| 522 | 
         
            +
                    const projectName = chunk.match(/<<<<<<< PROJECT_NAME_START ([\s\S]*?) >>>>>>> PROJECT_NAME_END/)?.[1]?.trim();
         
     | 
| 523 | 
         
            +
                    const formattedTitle = projectName?.toLowerCase()
         
     | 
| 524 | 
         
            +
                      .replace(/[^a-z0-9]+/g, "-")
         
     | 
| 525 | 
         
            +
                      .split("-")
         
     | 
| 526 | 
         
            +
                      .filter(Boolean)
         
     | 
| 527 | 
         
            +
                      .join("-")
         
     | 
| 528 | 
         
            +
                      .slice(0, 96);
         
     | 
| 529 | 
         
            +
                    const repo: RepoDesignation = {
         
     | 
| 530 | 
         
            +
                      type: "space",
         
     | 
| 531 | 
         
            +
                      name: `${user.name}/${formattedTitle}`,
         
     | 
| 532 | 
         
            +
                    };
         
     | 
| 533 | 
         
            +
                    const { repoUrl} = await createRepo({
         
     | 
| 534 | 
         
            +
                      repo,
         
     | 
| 535 | 
         
            +
                      accessToken: user.token as string,
         
     | 
| 536 | 
         
            +
                    });
         
     | 
| 537 | 
         
            +
                    repoId = repoUrl.split("/").slice(-2).join("/");
         
     | 
| 538 | 
         
            +
                    const colorFrom = COLORS[Math.floor(Math.random() * COLORS.length)];
         
     | 
| 539 | 
         
            +
                    const colorTo = COLORS[Math.floor(Math.random() * COLORS.length)];
         
     | 
| 540 | 
         
            +
                    const README = `---
         
     | 
| 541 | 
         
            +
            title: ${projectName}
         
     | 
| 542 | 
         
            +
            colorFrom: ${colorFrom}
         
     | 
| 543 | 
         
            +
            colorTo: ${colorTo}
         
     | 
| 544 | 
         
            +
            emoji: 🐳
         
     | 
| 545 | 
         
            +
            sdk: static
         
     | 
| 546 | 
         
            +
            pinned: false
         
     | 
| 547 | 
         
            +
            tags:
         
     | 
| 548 | 
         
            +
              - deepsite-v3
         
     | 
| 549 | 
         
            +
            ---
         
     | 
| 550 | 
         
            +
             
     | 
| 551 | 
         
            +
            # Welcome to your new DeepSite project!
         
     | 
| 552 | 
         
            +
            This project was created with [DeepSite](https://deepsite.hf.co).
         
     | 
| 553 | 
         
            +
                  `;
         
     | 
| 554 | 
         
            +
                    files.push(new File([README], "README.md", { type: "text/markdown" }));
         
     | 
| 555 | 
         
            +
                  }
         
     | 
| 556 | 
         
            +
             
     | 
| 557 | 
         
             
                  const response = await uploadFiles({
         
     | 
| 558 | 
         
             
                    repo: {
         
     | 
| 559 | 
         
             
                      type: "space",
         
     | 
| 
         | 
|
| 568 | 
         
             
                    ok: true,
         
     | 
| 569 | 
         
             
                    updatedLines,
         
     | 
| 570 | 
         
             
                    pages: updatedPages,
         
     | 
| 571 | 
         
            +
                    repoId,
         
     | 
| 572 | 
         
             
                    commit: {
         
     | 
| 573 | 
         
             
                      ...response.commit,
         
     | 
| 574 | 
         
             
                      title: prompt,
         
     | 
    	
        components/editor/ask-ai/index.tsx
    CHANGED
    
    | 
         @@ -41,7 +41,7 @@ export const AskAi = ({ 
     | 
|
| 41 | 
         
             
              onScrollToBottom?: () => void;
         
     | 
| 42 | 
         
             
            }) => {
         
     | 
| 43 | 
         
             
              const { user, projects } = useUser();
         
     | 
| 44 | 
         
            -
              const {  
     | 
| 45 | 
         
             
              const {
         
     | 
| 46 | 
         
             
                isAiWorking,
         
     | 
| 47 | 
         
             
                isThinking,
         
     | 
| 
         @@ -75,10 +75,6 @@ export const AskAi = ({ 
     | 
|
| 75 | 
         
             
              const [think, setThink] = useState("");
         
     | 
| 76 | 
         
             
              const [openThink, setOpenThink] = useState(false);
         
     | 
| 77 | 
         | 
| 78 | 
         
            -
              const isSameHtml = useMemo(() => {
         
     | 
| 79 | 
         
            -
                return isTheSameHtml(currentPageData.html);
         
     | 
| 80 | 
         
            -
              }, [currentPageData.html]);
         
     | 
| 81 | 
         
            -
             
     | 
| 82 | 
         
             
              const handleThink = (think: string) => {
         
     | 
| 83 | 
         
             
                setThink(think);
         
     | 
| 84 | 
         
             
                setIsThinking(true);
         
     | 
| 
         @@ -93,7 +89,7 @@ export const AskAi = ({ 
     | 
|
| 93 | 
         
             
                if (!redesignMarkdown && !prompt.trim()) return;
         
     | 
| 94 | 
         | 
| 95 | 
         
             
                if (isFollowUp && !redesignMarkdown && !isSameHtml) {
         
     | 
| 96 | 
         
            -
                  const result = await callAiFollowUp(prompt, enhancedSettings);
         
     | 
| 97 | 
         | 
| 98 | 
         
             
                  if (result?.error) {
         
     | 
| 99 | 
         
             
                    handleError(result.error, result.message);
         
     | 
| 
         @@ -223,7 +219,7 @@ export const AskAi = ({ 
     | 
|
| 223 | 
         
             
                      />
         
     | 
| 224 | 
         
             
                    </div>
         
     | 
| 225 | 
         
             
                    <div className="flex items-center justify-between gap-2 px-4 pb-3 mt-2">
         
     | 
| 226 | 
         
            -
                      <div className="flex-1 flex items-center justify-start gap-1.5">
         
     | 
| 227 | 
         
             
                        <PromptBuilder
         
     | 
| 228 | 
         
             
                          enhancedSettings={enhancedSettings!}
         
     | 
| 229 | 
         
             
                          setEnhancedSettings={setEnhancedSettings}
         
     | 
| 
         | 
|
| 41 | 
         
             
              onScrollToBottom?: () => void;
         
     | 
| 42 | 
         
             
            }) => {
         
     | 
| 43 | 
         
             
              const { user, projects } = useUser();
         
     | 
| 44 | 
         
            +
              const { isSameHtml, isUploading, pages, isLoadingProject } = useEditor();
         
     | 
| 45 | 
         
             
              const {
         
     | 
| 46 | 
         
             
                isAiWorking,
         
     | 
| 47 | 
         
             
                isThinking,
         
     | 
| 
         | 
|
| 75 | 
         
             
              const [think, setThink] = useState("");
         
     | 
| 76 | 
         
             
              const [openThink, setOpenThink] = useState(false);
         
     | 
| 77 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 78 | 
         
             
              const handleThink = (think: string) => {
         
     | 
| 79 | 
         
             
                setThink(think);
         
     | 
| 80 | 
         
             
                setIsThinking(true);
         
     | 
| 
         | 
|
| 89 | 
         
             
                if (!redesignMarkdown && !prompt.trim()) return;
         
     | 
| 90 | 
         | 
| 91 | 
         
             
                if (isFollowUp && !redesignMarkdown && !isSameHtml) {
         
     | 
| 92 | 
         
            +
                  const result = await callAiFollowUp(prompt, enhancedSettings, isNew);
         
     | 
| 93 | 
         | 
| 94 | 
         
             
                  if (result?.error) {
         
     | 
| 95 | 
         
             
                    handleError(result.error, result.message);
         
     | 
| 
         | 
|
| 219 | 
         
             
                      />
         
     | 
| 220 | 
         
             
                    </div>
         
     | 
| 221 | 
         
             
                    <div className="flex items-center justify-between gap-2 px-4 pb-3 mt-2">
         
     | 
| 222 | 
         
            +
                      <div className="flex-1 flex items-center justify-start gap-1.5 flex-wrap">
         
     | 
| 223 | 
         
             
                        <PromptBuilder
         
     | 
| 224 | 
         
             
                          enhancedSettings={enhancedSettings!}
         
     | 
| 225 | 
         
             
                          setEnhancedSettings={setEnhancedSettings}
         
     | 
    	
        components/editor/index.tsx
    CHANGED
    
    | 
         @@ -1,6 +1,6 @@ 
     | 
|
| 1 | 
         
             
            "use client";
         
     | 
| 2 | 
         
             
            import { useMemo, useRef, useState, useEffect } from "react";
         
     | 
| 3 | 
         
            -
            import { useCopyToClipboard } from "react-use";
         
     | 
| 4 | 
         
             
            import { CopyIcon } from "lucide-react";
         
     | 
| 5 | 
         
             
            import { toast } from "sonner";
         
     | 
| 6 | 
         
             
            import classNames from "classnames";
         
     | 
| 
         @@ -10,6 +10,7 @@ import Editor from "@monaco-editor/react"; 
     | 
|
| 10 | 
         
             
            import { useEditor } from "@/hooks/useEditor";
         
     | 
| 11 | 
         
             
            import { Header } from "@/components/editor/header";
         
     | 
| 12 | 
         
             
            import { useAi } from "@/hooks/useAi";
         
     | 
| 
         | 
|
| 13 | 
         | 
| 14 | 
         
             
            import { ListPages } from "./pages";
         
     | 
| 15 | 
         
             
            import { AskAi } from "./ask-ai";
         
     | 
| 
         @@ -47,7 +48,17 @@ export const AppEditor = ({ 
     | 
|
| 47 | 
         
             
              const editor = useRef<HTMLDivElement>(null);
         
     | 
| 48 | 
         
             
              const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
         
     | 
| 49 | 
         | 
| 50 | 
         
            -
               
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 51 | 
         
             
              useEffect(() => {
         
     | 
| 52 | 
         
             
                if (hasUnsavedChanges && !isAiWorking) {
         
     | 
| 53 | 
         
             
                  setShowSavePopup(true);
         
     | 
| 
         @@ -92,11 +103,9 @@ export const AppEditor = ({ 
     | 
|
| 92 | 
         
             
                            horizontal: "hidden",
         
     | 
| 93 | 
         
             
                          },
         
     | 
| 94 | 
         
             
                          wordWrap: "on",
         
     | 
| 95 | 
         
            -
                          readOnly: !!isAiWorking || !!currentCommit 
     | 
| 96 | 
         
             
                          readOnlyMessage: {
         
     | 
| 97 | 
         
            -
                            value:  
     | 
| 98 | 
         
            -
                              ? "You can't edit the code, as this is a new project. Ask DeepSite first."
         
     | 
| 99 | 
         
            -
                              : currentCommit
         
     | 
| 100 | 
         
             
                              ? "You can't edit the code, as this is an old version of the project."
         
     | 
| 101 | 
         
             
                              : "Wait for DeepSite to finish working...",
         
     | 
| 102 | 
         
             
                            isTrusted: true,
         
     | 
| 
         | 
|
| 1 | 
         
             
            "use client";
         
     | 
| 2 | 
         
             
            import { useMemo, useRef, useState, useEffect } from "react";
         
     | 
| 3 | 
         
            +
            import { useCopyToClipboard, useMount } from "react-use";
         
     | 
| 4 | 
         
             
            import { CopyIcon } from "lucide-react";
         
     | 
| 5 | 
         
             
            import { toast } from "sonner";
         
     | 
| 6 | 
         
             
            import classNames from "classnames";
         
     | 
| 
         | 
|
| 10 | 
         
             
            import { useEditor } from "@/hooks/useEditor";
         
     | 
| 11 | 
         
             
            import { Header } from "@/components/editor/header";
         
     | 
| 12 | 
         
             
            import { useAi } from "@/hooks/useAi";
         
     | 
| 13 | 
         
            +
            import { defaultHTML } from "@/lib/consts";
         
     | 
| 14 | 
         | 
| 15 | 
         
             
            import { ListPages } from "./pages";
         
     | 
| 16 | 
         
             
            import { AskAi } from "./ask-ai";
         
     | 
| 
         | 
|
| 48 | 
         
             
              const editor = useRef<HTMLDivElement>(null);
         
     | 
| 49 | 
         
             
              const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
         
     | 
| 50 | 
         | 
| 51 | 
         
            +
              useMount(() => {
         
     | 
| 52 | 
         
            +
                if (isNew) {
         
     | 
| 53 | 
         
            +
                  setPages([
         
     | 
| 54 | 
         
            +
                    {
         
     | 
| 55 | 
         
            +
                      path: "index.html",
         
     | 
| 56 | 
         
            +
                      html: defaultHTML,
         
     | 
| 57 | 
         
            +
                    },
         
     | 
| 58 | 
         
            +
                  ]);
         
     | 
| 59 | 
         
            +
                }
         
     | 
| 60 | 
         
            +
              });
         
     | 
| 61 | 
         
            +
             
     | 
| 62 | 
         
             
              useEffect(() => {
         
     | 
| 63 | 
         
             
                if (hasUnsavedChanges && !isAiWorking) {
         
     | 
| 64 | 
         
             
                  setShowSavePopup(true);
         
     | 
| 
         | 
|
| 103 | 
         
             
                            horizontal: "hidden",
         
     | 
| 104 | 
         
             
                          },
         
     | 
| 105 | 
         
             
                          wordWrap: "on",
         
     | 
| 106 | 
         
            +
                          readOnly: !!isAiWorking || !!currentCommit,
         
     | 
| 107 | 
         
             
                          readOnlyMessage: {
         
     | 
| 108 | 
         
            +
                            value: currentCommit
         
     | 
| 
         | 
|
| 
         | 
|
| 109 | 
         
             
                              ? "You can't edit the code, as this is an old version of the project."
         
     | 
| 110 | 
         
             
                              : "Wait for DeepSite to finish working...",
         
     | 
| 111 | 
         
             
                            isTrusted: true,
         
     | 
    	
        components/editor/preview/index.tsx
    CHANGED
    
    | 
         @@ -33,6 +33,7 @@ export const Preview = forwardRef<LivePreviewRef, { isNew: boolean }>( 
     | 
|
| 33 | 
         
             
                  pages,
         
     | 
| 34 | 
         
             
                  setPages,
         
     | 
| 35 | 
         
             
                  setCurrentPage,
         
     | 
| 
         | 
|
| 36 | 
         
             
                } = useEditor();
         
     | 
| 37 | 
         
             
                const {
         
     | 
| 38 | 
         
             
                  isEditableModeEnabled,
         
     | 
| 
         @@ -239,7 +240,7 @@ export const Preview = forwardRef<LivePreviewRef, { isNew: boolean }>( 
     | 
|
| 239 | 
         
             
                        </span>
         
     | 
| 240 | 
         
             
                      </div>
         
     | 
| 241 | 
         
             
                    )}
         
     | 
| 242 | 
         
            -
                    {isNew && !isLoadingProject && !globalAiLoading ? (
         
     | 
| 243 | 
         
             
                      <iframe
         
     | 
| 244 | 
         
             
                        className={classNames(
         
     | 
| 245 | 
         
             
                          "w-full select-none transition-all duration-200 bg-black h-full",
         
     | 
| 
         | 
|
| 33 | 
         
             
                  pages,
         
     | 
| 34 | 
         
             
                  setPages,
         
     | 
| 35 | 
         
             
                  setCurrentPage,
         
     | 
| 36 | 
         
            +
                  isSameHtml,
         
     | 
| 37 | 
         
             
                } = useEditor();
         
     | 
| 38 | 
         
             
                const {
         
     | 
| 39 | 
         
             
                  isEditableModeEnabled,
         
     | 
| 
         | 
|
| 240 | 
         
             
                        </span>
         
     | 
| 241 | 
         
             
                      </div>
         
     | 
| 242 | 
         
             
                    )}
         
     | 
| 243 | 
         
            +
                    {isNew && !isLoadingProject && !globalAiLoading && isSameHtml ? (
         
     | 
| 244 | 
         
             
                      <iframe
         
     | 
| 245 | 
         
             
                        className={classNames(
         
     | 
| 246 | 
         
             
                          "w-full select-none transition-all duration-200 bg-black h-full",
         
     | 
    	
        hooks/useAi.ts
    CHANGED
    
    | 
         @@ -14,7 +14,7 @@ import { LivePreviewRef } from "@/components/editor/live-preview"; 
     | 
|
| 14 | 
         
             
            export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefObject<LivePreviewRef | null>) => {
         
     | 
| 15 | 
         
             
              const client = useQueryClient();
         
     | 
| 16 | 
         
             
              const audio = useRef<HTMLAudioElement | null>(null);
         
     | 
| 17 | 
         
            -
              const { setPages, setCurrentPage, setPrompts, prompts, pages, project, setProject, commits, setCommits, setLastSavedPages } = useEditor();
         
     | 
| 18 | 
         
             
              const [controller, setController] = useState<AbortController | null>(null);
         
     | 
| 19 | 
         
             
              const [storageProvider, setStorageProvider] = useLocalStorage("provider", "auto");
         
     | 
| 20 | 
         
             
              const [storageModel, setStorageModel] = useLocalStorage("model", MODELS[0].value);
         
     | 
| 
         @@ -256,7 +256,7 @@ export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefO 
     | 
|
| 256 | 
         
             
                }
         
     | 
| 257 | 
         
             
              };
         
     | 
| 258 | 
         | 
| 259 | 
         
            -
              const callAiFollowUp = async (prompt: string, enhancedSettings?: EnhancedSettings) => {
         
     | 
| 260 | 
         
             
                if (isAiWorking) return;
         
     | 
| 261 | 
         
             
                if (!prompt.trim()) return;
         
     | 
| 262 | 
         | 
| 
         @@ -266,7 +266,7 @@ export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefO 
     | 
|
| 266 | 
         
             
                const abortController = new AbortController();
         
     | 
| 267 | 
         
             
                setController(abortController);
         
     | 
| 268 | 
         | 
| 269 | 
         
            -
                try { 
     | 
| 270 | 
         
             
                  const request = await fetch("/api/ask", {
         
     | 
| 271 | 
         
             
                    method: "PUT",
         
     | 
| 272 | 
         
             
                    body: JSON.stringify({
         
     | 
| 
         @@ -277,7 +277,9 @@ export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefO 
     | 
|
| 277 | 
         
             
                      pages,
         
     | 
| 278 | 
         
             
                      selectedElementHtml: selectedElement?.outerHTML,
         
     | 
| 279 | 
         
             
                      files: selectedFiles,
         
     | 
| 280 | 
         
            -
                      repoId: project?.space_id
         
     | 
| 
         | 
|
| 
         | 
|
| 281 | 
         
             
                    }),
         
     | 
| 282 | 
         
             
                    headers: {
         
     | 
| 283 | 
         
             
                      "Content-Type": "application/json",
         
     | 
| 
         @@ -307,20 +309,25 @@ export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefO 
     | 
|
| 307 | 
         
             
                    }
         
     | 
| 308 | 
         | 
| 309 | 
         
             
                    toast.success("AI responded successfully");
         
     | 
| 310 | 
         
            -
                    setIsAiWorking(false);
         
     | 
| 311 | 
         
             
                    const iframe = document.getElementById(
         
     | 
| 312 | 
         
             
                      "preview-iframe"
         
     | 
| 313 | 
         
             
                    ) as HTMLIFrameElement;
         
     | 
| 314 | 
         | 
| 315 | 
         
            -
                     
     | 
| 316 | 
         
            -
             
     | 
| 317 | 
         
            -
             
     | 
| 318 | 
         
            -
                     
     | 
| 319 | 
         
            -
                       
     | 
| 320 | 
         
            -
             
     | 
| 321 | 
         
            -
             
     | 
| 322 | 
         
            -
             
     | 
| 323 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 324 | 
         
             
                    if (audio.current) audio.current.play();
         
     | 
| 325 | 
         
             
                    if (iframe) {
         
     | 
| 326 | 
         
             
                      setTimeout(() => {
         
     | 
| 
         | 
|
| 14 | 
         
             
            export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefObject<LivePreviewRef | null>) => {
         
     | 
| 15 | 
         
             
              const client = useQueryClient();
         
     | 
| 16 | 
         
             
              const audio = useRef<HTMLAudioElement | null>(null);
         
     | 
| 17 | 
         
            +
              const { setPages, setCurrentPage, setPrompts, prompts, pages, project, setProject, commits, setCommits, setLastSavedPages, isSameHtml } = useEditor();
         
     | 
| 18 | 
         
             
              const [controller, setController] = useState<AbortController | null>(null);
         
     | 
| 19 | 
         
             
              const [storageProvider, setStorageProvider] = useLocalStorage("provider", "auto");
         
     | 
| 20 | 
         
             
              const [storageModel, setStorageModel] = useLocalStorage("model", MODELS[0].value);
         
     | 
| 
         | 
|
| 256 | 
         
             
                }
         
     | 
| 257 | 
         
             
              };
         
     | 
| 258 | 
         | 
| 259 | 
         
            +
              const callAiFollowUp = async (prompt: string, enhancedSettings?: EnhancedSettings, isNew?: boolean) => {
         
     | 
| 260 | 
         
             
                if (isAiWorking) return;
         
     | 
| 261 | 
         
             
                if (!prompt.trim()) return;
         
     | 
| 262 | 
         | 
| 
         | 
|
| 266 | 
         
             
                const abortController = new AbortController();
         
     | 
| 267 | 
         
             
                setController(abortController);
         
     | 
| 268 | 
         | 
| 269 | 
         
            +
                try {
         
     | 
| 270 | 
         
             
                  const request = await fetch("/api/ask", {
         
     | 
| 271 | 
         
             
                    method: "PUT",
         
     | 
| 272 | 
         
             
                    body: JSON.stringify({
         
     | 
| 
         | 
|
| 277 | 
         
             
                      pages,
         
     | 
| 278 | 
         
             
                      selectedElementHtml: selectedElement?.outerHTML,
         
     | 
| 279 | 
         
             
                      files: selectedFiles,
         
     | 
| 280 | 
         
            +
                      repoId: project?.space_id,
         
     | 
| 281 | 
         
            +
                      isNew,
         
     | 
| 282 | 
         
            +
                      enhancedSettings,
         
     | 
| 283 | 
         
             
                    }),
         
     | 
| 284 | 
         
             
                    headers: {
         
     | 
| 285 | 
         
             
                      "Content-Type": "application/json",
         
     | 
| 
         | 
|
| 309 | 
         
             
                    }
         
     | 
| 310 | 
         | 
| 311 | 
         
             
                    toast.success("AI responded successfully");
         
     | 
| 
         | 
|
| 312 | 
         
             
                    const iframe = document.getElementById(
         
     | 
| 313 | 
         
             
                      "preview-iframe"
         
     | 
| 314 | 
         
             
                    ) as HTMLIFrameElement;
         
     | 
| 315 | 
         | 
| 316 | 
         
            +
                    if (isNew && res.repoId) {
         
     | 
| 317 | 
         
            +
                      router.push(`/projects/${res.repoId}`);
         
     | 
| 318 | 
         
            +
                      setIsAiWorking(false);
         
     | 
| 319 | 
         
            +
                    } else {
         
     | 
| 320 | 
         
            +
                      setPages(res.pages);
         
     | 
| 321 | 
         
            +
                      setLastSavedPages([...res.pages]); // Mark AI changes as saved
         
     | 
| 322 | 
         
            +
                      setCommits([res.commit, ...commits]);
         
     | 
| 323 | 
         
            +
                      setPrompts(
         
     | 
| 324 | 
         
            +
                        [...prompts, prompt]
         
     | 
| 325 | 
         
            +
                      )
         
     | 
| 326 | 
         
            +
                      setSelectedElement(null);
         
     | 
| 327 | 
         
            +
                      setSelectedFiles([]);
         
     | 
| 328 | 
         
            +
                      setIsEditableModeEnabled(false);
         
     | 
| 329 | 
         
            +
                    }
         
     | 
| 330 | 
         
            +
             
     | 
| 331 | 
         
             
                    if (audio.current) audio.current.play();
         
     | 
| 332 | 
         
             
                    if (iframe) {
         
     | 
| 333 | 
         
             
                      setTimeout(() => {
         
     | 
    	
        hooks/useEditor.ts
    CHANGED
    
    | 
         @@ -7,6 +7,7 @@ import { useRouter } from "next/navigation"; 
     | 
|
| 7 | 
         
             
            import { defaultHTML } from "@/lib/consts";
         
     | 
| 8 | 
         
             
            import { Commit, HtmlHistory, Page, Project } from "@/types";
         
     | 
| 9 | 
         
             
            import { api } from "@/lib/api";
         
     | 
| 
         | 
|
| 10 | 
         | 
| 11 | 
         
             
            export const useEditor = (namespace?: string, repoId?: string) => {
         
     | 
| 12 | 
         
             
              const client = useQueryClient();
         
     | 
| 
         @@ -60,7 +61,7 @@ export const useEditor = (namespace?: string, repoId?: string) => { 
     | 
|
| 60 | 
         
             
              const { data: pages = [] } = useQuery<Page[]>({
         
     | 
| 61 | 
         
             
                queryKey: ["editor.pages"],
         
     | 
| 62 | 
         
             
                queryFn: async (): Promise<Page[]> => {
         
     | 
| 63 | 
         
            -
                  return  
     | 
| 64 | 
         
             
                    {
         
     | 
| 65 | 
         
             
                      path: "index.html",
         
     | 
| 66 | 
         
             
                      html: defaultHTML,
         
     | 
| 
         @@ -71,7 +72,7 @@ export const useEditor = (namespace?: string, repoId?: string) => { 
     | 
|
| 71 | 
         
             
                refetchOnReconnect: false,
         
     | 
| 72 | 
         
             
                refetchOnMount: false,
         
     | 
| 73 | 
         
             
                retry: false,
         
     | 
| 74 | 
         
            -
                initialData:  
     | 
| 75 | 
         
             
                  {
         
     | 
| 76 | 
         
             
                    path: "index.html",
         
     | 
| 77 | 
         
             
                    html: defaultHTML,
         
     | 
| 
         @@ -319,6 +320,10 @@ export const useEditor = (namespace?: string, repoId?: string) => { 
     | 
|
| 319 | 
         
             
                }
         
     | 
| 320 | 
         
             
              }, [namespace, repoId])
         
     | 
| 321 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 322 | 
         
             
              return {
         
     | 
| 323 | 
         
             
                isLoadingProject,
         
     | 
| 324 | 
         
             
                project,
         
     | 
| 
         @@ -341,6 +346,7 @@ export const useEditor = (namespace?: string, repoId?: string) => { 
     | 
|
| 341 | 
         
             
                currentCommit,
         
     | 
| 342 | 
         
             
                setCurrentCommit,
         
     | 
| 343 | 
         
             
                setProject,
         
     | 
| 
         | 
|
| 344 | 
         
             
                isUploading: uploadFilesMutation.isPending,
         
     | 
| 345 | 
         
             
                globalEditorLoading: uploadFilesMutation.isPending || isLoadingProject,
         
     | 
| 346 | 
         
             
                // Unsaved changes functionality
         
     | 
| 
         | 
|
| 7 | 
         
             
            import { defaultHTML } from "@/lib/consts";
         
     | 
| 8 | 
         
             
            import { Commit, HtmlHistory, Page, Project } from "@/types";
         
     | 
| 9 | 
         
             
            import { api } from "@/lib/api";
         
     | 
| 10 | 
         
            +
            import { isTheSameHtml } from "@/lib/compare-html-diff";
         
     | 
| 11 | 
         | 
| 12 | 
         
             
            export const useEditor = (namespace?: string, repoId?: string) => {
         
     | 
| 13 | 
         
             
              const client = useQueryClient();
         
     | 
| 
         | 
|
| 61 | 
         
             
              const { data: pages = [] } = useQuery<Page[]>({
         
     | 
| 62 | 
         
             
                queryKey: ["editor.pages"],
         
     | 
| 63 | 
         
             
                queryFn: async (): Promise<Page[]> => {
         
     | 
| 64 | 
         
            +
                  return [
         
     | 
| 65 | 
         
             
                    {
         
     | 
| 66 | 
         
             
                      path: "index.html",
         
     | 
| 67 | 
         
             
                      html: defaultHTML,
         
     | 
| 
         | 
|
| 72 | 
         
             
                refetchOnReconnect: false,
         
     | 
| 73 | 
         
             
                refetchOnMount: false,
         
     | 
| 74 | 
         
             
                retry: false,
         
     | 
| 75 | 
         
            +
                initialData: [
         
     | 
| 76 | 
         
             
                  {
         
     | 
| 77 | 
         
             
                    path: "index.html",
         
     | 
| 78 | 
         
             
                    html: defaultHTML,
         
     | 
| 
         | 
|
| 320 | 
         
             
                }
         
     | 
| 321 | 
         
             
              }, [namespace, repoId])
         
     | 
| 322 | 
         | 
| 323 | 
         
            +
              const isSameHtml = useMemo(() => {
         
     | 
| 324 | 
         
            +
                return isTheSameHtml(currentPageData.html);
         
     | 
| 325 | 
         
            +
              }, [pages]);
         
     | 
| 326 | 
         
            +
             
     | 
| 327 | 
         
             
              return {
         
     | 
| 328 | 
         
             
                isLoadingProject,
         
     | 
| 329 | 
         
             
                project,
         
     | 
| 
         | 
|
| 346 | 
         
             
                currentCommit,
         
     | 
| 347 | 
         
             
                setCurrentCommit,
         
     | 
| 348 | 
         
             
                setProject,
         
     | 
| 349 | 
         
            +
                isSameHtml,
         
     | 
| 350 | 
         
             
                isUploading: uploadFilesMutation.isPending,
         
     | 
| 351 | 
         
             
                globalEditorLoading: uploadFilesMutation.isPending || isLoadingProject,
         
     | 
| 352 | 
         
             
                // Unsaved changes functionality
         
     | 
    	
        lib/prompts.ts
    CHANGED
    
    | 
         @@ -74,21 +74,25 @@ If it's a new page, you MUST applied the following NEW_PAGE_START and UPDATE_PAG 
     | 
|
| 74 | 
         
             
            ${PROMPT_FOR_IMAGE_GENERATION}
         
     | 
| 75 | 
         
             
            Do NOT explain the changes or what you did, just return the expected results.
         
     | 
| 76 | 
         
             
            Update Format Rules:
         
     | 
| 77 | 
         
            -
            1. Start with ${ 
     | 
| 78 | 
         
            -
            2.  
     | 
| 79 | 
         
            -
            3. Close the start tag with the ${ 
     | 
| 80 | 
         
            -
            4. Start with ${ 
     | 
| 81 | 
         
            -
            5. Provide the  
     | 
| 82 | 
         
            -
            6.  
     | 
| 83 | 
         
            -
            7.  
     | 
| 84 | 
         
            -
            8.  
     | 
| 85 | 
         
            -
            9.  
     | 
| 86 | 
         
            -
            10.  
     | 
| 87 | 
         
            -
            11.  
     | 
| 88 | 
         
            -
            12.  
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 89 | 
         
             
            Example Modifying Code:
         
     | 
| 90 | 
         
             
            \`\`\`
         
     | 
| 91 | 
         
             
            Some explanation...
         
     | 
| 
         | 
|
| 92 | 
         
             
            ${UPDATE_PAGE_START}index.html${UPDATE_PAGE_END}
         
     | 
| 93 | 
         
             
            ${SEARCH_START}
         
     | 
| 94 | 
         
             
                <h1>Old Title</h1>
         
     | 
| 
         | 
|
| 74 | 
         
             
            ${PROMPT_FOR_IMAGE_GENERATION}
         
     | 
| 75 | 
         
             
            Do NOT explain the changes or what you did, just return the expected results.
         
     | 
| 76 | 
         
             
            Update Format Rules:
         
     | 
| 77 | 
         
            +
            1. Start with ${PROJECT_NAME_START}.
         
     | 
| 78 | 
         
            +
            2. Add the name of the project, right after the start tag.
         
     | 
| 79 | 
         
            +
            3. Close the start tag with the ${PROJECT_NAME_END}.
         
     | 
| 80 | 
         
            +
            4. Start with ${UPDATE_PAGE_START}
         
     | 
| 81 | 
         
            +
            5. Provide the name of the page you are modifying.
         
     | 
| 82 | 
         
            +
            6. Close the start tag with the ${UPDATE_PAGE_END}.
         
     | 
| 83 | 
         
            +
            7. Start with ${SEARCH_START}
         
     | 
| 84 | 
         
            +
            8. Provide the exact lines from the current code that need to be replaced.
         
     | 
| 85 | 
         
            +
            9. Use ${DIVIDER} to separate the search block from the replacement.
         
     | 
| 86 | 
         
            +
            10. Provide the new lines that should replace the original lines.
         
     | 
| 87 | 
         
            +
            11. End with ${REPLACE_END}
         
     | 
| 88 | 
         
            +
            12. You can use multiple SEARCH/REPLACE blocks if changes are needed in different parts of the file.
         
     | 
| 89 | 
         
            +
            13. To insert code, use an empty SEARCH block (only ${SEARCH_START} and ${DIVIDER} on their lines) if inserting at the very beginning, otherwise provide the line *before* the insertion point in the SEARCH block and include that line plus the new lines in the REPLACE block.
         
     | 
| 90 | 
         
            +
            14. To delete code, provide the lines to delete in the SEARCH block and leave the REPLACE block empty (only ${DIVIDER} and ${REPLACE_END} on their lines).
         
     | 
| 91 | 
         
            +
            15. IMPORTANT: The SEARCH block must *exactly* match the current code, including indentation and whitespace.
         
     | 
| 92 | 
         
             
            Example Modifying Code:
         
     | 
| 93 | 
         
             
            \`\`\`
         
     | 
| 94 | 
         
             
            Some explanation...
         
     | 
| 95 | 
         
            +
            ${PROJECT_NAME_START}Project Name${PROJECT_NAME_END}
         
     | 
| 96 | 
         
             
            ${UPDATE_PAGE_START}index.html${UPDATE_PAGE_END}
         
     | 
| 97 | 
         
             
            ${SEARCH_START}
         
     | 
| 98 | 
         
             
                <h1>Old Title</h1>
         
     |