import { t as useAccount } from "./use-account-DR6HXMw_.mjs";
import { t as QuickLinksWidget } from "./QuickLinksWidget-o7VeOJix.mjs";
import { t as PointsWidget } from "./PointsWidget-7OpNqRDe.mjs";
import { t as LayoutWidget } from "./LayoutWidget-ENLd1G__.mjs";
import { t as TextWidget } from "./TextWidget-B-2RFD_g.mjs";
import { t as AlertWidget } from "./AlertWidget-CdWJXZn1.mjs";
import { t as BulletListWidget } from "./BulletListWidget-DZrlNEa4.mjs";
import { t as CalendarWidget } from "./CalendarWidget-BvXf1NlC.mjs";
import { t as CardWidget } from "./CardWidget-Bs0hqVjy.mjs";
import { t as CarouselWidget } from "./CarouselWidget-BlS08Aru.mjs";
import { t as ChartWidget } from "./ChartWidget-LcbtV1uD.mjs";
import { t as ContainerWidget } from "./ContainerWidget-bbzSrNUJ.mjs";
import { t as EmbedWidget } from "./EmbedWidget-DfhOZVCm.mjs";
import { t as ImageWidget } from "./ImageWidget-B9gDlIyO.mjs";
import { t as LinkWidget } from "./LinkWidget-BNZzqXbl.mjs";
import { t as ListWidget } from "./ListWidget-ChaESAly.mjs";
import { t as MySiteWidget } from "./MySiteWidget-CIFkSvEk.mjs";
import { t as NestedWidget } from "./NestedWidget-CShcSb_k.mjs";
import { t as QuickShareWidget } from "./QuickShareWidget-ujuaFstp.mjs";
import { t as RecentActivityWidget } from "./RecentActivityWidget-DUHVpA_j.mjs";
import { t as SeparatorWidget } from "./SeparatorWidget-Cqzqs_G_.mjs";
import { t as SpacerWidget } from "./SpacerWidget-BJxZMWLm.mjs";
import { t as TableWidget } from "./TableWidget-CVN4Km00.mjs";
import { t as ToDoWidget } from "./ToDoWidget-DtM9JWAS.mjs";
import { t as VideoWidget } from "./VideoWidget-Cv7ErMg9.mjs";
import { t as ShopWidget } from "./ShopWidget-CwiD88Sr.mjs";
import { r as useOptionalAppNavigation } from "./AppNavigationContext-CUpMPyMJ.mjs";
import { t as useStore } from "./use-store-mvCr42YP.mjs";
import { createContext, createElement, use, useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
import { useQuery } from "@tanstack/react-query";
import { jsx } from "react/jsx-runtime";
import Color from "colorjs.io";
import { SearchSort } from "@fluid-app/ui-components/components/SearchSort";
import { createRoot } from "react-dom/client";
//#region ../../../node_modules/.pnpm/@tanstack+query-core@5.101.0/node_modules/@tanstack/query-core/build/modern/timeoutManager.js
var defaultTimeoutProvider = {
	setTimeout: (callback, delay) => setTimeout(callback, delay),
	clearTimeout: (timeoutId) => clearTimeout(timeoutId),
	setInterval: (callback, delay) => setInterval(callback, delay),
	clearInterval: (intervalId) => clearInterval(intervalId)
};
var TimeoutManager = class {
	#provider = defaultTimeoutProvider;
	#providerCalled = false;
	setTimeoutProvider(provider) {
		this.#provider = provider;
	}
	setTimeout(callback, delay) {
		return this.#provider.setTimeout(callback, delay);
	}
	clearTimeout(timeoutId) {
		this.#provider.clearTimeout(timeoutId);
	}
	setInterval(callback, delay) {
		return this.#provider.setInterval(callback, delay);
	}
	clearInterval(intervalId) {
		this.#provider.clearInterval(intervalId);
	}
};
new TimeoutManager();
function systemSetTimeoutZero(callback) {
	setTimeout(callback, 0);
}
typeof window === "undefined" || "Deno" in globalThis;
function matchQuery(filters, query) {
	const { type = "all", exact, fetchStatus, predicate, queryKey, stale } = filters;
	if (queryKey) {
		if (exact) {
			if (query.queryHash !== hashQueryKeyByOptions(queryKey, query.options)) return false;
		} else if (!partialMatchKey(query.queryKey, queryKey)) return false;
	}
	if (type !== "all") {
		const isActive = query.isActive();
		if (type === "active" && !isActive) return false;
		if (type === "inactive" && isActive) return false;
	}
	if (typeof stale === "boolean" && query.isStale() !== stale) return false;
	if (fetchStatus && fetchStatus !== query.state.fetchStatus) return false;
	if (predicate && !predicate(query)) return false;
	return true;
}
function hashQueryKeyByOptions(queryKey, options) {
	return (options?.queryKeyHashFn || hashKey)(queryKey);
}
function hashKey(queryKey) {
	return JSON.stringify(queryKey, (_, val) => isPlainObject(val) ? Object.keys(val).sort().reduce((result, key) => {
		result[key] = val[key];
		return result;
	}, {}) : val);
}
function partialMatchKey(a, b) {
	if (a === b) return true;
	if (typeof a !== typeof b) return false;
	if (a && b && typeof a === "object" && typeof b === "object") return Object.keys(b).every((key) => partialMatchKey(a[key], b[key]));
	return false;
}
function isPlainObject(o) {
	if (!hasObjectPrototype(o)) return false;
	const ctor = o.constructor;
	if (ctor === void 0) return true;
	const prot = ctor.prototype;
	if (!hasObjectPrototype(prot)) return false;
	if (!prot.hasOwnProperty("isPrototypeOf")) return false;
	if (Object.getPrototypeOf(o) !== Object.prototype) return false;
	return true;
}
function hasObjectPrototype(o) {
	return Object.prototype.toString.call(o) === "[object Object]";
}
//#endregion
//#region ../../../node_modules/.pnpm/@tanstack+query-core@5.101.0/node_modules/@tanstack/query-core/build/modern/notifyManager.js
var defaultScheduler = systemSetTimeoutZero;
function createNotifyManager() {
	let queue = [];
	let transactions = 0;
	let notifyFn = (callback) => {
		callback();
	};
	let batchNotifyFn = (callback) => {
		callback();
	};
	let scheduleFn = defaultScheduler;
	const schedule = (callback) => {
		if (transactions) queue.push(callback);
		else scheduleFn(() => {
			notifyFn(callback);
		});
	};
	const flush = () => {
		const originalQueue = queue;
		queue = [];
		if (originalQueue.length) scheduleFn(() => {
			batchNotifyFn(() => {
				originalQueue.forEach((callback) => {
					notifyFn(callback);
				});
			});
		});
	};
	return {
		batch: (callback) => {
			let result;
			transactions++;
			try {
				result = callback();
			} finally {
				transactions--;
				if (!transactions) flush();
			}
			return result;
		},
		batchCalls: (callback) => {
			return (...args) => {
				schedule(() => {
					callback(...args);
				});
			};
		},
		schedule,
		setNotifyFunction: (fn) => {
			notifyFn = fn;
		},
		setBatchNotifyFunction: (fn) => {
			batchNotifyFn = fn;
		},
		setScheduler: (fn) => {
			scheduleFn = fn;
		}
	};
}
var notifyManager = createNotifyManager();
//#endregion
//#region ../../../node_modules/.pnpm/@tanstack+query-persist-client-core@5.91.11/node_modules/@tanstack/query-persist-client-core/build/modern/createPersister.js
var PERSISTER_KEY_PREFIX = "tanstack-query";
function experimental_createQueryPersister({ storage, buster = "", maxAge = 1e3 * 60 * 60 * 24, serialize = JSON.stringify, deserialize = JSON.parse, prefix = PERSISTER_KEY_PREFIX, refetchOnRestore = true, filters }) {
	function isExpiredOrBusted(persistedQuery) {
		if (persistedQuery.state.dataUpdatedAt) {
			const expired = Date.now() - persistedQuery.state.dataUpdatedAt > maxAge;
			const busted = persistedQuery.buster !== buster;
			if (expired || busted) return true;
			return false;
		}
		return true;
	}
	async function retrieveQuery(queryHash, afterRestoreMacroTask) {
		if (storage != null) {
			const storageKey = `${prefix}-${queryHash}`;
			try {
				const storedData = await storage.getItem(storageKey);
				if (storedData) {
					const persistedQuery = await deserialize(storedData);
					if (isExpiredOrBusted(persistedQuery)) await storage.removeItem(storageKey);
					else {
						if (afterRestoreMacroTask) notifyManager.schedule(() => afterRestoreMacroTask(persistedQuery));
						return persistedQuery.state.data;
					}
				}
			} catch (err) {
				await storage.removeItem(storageKey);
			}
		}
	}
	async function persistQueryByKey(queryKey, queryClient) {
		if (storage != null) {
			const query = queryClient.getQueryCache().find({ queryKey });
			if (query) await persistQuery(query);
		}
	}
	async function persistQuery(query) {
		if (storage != null) {
			const storageKey = `${prefix}-${query.queryHash}`;
			storage.setItem(storageKey, await serialize({
				state: query.state,
				queryKey: query.queryKey,
				queryHash: query.queryHash,
				buster
			}));
		}
	}
	async function persisterFn(queryFn, ctx, query) {
		const matchesFilter = filters ? matchQuery(filters, query) : true;
		if (matchesFilter && query.state.data === void 0 && storage != null) {
			const restoredData = await retrieveQuery(query.queryHash, (persistedQuery) => {
				query.setState({
					dataUpdatedAt: persistedQuery.state.dataUpdatedAt,
					errorUpdatedAt: persistedQuery.state.errorUpdatedAt
				});
				if (refetchOnRestore === "always" || refetchOnRestore === true && query.isStale()) query.fetch();
			});
			if (restoredData !== void 0) return Promise.resolve(restoredData);
		}
		const queryFnResult = await queryFn(ctx);
		if (matchesFilter && storage != null) notifyManager.schedule(() => {
			persistQuery(query);
		});
		return Promise.resolve(queryFnResult);
	}
	async function persisterGc() {
		if (storage?.entries) {
			const entries = await storage.entries();
			for (const [key, value] of entries) if (key.startsWith(prefix)) {
				if (isExpiredOrBusted(await deserialize(value))) await storage.removeItem(key);
			}
		}
	}
	async function restoreQueries(queryClient, filters2 = {}) {
		const { exact, queryKey } = filters2;
		if (storage?.entries) {
			const entries = await storage.entries();
			for (const [key, value] of entries) if (key.startsWith(prefix)) {
				const persistedQuery = await deserialize(value);
				if (isExpiredOrBusted(persistedQuery)) {
					await storage.removeItem(key);
					continue;
				}
				if (queryKey) {
					if (exact) {
						if (persistedQuery.queryHash !== hashKey(queryKey)) continue;
					} else if (!partialMatchKey(persistedQuery.queryKey, queryKey)) continue;
				}
				queryClient.setQueryData(persistedQuery.queryKey, persistedQuery.state.data, { updatedAt: persistedQuery.state.dataUpdatedAt });
			}
		}
	}
	return {
		persisterFn,
		persistQuery,
		persistQueryByKey,
		retrieveQuery,
		persisterGc,
		restoreQueries
	};
}
//#endregion
//#region ../../platform/query-persister/src/persister.ts
const DB_NAME = "fluid_tanstack_query_cache";
const STORE_NAME = "fluid_queries";
const VERSION = 1;
let dbPromise = null;
async function deleteDatabase(reason = "error") {
	return new Promise((resolve, reject) => {
		console.warn(`[IDB] Deleting database due to ${reason}`);
		const req = indexedDB.deleteDatabase(DB_NAME);
		req.onsuccess = () => {
			console.log("[IDB] Database deleted successfully");
			dbPromise = null;
			resolve();
		};
		req.onerror = () => {
			console.error("[IDB] Failed to delete database:", req.error);
			reject(req.error ?? /* @__PURE__ */ new Error("deleteDatabase failed"));
		};
		req.onblocked = () => {
			console.warn("[IDB] Delete blocked: close all tabs using this database");
			dbPromise = null;
			resolve();
		};
	});
}
function openDatabase() {
	return new Promise((resolve, reject) => {
		const req = indexedDB.open(DB_NAME, VERSION);
		req.onupgradeneeded = () => {
			const upgradeDb = req.result;
			if (!upgradeDb.objectStoreNames.contains(STORE_NAME)) upgradeDb.createObjectStore(STORE_NAME);
		};
		req.onsuccess = () => {
			const conn = req.result;
			conn.onversionchange = () => {
				console.trace("[IDB] version change – closing connection");
				conn.close();
				dbPromise = null;
			};
			resolve(conn);
		};
		req.onblocked = () => {
			console.warn("[IDB] open blocked: another connection is holding the DB");
		};
		req.onerror = () => {
			reject(req.error instanceof Error ? req.error : /* @__PURE__ */ new Error(`IndexedDB open failed: ${String(req.error)}`));
		};
	});
}
async function getDbWithRecovery() {
	try {
		return await openDatabase();
	} catch (err) {
		console.warn("[IDB] Initial open failed, attempting recovery:", err);
		try {
			await deleteDatabase();
			console.log("[IDB] Retrying database open after deletion");
			return await openDatabase();
		} catch (retryErr) {
			console.error("[IDB] Recovery failed:", retryErr);
			throw retryErr;
		}
	}
}
function getDb() {
	if (dbPromise) return dbPromise;
	dbPromise = getDbWithRecovery().catch((err) => {
		dbPromise = null;
		throw err;
	});
	return dbPromise;
}
const storage = {
	async getItem(key) {
		try {
			const db = await getDb();
			return new Promise((res) => {
				try {
					const r = db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key);
					r.onsuccess = () => res(r.result);
					r.onerror = () => {
						console.trace("[IDB] getItem error:", r.error);
						res(void 0);
					};
				} catch (txErr) {
					console.trace("[IDB] getItem transaction error:", txErr);
					res(void 0);
				}
			});
		} catch (err) {
			console.trace("[IDB] getItem getDb error:", err);
			try {
				const db = await getDb();
				return new Promise((res) => {
					try {
						const r = db.transaction(STORE_NAME, "readonly").objectStore(STORE_NAME).get(key);
						r.onsuccess = () => res(r.result);
						r.onerror = () => {
							console.trace("[IDB] getItem retry error:", r.error);
							res(void 0);
						};
					} catch (txErr) {
						console.trace("[IDB] getItem retry transaction error:", txErr);
						res(void 0);
					}
				});
			} catch (recoveryErr) {
				console.trace("[IDB] getItem recovery failed:", recoveryErr);
			}
			return;
		}
	},
	async setItem(key, value) {
		const cloneableValue = JSON.parse(JSON.stringify(value));
		try {
			const db = await getDb();
			if (!db) return;
			await new Promise((resolve) => {
				try {
					const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneableValue, key);
					req.onsuccess = () => resolve();
					req.onerror = () => {
						console.trace("[IDB] setItem error:", req.error);
						resolve();
					};
				} catch (txErr) {
					console.trace("[IDB] setItem transaction error:", txErr);
					resolve();
				}
			});
		} catch (err) {
			console.trace("[IDB] setItem getDb error:", err);
			try {
				const db = await getDb();
				await new Promise((resolve) => {
					try {
						const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).put(cloneableValue, key);
						req.onsuccess = () => resolve();
						req.onerror = () => {
							console.trace("[IDB] setItem retry error:", req.error);
							resolve();
						};
					} catch (txErr) {
						console.trace("[IDB] setItem retry transaction error:", txErr);
						resolve();
					}
				});
			} catch (recoveryErr) {
				console.trace("[IDB] setItem recovery failed:", recoveryErr);
			}
		}
	},
	async removeItem(key) {
		try {
			const db = await getDb();
			if (!db) return;
			await new Promise((resolve) => {
				try {
					const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key);
					req.onsuccess = () => resolve();
					req.onerror = () => {
						console.trace("[IDB] removeItem error:", req.error);
						resolve();
					};
				} catch (txErr) {
					console.trace("[IDB] removeItem transaction error:", txErr);
					resolve();
				}
			});
		} catch (err) {
			console.trace("[IDB] removeItem getDb error:", err);
			try {
				const db = await getDb();
				await new Promise((resolve) => {
					try {
						const req = db.transaction(STORE_NAME, "readwrite").objectStore(STORE_NAME).delete(key);
						req.onsuccess = () => resolve();
						req.onerror = () => {
							console.trace("[IDB] removeItem retry error:", req.error);
							resolve();
						};
					} catch (txErr) {
						console.trace("[IDB] removeItem retry transaction error:", txErr);
						resolve();
					}
				});
			} catch (recoveryErr) {
				console.trace("[IDB] removeItem recovery failed:", recoveryErr);
			}
		}
	}
};
function createPersister() {
	return experimental_createQueryPersister({
		storage,
		serialize: (persistedQuery) => persistedQuery,
		deserialize: (cached) => cached
	});
}
//#endregion
//#region ../core/src/app-definition-api-context.ts
const AppDefinitionApiContext = createContext(null);
const AppDefinitionApiProvider = AppDefinitionApiContext.Provider;
function useAppDefinitionApi() {
	const api = use(AppDefinitionApiContext);
	if (!api) throw new Error("useAppDefinitionApi must be used within an AppDefinitionApiProvider");
	return api;
}
//#endregion
//#region ../../platform/theme-engine/src/types.ts
const SEMANTIC_COLOR_NAMES = [
	"background",
	"foreground",
	"primary",
	"secondary",
	"accent",
	"muted",
	"destructive"
];
const FONT_SIZE_KEYS = [
	"extraSmall",
	"small",
	"regular",
	"large",
	"extraLarge",
	"giant"
];
const FONT_FAMILY_KEYS = ["header", "body"];
const RADIUS_KEYS = [
	"small",
	"medium",
	"large",
	"extraLarge"
];
//#endregion
//#region ../../platform/theme-engine/src/color-engine.ts
const BARE_HEX_RE = /^[0-9a-fA-F]{6}$/;
/**
* Attempt to convert any string into a Color using colorjs.io.
* If the string is exactly 6 hex digits it is assumed to be a bare hex value
* (e.g. "3b82f6") and a "#" prefix is added before parsing. Six-letter
* named colours like "orange" or "maroon" are left untouched.
*
* @returns the parsed Color, or a neutral gray (`oklch(0.5 0 0)`) on failure
*/
function parseColor(value) {
	if (BARE_HEX_RE.test(value)) value = `#${value}`;
	try {
		return new Color(value);
	} catch (error) {
		console.warn("[theme] Failed to parse color:", value, error);
		return new Color("oklch", [
			.5,
			0,
			0
		]);
	}
}
/**
* Returns either the original foreground or a corrected lightness variant,
* whichever provides better contrast against `color`.
* Inversion triggers when the |APCA contrast| is below 50 — APCA is signed
* (negative for dark-on-light, positive for light-on-dark), so comparing the
* absolute value avoids flipping dark text that already contrasts well on a
* medium background.
*/
function getForegroundColor(foreground, color) {
	if (foreground.oklch.l == null || color.oklch.l == null) return foreground;
	const contrast = color.contrastAPCA(foreground);
	if (Math.abs(contrast) < 50) return new Color("oklch", [
		color.oklch.l < .7 ? .95 : .15,
		foreground.oklch.c || 0,
		foreground.oklch.h || 0
	]);
	return foreground;
}
const DARK_DERIVATION_CONFIG = {
	background: {
		baseLightness: .15,
		fgLightness: .93
	},
	foreground: {
		baseLightness: .93,
		fgLightness: .15
	},
	muted: {
		baseLightness: .22,
		fgLightness: .75
	},
	primary: {
		baseLightness: "invert",
		fgLightness: .95,
		chromaScale: .9
	},
	secondary: {
		baseLightness: "invert",
		fgLightness: .93,
		chromaScale: .85
	},
	accent: {
		baseLightness: "invert",
		fgLightness: .95,
		chromaScale: .9
	},
	destructive: {
		baseLightness: "invert",
		fgLightness: .95,
		chromaScale: .95
	}
};
/** Invert OKLCH lightness (1 - l), clamped to [0.35, 0.75] to avoid extremes. */
function invertLightness(l) {
	const inverted = 1 - l;
	return Math.max(.35, Math.min(.75, inverted));
}
/**
* Derive a dark-mode ThemeColorInput from its light-mode counterpart.
*/
function deriveDarkVariant(name, light) {
	const config = DARK_DERIVATION_CONFIG[name];
	const chromaScale = config.chromaScale ?? 1;
	const baseLightness = config.baseLightness === "invert" ? invertLightness(light.base.oklch.l ?? 0) : config.baseLightness;
	const fgLightness = config.fgLightness === "invert" ? invertLightness(light.foreground.oklch.l ?? 0) : config.fgLightness;
	return {
		base: new Color("oklch", [
			baseLightness,
			(light.base.oklch.c || 0) * chromaScale,
			light.base.oklch.h || 0
		]),
		foreground: new Color("oklch", [
			fgLightness,
			(light.foreground.oklch.c || 0) * chromaScale,
			light.foreground.oklch.h || 0
		])
	};
}
/**
* Merge auto-derived dark colors with any user-specified overrides.
* For each semantic color, if the user has fully overridden both base and
* foreground those are used; otherwise the missing channels are derived.
*/
function mergeDarkOverrides(def) {
	const darkColors = {};
	for (const name of SEMANTIC_COLOR_NAMES) {
		const lightInput = def.light[name];
		const darkOverride = def.dark[name];
		if (darkOverride?.base && darkOverride?.foreground) darkColors[name] = darkOverride;
		else if (darkOverride) {
			const base = darkOverride.base ?? deriveDarkVariant(name, lightInput).base;
			darkColors[name] = {
				base,
				foreground: darkOverride.foreground ?? getForegroundColor(def.light.foreground.base, base)
			};
		} else darkColors[name] = deriveDarkVariant(name, lightInput);
	}
	return darkColors;
}
function resolveColorSet(colors) {
	const resolved = {};
	for (const name of SEMANTIC_COLOR_NAMES) {
		const input = colors[name];
		resolved[name] = {
			base: input.base.clone(),
			foreground: input.foreground.clone()
		};
	}
	return resolved;
}
/**
* Resolve a ThemeDefinition into a complete ResolvedTheme.
* Dark mode colors are derived from light where not overridden.
*/
function resolveTheme(def) {
	return {
		id: def.id,
		name: def.name,
		light: resolveColorSet(def.light),
		dark: resolveColorSet(mergeDarkOverrides(def)),
		fontSizes: { ...def.fontSizes },
		fontFamilies: { ...def.fontFamilies },
		spacing: def.spacing,
		radii: { ...def.radii }
	};
}
//#endregion
//#region ../../platform/ui-primitives/src/tokens/manifest.ts
const TOKEN_MANIFEST = {
	colors: [
		"background",
		"foreground",
		"card",
		"card-foreground",
		"popover",
		"popover-foreground",
		"primary",
		"primary-foreground",
		"secondary",
		"secondary-foreground",
		"muted",
		"muted-foreground",
		"accent",
		"accent-foreground",
		"destructive",
		"destructive-foreground",
		"border",
		"input",
		"ring",
		"chart-1",
		"chart-2",
		"chart-3",
		"chart-4",
		"chart-5",
		"sidebar",
		"sidebar-foreground",
		"sidebar-primary",
		"sidebar-primary-foreground",
		"sidebar-accent",
		"sidebar-accent-foreground",
		"sidebar-border",
		"sidebar-ring",
		"contrast"
	],
	radii: [
		"",
		"sm",
		"md",
		"lg",
		"xl"
	],
	merchantCustomizable: [
		"background",
		"foreground",
		"primary",
		"secondary",
		"accent",
		"muted",
		"destructive"
	]
};
//#endregion
//#region ../../platform/theme-engine/src/tailwind-overrides.ts
const OVERRIDES = {
	"--color-gray-50": "var(--color-muted)",
	"--color-gray-100": "color-mix(in oklch, var(--color-muted), var(--color-foreground) 15%)",
	"--color-gray-200": "var(--color-border)"
};
/**
* Map Tailwind built-in color names to semantic theme colors using color-mix
* for shade interpolation. Each shade maps to a percentage of the semantic
* color mixed with transparent, so shades naturally adapt to both light and
* dark modes without inversion logic.
*/
function emitTailwindOverrides() {
	const TAILWIND_COLOR_MAP = {
		gray: "foreground",
		red: "destructive",
		blue: "primary",
		green: "accent"
	};
	const TAILWIND_SHADES = [
		50,
		100,
		200,
		300,
		400,
		500,
		600,
		700,
		800,
		900,
		950
	];
	const lines = [];
	for (const [twName, semantic] of Object.entries(TAILWIND_COLOR_MAP)) for (const shade of TAILWIND_SHADES) {
		const override = OVERRIDES[`--color-${twName}-${shade}`];
		if (override) lines.push(`--color-${twName}-${shade}: ${override};`);
		else {
			const percent = Math.max(10, Math.min(Math.round(shade / 10), 100));
			lines.push(`--color-${twName}-${shade}: color-mix(in oklch, var(--color-${semantic}) ${percent}%, transparent);`);
		}
	}
	lines.push("--color-white: var(--color-background);");
	lines.push("--color-black: var(--color-foreground);");
	return lines;
}
//#endregion
//#region ../../platform/theme-engine/src/css-generator.ts
function colorToCSS(color) {
	const result = color.toString({ format: "oklch" });
	if (result.includes("NaN")) {
		console.warn("[theme] colorToCSS produced NaN, using neutral fallback:", result);
		return "oklch(0.5 0 0)";
	}
	return result;
}
function camelToKebab(str) {
	return str.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
}
/**
* Emit --color-{name} and --color-{name}-foreground vars.
* Uses --color- prefix to match portal-widgets/tailwind.config.ts.
*/
function emitColorVars(colors) {
	const lines = [];
	for (const name of SEMANTIC_COLOR_NAMES) {
		const color = colors[name];
		lines.push(`--color-${name}: ${colorToCSS(color.base)};`);
		lines.push(`--color-${name}-foreground: ${colorToCSS(color.foreground)};`);
	}
	return lines;
}
/**
* Format a font family value for CSS output.
* - If the value starts with "var(" (legacy), pass through as-is
* - If the value already contains a comma (has fallback), pass through as-is
* - Otherwise, wrap in quotes and append a generic sans-serif fallback
*/
function formatFontFamily(value) {
	if (value.startsWith("var(")) return value;
	if (value.includes(",")) return value;
	return `'${value}', sans-serif`;
}
/**
* Emit non-color CSS variables (font sizes, families, spacing, radii).
*/
function emitNonColorVars(theme) {
	const lines = [];
	for (const key of FONT_SIZE_KEYS) lines.push(`--font-size-${camelToKebab(key)}: ${theme.fontSizes[key]};`);
	for (const key of FONT_FAMILY_KEYS) lines.push(`--font-${key}: ${formatFontFamily(theme.fontFamilies[key])};`);
	lines.push(`--spacing: ${theme.spacing};`);
	for (const key of RADIUS_KEYS) lines.push(`--radius-${camelToKebab(key)}: ${theme.radii[key]};`);
	return lines;
}
const MERCHANT_CUSTOMIZABLE = new Set(TOKEN_MANIFEST.merchantCustomizable);
/**
* Names that theme-engine emits via emitColorVars (the merchant-customizable
* base set) and their derived `-foreground` siblings. Used to skip duplicate
* emission of `--color-{name}` from the manifest derivation pass.
*
* Typed as `Set<string>` because the synthesized `${name}-foreground` strings
* include `"background-foreground"` / `"foreground-foreground"`, which are not
* members of `SemanticTokenName`. Membership checks against the manifest still
* work because `Set.has(string)` accepts the wider type.
*/
const EMITTED_BY_COLOR_VARS = new Set([...SEMANTIC_COLOR_NAMES, ...SEMANTIC_COLOR_NAMES.map((n) => `${n}-foreground`)]);
/**
* Resolve the alias expression for a non-merchant-customizable token.
*
* Returns `null` for tokens that should fall through to the canonical default
* defined in `ui-primitives.css` (chart palette is intentionally not merchant-themed).
*
* The exhaustive switch is the fail-fast guarantee: TypeScript rejects this
* file if a non-customizable token in the manifest is missing a case here.
*/
function aliasFor(token) {
	switch (token) {
		case "card": return "var(--color-muted)";
		case "card-foreground": return "var(--color-muted-foreground)";
		case "popover": return "var(--color-background)";
		case "popover-foreground": return "var(--color-foreground)";
		case "border": return "color-mix(in oklch, var(--color-background), var(--color-foreground) 15%)";
		case "input": return "var(--color-border)";
		case "ring": return "var(--color-primary)";
		case "chart-1":
		case "chart-2":
		case "chart-3":
		case "chart-4":
		case "chart-5": return null;
		case "sidebar": return "var(--color-muted)";
		case "sidebar-foreground": return "var(--color-muted-foreground)";
		case "sidebar-primary": return "var(--color-primary)";
		case "sidebar-primary-foreground": return "var(--color-primary-foreground)";
		case "sidebar-accent": return "var(--color-accent)";
		case "sidebar-accent-foreground": return "var(--color-accent-foreground)";
		case "sidebar-border": return "var(--color-border)";
		case "sidebar-ring": return "var(--color-primary)";
		case "contrast": return "var(--color-foreground)";
	}
}
/**
* Build the alias block emitted at the top of every `[data-theme]` rule.
*
* For each canonical token in `TOKEN_MANIFEST.colors`:
* - merchant-customizable tokens: `--color-{name}` is emitted by `emitColorVars`
*   from the resolved theme; this function only emits the short-name alias
*   (`--{name}: var(--color-{name})`) for compatibility with consumers that
*   reference short names.
* - non-customizable tokens: emit both `--color-{name}: <alias>` and the
*   short-name alias, unless `aliasFor` returns null. Returning null is the
*   intentional pass-through signal: ui-primitives.css already maps `--color-{name}`
*   to `var(--{name})` via `@theme inline`, so emitting `--{name}: var(--color-{name})`
*   inside `[data-theme]` would create a circular var() reference and resolve
*   to an invalid value (chart-1..5 would render transparent under any theme).
*
* Two static aliases (`--color-background-foreground`, `--color-foreground-foreground`)
* are not regular `-foreground` siblings; they are emitted explicitly because
* they swap the bg/fg pair rather than reading from a paired field.
*/
function buildAliasBlock() {
	const lines = ["--color-background-foreground: var(--color-foreground);", "--color-foreground-foreground: var(--color-background);"];
	for (const token of TOKEN_MANIFEST.colors) {
		if (MERCHANT_CUSTOMIZABLE.has(token)) {
			lines.push(`--${token}: var(--color-${token});`);
			continue;
		}
		if (EMITTED_BY_COLOR_VARS.has(token)) {
			lines.push(`--${token}: var(--color-${token});`);
			continue;
		}
		const alias = aliasFor(token);
		if (alias === null) continue;
		lines.push(`--color-${token}: ${alias};`);
		lines.push(`--${token}: var(--color-${token});`);
	}
	return lines;
}
/**
* Static alias variables emitted at the top of every theme rule.
* Derived from the canonical token manifest so new canonical tokens reach
* merchant theming automatically.
*/
const globalCSSOverride = [
	...buildAliasBlock(),
	"--radius-sm: var(--radius-small);",
	"--radius-md: var(--radius-medium);",
	"--radius-lg: var(--radius-large);",
	"--radius-xl: var(--radius-extra-large);",
	"--text-xs: var(--font-size-extra-small);",
	"--text-sm: var(--font-size-small);",
	"--text-base: var(--font-size-regular);",
	"--text-lg: var(--font-size-large);",
	"--text-xl: var(--font-size-extra-large);",
	"--text-2xl: var(--font-size-giant);"
];
/**
* Overrides for global tailwindcss for specifically dark mode.
*/
const globalDarkCSSOverride = ["--border: color-mix(in oklch, var(--color-background), var(--color-foreground) 15%);"];
/**
* Generate a complete CSS string for a resolved theme.
* Outputs 2–3 blocks: light default, dark explicit via `[data-theme-mode="dark"]`,
* and (unless `disableAutoTheme`) a `prefers-color-scheme: dark` media query block.
*/
function generateThemeCSS(theme, options = {}) {
	const sel = `[data-theme="${theme.id}"]`;
	const tw = options.mapTailwindColors ?? true;
	const blocks = [];
	blocks.push(`${sel} {`);
	blocks.push(...globalCSSOverride);
	blocks.push(...emitNonColorVars(theme));
	blocks.push(...emitColorVars(theme.light));
	if (tw) blocks.push(...emitTailwindOverrides());
	blocks.push(`}`);
	blocks.push(`${sel}[data-theme-mode="dark"] {`);
	blocks.push(...globalDarkCSSOverride);
	blocks.push(...emitColorVars(theme.dark));
	if (tw) blocks.push(...emitTailwindOverrides());
	blocks.push(`}`);
	if (!options.disableAutoTheme) {
		blocks.push(`@media (prefers-color-scheme: dark) {`);
		blocks.push(`${sel}:not([data-theme-mode]) {`);
		blocks.push(...globalDarkCSSOverride);
		blocks.push(...emitColorVars(theme.dark).map((l) => `${l}`));
		if (tw) blocks.push(...emitTailwindOverrides().map((l) => `${l}`));
		blocks.push(`}`);
		blocks.push(`}`);
	}
	return blocks.join("\n");
}
//#endregion
//#region ../../platform/theme-engine/src/defaults.ts
const DEFAULT_FONT_SIZES = {
	extraSmall: "0.75rem",
	small: "0.875rem",
	regular: "1rem",
	large: "1.125rem",
	extraLarge: "1.25rem",
	giant: "1.5rem"
};
const DEFAULT_FONT_FAMILIES = {
	header: "Inter",
	body: "Inter"
};
const DEFAULT_SPACING = "0.25rem";
const DEFAULT_RADII = {
	small: "0.25rem",
	medium: "0.5rem",
	large: "0.75rem",
	extraLarge: "1rem"
};
const DEFAULT_COLORS = {
	background: "#ffffff",
	foreground: "#1a1a1a",
	primary: "#3b82f6",
	secondary: "#6b7280",
	accent: "#10b981",
	muted: "#f3f4f6",
	destructive: "#ef4444",
	mutedForeground: "#6b7280"
};
const DEFAULT_THEME_ID = "default";
const DEFAULT_THEME_NAME = "Default Theme";
/**
* Build a fresh ThemeDefinition populated with all defaults.
* Returns a new object each call because Color instances are mutable — do not cache the result.
*/
function getDefaultThemeDefinition() {
	const bg = new Color(DEFAULT_COLORS.background);
	const fg = new Color(DEFAULT_COLORS.foreground);
	const primary = new Color(DEFAULT_COLORS.primary);
	const secondary = new Color(DEFAULT_COLORS.secondary);
	const accent = new Color(DEFAULT_COLORS.accent);
	const muted = new Color(DEFAULT_COLORS.muted);
	const destructive = new Color(DEFAULT_COLORS.destructive);
	const mutedFg = new Color(DEFAULT_COLORS.mutedForeground);
	const darkBg = new Color("#0a0a0a");
	const darkFg = new Color("#fafafa");
	const darkMuted = new Color("#171717");
	const darkMutedForeground = new Color("#dddddd");
	return {
		id: DEFAULT_THEME_ID,
		name: DEFAULT_THEME_NAME,
		light: {
			background: {
				base: bg,
				foreground: fg
			},
			foreground: {
				base: fg,
				foreground: bg
			},
			primary: {
				base: primary,
				foreground: getForegroundColor(fg, primary)
			},
			secondary: {
				base: secondary,
				foreground: getForegroundColor(fg, secondary)
			},
			accent: {
				base: accent,
				foreground: getForegroundColor(fg, accent)
			},
			muted: {
				base: muted,
				foreground: mutedFg
			},
			destructive: {
				base: destructive,
				foreground: getForegroundColor(fg, destructive)
			}
		},
		dark: {
			background: {
				base: darkBg,
				foreground: darkFg
			},
			foreground: {
				base: darkFg,
				foreground: darkBg
			},
			muted: {
				base: darkMuted,
				foreground: darkMutedForeground
			}
		},
		fontSizes: { ...DEFAULT_FONT_SIZES },
		fontFamilies: { ...DEFAULT_FONT_FAMILIES },
		spacing: DEFAULT_SPACING,
		radii: { ...DEFAULT_RADII }
	};
}
//#endregion
//#region ../../platform/theme-engine/src/serialisation.ts
function colorToPlain(color) {
	return {
		l: color.oklch.l ?? 0,
		c: color.oklch.c ?? 0,
		h: color.oklch.h ?? 0
	};
}
function plainToColor(plain) {
	return new Color("oklch", [
		plain.l,
		plain.c,
		plain.h
	]);
}
/**
* Serialise a ThemeDefinition (with Color objects) to a plain JSON payload
* suitable for backend storage.
*/
function serialiseTheme(def) {
	const light = {};
	for (const name of SEMANTIC_COLOR_NAMES) light[name] = {
		base: colorToPlain(def.light[name].base),
		foreground: colorToPlain(def.light[name].foreground)
	};
	const dark = {};
	for (const [name, value] of Object.entries(def.dark)) {
		if (!value) continue;
		dark[name] = {
			...value.base ? { base: colorToPlain(value.base) } : {},
			...value.foreground ? { foreground: colorToPlain(value.foreground) } : {}
		};
	}
	return {
		id: def.id,
		name: def.name,
		light,
		dark,
		fontSizes: { ...def.fontSizes },
		fontFamilies: { ...def.fontFamilies },
		spacing: def.spacing,
		radii: { ...def.radii },
		...def.syncWithBrandColors ? { syncWithBrandColors: true } : {}
	};
}
/**
* Deserialise a backend payload into a ThemeDefinition with Color objects.
* Accepts `Record<string, unknown>` because API data is untyped at the boundary.
* Falls back to default colors for any missing light-mode entries.
*/
function deserialiseTheme(payload) {
	const lightRaw = payload.light ?? {};
	const darkRaw = payload.dark ?? {};
	const defaults = getDefaultThemeDefinition();
	const light = {};
	for (const name of SEMANTIC_COLOR_NAMES) {
		const entry = lightRaw[name];
		if (entry) light[name] = {
			base: plainToColor(entry.base),
			foreground: plainToColor(entry.foreground)
		};
		else {
			console.warn(`[theme] deserialiseTheme: missing light color "${name}", using default`);
			light[name] = defaults.light[name];
		}
	}
	const dark = {};
	for (const [name, value] of Object.entries(darkRaw)) {
		if (!value) continue;
		dark[name] = {
			...value.base ? { base: plainToColor(value.base) } : {},
			...value.foreground ? { foreground: plainToColor(value.foreground) } : {}
		};
	}
	return {
		id: payload.id,
		name: payload.name,
		light,
		dark,
		fontSizes: payload.fontSizes ?? DEFAULT_FONT_SIZES,
		fontFamilies: payload.fontFamilies ?? DEFAULT_FONT_FAMILIES,
		spacing: payload.spacing ?? "0.25rem",
		radii: payload.radii ?? DEFAULT_RADII,
		...payload.syncWithBrandColors === true ? { syncWithBrandColors: true } : {}
	};
}
//#endregion
//#region ../../platform/theme-engine/src/transforms.ts
/**
* Check if a theme config uses the new structured format (has a `light` key
* that is an object) vs the legacy flat format.
*/
function isNewThemeFormat(config) {
	return config.light != null && typeof config.light === "object";
}
/**
* Convert a legacy flat config to a ThemeDefinition.
* Legacy format: { base: "#fff", text: "#000", primary: "oklch(0.6 0.2 250)", ... }
*/
function legacyConfigToDefinition(id, name, config) {
	const bg = parseColor(config.base ?? config.background ?? DEFAULT_COLORS.background);
	const fg = parseColor(config.text ?? config.foreground ?? DEFAULT_COLORS.foreground);
	const primary = parseColor(config.primary ?? DEFAULT_COLORS.primary);
	const secondary = parseColor(config.secondary ?? DEFAULT_COLORS.secondary);
	const accent = parseColor(config.accent ?? DEFAULT_COLORS.accent);
	const muted = parseColor(config.muted ?? DEFAULT_COLORS.muted);
	const destructive = parseColor(config.destructive ?? DEFAULT_COLORS.destructive);
	const mutedFg = parseColor(config.mutedForeground ?? DEFAULT_COLORS.mutedForeground);
	return {
		id: String(id),
		name,
		light: {
			background: {
				base: bg,
				foreground: fg
			},
			foreground: {
				base: fg,
				foreground: bg
			},
			primary: {
				base: primary,
				foreground: getForegroundColor(fg, primary)
			},
			secondary: {
				base: secondary,
				foreground: getForegroundColor(fg, secondary)
			},
			accent: {
				base: accent,
				foreground: getForegroundColor(fg, accent)
			},
			muted: {
				base: muted,
				foreground: mutedFg
			},
			destructive: {
				base: destructive,
				foreground: getForegroundColor(fg, destructive)
			}
		},
		dark: {},
		fontSizes: {
			extraSmall: config.extraSmall ?? DEFAULT_FONT_SIZES.extraSmall,
			small: config.small ?? DEFAULT_FONT_SIZES.small,
			regular: config.regular ?? DEFAULT_FONT_SIZES.regular,
			large: config.large ?? DEFAULT_FONT_SIZES.large,
			extraLarge: config.extraLarge ?? DEFAULT_FONT_SIZES.extraLarge,
			giant: config.giant ?? DEFAULT_FONT_SIZES.giant
		},
		fontFamilies: {
			header: config.headerFont ?? DEFAULT_FONT_FAMILIES.header,
			body: config.bodyFont ?? DEFAULT_FONT_FAMILIES.body
		},
		spacing: config.globalSpacing ?? "0.25rem",
		radii: {
			small: config.radiusSmall ?? DEFAULT_RADII.small,
			medium: config.radiusMedium ?? DEFAULT_RADII.medium,
			large: config.radiusLarge ?? DEFAULT_RADII.large,
			extraLarge: config.radiusExtraLarge ?? DEFAULT_RADII.extraLarge
		}
	};
}
/**
* Build a ThemeDefinition from a single API theme object.
* Handles both new structured format and legacy flat format.
*/
function buildThemeDefinition(theme) {
	const config = theme.config ?? {};
	if (isNewThemeFormat(config)) return deserialiseTheme({
		...config,
		id: String(theme.id),
		name: theme.name ?? "Untitled Theme"
	});
	return legacyConfigToDefinition(theme.id, theme.name ?? "Untitled Theme", config);
}
/**
* Transform raw API themes to ThemeDefinition[].
* Catches and logs errors per theme (graceful degradation).
*/
function transformThemes(themes) {
	return themes.flatMap((theme) => {
		try {
			return [buildThemeDefinition(theme)];
		} catch (error) {
			console.error(`[theme] Failed to build theme id=${theme.id}:`, error);
			return [];
		}
	});
}
/**
* Get the active theme ID from a list of raw API themes.
* Falls back to the first theme if none is marked active.
*/
function getActiveThemeId(themes) {
	const active = themes.find((t) => t.active) ?? themes[0];
	return active ? String(active.id) : void 0;
}
//#endregion
//#region ../../platform/theme-engine/src/theme-applicator.ts
const STYLE_PREFIX = "theme-style-";
const FONT_LINK_PREFIX = "theme-font-";
const SYSTEM_FONTS = new Set([
	"sans-serif",
	"serif",
	"monospace",
	"cursive",
	"fantasy",
	"system-ui",
	"ui-sans-serif",
	"ui-serif",
	"ui-monospace"
]);
/** Build a Google Fonts CSS2 URL for a given font family with all weights. */
function buildGoogleFontUrl(family) {
	return `https://fonts.googleapis.com/css2?family=${encodeURIComponent(family).replace(/%20/g, "+")}:wght@100;200;300;400;500;600;700;800;900&display=swap`;
}
/** Check if a font family value needs to be loaded (i.e. is not a CSS var or system font). */
function isLoadableFont(value) {
	if (!value) return false;
	if (value.startsWith("var(")) return false;
	return !SYSTEM_FONTS.has(value.toLowerCase());
}
/** Deterministic link element ID for a font family. */
function fontLinkId(family) {
	return `${FONT_LINK_PREFIX}${family.replace(/\s+/g, "-").toLowerCase()}`;
}
/**
* Inject or update `<link>` elements for Google Fonts used by the theme.
* Removes links for fonts that are no longer referenced.
*/
function loadThemeFonts(theme) {
	if (typeof document === "undefined") return;
	const fontsToLoad = /* @__PURE__ */ new Set();
	for (const key of FONT_FAMILY_KEYS) {
		const value = theme.fontFamilies[key];
		if (isLoadableFont(value)) fontsToLoad.add(value);
	}
	document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((link) => {
		const owners = link.getAttribute("data-font-theme-ids")?.split(",") ?? [];
		if (!owners.includes(theme.id)) return;
		const fontName = link.getAttribute("data-font-family");
		if (fontName && !fontsToLoad.has(fontName)) {
			const remaining = owners.filter((id) => id !== theme.id);
			if (remaining.length === 0) link.remove();
			else link.setAttribute("data-font-theme-ids", remaining.join(","));
		}
	});
	for (const family of fontsToLoad) {
		const id = fontLinkId(family);
		const existing = document.getElementById(id);
		if (existing) {
			const owners = existing.getAttribute("data-font-theme-ids")?.split(",") ?? [];
			if (!owners.includes(theme.id)) existing.setAttribute("data-font-theme-ids", [...owners, theme.id].join(","));
		} else {
			const link = document.createElement("link");
			link.id = id;
			link.rel = "stylesheet";
			link.href = buildGoogleFontUrl(family);
			link.setAttribute("data-font-family", family);
			link.setAttribute("data-font-theme-ids", theme.id);
			document.head.appendChild(link);
		}
	}
}
/** Remove all font `<link>` elements injected by the theme system. */
function removeAllFontLinks() {
	if (typeof document === "undefined") return;
	document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((el) => el.remove());
}
/**
* Inject or update a `<style>` element in `<head>` for the given theme.
* The element ID is deterministic (`theme-style-{themeId}`) so repeated calls
* for the same theme are idempotent — the existing element is updated in place.
* Also loads Google Fonts referenced by the theme's font families.
* No-op when `document` is unavailable (SSR).
*/
function applyTheme(theme, options) {
	if (typeof document === "undefined") return;
	try {
		loadThemeFonts(theme);
		const styleId = `${STYLE_PREFIX}${theme.id}`;
		let el = document.getElementById(styleId);
		if (!el) {
			el = document.createElement("style");
			el.id = styleId;
			document.head.appendChild(el);
		}
		el.textContent = generateThemeCSS(theme, options);
	} catch (error) {
		console.error(`[theme] applyTheme failed for "${theme.id}":`, error);
	}
}
/** Remove an injected theme stylesheet and clean up font link ownership. No-op during SSR. */
function removeTheme(themeId) {
	if (typeof document === "undefined") return;
	document.getElementById(`${STYLE_PREFIX}${themeId}`)?.remove();
	document.querySelectorAll(`link[id^="${FONT_LINK_PREFIX}"]`).forEach((link) => {
		const remaining = (link.getAttribute("data-font-theme-ids")?.split(",") ?? []).filter((id) => id !== themeId);
		if (remaining.length === 0) link.remove();
		else link.setAttribute("data-font-theme-ids", remaining.join(","));
	});
}
/** Remove all injected theme stylesheets and font links. No-op during SSR. */
function removeAllThemes() {
	if (typeof document === "undefined") return;
	document.querySelectorAll(`style[id^="${STYLE_PREFIX}"]`).forEach((el) => el.remove());
	removeAllFontLinks();
}
//#endregion
//#region ../core/src/widget-utils/utils.ts
function createWidgetRegistry(registry, plugins) {
	if (!plugins || plugins.length === 0) return registry;
	const pluginEntries = Object.fromEntries(plugins.map((p) => [p.type, p.component]));
	return {
		...registry,
		...pluginEntries
	};
}
function createScreen(registry, widgets) {
	widgets.forEach((widget) => {
		if (!(widget.type in registry)) throw new Error(`Widget type "${String(widget.type)}" not found in registry`);
	});
	return widgets;
}
function createWidgetFromShareable(item) {
	if ((item.kind === "video" || !!item.videoUrl) && item.videoUrl) return {
		type: "VideoWidget",
		props: {
			src: item.videoUrl,
			poster: item.imageUrl,
			caption: item.title
		}
	};
	return {
		type: "ImageWidget",
		props: {
			src: item.imageUrl,
			alt: item.title || "Image",
			objectFit: "cover"
		}
	};
}
//#endregion
//#region ../widgets/src/widgets/index.ts
const widgetPropertySchemas = {
	AlertWidget: () => import("./AlertWidget-CdWJXZn1.mjs").then((n) => n.n).then((m) => m.alertWidgetPropertySchema),
	BulletListWidget: () => import("./BulletListWidget-DZrlNEa4.mjs").then((n) => n.n).then((m) => m.bulletListWidgetPropertySchema),
	CalendarWidget: () => import("./CalendarWidget-BvXf1NlC.mjs").then((n) => n.n).then((m) => m.calendarWidgetPropertySchema),
	CardWidget: () => import("./CardWidget-Bs0hqVjy.mjs").then((n) => n.n).then((m) => m.cardWidgetPropertySchema),
	CarouselWidget: () => import("./CarouselWidget-BlS08Aru.mjs").then((n) => n.n).then((m) => m.carouselWidgetPropertySchema),
	CatchUpWidget: () => import("./CatchUpWidget-dROZKeGW.mjs").then((n) => n.n).then((m) => m.catchUpWidgetPropertySchema),
	ChartWidget: () => import("./ChartWidget-LcbtV1uD.mjs").then((n) => n.n).then((m) => m.chartWidgetPropertySchema),
	ContainerWidget: () => import("./ContainerWidget-bbzSrNUJ.mjs").then((n) => n.n).then((m) => m.containerWidgetPropertySchema),
	EmbedWidget: () => import("./EmbedWidget-DfhOZVCm.mjs").then((n) => n.n).then((m) => m.embedWidgetPropertySchema),
	ImageWidget: () => import("./ImageWidget-B9gDlIyO.mjs").then((n) => n.n).then((m) => m.imageWidgetPropertySchema),
	LayoutWidget: () => import("./LayoutWidget-ENLd1G__.mjs").then((n) => n.n).then((m) => m.layoutWidgetPropertySchema),
	LinkWidget: () => import("./LinkWidget-BNZzqXbl.mjs").then((n) => n.n).then((m) => m.linkWidgetPropertySchema),
	ListWidget: () => import("./ListWidget-ChaESAly.mjs").then((n) => n.n).then((m) => m.listWidgetPropertySchema),
	MySiteWidget: () => import("./MySiteWidget-CIFkSvEk.mjs").then((n) => n.n).then((m) => m.mySiteWidgetPropertySchema),
	NestedWidget: () => import("./NestedWidget-CShcSb_k.mjs").then((n) => n.n).then((m) => m.nestedWidgetPropertySchema),
	PointsWidget: () => import("./PointsWidget-7OpNqRDe.mjs").then((n) => n.n).then((m) => m.pointsWidgetPropertySchema),
	QuickLinksWidget: () => import("./QuickLinksWidget-o7VeOJix.mjs").then((n) => n.n).then((m) => m.quickLinksWidgetPropertySchema),
	QuickShareWidget: () => import("./QuickShareWidget-ujuaFstp.mjs").then((n) => n.n).then((m) => m.quickShareWidgetPropertySchema),
	RecentActivityWidget: () => import("./RecentActivityWidget-DUHVpA_j.mjs").then((n) => n.n).then((m) => m.recentActivityWidgetPropertySchema),
	SeparatorWidget: () => import("./SeparatorWidget-Cqzqs_G_.mjs").then((n) => n.n).then((m) => m.separatorWidgetPropertySchema),
	ShopWidget: () => import("./ShopWidget-CwiD88Sr.mjs").then((n) => n.n).then((m) => m.shopWidgetPropertySchema),
	SpacerWidget: () => import("./SpacerWidget-BJxZMWLm.mjs").then((n) => n.n).then((m) => m.spacerWidgetPropertySchema),
	TableWidget: () => import("./TableWidget-CVN4Km00.mjs").then((n) => n.n).then((m) => m.tableWidgetPropertySchema),
	TextWidget: () => import("./TextWidget-B-2RFD_g.mjs").then((n) => n.n).then((m) => m.textWidgetPropertySchema),
	ToDoWidget: () => import("./ToDoWidget-DtM9JWAS.mjs").then((n) => n.n).then((m) => m.toDoWidgetPropertySchema),
	VideoWidget: () => import("./VideoWidget-Cv7ErMg9.mjs").then((n) => n.n).then((m) => m.videoWidgetPropertySchema)
};
//#endregion
//#region src/core/default-widget-registry.ts
const DEFAULT_SDK_WIDGET_REGISTRY = createWidgetRegistry({
	AlertWidget,
	BulletListWidget,
	CalendarWidget,
	CardWidget,
	CarouselWidget,
	ChartWidget,
	ContainerWidget,
	EmbedWidget,
	ImageWidget,
	LayoutWidget,
	LinkWidget,
	ListWidget,
	MySiteWidget,
	NestedWidget,
	PointsWidget,
	QuickLinksWidget,
	QuickShareWidget,
	RecentActivityWidget,
	SeparatorWidget,
	ShopWidget,
	SpacerWidget,
	TableWidget,
	TextWidget,
	ToDoWidget,
	VideoWidget
});
const REMOTE_DOM_WIDGET_RUNTIME = "remote-dom";
const PACKAGE_FIELDS = new Set([
	"manifestVersion",
	"runtime",
	"packageId",
	"source",
	"version",
	"workerEntryUrl",
	"cssUrls",
	"assetUrls",
	"widgets"
]);
const WIDGET_FIELDS = new Set([
	"type",
	"displayName",
	"description",
	"icon",
	"category",
	"propertySchema",
	"defaultProps",
	"container",
	"resizable",
	"minSdkVersion",
	"capabilities"
]);
const CAPABILITY_FIELDS = new Set(["name", "version"]);
const UNSAFE_JSON_KEYS = new Set([
	"__proto__",
	"constructor",
	"prototype"
]);
function isRemoteDomWidgetPackageDescriptor(value) {
	return getRemoteDomWidgetPackageInvalidReason(value) === void 0;
}
function getRemoteDomWidgetPackageInvalidReason(value) {
	if (!isRecord$1(value)) return "descriptor must be an object";
	const unexpectedFieldReason = getUnexpectedFieldReason(value, PACKAGE_FIELDS, "descriptor");
	if (unexpectedFieldReason) return unexpectedFieldReason;
	if (value.manifestVersion !== 1) return `manifestVersion must be 1`;
	if (value.runtime !== "remote-dom") return `runtime must be ${REMOTE_DOM_WIDGET_RUNTIME}`;
	const packageIdReason = getNonEmptyStringReason(value.packageId, "packageId");
	if (packageIdReason) return packageIdReason;
	if (value.source !== "company" && value.source !== "droplet") return "source must be company or droplet";
	const versionReason = getNonEmptyStringReason(value.version, "version");
	if (versionReason) return versionReason;
	const workerEntryUrlReason = getHttpUrlReason(value.workerEntryUrl, "workerEntryUrl");
	if (workerEntryUrlReason) return workerEntryUrlReason;
	const cssUrlsReason = getUrlArrayReason(value.cssUrls, "cssUrls");
	if (cssUrlsReason) return cssUrlsReason;
	const assetUrlsReason = getUrlArrayReason(value.assetUrls, "assetUrls");
	if (assetUrlsReason) return assetUrlsReason;
	if (!Array.isArray(value.widgets) || value.widgets.length === 0) return "widgets must be a non-empty array";
	const widgetTypes = /* @__PURE__ */ new Set();
	for (let index = 0; index < value.widgets.length; index += 1) {
		const widget = value.widgets[index];
		const widgetReason = getWidgetReason(widget, `widgets[${index}]`);
		if (widgetReason) return widgetReason;
		const widgetType = widget.type;
		if (widgetTypes.has(widgetType)) return `widgets contains duplicate type ${widgetType}`;
		widgetTypes.add(widgetType);
	}
}
function getWidgetReason(value, path) {
	if (!isRecord$1(value)) return `${path} must be an object`;
	const unexpectedFieldReason = getUnexpectedFieldReason(value, WIDGET_FIELDS, path);
	if (unexpectedFieldReason) return unexpectedFieldReason;
	for (const field of [
		"type",
		"displayName",
		"description",
		"icon",
		"category",
		"minSdkVersion"
	]) {
		const reason = getNonEmptyStringReason(value[field], `${path}.${field}`);
		if (reason) return reason;
	}
	if (!isRecord$1(value.propertySchema) || !isJsonValue(value.propertySchema)) return `${path}.propertySchema must be a JSON object`;
	if (!isRecord$1(value.defaultProps) || !isJsonValue(value.defaultProps)) return `${path}.defaultProps must be a JSON object`;
	if (value.container !== "inline" && value.container !== "block" && value.container !== "card" && value.container !== "fullscreen") return `${path}.container is unsupported`;
	if (typeof value.resizable !== "boolean" && value.resizable !== "horizontal" && value.resizable !== "vertical" && value.resizable !== "both") return `${path}.resizable is unsupported`;
	if (!Array.isArray(value.capabilities)) return `${path}.capabilities must be an array`;
	const capabilityNames = /* @__PURE__ */ new Set();
	for (let index = 0; index < value.capabilities.length; index += 1) {
		const capability = value.capabilities[index];
		const capabilityPath = `${path}.capabilities[${index}]`;
		if (!isRecord$1(capability)) return `${capabilityPath} must be an object`;
		const unexpectedFieldReason = getUnexpectedFieldReason(capability, CAPABILITY_FIELDS, capabilityPath);
		if (unexpectedFieldReason) return unexpectedFieldReason;
		const capabilityName = capability.name;
		const nameReason = getNonEmptyStringReason(capabilityName, `${capabilityPath}.name`);
		if (nameReason) return nameReason;
		if (typeof capabilityName !== "string") return `${capabilityPath}.name must be a non-empty string`;
		const versionReason = getNonEmptyStringReason(capability.version, `${capabilityPath}.version`);
		if (versionReason) return versionReason;
		if (capabilityNames.has(capabilityName)) return `${path}.capabilities contains duplicate name ${capabilityName}`;
		capabilityNames.add(capabilityName);
	}
}
function getUrlArrayReason(value, path) {
	if (!Array.isArray(value)) return `${path} must be an array`;
	for (let index = 0; index < value.length; index += 1) {
		const reason = getHttpUrlReason(value[index], `${path}[${index}]`);
		if (reason) return reason;
	}
}
function getHttpUrlReason(value, path) {
	const stringReason = getNonEmptyStringReason(value, path);
	if (stringReason) return stringReason;
	if (typeof value !== "string") return `${path} must be a non-empty string`;
	try {
		const url = new URL(value);
		if (url.protocol !== "http:" && url.protocol !== "https:") return `${path} must use http or https`;
		if (url.username || url.password) return `${path} must not contain credentials`;
	} catch {
		return `${path} must be an absolute URL`;
	}
}
function getNonEmptyStringReason(value, path) {
	if (typeof value !== "string" || value.trim().length === 0) return `${path} must be a non-empty string`;
	if (value !== value.trim()) return `${path} must not have surrounding whitespace`;
	if (/\p{Cc}/u.test(value)) return `${path} must not contain control characters`;
}
function isJsonValue(value) {
	return isJsonValueAtDepth(value, /* @__PURE__ */ new WeakSet(), 0);
}
function isJsonValueAtDepth(value, ancestors, depth) {
	if (value === null || typeof value === "string" || typeof value === "boolean") return true;
	if (typeof value === "number") return Number.isFinite(value);
	if (typeof value !== "object" || depth > 50 || ancestors.has(value)) return false;
	ancestors.add(value);
	const prototype = Object.getPrototypeOf(value);
	const isJson = Array.isArray(value) ? value.every((item) => isJsonValueAtDepth(item, ancestors, depth + 1)) : (prototype === Object.prototype || prototype === null) && isRecord$1(value) && Object.entries(value).every(([key, item]) => !UNSAFE_JSON_KEYS.has(key) && isJsonValueAtDepth(item, ancestors, depth + 1));
	ancestors.delete(value);
	return isJson;
}
function isRecord$1(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
function getUnexpectedFieldReason(value, allowedFields, path) {
	const unexpectedField = Object.keys(value).find((field) => !allowedFields.has(field));
	return unexpectedField ? `${path} contains unsupported field ${unexpectedField}` : void 0;
}
//#endregion
//#region src/hooks/query-keys.ts
/**
* Company-scoped query key factory for TanStack Query.
*
* In portal-tenant, each tenant is a single company, so query keys are
* returned unscoped. The `createCompanyQueryKey` utility is preserved for
* potential future multi-company support.
*/
/**
* Create a company-scoped query key by prepending ["company", companyId].
*
* @param companyId - The company ID
* @param baseKey - The base query key segments (e.g. ["fluid", "profile"])
* @returns A tuple like ["company", 42, "fluid", "profile"]
*/
function createCompanyQueryKey(companyId, ...baseKey) {
	return [
		"company",
		companyId,
		...baseKey
	];
}
/**
* Hook that returns a `scopeKey` function.
*
* In portal-tenant, each tenant is a single company, so keys are
* returned unscoped (no company prefix).
*/
function useCompanyScopedQueryKey() {
	return {
		companyId: void 0,
		scopeKey: useCallback((baseKey) => {
			return baseKey;
		}, [])
	};
}
//#endregion
//#region src/transforms/screen-transforms.ts
/**
* Normalize component_tree to always be an array.
* The API stores component_tree as a hash (object), but the frontend expects an array.
*/
function normalizeComponentTree(componentTree) {
	if (!componentTree) return [];
	if (Array.isArray(componentTree)) return componentTree;
	if (typeof componentTree === "object") return [componentTree];
	return [];
}
/**
* Convert a raw FluidOS screen to ScreenDefinition.
* Normalizes component_tree and converts string IDs to numbers.
*/
function toScreenDefinition(screen) {
	return {
		id: Number(screen.id),
		slug: screen.slug ?? "",
		name: screen.name ?? "",
		component_tree: normalizeComponentTree(screen.component_tree)
	};
}
//#endregion
//#region src/transforms/navigation-transforms.ts
/**
* Convert a raw FluidOS navigation item to NavigationItem.
* Recursively transforms children and sorts by position.
*/
function toNavigationItem(item) {
	const children = (item.children ?? []).map(toNavigationItem).sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
	return {
		id: Number(item.id),
		label: item.label ?? "Untitled",
		...item.slug != null ? { slug: String(item.slug) } : {},
		...item.icon != null ? { icon: String(item.icon) } : {},
		...item.screen_id != null ? { screen_id: Number(item.screen_id) } : {},
		...item.parent_id != null ? { parent_id: Number(item.parent_id) } : {},
		...item.source != null ? { source: item.source } : {},
		position: item.position ?? 0,
		children
	};
}
//#endregion
//#region src/transforms/index.ts
/**
* Convert an `AppManifestResponse` into the `RawManifestResponse` shape
* expected by the transform pipeline. `AppManifestResponse` is structurally
* assignable to `RawManifestResponse`, so this is a runtime-only guard that
* rejects empty payloads.
*/
function toRawManifest(response) {
	if (!response.manifest) throw new Error("Portal BFF returned empty manifest");
	return response;
}
/**
* Transform a raw FluidOS manifest API response into RepAppData.
*
* Handles:
* - Theme transformation (legacy and new formats)
* - Screen normalization (component_tree array wrapping)
* - Navigation item transformation (recursive with position sorting)
*/
function transformManifestToRepAppData(response) {
	const manifest = response.manifest;
	const rawProfile = manifest.profile;
	const rawThemes = Array.isArray(rawProfile?.themes) ? rawProfile.themes : [];
	const screens = (manifest.screens ?? []).map((screen) => toScreenDefinition(screen));
	const navigationItems = (rawProfile?.navigation?.navigation_items ?? []).map(toNavigationItem);
	const nav = rawProfile?.navigation;
	const mobileNav = rawProfile?.mobile_navigation;
	const mobileNavigationItems = (mobileNav?.navigation_items ?? []).map(toNavigationItem);
	const activeThemeId = getActiveThemeId(rawThemes);
	const definitionId = manifest.definition_id ?? 0;
	return {
		definition_id: definitionId,
		published_version: manifest.published_version ?? 0,
		screens,
		profile: {
			name: rawProfile?.name ?? "Default",
			definition_id: rawProfile?.definition_id ?? definitionId,
			themes: transformThemes(rawThemes),
			...activeThemeId !== void 0 ? { activeThemeId } : {},
			navigation: {
				definition_id: nav?.definition_id ?? definitionId,
				id: nav?.id ?? 0,
				name: nav?.name ?? "Main Navigation",
				navigation_items: navigationItems,
				screens
			},
			...mobileNav ? { mobile_navigation: {
				definition_id: mobileNav.definition_id ?? definitionId,
				id: mobileNav.id ?? 0,
				name: mobileNav.name ?? "Mobile Navigation",
				navigation_items: mobileNavigationItems,
				screens
			} } : {}
		}
	};
}
//#endregion
//#region src/hooks/use-fluid-app.ts
/**
* Base query key for full app data (rendered manifest endpoint).
* Kept for backwards compatibility — the runtime key used by the hook
* includes a company prefix via {@link useCompanyScopedQueryKey}.
*/
const APP_DATA_QUERY_KEY = ["fluid", "app"];
/**
* Fetch the raw rendered manifest via the portal-tenant BFF adapter.
* Shared by `useFluidApp` and `useFluidProfile` to avoid drift.
*/
async function fetchRawManifest(api) {
	return toRawManifest(await api.fetchManifest());
}
const isViteDevelopmentBuild = import.meta.env?.DEV === true;
/**
* Module-level persister instance (browser only).
* Created once when the module loads so all calls to useFluidApp share
* the same IndexedDB connection. Applied at the query level so it works
* regardless of which QueryClient the app provides.
*/
const appDataPersister = typeof window !== "undefined" && !isViteDevelopmentBuild ? createPersister() : void 0;
/**
* Hook to fetch the full portal app data from the rendered-manifest endpoint.
*
* Returns a `RepAppData` object containing:
* - `screens` — all screen definitions with normalized component trees
* - `profile.themes` — fully-transformed ThemeDefinition[] (handles legacy + new formats)
* - `profile.activeThemeId` — the currently active theme ID
* - `profile.navigation.navigation_items` — sorted, recursive navigation tree
*
* Uses IndexedDB persistence so subsequent page loads hydrate instantly
* from cache while revalidating in the background. The raw API response
* (plain JSON) is cached; Color objects are recreated from cache via
* `select` on every restore — this is fast (CPU only, no network).
*
* @example
* ```tsx
* function App() {
*   const { data: appData, isLoading } = useFluidApp();
*
*   if (isLoading) return <Spinner />;
*
*   return (
*     <AppShell
*       appData={appData}
*       navigation={appData.profile.navigation.navigation_items}
*     />
*   );
* }
* ```
*/
function useFluidApp(options) {
	const appDefinitionApi = useAppDefinitionApi();
	const { scopeKey } = useCompanyScopedQueryKey();
	return useQuery({
		queryKey: scopeKey(APP_DATA_QUERY_KEY),
		queryFn: () => fetchRawManifest(appDefinitionApi),
		select: transformManifestToRepAppData,
		...appDataPersister && { persister: appDataPersister.persisterFn },
		...options?.enabled !== void 0 && { enabled: options.enabled }
	});
}
//#endregion
//#region src/widgets/remote-dom-built-in-capability-host.ts
const BUILT_IN_CAPABILITY_BY_NAME = {
	account: "account",
	store: "store",
	fluidApp: "fluidApp",
	fluidProfile: "fluidProfile",
	appNavigation: "appNavigation",
	tenantClient: "tenantClient",
	fullscreen: "fullscreen"
};
const MAX_SUMMARY_ENTRIES = 20;
const MAX_NAVIGATION_TARGET_LENGTH = 2048;
const FALLBACK_NAVIGATION_ORIGIN = "https://portal-sdk.remote-dom.invalid";
const ABSOLUTE_URL_SCHEME_PATTERN = /^[a-z][a-z\d+.-]*:/iu;
const PERCENT_ENCODED_C0_OR_DEL_PATTERN = /%(?:[01][0-9A-F]|7F)/iu;
function isPortalRemoteDomBuiltInCapability(capability) {
	return Object.prototype.hasOwnProperty.call(BUILT_IN_CAPABILITY_BY_NAME, capability);
}
function assertPortalRemoteDomBuiltInCapabilityVersion(capability, version) {
	if (!isPortalRemoteDomBuiltInCapability(capability) || version === "1") return;
	throwPortalFunctionHostError("UNSUPPORTED", `Portal SDK capability "${capability}" does not support version "${version}"; expected version "1".`);
}
function usePortalRemoteDomBuiltInCapabilityHost(appNavigation) {
	const account = useAccount();
	const store = useStore();
	const fluidApp = useFluidApp();
	const stateRef = useRef({
		account,
		store,
		fluidApp,
		appNavigation
	});
	stateRef.current = {
		account,
		store,
		fluidApp,
		appNavigation
	};
	return useMemo(() => createPortalRemoteDomBuiltInCapabilityHost(() => stateRef.current), []);
}
function createPortalRemoteDomBuiltInCapabilityHost(readState) {
	return async (request) => {
		if (!isPortalRemoteDomBuiltInCapability(request.capability)) return { handled: false };
		const capability = BUILT_IN_CAPABILITY_BY_NAME[request.capability];
		if (!capability) throw new Error(`Portal SDK capability "${request.capability}" has no built-in host mapping.`);
		assertPortalRemoteDomBuiltInCapabilityVersion(request.capability, request.capabilityVersion);
		const state = readState();
		switch (capability) {
			case "account":
				assertGetMethod(request);
				return handled(summarizeAccount(await resolveQueryData(state.account)));
			case "store":
				assertGetMethod(request);
				return handled(summarizeStore(await resolveQueryData(state.store)));
			case "fluidApp":
				assertGetMethod(request);
				return handled(summarizeFluidApp(await resolveQueryData(state.fluidApp)));
			case "fluidProfile":
				assertGetMethod(request);
				return handled(summarizeFluidProfile((await resolveQueryData(state.fluidApp)).profile));
			case "appNavigation": return handled(dispatchNavigation(request, state.appNavigation));
			case "tenantClient": throwPortalFunctionHostError("UNSUPPORTED", "Portal tenant client methods are not available to Remote DOM widgets. Authenticated fetch is intentionally not exposed through the capability bridge.");
			case "fullscreen": throwPortalFunctionHostError("HOST_FAILURE", "The \"fullscreen\" capability must be handled through the dedicated fullscreen host boundary.");
		}
	};
}
function handled(value) {
	return {
		handled: true,
		value
	};
}
function assertGetMethod(request) {
	if (request.method === "get") return;
	throwUnsupportedMethod(request);
}
async function resolveQueryData(query) {
	if (query.data !== void 0) return query.data;
	let result;
	try {
		result = await query.refetch();
	} catch {
		throwPortalFunctionHostError("UNAVAILABLE", "Portal SDK capability data is currently unavailable.");
	}
	if (result.data !== void 0) return result.data;
	throwPortalFunctionHostError("UNAVAILABLE", "Portal SDK capability data is currently unavailable.");
}
function summarizeAccount(value) {
	return {
		id: value.id,
		publicId: value.public_id,
		memberType: value.member_type,
		firstName: value.first_name,
		lastName: value.last_name,
		displayName: [value.first_name, value.last_name].filter(Boolean).join(" "),
		email: value.email,
		bio: value.bio,
		avatarUrl: value.avatar_url,
		slug: value.slug,
		socialLinks: value.social_links,
		defaultCountryIso: value.default_country_iso,
		marketCountryIso: value.market_country_iso
	};
}
function summarizeStore(value) {
	return {
		id: value.id,
		name: value.name,
		subdomain: value.subdomain,
		logoUrl: value.logo_url,
		iconUrl: value.icon_url,
		appStoreUrl: value.appstore_url,
		playStoreUrl: value.playstore_url,
		bundleSubscriptionsEnabled: value.bundle_subscriptions_enabled,
		rewardPointsLabelSingular: value.reward_points_label_singular,
		rewardPointsLabelPlural: value.reward_points_label_plural
	};
}
function summarizeFluidApp(value) {
	return {
		definitionId: value.definition_id ?? null,
		publishedVersion: value.published_version ?? null,
		profile: summarizeFluidProfile(value.profile),
		screens: summarizeCollection(value.screens, summarizeScreen)
	};
}
function summarizeFluidProfile(value) {
	return {
		name: value.name,
		definitionId: value.definition_id,
		activeThemeId: value.activeThemeId ?? null,
		themes: summarizeCollection(value.themes, summarizeNamedEntity),
		navigation: summarizeNavigation(value.navigation),
		mobileNavigation: summarizeNavigation(value.mobile_navigation)
	};
}
function summarizeCollection(value, summarizeItem) {
	if (!Array.isArray(value)) return {
		count: 0,
		items: []
	};
	return {
		count: value.length,
		items: value.slice(0, MAX_SUMMARY_ENTRIES).map(summarizeItem)
	};
}
function summarizeNamedEntity(value) {
	if (!isRecord(value)) return null;
	return {
		id: toNullableEntityId(value.id),
		definitionId: toNullableEntityId(value.definition_id),
		name: typeof value.name === "string" ? value.name : null,
		slug: typeof value.slug === "string" ? value.slug : null
	};
}
function summarizeScreen(value) {
	if (!isRecord(value)) return null;
	return {
		id: toNullableEntityId(value.id),
		definitionId: toNullableEntityId(value.definition_id),
		name: typeof value.name === "string" ? value.name : null,
		slug: typeof value.slug === "string" ? value.slug : null,
		componentCount: Array.isArray(value.components) ? value.components.length : Array.isArray(value.component_tree) ? value.component_tree.length : 0
	};
}
function summarizeNavigation(value) {
	if (!isRecord(value)) return null;
	return {
		id: toNullableEntityId(value.id),
		definitionId: toNullableEntityId(value.definition_id),
		name: typeof value.name === "string" ? value.name : null,
		navigationItemCount: Array.isArray(value.navigation_items) ? value.navigation_items.length : 0,
		screenCount: Array.isArray(value.screens) ? value.screens.length : 0
	};
}
function dispatchNavigation(request, navigation) {
	if (!navigation) throwPortalFunctionHostError("UNAVAILABLE", "Portal SDK capability \"appNavigation\" is unavailable outside an AppNavigationProvider.");
	switch (request.method) {
		case "get": return {
			currentSlug: navigation.currentSlug,
			previousSlug: navigation.previousSlug,
			basePath: navigation.basePath,
			navItems: navigation.navItems.map(summarizeNavigationItem)
		};
		case "buildHref": {
			const slug = readNavigationTarget(request, navigation.basePath);
			return navigation.buildHref(slug);
		}
		case "navigate": {
			const slug = readNavigationTarget(request, navigation.basePath);
			navigation.navigate(slug);
			return null;
		}
		default: throwUnsupportedMethod(request);
	}
}
function readNavigationTarget(request, basePath) {
	if (typeof request.payload === "string") return normalizeNavigationSlug(request.payload, request);
	if (!isRecord(request.payload)) throwInvalidNavigationPayload(request, "requires an object payload with exactly one string slug or href field.");
	const slug = typeof request.payload.slug === "string" ? request.payload.slug : void 0;
	const href = typeof request.payload.href === "string" ? request.payload.href : void 0;
	if (slug === void 0 === (href === void 0)) throwInvalidNavigationPayload(request, "requires exactly one string slug or href field.");
	if (slug !== void 0) return normalizeNavigationSlug(slug, request);
	if (href !== void 0) return normalizeNavigationHref(href, basePath, request);
	throwInvalidNavigationPayload(request, "requires exactly one string slug or href field.");
}
function normalizeNavigationSlug(slug, request) {
	validateNavigationText(slug, "slug", request);
	if (slug.startsWith("/") || slug.startsWith("//") || ABSOLUTE_URL_SCHEME_PATTERN.test(slug)) throwInvalidNavigationPayload(request, "slug must be a relative app route, not an absolute URL or root path.");
	assertSafeNavigationSegments(slug, request);
	let parsed;
	try {
		parsed = new URL(slug, navigationBaseUrl());
	} catch {
		throwInvalidNavigationPayload(request, "slug is not a valid URL path.");
	}
	if (parsed.origin !== navigationBaseUrl().origin) throwInvalidNavigationPayload(request, "slug must remain within the app.");
	const normalized = `${parsed.pathname.replace(/^\/+/, "")}${parsed.search}${parsed.hash}`;
	if (normalized === "" || /^[?#]/u.test(normalized)) throwInvalidNavigationPayload(request, "slug must include an app route.");
	return normalized;
}
function normalizeNavigationHref(href, basePath, request) {
	validateNavigationText(href, "href", request);
	let parsed;
	try {
		parsed = new URL(href, navigationBaseUrl());
	} catch {
		throwInvalidNavigationPayload(request, "href is not a valid URL.");
	}
	if (parsed.origin !== navigationBaseUrl().origin || parsed.username !== "" || parsed.password !== "") throwInvalidNavigationPayload(request, "href must be same-origin and must not include credentials.");
	const normalizedBasePath = normalizeBasePath(basePath);
	return normalizeNavigationSlug(`${normalizedBasePath === "/" ? parsed.pathname.replace(/^\/+/, "") : readSlugFromBasePath(parsed.pathname, normalizedBasePath, request)}${parsed.search}${parsed.hash}`, request);
}
function validateNavigationText(value, field, request) {
	if (value.length === 0) throwInvalidNavigationPayload(request, `${field} must not be empty.`);
	if (value.length > MAX_NAVIGATION_TARGET_LENGTH) throwInvalidNavigationPayload(request, `${field} exceeds the ${MAX_NAVIGATION_TARGET_LENGTH} character limit.`);
	if (containsControlCharacter(value) || PERCENT_ENCODED_C0_OR_DEL_PATTERN.test(value) || value.includes("\\")) throwInvalidNavigationPayload(request, `${field} contains unsupported characters.`);
}
function assertSafeNavigationSegments(value, request) {
	const path = value.split(/[?#]/u, 1)[0] ?? "";
	for (const segment of path.split("/")) {
		let decoded;
		try {
			decoded = decodeURIComponent(segment);
		} catch {
			throwInvalidNavigationPayload(request, "navigation path contains malformed percent encoding.");
		}
		if (decoded === "." || decoded === ".." || decoded.includes("/") || decoded.includes("\\")) throwInvalidNavigationPayload(request, "navigation path contains an unsafe segment.");
	}
}
function normalizeBasePath(basePath) {
	const trimmed = basePath.trim();
	if (trimmed === "" || trimmed === "/") return "/";
	return `/${trimmed.replace(/^\/+|\/+$/gu, "")}`;
}
function readSlugFromBasePath(pathname, basePath, request) {
	if (pathname === basePath) throwInvalidNavigationPayload(request, "href must include an app route.");
	const prefix = `${basePath}/`;
	if (!pathname.startsWith(prefix)) throwInvalidNavigationPayload(request, `href must remain within the portal base path "${basePath}".`);
	return pathname.slice(prefix.length);
}
function navigationBaseUrl() {
	return new URL(typeof window === "undefined" ? FALLBACK_NAVIGATION_ORIGIN : window.location.origin);
}
function containsControlCharacter(value) {
	for (let index = 0; index < value.length; index += 1) {
		const codeUnit = value.charCodeAt(index);
		if (codeUnit <= 31 || codeUnit === 127) return true;
	}
	return false;
}
function throwUnsupportedMethod(request) {
	throwPortalFunctionHostError("UNSUPPORTED", `Portal SDK capability "${request.capability}" does not support method "${request.method}".`);
}
function throwInvalidNavigationPayload(request, reason) {
	throwPortalFunctionHostError("INVALID_ARGUMENT", `Portal SDK capability "${request.capability}.${request.method}" ${reason}`);
}
function throwPortalFunctionHostError(code, message) {
	throw new Error(`PORTAL_FUNCTION_ERROR:${code}:${message}`);
}
function isRecord(value) {
	return typeof value === "object" && value !== null && !Array.isArray(value);
}
function toNullableEntityId(value) {
	return typeof value === "string" || typeof value === "number" ? value : null;
}
function summarizeNavigationItem(value) {
	return {
		id: value.id ?? null,
		label: value.label,
		slug: value.slug ?? null,
		icon: value.icon ?? null,
		section: value.section ?? null,
		screenId: value.screen_id ?? null,
		position: value.position ?? null,
		parentId: value.parent_id ?? null,
		source: value.source ?? null,
		children: value.children.map(summarizeNavigationItem)
	};
}
//#endregion
//#region src/widgets/screen-area-fullscreen-host.ts
const WIDGET_FRAME_SELECTOR = "[data-fluid-remote-dom-widget-frame]";
const SCREEN_BODY_SELECTOR = "[data-fluid-portal-screen-body]";
const SCREEN_AREA_SELECTOR = "[data-fluid-portal-screen-area]";
/**
* Coordinates one host-owned fullscreen session within a portal screen.
* "Fullscreen" deliberately means the screen body below ScreenHeader, never
* the browser Fullscreen API or the whole viewport.
*/
function createScreenAreaFullscreenCoordinator() {
	let activeSession = null;
	let snapshot = null;
	const listeners = /* @__PURE__ */ new Set();
	const emit = (next) => {
		snapshot = next;
		for (const listener of listeners) listener();
	};
	const endActiveSession = (focusMode) => {
		const session = activeSession;
		if (!session) return;
		activeSession = null;
		session.stopTrackingBounds();
		restoreStyleProperties(session.styleRecords);
		restoreAttributes(session.attributeRecords);
		restoreInertStates(session.inertRecords);
		session.boundary.screenArea.scrollLeft = session.scrollLeft;
		session.boundary.screenArea.scrollTop = session.scrollTop;
		emit(null);
		if (focusMode === "restore") focusSessionTarget(session);
		else if (focusMode === "defer-after-disposal") scheduleFocusAfterOwnerDisposal(session);
	};
	return {
		getSnapshot: () => snapshot,
		subscribe: (listener) => {
			listeners.add(listener);
			return () => listeners.delete(listener);
		},
		enter: (owner, displayName, host) => {
			const boundary = findFullscreenBoundary(host);
			if (!boundary) throwPortalFunctionHostError("UNSUPPORTED", "Fullscreen is not available outside a portal screen body.");
			if (activeSession?.owner === owner) {
				updateFrameBounds(boundary.frame, boundary.body);
				return;
			}
			const previousFocus = activeSession?.previousFocus ?? getDeepActiveElement(host.ownerDocument);
			endActiveSession("none");
			const inertRecords = isolateActiveBranch(boundary.frame, boundary.body);
			const attributeRecords = setSessionAttributes(boundary, displayName);
			const styleRecords = recordFrameBoundsStyles(boundary.frame);
			const scrollLeft = boundary.screenArea.scrollLeft;
			const scrollTop = boundary.screenArea.scrollTop;
			boundary.screenArea.scrollLeft = 0;
			boundary.screenArea.scrollTop = 0;
			activeSession = {
				owner,
				displayName,
				boundary,
				inertRecords,
				attributeRecords,
				styleRecords,
				previousFocus,
				scrollLeft,
				scrollTop,
				stopTrackingBounds: trackFrameBounds(boundary.frame, boundary.body)
			};
			emit({
				owner,
				displayName
			});
		},
		exit: (owner) => {
			if (owner !== void 0 && activeSession?.owner !== owner) return;
			endActiveSession("restore");
		},
		disposeOwner: (owner) => {
			if (activeSession?.owner === owner) endActiveSession("defer-after-disposal");
		},
		destroy: () => {
			endActiveSession("none");
			listeners.clear();
		},
		isActive: (owner) => activeSession?.owner === owner,
		isAvailable: (host) => findFullscreenBoundary(host) !== null
	};
}
async function handleScreenAreaRemoteDomFullscreenRequest(coordinator, owner, displayName, request, observe) {
	switch (request.method) {
		case "get":
			await observe?.(request);
			return {
				fullscreen: coordinator?.isActive(owner) ?? false,
				available: coordinator?.isAvailable(request.host) ?? false
			};
		case "request":
			if (!coordinator || !coordinator.isAvailable(request.host)) throwPortalFunctionHostError("UNSUPPORTED", "Fullscreen is not available outside a portal screen body.");
			await observe?.(request);
			coordinator.enter(owner, displayName, request.host);
			return null;
		case "exit":
			await observe?.(request);
			coordinator?.exit(owner);
			return null;
		default: throwPortalFunctionHostError("UNSUPPORTED", `Portal SDK capability "fullscreen" does not support method "${request.method}".`);
	}
}
function focusSessionTarget(session) {
	(session.previousFocus?.isConnected === true ? session.previousFocus : findStableFocusFallback(session.boundary))?.focus({ preventScroll: true });
}
function scheduleFocusAfterOwnerDisposal(session) {
	const document = session.boundary.frame.ownerDocument;
	const previousFocusWasInFrame = session.previousFocus ? isComposedDescendant(session.boundary.frame, session.previousFocus) : false;
	const view = document.defaultView;
	(view ? (callback) => {
		view.setTimeout(callback, 0);
	} : (callback) => queueMicrotask(callback))(() => {
		const currentFocus = getDeepActiveElement(document);
		if (currentFocus?.isConnected && currentFocus !== document.body && !isComposedDescendant(session.boundary.frame, currentFocus)) return;
		(!previousFocusWasInFrame && session.previousFocus?.isConnected ? session.previousFocus : findStableFocusFallback(session.boundary))?.focus({ preventScroll: true });
	});
}
function findStableFocusFallback(boundary) {
	return boundary.body.parentElement?.querySelector("[data-fluid-screen-header]") ?? boundary.screenArea;
}
function isComposedDescendant(ancestor, element) {
	let current = element;
	while (current) {
		if (current === ancestor) return true;
		const root = current.getRootNode();
		current = root instanceof ShadowRoot && root !== current ? root.host : current.parentNode;
	}
	return false;
}
function findFullscreenBoundary(host) {
	const frame = host.closest(WIDGET_FRAME_SELECTOR);
	const body = frame?.closest(SCREEN_BODY_SELECTOR);
	const screenArea = body?.closest(SCREEN_AREA_SELECTOR);
	return frame && body && screenArea ? {
		frame,
		body,
		screenArea
	} : null;
}
function isolateActiveBranch(frame, body) {
	const records = [];
	let current = frame;
	while (current !== body) {
		const parent = current.parentElement;
		if (!parent) break;
		for (const sibling of parent.children) {
			if (sibling === current || !(sibling instanceof HTMLElement)) continue;
			records.push({
				element: sibling,
				inert: sibling.inert
			});
			sibling.inert = true;
		}
		current = parent;
	}
	return records;
}
function restoreInertStates(records) {
	for (const { element, inert } of records) element.inert = inert;
}
function setSessionAttributes(boundary, displayName) {
	return [
		[
			boundary.frame,
			"data-fluid-remote-dom-widget-fullscreen",
			""
		],
		[
			boundary.frame,
			"role",
			"region"
		],
		[
			boundary.frame,
			"aria-label",
			displayName
		],
		[
			boundary.body,
			"data-fluid-portal-fullscreen-active",
			""
		],
		[
			boundary.screenArea,
			"data-fluid-portal-fullscreen-active",
			""
		]
	].map(([element, name, value]) => {
		const record = {
			element,
			name,
			value: element.getAttribute(name)
		};
		element.setAttribute(name, value);
		return record;
	});
}
function restoreAttributes(records) {
	for (const { element, name, value } of records) if (value === null) element.removeAttribute(name);
	else element.setAttribute(name, value);
}
const FRAME_BOUND_STYLE_PROPERTIES = [
	"--fluid-portal-fullscreen-top",
	"--fluid-portal-fullscreen-left",
	"--fluid-portal-fullscreen-width",
	"--fluid-portal-fullscreen-height"
];
function recordFrameBoundsStyles(frame) {
	return FRAME_BOUND_STYLE_PROPERTIES.map((name) => ({
		element: frame,
		name,
		value: frame.style.getPropertyValue(name),
		priority: frame.style.getPropertyPriority(name)
	}));
}
function restoreStyleProperties(records) {
	for (const { element, name, value, priority } of records) if (value) element.style.setProperty(name, value, priority);
	else element.style.removeProperty(name);
}
function trackFrameBounds(frame, body) {
	const update = () => updateFrameBounds(frame, body);
	update();
	const view = body.ownerDocument.defaultView;
	view?.addEventListener("resize", update);
	const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(update);
	observer?.observe(body);
	return () => {
		view?.removeEventListener("resize", update);
		observer?.disconnect();
	};
}
function updateFrameBounds(frame, body) {
	const bounds = body.getBoundingClientRect();
	frame.style.setProperty("--fluid-portal-fullscreen-top", `${bounds.top}px`);
	frame.style.setProperty("--fluid-portal-fullscreen-left", `${bounds.left}px`);
	frame.style.setProperty("--fluid-portal-fullscreen-width", `${bounds.width}px`);
	frame.style.setProperty("--fluid-portal-fullscreen-height", `${bounds.height}px`);
}
function getDeepActiveElement(document) {
	let active = document.activeElement;
	while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;
	return active && "focus" in active ? active : null;
}
//#endregion
//#region src/shell/ScreenAreaFullscreenContext.tsx
const ScreenAreaFullscreenContext = createContext(null);
const subscribeToNothing = () => () => void 0;
const getEmptySnapshot = () => null;
function ScreenAreaFullscreenProvider({ children }) {
	const coordinatorRef = useRef(null);
	if (!coordinatorRef.current) coordinatorRef.current = createScreenAreaFullscreenCoordinator();
	useEffect(() => {
		const coordinator = coordinatorRef.current;
		return () => coordinator?.destroy();
	}, []);
	return /* @__PURE__ */ jsx(ScreenAreaFullscreenContext.Provider, {
		value: coordinatorRef.current,
		children
	});
}
function useOptionalScreenAreaFullscreenCoordinator() {
	return use(ScreenAreaFullscreenContext);
}
function useScreenAreaFullscreenSnapshot() {
	const coordinator = useOptionalScreenAreaFullscreenCoordinator();
	return useSyncExternalStore(coordinator?.subscribe ?? subscribeToNothing, coordinator?.getSnapshot ?? getEmptySnapshot, getEmptySnapshot);
}
//#endregion
//#region ../widgets/src/remote-dom-elements.tsx
const FLUID_SPACER_WIDGET_TAG_NAME = "fluid-spacer-widget";
/** Registers the one portal-widgets element available to remote workers. */
function registerFluidSpacerWidgetElement() {
	if (typeof customElements === "undefined" || typeof HTMLElement === "undefined" || customElements.get("fluid-spacer-widget")) return;
	customElements.define(FLUID_SPACER_WIDGET_TAG_NAME, createFluidSpacerWidgetElement());
}
/** Keeps browser globals lazy so importing the registry remains safe during SSR. */
function createFluidSpacerWidgetElement() {
	return class FluidSpacerWidgetElement extends HTMLElement {
		#root;
		#customHeight;
		#previewMode = false;
		set customHeight(value) {
			this.#customHeight = typeof value === "string" ? value : void 0;
			this.#render();
		}
		get customHeight() {
			return this.#customHeight;
		}
		set previewMode(value) {
			this.#previewMode = value === true;
			this.#render();
		}
		get previewMode() {
			return this.#previewMode;
		}
		connectedCallback() {
			this.#render();
		}
		disconnectedCallback() {
			this.#root?.unmount();
			this.#root = void 0;
		}
		#render() {
			if (!this.isConnected) return;
			this.#root ??= createRoot(this);
			this.#root.render(/* @__PURE__ */ jsx(SpacerWidget, {
				...this.#customHeight === void 0 ? {} : { customHeight: this.#customHeight },
				previewMode: this.#previewMode
			}));
		}
	};
}
//#endregion
//#region src/widgets/remote-dom-elements.tsx
const FLUID_SEARCH_SORT_TAG_NAME = "fluid-ui-search-sort";
/** Registers the two explicitly supported host-owned Remote DOM elements. */
function registerRemoteDomHostElements() {
	registerFluidSpacerWidgetElement();
	if (typeof customElements === "undefined" || typeof HTMLElement === "undefined" || customElements.get("fluid-ui-search-sort")) return;
	customElements.define(FLUID_SEARCH_SORT_TAG_NAME, createFluidSearchSortElement());
}
/** Keeps browser globals lazy so descriptor projection remains safe during SSR. */
function createFluidSearchSortElement() {
	return class FluidSearchSortElement extends HTMLElement {
		#root;
		#searchValue = "";
		#placeholder;
		#sortOptions;
		#sortValue;
		set searchValue(value) {
			this.#searchValue = typeof value === "string" ? value : "";
			this.#render();
		}
		get searchValue() {
			return this.#searchValue;
		}
		set placeholder(value) {
			this.#placeholder = typeof value === "string" ? value : void 0;
			this.#render();
		}
		get placeholder() {
			return this.#placeholder;
		}
		set sortOptions(value) {
			this.#sortOptions = isSortOptions(value) ? value : void 0;
			this.#render();
		}
		get sortOptions() {
			return this.#sortOptions;
		}
		set sortValue(value) {
			this.#sortValue = typeof value === "string" ? value : void 0;
			this.#render();
		}
		get sortValue() {
			return this.#sortValue;
		}
		connectedCallback() {
			this.#render();
		}
		disconnectedCallback() {
			this.#root?.unmount();
			this.#root = void 0;
		}
		#render() {
			if (!this.isConnected) return;
			this.#root ??= createRoot(this);
			const onSearchChange = (value) => {
				this.dispatchEvent(new CustomEvent("search-change", {
					bubbles: true,
					composed: true,
					detail: value
				}));
			};
			const onSortChange = (value) => {
				this.dispatchEvent(new CustomEvent("sort-change", {
					bubbles: true,
					composed: true,
					detail: value
				}));
			};
			this.#root.render(this.#sortOptions && this.#sortValue !== void 0 ? /* @__PURE__ */ jsx(SearchSort, {
				searchValue: this.#searchValue,
				onSearchChange,
				...this.#placeholder === void 0 ? {} : { placeholder: this.#placeholder },
				sortOptions: this.#sortOptions,
				sortValue: this.#sortValue,
				onSortChange
			}) : /* @__PURE__ */ jsx(SearchSort, {
				searchValue: this.#searchValue,
				onSearchChange,
				...this.#placeholder === void 0 ? {} : { placeholder: this.#placeholder }
			}));
		}
	};
}
function isSortOptions(value) {
	return Array.isArray(value) && value.every((option) => typeof option === "object" && option !== null && "label" in option && typeof option.label === "string" && "value" in option && typeof option.value === "string");
}
//#endregion
//#region src/utils/build-widget-registry.ts
/**
* Projects canonical package descriptors into host React wrappers. The first
* accepted package/widget wins; built-ins can never be replaced.
*/
function buildRemoteWidgetRegistry(baseRegistry, packages, options = {}, warn = console.warn) {
	return projectRemoteWidgetPackages(baseRegistry, packages, options, warn).registry;
}
/** @internal Shared package-atomic projection for runtime and builder views. */
function projectRemoteWidgetPackages(baseRegistry, packages, options = {}, warn = console.warn) {
	const registry = { ...baseRegistry };
	const acceptedWidgets = [];
	const packageVersions = /* @__PURE__ */ new Set();
	const functionImplementations = indexPortalFunctionImplementations(options.functions ?? []);
	for (const remotePackage of packages) {
		const packageVersion = `${remotePackage.packageId}@${remotePackage.version}`;
		if (packageVersions.has(packageVersion)) {
			warn(`[portal-sdk] Ignoring duplicate widget package "${packageVersion}".`);
			continue;
		}
		packageVersions.add(packageVersion);
		if (remotePackage.widgets.some((widget) => isBuiltInWidgetType(widget.type) || Object.prototype.hasOwnProperty.call(registry, widget.type))) {
			warn(`[portal-sdk] Ignoring widget package "${packageVersion}" because one of its widget types is already registered.`);
			continue;
		}
		for (const widget of remotePackage.widgets) {
			const component = createRemoteWidgetComponent(remotePackage, widget, options, functionImplementations);
			registry[widget.type] = component;
			acceptedWidgets.push({
				remotePackage,
				widget,
				component
			});
		}
	}
	return {
		registry,
		acceptedWidgets
	};
}
function createRemoteWidgetComponent(remotePackage, widget, options, functionImplementations) {
	function RemoteWidget(props) {
		const hostRef = useRef(null);
		const handleRef = useRef(null);
		const fullscreenOwner = useRef(Symbol(widget.type)).current;
		const fullscreenCoordinator = useOptionalScreenAreaFullscreenCoordinator();
		const fullscreenCoordinatorRef = useRef(fullscreenCoordinator);
		fullscreenCoordinatorRef.current = fullscreenCoordinator;
		const navigation = useOptionalAppNavigation();
		const navigationRef = useRef(navigation);
		navigationRef.current = navigation;
		const builtInCapabilityHost = usePortalRemoteDomBuiltInCapabilityHost(navigation);
		const builtInCapabilityHostRef = useRef(builtInCapabilityHost);
		builtInCapabilityHostRef.current = builtInCapabilityHost;
		const [error, setError] = useState(null);
		const serialization = useMemo(() => serializeProps(props), [props]);
		const serializablePropsRef = useRef(null);
		if (serialization.ok) serializablePropsRef.current = serialization.value;
		useEffect(() => {
			registerRemoteDomHostElements();
			const host = hostRef.current;
			if (!host || !serialization.ok) return;
			let handle;
			let active = true;
			import("@fluid-app/widget-runtime/remote-dom").then(({ mountRemoteDomWidget }) => {
				if (!active) return;
				setError(null);
				handle = mountRemoteDomWidget({
					host,
					artifact: {
						packageId: remotePackage.packageId,
						version: remotePackage.version,
						widgetType: widget.type,
						workerEntryUrl: remotePackage.workerEntryUrl,
						cssUrls: remotePackage.cssUrls,
						assetUrls: remotePackage.assetUrls,
						capabilities: widget.capabilities
					},
					props: serializablePropsRef.current,
					hostOrigin: window.location.origin,
					onNavigate: (intent) => {
						if (options.onNavigate) {
							options.onNavigate(intent);
							return;
						}
						navigationRef.current?.navigate(toNavigationSlug(intent.url));
					},
					onFullscreen: async (request) => {
						const declaration = widget.capabilities.find(({ name }) => name === "fullscreen");
						if (!declaration) throwPortalFunctionHostError("NOT_DECLARED", "Widget requested undeclared capability \"fullscreen\".");
						assertPortalRemoteDomBuiltInCapabilityVersion("fullscreen", declaration.version);
						const onFullscreen = options.onFullscreen;
						const observe = onFullscreen ? async (observedRequest) => {
							try {
								if (!isJsonValue(await onFullscreen(observedRequest))) throwPortalFunctionHostError("INVALID_RESPONSE", "Host implementation returned an invalid response for portal function \"fullscreen\".");
							} catch (error) {
								if (isPortalFunctionHostError(error)) throw error;
								throwPortalFunctionHostError("HOST_FAILURE", "Host implementation failed for portal function \"fullscreen\".");
							}
						} : void 0;
						return handleScreenAreaRemoteDomFullscreenRequest(fullscreenCoordinatorRef.current, fullscreenOwner, widget.displayName, request, observe);
					},
					callCapability: async (capability, method, payload) => {
						const declaration = widget.capabilities.find(({ name }) => name === capability);
						if (!declaration) throwPortalFunctionHostError("NOT_DECLARED", `Widget requested undeclared capability "${capability}".`);
						const request = {
							packageId: remotePackage.packageId,
							packageVersion: remotePackage.version,
							widgetType: widget.type,
							capability,
							capabilityVersion: declaration.version,
							method,
							...payload === void 0 ? {} : { payload }
						};
						const builtInResult = await builtInCapabilityHostRef.current(request);
						if (builtInResult.handled) return builtInResult.value;
						const implementation = functionImplementations.get(portalFunctionKey({
							capability,
							version: declaration.version,
							method
						}));
						if (!implementation) throwPortalFunctionHostError("UNAVAILABLE", `No host implementation is configured for portal function "${capability}.${method}" version "${declaration.version}".`);
						let result;
						try {
							result = payload === void 0 ? await implementation.handler() : await implementation.handler(payload);
						} catch {
							throwPortalFunctionHostError("HOST_FAILURE", `Host implementation failed for portal function "${capability}.${method}".`);
						}
						if (!isJsonValue(result)) throwPortalFunctionHostError("INVALID_RESPONSE", `Host implementation returned an invalid response for portal function "${capability}.${method}".`);
						return result;
					},
					onError: (runtimeError) => {
						if (active) setError(runtimeError);
					}
				});
				handleRef.current = handle;
				handle.ready.catch((readyError) => {
					if (active) setError(toError(readyError));
				});
			}).catch((mountError) => {
				if (active) setError(toError(mountError));
			});
			return () => {
				active = false;
				fullscreenCoordinatorRef.current?.disposeOwner(fullscreenOwner);
				handleRef.current = null;
				handle?.dispose();
			};
		}, [fullscreenOwner, serialization.ok]);
		useEffect(() => {
			if (!handleRef.current || !serialization.ok) return;
			try {
				handleRef.current.updateProps(serialization.value);
				setError(null);
			} catch (updateError) {
				setError(toError(updateError));
			}
		}, [serialization]);
		const displayedError = serialization.ok ? error : serialization.error;
		return createElement("div", { "data-fluid-remote-dom-widget-frame": widget.type }, createElement("div", {
			ref: hostRef,
			"data-fluid-remote-dom-widget": widget.type
		}), displayedError ? createElement("p", { role: "alert" }, `Unable to render ${widget.displayName}.`) : null);
	}
	RemoteWidget.displayName = `RemoteDomWidget(${widget.type})`;
	return RemoteWidget;
}
function indexPortalFunctionImplementations(implementations) {
	const byKey = /* @__PURE__ */ new Map();
	for (const implementation of implementations) {
		const { capability, version, method } = implementation.definition;
		if (isPortalRemoteDomBuiltInCapability(capability)) throw new Error(`Portal function "${capability}.${method}" cannot replace a Portal SDK built-in capability.`);
		const key = portalFunctionKey({
			capability,
			version,
			method
		});
		if (byKey.has(key)) throw new Error(`Duplicate host implementation for portal function "${capability}.${method}" version "${version}".`);
		byKey.set(key, implementation);
	}
	return byKey;
}
function portalFunctionKey({ capability, version, method }) {
	return JSON.stringify([
		capability,
		version,
		method
	]);
}
function isPortalFunctionHostError(error) {
	return error instanceof Error && error.message.includes("PORTAL_FUNCTION_ERROR:");
}
function serializeProps(value) {
	return isJsonValue(value) ? {
		ok: true,
		value
	} : {
		ok: false,
		error: /* @__PURE__ */ new TypeError("Remote DOM widget props must contain only finite JSON values.")
	};
}
function toNavigationSlug(url) {
	const base = typeof window === "undefined" ? "https://portal.invalid" : window.location.origin;
	const parsed = new URL(url, base);
	return `${parsed.pathname.replace(/^\/+/, "")}${parsed.search}${parsed.hash}`;
}
function toError(error) {
	return error instanceof Error ? error : new Error(String(error));
}
function isBuiltInWidgetType(widgetType) {
	return Object.prototype.hasOwnProperty.call(DEFAULT_SDK_WIDGET_REGISTRY, widgetType);
}
//#endregion
export { DEFAULT_COLORS as A, getForegroundColor as B, removeAllThemes as C, transformThemes as D, getActiveThemeId as E, DEFAULT_THEME_ID as F, FONT_SIZE_KEYS as G, parseColor as H, DEFAULT_THEME_NAME as I, AppDefinitionApiProvider as J, RADIUS_KEYS as K, getDefaultThemeDefinition as L, DEFAULT_FONT_SIZES as M, DEFAULT_RADII as N, deserialiseTheme as O, DEFAULT_SPACING as P, generateThemeCSS as R, applyTheme as S, buildThemeDefinition as T, resolveTheme as U, mergeDarkOverrides as V, FONT_FAMILY_KEYS as W, createPersister as X, useAppDefinitionApi as Y, deleteDatabase as Z, DEFAULT_SDK_WIDGET_REGISTRY as _, useScreenAreaFullscreenSnapshot as a, createWidgetFromShareable as b, useFluidApp as c, toNavigationItem as d, normalizeComponentTree as f, isRemoteDomWidgetPackageDescriptor as g, useCompanyScopedQueryKey as h, useOptionalScreenAreaFullscreenCoordinator as i, DEFAULT_FONT_FAMILIES as j, serialiseTheme as k, toRawManifest as l, createCompanyQueryKey as m, projectRemoteWidgetPackages as n, APP_DATA_QUERY_KEY as o, toScreenDefinition as p, SEMANTIC_COLOR_NAMES as q, ScreenAreaFullscreenProvider as r, fetchRawManifest as s, buildRemoteWidgetRegistry as t, transformManifestToRepAppData as u, widgetPropertySchemas as v, removeTheme as w, createWidgetRegistry as x, createScreen as y, deriveDarkVariant as z };

//# sourceMappingURL=build-widget-registry-CO8Mowhe.mjs.map