(function () { const WIDGET_SHELL_SELECTOR = ".js-gia-widget-shell"; const WIDGET_SPAWN_SELECTOR = ".js-widget-spawn-trigger"; const WIDGET_LOAD_TARGET_SELECTOR = '[hx-target="#widgets-here"], [data-widget-url]'; const WIDGET_ACTION_SELECTOR = ".js-gia-widget-action"; const TASKBAR_SELECTOR = "#gia-taskbar"; const TASKBAR_ITEMS_SELECTOR = "#gia-taskbar-items"; const WORKSPACE_STASH_SELECTOR = "#gia-workspace-stash"; const SNAP_ASSISTANT_SELECTOR = "#gia-snap-assistant"; const SNAP_ASSISTANT_OPTIONS_SELECTOR = "#gia-snap-assistant-options"; const SNAP_ASSISTANT_CLOSE_SELECTOR = ".js-gia-snap-assistant-close"; const MOBILE_MEDIA_QUERY = "(max-width: 768px)"; const DESKTOP_MEDIA_QUERY = "(min-width: 1216px)"; const GRID_COLUMNS = 12; const GRID_ROWS = 12; const MIN_QUARTER_TILE_HEIGHT = 340; const MIN_QUARTER_TILE_WIDTH = 420; const assetPromises = { styles: {}, scripts: {}, }; let widgetShellProcessingChain = Promise.resolve(); const workspaceState = { order: [], minimized: new Map(), pendingWidgetIds: new Set(), activeWidgetId: "", anchorWidgetId: "", pendingSpawnSourceId: "", pendingSpawnTs: 0, lastSpawnedId: "", snapLeftId: "", snapRightId: "", snapTopId: "", snapBottomId: "", snapAssistantSourceId: "", snapAssistantMode: "", }; function withDocumentRoot(root) { return root && typeof root.querySelectorAll === "function" ? root : document; } function toArray(value) { return Array.isArray(value) ? value : Array.from(value || []); } function getGridElement() { return window.gridElement || document.getElementById("grid-stack-main"); } function getTaskbar() { return document.querySelector(TASKBAR_SELECTOR); } function getTaskbarItems() { return document.querySelector(TASKBAR_ITEMS_SELECTOR); } function getWorkspaceStash() { return document.querySelector(WORKSPACE_STASH_SELECTOR); } function getSnapAssistant() { return document.querySelector(SNAP_ASSISTANT_SELECTOR); } function getSnapAssistantOptions() { return document.querySelector(SNAP_ASSISTANT_OPTIONS_SELECTOR); } function getGridViewportMetrics() { const gridElement = getGridElement(); const gridColumn = gridElement && gridElement.parentElement ? gridElement.parentElement : null; const main = gridColumn && gridColumn.parentElement ? gridColumn.parentElement : null; const source = gridColumn || main; const rect = source && source.getBoundingClientRect ? source.getBoundingClientRect() : null; return { width: Math.max(0, Math.floor((rect && rect.width) || window.innerWidth || 0)), height: Math.max(0, Math.floor((rect && rect.height) || window.innerHeight || 0)), }; } function getWidgetNode(widgetId) { const id = String(widgetId || "").trim(); return id ? document.getElementById(id) : null; } function writeGridPositionAttrs(widgetNode, layoutItem) { if (!widgetNode || !layoutItem) { return; } widgetNode.setAttribute("gs-id", String(widgetNode.id || "")); ["x", "y", "w", "h"].forEach(function (key) { const value = Number(layoutItem[key]); if (!Number.isFinite(value)) { return; } const attr = "gs-" + key; if ((key === "w" || key === "h") && value <= 1) { widgetNode.removeAttribute(attr); return; } widgetNode.setAttribute(attr, String(value)); }); } function syncKnownWidgetOrder() { const ordered = workspaceState.order.filter(function (widgetId, index, items) { return !!widgetId && items.indexOf(widgetId) === index && hasWidget(widgetId); }); toArray(document.querySelectorAll(".grid-stack-item[id]")).forEach(function (node) { if (!ordered.includes(node.id)) { ordered.push(node.id); } }); workspaceState.order = ordered; return ordered; } function getVisibleWidgetNodes() { const gridElement = getGridElement(); return syncKnownWidgetOrder() .map(getWidgetNode) .filter(function (node) { return !!(node && node.parentElement === gridElement); }); } function getVisibleWidgetIds() { return getVisibleWidgetNodes().map(function (node) { return node.id; }); } function hasWidget(widgetId) { return !!getWidgetNode(widgetId); } function getAnchorWidgetId(visibleIds) { const ids = Array.isArray(visibleIds) && visibleIds.length ? visibleIds.map(function (value) { return String(value || "").trim(); }) : getVisibleWidgetIds(); const explicitAnchorId = String(workspaceState.anchorWidgetId || "").trim(); if ( explicitAnchorId && ids.includes(explicitAnchorId) && !workspaceState.minimized.has(explicitAnchorId) ) { return explicitAnchorId; } return ""; } function executeWidgetScript(scriptNode) { if (!scriptNode) { return; } const replacement = document.createElement("script"); toArray(scriptNode.attributes).forEach(function (attribute) { replacement.setAttribute(attribute.name, attribute.value); }); replacement.text = scriptNode.textContent || ""; document.body.appendChild(replacement); replacement.remove(); scriptNode.remove(); } function parseAssetList(value) { return String(value || "") .split("|") .map(function (item) { return String(item || "").trim(); }) .filter(Boolean); } function findStylesheet(href) { return toArray(document.querySelectorAll('link[rel="stylesheet"]')).find( function (node) { return node.getAttribute("href") === href; } ); } function findScript(src) { return toArray(document.querySelectorAll("script[src]")).find(function (node) { return node.getAttribute("src") === src; }); } function ensureStylesheet(href) { if (!href) { return Promise.resolve(null); } if (assetPromises.styles[href]) { return assetPromises.styles[href]; } const existing = findStylesheet(href); if (existing) { assetPromises.styles[href] = Promise.resolve(existing); return assetPromises.styles[href]; } assetPromises.styles[href] = new Promise(function (resolve) { const node = document.createElement("link"); node.rel = "stylesheet"; node.href = href; node.addEventListener( "load", function () { resolve(node); }, { once: true } ); node.addEventListener( "error", function () { resolve(node); }, { once: true } ); document.head.appendChild(node); }); return assetPromises.styles[href]; } function ensureScript(src) { if (!src) { return Promise.resolve(null); } if (assetPromises.scripts[src]) { return assetPromises.scripts[src]; } const existing = findScript(src); if (existing) { assetPromises.scripts[src] = Promise.resolve(existing); return assetPromises.scripts[src]; } assetPromises.scripts[src] = new Promise(function (resolve) { const node = document.createElement("script"); node.src = src; node.async = false; node.addEventListener( "load", function () { resolve(node); }, { once: true } ); node.addEventListener( "error", function () { resolve(node); }, { once: true } ); document.head.appendChild(node); }); return assetPromises.scripts[src]; } function ensureContainerAssets(container) { if (!container) { return Promise.resolve(); } const styleHrefs = parseAssetList(container.getAttribute("data-gia-style-hrefs")); const scriptSrcs = parseAssetList(container.getAttribute("data-gia-script-srcs")); const styleTasks = styleHrefs.map(function (href) { return ensureStylesheet(href); }); const scriptChain = scriptSrcs.reduce(function (promise, src) { return promise.then(function () { return ensureScript(src); }); }, Promise.resolve()); return Promise.all(styleTasks).then(function () { return scriptChain; }); } function getWidgetTitle(widgetNode) { if (!widgetNode) { return "Window"; } const heading = widgetNode.querySelector(".gia-widget-title"); const title = String(heading ? heading.textContent || "" : "").trim(); return title || "Window"; } function getWidgetIconClass(widgetNode) { if (!widgetNode) { return "fa-solid fa-window-maximize"; } const icon = widgetNode.querySelector(".gia-widget-heading-icon i"); const value = String(icon ? icon.className || "" : "").trim(); return value || "fa-solid fa-window-maximize"; } function getWidgetMeta(widgetId) { const node = getWidgetNode(widgetId); const minimizedMeta = workspaceState.minimized.get(widgetId) || null; return { id: widgetId, node: node, minimized: workspaceState.minimized.has(widgetId), title: minimizedMeta && minimizedMeta.title ? String(minimizedMeta.title) : getWidgetTitle(node), iconClass: minimizedMeta && minimizedMeta.iconClass ? String(minimizedMeta.iconClass) : getWidgetIconClass(node), }; } function normalizeComposeWidgetSegment(value) { const cleaned = String(value || "") .trim() .toLowerCase() .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, ""); return cleaned || "none"; } function buildComposeWidgetIdFromUrl(urlValue) { const raw = String(urlValue || "").trim(); if (!raw) { return ""; } try { const parsed = new URL(raw, window.location.origin); if (!/\/compose\/widget\/?$/.test(parsed.pathname)) { return ""; } const service = normalizeComposeWidgetSegment(parsed.searchParams.get("service")); const identifier = normalizeComposeWidgetSegment( String(parsed.searchParams.get("identifier") || "").split("@", 1)[0] ); const person = normalizeComposeWidgetSegment(parsed.searchParams.get("person")); return ["widget", "compose-widget", service, identifier, person].join("-"); } catch (_err) { return ""; } } function getWidgetUrlFromTrigger(trigger) { if (!trigger || typeof trigger.getAttribute !== "function") { return ""; } return String( trigger.getAttribute("data-widget-url") || trigger.getAttribute("hx-get") || "" ).trim(); } function getRequestedWidgetIdFromTrigger(trigger) { if (!trigger || typeof trigger.getAttribute !== "function") { return ""; } const explicitId = String(trigger.getAttribute("data-gia-widget-id") || "").trim(); if (explicitId) { return explicitId; } return buildComposeWidgetIdFromUrl(getWidgetUrlFromTrigger(trigger)); } function clearPendingWidgetRequest(trigger) { if (!trigger || !trigger.dataset) { return; } const widgetId = String(trigger.dataset.giaPendingWidgetId || "").trim(); if (widgetId) { workspaceState.pendingWidgetIds.delete(widgetId); } delete trigger.dataset.giaPendingWidgetId; } function activateExistingWidget(widgetId) { const id = String(widgetId || "").trim(); if (!id || !hasWidget(id)) { return false; } if (workspaceState.minimized.has(id)) { restoreWidget(id); } else { setActiveWidget(id); syncWidgetChrome(); renderTaskbar(); } if (window.GIAComposePanel && typeof window.GIAComposePanel.scrollWidgetToLatest === "function") { window.GIAComposePanel.scrollWidgetToLatest(id); } return true; } function clearSnapAssistant() { workspaceState.snapAssistantSourceId = ""; workspaceState.snapAssistantMode = ""; const assistant = getSnapAssistant(); if (assistant) { assistant.classList.add("is-hidden"); } const options = getSnapAssistantOptions(); if (options) { options.innerHTML = ""; } } function normalizeSnapState() { if ( workspaceState.anchorWidgetId && ( !hasWidget(workspaceState.anchorWidgetId) || workspaceState.minimized.has(workspaceState.anchorWidgetId) ) ) { workspaceState.anchorWidgetId = ""; } if (!hasWidget(workspaceState.snapLeftId)) { workspaceState.snapLeftId = ""; } if (!hasWidget(workspaceState.snapRightId)) { workspaceState.snapRightId = ""; } if (!hasWidget(workspaceState.snapTopId)) { workspaceState.snapTopId = ""; } if (!hasWidget(workspaceState.snapBottomId)) { workspaceState.snapBottomId = ""; } if (workspaceState.snapLeftId && workspaceState.snapLeftId === workspaceState.snapRightId) { workspaceState.snapRightId = ""; } if (workspaceState.snapTopId && workspaceState.snapTopId === workspaceState.snapBottomId) { workspaceState.snapBottomId = ""; } if (workspaceState.snapTopId || workspaceState.snapBottomId) { workspaceState.snapLeftId = ""; workspaceState.snapRightId = ""; } if (workspaceState.snapLeftId || workspaceState.snapRightId) { workspaceState.snapTopId = ""; workspaceState.snapBottomId = ""; } if ( workspaceState.snapAssistantSourceId && workspaceState.snapAssistantSourceId !== workspaceState.snapLeftId && workspaceState.snapAssistantSourceId !== workspaceState.snapTopId ) { workspaceState.snapAssistantSourceId = ""; workspaceState.snapAssistantMode = ""; } } function getSnapAssistantMeta() { if (workspaceState.snapAssistantMode === "vertical") { return { title: "Snap Bottom", message: "Choose a second window for the bottom side.", }; } return { title: "Snap Right", message: "Choose a second window for the right side.", }; } function setActiveWidget(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return; } workspaceState.activeWidgetId = id; toArray(document.querySelectorAll(".grid-stack-item.is-gia-active")).forEach( function (node) { node.classList.remove("is-gia-active"); } ); const widgetNode = getWidgetNode(id); if (widgetNode) { widgetNode.classList.add("is-gia-active"); } syncWidgetChrome(); renderTaskbar(); } function syncWidgetChrome() { const anchorId = getAnchorWidgetId(syncKnownWidgetOrder()); toArray(document.querySelectorAll(".grid-stack-item[id]")).forEach(function (node) { const widgetId = String(node.id || "").trim(); const spawned = widgetId && widgetId === workspaceState.lastSpawnedId; const gridNode = node.gridstackNode || null; const atLeftEdge = !!( gridNode && Number(gridNode.x || 0) === 0 && Number(gridNode.w || GRID_COLUMNS) < GRID_COLUMNS ); const atRightEdge = !!( gridNode && Number((gridNode.x || 0) + (gridNode.w || GRID_COLUMNS)) === GRID_COLUMNS && Number(gridNode.w || GRID_COLUMNS) < GRID_COLUMNS ); node.classList.toggle("is-gia-anchor", widgetId === anchorId); node.classList.toggle("is-gia-spawned", spawned); node.querySelectorAll('.js-gia-widget-action[data-gia-action="snap-left"]').forEach( function (button) { const icon = button.querySelector("i"); const label = atLeftEdge ? "Choose window for right side" : "Snap window left"; button.setAttribute("aria-label", label); button.setAttribute("title", label); button.classList.toggle("is-link", atLeftEdge); if (icon) { icon.className = atLeftEdge ? "fa-solid fa-table-columns" : "fa-solid fa-arrow-left"; } } ); node.querySelectorAll('.js-gia-widget-action[data-gia-action="snap-right"]').forEach( function (button) { const icon = button.querySelector("i"); const label = atRightEdge ? "Choose window for left side" : "Snap window right"; button.setAttribute("aria-label", label); button.setAttribute("title", label); button.classList.toggle("is-link", atRightEdge); if (icon) { icon.className = atRightEdge ? "fa-solid fa-table-columns" : "fa-solid fa-arrow-right"; } } ); }); } function flashSpawnedWidget(widgetNode) { if (!widgetNode || !widgetNode.id) { return; } workspaceState.lastSpawnedId = widgetNode.id; syncWidgetChrome(); window.setTimeout(function () { if (workspaceState.lastSpawnedId === widgetNode.id) { workspaceState.lastSpawnedId = ""; syncWidgetChrome(); } }, 1800); } function bindWidgetLifecycle(widgetNode) { if (!widgetNode || widgetNode.dataset.giaWidgetBound === "1") { return; } widgetNode.dataset.giaWidgetBound = "1"; widgetNode.addEventListener("pointerdown", function () { setActiveWidget(widgetNode.id); }); } function registerWidget(widgetNode, insertIndex) { if (!widgetNode || !widgetNode.id) { return; } if (!workspaceState.order.includes(widgetNode.id)) { if (typeof insertIndex === "number" && insertIndex >= 0) { const boundedIndex = Math.min(insertIndex, workspaceState.order.length); workspaceState.order.splice(boundedIndex, 0, widgetNode.id); } else { workspaceState.order.push(widgetNode.id); } } bindWidgetLifecycle(widgetNode); setActiveWidget(widgetNode.id); } function rememberPendingSpawn(trigger) { const sourceWidget = trigger && trigger.closest ? trigger.closest(".grid-stack-item[id]") : null; const sourceId = String(sourceWidget && sourceWidget.id ? sourceWidget.id : "").trim(); workspaceState.pendingSpawnSourceId = sourceId; workspaceState.pendingSpawnTs = Date.now(); if (sourceId) { workspaceState.anchorWidgetId = sourceId; syncWidgetChrome(); renderTaskbar(); } } function consumePendingSpawnContext() { const sourceId = String(workspaceState.pendingSpawnSourceId || "").trim(); const spawnTs = Number(workspaceState.pendingSpawnTs || 0); workspaceState.pendingSpawnSourceId = ""; workspaceState.pendingSpawnTs = 0; if (!sourceId || (spawnTs > 0 && Date.now() - spawnTs > 15000)) { return { sourceId: "" }; } return { sourceId: sourceId }; } function renderTaskbar() { const taskbar = getTaskbar(); const itemsNode = getTaskbarItems(); if (!taskbar || !itemsNode) { return; } const widgetIds = syncKnownWidgetOrder(); itemsNode.innerHTML = ""; if (!widgetIds.length) { taskbar.classList.add("is-hidden"); return; } widgetIds.forEach(function (widgetId) { const widgetNode = getWidgetNode(widgetId); const minimized = workspaceState.minimized.has(widgetId); const meta = minimized ? (workspaceState.minimized.get(widgetId) || {}) : { title: getWidgetTitle(widgetNode), iconClass: getWidgetIconClass(widgetNode), }; const item = document.createElement("li"); if (widgetId === workspaceState.activeWidgetId) { item.classList.add("is-active"); } item.classList.add(minimized ? "is-minimized" : "is-open"); const link = document.createElement("a"); link.href = "#"; link.className = "js-gia-taskbar-item"; link.dataset.giaWidgetId = widgetId; link.dataset.giaWidgetState = minimized ? "minimized" : "open"; link.setAttribute( "aria-label", (minimized ? "Restore " : "Minimize ") + String(meta.title || "window") ); const iconWrap = document.createElement("span"); iconWrap.className = "icon is-small"; const icon = document.createElement("i"); icon.className = String(meta.iconClass || "fa-solid fa-window-maximize"); iconWrap.appendChild(icon); const label = document.createElement("span"); label.textContent = String(meta.title || "Window"); link.appendChild(iconWrap); link.appendChild(label); item.appendChild(link); itemsNode.appendChild(item); }); syncWidgetChrome(); taskbar.classList.remove("is-hidden"); } function buildSnapAssistantOption(widgetMeta) { const button = document.createElement("button"); button.type = "button"; button.className = "button is-light js-gia-snap-target"; button.dataset.giaWidgetId = widgetMeta.id; if (widgetMeta.minimized) { button.dataset.giaWidgetState = "minimized"; } const leftGroup = document.createElement("span"); leftGroup.className = "is-inline-flex is-align-items-center"; const iconWrap = document.createElement("span"); iconWrap.className = "icon is-small mr-2"; const icon = document.createElement("i"); icon.className = widgetMeta.iconClass; iconWrap.appendChild(icon); const label = document.createElement("span"); label.textContent = widgetMeta.title; leftGroup.appendChild(iconWrap); leftGroup.appendChild(label); const actionLabel = document.createElement("span"); actionLabel.textContent = widgetMeta.minimized ? "Restore" : "Use"; button.appendChild(leftGroup); button.appendChild(actionLabel); return button; } function renderSnapAssistant() { const assistant = getSnapAssistant(); const options = getSnapAssistantOptions(); if (!assistant || !options) { return; } normalizeSnapState(); const sourceId = workspaceState.snapAssistantSourceId; if (!sourceId) { clearSnapAssistant(); return; } const candidates = syncKnownWidgetOrder().filter(function (widgetId) { return widgetId !== sourceId && hasWidget(widgetId); }); if (!candidates.length) { clearSnapAssistant(); return; } options.innerHTML = ""; candidates.forEach(function (widgetId) { options.appendChild(buildSnapAssistantOption(getWidgetMeta(widgetId))); }); const meta = getSnapAssistantMeta(); const headingText = assistant.querySelector(".gia-snap-assistant-title"); if (headingText) { headingText.textContent = meta.title; } const bodyText = assistant.querySelector(".gia-snap-assistant-message"); if (bodyText) { bodyText.textContent = meta.message; } assistant.classList.remove("is-hidden"); } function getLayoutProfile() { const isMobile = window.matchMedia(MOBILE_MEDIA_QUERY).matches; const isDesktop = window.matchMedia(DESKTOP_MEDIA_QUERY).matches; const viewport = getGridViewportMetrics(); const canFitQuarterTiles = ( isDesktop && viewport.width >= MIN_QUARTER_TILE_WIDTH * 2 && viewport.height >= MIN_QUARTER_TILE_HEIGHT * 2 ); return { isMobile: isMobile, isDesktop: isDesktop, maxVisible: isMobile ? 1 : canFitQuarterTiles ? 4 : 2, }; } function syncGridMetrics() { if (!window.grid || typeof window.grid.cellHeight !== "function") { return; } const gridElement = getGridElement(); if (!gridElement) { return; } const viewport = getGridViewportMetrics(); const height = Math.max(240, viewport.height || 0); const cellHeight = Math.max(32, Math.floor(height / GRID_ROWS)); gridElement.style.height = height + "px"; window.grid.cellHeight(cellHeight); if (typeof window.grid.setAnimation === "function") { window.grid.setAnimation(!window.matchMedia(MOBILE_MEDIA_QUERY).matches); } if (typeof window.grid.column === "function") { window.grid.column(GRID_COLUMNS); } } function buildDefaultLayout(widgetNodes, profile) { if (!widgetNodes.length) { return []; } if (profile.isMobile) { return [ { node: widgetNodes[0], x: 0, y: 0, w: GRID_COLUMNS, h: GRID_ROWS, }, ]; } if (widgetNodes.length === 1) { return [ { node: widgetNodes[0], x: 0, y: 0, w: GRID_COLUMNS, h: GRID_ROWS, }, ]; } if (!profile.isDesktop) { return widgetNodes.slice(0, 2).map(function (node, index) { return { node: node, x: 0, y: index * (GRID_ROWS / 2), w: GRID_COLUMNS, h: GRID_ROWS / 2, }; }); } if (widgetNodes.length === 2) { return widgetNodes.map(function (node, index) { return { node: node, x: index * (GRID_COLUMNS / 2), y: 0, w: GRID_COLUMNS / 2, h: GRID_ROWS, }; }); } if (widgetNodes.length === 3) { const dominantNode = widgetNodes.find(function (node) { return node.id === workspaceState.activeWidgetId; }) || widgetNodes[widgetNodes.length - 1]; const secondaryNodes = widgetNodes.filter(function (node) { return node !== dominantNode; }); return [ { node: dominantNode, x: 0, y: 0, w: GRID_COLUMNS / 2, h: GRID_ROWS, }, { node: secondaryNodes[0], x: GRID_COLUMNS / 2, y: 0, w: GRID_COLUMNS / 2, h: GRID_ROWS / 2, }, { node: secondaryNodes[1], x: GRID_COLUMNS / 2, y: GRID_ROWS / 2, w: GRID_COLUMNS / 2, h: GRID_ROWS / 2, }, ]; } return widgetNodes.slice(0, 4).map(function (node, index) { return { node: node, x: (index % 2) * (GRID_COLUMNS / 2), y: Math.floor(index / 2) * (GRID_ROWS / 2), w: GRID_COLUMNS / 2, h: GRID_ROWS / 2, }; }); } function buildWorkspaceLayoutItems(widgetNodes, profile) { const snappedLayout = buildSnappedLayout(profile); const anchoredLayout = snappedLayout ? null : buildAnchoredLayout(widgetNodes, profile); return snappedLayout || anchoredLayout || buildDefaultLayout(widgetNodes, profile); } function buildIncomingLayout(widgetNode, spawnSourceId) { if (!widgetNode) { return null; } const sourceId = String(spawnSourceId || "").trim(); const profile = getLayoutProfile(); const existingNodes = getVisibleWidgetNodes().filter(function (node) { return node.id !== widgetNode.id; }); let widgetNodes = existingNodes.slice(); if (sourceId) { const sourceIndex = widgetNodes.findIndex(function (node) { return node.id === sourceId; }); if (sourceIndex >= 0) { widgetNodes.splice(sourceIndex + 1, 0, widgetNode); } else { widgetNodes.push(widgetNode); } } else { widgetNodes.push(widgetNode); } const previousSpawnedId = workspaceState.lastSpawnedId; if (sourceId && sourceId !== widgetNode.id) { workspaceState.lastSpawnedId = widgetNode.id; } const layoutItems = buildWorkspaceLayoutItems(widgetNodes, profile); workspaceState.lastSpawnedId = previousSpawnedId; return layoutItems.find(function (item) { return item && item.node === widgetNode; }) || null; } function buildAnchoredLayout(widgetNodes, profile) { if (!widgetNodes.length || widgetNodes.length < 2) { return null; } const anchorId = getAnchorWidgetId( widgetNodes.map(function (node) { return node.id; }) ); if (!anchorId) { return null; } const anchorNode = widgetNodes.find(function (node) { return node.id === anchorId; }); if (!anchorNode) { return null; } let secondaryNodes = widgetNodes.filter(function (node) { return node.id !== anchorId; }); const spawnedIndex = secondaryNodes.findIndex(function (node) { return node.id === workspaceState.lastSpawnedId; }); if (spawnedIndex > 0) { secondaryNodes = [secondaryNodes[spawnedIndex]].concat( secondaryNodes.filter(function (_node, index) { return index !== spawnedIndex; }) ); } if (!secondaryNodes.length) { return null; } if (profile.isMobile || !profile.isDesktop) { return [ { node: anchorNode, x: 0, y: 0, w: GRID_COLUMNS, h: GRID_ROWS / 2, }, { node: secondaryNodes[0], x: 0, y: GRID_ROWS / 2, w: GRID_COLUMNS, h: GRID_ROWS / 2, }, ]; } const stackedNodes = secondaryNodes.slice(0, 3); const layoutItems = [ { node: anchorNode, x: 0, y: 0, w: GRID_COLUMNS / 2, h: GRID_ROWS, }, ]; const baseHeight = Math.floor(GRID_ROWS / stackedNodes.length); let offsetY = 0; stackedNodes.forEach(function (node, index) { const height = index === stackedNodes.length - 1 ? GRID_ROWS - offsetY : baseHeight; layoutItems.push({ node: node, x: GRID_COLUMNS / 2, y: offsetY, w: GRID_COLUMNS / 2, h: height, }); offsetY += height; }); return layoutItems; } function buildSnappedLayout(profile) { const gridElement = getGridElement(); const leftNode = getWidgetNode(workspaceState.snapLeftId); const rightNode = getWidgetNode(workspaceState.snapRightId); if ( leftNode && rightNode && leftNode.parentElement === gridElement && rightNode.parentElement === gridElement && !profile.isMobile ) { return [ { node: leftNode, x: 0, y: 0, w: GRID_COLUMNS / 2, h: GRID_ROWS, }, { node: rightNode, x: GRID_COLUMNS / 2, y: 0, w: GRID_COLUMNS / 2, h: GRID_ROWS, }, ]; } const topNode = getWidgetNode(workspaceState.snapTopId); const bottomNode = getWidgetNode(workspaceState.snapBottomId); if ( !topNode || !bottomNode || topNode.parentElement !== gridElement || bottomNode.parentElement !== gridElement ) { return null; } return [ { node: topNode, x: 0, y: 0, w: GRID_COLUMNS, h: GRID_ROWS / 2, }, { node: bottomNode, x: 0, y: GRID_ROWS / 2, w: GRID_COLUMNS, h: GRID_ROWS / 2, }, ]; } function detachWidgetFromGrid(widgetNode) { if ( widgetNode && window.grid && typeof window.grid.removeWidget === "function" && widgetNode.parentElement === getGridElement() ) { window.grid.removeWidget(widgetNode, false, false); } } function attachWidgetToGrid(widgetNode) { if (!widgetNode || !window.grid || !getGridElement()) { return; } widgetNode.classList.remove("is-hidden"); if (widgetNode.parentElement !== getGridElement()) { getGridElement().appendChild(widgetNode); } if (typeof window.grid.makeWidget === "function") { window.grid.makeWidget(widgetNode); } if (window.htmx && typeof window.htmx.process === "function") { window.htmx.process(widgetNode); } } function chooseFallbackActiveWidget() { const visibleIds = getVisibleWidgetIds(); if (!visibleIds.length) { workspaceState.activeWidgetId = ""; return; } const anchorId = getAnchorWidgetId(visibleIds); workspaceState.activeWidgetId = anchorId || visibleIds[visibleIds.length - 1]; } function minimizeWidget(widgetId, options) { const config = options || {}; const widgetNode = getWidgetNode(widgetId); if (!widgetNode || workspaceState.minimized.has(widgetId)) { return; } workspaceState.minimized.set(widgetId, { title: getWidgetTitle(widgetNode), iconClass: getWidgetIconClass(widgetNode), ts: Date.now(), }); if (workspaceState.activeWidgetId === widgetId) { chooseFallbackActiveWidget(); } if (workspaceState.lastSpawnedId === widgetId) { workspaceState.lastSpawnedId = ""; } if (workspaceState.snapLeftId === widgetId) { workspaceState.snapLeftId = ""; workspaceState.snapRightId = ""; clearSnapAssistant(); } if (workspaceState.snapRightId === widgetId) { workspaceState.snapRightId = ""; clearSnapAssistant(); } if (workspaceState.snapTopId === widgetId) { workspaceState.snapTopId = ""; workspaceState.snapBottomId = ""; clearSnapAssistant(); } if (workspaceState.snapBottomId === widgetId) { workspaceState.snapBottomId = ""; clearSnapAssistant(); } if (workspaceState.anchorWidgetId === widgetId) { workspaceState.anchorWidgetId = ""; } detachWidgetFromGrid(widgetNode); const stash = getWorkspaceStash(); if (stash) { stash.appendChild(widgetNode); } widgetNode.classList.add("is-hidden"); renderTaskbar(); if (!config.skipLayout) { layoutWorkspace(); } } function restoreWidget(widgetId, options) { const config = options || {}; const widgetNode = getWidgetNode(widgetId); if (!widgetNode || !workspaceState.minimized.has(widgetId)) { return; } workspaceState.minimized.delete(widgetId); attachWidgetToGrid(widgetNode); registerWidget(widgetNode); renderTaskbar(); if (!config.skipLayout) { layoutWorkspace(); } } function clearWidgetState(widgetId) { workspaceState.order = workspaceState.order.filter(function (item) { return item !== widgetId; }); workspaceState.minimized.delete(widgetId); if (workspaceState.activeWidgetId === widgetId) { workspaceState.activeWidgetId = ""; } if (workspaceState.anchorWidgetId === widgetId) { workspaceState.anchorWidgetId = ""; } if (workspaceState.lastSpawnedId === widgetId) { workspaceState.lastSpawnedId = ""; } if (workspaceState.snapLeftId === widgetId) { workspaceState.snapLeftId = ""; } if (workspaceState.snapRightId === widgetId) { workspaceState.snapRightId = ""; } if (workspaceState.snapTopId === widgetId) { workspaceState.snapTopId = ""; } if (workspaceState.snapBottomId === widgetId) { workspaceState.snapBottomId = ""; } if (workspaceState.snapAssistantSourceId === widgetId) { clearSnapAssistant(); } } function removeWidget(widgetId) { const widgetNode = getWidgetNode(widgetId); if (!widgetNode) { return; } clearWidgetState(widgetId); if ( window.grid && typeof window.grid.removeWidget === "function" && widgetNode.parentElement === getGridElement() ) { window.grid.removeWidget(widgetNode, true, false); } else { widgetNode.remove(); } chooseFallbackActiveWidget(); renderTaskbar(); layoutWorkspace(); } function enforceVisibleLimit(profile) { normalizeSnapState(); const snappedLayout = buildSnappedLayout(profile); const anchorId = getAnchorWidgetId(syncKnownWidgetOrder()); const visibleIds = getVisibleWidgetIds(); const hardProtectedIds = new Set([anchorId].filter(Boolean)); const softProtectedIds = new Set( [ workspaceState.activeWidgetId, workspaceState.snapLeftId, workspaceState.snapRightId, workspaceState.snapTopId, workspaceState.snapBottomId, ].filter(Boolean) ); const anchoredMobileSplit = !snappedLayout && !!anchorId && profile.isMobile; const maxVisible = snappedLayout ? 2 : anchoredMobileSplit ? 2 : profile.maxVisible; if (visibleIds.length <= maxVisible) { return; } const removableIds = visibleIds.filter(function (widgetId) { return !hardProtectedIds.has(widgetId) && !softProtectedIds.has(widgetId); }); while (getVisibleWidgetIds().length > maxVisible && removableIds.length) { minimizeWidget(removableIds.shift(), { skipLayout: true }); } const remainingIds = getVisibleWidgetIds(); while (remainingIds.length > maxVisible) { const candidate = remainingIds.find(function (widgetId) { return !hardProtectedIds.has(widgetId) && !softProtectedIds.has(widgetId); }) || remainingIds.find(function (widgetId) { return !hardProtectedIds.has(widgetId); }); if (!candidate) { break; } minimizeWidget(candidate, { skipLayout: true }); remainingIds.splice(remainingIds.indexOf(candidate), 1); } } function applyLayout(layoutItems) { if (!window.grid || !layoutItems.length) { return; } syncGridMetrics(); layoutItems.forEach(function (item) { writeGridPositionAttrs(item.node, item); }); const canLoadAtomically = typeof window.grid.load === "function" && layoutItems.every(function (item) { return !!( item && item.node && item.node.id && item.node.gridstackNode && String(item.node.gridstackNode.id || "") === String(item.node.id || "") ); }); if (canLoadAtomically) { window.grid.load( layoutItems.map(function (item) { return { id: item.node.id, x: item.x, y: item.y, w: item.w, h: item.h, }; }), false ); return; } if (typeof window.grid.batchUpdate === "function") { window.grid.batchUpdate(true); } layoutItems.forEach(function (item) { window.grid.update(item.node, { x: item.x, y: item.y, w: item.w, h: item.h, }); }); if (typeof window.grid.batchUpdate === "function") { window.grid.batchUpdate(false); } } function layoutWorkspace() { if (!window.grid || !getGridElement()) { return; } normalizeSnapState(); const profile = getLayoutProfile(); enforceVisibleLimit(profile); renderSnapAssistant(); const visibleNodes = getVisibleWidgetNodes(); const layoutItems = buildWorkspaceLayoutItems(visibleNodes, profile); applyLayout(layoutItems); if (!workspaceState.activeWidgetId && layoutItems.length) { setActiveWidget(layoutItems[layoutItems.length - 1].node.id); } else if (workspaceState.activeWidgetId) { const activeNode = getWidgetNode(workspaceState.activeWidgetId); if (activeNode) { activeNode.classList.add("is-gia-active"); } } renderTaskbar(); } function tileWidget(widgetId) { if (!widgetId) { return; } if (workspaceState.snapLeftId === widgetId || workspaceState.snapRightId === widgetId) { workspaceState.snapLeftId = ""; workspaceState.snapRightId = ""; } if (workspaceState.snapTopId === widgetId || workspaceState.snapBottomId === widgetId) { workspaceState.snapTopId = ""; workspaceState.snapBottomId = ""; } if (workspaceState.snapAssistantSourceId === widgetId) { clearSnapAssistant(); } setActiveWidget(widgetId); layoutWorkspace(); } function getSnapCandidateIds(excludeId, options) { const config = options || {}; const candidates = []; const pushCandidate = function (widgetId) { const id = String(widgetId || "").trim(); if (!id || id === excludeId || candidates.includes(id) || !hasWidget(id)) { return; } candidates.push(id); }; const visibleIds = getVisibleWidgetIds().filter(function (widgetId) { return widgetId !== excludeId; }); pushCandidate(getAnchorWidgetId(visibleIds)); pushCandidate(workspaceState.activeWidgetId); visibleIds.forEach(pushCandidate); if (config.includeMinimized) { getLockedWidgetIds().forEach(pushCandidate); syncKnownWidgetOrder().forEach(function (widgetId) { if (workspaceState.minimized.has(widgetId)) { pushCandidate(widgetId); } }); } return candidates; } function chooseSnapCandidate(excludeId, options) { const candidates = getSnapCandidateIds(excludeId, options); return candidates.length ? candidates[0] : ""; } function ensureWidgetVisible(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return ""; } if (workspaceState.minimized.has(id)) { restoreWidget(id, { skipLayout: true }); } return id; } function snapWidgetLeft(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return; } const candidates = getSnapCandidateIds(id, { includeMinimized: true }); workspaceState.snapTopId = ""; workspaceState.snapBottomId = ""; workspaceState.snapLeftId = id; if (candidates.length === 1) { workspaceState.snapRightId = ensureWidgetVisible(candidates[0]); clearSnapAssistant(); } else { workspaceState.snapRightId = ""; workspaceState.snapAssistantSourceId = id; workspaceState.snapAssistantMode = "horizontal"; } setActiveWidget(id); layoutWorkspace(); } function snapWidgetRight(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return; } if (!workspaceState.snapLeftId || workspaceState.snapLeftId === id) { workspaceState.snapLeftId = ensureWidgetVisible( chooseSnapCandidate(id, { includeMinimized: true }) ); } workspaceState.snapTopId = ""; workspaceState.snapBottomId = ""; workspaceState.snapRightId = id; clearSnapAssistant(); setActiveWidget(id); layoutWorkspace(); } function chooseSnapRight(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return; } ensureWidgetVisible(id); workspaceState.snapTopId = ""; workspaceState.snapBottomId = ""; workspaceState.snapRightId = id; clearSnapAssistant(); setActiveWidget(id); layoutWorkspace(); } function snapWidgetTop(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return; } const partnerId = ensureWidgetVisible( chooseSnapCandidate(id, { includeMinimized: true }) ); workspaceState.snapLeftId = ""; workspaceState.snapRightId = ""; workspaceState.snapTopId = id; workspaceState.snapBottomId = partnerId; clearSnapAssistant(); setActiveWidget(id); layoutWorkspace(); } function snapWidgetBottom(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return; } const partnerId = ensureWidgetVisible( chooseSnapCandidate(id, { includeMinimized: true }) ); workspaceState.snapLeftId = ""; workspaceState.snapRightId = ""; workspaceState.snapTopId = partnerId; workspaceState.snapBottomId = id; clearSnapAssistant(); setActiveWidget(id); layoutWorkspace(); } function chooseSnapBottom(widgetId) { const id = String(widgetId || "").trim(); if (!id) { return; } ensureWidgetVisible(id); workspaceState.snapLeftId = ""; workspaceState.snapRightId = ""; workspaceState.snapBottomId = id; clearSnapAssistant(); setActiveWidget(id); layoutWorkspace(); } function replaceExistingWidget(widgetNode) { if (!widgetNode || !widgetNode.id) { return -1; } const existingWidget = document.getElementById(widgetNode.id); if (!existingWidget) { return -1; } const previousIndex = workspaceState.order.indexOf(widgetNode.id); clearWidgetState(widgetNode.id); if ( window.grid && typeof window.grid.removeWidget === "function" && existingWidget.parentElement === getGridElement() ) { window.grid.removeWidget(existingWidget, true, false); } else { existingWidget.remove(); } return previousIndex; } function processWidgetShell(container) { if (!container || container.dataset.giaWidgetProcessed === "1") { return Promise.resolve(); } container.dataset.giaWidgetProcessed = "1"; const spawnContext = consumePendingSpawnContext(); const widgetNode = container.firstElementChild ? container.firstElementChild.cloneNode(true) : null; if (!widgetNode) { container.remove(); return Promise.resolve(); } const scripts = toArray(widgetNode.querySelectorAll("script")); const previousIndex = replaceExistingWidget(widgetNode); container.remove(); if (!window.grid || !getGridElement()) { return Promise.resolve(); } const initialLayout = buildIncomingLayout(widgetNode, spawnContext.sourceId); if (initialLayout) { writeGridPositionAttrs(widgetNode, initialLayout); } getGridElement().appendChild(widgetNode); if (typeof window.grid.makeWidget === "function") { window.grid.makeWidget(widgetNode); } else if (typeof window.grid.addWidget === "function") { window.grid.addWidget(widgetNode); } if (window.htmx && typeof window.htmx.process === "function") { window.htmx.process(widgetNode); } window.giaEnableWidgetSpawnButtons(widgetNode); const liveWidget = widgetNode.id ? document.getElementById(widgetNode.id) : widgetNode; if (liveWidget && liveWidget.id) { workspaceState.pendingWidgetIds.delete(liveWidget.id); } let insertIndex = previousIndex; if (spawnContext.sourceId && spawnContext.sourceId !== liveWidget.id) { const sourceIndex = workspaceState.order.indexOf(spawnContext.sourceId); if (sourceIndex >= 0) { insertIndex = sourceIndex + 1; } workspaceState.anchorWidgetId = spawnContext.sourceId; liveWidget.dataset.giaSpawnSourceId = spawnContext.sourceId; } else { delete liveWidget.dataset.giaSpawnSourceId; } registerWidget(liveWidget, insertIndex); return ensureContainerAssets(container).then(function () { scripts.forEach(executeWidgetScript); if (window.GIAComposePanel && typeof window.GIAComposePanel.initAll === "function") { window.GIAComposePanel.initAll(liveWidget); } if (spawnContext.sourceId && spawnContext.sourceId !== liveWidget.id) { flashSpawnedWidget(liveWidget); } layoutWorkspace(); }); } function processPendingWidgetShells(root) { const scope = withDocumentRoot(root); const containers = []; if ( scope !== document && typeof scope.matches === "function" && scope.matches(WIDGET_SHELL_SELECTOR) ) { containers.push(scope); } scope.querySelectorAll(WIDGET_SHELL_SELECTOR).forEach(function (node) { containers.push(node); }); widgetShellProcessingChain = widgetShellProcessingChain .catch(function () { return null; }) .then(function () { return containers.reduce(function (promise, containerNode) { return promise.then(function () { return processWidgetShell(containerNode); }); }, Promise.resolve()); }) .finally(function () { layoutWorkspace(); window.giaEnableWidgetSpawnButtons(document); }); return widgetShellProcessingChain; } function enableFloatingWindowInteractions(windowEl) { if (!windowEl || windowEl.dataset.giaWindowInteractive === "1") { return; } windowEl.dataset.giaWindowInteractive = "1"; windowEl.setAttribute("unmovable", ""); const heading = windowEl.querySelector(".panel-heading"); if (!heading) { return; } let dragging = false; let startX = 0; let startY = 0; let startLeft = 0; let startTop = 0; const onMove = function (event) { if (!dragging) { return; } const deltaX = event.clientX - startX; const deltaY = event.clientY - startY; windowEl.style.left = startLeft + deltaX + "px"; windowEl.style.top = startTop + deltaY + "px"; windowEl.style.right = "auto"; windowEl.style.bottom = "auto"; }; const stopDrag = function () { dragging = false; document.removeEventListener("pointermove", onMove); document.removeEventListener("pointerup", stopDrag); }; heading.addEventListener("pointerdown", function (event) { if (event.button !== 0) { return; } const interactive = event.target.closest( "button, a, input, textarea, select, label, .delete, .icon" ); if (interactive) { return; } const windowRect = windowEl.getBoundingClientRect(); windowEl.style.position = "fixed"; startLeft = windowRect.left; startTop = windowRect.top; startX = event.clientX; startY = event.clientY; dragging = true; document.addEventListener("pointermove", onMove); document.addEventListener("pointerup", stopDrag); event.preventDefault(); }); } function positionFloatingWindow(windowEl) { if (!windowEl) { return; } const margin = 12; const rect = windowEl.getBoundingClientRect(); const anchor = window.giaWindowAnchor || null; windowEl.style.position = "fixed"; if (!anchor || Date.now() - anchor.ts > 10000) { const fallbackLeft = Math.max(margin, Math.round((window.innerWidth - rect.width) / 2)); const fallbackTop = Math.max(margin, Math.round((window.innerHeight - rect.height) / 2)); windowEl.style.left = fallbackLeft + "px"; windowEl.style.top = fallbackTop + "px"; return; } const desiredLeftViewport = anchor.left; const desiredTopViewport = anchor.bottom + 6; const maxLeftViewport = window.innerWidth - rect.width - margin; const maxTopViewport = window.innerHeight - rect.height - margin; const boundedLeftViewport = Math.max( margin, Math.min(desiredLeftViewport, maxLeftViewport) ); const boundedTopViewport = Math.max( margin, Math.min(desiredTopViewport, maxTopViewport) ); windowEl.style.left = boundedLeftViewport + "px"; windowEl.style.top = boundedTopViewport + "px"; windowEl.style.right = "auto"; windowEl.style.bottom = "auto"; windowEl.style.transform = "none"; window.giaWindowAnchor = null; } function handleWidgetAction(action, widgetId) { if (!action || !widgetId) { return; } if (action === "close") { removeWidget(widgetId); return; } if (action === "minimize") { minimizeWidget(widgetId); return; } if (action === "tile") { tileWidget(widgetId); return; } if (action === "snap-left") { snapWidgetLeft(widgetId); return; } if (action === "snap-top") { snapWidgetTop(widgetId); return; } if (action === "snap-right") { snapWidgetRight(widgetId); return; } if (action === "snap-bottom") { snapWidgetBottom(widgetId); } } function initWorkspaceShell() { if (window.giaWorkspaceShellInitialised) { processPendingWidgetShells(document); return; } const gridElement = document.getElementById("grid-stack-main"); if (!gridElement || !window.GridStack) { return; } window.giaWorkspaceShellInitialised = true; window.gridElement = gridElement; window.giaWindowAnchor = null; document.body.classList.add("gia-has-workspace"); document.documentElement.classList.add("gia-has-workspace-root"); window.giaPrepareWidgetTarget = function () { return document.getElementById("widgets-here"); }; window.giaCanSpawnWidgets = function () { return !!( window.grid && typeof window.grid.addWidget === "function" && document.getElementById("widgets-here") ); }; window.giaEnableWidgetSpawnButtons = function (root) { const scope = withDocumentRoot(root); const canSpawn = window.giaCanSpawnWidgets(); scope.querySelectorAll(WIDGET_SPAWN_SELECTOR).forEach(function (button) { const widgetUrl = String( button.getAttribute("data-widget-url") || button.getAttribute("hx-get") || "" ).trim(); const visible = canSpawn && !!widgetUrl; button.classList.toggle("is-hidden", !visible); button.setAttribute("aria-hidden", visible ? "false" : "true"); }); }; window.giaPrepareWindowAnchor = function (trigger) { if (!trigger || !trigger.getBoundingClientRect) { window.giaWindowAnchor = null; return; } const rect = trigger.getBoundingClientRect(); window.giaWindowAnchor = { left: rect.left, right: rect.right, top: rect.top, bottom: rect.bottom, width: rect.width, height: rect.height, ts: Date.now(), }; }; window.giaPositionFloatingWindow = positionFloatingWindow; window.giaEnableFloatingWindowInteractions = enableFloatingWindowInteractions; window.giaCompactGrid = layoutWorkspace; window.giaRemoveWidget = removeWidget; window.giaMinimizeWidget = minimizeWidget; window.giaRestoreWidget = restoreWidget; window.giaProcessWidgetShells = processPendingWidgetShells; window.grid = window.GridStack.init( { animate: true, auto: false, cellHeight: 40, column: GRID_COLUMNS, float: false, margin: 8, removable: false, staticGrid: true, }, gridElement ); document.addEventListener("click", function (event) { const spawnTrigger = event.target.closest(WIDGET_SPAWN_SELECTOR) || event.target.closest(WIDGET_LOAD_TARGET_SELECTOR); if (spawnTrigger) { rememberPendingSpawn(spawnTrigger); window.giaPrepareWidgetTarget(); } const widgetAction = event.target.closest(WIDGET_ACTION_SELECTOR); if (widgetAction) { event.preventDefault(); event.stopPropagation(); handleWidgetAction( String(widgetAction.dataset.giaAction || ""), String(widgetAction.dataset.giaWidgetId || "") ); return; } const taskbarItem = event.target.closest(".js-gia-taskbar-item"); if (taskbarItem) { event.preventDefault(); const widgetId = String(taskbarItem.dataset.giaWidgetId || ""); if (workspaceState.minimized.has(widgetId)) { restoreWidget(widgetId); } else { minimizeWidget(widgetId); } return; } const snapTarget = event.target.closest(".js-gia-snap-target"); if (snapTarget) { event.preventDefault(); if (workspaceState.snapAssistantMode === "vertical") { chooseSnapBottom(String(snapTarget.dataset.giaWidgetId || "")); } else { chooseSnapRight(String(snapTarget.dataset.giaWidgetId || "")); } return; } if (event.target.closest(SNAP_ASSISTANT_CLOSE_SELECTOR)) { event.preventDefault(); clearSnapAssistant(); } }); document.body.addEventListener("htmx:beforeRequest", function (event) { const trigger = event && event.detail ? event.detail.elt : null; if (!trigger || typeof trigger.getAttribute !== "function") { return; } const target = event.detail ? event.detail.target : null; const targetId = String((target && target.id) || trigger.getAttribute("hx-target") || "") .replace(/^#/, "") .trim(); if (targetId !== "widgets-here") { return; } const widgetId = getRequestedWidgetIdFromTrigger(trigger); if (!widgetId) { return; } if (workspaceState.pendingWidgetIds.has(widgetId) || hasWidget(widgetId)) { event.preventDefault(); activateExistingWidget(widgetId); return; } workspaceState.pendingWidgetIds.add(widgetId); trigger.dataset.giaPendingWidgetId = widgetId; }); document.body.addEventListener("htmx:afterRequest", function (event) { const trigger = event && event.detail ? event.detail.elt : null; if (!trigger || !trigger.dataset || !trigger.dataset.giaPendingWidgetId) { return; } if (event.detail && event.detail.successful) { return; } clearPendingWidgetRequest(trigger); }); document.body.addEventListener("htmx:afterSwap", function (event) { const target = (event && event.target) || document; window.giaEnableWidgetSpawnButtons(target); if (((target && target.id) || "") === "widgets-here") { processPendingWidgetShells(target); return; } if (((target && target.id) || "") !== "windows-here") { return; } target.querySelectorAll(".floating-window").forEach(function (floatingWindow) { window.setTimeout(function () { positionFloatingWindow(floatingWindow); enableFloatingWindowInteractions(floatingWindow); }, 0); }); }); document.addEventListener("load-widget", function (event) { processPendingWidgetShells(event && event.detail && event.detail.root); }); document.addEventListener("gia:load-widget", function (event) { processPendingWidgetShells(event && event.detail && event.detail.root); }); window.addEventListener("resize", function () { window.requestAnimationFrame(layoutWorkspace); }); window.giaEnableWidgetSpawnButtons(document); processPendingWidgetShells(document); window.requestAnimationFrame(layoutWorkspace); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", initWorkspaceShell); } else { initWorkspaceShell(); } })();