From 07ccdf4bd8f247803c2c4c29df7c9e341c3aa85a Mon Sep 17 00:00:00 2001 From: bender Date: Thu, 5 Mar 2026 04:40:07 +0000 Subject: [PATCH 1/2] Update src/app/layout.tsx --- src/app/layout.tsx | 103 ++++++++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 33 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index d880c4b..476f9c6 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,10 +5,20 @@ import { Roboto } from "next/font/google"; import "./globals.css"; import { ServiceWrapper } from "@/components/ServiceWrapper"; import Tag from "@/tag/Tag"; -import { Nunito } from "next/font/google"; +const halant = Halant({ + variable: "--font-halant", subsets: ["latin"], + weight: ["300", "400", "500", "600", "700"], +}); +const inter = Inter({ + variable: "--font-inter", subsets: ["latin"], +}); +const roboto = Roboto({ + variable: "--font-roboto", subsets: ["latin"], + weight: ["100", "300", "400", "500", "700", "900"], +}); export const metadata: Metadata = { title: "Liberty Landscaping | Southern Gardens & Authentic Landscape Design", description: "Handcrafted Southern landscapes rooted in tradition. Liberty Landscaping transforms outdoor spaces into cherished gathering places. Three generations of expertise serving the Southeast.", keywords: "landscaping, Southern gardens, landscape design, garden maintenance, hardscape, heritage plants, outdoor design, Southeast landscaping", metadataBase: new URL("https://libertyland.com"), @@ -31,11 +41,6 @@ export const metadata: Metadata = { } }; -const nunito = Nunito({ - variable: "--font-nunito", - subsets: ["latin"], -}); - export default function RootLayout({ children, }: Readonly<{ @@ -44,7 +49,9 @@ export default function RootLayout({ return ( - + {children} @@ -1656,7 +1663,9 @@ export default function RootLayout({ const getElementInfo = (element, assignId = false) => { const rect = element.getBoundingClientRect(); const tagName = element.tagName.toLowerCase(); - + const selector = getUniqueSelector(element, assignId); + const sectionId = getSectionId(element); + let className = undefined; try { if (element.className) { @@ -1684,7 +1693,8 @@ export default function RootLayout({ }; if (tagName === 'img') { - info.imageData = { + const originalSrc = extractOriginalUrl(element.src); + info.imageData = { src: originalSrc, alt: element.alt || undefined, naturalWidth: element.naturalWidth, @@ -1695,7 +1705,8 @@ export default function RootLayout({ if (tagName === 'video') { const rawSrc = element.src || element.currentSrc || (element.querySelector('source') && element.querySelector('source').src) || ''; - info.imageData = { + const resolvedSrc = extractOriginalUrl(rawSrc); + info.imageData = { src: resolvedSrc, alt: element.getAttribute('aria-label') || undefined, isBackground: false, @@ -1708,7 +1719,8 @@ export default function RootLayout({ if (backgroundImage && backgroundImage !== 'none') { const urlMatch = backgroundImage.match(/url(['"]?([^'")]+)['"]?)/); if (urlMatch) { - if (tagName !== 'img') { + const originalBgSrc = extractOriginalUrl(urlMatch[1]); + if (tagName !== 'img') { info.imageData = { src: originalBgSrc, isBackground: true @@ -1720,7 +1732,8 @@ export default function RootLayout({ } } - info.elementType = elementType; + const elementType = getElementType(element); + info.elementType = elementType; if (elementType === 'Button') { const buttonText = element.textContent?.trim() || element.value || element.getAttribute('aria-label') || ''; @@ -1813,11 +1826,13 @@ export default function RootLayout({ }; const isTextElement = (element) => { - return elementType === 'Text'; + const elementType = getElementType(element); + return elementType === 'Text'; }; const isButtonElement = (element) => { - return elementType === 'Button'; + const elementType = getElementType(element); + return elementType === 'Button'; }; const updateButtonText = (element, newText) => { @@ -1892,7 +1907,8 @@ export default function RootLayout({ }; const handleInput = () => { - let currentText = element.textContent; + const elementInfo = getElementInfo(element); + let currentText = element.textContent; // Ensure there's always at least a space to keep the element editable if (currentText === '' || currentText === null || currentText.length === 0) { @@ -2005,7 +2021,8 @@ export default function RootLayout({ }, '*'); if (save && originalContent !== element.textContent) { - let finalText = element.textContent; + const elementInfo = getElementInfo(element); + let finalText = element.textContent; // Trim the final text and convert space-only to empty string for saving if (finalText === ' ' || finalText.trim() === '') { @@ -2134,7 +2151,7 @@ export default function RootLayout({ lastMouseX = e.clientX; lastMouseY = e.clientY; - || e.target; + const target = getMostSpecificElement(e.clientX, e.clientY) || e.target; if (!isValidElement(target) || target === hoveredElement || target === selectedElement) { return; @@ -2166,7 +2183,8 @@ export default function RootLayout({ hoverOverlay = createHoverOverlay(target); } - showElementTypeLabel(target, elementType); + const elementType = getElementType(target); + showElementTypeLabel(target, elementType); window.parent.postMessage({ type: 'webild-element-hover', @@ -2208,7 +2226,7 @@ export default function RootLayout({ e.preventDefault(); e.stopPropagation(); - || e.target; + const target = getMostSpecificElement(e.clientX, e.clientY) || e.target; if (!isValidElement(target)) return; if (selectedElement && selectedElement !== target) { @@ -2253,7 +2271,8 @@ export default function RootLayout({ hoveredElement = null; } - selectedElement.dataset.webildSelector = elementInfo.selector; + const elementInfo = getElementInfo(target, true); + selectedElement.dataset.webildSelector = elementInfo.selector; showElementTypeLabel(target, elementInfo.elementType); window.parent.postMessage({ @@ -2336,7 +2355,8 @@ export default function RootLayout({ isScrolling = false; if (lastMouseX > 0 && lastMouseY > 0) { - if (target && isValidElement(target) && target !== selectedElement) { + const target = getMostSpecificElement(lastMouseX, lastMouseY); + if (target && isValidElement(target) && target !== selectedElement) { hoveredElement = target; const computedStyle = window.getComputedStyle(target); @@ -2350,7 +2370,8 @@ export default function RootLayout({ hoveredElement.classList.add(hoverClass); hoverOverlay = createHoverOverlay(target); - showElementTypeLabel(target, elementType); + const elementType = getElementType(target); + showElementTypeLabel(target, elementType); window.parent.postMessage({ type: 'webild-element-hover', @@ -2373,7 +2394,8 @@ export default function RootLayout({ const saveChangeToStorage = (change) => { try { - const existingChanges = JSON.parse(localStorage.getItem(storageKey) || '[]'); + const storageKey = getStorageKey(); + const existingChanges = JSON.parse(localStorage.getItem(storageKey) || '[]'); const filteredChanges = existingChanges.filter(c => { return !(c.oldValue === change.oldValue && c.sectionId === change.sectionId); @@ -2393,7 +2415,8 @@ export default function RootLayout({ const clearLocalChanges = () => { try { - localStorage.removeItem(storageKey); + const storageKey = getStorageKey(); + localStorage.removeItem(storageKey); window.parent.postMessage({ type: 'webild-local-changes-cleared', data: {} @@ -2442,7 +2465,8 @@ export default function RootLayout({ if (e.data.type === 'webild-cancel-changes') { try { - const savedChanges = localStorage.getItem(storageKey); + const storageKey = getStorageKey(); + const savedChanges = localStorage.getItem(storageKey); if (savedChanges) { const changes = JSON.parse(savedChanges); changes.forEach(change => { @@ -2464,7 +2488,8 @@ export default function RootLayout({ if (isBackground) { element.style.backgroundImage = change.oldValue ? 'url(' + change.oldValue + ')' : ''; } else { - if (revertTag === 'video' && oldMediaType === 'image') { + const oldMediaType = getMediaTypeFromUrl(change.oldValue); + if (revertTag === 'video' && oldMediaType === 'image') { swapMediaElement(element, 'img', change.oldValue); } else if (revertTag === 'img' && oldMediaType === 'video') { swapMediaElement(element, 'video', change.oldValue); @@ -2512,7 +2537,8 @@ export default function RootLayout({ const el = textElements[i]; if (isTextElement(el) && el.textContent.trim() === (oldValue || '').trim()) { element = el; - if (newSelector) { + const newSelector = getUniqueSelector(element, true); + if (newSelector) { element.dataset.webildSelector = newSelector; } break; @@ -2603,8 +2629,10 @@ export default function RootLayout({ replaced = true; } else if (element.tagName.toLowerCase() === 'img') { oldValue = element.src; - if (newMediaType === 'video' && allowMediaTypeSwap) { - if (selectedElement === element) selectedElement = swapped; + const newMediaType = getMediaTypeFromUrl(newSrc); + if (newMediaType === 'video' && allowMediaTypeSwap) { + const swapped = swapMediaElement(element, 'video', newSrc); + if (selectedElement === element) selectedElement = swapped; element = swapped; } else { element.src = newSrc; @@ -2612,9 +2640,11 @@ export default function RootLayout({ replaced = true; } else if (element.tagName.toLowerCase() === 'video') { oldValue = element.src || element.currentSrc || ''; - const sources = element.querySelectorAll('source'); + const newMediaType = getMediaTypeFromUrl(newSrc); + const sources = element.querySelectorAll('source'); if (newMediaType === 'image' && allowMediaTypeSwap) { - if (selectedElement === element) selectedElement = swapped; + const swapped = swapMediaElement(element, 'img', newSrc); + if (selectedElement === element) selectedElement = swapped; element = swapped; } else { if (sources.length > 0) { @@ -2636,7 +2666,8 @@ export default function RootLayout({ } if (replaced) { - + const elementInfo = getElementInfo(element); + let cleanOldValue = oldValue; if (oldValue.includes('url(')) { const urlMatch = oldValue.match(/url(['"]?([^'")]+)['"]?)/); @@ -2707,7 +2738,13 @@ export default function RootLayout({ } }, true); - + const urlCheckInterval = setInterval(() => { + if (lastPathname !== window.location.pathname) { + lastPathname = window.location.pathname; + notifyPageChange(); + } + }, 500); + notifyPageChange(); window.webildCleanup = () => { From 36ae3d4fa502b01a838f41301c15cb66a40b77ac Mon Sep 17 00:00:00 2001 From: bender Date: Thu, 5 Mar 2026 04:40:08 +0000 Subject: [PATCH 2/2] Update src/app/page.tsx --- src/app/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/page.tsx b/src/app/page.tsx index 57a0887..f5fc7b7 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -101,7 +101,7 @@ export default function LandingPage() { }, { id: 2, - tag: "Ongoing Care", title: "Landscape Maintenance", subtitle: "Beauty that lasts year-round", description: "Our seasonal maintenance programs keep your gardens thriving throughout the year. From spring planting to winter preparation, we ensure your landscape stays healthy, vibrant, and true to its design through dedicated, personalized care.", imageSrc: "http://img.b2bpic.net/free-photo/people-operating-heavy-duty-leaf-blower_1153-7263.jpg", imageAlt: "Professional landscape maintenance services" + tag: "Ongoing Care", title: "Landscape Maintenance", subtitle: "Beauty that lasts year-round", description: "Our seasonal maintenance programs keep your gardens thriving throughout the year. From spring planting to winter preparation, we ensure your landscape stays healthy, vibrant, and true to its design through dedicated, personalized care.", imageSrc: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AP6m7l7hwi4GIZuXGy8tnhucL3/uploaded-1772685598941-byxcbb57.jpg", imageAlt: "Professional landscape maintenance services" }, { id: 3,