Spaces:
Running
Running
real html compare
Browse files
components/editor/ask-ai/index.tsx
CHANGED
|
@@ -1,13 +1,12 @@
|
|
| 1 |
"use client";
|
| 2 |
/* eslint-disable @typescript-eslint/no-explicit-any */
|
| 3 |
-
import { useState, useRef } from "react";
|
| 4 |
import classNames from "classnames";
|
| 5 |
import { toast } from "sonner";
|
| 6 |
import { useLocalStorage, useUpdateEffect } from "react-use";
|
| 7 |
import { ArrowUp, ChevronDown, Crosshair } from "lucide-react";
|
| 8 |
import { FaStopCircle } from "react-icons/fa";
|
| 9 |
|
| 10 |
-
import { defaultHTML } from "@/lib/consts";
|
| 11 |
import ProModal from "@/components/pro-modal";
|
| 12 |
import { Button } from "@/components/ui/button";
|
| 13 |
import { MODELS } from "@/lib/providers";
|
|
@@ -22,6 +21,7 @@ import { Tooltip, TooltipTrigger } from "@/components/ui/tooltip";
|
|
| 22 |
import { TooltipContent } from "@radix-ui/react-tooltip";
|
| 23 |
import { SelectedHtmlElement } from "./selected-html-element";
|
| 24 |
import { FollowUpTooltip } from "./follow-up-tooltip";
|
|
|
|
| 25 |
|
| 26 |
export function AskAI({
|
| 27 |
html,
|
|
@@ -84,7 +84,7 @@ export function AskAI({
|
|
| 84 |
setController(abortController);
|
| 85 |
try {
|
| 86 |
onNewPrompt(prompt);
|
| 87 |
-
if (isFollowUp && !redesignMarkdown &&
|
| 88 |
const selectedElementHtml = selectedElement
|
| 89 |
? selectedElement.outerHTML
|
| 90 |
: "";
|
|
@@ -135,7 +135,7 @@ export function AskAI({
|
|
| 135 |
prompt,
|
| 136 |
provider,
|
| 137 |
model,
|
| 138 |
-
html:
|
| 139 |
redesignMarkdown,
|
| 140 |
}),
|
| 141 |
headers: {
|
|
@@ -276,6 +276,10 @@ export function AskAI({
|
|
| 276 |
}
|
| 277 |
}, [isThinking]);
|
| 278 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 279 |
return (
|
| 280 |
<div className="px-3">
|
| 281 |
<div className="relative bg-neutral-800 border border-neutral-700 rounded-2xl ring-[4px] focus-within:ring-neutral-500/30 focus-within:border-neutral-600 ring-transparent z-10 w-full group">
|
|
@@ -371,7 +375,7 @@ export function AskAI({
|
|
| 371 |
<div className="flex items-center justify-between gap-2 px-4 pb-3">
|
| 372 |
<div className="flex-1 flex items-center justify-start gap-1.5">
|
| 373 |
<ReImagine onRedesign={(md) => callAi(md)} />
|
| 374 |
-
{
|
| 375 |
<Tooltip>
|
| 376 |
<TooltipTrigger asChild>
|
| 377 |
<Button
|
|
@@ -408,7 +412,7 @@ export function AskAI({
|
|
| 408 |
onModelChange={setModel}
|
| 409 |
open={openProvider}
|
| 410 |
error={providerError}
|
| 411 |
-
isFollowUp={
|
| 412 |
onClose={setOpenProvider}
|
| 413 |
/>
|
| 414 |
<Button
|
|
@@ -426,7 +430,7 @@ export function AskAI({
|
|
| 426 |
open={openProModal}
|
| 427 |
onClose={() => setOpenProModal(false)}
|
| 428 |
/>
|
| 429 |
-
{
|
| 430 |
<div className="absolute top-0 right-0 -translate-y-[calc(100%+8px)] select-none text-xs text-neutral-400 flex items-center justify-center gap-2 bg-neutral-800 border border-neutral-700 rounded-md p-1 pr-2.5">
|
| 431 |
<label
|
| 432 |
htmlFor="diff-patch-checkbox"
|
|
|
|
| 1 |
"use client";
|
| 2 |
/* eslint-disable @typescript-eslint/no-explicit-any */
|
| 3 |
+
import { useState, useRef, useMemo } from "react";
|
| 4 |
import classNames from "classnames";
|
| 5 |
import { toast } from "sonner";
|
| 6 |
import { useLocalStorage, useUpdateEffect } from "react-use";
|
| 7 |
import { ArrowUp, ChevronDown, Crosshair } from "lucide-react";
|
| 8 |
import { FaStopCircle } from "react-icons/fa";
|
| 9 |
|
|
|
|
| 10 |
import ProModal from "@/components/pro-modal";
|
| 11 |
import { Button } from "@/components/ui/button";
|
| 12 |
import { MODELS } from "@/lib/providers";
|
|
|
|
| 21 |
import { TooltipContent } from "@radix-ui/react-tooltip";
|
| 22 |
import { SelectedHtmlElement } from "./selected-html-element";
|
| 23 |
import { FollowUpTooltip } from "./follow-up-tooltip";
|
| 24 |
+
import { isTheSameHtml } from "@/lib/compare-html-diff";
|
| 25 |
|
| 26 |
export function AskAI({
|
| 27 |
html,
|
|
|
|
| 84 |
setController(abortController);
|
| 85 |
try {
|
| 86 |
onNewPrompt(prompt);
|
| 87 |
+
if (isFollowUp && !redesignMarkdown && !isSameHtml) {
|
| 88 |
const selectedElementHtml = selectedElement
|
| 89 |
? selectedElement.outerHTML
|
| 90 |
: "";
|
|
|
|
| 135 |
prompt,
|
| 136 |
provider,
|
| 137 |
model,
|
| 138 |
+
html: isSameHtml ? "" : html,
|
| 139 |
redesignMarkdown,
|
| 140 |
}),
|
| 141 |
headers: {
|
|
|
|
| 276 |
}
|
| 277 |
}, [isThinking]);
|
| 278 |
|
| 279 |
+
const isSameHtml = useMemo(() => {
|
| 280 |
+
return isTheSameHtml(html);
|
| 281 |
+
}, [html]);
|
| 282 |
+
|
| 283 |
return (
|
| 284 |
<div className="px-3">
|
| 285 |
<div className="relative bg-neutral-800 border border-neutral-700 rounded-2xl ring-[4px] focus-within:ring-neutral-500/30 focus-within:border-neutral-600 ring-transparent z-10 w-full group">
|
|
|
|
| 375 |
<div className="flex items-center justify-between gap-2 px-4 pb-3">
|
| 376 |
<div className="flex-1 flex items-center justify-start gap-1.5">
|
| 377 |
<ReImagine onRedesign={(md) => callAi(md)} />
|
| 378 |
+
{!isSameHtml && (
|
| 379 |
<Tooltip>
|
| 380 |
<TooltipTrigger asChild>
|
| 381 |
<Button
|
|
|
|
| 412 |
onModelChange={setModel}
|
| 413 |
open={openProvider}
|
| 414 |
error={providerError}
|
| 415 |
+
isFollowUp={!isSameHtml}
|
| 416 |
onClose={setOpenProvider}
|
| 417 |
/>
|
| 418 |
<Button
|
|
|
|
| 430 |
open={openProModal}
|
| 431 |
onClose={() => setOpenProModal(false)}
|
| 432 |
/>
|
| 433 |
+
{!isSameHtml && (
|
| 434 |
<div className="absolute top-0 right-0 -translate-y-[calc(100%+8px)] select-none text-xs text-neutral-400 flex items-center justify-center gap-2 bg-neutral-800 border border-neutral-700 rounded-md p-1 pr-2.5">
|
| 435 |
<label
|
| 436 |
htmlFor="diff-patch-checkbox"
|
components/editor/index.tsx
CHANGED
|
@@ -25,6 +25,7 @@ import { DeployButton } from "./deploy-button";
|
|
| 25 |
import { Project } from "@/types";
|
| 26 |
import { SaveButton } from "./save-button";
|
| 27 |
import { LoadProject } from "../my-projects/load-project";
|
|
|
|
| 28 |
|
| 29 |
export const AppEditor = ({ project }: { project?: Project | null }) => {
|
| 30 |
const [htmlStorage, , removeHtmlStorage] = useLocalStorage("html_content");
|
|
@@ -148,7 +149,7 @@ export const AppEditor = ({ project }: { project?: Project | null }) => {
|
|
| 148 |
|
| 149 |
// Prevent accidental navigation away when AI is working or content has changed
|
| 150 |
useEvent("beforeunload", (e) => {
|
| 151 |
-
if (isAiWorking || html
|
| 152 |
e.preventDefault();
|
| 153 |
return "";
|
| 154 |
}
|
|
|
|
| 25 |
import { Project } from "@/types";
|
| 26 |
import { SaveButton } from "./save-button";
|
| 27 |
import { LoadProject } from "../my-projects/load-project";
|
| 28 |
+
import { isTheSameHtml } from "@/lib/compare-html-diff";
|
| 29 |
|
| 30 |
export const AppEditor = ({ project }: { project?: Project | null }) => {
|
| 31 |
const [htmlStorage, , removeHtmlStorage] = useLocalStorage("html_content");
|
|
|
|
| 149 |
|
| 150 |
// Prevent accidental navigation away when AI is working or content has changed
|
| 151 |
useEvent("beforeunload", (e) => {
|
| 152 |
+
if (isAiWorking || !isTheSameHtml(html)) {
|
| 153 |
e.preventDefault();
|
| 154 |
return "";
|
| 155 |
}
|
components/login-modal/index.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
import { useLocalStorage } from "react-use";
|
| 2 |
-
import { defaultHTML } from "@/lib/consts";
|
| 3 |
import { Button } from "@/components/ui/button";
|
| 4 |
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
| 5 |
import { useUser } from "@/hooks/useUser";
|
|
|
|
| 6 |
|
| 7 |
export const LoginModal = ({
|
| 8 |
open,
|
|
@@ -20,7 +20,7 @@ export const LoginModal = ({
|
|
| 20 |
const { openLoginWindow } = useUser();
|
| 21 |
const [, setStorage] = useLocalStorage("html_content");
|
| 22 |
const handleClick = async () => {
|
| 23 |
-
if (html && html
|
| 24 |
setStorage(html);
|
| 25 |
}
|
| 26 |
openLoginWindow();
|
|
|
|
| 1 |
import { useLocalStorage } from "react-use";
|
|
|
|
| 2 |
import { Button } from "@/components/ui/button";
|
| 3 |
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
| 4 |
import { useUser } from "@/hooks/useUser";
|
| 5 |
+
import { isTheSameHtml } from "@/lib/compare-html-diff";
|
| 6 |
|
| 7 |
export const LoginModal = ({
|
| 8 |
open,
|
|
|
|
| 20 |
const { openLoginWindow } = useUser();
|
| 21 |
const [, setStorage] = useLocalStorage("html_content");
|
| 22 |
const handleClick = async () => {
|
| 23 |
+
if (html && !isTheSameHtml(html)) {
|
| 24 |
setStorage(html);
|
| 25 |
}
|
| 26 |
openLoginWindow();
|
components/pro-modal/index.tsx
CHANGED
|
@@ -1,8 +1,8 @@
|
|
| 1 |
import { useLocalStorage } from "react-use";
|
| 2 |
-
import { defaultHTML } from "@/lib/consts";
|
| 3 |
import { Button } from "../ui/button";
|
| 4 |
import { Dialog, DialogContent } from "../ui/dialog";
|
| 5 |
import { CheckCheck } from "lucide-react";
|
|
|
|
| 6 |
|
| 7 |
export const ProModal = ({
|
| 8 |
open,
|
|
@@ -15,7 +15,7 @@ export const ProModal = ({
|
|
| 15 |
}) => {
|
| 16 |
const [, setStorage] = useLocalStorage("html_content");
|
| 17 |
const handleProClick = () => {
|
| 18 |
-
if (html
|
| 19 |
setStorage(html);
|
| 20 |
}
|
| 21 |
window.open("https://huggingface.co/subscribe/pro?from=DeepSite", "_blank");
|
|
|
|
| 1 |
import { useLocalStorage } from "react-use";
|
|
|
|
| 2 |
import { Button } from "../ui/button";
|
| 3 |
import { Dialog, DialogContent } from "../ui/dialog";
|
| 4 |
import { CheckCheck } from "lucide-react";
|
| 5 |
+
import { isTheSameHtml } from "@/lib/compare-html-diff";
|
| 6 |
|
| 7 |
export const ProModal = ({
|
| 8 |
open,
|
|
|
|
| 15 |
}) => {
|
| 16 |
const [, setStorage] = useLocalStorage("html_content");
|
| 17 |
const handleProClick = () => {
|
| 18 |
+
if (!isTheSameHtml(html)) {
|
| 19 |
setStorage(html);
|
| 20 |
}
|
| 21 |
window.open("https://huggingface.co/subscribe/pro?from=DeepSite", "_blank");
|
lib/compare-html-diff.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import { defaultHTML } from "./consts";
|
| 2 |
+
|
| 3 |
+
export const isTheSameHtml = (currentHtml: string): boolean => {
|
| 4 |
+
const normalize = (html: string): string =>
|
| 5 |
+
html
|
| 6 |
+
.replace(/<!--[\s\S]*?-->/g, "")
|
| 7 |
+
.replace(/\s+/g, " ")
|
| 8 |
+
.trim();
|
| 9 |
+
|
| 10 |
+
return normalize(defaultHTML) === normalize(currentHtml);
|
| 11 |
+
};
|