diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index 91321cf..291afbb 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,36 +1,1421 @@
import type { Metadata } from "next";
-import { Geist, Geist_Mono } from "next/font/google";
+import { Halant } from "next/font/google";
+import { Inter } from "next/font/google";
+import { Manrope } from "next/font/google";
import "./globals.css";
-import { ServiceWrapper } from "@/providers/themeProvider/ServiceWrapper";
-import { Tag } from "@/components/tag/Tag";
+import { ServiceWrapper } from "@/components/ServiceWrapper";
+import Tag from "@/tag/Tag";
-const geist = Geist({
- variable: "--font-geist-sans", subsets: ["latin"],
+const halant = Halant({
+ variable: "--font-halant", subsets: ["latin"],
+ weight: ["300", "400", "500", "600", "700"],
});
-const geist_mono = Geist_Mono({
- variable: "--font-geist-mono", subsets: ["latin"],
+const inter = Inter({
+ variable: "--font-inter", subsets: ["latin"],
+});
+
+const manrope = Manrope({
+ variable: "--font-manrope", subsets: ["latin"],
});
export const metadata: Metadata = {
- title: "Portfolio | Creative Design & UX Expertise", description: "Explore innovative design solutions and digital experiences crafted with strategic thinking and creative excellence."};
+ title: "Portfolio — Creative Design Excellence", description: "Innovative digital design solutions showcasing brand strategy, UX/UI design, and product design expertise. Explore featured projects and services.", keywords: "portfolio, graphic design, UX design, brand identity, digital design, creative services", openGraph: {
+ title: "Portfolio — Creative Design Excellence", description: "Discover innovative design solutions and creative work. Brand strategy, UX/UI, and digital product design.", siteName: "Portfolio", type: "website", images: [
+ {
+ url: "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AhRowzw9k0ZSJ87n7KX34EwoE1/a-stunning-creative-portfolio-showcasing-1773043068077-12221410.png", alt: "Portfolio showcase"},
+ ],
+ },
+ twitter: {
+ card: "summary_large_image", title: "Portfolio — Creative Design Excellence", description: "Innovative digital design solutions and creative work", images: [
+ "https://webuild-dev.s3.eu-north-1.amazonaws.com/users/user_3AhRowzw9k0ZSJ87n7KX34EwoE1/a-stunning-creative-portfolio-showcasing-1773043068077-12221410.png"],
+ },
+ robots: {
+ index: true,
+ follow: true,
+ },
+};
export default function RootLayout({
children,
-}: {
+}: Readonly<{
children: React.ReactNode;
-}) {
+}>) {
return (
-
-
+
+
{children}
-
+
+ dangerouslySetInnerHTML={{
+ __html: `
+(function() {
+ if (window.self === window.top) return;
+
+ if (window.__webildEditorInitialized) return;
+ window.__webildEditorInitialized = true;
+
+ let isActive = false;
+ let hoveredElement = null;
+ let selectedElement = null;
+ let originalContent = null;
+ let isEditing = false;
+ let elementTypeLabel = null;
+ let hoverOverlay = null;
+ let scrollTimeout = null;
+ let isScrolling = false;
+
+ const invalidElements = ['html', 'body', 'script', 'style', 'meta', 'link', 'head', 'noscript', 'title'];
+ const hoverClass = 'webild-hover';
+ const selectedClass = 'webild-selected';
+
+ const style = document.createElement('style');
+ style.id = 'webild-inspector-styles';
+ style.textContent = '' +
+ '.webild-hover {' +
+ ' outline: 2px dashed #4d96ff80 !important;' +
+ ' border-radius: 0 !important;' +
+ ' outline-offset: 2px !important;' +
+ ' cursor: pointer !important;' +
+ ' transition: outline 0.15s ease !important;' +
+ ' background-color: #4d96ff05 !important;' +
+ '}' +
+ '.webild-selected {' +
+ ' outline: 2px solid #4d96ff !important;' +
+ ' outline-offset: 2px !important;' +
+ ' transition: outline 0.15s ease !important;' +
+ ' background-color: #4d96ff05 !important;' +
+ ' border-radius: 0 !important;' +
+ '}' +
+ '[contenteditable="true"].webild-selected {' +
+ ' outline: 2px solid #4d96ff !important;' +
+ ' background-color: #4d96ff05 !important;' +
+ '}' +
+ 'img.webild-hover,' +
+ 'img.webild-selected,' +
+ 'video.webild-hover,' +
+ 'video.webild-selected {' +
+ ' outline-offset: 2px !important;' +
+ '}' +
+ '.webild-element-type-label {' +
+ ' position: fixed !important;' +
+ ' z-index: 999999 !important;' +
+ ' background: #4d96ff !important;' +
+ ' color: white !important;' +
+ ' padding: 4px 8px !important;' +
+ ' font-size: 11px !important;' +
+ ' font-weight: 600 !important;' +
+ ' font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif !important;' +
+ ' pointer-events: none !important;' +
+ ' white-space: nowrap !important;' +
+ ' box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15) !important;' +
+ ' letter-spacing: 0.3px !important;' +
+ ' border: 1px solid #4d96ff20 !important;' +
+ '}' +
+ '.webild-element-type-label.label-top {' +
+ ' border-radius: 6px 6px 0 0 !important;' +
+ '}' +
+ '.webild-element-type-label.label-bottom {' +
+ ' border-radius: 0 0 6px 6px !important;' +
+ '}' +
+ '.webild-hover-overlay {' +
+ ' position: fixed !important;' +
+ ' background-color: #4d96ff15 !important;' +
+ ' pointer-events: none !important;' +
+ ' z-index: 999998 !important;' +
+ ' transition: all 0.15s ease !important;' +
+ '}';
+ document.head.appendChild(style);
+
+ const getUniqueSelector = (element, assignId = false) => {
+ if (element.dataset && element.dataset.webildSelector) {
+ return element.dataset.webildSelector;
+ }
+
+ const existingId = element.getAttribute('data-webild-id');
+ if (existingId) {
+ return '[data-webild-id="' + existingId + '"]';
+ }
+
+ if (assignId) {
+ const uniqueId = 'webild-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
+ element.setAttribute('data-webild-id', uniqueId);
+ return '[data-webild-id="' + uniqueId + '"]';
+ }
+
+ return null;
+ };
+
+ const getSectionId = (element) => {
+ let current = element;
+ while (current && current !== document.body) {
+ const sectionId = current.getAttribute('data-section');
+ if (sectionId) {
+ return sectionId;
+ }
+ current = current.parentElement;
+ }
+ return 'hero';
+ };
+
+ const getElementType = (element) => {
+ const tagName = element.tagName.toLowerCase();
+ const computedStyle = window.getComputedStyle(element);
+
+ if (tagName === 'img') {
+ return 'Image';
+ }
+
+ if (tagName === 'video') {
+ return 'Video';
+ }
+
+ const backgroundImage = computedStyle.backgroundImage;
+ if (backgroundImage && backgroundImage !== 'none') {
+ const urlMatch = backgroundImage.match(/url(['"]?([^'")]+)['"]?)/);
+ if (urlMatch && urlMatch[1] && !urlMatch[1].includes('gradient')) {
+ const area = element.offsetWidth * element.offsetHeight;
+ const hasReasonableSize = area > 1000;
+ const hasFewChildren = element.children.length <= 2;
+
+ if (hasReasonableSize && hasFewChildren) {
+ return 'Image';
+ }
+ }
+ }
+
+ if (tagName === 'button') return 'Button';
+ if (tagName === 'a' && element.getAttribute('href')) return 'Button';
+ if (element.getAttribute('role') === 'button') return 'Button';
+
+ const buttonClasses = ['btn', 'button', 'cta', 'action-button'];
+ const hasButtonClass = buttonClasses.some(cls =>
+ element.classList.contains(cls) || element.classList.contains(\`btn-\${cls}\`)
+ );
+
+ if (hasButtonClass && element.textContent && element.textContent.trim().length > 0) {
+ return 'Button';
+ }
+
+ const textTags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'span', 'label', 'li'];
+ if (textTags.includes(tagName)) {
+ return 'Text';
+ }
+
+ if (tagName === 'div') {
+ const hasDirectText = Array.from(element.childNodes).some(node =>
+ node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim().length > 0
+ );
+
+ if (hasDirectText && !element.querySelector('div, section, article, main, header, footer')) {
+ return 'Text';
+ }
+
+ return 'Div';
+ }
+
+ if (tagName === 'article') {
+ return 'Article';
+ }
+
+ if (tagName === 'a' && !element.getAttribute('href')) {
+ return 'Text';
+ }
+
+ return 'Section';
+ };
+
+ const extractOriginalUrl = (url) => {
+ if (!url) return url;
+
+ if (url.includes('/_next/')) {
+ try {
+ const urlObj = new URL(url);
+ const originalPath = urlObj.searchParams.get('url');
+ if (originalPath) {
+ return originalPath;
+ }
+ } catch (e) {
+ return url;
+ }
+ }
+
+ if (url.includes('.webild.io/')) {
+ try {
+ const urlObj = new URL(url);
+ return urlObj.pathname;
+ } catch (e) {
+ return url;
+ }
+ }
+
+ 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();
+ const selector = getUniqueSelector(element, assignId);
+ const sectionId = getSectionId(element);
+
+ let className = undefined;
+ try {
+ if (element.className) {
+ if (typeof element.className === 'string') {
+ className = element.className;
+ } else if (element.className.baseVal !== undefined) {
+ className = element.className.baseVal;
+ }
+ }
+ } catch (e) {}
+
+ const info = {
+ tagName: tagName,
+ id: element.id || undefined,
+ className: className,
+ selector: selector,
+ elementType: null,
+ sectionId: sectionId,
+ boundingBox: {
+ x: rect.left,
+ y: rect.top,
+ width: rect.width,
+ height: rect.height
+ }
+ };
+
+ if (tagName === 'img') {
+ const originalSrc = extractOriginalUrl(element.src);
+ info.imageData = {
+ src: originalSrc,
+ alt: element.alt || undefined,
+ naturalWidth: element.naturalWidth,
+ naturalHeight: element.naturalHeight,
+ 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') {
+ const urlMatch = backgroundImage.match(/url(['"]?([^'")]+)['"]?)/);
+ if (urlMatch) {
+ const originalBgSrc = extractOriginalUrl(urlMatch[1]);
+ if (tagName !== 'img') {
+ info.imageData = {
+ src: originalBgSrc,
+ isBackground: true
+ };
+ } else {
+ if (!info.imageData) info.imageData = {};
+ info.imageData.backgroundImageSrc = originalBgSrc;
+ }
+ }
+ }
+
+ const elementType = getElementType(element);
+ info.elementType = elementType;
+
+ if (elementType === 'Button') {
+ const buttonText = element.textContent?.trim() || element.value || element.getAttribute('aria-label') || '';
+ const buttonHref = element.getAttribute('href') ||
+ element.getAttribute('data-href') ||
+ element.getAttribute('onclick') ||
+ element.dataset?.link ||
+ undefined;
+
+ info.buttonData = {
+ text: buttonText,
+ href: buttonHref
+ };
+ }
+
+ if (elementType === 'Text') {
+ info.textContent = element.textContent || '';
+ }
+
+ return info;
+ };
+
+ const isValidElement = (element) => {
+ if (!isActive) return false;
+ const tagName = element.tagName?.toLowerCase();
+ if (invalidElements.includes(tagName)) return false;
+ const isImage = tagName === 'img';
+ 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;
+ if (!hasInnerHTML && !hasTextContent && !hasChildren) {
+ return false;
+ }
+ const hasBackgroundImage = window.getComputedStyle(element).backgroundImage !== 'none';
+ if (hasBackgroundImage && !hasChildren && !hasTextContent) {
+ return false;
+ }
+
+ return true;
+ };
+
+ const getMostSpecificElement = (x, y) => {
+ const elements = document.elementsFromPoint(x, y);
+ const validElements = elements.filter(el =>
+ isValidElement(el) &&
+ !el.classList.contains('webild-hover-overlay') &&
+ !el.classList.contains('webild-element-type-label')
+ );
+
+ if (validElements.length === 0) return null;
+ const scoredElements = validElements.map(element => {
+ let score = 0;
+ const rect = element.getBoundingClientRect();
+ const tagName = element.tagName.toLowerCase();
+ const computedStyle = window.getComputedStyle(element);
+ let depth = 0;
+ let current = element;
+ while (current && current !== document.body) {
+ depth++;
+ current = current.parentElement;
+ }
+ score += depth * 2;
+ const hasDirectText = Array.from(element.childNodes).some(node =>
+ node.nodeType === Node.TEXT_NODE && node.textContent && node.textContent.trim().length > 0
+ );
+
+ 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;
+ const viewportArea = window.innerWidth * window.innerHeight;
+ const isSmallElement = area < viewportArea * 0.1;
+ if (hasDirectText) score += 20;
+ if (hasImages) score += 15;
+ if (isInteractive) score += 25;
+ if (hasFewChildren) score += 10;
+ if (isSmallElement) score += 5;
+ if (area > viewportArea * 0.3) score -= 30;
+ if (element.hasAttribute('data-section')) score -= 15;
+ if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tagName)) score += 20;
+ if (['p', 'span', 'label'].includes(tagName)) score += 15;
+ if (tagName === 'div' && !hasDirectText && element.children.length > 2) score -= 10;
+
+ return { element, score };
+ });
+ scoredElements.sort((a, b) => b.score - a.score);
+ return scoredElements[0]?.element || validElements[0];
+ };
+
+ const isTextElement = (element) => {
+ const elementType = getElementType(element);
+ return elementType === 'Text';
+ };
+
+ const isButtonElement = (element) => {
+ const elementType = getElementType(element);
+ return elementType === 'Button';
+ };
+
+ const updateButtonText = (element, newText) => {
+ const textNodes = [];
+ const walker = document.createTreeWalker(
+ element,
+ NodeFilter.SHOW_TEXT,
+ null
+ );
+
+ let node;
+ while (node = walker.nextNode()) {
+ if (node.textContent && node.textContent.trim()) {
+ textNodes.push(node);
+ }
+ }
+
+ if (textNodes.length > 0) {
+ textNodes[0].textContent = newText;
+ for (let i = 1; i < textNodes.length; i++) {
+ textNodes[i].textContent = '';
+ }
+ } else {
+ element.textContent = newText;
+ }
+ };
+
+ const makeEditable = (element, clickEvent) => {
+ if (!isTextElement(element)) return;
+
+ originalContent = element.textContent;
+ element.contentEditable = 'true';
+
+ if (!element.dataset.webildOriginalWhiteSpace) {
+ const computedStyle = window.getComputedStyle(element);
+ element.dataset.webildOriginalWhiteSpace = computedStyle.whiteSpace;
+ element.dataset.webildOriginalWordWrap = computedStyle.wordWrap;
+ element.dataset.webildOriginalOverflowWrap = computedStyle.overflowWrap;
+ element.dataset.webildOriginalOverflow = computedStyle.overflow;
+ }
+
+ element.style.whiteSpace = 'pre-wrap';
+ element.style.wordWrap = 'break-word';
+ element.style.overflowWrap = 'break-word';
+ element.style.overflow = 'visible';
+
+ element.focus();
+ isEditing = true;
+
+ window.parent.postMessage({
+ type: 'webild-text-editing-started',
+ data: { selector: getElementInfo(element).selector }
+ }, '*');
+
+ const handleBeforeInput = (e) => {
+ // Prevent deletion if it would leave the element empty
+ const currentText = element.textContent || '';
+ const inputType = e.inputType;
+
+ // Check if this is a delete operation that would leave the element empty
+ if ((inputType === 'deleteContentBackward' || inputType === 'deleteContentForward' || inputType === 'deleteByCut') && currentText.length <= 1) {
+ e.preventDefault();
+ element.textContent = ' ';
+ // Move cursor to the beginning
+ const range = document.createRange();
+ const sel = window.getSelection();
+ range.setStart(element.firstChild || element, 0);
+ range.collapse(true);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ }
+ };
+
+ const handleInput = () => {
+ 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) {
+ element.textContent = ' ';
+ currentText = ' ';
+ // Move cursor to the beginning
+ try {
+ const range = document.createRange();
+ const sel = window.getSelection();
+ range.setStart(element.firstChild || element, 0);
+ range.collapse(true);
+ sel.removeAllRanges();
+ sel.addRange(range);
+ } catch (e) {
+ // Ignore cursor positioning errors
+ }
+ }
+
+ window.parent.postMessage({
+ type: 'webild-element-changed',
+ data: {
+ type: 'updateText',
+ selector: elementInfo.selector,
+ oldValue: originalContent,
+ newValue: currentText,
+ elementType: elementInfo.elementType,
+ sectionId: elementInfo.sectionId,
+ timestamp: Date.now()
+ }
+ }, '*');
+
+ if (currentText !== originalContent) {
+ window.parent.postMessage({
+ type: 'webild-text-changed',
+ data: {
+ selector: elementInfo.selector,
+ hasChanges: true
+ }
+ }, '*');
+ }
+ };
+
+ element.addEventListener('beforeinput', handleBeforeInput);
+ element.addEventListener('input', handleInput);
+ element.dataset.inputHandler = 'true';
+ element.dataset.beforeInputHandler = 'true';
+
+ if (clickEvent && element.childNodes.length > 0) {
+ try {
+ let range = null;
+
+ if (document.caretRangeFromPoint) {
+ range = document.caretRangeFromPoint(clickEvent.clientX, clickEvent.clientY);
+ } else if (document.caretPositionFromPoint) {
+ const position = document.caretPositionFromPoint(clickEvent.clientX, clickEvent.clientY);
+ if (position) {
+ range = document.createRange();
+ range.setStart(position.offsetNode, position.offset);
+ range.collapse(true);
+ }
+ }
+
+ if (range) {
+ const selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ }
+ } catch (e) {
+ console.warn('[Webild] Could not set caret position:', e);
+ }
+ }
+ };
+
+ const makeUneditable = (element, save = false) => {
+ if (!element || element.contentEditable !== 'true') return;
+
+ element.contentEditable = 'false';
+ isEditing = false;
+
+ if (element.dataset.webildOriginalWhiteSpace) {
+ element.style.whiteSpace = element.dataset.webildOriginalWhiteSpace === 'normal' ? '' : element.dataset.webildOriginalWhiteSpace;
+ delete element.dataset.webildOriginalWhiteSpace;
+ }
+ if (element.dataset.webildOriginalWordWrap) {
+ element.style.wordWrap = element.dataset.webildOriginalWordWrap === 'normal' ? '' : element.dataset.webildOriginalWordWrap;
+ delete element.dataset.webildOriginalWordWrap;
+ }
+ if (element.dataset.webildOriginalOverflowWrap) {
+ element.style.overflowWrap = element.dataset.webildOriginalOverflowWrap === 'normal' ? '' : element.dataset.webildOriginalOverflowWrap;
+ delete element.dataset.webildOriginalOverflowWrap;
+ }
+ if (element.dataset.webildOriginalOverflow) {
+ element.style.overflow = element.dataset.webildOriginalOverflow === 'visible' ? '' : element.dataset.webildOriginalOverflow;
+ delete element.dataset.webildOriginalOverflow;
+ }
+
+ if (element.dataset.beforeInputHandler === 'true') {
+ element.removeEventListener('beforeinput', () => {});
+ delete element.dataset.beforeInputHandler;
+ }
+
+ if (element.dataset.inputHandler === 'true') {
+ element.removeEventListener('input', () => {});
+ delete element.dataset.inputHandler;
+ }
+
+ window.parent.postMessage({
+ type: 'webild-text-editing-ended',
+ data: { selector: getElementInfo(element).selector }
+ }, '*');
+
+ if (save && originalContent !== 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() === '') {
+ finalText = '';
+ // Update the actual element text to empty for display
+ element.textContent = '';
+ }
+
+ const change = {
+ type: 'updateText',
+ selector: elementInfo.selector,
+ oldValue: originalContent,
+ newValue: finalText,
+ elementType: elementInfo.elementType,
+ sectionId: elementInfo.sectionId,
+ timestamp: Date.now()
+ };
+
+ saveChangeToStorage(change);
+
+ window.parent.postMessage({
+ type: 'webild-element-changed',
+ data: change
+ }, '*');
+ } else if (!save && originalContent !== null) {
+ element.textContent = originalContent;
+ }
+
+ originalContent = null;
+ };
+
+ const createHoverOverlay = (element) => {
+ const rect = element.getBoundingClientRect();
+ const overlay = document.createElement('div');
+ overlay.className = 'webild-hover-overlay';
+ overlay.style.cssText = \`
+ position: fixed !important;
+ top: \${rect.top - 2}px !important;
+ left: \${rect.left - 2}px !important;
+ width: \${rect.width + 4}px !important;
+ height: \${rect.height + 4}px !important;
+ background-color: rgba(90, 113, 230, 0.15) !important;
+ pointer-events: none !important;
+ z-index: 999998 !important;
+ transition: all 0.15s ease !important;
+ \`;
+ document.body.appendChild(overlay);
+ return overlay;
+ };
+
+ const removeHoverOverlay = () => {
+ if (hoverOverlay) {
+ hoverOverlay.remove();
+ hoverOverlay = null;
+ }
+ };
+
+ const showElementTypeLabel = (element, elementType) => {
+ if (!elementType) return;
+
+ removeElementTypeLabel();
+
+ const rect = element.getBoundingClientRect();
+ elementTypeLabel = document.createElement('div');
+ elementTypeLabel.className = 'webild-element-type-label';
+ const ariaLabel = element.getAttribute('aria-label');
+ let labelText;
+
+ if (elementType === 'Div') {
+ labelText = 'Div';
+ } else if (elementType === 'Article') {
+ labelText = 'Article';
+ } else if (elementType === 'Section') {
+ labelText = ariaLabel || 'Section';
+ } else {
+ labelText = elementType;
+ }
+
+ elementTypeLabel.textContent = labelText;
+ document.body.appendChild(elementTypeLabel);
+ const labelRect = elementTypeLabel.getBoundingClientRect();
+ let labelTop = rect.top - labelRect.height - 2;
+ let labelLeft = rect.left - 3;
+ let isLabelOnTop = true;
+ if (labelTop < 0) {
+ labelTop = rect.bottom + 1;
+ isLabelOnTop = false;
+ }
+ if (labelTop + labelRect.height > window.innerHeight) {
+ labelTop = rect.bottom - labelRect.height;
+ isLabelOnTop = false;
+ if (labelTop < 0) {
+ labelTop = rect.top;
+ isLabelOnTop = true;
+ }
+ }
+ if (labelLeft + labelRect.width > window.innerWidth) {
+ labelLeft = window.innerWidth - labelRect.width;
+ }
+ if (labelLeft < 0) {
+ labelLeft = 0;
+ }
+ if (isLabelOnTop) {
+ elementTypeLabel.classList.add('label-top');
+ } else {
+ elementTypeLabel.classList.add('label-bottom');
+ }
+
+ elementTypeLabel.style.cssText = \`
+ left: \${labelLeft}px !important;
+ top: \${labelTop}px !important;
+ transform: none !important;
+ \`;
+ };
+
+ const removeElementTypeLabel = () => {
+ if (elementTypeLabel) {
+ elementTypeLabel.remove();
+ elementTypeLabel = null;
+ }
+ };
+
+ const handleMouseOver = (e) => {
+ if (!isActive) return;
+
+ lastMouseX = e.clientX;
+ lastMouseY = e.clientY;
+
+ const target = getMostSpecificElement(e.clientX, e.clientY) || e.target;
+
+ if (!isValidElement(target) || target === hoveredElement || target === selectedElement) {
+ return;
+ }
+
+ if (hoveredElement && hoveredElement !== selectedElement) {
+ hoveredElement.classList.remove(hoverClass);
+ if (hoveredElement.dataset.webildOriginalPosition) {
+ hoveredElement.style.position = hoveredElement.dataset.webildOriginalPosition === 'none' ? '' : hoveredElement.dataset.webildOriginalPosition;
+ delete hoveredElement.dataset.webildOriginalPosition;
+ }
+ removeHoverOverlay();
+ removeElementTypeLabel();
+ }
+
+ hoveredElement = target;
+
+ const computedStyle = window.getComputedStyle(target);
+ const currentPosition = computedStyle.position;
+
+ if (currentPosition === 'static' || currentPosition === '') {
+ hoveredElement.dataset.webildOriginalPosition = currentPosition || 'none';
+ hoveredElement.style.position = 'relative';
+ }
+
+ hoveredElement.classList.add(hoverClass);
+
+ if ((!selectedElement || selectedElement !== target) && !isScrolling) {
+ hoverOverlay = createHoverOverlay(target);
+ }
+
+ const elementType = getElementType(target);
+ showElementTypeLabel(target, elementType);
+
+ window.parent.postMessage({
+ type: 'webild-element-hover',
+ data: getElementInfo(target, false)
+ }, '*');
+ };
+
+ const handleMouseOut = (e) => {
+ if (!isActive) return;
+
+ if (hoveredElement && hoveredElement !== selectedElement) {
+ hoveredElement.classList.remove(hoverClass);
+
+ if (hoveredElement.dataset.webildOriginalPosition) {
+ hoveredElement.style.position = hoveredElement.dataset.webildOriginalPosition === 'none' ? '' : hoveredElement.dataset.webildOriginalPosition;
+ delete hoveredElement.dataset.webildOriginalPosition;
+ }
+
+ removeHoverOverlay();
+ removeElementTypeLabel();
+
+ hoveredElement = null;
+
+ window.parent.postMessage({
+ type: 'webild-element-hover',
+ data: null
+ }, '*');
+ }
+ };
+
+ const handleClick = (e) => {
+ if (!isActive) return;
+
+ if (isEditing) {
+ e.stopPropagation();
+ return;
+ }
+
+ e.preventDefault();
+ e.stopPropagation();
+
+ const target = getMostSpecificElement(e.clientX, e.clientY) || e.target;
+ if (!isValidElement(target)) return;
+
+ if (selectedElement && selectedElement !== target) {
+ makeUneditable(selectedElement, false);
+ selectedElement.classList.remove(selectedClass);
+ selectedElement.classList.remove(hoverClass);
+
+ if (selectedElement.dataset.webildOriginalPosition) {
+ selectedElement.style.position = selectedElement.dataset.webildOriginalPosition === 'none' ? '' : selectedElement.dataset.webildOriginalPosition;
+ delete selectedElement.dataset.webildOriginalPosition;
+ }
+
+ removeHoverOverlay();
+ removeElementTypeLabel();
+ }
+
+ if (selectedElement === target) {
+ if (target.dataset.webildOriginalPosition) {
+ target.style.position = target.dataset.webildOriginalPosition === 'none' ? '' : target.dataset.webildOriginalPosition;
+ delete target.dataset.webildOriginalPosition;
+ }
+
+ removeHoverOverlay();
+ removeElementTypeLabel();
+
+ selectedElement = null;
+ window.parent.postMessage({
+ type: 'webild-element-selected',
+ data: null
+ }, '*');
+ return;
+ }
+
+ selectedElement = target;
+ selectedElement.classList.add(selectedClass);
+
+ removeHoverOverlay();
+ removeElementTypeLabel();
+
+ if (hoveredElement === target) {
+ hoveredElement.classList.remove(hoverClass);
+ hoveredElement = null;
+ }
+
+ const elementInfo = getElementInfo(target, true);
+ selectedElement.dataset.webildSelector = elementInfo.selector;
+ showElementTypeLabel(target, elementInfo.elementType);
+
+ window.parent.postMessage({
+ type: 'webild-element-selected',
+ data: elementInfo
+ }, '*');
+
+ if (isTextElement(target)) {
+ setTimeout(() => makeEditable(target, e), 50);
+ }
+ };
+
+ const handleKeyDown = (e) => {
+ if (!isActive) return;
+ if (!isEditing || !selectedElement) return;
+
+ if (e.key === 'Enter' && !e.shiftKey) {
+ e.preventDefault();
+ makeUneditable(selectedElement, true);
+ } else if (e.key === 'Escape') {
+ e.preventDefault();
+ makeUneditable(selectedElement, false);
+ }
+ };
+
+ const handleBlur = (e) => {
+ if (!isActive) return;
+ if (isEditing && selectedElement && e.target === selectedElement) {
+ makeUneditable(selectedElement, true);
+ }
+ };
+
+ let lastMouseX = 0;
+ let lastMouseY = 0;
+
+ const handleScroll = () => {
+ if (!isActive) return;
+
+ if (isEditing) return;
+
+ if (selectedElement) {
+ makeUneditable(selectedElement, false);
+ selectedElement.classList.remove(selectedClass);
+ if (selectedElement.dataset.webildOriginalPosition) {
+ selectedElement.style.position = selectedElement.dataset.webildOriginalPosition === 'none' ? '' : selectedElement.dataset.webildOriginalPosition;
+ delete selectedElement.dataset.webildOriginalPosition;
+ }
+ selectedElement = null;
+
+ window.parent.postMessage({
+ type: 'webild-element-selected',
+ data: null
+ }, '*');
+ }
+
+ if (hoveredElement) {
+ hoveredElement.classList.remove(hoverClass);
+ if (hoveredElement.dataset.webildOriginalPosition) {
+ hoveredElement.style.position = hoveredElement.dataset.webildOriginalPosition === 'none' ? '' : hoveredElement.dataset.webildOriginalPosition;
+ delete hoveredElement.dataset.webildOriginalPosition;
+ }
+ hoveredElement = null;
+
+ window.parent.postMessage({
+ type: 'webild-element-hover',
+ data: null
+ }, '*');
+ }
+
+ removeHoverOverlay();
+ removeElementTypeLabel();
+
+ isScrolling = true;
+
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout);
+ }
+
+ scrollTimeout = setTimeout(() => {
+ isScrolling = false;
+
+ if (lastMouseX > 0 && lastMouseY > 0) {
+ const target = getMostSpecificElement(lastMouseX, lastMouseY);
+ if (target && isValidElement(target) && target !== selectedElement) {
+ hoveredElement = target;
+
+ const computedStyle = window.getComputedStyle(target);
+ const currentPosition = computedStyle.position;
+
+ if (currentPosition === 'static' || currentPosition === '') {
+ hoveredElement.dataset.webildOriginalPosition = currentPosition || 'none';
+ hoveredElement.style.position = 'relative';
+ }
+
+ hoveredElement.classList.add(hoverClass);
+ hoverOverlay = createHoverOverlay(target);
+
+ const elementType = getElementType(target);
+ showElementTypeLabel(target, elementType);
+
+ window.parent.postMessage({
+ type: 'webild-element-hover',
+ data: getElementInfo(target, false)
+ }, '*');
+ }
+ }
+ }, 150);
+
+ window.parent.postMessage({
+ type: 'webild-iframe-scroll'
+ }, '*');
+ };
+
+ const getStorageKey = () => {
+ const url = new URL(window.location.href);
+ const pathParts = url.pathname.split('/').filter(Boolean);
+ return \`webild-changes-\${pathParts.join('-')}\`;
+ };
+
+ const saveChangeToStorage = (change) => {
+ try {
+ const storageKey = getStorageKey();
+ const existingChanges = JSON.parse(localStorage.getItem(storageKey) || '[]');
+
+ const filteredChanges = existingChanges.filter(c => {
+ return !(c.oldValue === change.oldValue && c.sectionId === change.sectionId);
+ });
+ filteredChanges.push(change);
+
+ localStorage.setItem(storageKey, JSON.stringify(filteredChanges));
+
+ window.parent.postMessage({
+ type: 'webild-change-saved-locally',
+ data: { change, allChanges: filteredChanges }
+ }, '*');
+ } catch (error) {
+ console.error('Failed to save change to localStorage:', error);
+ }
+ };
+
+ const clearLocalChanges = () => {
+ try {
+ const storageKey = getStorageKey();
+ localStorage.removeItem(storageKey);
+ window.parent.postMessage({
+ type: 'webild-local-changes-cleared',
+ data: {}
+ }, '*');
+ } catch (error) {
+ console.error('Failed to clear local changes:', error);
+ }
+ };
+
+ const handleMessage = (e) => {
+ if (!e.data || !e.data.type) return;
+
+ if (e.data.type === 'webild-activate-editor') {
+ if (!isActive) {
+ isActive = true;
+ window.parent.postMessage({ type: 'webild-editor-activated' }, '*');
+ }
+ return;
+ }
+
+ if (e.data.type === 'webild-deactivate-editor') {
+ if (isActive) {
+ isActive = false;
+
+ if (selectedElement) {
+ makeUneditable(selectedElement, false);
+ selectedElement.classList.remove(selectedClass);
+ selectedElement = null;
+ }
+ if (hoveredElement) {
+ hoveredElement.classList.remove(hoverClass);
+ hoveredElement = null;
+ }
+
+ removeHoverOverlay();
+ removeElementTypeLabel();
+ window.parent.postMessage({ type: 'webild-editor-deactivated' }, '*');
+ }
+ return;
+ }
+
+ if (e.data.type === 'webild-clear-local-changes') {
+ clearLocalChanges();
+ return;
+ }
+
+ if (e.data.type === 'webild-cancel-changes') {
+ try {
+ const storageKey = getStorageKey();
+ const savedChanges = localStorage.getItem(storageKey);
+ if (savedChanges) {
+ const changes = JSON.parse(savedChanges);
+ changes.forEach(change => {
+ try {
+ const element = document.querySelector(change.selector);
+ if (!element) return;
+
+ if (change.type === 'updateText') {
+ if (isTextElement(element)) {
+ element.textContent = change.oldValue;
+ }
+ } else if (change.type === 'updateButton') {
+ if (isButtonElement(element)) {
+ updateButtonText(element, change.oldValue);
+ }
+ } else if (change.type === 'replaceImage') {
+ const revertTag = element.tagName.toLowerCase();
+ const isBackground = revertTag !== 'img' && revertTag !== 'video';
+ if (isBackground) {
+ element.style.backgroundImage = change.oldValue ? 'url(' + change.oldValue + ')' : '';
+ } else {
+ 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) {
+ console.warn('[Webild] Failed to revert change:', err);
+ }
+ });
+ }
+ clearLocalChanges();
+ } catch (error) {
+ console.error('[Webild] Failed to cancel changes:', error);
+ }
+ return;
+ }
+
+ if (e.data.type === 'webild-update-text') {
+ const { selector, newValue, oldValue, sectionId } = e.data.data;
+ try {
+ let element = null;
+
+ if (selectedElement && isTextElement(selectedElement)) {
+ element = selectedElement;
+ }
+ else if (selector) {
+ try {
+ element = document.querySelector(selector);
+ } catch (err) {
+ console.warn('[Webild] Invalid selector:', selector);
+ }
+ }
+
+ if (!element && sectionId) {
+ const sectionElement = document.querySelector('[data-section="' + sectionId + '"]');
+ if (sectionElement) {
+ const textElements = sectionElement.querySelectorAll('h1, h2, h3, h4, h5, h6, p, span, a, button, div');
+ for (let i = 0; i < textElements.length; i++) {
+ const el = textElements[i];
+ if (isTextElement(el) && el.textContent.trim() === (oldValue || '').trim()) {
+ element = el;
+ const newSelector = getUniqueSelector(element, true);
+ if (newSelector) {
+ element.dataset.webildSelector = newSelector;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if (element && isTextElement(element)) {
+ element.textContent = newValue;
+ const finalSelector = element.dataset.webildSelector || getUniqueSelector(element, true);
+
+ window.parent.postMessage({
+ type: 'webild-text-update-success',
+ data: {
+ selector: finalSelector,
+ newValue: newValue
+ }
+ }, '*');
+ } else {
+ window.parent.postMessage({
+ type: 'webild-text-update-failed',
+ data: { selector, newValue }
+ }, '*');
+ }
+ } catch (error) {
+ window.parent.postMessage({
+ type: 'webild-text-update-failed',
+ data: { selector, newValue, error: error.message }
+ }, '*');
+ }
+ return;
+ }
+
+ if (e.data.type === 'webild-update-button') {
+ const { selector, text, href } = e.data.data;
+ try {
+ const element = document.querySelector(selector);
+ if (element && isButtonElement(element)) {
+ if (text !== undefined) {
+ updateButtonText(element, text);
+ }
+ if (href !== undefined) {
+ if (element.tagName.toLowerCase() === 'a') {
+ element.href = href;
+ } else {
+ element.setAttribute('data-href', href);
+ }
+ }
+ }
+ } catch (error) {
+ console.error('[Webild] Invalid selector for button update:', selector, error);
+ }
+ return;
+ }
+
+ if (!isActive) return;
+
+ if (e.data.type === 'webild-replace-image') {
+ const { selector, newSrc, isBackground, allowMediaTypeSwap } = e.data.data;
+ let element = null;
+
+ try {
+ element = document.querySelector(selector);
+ } catch {
+ window.parent.postMessage({
+ type: 'webild-image-replacement-error',
+ data: { selector, message: 'Invalid selector: ' + error.message, success: false }
+ }, '*');
+ return;
+ }
+
+ if (!element) {
+ window.parent.postMessage({
+ type: 'webild-image-replacement-error',
+ data: { selector, message: 'Element not found', success: false }
+ }, '*');
+ return;
+ }
+
+ try {
+ let replaced = false;
+ let oldValue = '';
+
+ if (isBackground) {
+ oldValue = window.getComputedStyle(element).backgroundImage;
+ element.style.backgroundImage = \`url('\${newSrc}')\`;
+ replaced = true;
+ } else if (element.tagName.toLowerCase() === 'img') {
+ oldValue = element.src;
+ 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';
+ if (hasBackgroundImage) {
+ oldValue = window.getComputedStyle(element).backgroundImage;
+ element.style.backgroundImage = \`url('\${newSrc}')\`;
+ replaced = true;
+ }
+ }
+
+ if (replaced) {
+ const elementInfo = getElementInfo(element);
+
+ let cleanOldValue = oldValue;
+ if (oldValue.includes('url(')) {
+ const urlMatch = oldValue.match(/url(['"]?([^'")]+)['"]?)/);
+ if (urlMatch) {
+ cleanOldValue = urlMatch[1];
+ }
+ }
+
+ cleanOldValue = extractOriginalUrl(cleanOldValue);
+
+ const change = {
+ type: 'replaceImage',
+ selector: selector,
+ oldValue: cleanOldValue,
+ newValue: newSrc,
+ elementType: elementInfo.elementType,
+ sectionId: elementInfo.sectionId,
+ timestamp: Date.now()
+ };
+
+ saveChangeToStorage(change);
+
+ window.parent.postMessage({
+ type: 'webild-element-changed',
+ data: change
+ }, '*');
+
+ window.parent.postMessage({
+ type: 'webild-image-replaced',
+ data: { selector, newSrc, success: true }
+ }, '*');
+ } else {
+ window.parent.postMessage({
+ type: 'webild-image-replacement-error',
+ data: { selector, message: 'Could not determine how to replace image', success: false }
+ }, '*');
+ }
+ } catch (error) {
+ window.parent.postMessage({
+ type: 'webild-image-replacement-error',
+ data: { selector, message: error.message || 'Failed to replace image', success: false }
+ }, '*');
+ }
+ }
+ };
+
+ document.addEventListener('mouseover', handleMouseOver, true);
+ document.addEventListener('mouseout', handleMouseOut, true);
+ document.addEventListener('click', handleClick, true);
+ document.addEventListener('keydown', handleKeyDown, true);
+ document.addEventListener('blur', handleBlur, true);
+ window.addEventListener('scroll', handleScroll, true);
+ window.addEventListener('message', handleMessage, true);
+
+ let lastPathname = window.location.pathname;
+
+ const notifyPageChange = () => {
+ window.parent.postMessage({
+ type: 'webild-page-changed',
+ data: { pathname: window.location.pathname }
+ }, '*');
+ };
+
+ window.addEventListener('popstate', () => {
+ if (lastPathname !== window.location.pathname) {
+ lastPathname = window.location.pathname;
+ notifyPageChange();
+ }
+ }, true);
+
+ const urlCheckInterval = setInterval(() => {
+ if (lastPathname !== window.location.pathname) {
+ lastPathname = window.location.pathname;
+ notifyPageChange();
+ }
+ }, 500);
+
+ notifyPageChange();
+
+ window.webildCleanup = () => {
+ isActive = false;
+
+ if (selectedElement) {
+ makeUneditable(selectedElement, false);
+ }
+
+ removeHoverOverlay();
+ removeElementTypeLabel();
+
+ if (urlCheckInterval) {
+ clearInterval(urlCheckInterval);
+ }
+
+ document.removeEventListener('mouseover', handleMouseOver, true);
+ document.removeEventListener('mouseout', handleMouseOut, true);
+ document.removeEventListener('click', handleClick, true);
+ document.removeEventListener('keydown', handleKeyDown, true);
+ document.removeEventListener('blur', handleBlur, true);
+ window.removeEventListener('scroll', handleScroll, true);
+ window.removeEventListener('message', handleMessage, true);
+
+ document.querySelectorAll('.' + hoverClass).forEach(el => {
+ el.classList.remove(hoverClass);
+ });
+ document.querySelectorAll('.' + selectedClass).forEach(el => {
+ el.classList.remove(selectedClass);
+ });
+
+ const styleEl = document.getElementById('webild-inspector-styles');
+ if (styleEl) styleEl.remove();
+
+ hoveredElement = null;
+ selectedElement = null;
+ };
+
+ window.parent.postMessage({ type: 'webild-editor-ready' }, '*');
+})();
+`
+ }}
+ />
+
);
}