import "node:module";
import fs from "node:fs";
import path from "node:path";
import { randomBytes, timingSafeEqual } from "node:crypto";
//#region \0rolldown/runtime.js
var __defProp = Object.defineProperty;
var __exportAll = (all, no_symbols) => {
	let target = {};
	for (var name in all) __defProp(target, name, {
		get: all[name],
		enumerable: true
	});
	if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
	return target;
};
//#endregion
//#region src/security.ts
const DEFAULT_API_BODY_LIMIT_BYTES = 1024 * 1024;
var HttpError = class extends Error {
	status;
	constructor(message, status) {
		super(message);
		this.name = "HttpError";
		this.status = status;
	}
};
function createDevSessionToken() {
	return randomBytes(32).toString("base64url");
}
function realpathNearest(targetPath) {
	let current = path.resolve(targetPath);
	const missingParts = [];
	while (true) try {
		const real = fs.realpathSync.native(current);
		return missingParts.length > 0 ? path.join(real, ...missingParts.reverse()) : real;
	} catch {
		const parent = path.dirname(current);
		if (parent === current) return path.resolve(targetPath);
		missingParts.push(path.basename(current));
		current = parent;
	}
}
function isResolvedPathInside(parentDir, candidatePath) {
	const parent = path.resolve(parentDir);
	const candidate = path.resolve(candidatePath);
	const relative = path.relative(parent, candidate);
	return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
}
function isPathInsideAny(parentDirs, candidatePath) {
	const candidate = realpathNearest(candidatePath);
	return parentDirs.some((parentDir) => isResolvedPathInside(realpathNearest(parentDir), candidate));
}
function resolveInside(parentDir, candidatePath, label = "path") {
	return resolveInsideAny([parentDir], candidatePath, label);
}
function resolveInsideAny(parentDirs, candidatePath, label = "path") {
	if (candidatePath.includes("\0")) throw new HttpError(`${label} contains an invalid character`, 400);
	if (parentDirs.length === 0) throw new HttpError(`No allowed directories configured for ${label}`, 500);
	const parent = path.resolve(parentDirs[0] ?? ".");
	const resolved = path.isAbsolute(candidatePath) ? path.resolve(candidatePath) : path.resolve(parent, candidatePath);
	if (!isPathInsideAny(parentDirs, resolved)) throw new HttpError(`${label} escapes the allowed directory`, 400);
	return resolved;
}
function resolveUrlPathInside(parentDir, requestUrl, label = "path") {
	let pathname = decodeUrlComponent(requestUrl.split(/[?#]/, 1)[0] || "/", label);
	pathname = pathname.replaceAll("\\", "/");
	if (pathname.split("/").includes("..")) throw new HttpError(`${label} must not contain parent directory segments`, 400);
	return resolveInside(parentDir, `.${pathname}`, label);
}
function decodeUrlComponent(value, label = "path") {
	try {
		return decodeURIComponent(value);
	} catch {
		throw new HttpError(`${label} is not valid URL encoding`, 400);
	}
}
function collectRequestBody(req, limit = DEFAULT_API_BODY_LIMIT_BYTES) {
	return new Promise((resolve, reject) => {
		let body = "";
		let size = 0;
		let completed = false;
		req.on("data", (chunk) => {
			if (completed) return;
			const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
			size += buffer.byteLength;
			if (size > limit) {
				completed = true;
				reject(new HttpError(`Request body exceeds ${limit} bytes`, 413));
				return;
			}
			body += buffer.toString("utf-8");
		});
		req.on("end", () => {
			if (!completed) {
				completed = true;
				resolve(body);
			}
		});
		req.on("error", (error) => {
			if (!completed) {
				completed = true;
				reject(error);
			}
		});
	});
}
function parseJsonBody(body) {
	try {
		return JSON.parse(body);
	} catch (error) {
		if (error instanceof SyntaxError) throw new HttpError("Malformed JSON body", 400);
		throw error;
	}
}
function validateDevApiRequest(req, sessionToken) {
	const originError = validateOrigin(req);
	if (originError) return originError;
	if (!isUnsafeMethod(req.method)) return null;
	if (!hasValidSessionToken(req, sessionToken)) return new HttpError("Invalid Musea dev session token", 403);
	if (!isJsonRequest(req)) return new HttpError("Content-Type must be application/json", 415);
	return null;
}
function serializeScriptValue(value) {
	return (JSON.stringify(value) ?? "undefined").replace(/[<>&\u2028\u2029]/g, (char) => {
		switch (char) {
			case "<": return "\\u003C";
			case ">": return "\\u003E";
			case "&": return "\\u0026";
			case "\u2028": return "\\u2028";
			case "\u2029": return "\\u2029";
			default: return char;
		}
	});
}
function isUnsafeMethod(method) {
	return method === "POST" || method === "PUT" || method === "PATCH" || method === "DELETE";
}
function isJsonRequest(req) {
	return getHeader(req, "content-type")?.split(";")[0]?.trim().toLowerCase() === "application/json";
}
function validateOrigin(req) {
	if (getHeader(req, "sec-fetch-site") === "cross-site") return new HttpError("Cross-origin Musea API requests are not allowed", 403);
	const origin = getHeader(req, "origin");
	if (!origin) return null;
	const host = getHeader(req, "host");
	if (!host) return new HttpError("Missing Host header", 400);
	try {
		if (new URL(origin).host !== host) return new HttpError("Cross-origin Musea API requests are not allowed", 403);
	} catch {
		return new HttpError("Invalid Origin header", 400);
	}
	return null;
}
function hasValidSessionToken(req, expectedToken) {
	const actualToken = getHeader(req, "x-musea-session");
	if (!actualToken) return false;
	const actual = Buffer.from(actualToken);
	const expected = Buffer.from(expectedToken);
	return actual.length === expected.length && timingSafeEqual(actual, expected);
}
function getHeader(req, name) {
	const value = req.headers[name];
	if (Array.isArray(value)) return value[0];
	return value;
}
//#endregion
//#region src/gallery/styles-base.css?inline
var styles_base_default = ":root {\n  --musea-bg-primary: #e6e2d6;\n  --musea-bg-secondary: #ddd9cd;\n  --musea-bg-tertiary: #d4d0c4;\n  --musea-bg-elevated: #e6e2d6;\n  --musea-accent: #121212;\n  --musea-accent-hover: #2a2a2a;\n  --musea-accent-subtle: #12121214;\n  --musea-text: #121212;\n  --musea-text-secondary: #3a3a3a;\n  --musea-text-muted: #6b6b6b;\n  --musea-border: #c8c4b8;\n  --musea-border-subtle: #d4d0c4;\n  --musea-success: #16a34a;\n  --musea-shadow: 0 4px 24px #00000014;\n  --musea-radius-sm: 4px;\n  --musea-radius-md: 6px;\n  --musea-radius-lg: 8px;\n  --musea-transition: .15s ease;\n}\n\n* {\n  box-sizing: border-box;\n  margin: 0;\n  padding: 0;\n}\n\nbody {\n  background: var(--musea-bg-primary);\n  color: var(--musea-text);\n  -webkit-font-smoothing: antialiased;\n  min-height: 100vh;\n  font-family: Helvetica Neue, Helvetica, Arial, sans-serif;\n  line-height: 1.5;\n}\n";
//#endregion
//#region src/gallery/styles-layout.css?inline
var styles_layout_default = ".header {\n  background: var(--musea-bg-secondary);\n  border-bottom: 1px solid var(--musea-border);\n  z-index: 100;\n  justify-content: space-between;\n  align-items: center;\n  height: 56px;\n  padding: 0 1.5rem;\n  display: flex;\n  position: sticky;\n  top: 0;\n}\n\n.header-left {\n  align-items: center;\n  gap: 1.5rem;\n  display: flex;\n}\n\n.logo {\n  color: var(--musea-accent);\n  align-items: center;\n  gap: .5rem;\n  font-size: 1.125rem;\n  font-weight: 700;\n  text-decoration: none;\n  display: flex;\n}\n\n.logo-svg {\n  flex-shrink: 0;\n  width: 32px;\n  height: 32px;\n}\n\n.logo-icon svg {\n  width: 16px;\n  height: 16px;\n  color: var(--musea-text);\n}\n\n.header-subtitle {\n  color: var(--musea-text-muted);\n  border-left: 1px solid var(--musea-border);\n  padding-left: 1.5rem;\n  font-size: .8125rem;\n  font-weight: 500;\n}\n\n.search-container {\n  width: 280px;\n  position: relative;\n}\n\n.search-input {\n  background: var(--musea-bg-tertiary);\n  border: 1px solid var(--musea-border);\n  border-radius: var(--musea-radius-md);\n  width: 100%;\n  color: var(--musea-text);\n  transition: border-color var(--musea-transition),\n    background var(--musea-transition);\n  outline: none;\n  padding: .5rem .75rem .5rem 2.25rem;\n  font-size: .8125rem;\n}\n\n.search-input::placeholder {\n  color: var(--musea-text-muted);\n}\n\n.search-input:focus {\n  border-color: var(--musea-accent);\n  background: var(--musea-bg-elevated);\n}\n\n.search-icon {\n  color: var(--musea-text-muted);\n  pointer-events: none;\n  position: absolute;\n  top: 50%;\n  left: .75rem;\n  transform: translateY(-50%);\n}\n\n.main {\n  grid-template-columns: 260px 1fr;\n  min-height: calc(100vh - 56px);\n  display: grid;\n}\n\n.sidebar {\n  background: var(--musea-bg-secondary);\n  border-right: 1px solid var(--musea-border);\n  overflow: hidden auto;\n}\n\n.sidebar::-webkit-scrollbar {\n  width: 6px;\n}\n\n.sidebar::-webkit-scrollbar-track {\n  background: none;\n}\n\n.sidebar::-webkit-scrollbar-thumb {\n  background: var(--musea-border);\n  border-radius: 3px;\n}\n\n.sidebar-section {\n  padding: .75rem;\n}\n\n.category-header {\n  text-transform: uppercase;\n  letter-spacing: .08em;\n  color: var(--musea-text-muted);\n  cursor: pointer;\n  user-select: none;\n  border-radius: var(--musea-radius-sm);\n  transition: background var(--musea-transition);\n  align-items: center;\n  gap: .5rem;\n  padding: .625rem .75rem;\n  font-size: .6875rem;\n  font-weight: 600;\n  display: flex;\n}\n\n.category-header:hover {\n  background: var(--musea-bg-tertiary);\n}\n\n.category-icon {\n  width: 16px;\n  height: 16px;\n  transition: transform var(--musea-transition);\n}\n\n.category-header.collapsed .category-icon {\n  transform: rotate(-90deg);\n}\n\n.category-count {\n  background: var(--musea-bg-tertiary);\n  border-radius: 4px;\n  margin-left: auto;\n  padding: .125rem .375rem;\n  font-size: .625rem;\n}\n\n.art-list {\n  margin-top: .25rem;\n  list-style: none;\n}\n\n.art-item {\n  border-radius: var(--musea-radius-sm);\n  cursor: pointer;\n  color: var(--musea-text-secondary);\n  transition: all var(--musea-transition);\n  align-items: center;\n  gap: .625rem;\n  padding: .5rem .75rem .5rem 1.75rem;\n  font-size: .8125rem;\n  display: flex;\n  position: relative;\n}\n\n.art-item:before {\n  content: \"\";\n  background: var(--musea-border);\n  width: 6px;\n  height: 6px;\n  transition: background var(--musea-transition);\n  border-radius: 50%;\n  position: absolute;\n  top: 50%;\n  left: .75rem;\n  transform: translateY(-50%);\n}\n\n.art-item:hover {\n  background: var(--musea-bg-tertiary);\n  color: var(--musea-text);\n}\n\n.art-item:hover:before {\n  background: var(--musea-text-muted);\n}\n\n.art-item.active {\n  background: var(--musea-accent-subtle);\n  color: var(--musea-accent-hover);\n}\n\n.art-item.active:before {\n  background: var(--musea-accent);\n}\n\n.art-variant-count {\n  color: var(--musea-text-muted);\n  opacity: 0;\n  transition: opacity var(--musea-transition);\n  margin-left: auto;\n  font-size: .6875rem;\n}\n\n.art-item:hover .art-variant-count {\n  opacity: 1;\n}\n\n.content {\n  background: var(--musea-bg-primary);\n  overflow-y: auto;\n}\n\n.content-inner {\n  max-width: 1400px;\n  margin: 0 auto;\n  padding: 2rem;\n}\n\n.content-header {\n  margin-bottom: 2rem;\n}\n\n.content-title {\n  margin-bottom: .5rem;\n  font-size: 1.5rem;\n  font-weight: 700;\n}\n\n.content-description {\n  color: var(--musea-text-muted);\n  max-width: 600px;\n  font-size: .9375rem;\n}\n\n.content-meta {\n  align-items: center;\n  gap: 1rem;\n  margin-top: 1rem;\n  display: flex;\n}\n\n.meta-tag {\n  background: var(--musea-bg-secondary);\n  border: 1px solid var(--musea-border);\n  border-radius: var(--musea-radius-sm);\n  color: var(--musea-text-muted);\n  align-items: center;\n  gap: .375rem;\n  padding: .25rem .625rem;\n  font-size: .75rem;\n  display: inline-flex;\n}\n\n.meta-tag svg {\n  width: 12px;\n  height: 12px;\n}\n";
//#endregion
//#region src/gallery/styles-components.css?inline
var styles_components_default = ".gallery {\n  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n  gap: 1.25rem;\n  display: grid;\n}\n\n.variant-card {\n  background: var(--musea-bg-secondary);\n  border: 1px solid var(--musea-border);\n  border-radius: var(--musea-radius-lg);\n  overflow: hidden;\n}\n\n.variant-preview {\n  aspect-ratio: 16 / 7;\n  background: var(--musea-bg-tertiary);\n  box-sizing: border-box;\n  justify-content: center;\n  align-items: center;\n  display: flex;\n  position: relative;\n  overflow: hidden;\n}\n\n.variant-preview iframe {\n  border-radius: var(--musea-radius-md);\n  background: #fff;\n  border: none;\n  width: 70%;\n  max-width: 100%;\n  height: 100%;\n  max-height: 100%;\n  box-shadow: 0 12px 30px #0d0d0d1f;\n}\n\n.variant-preview-placeholder {\n  color: var(--musea-text-muted);\n  text-align: center;\n  padding: 1rem;\n  font-size: .8125rem;\n}\n\n.variant-preview-code {\n  color: var(--musea-text-muted);\n  background: var(--musea-bg-primary);\n  width: 100%;\n  max-height: 100%;\n  padding: 1rem;\n  font-family: JetBrains Mono, SF Mono, Fira Code, monospace;\n  font-size: .75rem;\n  overflow: auto;\n}\n\n.variant-info {\n  border-top: 1px solid var(--musea-border);\n  justify-content: space-between;\n  align-items: center;\n  padding: 1rem;\n  display: flex;\n}\n\n.variant-name {\n  font-size: .875rem;\n  font-weight: 600;\n}\n\n.variant-badge {\n  text-transform: uppercase;\n  letter-spacing: .04em;\n  background: var(--musea-accent-subtle);\n  color: var(--musea-accent);\n  border-radius: 4px;\n  padding: .1875rem .5rem;\n  font-size: .625rem;\n  font-weight: 600;\n}\n\n.variant-actions {\n  gap: .5rem;\n  display: flex;\n}\n\n.variant-action-btn {\n  background: var(--musea-bg-tertiary);\n  border-radius: var(--musea-radius-sm);\n  width: 28px;\n  height: 28px;\n  color: var(--musea-text-muted);\n  cursor: pointer;\n  transition: all var(--musea-transition);\n  border: none;\n  justify-content: center;\n  align-items: center;\n  display: flex;\n}\n\n.variant-action-btn:hover {\n  background: var(--musea-bg-elevated);\n  color: var(--musea-text);\n}\n\n.variant-action-btn svg {\n  width: 14px;\n  height: 14px;\n}\n\n.empty-state {\n  text-align: center;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  min-height: 400px;\n  padding: 2rem;\n  display: flex;\n}\n\n.empty-state-icon {\n  background: var(--musea-bg-secondary);\n  border-radius: var(--musea-radius-lg);\n  justify-content: center;\n  align-items: center;\n  width: 80px;\n  height: 80px;\n  margin-bottom: 1.5rem;\n  display: flex;\n}\n\n.empty-state-icon svg {\n  width: 40px;\n  height: 40px;\n  color: var(--musea-text-muted);\n}\n\n.empty-state-title {\n  margin-bottom: .5rem;\n  font-size: 1.125rem;\n  font-weight: 600;\n}\n\n.empty-state-text {\n  color: var(--musea-text-muted);\n  max-width: 300px;\n  font-size: .875rem;\n}\n\n.loading {\n  min-height: 200px;\n  color: var(--musea-text-muted);\n  justify-content: center;\n  align-items: center;\n  gap: .75rem;\n  display: flex;\n}\n\n.loading-spinner {\n  border: 2px solid var(--musea-border);\n  border-top-color: var(--musea-accent);\n  border-radius: 50%;\n  width: 20px;\n  height: 20px;\n  animation: .8s linear infinite spin;\n}\n\n@keyframes spin {\n  to {\n    transform: rotate(360deg);\n  }\n}\n\n@media (width <= 768px) {\n  .main {\n    grid-template-columns: 1fr;\n  }\n\n  .sidebar, .header-subtitle {\n    display: none;\n  }\n}\n";
//#endregion
//#region src/gallery/styles.ts
/**
* CSS theme variables and style definitions for the Musea gallery.
*
* CSS is split into separate .css files and imported as text
* via tsdown's `?inline` support.
*/
/**
* Generate the full gallery CSS styles string.
*/
function generateGalleryStyles() {
	return `${styles_base_default}\n${styles_layout_default}\n${styles_components_default}`;
}
//#endregion
//#region src/gallery/template.ts
/**
* HTML structure and inline JS generation for the Musea gallery.
*
* Extracted from gallery.ts to keep file sizes manageable.
*/
function escapeHtmlAttribute(value) {
	return value.replace(/[&<>"']/g, (char) => {
		switch (char) {
			case "&": return "&amp;";
			case "<": return "&lt;";
			case ">": return "&gt;";
			case "\"": return "&quot;";
			case "'": return "&#39;";
			default: return char;
		}
	});
}
/**
* Generate the gallery HTML body (header, sidebar, content, and inline script).
*/
function generateGalleryBody(basePath) {
	return `
  <header class="header">
    <div class="header-left">
      <a href="${escapeHtmlAttribute(basePath)}" class="logo">
        <svg class="logo-svg" width="32" height="32" viewBox="0 0 200 200" fill="none">
          <g transform="translate(30, 25) scale(1.2)">
            <g transform="translate(15, 10) skewX(-15)">
              <path d="M 65 0 L 40 60 L 70 20 L 65 0 Z" fill="currentColor"/>
              <path d="M 20 0 L 40 60 L 53 13 L 20 0 Z" fill="currentColor"/>
            </g>
          </g>
          <g transform="translate(110, 120)">
            <line x1="5" y1="10" x2="5" y2="50" stroke="currentColor" stroke-width="3" stroke-linecap="round"/>
            <line x1="60" y1="10" x2="60" y2="50" stroke="currentColor" stroke-width="3" stroke-linecap="round"/>
            <path d="M 0 10 L 32.5 0 L 65 10" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
            <rect x="15" y="18" width="14" height="12" rx="1" fill="none" stroke="currentColor" stroke-width="1.5" opacity="0.7"/>
            <rect x="36" y="18" width="14" height="12" rx="1" fill="none" stroke="currentColor" stroke-width="1.5" opacity="0.7"/>
            <rect x="23" y="35" width="18" height="12" rx="1" fill="none" stroke="currentColor" stroke-width="1.5" opacity="0.6"/>
          </g>
        </svg>
        Musea
      </a>
      <span class="header-subtitle">Component Gallery</span>
    </div>
    <div class="search-container">
      <svg class="search-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
        <circle cx="11" cy="11" r="8"/><path d="m21 21-4.35-4.35"/>
      </svg>
      <input type="text" class="search-input" placeholder="Search components..." id="search">
    </div>
  </header>

  <main class="main">
    <aside class="sidebar" id="sidebar">
      <div class="loading">
        <div class="loading-spinner"></div>
        Loading...
      </div>
    </aside>
    <section class="content" id="content">
      <div class="empty-state">
        <div class="empty-state-icon">
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
            <path d="M4 5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5Z"/>
            <path d="M4 13a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-6Z"/>
            <path d="M16 13a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-6Z"/>
          </svg>
        </div>
        <div class="empty-state-title">Select a component</div>
        <div class="empty-state-text">Choose a component from the sidebar to view its variants and documentation</div>
      </div>
    </section>
  </main>`;
}
/**
* Generate the gallery inline script (SPA logic).
*/
function generateGalleryScript(basePath) {
	return `
    const basePath = ${serializeScriptValue(basePath)};
    let arts = [];
    let selectedArt = null;
    let searchQuery = '';

    async function loadArts() {
      try {
        const res = await fetch(basePath + '/api/arts');
        arts = await res.json();
        renderSidebar();
      } catch (e) {
        console.error('Failed to load arts:', e);
        document.getElementById('sidebar').innerHTML = '<div class="loading">Failed to load</div>';
      }
    }

    function renderSidebar() {
      const sidebar = document.getElementById('sidebar');
      const categories = {};

      const filtered = searchQuery
        ? arts.filter(a => a.metadata.title.toLowerCase().includes(searchQuery.toLowerCase()))
        : arts;

      for (const art of filtered) {
        const cat = art.metadata.category || 'Components';
        if (!categories[cat]) categories[cat] = [];
        categories[cat].push(art);
      }

      if (Object.keys(categories).length === 0) {
        sidebar.innerHTML = '<div class="sidebar-section"><div class="loading">No components found</div></div>';
        return;
      }

      let html = '';
      for (const [category, items] of Object.entries(categories)) {
        const escapedCategory = escapeHtml(category);
        html += '<div class="sidebar-section">';
        html += '<div class="category-header" data-category="' + escapedCategory + '">';
        html += '<svg class="category-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m9 18 6-6-6-6"/></svg>';
        html += '<span>' + escapedCategory + '</span>';
        html += '<span class="category-count">' + items.length + '</span>';
        html += '</div>';
        html += '<ul class="art-list" data-category="' + escapedCategory + '">';
        for (const art of items) {
          const active = selectedArt?.path === art.path ? 'active' : '';
          const variantCount = art.variants?.length || 0;
          html += '<li class="art-item ' + active + '" data-path="' + escapeHtml(art.path) + '">';
          html += '<span>' + escapeHtml(art.metadata.title) + '</span>';
          html += '<span class="art-variant-count">' + variantCount + ' variant' + (variantCount !== 1 ? 's' : '') + '</span>';
          html += '</li>';
        }
        html += '</ul>';
        html += '</div>';
      }

      sidebar.innerHTML = html;

      sidebar.querySelectorAll('.art-item').forEach(item => {
        item.addEventListener('click', () => {
          const artPath = item.dataset.path;
          selectedArt = arts.find(a => a.path === artPath);
          renderSidebar();
          renderContent();
        });
      });

      sidebar.querySelectorAll('.category-header').forEach(header => {
        header.addEventListener('click', () => {
          header.classList.toggle('collapsed');
          const list = header.parentElement?.querySelector('.art-list');
          if (list) list.style.display = header.classList.contains('collapsed') ? 'none' : 'block';
        });
      });
    }

    function renderContent() {
      const content = document.getElementById('content');
      if (!selectedArt) {
        content.innerHTML = \`
          <div class="empty-state">
            <div class="empty-state-icon">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
                <path d="M4 5a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5Z"/>
                <path d="M4 13a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-6Z"/>
                <path d="M16 13a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1h-2a1 1 0 0 1-1-1v-6Z"/>
              </svg>
            </div>
            <div class="empty-state-title">Select a component</div>
            <div class="empty-state-text">Choose a component from the sidebar to view its variants</div>
          </div>
        \`;
        return;
      }

      const meta = selectedArt.metadata;
      const tags = meta.tags || [];
      const variantCount = selectedArt.variants?.length || 0;

      let html = '<div class="content-inner">';
      html += '<div class="content-header">';
      html += '<h1 class="content-title">' + escapeHtml(meta.title) + '</h1>';
      if (meta.description) {
        html += '<p class="content-description">' + escapeHtml(meta.description) + '</p>';
      }
      html += '<div class="content-meta">';
      html += '<span class="meta-tag"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>' + variantCount + ' variant' + (variantCount !== 1 ? 's' : '') + '</span>';
      if (meta.category) {
        html += '<span class="meta-tag"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>' + escapeHtml(meta.category) + '</span>';
      }
      for (const tag of tags) {
        html += '<span class="meta-tag">#' + escapeHtml(tag) + '</span>';
      }
      html += '</div>';
      html += '</div>';

      html += '<div class="gallery">';
      for (const variant of selectedArt.variants) {
        const previewUrl = basePath + '/preview?art=' + encodeURIComponent(selectedArt.path) + '&variant=' + encodeURIComponent(variant.name);
        const escapedPreviewUrl = escapeHtml(previewUrl);

        html += '<div class="variant-card">';
        html += '<div class="variant-preview">';
        html += '<iframe src="' + escapedPreviewUrl + '" loading="lazy" title="' + escapeHtml(variant.name) + '"></iframe>';
        html += '</div>';
        html += '<div class="variant-info">';
        html += '<div>';
        html += '<span class="variant-name">' + escapeHtml(variant.name) + '</span>';
        if (variant.isDefault) html += ' <span class="variant-badge">Default</span>';
        html += '</div>';
        html += '<div class="variant-actions">';
        html += '<button class="variant-action-btn" title="Open in new tab" data-preview-url="' + escapedPreviewUrl + '"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg></button>';
        html += '</div>';
        html += '</div>';
        html += '</div>';
      }
      html += '</div>';
      html += '</div>';

      content.innerHTML = html;

      content.querySelectorAll('.variant-action-btn[data-preview-url]').forEach(button => {
        button.addEventListener('click', () => {
          const previewUrl = button.dataset.previewUrl;
          if (previewUrl) window.open(previewUrl, '_blank', 'noopener');
        });
      });
    }

    function escapeHtml(str) {
      if (!str) return '';
      return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#39;');
    }

    // Search
    document.getElementById('search').addEventListener('input', (e) => {
      searchQuery = e.target.value;
      renderSidebar();
    });

    // Keyboard shortcut for search
    document.addEventListener('keydown', (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
        e.preventDefault();
        document.getElementById('search').focus();
      }
    });

    loadArts();`;
}
//#endregion
//#region src/gallery/index.ts
/**
* Gallery HTML generation for the Musea component gallery.
*
* Contains the inline gallery SPA template (used as a fallback when the
* pre-built gallery is not available) and the gallery virtual module.
*/
var gallery_exports = /* @__PURE__ */ __exportAll({
	generateGalleryHtml: () => generateGalleryHtml,
	generateGalleryModule: () => generateGalleryModule
});
/**
* Generate the inline gallery HTML page.
*/
function generateGalleryHtml(basePath, devSessionToken, themeConfig) {
	const themeScript = themeConfig ? `window.__MUSEA_THEME_CONFIG__=${serializeScriptValue(themeConfig)};` : "";
	return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Musea - Component Gallery</title>
  <script>window.__MUSEA_BASE_PATH__=${serializeScriptValue(basePath)};window.__MUSEA_SESSION_TOKEN__=${serializeScriptValue(devSessionToken)};${themeScript}<\/script>
  <style>${generateGalleryStyles()}
  </style>
</head>
<body>${generateGalleryBody(basePath)}

  <script type="module">${generateGalleryScript(basePath)}
  <\/script>
</body>
</html>`;
}
/**
* Generate the virtual gallery module code.
*/
function generateGalleryModule(basePath) {
	return `
export const basePath = ${serializeScriptValue(basePath)};
export async function loadArts() {
  const res = await fetch(basePath + '/api/arts');
  return res.json();
}
`;
}
//#endregion
export { collectRequestBody as a, parseJsonBody as c, resolveUrlPathInside as d, serializeScriptValue as f, HttpError as i, resolveInside as l, generateGalleryModule as n, createDevSessionToken as o, validateDevApiRequest as p, DEFAULT_API_BODY_LIMIT_BYTES as r, decodeUrlComponent as s, gallery_exports as t, resolveInsideAny as u };

//# sourceMappingURL=gallery-kaxHo2Kt.mjs.map