Update Dockerfile

#452
by XciD HF Staff - opened
Files changed (3) hide show
  1. Dockerfile +11 -14
  2. app/api/ask/route.ts +119 -60
  3. hooks/useAi.ts +3 -9
Dockerfile CHANGED
@@ -1,19 +1,16 @@
1
- FROM node:20-alpine
2
- USER root
3
 
4
- USER 1000
5
- WORKDIR /usr/src/app
6
- # Copy package.json and package-lock.json to the container
7
- COPY --chown=1000 package.json package-lock.json ./
8
 
9
- # Copy the rest of the application files to the container
10
- COPY --chown=1000 . .
11
 
12
- RUN npm install
13
- RUN npm run build
14
 
15
- # Expose the application port (assuming your app runs on port 3000)
16
- EXPOSE 3000
17
 
18
- # Start the application
19
- CMD ["npm", "start"]
 
1
+ FROM registry.hf.space/enzostvs-deepsite:cpu-bbc7882
2
+ # FROM node:22
3
 
4
+ # USER 1000
5
+ # WORKDIR /usr/src/app
 
 
6
 
7
+ # # Copy the rest of the application files to the container
8
+ # COPY --chown=1000 . .
9
 
10
+ # RUN npm ci && npm run build
 
11
 
12
+ # # Expose the application port (assuming your app runs on port 3000)
13
+ # EXPOSE 3000
14
 
15
+ # # Start the application
16
+ # CMD ["npm", "start"]
app/api/ask/route.ts CHANGED
@@ -285,11 +285,35 @@ export async function PUT(request: NextRequest) {
285
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
286
  };
287
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
  const createFlexibleHtmlRegex = (searchBlock: string) => {
289
- let searchRegex = escapeRegExp(searchBlock)
290
- .replace(/\s+/g, '\\s*')
 
 
 
 
291
  .replace(/>\s*</g, '>\\s*<')
292
- .replace(/\s*>/g, '\\s*>');
 
293
 
294
  return new RegExp(searchRegex, 'g');
295
  };
@@ -300,41 +324,9 @@ export async function PUT(request: NextRequest) {
300
  const systemPrompt = FOLLOW_UP_SYSTEM_PROMPT + (isNew ? PROMPT_FOR_PROJECT_NAME : "");
301
  const userContext = "You are modifying the HTML file based on the user's request.";
302
 
303
- const getRelevantPages = (pages: Page[], prompt: string, maxPages: number = 2): Page[] => {
304
- if (pages.length <= maxPages) return pages;
305
-
306
- const indexPage = pages.find(p => p.path === '/' || p.path === '/index' || p.path === 'index');
307
- const otherPages = pages.filter(p => p !== indexPage);
308
-
309
- if (selectedElementHtml) {
310
- const elementKeywords = selectedElementHtml.toLowerCase().match(/class=["']([^"']*)["']|id=["']([^"']*)["']/g) || [];
311
- const relevantPages = otherPages.filter(page => {
312
- const pageContent = page.html.toLowerCase();
313
- return elementKeywords.some((keyword: string) => pageContent.includes(keyword.toLowerCase()));
314
- });
315
-
316
- return indexPage ? [indexPage, ...relevantPages.slice(0, maxPages - 1)] : relevantPages.slice(0, maxPages);
317
- }
318
-
319
- const keywords = prompt.toLowerCase().split(/\s+/).filter(word => word.length > 3);
320
- const scoredPages = otherPages.map(page => {
321
- const pageContent = (page.path + ' ' + page.html).toLowerCase();
322
- const score = keywords.reduce((acc, keyword) => {
323
- return acc + (pageContent.includes(keyword) ? 1 : 0);
324
- }, 0);
325
- return { page, score };
326
- });
327
-
328
- const topPages = scoredPages
329
- .sort((a, b) => b.score - a.score)
330
- .slice(0, maxPages - (indexPage ? 1 : 0))
331
- .map(item => item.page);
332
-
333
- return indexPage ? [indexPage, ...topPages] : topPages;
334
- };
335
-
336
- const relevantPages = getRelevantPages(pages || [], prompt);
337
- const pagesContext = relevantPages
338
  .map((p: Page) => `- ${p.path}\n${p.html}`)
339
  .join("\n\n");
340
 
@@ -342,7 +334,7 @@ export async function PUT(request: NextRequest) {
342
  selectedElementHtml
343
  ? `\n\nYou have to update ONLY the following element, NOTHING ELSE: \n\n\`\`\`html\n${selectedElementHtml}\n\`\`\` Could be in multiple pages, if so, update all the pages.`
344
  : ""
345
- }. Current pages (${relevantPages.length}/${pages?.length || 0} shown): ${pagesContext}. ${files?.length > 0 ? `Available images: ${files?.map((f: string) => f).join(', ')}.` : ""}`;
346
 
347
  const estimatedInputTokens = estimateInputTokens(systemPrompt, prompt, userContext + assistantContext);
348
  const dynamicMaxTokens = calculateMaxTokens(selectedProvider, estimatedInputTokens, false);
@@ -450,17 +442,49 @@ export async function PUT(request: NextRequest) {
450
  updatedLines.push([1, replaceBlock.split("\n").length]);
451
  } else {
452
  const regex = createFlexibleHtmlRegex(searchBlock);
453
- const match = regex.exec(pageHtml);
 
 
 
454
 
455
  if (match) {
456
- const matchedText = match[0];
457
- const beforeText = pageHtml.substring(0, match.index);
458
- const startLineNumber = beforeText.split("\n").length;
459
- const replaceLines = replaceBlock.split("\n").length;
460
- const endLineNumber = startLineNumber + replaceLines - 1;
461
-
462
- updatedLines.push([startLineNumber, endLineNumber]);
463
- pageHtml = pageHtml.replace(matchedText, replaceBlock);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  }
465
  }
466
 
@@ -539,17 +563,54 @@ export async function PUT(request: NextRequest) {
539
  updatedLines.push([1, replaceBlock.split("\n").length]);
540
  } else {
541
  const regex = createFlexibleHtmlRegex(searchBlock);
542
- const match = regex.exec(newHtml);
 
 
 
 
 
 
 
 
 
543
 
544
  if (match) {
545
- const matchedText = match[0];
546
- const beforeText = newHtml.substring(0, match.index);
547
- const startLineNumber = beforeText.split("\n").length;
548
- const replaceLines = replaceBlock.split("\n").length;
549
- const endLineNumber = startLineNumber + replaceLines - 1;
550
-
551
- updatedLines.push([startLineNumber, endLineNumber]);
552
- newHtml = newHtml.replace(matchedText, replaceBlock);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
553
  }
554
  }
555
 
@@ -598,7 +659,6 @@ pinned: false
598
  tags:
599
  - deepsite-v3
600
  ---
601
-
602
  # Welcome to your new DeepSite project!
603
  This project was created with [DeepSite](https://deepsite.hf.co).
604
  `;
@@ -652,5 +712,4 @@ This project was created with [DeepSite](https://deepsite.hf.co).
652
  { status: 500 }
653
  );
654
  }
655
- }
656
-
 
285
  return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
286
  };
287
 
288
+ const normalizeHtml = (html: string): string => {
289
+ return html
290
+ // Normalize whitespace within tags
291
+ .replace(/\s+/g, ' ')
292
+ // Remove spaces before closing >
293
+ .replace(/\s+>/g, '>')
294
+ // Remove spaces before />
295
+ .replace(/\s+\/>/g, '/>')
296
+ // Normalize spaces around = in attributes
297
+ .replace(/\s*=\s*/g, '=')
298
+ // Normalize quotes (convert single to double)
299
+ .replace(/='([^']*)'/g, '="$1"')
300
+ // Remove trailing spaces in opening/closing tags
301
+ .replace(/<([^>]*?)\s+>/g, '<$1>')
302
+ // Normalize self-closing tags
303
+ .replace(/\/\s*>/g, '/>')
304
+ .trim();
305
+ };
306
+
307
  const createFlexibleHtmlRegex = (searchBlock: string) => {
308
+ // Normalize both the search block for comparison
309
+ const normalizedSearch = normalizeHtml(searchBlock);
310
+
311
+ // Escape regex special characters
312
+ let searchRegex = escapeRegExp(normalizedSearch)
313
+ // Make whitespace flexible (but only between elements, not within tags)
314
  .replace(/>\s*</g, '>\\s*<')
315
+ // Make line breaks and spaces around content flexible
316
+ .replace(/>\s*([^<]+)\s*</g, '>\\s*$1\\s*<');
317
 
318
  return new RegExp(searchRegex, 'g');
319
  };
 
324
  const systemPrompt = FOLLOW_UP_SYSTEM_PROMPT + (isNew ? PROMPT_FOR_PROJECT_NAME : "");
325
  const userContext = "You are modifying the HTML file based on the user's request.";
326
 
327
+ // Send all pages without filtering
328
+ const allPages = pages || [];
329
+ const pagesContext = allPages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  .map((p: Page) => `- ${p.path}\n${p.html}`)
331
  .join("\n\n");
332
 
 
334
  selectedElementHtml
335
  ? `\n\nYou have to update ONLY the following element, NOTHING ELSE: \n\n\`\`\`html\n${selectedElementHtml}\n\`\`\` Could be in multiple pages, if so, update all the pages.`
336
  : ""
337
+ }. Current pages (${allPages.length} total): ${pagesContext}. ${files?.length > 0 ? `Available images: ${files?.map((f: string) => f).join(', ')}.` : ""}`;
338
 
339
  const estimatedInputTokens = estimateInputTokens(systemPrompt, prompt, userContext + assistantContext);
340
  const dynamicMaxTokens = calculateMaxTokens(selectedProvider, estimatedInputTokens, false);
 
442
  updatedLines.push([1, replaceBlock.split("\n").length]);
443
  } else {
444
  const regex = createFlexibleHtmlRegex(searchBlock);
445
+
446
+ // Normalize the pageHtml for matching
447
+ const normalizedPageHtml = normalizeHtml(pageHtml);
448
+ const match = regex.exec(normalizedPageHtml);
449
 
450
  if (match) {
451
+ // Find the original match in the non-normalized HTML
452
+ const normalizedSearch = normalizeHtml(searchBlock);
453
+ const originalMatchIndex = pageHtml.indexOf(searchBlock);
454
+
455
+ if (originalMatchIndex !== -1) {
456
+ const beforeText = pageHtml.substring(0, originalMatchIndex);
457
+ const startLineNumber = beforeText.split("\n").length;
458
+ const replaceLines = replaceBlock.split("\n").length;
459
+ const endLineNumber = startLineNumber + replaceLines - 1;
460
+
461
+ updatedLines.push([startLineNumber, endLineNumber]);
462
+ pageHtml = pageHtml.replace(searchBlock, replaceBlock);
463
+ } else {
464
+ // Fallback: try to find similar pattern in the original HTML
465
+ const flexibleRegex = new RegExp(
466
+ escapeRegExp(searchBlock)
467
+ .replace(/\s+/g, '\\s+')
468
+ .replace(/\s*=\s*/g, '\\s*=\\s*')
469
+ .replace(/'\s*([^']*)\s*'/g, "'\\s*$1\\s*'")
470
+ .replace(/"\s*([^"]*)\s*"/g, '"\\s*$1\\s*"')
471
+ .replace(/\s*>/g, '\\s*>')
472
+ .replace(/\s*\/>/g, '\\s*/>'),
473
+ 'g'
474
+ );
475
+
476
+ const flexibleMatch = flexibleRegex.exec(pageHtml);
477
+ if (flexibleMatch) {
478
+ const matchedText = flexibleMatch[0];
479
+ const beforeText = pageHtml.substring(0, flexibleMatch.index);
480
+ const startLineNumber = beforeText.split("\n").length;
481
+ const replaceLines = replaceBlock.split("\n").length;
482
+ const endLineNumber = startLineNumber + replaceLines - 1;
483
+
484
+ updatedLines.push([startLineNumber, endLineNumber]);
485
+ pageHtml = pageHtml.replace(matchedText, replaceBlock);
486
+ }
487
+ }
488
  }
489
  }
490
 
 
563
  updatedLines.push([1, replaceBlock.split("\n").length]);
564
  } else {
565
  const regex = createFlexibleHtmlRegex(searchBlock);
566
+
567
+ // Get the main page HTML (first page or index page)
568
+ const mainPage = updatedPages.find(p => p.path === '/' || p.path === '/index' || p.path === 'index') || updatedPages[0];
569
+ if (!mainPage) continue;
570
+
571
+ newHtml = mainPage.html;
572
+
573
+ // Normalize the newHtml for matching
574
+ const normalizedNewHtml = normalizeHtml(newHtml);
575
+ const match = regex.exec(normalizedNewHtml);
576
 
577
  if (match) {
578
+ // Find the original match in the non-normalized HTML
579
+ const originalMatchIndex = newHtml.indexOf(searchBlock);
580
+
581
+ if (originalMatchIndex !== -1) {
582
+ const beforeText = newHtml.substring(0, originalMatchIndex);
583
+ const startLineNumber = beforeText.split("\n").length;
584
+ const replaceLines = replaceBlock.split("\n").length;
585
+ const endLineNumber = startLineNumber + replaceLines - 1;
586
+
587
+ updatedLines.push([startLineNumber, endLineNumber]);
588
+ newHtml = newHtml.replace(searchBlock, replaceBlock);
589
+ } else {
590
+ // Fallback: try to find similar pattern in the original HTML
591
+ const flexibleRegex = new RegExp(
592
+ escapeRegExp(searchBlock)
593
+ .replace(/\s+/g, '\\s+')
594
+ .replace(/\s*=\s*/g, '\\s*=\\s*')
595
+ .replace(/'\s*([^']*)\s*'/g, "'\\s*$1\\s*'")
596
+ .replace(/"\s*([^"]*)\s*"/g, '"\\s*$1\\s*"')
597
+ .replace(/\s*>/g, '\\s*>')
598
+ .replace(/\s*\/>/g, '\\s*/>'),
599
+ 'g'
600
+ );
601
+
602
+ const flexibleMatch = flexibleRegex.exec(newHtml);
603
+ if (flexibleMatch) {
604
+ const matchedText = flexibleMatch[0];
605
+ const beforeText = newHtml.substring(0, flexibleMatch.index);
606
+ const startLineNumber = beforeText.split("\n").length;
607
+ const replaceLines = replaceBlock.split("\n").length;
608
+ const endLineNumber = startLineNumber + replaceLines - 1;
609
+
610
+ updatedLines.push([startLineNumber, endLineNumber]);
611
+ newHtml = newHtml.replace(matchedText, replaceBlock);
612
+ }
613
+ }
614
  }
615
  }
616
 
 
659
  tags:
660
  - deepsite-v3
661
  ---
 
662
  # Welcome to your new DeepSite project!
663
  This project was created with [DeepSite](https://deepsite.hf.co).
664
  `;
 
712
  { status: 500 }
713
  );
714
  }
715
+ }
 
hooks/useAi.ts CHANGED
@@ -10,7 +10,6 @@ import { api } from "@/lib/api";
10
  import { useRouter } from "next/navigation";
11
  import { useUser } from "./useUser";
12
  import { LivePreviewRef } from "@/components/editor/live-preview";
13
- import { isTheSameHtml } from "@/lib/compare-html-diff";
14
 
15
  export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefObject<LivePreviewRef | null>) => {
16
  const client = useQueryClient();
@@ -199,15 +198,10 @@ export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefO
199
  }
200
 
201
  const newPages = formatPages(contentResponse);
202
- let projectName = contentResponse.match(/<<<<<<< PROJECT_NAME_START ([\s\S]*?) >>>>>>> PROJECT_NAME_END/)?.[1]?.trim();
203
- if (!projectName) {
204
- projectName = prompt.substring(0, 40).replace(/[^a-zA-Z0-9]/g, "-").slice(0, 40);
205
- }
206
  setPages(newPages);
207
- setLastSavedPages([...newPages]);
208
- if (newPages.length > 0 && !isTheSameHtml(newPages[0].html)) {
209
- createNewProject(prompt, newPages, projectName, isLoggedIn);
210
- }
211
  setPrompts([...prompts, prompt]);
212
 
213
  return { success: true, pages: newPages };
 
10
  import { useRouter } from "next/navigation";
11
  import { useUser } from "./useUser";
12
  import { LivePreviewRef } from "@/components/editor/live-preview";
 
13
 
14
  export const useAi = (onScrollToBottom?: () => void, livePreviewRef?: React.RefObject<LivePreviewRef | null>) => {
15
  const client = useQueryClient();
 
198
  }
199
 
200
  const newPages = formatPages(contentResponse);
201
+ const projectName = contentResponse.match(/<<<<<<< PROJECT_NAME_START ([\s\S]*?) >>>>>>> PROJECT_NAME_END/)?.[1]?.trim();
 
 
 
202
  setPages(newPages);
203
+ setLastSavedPages([...newPages]); // Mark initial pages as saved
204
+ createNewProject(prompt, newPages, projectName, isLoggedIn);
 
 
205
  setPrompts([...prompts, prompt]);
206
 
207
  return { success: true, pages: newPages };