From aab6ee8bc6a1f737bbac60d34d64756fdf91d492 Mon Sep 17 00:00:00 2001 From: bender Date: Fri, 20 Feb 2026 16:29:42 +0000 Subject: [PATCH] Update src/app/layout.tsx --- src/app/layout.tsx | 126 +++++++++++++++++++++++++++++++++++---------- 1 file changed, 100 insertions(+), 26 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 04d46f2..ce2fb48 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -5,36 +5,24 @@ import { ServiceWrapper } from "@/components/ServiceWrapper"; import Tag from "@/tag/Tag"; const publicSans = Public_Sans({ - variable: "--font-public-sans", - subsets: ["latin"], + variable: "--font-public-sans", subsets: ["latin"], }); export const metadata: Metadata = { - title: "Professional Trucking & Construction Services Texas | Reliable Hauling", - description: "Family-owned trucking and construction company in Texas. Heavy hauling, freight transport, equipment services. Fully insured, professional drivers. Request a quote today.", - keywords: ["trucking company Texas", "heavy hauling", "freight transport", "construction services", "equipment transport", "Texas trucking"], + title: "Professional Trucking & Construction Services Texas | Reliable Hauling", description: "Family-owned trucking and construction company in Texas. Heavy hauling, freight transport, equipment services. Fully insured, professional drivers. Request a quote today.", keywords: ["trucking company Texas", "heavy hauling", "freight transport", "construction services", "equipment transport", "Texas trucking"], metadataBase: new URL("https://truckingandconstructiontexas.com"), alternates: { canonical: "https://truckingandconstructiontexas.com" }, openGraph: { - title: "Trucking and Construction Company of Texas", - description: "Professional trucking, hauling, and construction services you can count on.", - url: "https://truckingandconstructiontexas.com", - siteName: "Trucking and Construction Company of Texas", - type: "website", - images: [ + title: "Trucking and Construction Company of Texas", description: "Professional trucking, hauling, and construction services you can count on.", url: "https://truckingandconstructiontexas.com", siteName: "Trucking and Construction Company of Texas", type: "website", images: [ { - url: "https://img.b2bpic.net/free-photo/truck-logistics-operations-dusk_23-2151468803.jpg", - alt: "Semi truck on Texas highway" + url: "https://img.b2bpic.net/free-photo/truck-logistics-operations-dusk_23-2151468803.jpg", alt: "Semi truck on Texas highway" } ] }, twitter: { - card: "summary_large_image", - title: "Trucking and Construction Company of Texas", - description: "Professional trucking, hauling, and construction services you can count on.", - images: ["https://img.b2bpic.net/free-photo/truck-logistics-operations-dusk_23-2151468803.jpg"] + card: "summary_large_image", title: "Trucking and Construction Company of Texas", description: "Professional trucking, hauling, and construction services you can count on.", images: ["https://img.b2bpic.net/free-photo/truck-logistics-operations-dusk_23-2151468803.jpg"] }, robots: { index: true, @@ -102,7 +90,9 @@ export default function RootLayout({ ' background-color: #4d96ff05 !important;' + '}' + 'img.webild-hover,' + - 'img.webild-selected {' + + 'img.webild-selected,' + + 'video.webild-hover,' + + 'video.webild-selected {' + ' outline-offset: 2px !important;' + '}' + '.webild-element-type-label {' + @@ -174,6 +164,10 @@ export default function RootLayout({ return 'Image'; } + if (tagName === 'video') { + return 'Video'; + } + const backgroundImage = computedStyle.backgroundImage; if (backgroundImage && backgroundImage !== 'none') { const urlMatch = backgroundImage.match(/url(['"]?([^'")]+)['"]?)/); @@ -256,6 +250,38 @@ export default function RootLayout({ return url; }; + const getMediaTypeFromUrl = (url) => { + const videoExts = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv', '.m4v', '.wmv']; + const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg', '.bmp', '.ico', '.tiff', '.avif']; + try { + const pathname = new URL(url).pathname.toLowerCase(); + if (videoExts.some(function(ext) { return pathname.endsWith(ext); })) return 'video'; + if (imageExts.some(function(ext) { return pathname.endsWith(ext); })) return 'image'; + } catch(e) {} + return 'unknown'; + }; + + const swapMediaElement = (oldEl, newTag, newSrc) => { + const newEl = document.createElement(newTag); + Array.from(oldEl.attributes).forEach(function(attr) { + if (attr.name !== 'src' && attr.name !== 'alt' && attr.name !== 'srcset' && attr.name !== 'autoplay' && attr.name !== 'loop' && attr.name !== 'muted' && attr.name !== 'playsinline') { + try { newEl.setAttribute(attr.name, attr.value); } catch(e) {} + } + }); + newEl.style.cssText = oldEl.style.cssText; + if (newTag === 'video') { + newEl.setAttribute('autoplay', ''); + newEl.setAttribute('loop', ''); + newEl.setAttribute('muted', ''); + newEl.setAttribute('playsinline', ''); + } + newEl.src = newSrc; + if (oldEl.parentNode) { + oldEl.parentNode.replaceChild(newEl, oldEl); + } + return newEl; + }; + const getElementInfo = (element, assignId = false) => { const rect = element.getBoundingClientRect(); const tagName = element.tagName.toLowerCase(); @@ -298,7 +324,18 @@ export default function RootLayout({ isBackground: false }; } - + + if (tagName === 'video') { + const rawSrc = element.src || element.currentSrc || (element.querySelector('source') && element.querySelector('source').src) || ''; + const resolvedSrc = extractOriginalUrl(rawSrc); + info.imageData = { + src: resolvedSrc, + alt: element.getAttribute('aria-label') || undefined, + isBackground: false, + isVideo: true + }; + } + const computedStyle = window.getComputedStyle(element); const backgroundImage = computedStyle.backgroundImage; if (backgroundImage && backgroundImage !== 'none') { @@ -346,7 +383,8 @@ export default function RootLayout({ const tagName = element.tagName?.toLowerCase(); if (invalidElements.includes(tagName)) return false; const isImage = tagName === 'img'; - if (isImage) return true; + const isVideo = tagName === 'video'; + if (isImage || isVideo) return true; const hasInnerHTML = element.innerHTML && element.innerHTML.trim().length > 0; const hasTextContent = element.textContent && element.textContent.trim().length > 0; const hasChildren = element.children && element.children.length > 0; @@ -386,7 +424,7 @@ export default function RootLayout({ node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim().length > 0 ); - const hasImages = element.tagName === 'IMG' || computedStyle.backgroundImage !== 'none' || element.querySelector('img'); + const hasImages = element.tagName === 'IMG' || element.tagName === 'VIDEO' || computedStyle.backgroundImage !== 'none' || element.querySelector('img') || element.querySelector('video'); const isInteractive = ['BUTTON', 'A', 'INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName); const hasFewChildren = element.children.length <= 3; const area = rect.width * rect.height; @@ -1067,11 +1105,22 @@ export default function RootLayout({ updateButtonText(element, change.oldValue); } } else if (change.type === 'replaceImage') { - const isBackground = element.tagName.toLowerCase() !== 'img'; + const revertTag = element.tagName.toLowerCase(); + const isBackground = revertTag !== 'img' && revertTag !== 'video'; if (isBackground) { element.style.backgroundImage = change.oldValue ? 'url(' + change.oldValue + ')' : ''; } else { - element.src = change.oldValue; + 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); + } else if (revertTag === 'video') { + element.src = change.oldValue; + element.load(); + } else { + element.src = change.oldValue; + } } } } catch (err) { @@ -1171,7 +1220,7 @@ export default function RootLayout({ if (!isActive) return; if (e.data.type === 'webild-replace-image') { - const { selector, newSrc, isBackground } = e.data.data; + const { selector, newSrc, isBackground, allowMediaTypeSwap } = e.data.data; let element = null; try { @@ -1202,7 +1251,32 @@ export default function RootLayout({ replaced = true; } else if (element.tagName.toLowerCase() === 'img') { oldValue = element.src; - element.src = newSrc; + 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; + } + replaced = true; + } else if (element.tagName.toLowerCase() === 'video') { + oldValue = element.src || element.currentSrc || ''; + const newMediaType = getMediaTypeFromUrl(newSrc); + const sources = element.querySelectorAll('source'); + if (newMediaType === 'image' && allowMediaTypeSwap) { + const swapped = swapMediaElement(element, 'img', newSrc); + if (selectedElement === element) selectedElement = swapped; + element = swapped; + } else { + if (sources.length > 0) { + sources.forEach(function(source) { source.src = newSrc; }); + element.load(); + } else { + element.src = newSrc; + element.load(); + } + } replaced = true; } else { const hasBackgroundImage = window.getComputedStyle(element).backgroundImage !== 'none'; @@ -1340,4 +1414,4 @@ export default function RootLayout({ ); -} \ No newline at end of file +}