Spaces:
				
			
			
	
			
			
					
		Running
		
	
	
	
			
			
	
	
	
	
		
		
					
		Running
		
	Update Dockerfile
#446
by
						
XciD
	
							HF Staff
						- opened
							
					
- Dockerfile +2 -6
- app/api/ask/route.ts +114 -21
- hooks/useAi.ts +3 -9
    	
        Dockerfile
    CHANGED
    
    | @@ -1,16 +1,12 @@ | |
| 1 | 
            -
            FROM node: | 
| 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
         | 
|  | |
| 1 | 
            +
            FROM node:22
         | 
|  | |
| 2 |  | 
| 3 | 
             
            USER 1000
         | 
| 4 | 
             
            WORKDIR /usr/src/app
         | 
|  | |
|  | |
| 5 |  | 
| 6 | 
             
            # Copy the rest of the application files to the container
         | 
| 7 | 
             
            COPY --chown=1000 . .
         | 
| 8 |  | 
| 9 | 
            +
            RUN npm install && npm run build
         | 
|  | |
| 10 |  | 
| 11 | 
             
            # Expose the application port (assuming your app runs on port 3000)
         | 
| 12 | 
             
            EXPOSE 3000
         | 
    	
        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 | 
            -
                 | 
| 290 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
| 291 | 
             
                  .replace(/>\s*</g, '>\\s*<')
         | 
| 292 | 
            -
                   | 
|  | |
| 293 |  | 
| 294 | 
             
                return new RegExp(searchRegex, 'g');
         | 
| 295 | 
             
              };
         | 
| @@ -450,17 +474,49 @@ export async function PUT(request: NextRequest) { | |
| 450 | 
             
                          updatedLines.push([1, replaceBlock.split("\n").length]);
         | 
| 451 | 
             
                        } else {
         | 
| 452 | 
             
                          const regex = createFlexibleHtmlRegex(searchBlock);
         | 
| 453 | 
            -
                           | 
|  | |
|  | |
|  | |
| 454 |  | 
| 455 | 
             
                          if (match) {
         | 
| 456 | 
            -
                             | 
| 457 | 
            -
                            const  | 
| 458 | 
            -
                            const  | 
| 459 | 
            -
                             | 
| 460 | 
            -
                             | 
| 461 | 
            -
             | 
| 462 | 
            -
             | 
| 463 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 464 | 
             
                          }
         | 
| 465 | 
             
                        }
         | 
| 466 |  | 
| @@ -539,17 +595,54 @@ export async function PUT(request: NextRequest) { | |
| 539 | 
             
                        updatedLines.push([1, replaceBlock.split("\n").length]);
         | 
| 540 | 
             
                      } else {
         | 
| 541 | 
             
                        const regex = createFlexibleHtmlRegex(searchBlock);
         | 
| 542 | 
            -
                         | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 543 |  | 
| 544 | 
             
                        if (match) {
         | 
| 545 | 
            -
                           | 
| 546 | 
            -
                          const  | 
| 547 | 
            -
                           | 
| 548 | 
            -
                           | 
| 549 | 
            -
             | 
| 550 | 
            -
             | 
| 551 | 
            -
             | 
| 552 | 
            -
             | 
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
|  | |
| 553 | 
             
                        }
         | 
| 554 | 
             
                      }
         | 
| 555 |  | 
|  | |
| 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 | 
             
              };
         | 
|  | |
| 474 | 
             
                          updatedLines.push([1, replaceBlock.split("\n").length]);
         | 
| 475 | 
             
                        } else {
         | 
| 476 | 
             
                          const regex = createFlexibleHtmlRegex(searchBlock);
         | 
| 477 | 
            +
                          
         | 
| 478 | 
            +
                          // Normalize the pageHtml for matching
         | 
| 479 | 
            +
                          const normalizedPageHtml = normalizeHtml(pageHtml);
         | 
| 480 | 
            +
                          const match = regex.exec(normalizedPageHtml);
         | 
| 481 |  | 
| 482 | 
             
                          if (match) {
         | 
| 483 | 
            +
                            // Find the original match in the non-normalized HTML
         | 
| 484 | 
            +
                            const normalizedSearch = normalizeHtml(searchBlock);
         | 
| 485 | 
            +
                            const originalMatchIndex = pageHtml.indexOf(searchBlock);
         | 
| 486 | 
            +
                            
         | 
| 487 | 
            +
                            if (originalMatchIndex !== -1) {
         | 
| 488 | 
            +
                              const beforeText = pageHtml.substring(0, originalMatchIndex);
         | 
| 489 | 
            +
                              const startLineNumber = beforeText.split("\n").length;
         | 
| 490 | 
            +
                              const replaceLines = replaceBlock.split("\n").length;
         | 
| 491 | 
            +
                              const endLineNumber = startLineNumber + replaceLines - 1;
         | 
| 492 | 
            +
             | 
| 493 | 
            +
                              updatedLines.push([startLineNumber, endLineNumber]);
         | 
| 494 | 
            +
                              pageHtml = pageHtml.replace(searchBlock, replaceBlock);
         | 
| 495 | 
            +
                            } else {
         | 
| 496 | 
            +
                              // Fallback: try to find similar pattern in the original HTML
         | 
| 497 | 
            +
                              const flexibleRegex = new RegExp(
         | 
| 498 | 
            +
                                escapeRegExp(searchBlock)
         | 
| 499 | 
            +
                                  .replace(/\s+/g, '\\s+')
         | 
| 500 | 
            +
                                  .replace(/\s*=\s*/g, '\\s*=\\s*')
         | 
| 501 | 
            +
                                  .replace(/'\s*([^']*)\s*'/g, "'\\s*$1\\s*'")
         | 
| 502 | 
            +
                                  .replace(/"\s*([^"]*)\s*"/g, '"\\s*$1\\s*"')
         | 
| 503 | 
            +
                                  .replace(/\s*>/g, '\\s*>')
         | 
| 504 | 
            +
                                  .replace(/\s*\/>/g, '\\s*/>'),
         | 
| 505 | 
            +
                                'g'
         | 
| 506 | 
            +
                              );
         | 
| 507 | 
            +
                              
         | 
| 508 | 
            +
                              const flexibleMatch = flexibleRegex.exec(pageHtml);
         | 
| 509 | 
            +
                              if (flexibleMatch) {
         | 
| 510 | 
            +
                                const matchedText = flexibleMatch[0];
         | 
| 511 | 
            +
                                const beforeText = pageHtml.substring(0, flexibleMatch.index);
         | 
| 512 | 
            +
                                const startLineNumber = beforeText.split("\n").length;
         | 
| 513 | 
            +
                                const replaceLines = replaceBlock.split("\n").length;
         | 
| 514 | 
            +
                                const endLineNumber = startLineNumber + replaceLines - 1;
         | 
| 515 | 
            +
             | 
| 516 | 
            +
                                updatedLines.push([startLineNumber, endLineNumber]);
         | 
| 517 | 
            +
                                pageHtml = pageHtml.replace(matchedText, replaceBlock);
         | 
| 518 | 
            +
                              }
         | 
| 519 | 
            +
                            }
         | 
| 520 | 
             
                          }
         | 
| 521 | 
             
                        }
         | 
| 522 |  | 
|  | |
| 595 | 
             
                        updatedLines.push([1, replaceBlock.split("\n").length]);
         | 
| 596 | 
             
                      } else {
         | 
| 597 | 
             
                        const regex = createFlexibleHtmlRegex(searchBlock);
         | 
| 598 | 
            +
                        
         | 
| 599 | 
            +
                        // Get the main page HTML (first page or index page)
         | 
| 600 | 
            +
                        const mainPage = updatedPages.find(p => p.path === '/' || p.path === '/index' || p.path === 'index') || updatedPages[0];
         | 
| 601 | 
            +
                        if (!mainPage) continue;
         | 
| 602 | 
            +
                        
         | 
| 603 | 
            +
                        newHtml = mainPage.html;
         | 
| 604 | 
            +
                        
         | 
| 605 | 
            +
                        // Normalize the newHtml for matching  
         | 
| 606 | 
            +
                        const normalizedNewHtml = normalizeHtml(newHtml);
         | 
| 607 | 
            +
                        const match = regex.exec(normalizedNewHtml);
         | 
| 608 |  | 
| 609 | 
             
                        if (match) {
         | 
| 610 | 
            +
                          // Find the original match in the non-normalized HTML
         | 
| 611 | 
            +
                          const originalMatchIndex = newHtml.indexOf(searchBlock);
         | 
| 612 | 
            +
                          
         | 
| 613 | 
            +
                          if (originalMatchIndex !== -1) {
         | 
| 614 | 
            +
                            const beforeText = newHtml.substring(0, originalMatchIndex);
         | 
| 615 | 
            +
                            const startLineNumber = beforeText.split("\n").length;
         | 
| 616 | 
            +
                            const replaceLines = replaceBlock.split("\n").length;
         | 
| 617 | 
            +
                            const endLineNumber = startLineNumber + replaceLines - 1;
         | 
| 618 | 
            +
             | 
| 619 | 
            +
                            updatedLines.push([startLineNumber, endLineNumber]);
         | 
| 620 | 
            +
                            newHtml = newHtml.replace(searchBlock, replaceBlock);
         | 
| 621 | 
            +
                          } else {
         | 
| 622 | 
            +
                            // Fallback: try to find similar pattern in the original HTML
         | 
| 623 | 
            +
                            const flexibleRegex = new RegExp(
         | 
| 624 | 
            +
                              escapeRegExp(searchBlock)
         | 
| 625 | 
            +
                                .replace(/\s+/g, '\\s+')
         | 
| 626 | 
            +
                                .replace(/\s*=\s*/g, '\\s*=\\s*')
         | 
| 627 | 
            +
                                .replace(/'\s*([^']*)\s*'/g, "'\\s*$1\\s*'")
         | 
| 628 | 
            +
                                .replace(/"\s*([^"]*)\s*"/g, '"\\s*$1\\s*"')
         | 
| 629 | 
            +
                                .replace(/\s*>/g, '\\s*>')
         | 
| 630 | 
            +
                                .replace(/\s*\/>/g, '\\s*/>'),
         | 
| 631 | 
            +
                              'g'
         | 
| 632 | 
            +
                            );
         | 
| 633 | 
            +
                            
         | 
| 634 | 
            +
                            const flexibleMatch = flexibleRegex.exec(newHtml);
         | 
| 635 | 
            +
                            if (flexibleMatch) {
         | 
| 636 | 
            +
                              const matchedText = flexibleMatch[0];
         | 
| 637 | 
            +
                              const beforeText = newHtml.substring(0, flexibleMatch.index);
         | 
| 638 | 
            +
                              const startLineNumber = beforeText.split("\n").length;
         | 
| 639 | 
            +
                              const replaceLines = replaceBlock.split("\n").length;
         | 
| 640 | 
            +
                              const endLineNumber = startLineNumber + replaceLines - 1;
         | 
| 641 | 
            +
             | 
| 642 | 
            +
                              updatedLines.push([startLineNumber, endLineNumber]);
         | 
| 643 | 
            +
                              newHtml = newHtml.replace(matchedText, replaceBlock);
         | 
| 644 | 
            +
                            }
         | 
| 645 | 
            +
                          }
         | 
| 646 | 
             
                        }
         | 
| 647 | 
             
                      }
         | 
| 648 |  | 
    	
        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 | 
            -
                         | 
| 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 | 
            -
                         | 
| 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 };
         |