@developer.krd/discord-dashboard 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3736 -898
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +72 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +3736 -898
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/Types/elysia.ts","../src/handlers/DiscordHelpers.ts","../src/templates/scripts/client.ts","../src/templates/layouts/compact.ts","../src/templates/layouts/default.ts","../src/templates/layouts/shadcn-magic.ts","../src/templates/index.ts","../src/templates/themes/index.ts","../src/handlers/TemplateManager.ts","../src/core/DashboardEngine.ts","../src/handlers/DashboardDesigner.ts","../src/adapters/express.ts","../src/adapters/elysia.ts","../src/adapters/fastify.ts"],"sourcesContent":["export * from \"./Types\";\n\nexport { DashboardEngine } from \"./core/DashboardEngine\";\nexport { DashboardDesigner } from \"./handlers/DashboardDesigner\";\n\nexport { createExpressAdapter } from \"./adapters/express\";\nexport { createElysiaAdapter } from \"./adapters/elysia\";\nexport { createFastifyAdapter } from \"./adapters/fastify\";\n","import { Static, t } from \"elysia\";\n\nconst UserSchema = t.Object({\n id: t.String(),\n username: t.String(),\n discriminator: t.String(),\n avatar: t.Nullable(t.String()),\n global_name: t.Optional(t.Nullable(t.String())),\n avatarUrl: t.Optional(t.Nullable(t.String())),\n});\n\nconst GuildSchema = t.Object({\n id: t.String(),\n name: t.String(),\n icon: t.Nullable(t.String()),\n owner: t.Boolean(),\n permissions: t.String(),\n iconUrl: t.Optional(t.Nullable(t.String())),\n botInGuild: t.Optional(t.Boolean()),\n inviteUrl: t.Optional(t.String()),\n});\n\nexport const SessionSchema = t.Object({\n oauthState: t.Optional(t.String()),\n discordAuth: t.Optional(\n t.Object({\n accessToken: t.String(),\n user: UserSchema,\n guilds: t.Array(GuildSchema),\n }),\n ),\n});\n\nexport type SessionData = Static<typeof SessionSchema>;\n","import type { DashboardDiscordHelpers, DiscordChannel, DiscordMember, DiscordRole } from \"../Types\";\n\nexport class DiscordHelpers implements DashboardDiscordHelpers {\n private readonly botToken: string;\n private readonly DISCORD_API = \"https://discord.com/api/v10\";\n\n constructor(botToken: string) {\n this.botToken = botToken;\n }\n\n private async fetchDiscordWithBot<T>(path: string): Promise<T | null> {\n const response = await fetch(`${this.DISCORD_API}${path}`, {\n headers: {\n Authorization: `Bot ${this.botToken}`,\n },\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as T;\n }\n\n public async getChannel(channelId: string): Promise<DiscordChannel | null> {\n return await this.fetchDiscordWithBot<DiscordChannel>(`/channels/${channelId}`);\n }\n\n public async getGuildChannels(guildId: string): Promise<DiscordChannel[]> {\n return (await this.fetchDiscordWithBot<DiscordChannel[]>(`/guilds/${guildId}/channels`)) ?? [];\n }\n\n public async searchGuildChannels(guildId: string, query: string, options?: { limit?: number; nsfw?: boolean; channelTypes?: number[] }): Promise<DiscordChannel[]> {\n const channels = (await this.fetchDiscordWithBot<DiscordChannel[]>(`/guilds/${guildId}/channels`)) ?? [];\n const normalizedQuery = query.trim().toLowerCase();\n const limit = Math.max(1, Math.min(options?.limit ?? 10, 50));\n\n return channels\n .filter((channel) => {\n if (options?.nsfw !== undefined && Boolean(channel.nsfw) !== options.nsfw) {\n return false;\n }\n\n if (options?.channelTypes && options.channelTypes.length > 0 && !options.channelTypes.includes(channel.type)) {\n return false;\n }\n\n if (!normalizedQuery) {\n return true;\n }\n\n return channel.name.toLowerCase().includes(normalizedQuery);\n })\n .slice(0, limit);\n }\n\n public async getRole(guildId: string, roleId: string): Promise<DiscordRole | null> {\n const roles = await this.fetchDiscordWithBot<DiscordRole[]>(`/guilds/${guildId}/roles`);\n if (!roles) {\n return null;\n }\n\n return roles.find((role) => role.id === roleId) ?? null;\n }\n\n public async getGuildRoles(guildId: string): Promise<DiscordRole[]> {\n return (await this.fetchDiscordWithBot<DiscordRole[]>(`/guilds/${guildId}/roles`)) ?? [];\n }\n\n public async searchGuildRoles(guildId: string, query: string, options?: { limit?: number; includeManaged?: boolean }): Promise<DiscordRole[]> {\n const roles = (await this.fetchDiscordWithBot<DiscordRole[]>(`/guilds/${guildId}/roles`)) ?? [];\n const normalizedQuery = query.trim().toLowerCase();\n const limit = Math.max(1, Math.min(options?.limit ?? 10, 50));\n\n return roles\n .filter((role) => {\n if (!options?.includeManaged && role.managed) {\n return false;\n }\n\n if (!normalizedQuery) {\n return true;\n }\n\n return role.name.toLowerCase().includes(normalizedQuery);\n })\n .sort((a, b) => b.position - a.position)\n .slice(0, limit);\n }\n\n public async searchGuildMembers(guildId: string, query: string, options?: { limit?: number }): Promise<DiscordMember[]> {\n const limit = Math.max(1, Math.min(options?.limit ?? 10, 1000));\n const params = new URLSearchParams({\n query: query.trim(),\n limit: String(limit),\n });\n\n return (await this.fetchDiscordWithBot<DiscordMember[]>(`/guilds/${guildId}/members/search?${params.toString()}`)) ?? [];\n }\n\n public async getGuildMember(guildId: string, userId: string): Promise<DiscordMember | null> {\n return await this.fetchDiscordWithBot<DiscordMember>(`/guilds/${guildId}/members/${userId}`);\n }\n\n public getGuildIconUrl(guildId: string, iconHash: string | null): string | null {\n if (!iconHash) return null;\n\n const extension = iconHash.startsWith(\"a_\") ? \"gif\" : \"png\";\n return `https://cdn.discordapp.com/icons/${guildId}/${iconHash}.${extension}?size=128`;\n }\n\n public getUserAvatarUrl(userId: string, avatarHash: string | null): string | null {\n if (!avatarHash) return null;\n const extension = avatarHash.startsWith(\"a_\") ? \"gif\" : \"png\";\n return `https://cdn.discordapp.com/avatars/${userId}/${avatarHash}.${extension}?size=128`;\n }\n}\n","export function getClientScript(basePath: string, setupDesign: any = {}): string {\n const scriptData = JSON.stringify({ basePath, setupDesign });\n\n return `\n const dashboardConfig = ${scriptData};\n const state = {\n session: null,\n guilds: [],\n selectedGuildId: null,\n homeCategories: [],\n selectedHomeCategoryId: null,\n activeMainTab: \"home\"\n };\n \n const el = {\n serverRail: document.getElementById(\"serverRail\"),\n userMeta: document.getElementById(\"userMeta\"),\n centerTitle: document.getElementById(\"centerTitle\"),\n tabHome: document.getElementById(\"tabHome\"),\n tabPlugins: document.getElementById(\"tabPlugins\"),\n homeArea: document.getElementById(\"homeArea\"),\n pluginsArea: document.getElementById(\"pluginsArea\"),\n homeCategories: document.getElementById(\"homeCategories\"),\n homeSections: document.getElementById(\"homeSections\"),\n overviewArea: document.getElementById(\"overviewArea\"),\n overviewCards: document.getElementById(\"overviewCards\"),\n plugins: document.getElementById(\"plugins\")\n };\n \n const fetchJson = async (url, init) => {\n const response = await fetch(url, init);\n if (!response.ok) throw new Error(await response.text());\n return response.json();\n };\n \n const buildApiUrl = (path) => {\n if (!state.selectedGuildId) return dashboardConfig.basePath + path;\n const separator = path.includes(\"?\") ? \"&\" : \"?\";\n return dashboardConfig.basePath + path + separator + \"guildId=\" + encodeURIComponent(state.selectedGuildId);\n };\n \n const escapeHtml = (value) => String(value)\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n \n const normalizeBoxWidth = (value) => {\n const numeric = Number(value);\n if (numeric === 50 || numeric === 33 || numeric === 20) return numeric;\n return 100;\n };\n \n const makeButton = (action, pluginId, panelId, panelElement) => {\n const button = document.createElement(\"button\");\n button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n \n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n let payload = {};\n if (action.collectFields && panelElement) {\n const values = {};\n const inputs = panelElement.querySelectorAll(\"[data-plugin-field-id]\");\n inputs.forEach((inputEl) => {\n const fieldId = inputEl.dataset.pluginFieldId;\n const fieldType = inputEl.dataset.pluginFieldType || \"text\";\n if (!fieldId) return;\n values[fieldId] = toFieldValue({ type: fieldType }, inputEl);\n });\n payload = { panelId, values };\n }\n \n const actionUrl = buildApiUrl(\"/api/plugins/\" + encodeURIComponent(pluginId) + \"/\" + encodeURIComponent(action.id));\n const result = await fetchJson(actionUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload)\n });\n if (result.message) alert(result.message);\n if (result.refresh) await refreshContent();\n } catch (error) {\n alert(error instanceof Error ? error.message : \"Action failed\");\n } finally {\n button.disabled = false;\n }\n });\n return button;\n };\n \n const renderCards = (cards) => {\n if (!cards.length) {\n el.overviewCards.innerHTML = '<div class=\"empty\">No cards configured yet.</div>';\n return;\n }\n el.overviewCards.innerHTML = cards.map((card) => {\n const subtitle = card.subtitle ? '<div class=\"subtitle\">' + escapeHtml(card.subtitle) + '</div>' : \"\";\n return '<article class=\"panel\">'\n + '<div class=\"title\">' + escapeHtml(card.title) + '</div>'\n + '<div class=\"value\">' + escapeHtml(card.value) + '</div>'\n + subtitle\n + '</article>';\n }).join(\"\");\n };\n \n const shortName = (name) => {\n if (!name) return \"?\";\n const parts = String(name).trim().split(/\\\\s+/).filter(Boolean);\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return (parts[0][0] + parts[1][0]).toUpperCase();\n };\n \n const addAvatarOrFallback = (button, item) => {\n if (!item.avatarUrl) {\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n return;\n }\n const avatar = document.createElement(\"img\");\n avatar.className = \"server-avatar\";\n avatar.src = item.avatarUrl;\n avatar.alt = item.name;\n avatar.addEventListener(\"error\", () => {\n avatar.remove();\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n });\n button.appendChild(avatar);\n };\n \n const renderServerRail = () => {\n const items = [{ id: null, name: \"User Dashboard\", short: \"ME\", avatarUrl: state.session?.user?.avatarUrl ?? null, botInGuild: true }].concat(\n state.guilds.map((guild) => ({\n id: guild.id,\n name: guild.name,\n short: shortName(guild.name),\n avatarUrl: guild.iconUrl ?? null,\n botInGuild: guild.botInGuild !== false,\n inviteUrl: guild.inviteUrl\n }))\n );\n \n el.serverRail.innerHTML = \"\";\n items.forEach((item) => {\n const button = document.createElement(\"button\");\n button.className = \"server-item cursor-pointer\" + (item.id === state.selectedGuildId ? \" active\" : \"\");\n button.title = item.id && !item.botInGuild ? (item.name + \" • Invite bot\") : item.name;\n \n const activeIndicator = document.createElement(\"span\");\n activeIndicator.className = \"server-item-indicator\";\n button.appendChild(activeIndicator);\n \n addAvatarOrFallback(button, item);\n \n if (item.id) {\n const status = document.createElement(\"span\");\n status.className = \"server-status\" + (item.botInGuild ? \"\" : \" offline\");\n button.appendChild(status);\n }\n \n button.addEventListener(\"click\", async () => {\n if (item.id && !item.botInGuild && item.inviteUrl) {\n const opened = window.open(item.inviteUrl, \"_blank\", \"noopener,noreferrer\");\n if (!opened && typeof alert === \"function\") alert(\"Popup blocked. Please allow popups.\");\n return;\n }\n state.selectedGuildId = item.id;\n renderServerRail();\n updateContextLabel();\n await refreshContent();\n });\n el.serverRail.appendChild(button);\n });\n };\n \n const applyMainTab = () => {\n const homeActive = state.activeMainTab === \"home\";\n el.homeArea.style.display = homeActive ? \"block\" : \"none\";\n el.pluginsArea.style.display = homeActive ? \"none\" : \"block\";\n el.tabHome.className = \"main-tab cursor-pointer\" + (homeActive ? \" active\" : \"\");\n el.tabPlugins.className = \"main-tab cursor-pointer\" + (!homeActive ? \" active\" : \"\");\n };\n \n const updateContextLabel = () => {\n if (!state.selectedGuildId) {\n el.centerTitle.textContent = \"User Dashboard\";\n return;\n }\n const selectedGuild = state.guilds.find((guild) => guild.id === state.selectedGuildId);\n el.centerTitle.textContent = selectedGuild ? (selectedGuild.name + \" Dashboard\") : \"Server Dashboard\";\n };\n \n const renderHomeCategories = () => {\n if (!state.homeCategories.length) {\n el.homeCategories.innerHTML = \"\";\n return;\n }\n el.homeCategories.innerHTML = \"\";\n state.homeCategories.forEach((category) => {\n const button = document.createElement(\"button\");\n button.className = \"home-category-btn cursor-pointer\" + (state.selectedHomeCategoryId === category.id ? \" active\" : \"\");\n button.textContent = category.label;\n button.title = category.description || category.label;\n button.addEventListener(\"click\", async () => {\n state.selectedHomeCategoryId = category.id;\n renderHomeCategories();\n await refreshContent();\n });\n el.homeCategories.appendChild(button);\n });\n };\n \n const toFieldValue = (field, element) => {\n if (field.type === \"string-list\") {\n const raw = element.dataset.listValues;\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch { return []; }\n }\n if (field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\") {\n const raw = element.dataset.selectedObject;\n if (!raw) return null;\n try { return JSON.parse(raw); } catch { return null; }\n }\n if (field.type === \"boolean\") return Boolean(element.checked);\n if (field.type === \"number\") return element.value === \"\" ? null : Number(element.value);\n return element.value;\n };\n \n const setupStringListField = (field, input, fieldWrap) => {\n const editor = document.createElement(\"div\");\n editor.className = \"list-editor\";\n const itemsWrap = document.createElement(\"div\");\n itemsWrap.className = \"list-items\";\n const addButton = document.createElement(\"button\");\n addButton.type = \"button\";\n addButton.className = \"list-add cursor-pointer\";\n addButton.textContent = \"Add Button\";\n \n const normalizeValues = () => {\n const values = Array.from(itemsWrap.querySelectorAll(\".list-input\")).map((item) => item.value.trim()).filter((item) => item.length > 0);\n input.dataset.listValues = JSON.stringify(values);\n };\n \n const makeRow = (value = \"\") => {\n const row = document.createElement(\"div\");\n row.className = \"list-item\";\n row.draggable = true;\n const handle = document.createElement(\"span\");\n handle.className = \"drag-handle cursor-pointer\";\n handle.textContent = \"⋮⋮\";\n const textInput = document.createElement(\"input\");\n textInput.type = \"text\";\n textInput.className = \"list-input\";\n textInput.value = value;\n textInput.placeholder = \"Button label\";\n textInput.addEventListener(\"input\", normalizeValues);\n const removeButton = document.createElement(\"button\");\n removeButton.type = \"button\";\n removeButton.className = \"cursor-pointer\";\n removeButton.textContent = \"×\";\n removeButton.addEventListener(\"click\", () => { row.remove(); normalizeValues(); });\n \n row.addEventListener(\"dragstart\", () => row.classList.add(\"dragging\"));\n row.addEventListener(\"dragend\", () => { row.classList.remove(\"dragging\"); normalizeValues(); });\n \n row.appendChild(handle); row.appendChild(textInput); row.appendChild(removeButton);\n return row;\n };\n \n itemsWrap.addEventListener(\"dragover\", (event) => {\n event.preventDefault();\n const dragging = itemsWrap.querySelector(\".dragging\");\n if (!dragging) return;\n const siblings = Array.from(itemsWrap.querySelectorAll(\".list-item:not(.dragging)\"));\n let inserted = false;\n for (const sibling of siblings) {\n const rect = sibling.getBoundingClientRect();\n if (event.clientY < rect.top + rect.height / 2) {\n itemsWrap.insertBefore(dragging, sibling);\n inserted = true;\n break;\n }\n }\n if (!inserted) itemsWrap.appendChild(dragging);\n });\n \n const initialValues = Array.isArray(field.value) ? field.value.map((item) => String(item)) : [];\n if (initialValues.length === 0) initialValues.push(\"Yes\", \"No\");\n initialValues.forEach((value) => itemsWrap.appendChild(makeRow(value)));\n \n addButton.addEventListener(\"click\", () => { itemsWrap.appendChild(makeRow(\"\")); normalizeValues(); });\n editor.appendChild(itemsWrap); editor.appendChild(addButton); fieldWrap.appendChild(editor);\n normalizeValues();\n };\n \n const showLookupResults = (container, items, labelResolver, onSelect) => {\n container.innerHTML = \"\";\n if (!items.length) { container.style.display = \"none\"; return; }\n items.forEach((item) => {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"lookup-item cursor-pointer\";\n btn.textContent = labelResolver(item);\n btn.addEventListener(\"click\", () => { onSelect(item); container.style.display = \"none\"; });\n container.appendChild(btn);\n });\n container.style.display = \"block\";\n };\n \n const setupLookupField = (field, input, fieldWrap) => {\n const wrap = document.createElement(\"div\");\n wrap.className = \"lookup-wrap\";\n const results = document.createElement(\"div\");\n results.className = \"lookup-results\";\n const selected = document.createElement(\"div\");\n selected.className = \"lookup-selected\";\n selected.textContent = \"No selection\";\n \n wrap.appendChild(input); wrap.appendChild(results); fieldWrap.appendChild(wrap); fieldWrap.appendChild(selected);\n \n const minQueryLength = Math.max(0, field.lookup?.minQueryLength ?? 1);\n const limit = Math.max(1, Math.min(field.lookup?.limit ?? 10, 50));\n \n const runSearch = async () => {\n const query = String(input.value || \"\");\n if (query.length < minQueryLength) { results.style.display = \"none\"; return; }\n try {\n if (field.type === \"role-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n if (field.lookup?.includeManaged !== undefined) params.set(\"includeManaged\", String(Boolean(field.lookup.includeManaged)));\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/roles?\" + params.toString()));\n showLookupResults(results, payload.roles || [], (item) => \"@\" + item.name, (item) => {\n input.value = item.name;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected role: @\" + item.name + \" (\" + item.id + \")\";\n });\n } else if (field.type === \"channel-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n if (field.lookup?.nsfw !== undefined) params.set(\"nsfw\", String(Boolean(field.lookup.nsfw)));\n if (field.lookup?.channelTypes && field.lookup.channelTypes.length > 0) params.set(\"channelTypes\", field.lookup.channelTypes.join(\",\"));\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/channels?\" + params.toString()));\n showLookupResults(results, payload.channels || [], (item) => \"#\" + item.name, (item) => {\n input.value = item.name;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected channel: #\" + item.name + \" (\" + item.id + \")\";\n });\n } else if (field.type === \"member-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/members?\" + params.toString()));\n showLookupResults(results, payload.members || [], \n (item) => (item?.user?.username || \"unknown\") + (item?.nick ? \" (\" + item.nick + \")\" : \"\"), \n (item) => {\n const username = item?.user?.username || \"unknown\";\n input.value = username;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected member: \" + username + \" (\" + (item?.user?.id || \"unknown\") + \")\";\n });\n }\n } catch { results.style.display = \"none\"; }\n };\n \n input.addEventListener(\"input\", () => { input.dataset.selectedObject = \"\"; selected.textContent = \"No selection\"; runSearch(); });\n input.addEventListener(\"blur\", () => setTimeout(() => { results.style.display = \"none\"; }, 120));\n };\n \n const renderHomeSections = (sections) => {\n if (!sections.length) { el.homeSections.innerHTML = '<div class=\"empty\">No home sections configured.</div>'; return; }\n el.homeSections.innerHTML = \"\";\n sections.forEach((section) => {\n const wrap = document.createElement(\"article\");\n wrap.className = \"panel home-section-panel home-width-\" + normalizeBoxWidth(section.width);\n \n const heading = document.createElement(\"h3\");\n heading.textContent = section.title; heading.style.margin = \"0\"; wrap.appendChild(heading);\n \n if (section.description) {\n const desc = document.createElement(\"div\");\n desc.className = \"subtitle\"; desc.textContent = section.description; wrap.appendChild(desc);\n }\n \n const message = document.createElement(\"div\");\n message.className = \"home-message\";\n \n if (section.fields?.length) {\n const fieldsWrap = document.createElement(\"div\");\n fieldsWrap.className = \"home-fields\";\n \n section.fields.forEach((field) => {\n const fieldWrap = document.createElement(\"div\");\n fieldWrap.className = \"home-field\";\n \n const label = document.createElement(\"label\");\n label.textContent = field.label; fieldWrap.appendChild(label);\n \n let input;\n if (field.type === \"textarea\") {\n input = document.createElement(\"textarea\"); input.className = \"home-textarea\"; input.value = field.value == null ? \"\" : String(field.value);\n } else if (field.type === \"select\") {\n input = document.createElement(\"select\"); input.className = \"home-select cursor-pointer\";\n (field.options || []).forEach((option) => {\n const optionEl = document.createElement(\"option\"); optionEl.value = option.value; optionEl.textContent = option.label;\n if (String(field.value ?? \"\") === option.value) optionEl.selected = true;\n input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\"); row.className = \"home-field-row\";\n input = document.createElement(\"input\"); input.type = \"checkbox\"; input.className = \"home-checkbox cursor-pointer\"; input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\"); stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\");\n row.appendChild(input); row.appendChild(stateText); fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\"); input.className = \"home-input\";\n input.type = field.type === \"number\" ? \"number\" : \"text\"; input.value = field.value == null ? \"\" : String(field.value);\n }\n \n if (input) {\n input.dataset.homeFieldId = field.id; input.dataset.homeFieldType = field.type;\n if (field.placeholder && \"placeholder\" in input) input.placeholder = field.placeholder;\n if (field.required && \"required\" in input) input.required = true;\n if (field.readOnly) { if (\"readOnly\" in input) input.readOnly = true; if (\"disabled\" in input) input.disabled = true; }\n \n if (field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\") { setupLookupField(field, input, fieldWrap); }\n else if (field.type !== \"boolean\") { fieldWrap.appendChild(input); }\n }\n fieldsWrap.appendChild(fieldWrap);\n });\n wrap.appendChild(fieldsWrap);\n }\n \n if (section.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n section.actions.forEach((action) => {\n const button = document.createElement(\"button\"); button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n \n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n const values = {};\n const inputs = wrap.querySelectorAll(\"[data-home-field-id]\");\n inputs.forEach((inputEl) => {\n const fieldId = inputEl.dataset.homeFieldId;\n const fieldType = inputEl.dataset.homeFieldType || \"text\";\n if (!fieldId) return;\n values[fieldId] = toFieldValue({ type: fieldType }, inputEl);\n });\n const result = await fetchJson(buildApiUrl(\"/api/home/\" + encodeURIComponent(action.id)), {\n method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ sectionId: section.id, values })\n });\n message.textContent = result.message || \"Saved.\";\n if (result.refresh) await refreshContent();\n } catch (error) { message.textContent = error instanceof Error ? error.message : \"Save failed\"; }\n finally { button.disabled = false; }\n });\n actions.appendChild(button);\n });\n wrap.appendChild(actions);\n }\n wrap.appendChild(message); el.homeSections.appendChild(wrap);\n });\n };\n \n const renderPlugins = (plugins) => {\n if (!plugins.length) { el.plugins.innerHTML = '<div class=\"empty\">No plugins configured yet.</div>'; return; }\n el.plugins.innerHTML = \"\";\n plugins.forEach((plugin) => {\n const wrap = document.createElement(\"article\"); wrap.className = \"panel\";\n const heading = document.createElement(\"div\"); heading.className = \"title\"; heading.textContent = plugin.name; wrap.appendChild(heading);\n if (plugin.description) { const desc = document.createElement(\"div\"); desc.className = \"subtitle\"; desc.textContent = plugin.description; wrap.appendChild(desc); }\n \n (plugin.panels || []).forEach((panel) => {\n const panelBody = document.createElement(\"div\");\n const panelTitle = document.createElement(\"h4\"); panelTitle.textContent = panel.title; panelTitle.style.marginBottom = \"4px\"; panelBody.appendChild(panelTitle);\n if (panel.description) { const p = document.createElement(\"div\"); p.className = \"subtitle\"; p.textContent = panel.description; panelBody.appendChild(p); }\n \n if (panel.fields?.length) {\n const fieldsWrap = document.createElement(\"div\"); fieldsWrap.className = \"plugin-fields\";\n panel.fields.forEach((field) => {\n const fieldWrap = document.createElement(\"div\"); fieldWrap.className = field.editable ? \"plugin-field\" : \"kv-item\";\n \n if (!field.editable) {\n const display = field.value == null ? \"\" : typeof field.value === \"object\" ? JSON.stringify(field.value) : String(field.value);\n fieldWrap.innerHTML = '<strong>' + escapeHtml(field.label) + '</strong><span>' + escapeHtml(display) + '</span>';\n fieldsWrap.appendChild(fieldWrap); return;\n }\n \n const label = document.createElement(\"label\"); label.textContent = field.label; fieldWrap.appendChild(label);\n \n let input;\n if (field.type === \"textarea\") { input = document.createElement(\"textarea\"); input.className = \"home-textarea\"; input.value = field.value == null ? \"\" : String(field.value); }\n else if (field.type === \"select\") {\n input = document.createElement(\"select\"); input.className = \"home-select cursor-pointer\";\n (field.options || []).forEach((option) => {\n const optionEl = document.createElement(\"option\"); optionEl.value = option.value; optionEl.textContent = option.label;\n if (String(field.value ?? \"\") === option.value) optionEl.selected = true;\n input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\"); row.className = \"home-field-row\";\n input = document.createElement(\"input\"); input.type = \"checkbox\"; input.className = \"home-checkbox cursor-pointer\"; input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\"); stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\");\n row.appendChild(input); row.appendChild(stateText); fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\"); input.className = \"home-input\";\n input.type = field.type === \"number\" ? \"number\" : field.type === \"url\" ? \"url\" : \"text\"; input.value = field.value == null ? \"\" : String(field.value);\n }\n \n if (input) {\n input.dataset.pluginFieldId = field.id || field.label; input.dataset.pluginFieldType = field.type || \"text\";\n if (field.placeholder && \"placeholder\" in input) input.placeholder = field.placeholder;\n if (field.required && \"required\" in input) input.required = true;\n \n const isLookup = field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\";\n if (isLookup) { setupLookupField(field, input, fieldWrap); }\n else if (field.type === \"string-list\") { setupStringListField(field, input, fieldWrap); }\n else if (field.type !== \"boolean\") { fieldWrap.appendChild(input); }\n }\n fieldsWrap.appendChild(fieldWrap);\n });\n panelBody.appendChild(fieldsWrap);\n }\n \n if (panel.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n panel.actions.forEach((action) => { actions.appendChild(makeButton(action, plugin.id, panel.id, panelBody)); });\n panelBody.appendChild(actions);\n }\n wrap.appendChild(panelBody);\n });\n el.plugins.appendChild(wrap);\n });\n };\n \n const refreshContent = async () => {\n const categoriesPayload = await fetchJson(buildApiUrl(\"/api/home/categories\"));\n state.homeCategories = categoriesPayload.categories || [];\n if (!state.selectedHomeCategoryId || !state.homeCategories.some((item) => item.id === state.selectedHomeCategoryId)) {\n const overviewCategory = state.homeCategories.find((item) => item.id === \"overview\");\n state.selectedHomeCategoryId = overviewCategory ? overviewCategory.id : (state.homeCategories[0]?.id ?? null);\n }\n renderHomeCategories();\n \n const homePath = state.selectedHomeCategoryId ? \"/api/home?categoryId=\" + encodeURIComponent(state.selectedHomeCategoryId) : \"/api/home\";\n const [home, overview, plugins] = await Promise.all([ fetchJson(buildApiUrl(homePath)), fetchJson(buildApiUrl(\"/api/overview\")), fetchJson(buildApiUrl(\"/api/plugins\")) ]);\n \n renderHomeSections(home.sections || []);\n const showOverviewArea = state.selectedHomeCategoryId === \"overview\";\n el.overviewArea.style.display = showOverviewArea ? \"block\" : \"none\";\n renderCards(overview.cards || []);\n renderPlugins(plugins.plugins || []);\n };\n \n const loadInitialData = async () => {\n const session = await fetchJson(dashboardConfig.basePath + \"/api/session\");\n if (!session.authenticated) { window.location.href = dashboardConfig.basePath + \"/login\"; return; }\n \n state.session = session;\n el.userMeta.textContent = session.user.username + \" • \" + session.guildCount + \" guild(s)\";\n const guilds = await fetchJson(dashboardConfig.basePath + \"/api/guilds\");\n state.guilds = guilds.guilds || [];\n state.selectedGuildId = null;\n renderServerRail(); updateContextLabel();\n \n el.tabHome.addEventListener(\"click\", () => { state.activeMainTab = \"home\"; applyMainTab(); });\n el.tabPlugins.addEventListener(\"click\", () => { state.activeMainTab = \"plugins\"; applyMainTab(); });\n \n applyMainTab(); await refreshContent();\n };\n \n loadInitialData().catch((error) => { el.userMeta.textContent = \"Load failed\"; console.error(error); });\n `;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderCompactLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#0f1221\"};\n --rail: ${design.rail ?? \"#171a2d\"};\n --content-bg: ${design.contentBg ?? \"#0f1426\"};\n --panel: ${design.panel ?? \"#1f243b\"};\n --panel-2: ${design.panel2 ?? \"#2a314e\"};\n --text: ${design.text ?? \"#f5f7ff\"};\n --muted: ${design.muted ?? \"#aab1d6\"};\n --primary: ${design.primary ?? \"#7c87ff\"};\n --success: ${design.success ?? \"#2bd4a6\"};\n --warning: ${design.warning ?? \"#ffd166\"};\n --danger: ${design.danger ?? \"#ff6f91\"};\n --info: ${design.info ?? \"#66d9ff\"};\n --border: ${design.border ?? \"rgba(255, 255, 255, 0.12)\"};\n }\n * { box-sizing: border-box; }\n body {\n margin: 0;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;\n background: radial-gradient(circle at 0% 0%, #1b2140 0%, var(--bg) 45%);\n color: var(--text);\n }\n .shell { min-height: 100vh; display: grid; grid-template-rows: auto 1fr; }\n .topbar { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--border); background: rgba(15, 20, 38, 0.7); backdrop-filter: blur(8px); }\n .brand { font-weight: 800; letter-spacing: .2px; }\n .center-title { text-align: center; font-weight: 700; color: #d8defc; }\n .pill { justify-self: end; padding: 4px 8px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted); font-size: .75rem; }\n .layout { display: grid; grid-template-columns: 80px 1fr; min-height: 0; }\n .sidebar { border-right: 1px solid var(--border); background: linear-gradient(180deg, var(--rail), #121528); padding: 10px 0; }\n .server-rail { display: flex; flex-direction: column; align-items: center; gap: 10px; }\n .server-item { position: relative; width: 46px; height: 46px; border: none; border-radius: 14px; overflow: visible; background: var(--panel); color: #fff; font-weight: 700; transition: transform .15s ease, background .15s ease; }\n .server-item:hover { transform: translateY(-1px); background: #323b5f; }\n .server-item.active { background: var(--primary); transform: translateY(-2px); }\n .server-item-indicator { position: absolute; left: -8px; top: 50%; transform: translateY(-50%) scaleY(.5); width: 3px; height: 18px; background: #fff; border-radius: 999px; opacity: 0; transition: opacity .15s ease, transform .15s ease; }\n .server-item.active .server-item-indicator { opacity: 1; transform: translateY(-50%) scaleY(1); }\n .server-avatar { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }\n .server-fallback { display: grid; place-items: center; width: 100%; height: 100%; }\n .server-status { position: absolute; right: -3px; bottom: -3px; width: 11px; height: 11px; border-radius: 999px; border: 2px solid var(--rail); background: #35d489; }\n .server-status.offline { background: #7f8bb3; }\n .content { min-width: 0; padding: 12px; }\n .container { background: rgba(23, 28, 48, 0.6); border: 1px solid var(--border); border-radius: 12px; padding: 12px; }\n .main-tabs { display: flex; gap: 8px; margin-bottom: 10px; }\n button { border: 1px solid var(--border); background: var(--panel-2); color: var(--text); border-radius: 8px; padding: 7px 10px; }\n button.primary { background: var(--primary); border: none; }\n button.danger { background: #4a2230; border-color: rgba(255,111,145,.45); }\n .main-tab.active, .home-category-btn.active { background: var(--primary); border-color: transparent; }\n .section-title { margin: 12px 0 8px; color: #dce3ff; font-size: .95rem; }\n .grid { display: grid; gap: 10px; }\n .cards { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }\n .panel { background: linear-gradient(180deg, rgba(42,49,78,.7), rgba(31,36,59,.85)); border: 1px solid var(--border); border-radius: 10px; padding: 12px; }\n .title { color: var(--muted); font-size: .83rem; }\n .value { font-size: 1.25rem; font-weight: 800; margin-top: 5px; }\n .subtitle { margin-top: 5px; color: var(--muted); font-size: .8rem; }\n .home-categories { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }\n .home-sections { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 5px); max-width: calc(50% - 5px); }\n .home-width-33 { flex-basis: calc(33.333333% - 6.67px); max-width: calc(33.333333% - 6.67px); }\n .home-width-20 { flex-basis: calc(20% - 8px); max-width: calc(20% - 8px); }\n .home-fields, .plugin-fields { display: grid; gap: 8px; margin-top: 8px; }\n .home-field, .plugin-field { display: grid; gap: 5px; }\n .home-field label, .plugin-field > label { color: var(--muted); font-size: .8rem; }\n .home-input, .home-textarea, .home-select { width: 100%; border: 1px solid var(--border); background: var(--panel-2); color: var(--text); border-radius: 8px; padding: 7px 9px; }\n .home-textarea { min-height: 88px; resize: vertical; }\n .home-checkbox { width: 17px; height: 17px; }\n .home-field-row { display: flex; align-items: center; gap: 8px; }\n .home-message { margin-top: 6px; color: var(--muted); font-size: .8rem; }\n .lookup-wrap { position: relative; }\n .lookup-results { position: absolute; left: 0; right: 0; top: calc(100% + 5px); z-index: 20; border: 1px solid var(--border); background: #1f2742; border-radius: 8px; max-height: 220px; overflow: auto; display: none; }\n .lookup-item { width: 100%; border: none; border-radius: 0; text-align: left; padding: 8px 10px; background: transparent; }\n .lookup-item:hover { background: #2d3658; }\n .lookup-selected { margin-top: 5px; font-size: .8rem; color: var(--muted); }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }\n .kv-item { display: flex; justify-content: space-between; border: 1px solid var(--border); border-radius: 8px; padding: 7px 9px; background: var(--panel-2); }\n .list-editor { border: 1px solid var(--border); border-radius: 8px; background: var(--panel-2); padding: 8px; display: grid; gap: 8px; }\n .list-items { display: grid; gap: 6px; }\n .list-item { display: grid; grid-template-columns: auto 1fr auto; gap: 8px; align-items: center; border: 1px solid var(--border); border-radius: 8px; padding: 6px 8px; background: var(--panel); }\n .list-item.dragging { opacity: .6; }\n .drag-handle { color: var(--muted); user-select: none; font-size: .9rem; }\n .list-input { width: 100%; border: none; outline: none; background: transparent; color: var(--text); }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: .9rem; }\n .cursor-pointer { cursor: pointer; }\n @media (max-width: 980px) {\n .layout { grid-template-columns: 70px 1fr; }\n .home-width-50, .home-width-33, .home-width-20 { flex-basis: 100%; max-width: 100%; }\n }\n\n /* Inject User Custom CSS Here */\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"shell\">\n <header class=\"topbar\">\n <div class=\"brand\">${safeName}</div>\n <div id=\"centerTitle\" class=\"center-title\">User Dashboard</div>\n <div id=\"userMeta\" class=\"pill\">Loading...</div>\n </header>\n\n <div class=\"layout\">\n <aside class=\"sidebar\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </aside>\n\n <main class=\"content\">\n <div class=\"container\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">Plugins</button>\n </div>\n\n <section id=\"homeArea\">\n <div class=\"section-title\">Home</div>\n <section id=\"homeCategories\" class=\"home-categories\"></section>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">Dashboard Stats</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Plugins</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderDefaultLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n const scriptData = JSON.stringify({ basePath: context.basePath });\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#08090f\"};\n --content-bg: ${design.contentBg ?? \"radial-gradient(circle at top right, #171a2f, #08090f)\"};\n --panel: ${design.panel ?? \"rgba(20, 23, 43, 0.5)\"};\n --panel-2: ${design.panel2 ?? \"rgba(0, 0, 0, 0.4)\"};\n --text: ${design.text ?? \"#e0e6ff\"};\n --muted: ${design.muted ?? \"#8a93bc\"};\n --primary: ${design.primary ?? \"#5865F2\"};\n --primary-glow: rgba(88, 101, 242, 0.4);\n --success: ${design.success ?? \"#00E676\"};\n --danger: ${design.danger ?? \"#FF3D00\"};\n --border: ${design.border ?? \"rgba(255, 255, 255, 0.05)\"};\n }\n \n * { box-sizing: border-box; }\n \n body {\n margin: 0;\n padding: 24px; /* Creates the outer gap for the floating effect */\n height: 100vh;\n font-family: \"Inter\", \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n background: var(--content-bg); /* Applies gradient to entire screen */\n color: var(--text);\n overflow: hidden; \n }\n\n /* FLOATING ISLAND STRUCTURE */\n .layout { \n display: flex; \n gap: 24px; /* Gap between the floating islands */\n height: 100%; \n width: 100%; \n }\n \n /* Base styles for all floating windows */\n .floating-window {\n background: var(--panel);\n backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--border);\n border-radius: 24px;\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);\n }\n\n /* 1. SERVER RAIL */\n .sidebar { \n width: 84px; \n flex-shrink: 0; \n padding: 20px 0; \n z-index: 10;\n overflow-y: auto; \n scrollbar-width: none; \n /* Changed from flex to block to stop height crushing */\n display: block; \n }\n .sidebar::-webkit-scrollbar { display: none; } \n \n .server-rail { \n display: flex; \n flex-direction: column; \n align-items: center; \n gap: 16px; \n width: 100%; \n /* This tells the rail it is allowed to be taller than the screen */\n min-height: min-content; \n }\n \n .server-separator { \n width: 32px; \n height: 2px; \n min-height: 2px; /* Lock separator height */\n background: var(--border); \n border-radius: 2px; \n }\n \n .server-item { \n position: relative; \n /* The !important tags force the browser to respect the size */\n width: 54px !important; \n height: 54px !important; \n min-width: 54px !important; \n min-height: 54px !important; \n flex: 0 0 54px !important; \n \n border-radius: 16px; \n border: 1px solid transparent; \n background: var(--panel-2); \n color: var(--text); \n font-weight: 700; \n font-size: 16px; \n display: flex; \n align-items: center; \n justify-content: center; \n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n overflow: visible;\n }\n .server-item:hover { border-color: var(--primary); box-shadow: 0 0 15px var(--primary-glow); transform: translateY(-3px); }\n .server-item.active { background: var(--primary); border-color: var(--primary); box-shadow: 0 0 20px var(--primary-glow); color: #fff; }\n \n .server-item-indicator { position: absolute; left: -16px; width: 6px; height: 20px; border-radius: 4px; background: var(--primary); opacity: 0; transition: all 0.3s ease; box-shadow: 0 0 10px var(--primary-glow); }\n .server-item.active .server-item-indicator { opacity: 1; height: 32px; }\n \n .server-avatar { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }\n .server-fallback { font-weight: 700; font-size: 1rem; }\n .server-status { position: absolute; right: -4px; bottom: -4px; width: 16px; height: 16px; border-radius: 50%; border: 3px solid #141622; background: var(--success); z-index: 2; box-shadow: 0 0 10px var(--success); }\n .server-status.offline { background: var(--muted); box-shadow: none; border-color: transparent; }\n\n /* 2. SECONDARY SIDEBAR */\n .secondary-sidebar { \n width: 280px; flex-shrink: 0; \n display: flex; flex-direction: column; z-index: 9; \n overflow: hidden; /* Keeps user area inside border radius */\n }\n .sidebar-header { height: 72px; padding: 0 24px; display: flex; align-items: center; font-weight: 800; font-size: 18px; letter-spacing: 1px; text-transform: uppercase; background: linear-gradient(90deg, #fff, var(--primary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; border-bottom: 1px solid var(--border); flex-shrink: 0; }\n .sidebar-content { flex: 1; overflow-y: auto; padding: 20px 16px; scrollbar-width: none; }\n .sidebar-content::-webkit-scrollbar { display: none; }\n \n .category-header { font-size: 11px; font-weight: 800; color: var(--primary); text-transform: uppercase; margin: 24px 8px 8px; letter-spacing: 1.5px; opacity: 0.8; }\n .category-header:first-child { margin-top: 0; }\n \n .channel-btn { \n width: 100%; text-align: left; padding: 12px 16px; border-radius: 12px; background: transparent; border: 1px solid transparent; \n color: var(--muted); display: flex; align-items: center; gap: 12px; font-size: 14px; font-weight: 600; margin-bottom: 6px; transition: all 0.2s; \n }\n .channel-btn:hover { background: var(--panel-2); color: var(--text); transform: translateX(4px); }\n .channel-btn.active { background: rgba(88, 101, 242, 0.15); border-left: 4px solid var(--primary); color: #fff; transform: translateX(4px); }\n \n .user-area { height: 80px; background: rgba(0,0,0,0.2); padding: 0 20px; display: flex; align-items: center; flex-shrink: 0; gap: 14px; border-top: 1px solid var(--border); }\n .user-avatar-small { \n width: 44px; \n height: 44px; \n min-width: 44px; \n min-height: 44px; \n flex: 0 0 44px;\n \n border-radius: 14px; \n background: var(--primary); \n border: 2px solid var(--border); \n padding: 2px; \n }\n .user-details { display: flex; flex-direction: column; line-height: 1.4; }\n .user-details .name { color: #fff; font-size: 15px; font-weight: 700; }\n .user-details .sub { color: var(--primary); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; }\n\n /* 3. MAIN CONTENT */\n .content { flex: 1; display: flex; flex-direction: column; min-width: 0; gap: 24px; }\n \n /* Topbar is its own floating island now */\n .topbar { height: 72px; padding: 0 28px; display: flex; align-items: center; font-weight: 700; font-size: 20px; letter-spacing: 0.5px; z-index: 5; flex-shrink: 0; }\n \n /* Container background is transparent so inner panels float over the main gradient */\n .container { flex: 1; padding: 0 8px 0 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--primary) transparent; }\n .container::-webkit-scrollbar { width: 6px; }\n .container::-webkit-scrollbar-thumb { background: var(--primary); border-radius: 3px; }\n \n .section-title { font-size: 26px; font-weight: 800; margin: 0 0 24px 0; color: #fff; letter-spacing: -0.5px; }\n .subtitle { margin-top: 6px; color: var(--muted); font-size: 14px; line-height: 1.5; }\n \n .grid { display: grid; gap: 24px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }\n \n /* The content panels are also floating windows */\n .panel { \n background: var(--panel); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--border); border-radius: 20px; padding: 28px; \n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); transition: transform 0.3s, box-shadow 0.3s; \n }\n .panel:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(0,0,0,0.4), 0 0 20px rgba(88, 101, 242, 0.1); border-color: rgba(88, 101, 242, 0.3); }\n \n /* Inputs & Forms */\n .home-sections { display: flex; flex-wrap: wrap; gap: 24px; margin-bottom: 40px; }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 12px); max-width: calc(50% - 12px); }\n @media (max-width: 1300px) { .home-width-50 { flex-basis: 100%; max-width: 100%; } }\n \n .home-fields { display: grid; gap: 20px; margin-top: 24px; }\n .home-field { display: grid; gap: 10px; }\n .home-field label { color: var(--muted); font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; }\n \n .home-input, .home-textarea, .home-select { \n width: 100%; border: 1px solid var(--border); background: var(--panel-2); color: #fff; \n border-radius: 12px; padding: 14px 18px; font-size: 15px; outline: none; transition: all 0.3s ease; \n }\n .home-input:focus, .home-textarea:focus, .home-select:focus { \n border-color: var(--primary); box-shadow: 0 0 15px var(--primary-glow); background: rgba(0,0,0,0.6); \n }\n .home-textarea { min-height: 120px; resize: vertical; }\n \n .actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 28px; }\n button { \n border: 1px solid var(--border); background: rgba(255,255,255,0.05); color: #fff; \n border-radius: 10px; padding: 12px 24px; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; \n transition: all 0.2s; backdrop-filter: blur(4px);\n }\n button:hover { background: rgba(255,255,255,0.1); transform: translateY(-2px); }\n button.primary { background: linear-gradient(135deg, var(--primary), #7c88f9); border: none; box-shadow: 0 4px 15px var(--primary-glow); }\n button.primary:hover { box-shadow: 0 6px 20px var(--primary-glow); filter: brightness(1.1); }\n button.danger { background: linear-gradient(135deg, var(--danger), #ff7a59); border: none; box-shadow: 0 4px 15px rgba(255, 61, 0, 0.3); }\n .cursor-pointer { cursor: pointer; }\n\n .home-field-row { display: flex; align-items: center; gap: 12px; }\n .home-checkbox { width: 24px; height: 24px; accent-color: var(--primary); border-radius: 6px; }\n \n .lookup-wrap { position: relative; }\n .lookup-results { position: absolute; left: 0; right: 0; top: calc(100% + 8px); z-index: 20; background: #0e101a; border: 1px solid var(--primary); border-radius: 12px; max-height: 220px; overflow: auto; display: none; box-shadow: 0 10px 30px rgba(0,0,0,0.5), 0 0 20px var(--primary-glow); }\n .lookup-item { width: 100%; text-align: left; padding: 14px; background: transparent; border: none; border-bottom: 1px solid var(--border); color: var(--text); }\n .lookup-item:hover { background: var(--primary); color: #fff; }\n\n .value { font-size: 36px; font-weight: 800; background: linear-gradient(to right, #fff, #aab4ff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-top: 10px; }\n .title { color: var(--primary); font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: 1.5px; }\n\n .list-editor { background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 16px; display: grid; gap: 12px; }\n .list-item { display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center; background: rgba(0,0,0,0.5); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; transition: border-color 0.2s; }\n .list-item:hover { border-color: var(--primary); }\n .list-input { width: 100%; border: none; background: transparent; color: #fff; outline: none; font-size: 15px; }\n .drag-handle { color: var(--primary); font-size: 18px; }\n \n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"layout\">\n <nav class=\"sidebar floating-window\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </nav>\n\n <aside class=\"secondary-sidebar floating-window\">\n <header class=\"sidebar-header brand\">${safeName}</header>\n <div class=\"sidebar-content\">\n <div class=\"category-header\">System</div>\n <button id=\"tabHome\" class=\"channel-btn cursor-pointer active\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"></path><polyline points=\"9 22 9 12 15 12 15 22\"></polyline></svg>\n Dashboard\n </button>\n <button id=\"tabPlugins\" class=\"channel-btn cursor-pointer\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\"></path></svg>\n Extensions\n </button>\n \n <div class=\"category-header\" style=\"margin-top: 32px;\">Modules</div>\n <div id=\"homeCategories\"></div>\n </div>\n \n <div class=\"user-area\" id=\"userMeta\">\n <div class=\"user-avatar-small\"></div>\n <div class=\"user-details\">\n <span class=\"name\">Initializing...</span>\n <span class=\"sub\">Standby</span>\n </div>\n </div>\n </aside>\n\n <main class=\"content\">\n <header class=\"topbar floating-window\">\n <span id=\"centerTitle\" style=\"background: linear-gradient(90deg, #fff, var(--muted)); -webkit-background-clip: text; -webkit-text-fill-color: transparent;\">Interface Active</span>\n </header>\n\n <div class=\"container\">\n <section id=\"homeArea\">\n <div class=\"section-title\">Configuration Matrix</div>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\" style=\"margin-top: 24px;\">Telemetry Data</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Active Extensions</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n\n <script>\n const dashboardConfig = ${scriptData};\n const state = { session: null, guilds: [], selectedGuildId: null, homeCategories: [], selectedHomeCategoryId: null, activeMainTab: \"home\" };\n\n const el = {\n serverRail: document.getElementById(\"serverRail\"), userMeta: document.getElementById(\"userMeta\"),\n centerTitle: document.getElementById(\"centerTitle\"), tabHome: document.getElementById(\"tabHome\"),\n tabPlugins: document.getElementById(\"tabPlugins\"), homeArea: document.getElementById(\"homeArea\"),\n pluginsArea: document.getElementById(\"pluginsArea\"), homeCategories: document.getElementById(\"homeCategories\"),\n homeSections: document.getElementById(\"homeSections\"), overviewArea: document.getElementById(\"overviewArea\"),\n overviewCards: document.getElementById(\"overviewCards\"), plugins: document.getElementById(\"plugins\")\n };\n\n const fetchJson = async (url, init) => {\n const response = await fetch(url, init);\n if (!response.ok) throw new Error(await response.text());\n return response.json();\n };\n\n const buildApiUrl = (path) => {\n if (!state.selectedGuildId) return dashboardConfig.basePath + path;\n const separator = path.includes(\"?\") ? \"&\" : \"?\";\n return dashboardConfig.basePath + path + separator + \"guildId=\" + encodeURIComponent(state.selectedGuildId);\n };\n\n const escapeHtml = (value) => String(value).replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n const normalizeBoxWidth = (value) => [50, 33, 20].includes(Number(value)) ? Number(value) : 100;\n\n const makeButton = (action, pluginId, panelId, panelElement) => {\n const button = document.createElement(\"button\");\n button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n let payload = {};\n if (action.collectFields && panelElement) {\n const values = {};\n panelElement.querySelectorAll(\"[data-plugin-field-id]\").forEach((inputEl) => {\n const fieldId = inputEl.dataset.pluginFieldId;\n if (fieldId) values[fieldId] = toFieldValue({ type: inputEl.dataset.pluginFieldType || \"text\" }, inputEl);\n });\n payload = { panelId, values };\n }\n\n const result = await fetchJson(buildApiUrl(\"/api/plugins/\" + encodeURIComponent(pluginId) + \"/\" + encodeURIComponent(action.id)), {\n method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify(payload)\n });\n if (result.message) alert(result.message);\n if (result.refresh) await refreshContent();\n } catch (error) { alert(error instanceof Error ? error.message : \"Action failed\"); } \n finally { button.disabled = false; }\n });\n return button;\n };\n\n const renderCards = (cards) => {\n if (!cards.length) { el.overviewCards.innerHTML = '<div style=\"color:var(--muted)\">Awaiting telemetry...</div>'; return; }\n el.overviewCards.innerHTML = cards.map((card) => \n '<article class=\"panel\"><div class=\"title\">' + escapeHtml(card.title) + '</div><div class=\"value\">' + escapeHtml(card.value) + '</div>' + \n (card.subtitle ? '<div class=\"subtitle\">' + escapeHtml(card.subtitle) + '</div>' : \"\") + '</article>'\n ).join(\"\");\n };\n\n const shortName = (name) => {\n if (!name) return \"?\";\n const parts = String(name).trim().split(/\\\\s+/).filter(Boolean);\n return parts.length === 1 ? parts[0].slice(0, 2).toUpperCase() : (parts[0][0] + parts[1][0]).toUpperCase();\n };\n\n const addAvatarOrFallback = (button, item) => {\n if (!item.avatarUrl) {\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n return;\n }\n const avatar = document.createElement(\"img\");\n avatar.className = \"server-avatar\";\n avatar.src = item.avatarUrl;\n avatar.alt = item.name;\n avatar.addEventListener(\"error\", () => {\n avatar.remove();\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n });\n button.appendChild(avatar);\n };\n\n const renderServerRail = () => {\n const meItem = { id: null, name: \"Global Control\", short: \"HQ\", avatarUrl: state.session?.user?.avatarUrl ?? null, botInGuild: true };\n el.serverRail.innerHTML = \"\";\n \n const meBtn = document.createElement(\"button\");\n meBtn.className = \"server-item cursor-pointer\" + (null === state.selectedGuildId ? \" active\" : \"\");\n meBtn.title = meItem.name;\n \n const meInd = document.createElement(\"span\");\n meInd.className = \"server-item-indicator\";\n meBtn.appendChild(meInd);\n addAvatarOrFallback(meBtn, meItem);\n \n meBtn.addEventListener(\"click\", async () => {\n state.selectedGuildId = null;\n renderServerRail();\n updateContextLabel();\n await refreshContent();\n });\n \n el.serverRail.appendChild(meBtn);\n \n const sep = document.createElement(\"div\");\n sep.className = \"server-separator\";\n el.serverRail.appendChild(sep);\n\n state.guilds.forEach((guild) => {\n const item = { id: guild.id, name: guild.name, short: shortName(guild.name), avatarUrl: guild.iconUrl ?? null, botInGuild: guild.botInGuild !== false, inviteUrl: guild.inviteUrl };\n const button = document.createElement(\"button\");\n button.className = \"server-item cursor-pointer\" + (item.id === state.selectedGuildId ? \" active\" : \"\");\n button.title = item.id && !item.botInGuild ? (item.name + \" • Deploy Bot\") : item.name;\n\n const activeIndicator = document.createElement(\"span\");\n activeIndicator.className = \"server-item-indicator\";\n button.appendChild(activeIndicator);\n addAvatarOrFallback(button, item);\n\n if (item.id) {\n const status = document.createElement(\"span\");\n status.className = \"server-status\" + (item.botInGuild ? \"\" : \" offline\");\n button.appendChild(status);\n }\n\n button.addEventListener(\"click\", async () => {\n if (item.id && !item.botInGuild && item.inviteUrl) {\n const opened = window.open(item.inviteUrl, \"_blank\", \"noopener,noreferrer\");\n if (!opened) alert(\"Popup blocked. Please allow popups to open the deployment page.\");\n return;\n }\n state.selectedGuildId = item.id;\n renderServerRail();\n updateContextLabel();\n await refreshContent();\n });\n el.serverRail.appendChild(button);\n });\n };\n\n const applyMainTab = () => {\n const homeActive = state.activeMainTab === \"home\";\n el.homeArea.style.display = homeActive ? \"block\" : \"none\";\n el.pluginsArea.style.display = homeActive ? \"none\" : \"block\";\n el.tabHome.classList.toggle(\"active\", homeActive);\n el.tabPlugins.classList.toggle(\"active\", !homeActive);\n };\n\n const updateContextLabel = () => {\n if (!state.selectedGuildId) { el.centerTitle.textContent = \"Global Dashboard\"; return; }\n const selectedGuild = state.guilds.find((guild) => guild.id === state.selectedGuildId);\n el.centerTitle.textContent = selectedGuild ? selectedGuild.name : \"System Interface\";\n };\n\n const renderUserBlock = () => {\n const user = state.session?.user;\n if (!user) return;\n const avatarHtml = user.avatarUrl ? '<img src=\"'+user.avatarUrl+'\" style=\"width:100%;height:100%;border-radius:12px;object-fit:cover;\">' : '';\n el.userMeta.innerHTML = \n '<div class=\"user-avatar-small\">'+avatarHtml+'</div>' +\n '<div class=\"user-details\"><span class=\"name\">' + escapeHtml(user.global_name || user.username) + '</span><span class=\"sub\">' + state.guilds.length + ' Nodes</span></div>';\n };\n\n const renderHomeCategories = () => {\n el.homeCategories.innerHTML = \"\";\n if (!state.homeCategories.length) return;\n state.homeCategories.forEach((category) => {\n const button = document.createElement(\"button\");\n button.className = \"channel-btn cursor-pointer\" + (state.selectedHomeCategoryId === category.id ? \" active\" : \"\");\n button.innerHTML = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\"></rect><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\"></rect><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\"></rect><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\"></rect></svg> ' + escapeHtml(category.label);\n button.title = category.description || category.label;\n button.addEventListener(\"click\", async () => { state.selectedHomeCategoryId = category.id; renderHomeCategories(); await refreshContent(); });\n el.homeCategories.appendChild(button);\n });\n };\n\n const toFieldValue = (field, element) => {\n if (field.type === \"string-list\") { try { return JSON.parse(element.dataset.listValues || \"[]\"); } catch { return []; } }\n if ([\"role-search\", \"channel-search\", \"member-search\"].includes(field.type)) { try { return JSON.parse(element.dataset.selectedObject || \"null\"); } catch { return null; } }\n if (field.type === \"boolean\") return Boolean(element.checked);\n if (field.type === \"number\") return element.value === \"\" ? null : Number(element.value);\n return element.value;\n };\n\n const setupStringListField = (field, input, fieldWrap) => {\n const editor = document.createElement(\"div\"); editor.className = \"list-editor\";\n const itemsWrap = document.createElement(\"div\"); itemsWrap.className = \"list-items\";\n const addButton = document.createElement(\"button\"); addButton.type = \"button\"; addButton.className = \"list-add cursor-pointer\"; addButton.textContent = \"+ Add Sequence\";\n\n const normalizeValues = () => { input.dataset.listValues = JSON.stringify(Array.from(itemsWrap.querySelectorAll(\".list-input\")).map(i => i.value.trim()).filter(i => i.length > 0)); };\n\n const makeRow = (value = \"\") => {\n const row = document.createElement(\"div\"); row.className = \"list-item\"; row.draggable = true;\n const handle = document.createElement(\"span\"); handle.className = \"drag-handle cursor-pointer\"; handle.innerHTML = \"⣿\";\n const textInput = document.createElement(\"input\"); textInput.type = \"text\"; textInput.className = \"list-input\"; textInput.value = value;\n textInput.addEventListener(\"input\", normalizeValues);\n const removeBtn = document.createElement(\"button\"); removeBtn.type = \"button\"; removeBtn.className = \"cursor-pointer\"; removeBtn.textContent = \"×\"; removeBtn.style.padding = \"4px 8px\"; removeBtn.style.background = \"transparent\"; removeBtn.style.color = \"var(--danger)\";\n removeBtn.addEventListener(\"click\", () => { row.remove(); normalizeValues(); });\n row.append(handle, textInput, removeBtn);\n return row;\n };\n\n const initialValues = Array.isArray(field.value) ? field.value.map(String) : [];\n if (!initialValues.length) initialValues.push(\"Value_01\");\n initialValues.forEach(v => itemsWrap.appendChild(makeRow(v)));\n\n addButton.addEventListener(\"click\", () => { itemsWrap.appendChild(makeRow(\"\")); normalizeValues(); });\n editor.append(itemsWrap, addButton); fieldWrap.appendChild(editor); normalizeValues();\n };\n\n const showLookupResults = (container, items, labelResolver, onSelect) => {\n container.innerHTML = \"\";\n if (!items.length) { container.style.display = \"none\"; return; }\n items.forEach(item => {\n const btn = document.createElement(\"button\"); btn.type = \"button\"; btn.className = \"lookup-item cursor-pointer\"; btn.textContent = labelResolver(item);\n btn.addEventListener(\"click\", () => { onSelect(item); container.style.display = \"none\"; });\n container.appendChild(btn);\n });\n container.style.display = \"block\";\n };\n\n const setupLookupField = (field, input, fieldWrap) => {\n const wrap = document.createElement(\"div\"); wrap.className = \"lookup-wrap\";\n const results = document.createElement(\"div\"); results.className = \"lookup-results\";\n wrap.append(input, results); fieldWrap.appendChild(wrap);\n\n const runSearch = async () => {\n const query = String(input.value || \"\");\n if (query.length < 1) { results.style.display = \"none\"; return; }\n try {\n const params = new URLSearchParams({ q: query, limit: \"10\" });\n const ep = field.type === \"role-search\" ? \"roles\" : field.type === \"channel-search\" ? \"channels\" : \"members\";\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/\" + ep + \"?\" + params));\n \n showLookupResults(results, payload[ep] || [], \n (item) => field.type === \"channel-search\" ? \"#\"+item.name : (item.name || item.user?.username), \n (item) => { input.value = item.name || item.user?.username; input.dataset.selectedObject = JSON.stringify(item); }\n );\n } catch { results.style.display = \"none\"; }\n };\n input.addEventListener(\"input\", () => { input.dataset.selectedObject = \"\"; runSearch(); });\n input.addEventListener(\"blur\", () => setTimeout(() => { results.style.display = \"none\"; }, 150));\n };\n\n const renderHomeSections = (sections) => {\n el.homeSections.innerHTML = \"\";\n if (!sections.length) return;\n \n sections.forEach((section) => {\n const wrap = document.createElement(\"article\");\n wrap.className = \"panel home-section-panel home-width-\" + normalizeBoxWidth(section.width);\n\n const heading = document.createElement(\"h3\"); heading.textContent = section.title; heading.style.margin = \"0\"; wrap.appendChild(heading);\n if (section.description) { const desc = document.createElement(\"div\"); desc.className = \"subtitle\"; desc.textContent = section.description; wrap.appendChild(desc); }\n\n if (section.fields?.length) {\n const fieldsWrap = document.createElement(\"div\"); fieldsWrap.className = \"home-fields\";\n\n section.fields.forEach((field) => {\n const fieldWrap = document.createElement(\"div\"); fieldWrap.className = \"home-field\";\n const label = document.createElement(\"label\"); label.textContent = field.label; fieldWrap.appendChild(label);\n\n let input;\n if (field.type === \"textarea\") {\n input = document.createElement(\"textarea\"); input.className = \"home-textarea\"; input.value = field.value == null ? \"\" : String(field.value);\n } else if (field.type === \"select\") {\n input = document.createElement(\"select\"); input.className = \"home-select cursor-pointer\";\n (field.options || []).forEach((opt) => {\n const optionEl = document.createElement(\"option\"); optionEl.value = opt.value; optionEl.textContent = opt.label;\n if (String(field.value ?? \"\") === opt.value) optionEl.selected = true; input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\"); row.className = \"home-field-row\";\n input = document.createElement(\"input\"); input.type = \"checkbox\"; input.className = \"home-checkbox cursor-pointer\"; input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\"); stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\");\n row.append(input, stateText); fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\"); input.className = \"home-input\"; input.type = field.type === \"number\" ? \"number\" : field.type === \"url\" ? \"url\" : \"text\"; input.value = field.value == null ? \"\" : String(field.value);\n }\n\n if (input) {\n input.dataset.homeFieldId = field.id; input.dataset.homeFieldType = field.type;\n if (field.placeholder) input.placeholder = field.placeholder;\n if (field.readOnly) { input.readOnly = true; input.disabled = true; input.style.opacity = \"0.6\"; }\n if ([\"role-search\", \"channel-search\", \"member-search\"].includes(field.type)) setupLookupField(field, input, fieldWrap);\n else if (field.type === \"string-list\") setupStringListField(field, input, fieldWrap);\n else if (field.type !== \"boolean\") fieldWrap.appendChild(input);\n }\n fieldsWrap.appendChild(fieldWrap);\n });\n wrap.appendChild(fieldsWrap);\n }\n\n if (section.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n section.actions.forEach((action) => {\n const button = document.createElement(\"button\"); button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n const values = {};\n wrap.querySelectorAll(\"[data-home-field-id]\").forEach((el) => { values[el.dataset.homeFieldId] = toFieldValue({ type: el.dataset.homeFieldType }, el); });\n const result = await fetchJson(buildApiUrl(\"/api/home/\" + encodeURIComponent(action.id)), { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ sectionId: section.id, values }) });\n if (result.message) alert(result.message);\n if (result.refresh) await refreshContent();\n } catch (error) { alert(\"Execution Failed\"); } finally { button.disabled = false; }\n });\n actions.appendChild(button);\n });\n wrap.appendChild(actions);\n }\n el.homeSections.appendChild(wrap);\n });\n };\n\n const renderPlugins = (plugins) => {\n el.plugins.innerHTML = \"\";\n if (!plugins.length) { el.plugins.innerHTML = '<div style=\"color:var(--muted)\">No modules online.</div>'; return; }\n \n plugins.forEach((plugin) => {\n const wrap = document.createElement(\"article\"); wrap.className = \"panel\";\n const heading = document.createElement(\"h3\"); heading.textContent = plugin.name; heading.style.margin=\"0\"; wrap.appendChild(heading);\n if (plugin.description) { const desc = document.createElement(\"div\"); desc.className = \"subtitle\"; desc.textContent = plugin.description; wrap.appendChild(desc); }\n\n (plugin.panels || []).forEach((panel) => {\n const pBody = document.createElement(\"div\"); pBody.style.marginTop = \"24px\";\n const pTitle = document.createElement(\"h4\"); pTitle.textContent = panel.title; pTitle.style.margin = \"0 0 12px 0\"; pTitle.style.color = \"var(--primary)\"; pTitle.style.textTransform = \"uppercase\"; pBody.appendChild(pTitle);\n \n if (panel.fields?.length) {\n const fWrap = document.createElement(\"div\"); fWrap.className = \"home-fields\";\n panel.fields.forEach((field) => {\n const fw = document.createElement(\"div\"); fw.className = \"home-field\";\n if (!field.editable) {\n fw.innerHTML = '<label>' + escapeHtml(field.label) + '</label><div style=\"padding:14px 18px;background:var(--panel-2);border-radius:12px;border:1px solid var(--border);\">' + escapeHtml(field.value) + '</div>';\n } else {\n fw.innerHTML = '<label>' + escapeHtml(field.label) + '</label><input type=\"text\" class=\"home-input\" data-plugin-field-id=\"'+field.id+'\" value=\"'+escapeHtml(field.value)+'\">';\n }\n fWrap.appendChild(fw);\n });\n pBody.appendChild(fWrap);\n }\n\n if (panel.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n panel.actions.forEach((act) => actions.appendChild(makeButton(act, plugin.id, panel.id, pBody)));\n pBody.appendChild(actions);\n }\n wrap.appendChild(pBody);\n });\n el.plugins.appendChild(wrap);\n });\n };\n\n const refreshContent = async () => {\n const categoriesPayload = await fetchJson(buildApiUrl(\"/api/home/categories\"));\n state.homeCategories = categoriesPayload.categories || [];\n if (!state.selectedHomeCategoryId || !state.homeCategories.some(i => i.id === state.selectedHomeCategoryId)) {\n state.selectedHomeCategoryId = state.homeCategories.find(i => i.id === \"overview\")?.id || state.homeCategories[0]?.id || null;\n }\n renderHomeCategories();\n\n const homePath = state.selectedHomeCategoryId ? \"/api/home?categoryId=\" + encodeURIComponent(state.selectedHomeCategoryId) : \"/api/home\";\n const [home, overview, plugins] = await Promise.all([ fetchJson(buildApiUrl(homePath)), fetchJson(buildApiUrl(\"/api/overview\")), fetchJson(buildApiUrl(\"/api/plugins\")) ]);\n\n renderHomeSections(home.sections || []);\n el.overviewArea.style.display = state.selectedHomeCategoryId === \"overview\" ? \"block\" : \"none\";\n renderCards(overview.cards || []);\n renderPlugins(plugins.plugins || []);\n };\n\n const loadInitialData = async () => {\n const session = await fetchJson(dashboardConfig.basePath + \"/api/session\");\n if (!session.authenticated) { window.location.href = dashboardConfig.basePath + \"/login\"; return; }\n\n state.session = session;\n const guilds = await fetchJson(dashboardConfig.basePath + \"/api/guilds\");\n state.guilds = guilds.guilds || [];\n \n renderUserBlock();\n renderServerRail();\n updateContextLabel();\n\n el.tabHome.addEventListener(\"click\", () => { state.activeMainTab = \"home\"; applyMainTab(); });\n el.tabPlugins.addEventListener(\"click\", () => { state.activeMainTab = \"plugins\"; applyMainTab(); });\n\n applyMainTab();\n await refreshContent();\n };\n\n loadInitialData().catch(console.error);\n </script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderShadcnMagicLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#06070b\"};\n --surface: ${design.surface ?? \"rgba(10, 12, 18, 0.84)\"};\n --card: ${design.card ?? \"rgba(16, 20, 30, 0.82)\"};\n --card-2: ${design.card2 ?? \"rgba(23, 29, 42, 0.86)\"};\n --text: ${design.text ?? \"#f8fafc\"};\n --muted: ${design.muted ?? \"#94a3b8\"};\n --primary: ${design.primary ?? \"#c084fc\"};\n --accent: ${design.accent ?? \"#22d3ee\"};\n --border: ${design.border ?? \"rgba(148, 163, 184, 0.26)\"};\n --radius-lg: ${design.radiusLg ?? \"18px\"};\n --radius-md: ${design.radiusMd ?? \"12px\"};\n }\n\n * { box-sizing: border-box; }\n\n html, body { min-height: 100%; }\n\n body {\n margin: 0;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;\n color: var(--text);\n background:\n radial-gradient(circle at 8% 12%, rgba(192,132,252,.22), transparent 34%),\n radial-gradient(circle at 92% 8%, rgba(34,211,238,.18), transparent 36%),\n radial-gradient(circle at 76% 86%, rgba(244,114,182,.14), transparent 34%),\n var(--bg);\n }\n\n body::before {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n background-image:\n linear-gradient(rgba(148,163,184,.06) 1px, transparent 1px),\n linear-gradient(90deg, rgba(148,163,184,.06) 1px, transparent 1px);\n background-size: 24px 24px;\n mask-image: radial-gradient(circle at center, rgba(0,0,0,.9), transparent 72%);\n }\n\n .page {\n min-height: 100vh;\n width: 100%;\n padding: 0;\n }\n\n .shell {\n border: 1px solid var(--border);\n border-radius: 0;\n background: linear-gradient(180deg, rgba(8, 10, 14, .78), rgba(6, 8, 12, .72));\n backdrop-filter: blur(10px);\n min-height: 100vh;\n width: 100%;\n overflow: hidden;\n }\n\n .topbar {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto auto;\n gap: 12px;\n align-items: center;\n padding: 14px 16px;\n border-bottom: 1px solid var(--border);\n background: rgba(6, 8, 12, .66);\n }\n\n .brand {\n font-weight: 700;\n font-size: 1rem;\n letter-spacing: .2px;\n }\n\n .center-title {\n border: 1px solid var(--border);\n background: rgba(15, 23, 42, .44);\n border-radius: 999px;\n padding: 6px 12px;\n font-size: .82rem;\n color: #e2e8f0;\n }\n\n .pill {\n border: 1px solid var(--border);\n border-radius: 999px;\n padding: 6px 12px;\n font-size: .78rem;\n color: var(--muted);\n background: rgba(15, 23, 42, .52);\n }\n\n .server-strip {\n padding: 12px 16px;\n border-bottom: 1px solid var(--border);\n background: rgba(8, 12, 18, .48);\n }\n\n .server-rail {\n display: flex;\n gap: 10px;\n overflow-x: auto;\n padding-bottom: 2px;\n scrollbar-width: thin;\n }\n\n .server-item {\n position: relative;\n min-width: 210px;\n height: 60px;\n border-radius: var(--radius-md);\n border: 1px solid var(--border);\n background: linear-gradient(180deg, rgba(30, 41, 59, .74), rgba(15, 23, 42, .7));\n color: var(--text);\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n padding: 8px 72px 8px 56px;\n transition: transform .15s ease, border-color .15s ease, box-shadow .15s ease;\n }\n\n .server-item::before {\n content: attr(title);\n display: block;\n max-width: 100%;\n font-size: .82rem;\n font-weight: 600;\n line-height: 1.2;\n letter-spacing: .1px;\n color: #e2e8f0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .server-item:hover {\n transform: translateY(-2px);\n border-color: rgba(192,132,252,.72);\n }\n\n .server-item.active {\n border-color: rgba(34,211,238,.86);\n box-shadow: 0 0 0 1px rgba(34,211,238,.3), 0 10px 26px rgba(34,211,238,.17);\n }\n\n .server-item-indicator {\n position: absolute;\n left: 8px;\n top: 50%;\n transform: translateY(-50%);\n width: 4px;\n height: 0;\n border-radius: 999px;\n background: linear-gradient(180deg, var(--primary), var(--accent));\n opacity: 0;\n transition: opacity .15s ease, height .15s ease;\n }\n\n .server-item.active .server-item-indicator {\n opacity: 1;\n height: 32px;\n }\n\n .server-avatar {\n position: absolute;\n left: 16px;\n top: 50%;\n transform: translateY(-50%);\n width: 26px;\n height: 26px;\n border-radius: 8px;\n object-fit: cover;\n }\n\n .server-fallback {\n position: absolute;\n left: 16px;\n top: 50%;\n transform: translateY(-50%);\n width: 26px;\n height: 26px;\n border-radius: 8px;\n display: grid;\n place-items: center;\n background: rgba(148,163,184,.18);\n font-size: .75rem;\n }\n\n .server-status {\n position: absolute;\n right: 12px;\n top: 50%;\n transform: translateY(-50%);\n width: 9px;\n height: 9px;\n border-radius: 999px;\n background: #22c55e;\n }\n\n .server-status::after {\n position: absolute;\n right: 16px;\n top: 50%;\n transform: translateY(-50%);\n font-size: .7rem;\n font-weight: 500;\n letter-spacing: .15px;\n color: #86efac;\n white-space: nowrap;\n }\n\n .server-status.offline {\n background: #94a3b8;\n }\n\n .server-status.offline::after {\n content: \"Invite Bot\";\n color: #fda4af;\n }\n\n .content {\n padding: 16px;\n }\n\n .container {\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n background: var(--surface);\n padding: 14px;\n }\n\n .main-tabs {\n display: inline-flex;\n gap: 8px;\n padding: 5px;\n border: 1px solid var(--border);\n border-radius: 999px;\n background: rgba(15, 23, 42, .4);\n margin-bottom: 14px;\n }\n\n button {\n border: 1px solid var(--border);\n background: var(--card-2);\n color: var(--text);\n border-radius: 999px;\n padding: 7px 13px;\n }\n\n button.primary {\n border: none;\n color: #0b1020;\n font-weight: 700;\n background: linear-gradient(90deg, rgba(192,132,252,.95), rgba(34,211,238,.95));\n }\n\n button.danger {\n background: rgba(190,24,93,.2);\n border-color: rgba(244,114,182,.45);\n }\n\n .main-tab.active,\n .home-category-btn.active {\n background: rgba(34,211,238,.2);\n border-color: rgba(34,211,238,.62);\n }\n\n .section-title {\n margin: 10px 0 9px;\n font-size: .92rem;\n color: #e2e8f0;\n letter-spacing: .15px;\n }\n\n .grid { display: grid; gap: 10px; }\n .cards { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }\n\n .panel {\n border: 1px solid var(--border);\n border-radius: var(--radius-md);\n background: var(--card);\n padding: 12px;\n }\n\n .title { color: var(--muted); font-size: .82rem; }\n .value { margin-top: 6px; font-size: 1.34rem; font-weight: 700; }\n .subtitle { margin-top: 6px; color: var(--muted); font-size: .8rem; }\n\n .home-categories,\n .home-sections {\n display: flex;\n gap: 10px;\n flex-wrap: wrap;\n margin-bottom: 10px;\n }\n\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 5px); max-width: calc(50% - 5px); }\n .home-width-33 { flex-basis: calc(33.333333% - 6.67px); max-width: calc(33.333333% - 6.67px); }\n .home-width-20 { flex-basis: calc(20% - 8px); max-width: calc(20% - 8px); }\n\n .home-fields,\n .plugin-fields { display: grid; gap: 9px; margin-top: 8px; }\n .home-field,\n .plugin-field { display: grid; gap: 5px; }\n .home-field label,\n .plugin-field > label { color: var(--muted); font-size: .8rem; }\n\n .home-input,\n .home-textarea,\n .home-select {\n width: 100%;\n border: 1px solid var(--border);\n background: var(--card-2);\n color: var(--text);\n border-radius: 10px;\n padding: 8px 10px;\n }\n\n .home-textarea { min-height: 90px; resize: vertical; }\n .home-checkbox { width: 17px; height: 17px; }\n .home-field-row { display: flex; align-items: center; gap: 8px; }\n .home-message { margin-top: 6px; color: var(--muted); font-size: .8rem; }\n\n .lookup-wrap { position: relative; }\n .lookup-results {\n position: absolute;\n left: 0;\n right: 0;\n top: calc(100% + 6px);\n z-index: 20;\n border: 1px solid var(--border);\n background: rgba(15, 23, 42, .96);\n border-radius: 10px;\n max-height: 220px;\n overflow: auto;\n display: none;\n }\n\n .lookup-item {\n width: 100%;\n border: none;\n border-radius: 0;\n text-align: left;\n padding: 8px 10px;\n background: transparent;\n }\n\n .lookup-item:hover { background: rgba(51,65,85,.56); }\n .lookup-selected { margin-top: 5px; font-size: .8rem; color: var(--muted); }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 10px; }\n\n .kv-item {\n display: flex;\n justify-content: space-between;\n border: 1px solid var(--border);\n border-radius: 10px;\n padding: 7px 10px;\n background: var(--card-2);\n }\n\n .list-editor {\n border: 1px solid var(--border);\n border-radius: 10px;\n background: var(--card-2);\n padding: 8px;\n display: grid;\n gap: 8px;\n }\n\n .list-items { display: grid; gap: 6px; }\n\n .list-item {\n display: grid;\n grid-template-columns: auto 1fr auto;\n gap: 8px;\n align-items: center;\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 6px 8px;\n background: rgba(23, 29, 42, .78);\n }\n\n .list-item.dragging { opacity: .6; }\n .drag-handle { color: var(--muted); user-select: none; font-size: .9rem; }\n .list-input { width: 100%; border: none; outline: none; background: transparent; color: var(--text); }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: .9rem; }\n .cursor-pointer { cursor: pointer; }\n\n @media (max-width: 980px) {\n .topbar { grid-template-columns: 1fr auto; }\n .center-title { display: none; }\n .server-item { min-width: 180px; }\n .home-width-50,\n .home-width-33,\n .home-width-20 { flex-basis: 100%; max-width: 100%; }\n }\n\n /* Inject User Custom CSS Here */\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"page\">\n <div class=\"shell\">\n <header class=\"topbar\">\n <div class=\"brand\">${safeName}</div>\n <div id=\"centerTitle\" class=\"center-title\">Workspace Hub</div>\n <div id=\"userMeta\" class=\"pill\">Loading...</div>\n </header>\n\n <section class=\"server-strip\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </section>\n\n <main class=\"content\">\n <section class=\"container\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">Plugins</button>\n </div>\n\n <section id=\"homeArea\">\n <div class=\"section-title\">Dashboard Workspace</div>\n <section id=\"homeCategories\" class=\"home-categories\"></section>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">Overview Metrics</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Plugin Center</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </section>\n </main>\n </div>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderer } from \"../Types\";\nimport { renderCompactLayout } from \"./layouts/compact\";\nimport { renderDefaultLayout } from \"./layouts/default\";\nimport { renderShadcnMagicLayout } from \"./layouts/shadcn-magic\";\n\nexport const BuiltinLayouts = {\n default: renderDefaultLayout,\n compact: renderCompactLayout,\n \"shadcn-magic\": renderShadcnMagicLayout,\n} satisfies Record<string, DashboardTemplateRenderer>;\n","import type { DashboardDesignConfig } from \"../../Types\";\n\nexport const BuiltinThemes = {\n default: {\n bg: \"#08090f\",\n rail: \"#0e101a\",\n contentBg: \"radial-gradient(circle at top right, #171a2f, #08090f)\",\n panel: \"rgba(20, 23, 43, 0.6)\",\n panel2: \"rgba(0, 0, 0, 0.3)\",\n text: \"#e0e6ff\",\n muted: \"#8a93bc\",\n primary: \"#5865F2\",\n success: \"#00E676\",\n warning: \"#FFD600\",\n danger: \"#FF3D00\",\n border: \"rgba(88, 101, 242, 0.2)\",\n },\n compact: {\n bg: \"#0f1221\",\n rail: \"#171a2d\",\n contentBg: \"#0f1426\",\n panel: \"#1f243b\",\n panel2: \"#2a314e\",\n text: \"#f5f7ff\",\n muted: \"#aab1d6\",\n primary: \"#7c87ff\",\n success: \"#2bd4a6\",\n warning: \"#ffd166\",\n danger: \"#ff6f91\",\n info: \"#66d9ff\",\n border: \"rgba(255, 255, 255, 0.12)\",\n },\n \"shadcn-magic\": {\n bg: \"#06070b\",\n surface: \"rgba(10, 12, 18, 0.84)\",\n card: \"rgba(16, 20, 30, 0.82)\",\n card2: \"rgba(23, 29, 42, 0.86)\",\n text: \"#f8fafc\",\n muted: \"#94a3b8\",\n primary: \"#c084fc\",\n accent: \"#22d3ee\",\n border: \"rgba(148, 163, 184, 0.26)\",\n radiusLg: \"18px\",\n radiusMd: \"12px\",\n },\n} satisfies Record<string, DashboardDesignConfig>;\n","import { BuiltinLayouts } from \"../templates\";\nimport { BuiltinThemes } from \"../templates/themes\";\nimport type { DashboardDesignConfig, DashboardOptions, DashboardTemplateRenderContext, DashboardTemplateRenderer } from \"../Types\";\n\nexport class TemplateManager {\n private layoutRenderer: DashboardTemplateRenderer;\n private resolvedDesign: DashboardDesignConfig;\n\n constructor(options: DashboardOptions) {\n this.layoutRenderer = this.resolveLayout(options.uiTemplate);\n this.resolvedDesign = this.resolveTheme(options.uiTheme, options.setupDesign);\n }\n\n private resolveLayout(layoutInput?: DashboardOptions[\"uiTemplate\"]): DashboardTemplateRenderer {\n if (typeof layoutInput === \"function\") {\n return layoutInput;\n }\n\n if (typeof layoutInput === \"string\" && BuiltinLayouts[layoutInput]) {\n return BuiltinLayouts[layoutInput];\n }\n\n return BuiltinLayouts[\"default\"];\n }\n\n private resolveTheme(themeInput?: DashboardOptions[\"uiTheme\"], customOverrides?: DashboardDesignConfig): DashboardDesignConfig {\n let baseTheme: DashboardDesignConfig = {};\n\n if (typeof themeInput === \"string\" && BuiltinThemes[themeInput]) {\n baseTheme = BuiltinThemes[themeInput];\n } else if (typeof themeInput === \"object\") {\n baseTheme = themeInput;\n }\n\n const merged: DashboardDesignConfig = {\n ...baseTheme,\n ...customOverrides,\n };\n\n const combinedCss = [baseTheme.customCss, customOverrides?.customCss].filter((css) => css && css.trim().length > 0).join(\"\\n\\n\");\n\n if (combinedCss) {\n merged.customCss = combinedCss;\n }\n\n return merged;\n }\n\n public render(contextBase: Omit<DashboardTemplateRenderContext, \"setupDesign\">): string {\n const finalContext: DashboardTemplateRenderContext = {\n ...contextBase,\n setupDesign: this.resolvedDesign,\n };\n\n return this.layoutRenderer(finalContext);\n }\n}\n","import { DiscordHelpers } from \"../handlers/DiscordHelpers\";\nimport { TemplateManager } from \"../handlers/TemplateManager\";\nimport type { DashboardGuild, DashboardOptions, DashboardTemplateRenderer, DashboardUser } from \"../Types\";\n\nexport class DashboardEngine {\n public helpers: DiscordHelpers;\n private templateManager: TemplateManager;\n private DISCORD_API = \"https://discord.com/api/v10\";\n\n constructor(public options: DashboardOptions) {\n this.helpers = new DiscordHelpers(options.botToken);\n this.templateManager = new TemplateManager(options);\n }\n\n public getAuthUrl(state: string): string {\n const scope = this.options.scopes && this.options.scopes.length > 0 ? this.options.scopes.join(\" \") : [\"identify\", \"guilds\"].join(\" \");\n const query = new URLSearchParams({\n client_id: this.options.clientId,\n redirect_uri: this.options.redirectUri,\n response_type: \"code\",\n scope,\n state,\n prompt: \"none\",\n });\n return `https://discord.com/oauth2/authorize?${query.toString()}`;\n }\n\n public async exchangeCode(code: string): Promise<{ access_token: string; refresh_token?: string; expires_in?: number }> {\n const response = await fetch(`${this.DISCORD_API}/oauth2/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret,\n grant_type: \"authorization_code\",\n code,\n redirect_uri: this.options.redirectUri,\n }),\n });\n\n if (!response.ok) throw new Error(`Failed token exchange: ${response.status} ${await response.text()}`);\n return await response.json();\n }\n\n public async fetchUser(token: string): Promise<DashboardUser> {\n const res = await fetch(`${this.DISCORD_API}/users/@me`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) throw new Error(`Failed to fetch user: ${res.status} ${await res.text()}`);\n return await res.json();\n }\n\n public async fetchGuilds(token: string): Promise<DashboardGuild[]> {\n const res = await fetch(`${this.DISCORD_API}/users/@me/guilds`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) throw new Error(`Failed to fetch guilds: ${res.status} ${await res.text()}`);\n return await res.json();\n }\n\n public render(basePath: string): string {\n return this.templateManager.render({\n dashboardName: this.options.dashboardName ?? \"Discord Dashboard\",\n basePath,\n });\n }\n}\n","import type { DashboardAction, DashboardCategory, DashboardDesignConfig, DashboardField, DashboardOptions, DashboardTemplateRenderer } from \"../Types\";\n\nexport class CategoryBuilder {\n public data: DashboardCategory;\n\n constructor(id: string, label: string) {\n this.data = { id, label };\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n}\n\nexport class SectionBuilder {\n public data: any = { fields: [], actions: [] };\n\n constructor(\n public id: string,\n public categoryId: string,\n public title: string,\n ) {\n this.data.id = id;\n this.data.categoryId = categoryId;\n this.data.title = title;\n this.data.width = 100;\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n\n public setWidth(width: 20 | 33 | 50 | 100) {\n this.data.width = width;\n return this;\n }\n\n public addField(field: DashboardField) {\n this.data.fields.push(field);\n return this;\n }\n\n public addAction(id: string, label: string, options?: { variant?: \"primary\" | \"danger\" | \"default\"; collectFields?: boolean }, handler?: DashboardAction[\"handler\"]) {\n this.data.actions.push({ id, label, ...options, handler });\n return this;\n }\n}\n\nexport class PanelBuilder {\n public data: any = { fields: [], actions: [] };\n\n constructor(\n public id: string,\n public title: string,\n ) {\n this.data.id = id;\n this.data.title = title;\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n\n public addField(field: DashboardField) {\n this.data.fields.push(field);\n return this;\n }\n\n public addAction(id: string, label: string, options?: { variant?: \"primary\" | \"danger\" | \"default\"; collectFields?: boolean }, handler?: DashboardAction[\"handler\"]) {\n this.data.actions.push({ id, label, ...options, handler });\n return this;\n }\n}\n\nexport class PluginBuilder {\n public data: any = { panels: [] };\n\n constructor(\n public id: string,\n public name: string,\n ) {\n this.data.id = id;\n this.data.name = name;\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n\n public addPanel(panel: PanelBuilder) {\n this.data.panels.push(panel.data);\n return this;\n }\n}\n\nexport class DashboardDesigner {\n private config: Partial<DashboardOptions> = {\n setupDesign: {},\n uiTemplates: {},\n };\n\n private categories: DashboardCategory[] = [];\n private sections: any[] = [];\n private plugins: any[] = [];\n private overviewCards: { title: string; value: string; subtitle?: string }[] = [];\n\n constructor(dashboardName?: string) {\n if (dashboardName) {\n this.config.dashboardName = dashboardName;\n }\n }\n\n public setLayout(template: DashboardOptions[\"uiTemplate\"]) {\n this.config.uiTemplate = template;\n return this;\n }\n\n public setTheme(theme: DashboardOptions[\"uiTheme\"]) {\n this.config.uiTheme = theme;\n return this;\n }\n\n public setColors(colors: DashboardDesignConfig) {\n this.config.setupDesign = { ...this.config.setupDesign, ...colors };\n return this;\n }\n\n public setCustomCss(css: string) {\n this.config.setupDesign!.customCss = css;\n return this;\n }\n\n public registerCustomLayout(name: string, renderer: DashboardTemplateRenderer) {\n this.config.uiTemplates![name] = renderer;\n return this;\n }\n\n // --- Content Building Methods ---\n\n public addOverviewCard(title: string, value: string, subtitle?: string) {\n this.overviewCards.push({ title, value, subtitle });\n return this;\n }\n\n public addCategory(category: CategoryBuilder) {\n this.categories.push(category.data);\n return this;\n }\n\n public addSection(section: SectionBuilder) {\n this.sections.push(section.data);\n return this;\n }\n\n public addPlugin(plugin: PluginBuilder) {\n this.plugins.push(plugin.data);\n return this;\n }\n public build(): Partial<DashboardOptions> {\n return {\n ...this.config,\n dashboardData: {\n categories: this.categories,\n sections: this.sections,\n plugins: this.plugins,\n overviewCards: this.overviewCards,\n },\n };\n }\n}\n","import compression from \"compression\";\nimport express, { Router, type Express } from \"express\";\nimport session from \"express-session\";\nimport helmet from \"helmet\";\nimport { randomBytes } from \"node:crypto\";\nimport { DashboardEngine } from \"../core/DashboardEngine\";\nimport type { DashboardContext, DashboardOptions } from \"../Types\";\n\nexport function createExpressAdapter(options: DashboardOptions) {\n const engine = new DashboardEngine(options);\n const app = (options.app as Express) ?? express();\n const router = Router();\n const basePath = options.basePath ?? \"/dashboard\";\n\n router.use(compression());\n router.use(helmet({ contentSecurityPolicy: false }));\n router.use(express.json());\n router.use(\n session({\n secret: options.sessionSecret,\n resave: false,\n saveUninitialized: false,\n name: options.sessionName ?? \"discord_dashboard.sid\",\n }),\n );\n\n const buildContext = (req: express.Request): DashboardContext => ({\n user: req.session.discordAuth!.user,\n guilds: req.session.discordAuth!.guilds || [],\n accessToken: req.session.discordAuth!.accessToken || \"\",\n selectedGuildId: ((req.query as any).guildId as string) || undefined,\n helpers: engine.helpers,\n });\n\n router.get(\"/\", (req, res) => {\n if (!req.session.discordAuth) return res.redirect(`${basePath}/login`);\n res.type(\"html\").send(engine.render(basePath));\n });\n\n router.get(\"/login\", (req, res) => {\n const state = randomBytes(16).toString(\"hex\");\n req.session.oauthState = state;\n res.redirect(engine.getAuthUrl(state));\n });\n\n router.get(\"/callback\", async (req, res) => {\n const { code, state } = req.query;\n if (state !== req.session.oauthState) return res.status(403).send(\"Invalid OAuth2 state\");\n\n try {\n const tokens = await engine.exchangeCode(code as string);\n const [user, rawGuilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n const ADMIN = 8n;\n const MANAGE_GUILD = 32n;\n\n const processedGuilds = rawGuilds\n .filter((guild) => {\n const perms = BigInt(guild.permissions || \"0\");\n return (perms & ADMIN) === ADMIN || (perms & MANAGE_GUILD) === MANAGE_GUILD;\n })\n .map((guild) => ({\n ...guild,\n iconUrl: engine.helpers.getGuildIconUrl(guild.id, guild.icon),\n botInGuild: options.client.guilds.cache.has(guild.id),\n }));\n\n req.session.discordAuth = {\n accessToken: tokens.access_token,\n user: {\n ...user,\n avatarUrl: engine.helpers.getUserAvatarUrl(user.id, user.avatar),\n },\n guilds: processedGuilds,\n };\n\n res.redirect(basePath);\n } catch (error) {\n console.error(\"Dashboard Auth Error:\", error);\n res.redirect(`${basePath}/login`);\n }\n });\n\n router.get(\"/api/session\", (req, res) => {\n if (!req.session.discordAuth) return res.json({ authenticated: false });\n res.json({\n authenticated: true,\n user: req.session.discordAuth.user,\n guildCount: req.session.discordAuth.guilds.length,\n });\n });\n\n router.get(\"/api/guilds\", (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n res.json({ guilds: req.session.discordAuth.guilds });\n });\n\n router.get(\"/api/overview\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const cards = options.getOverviewCards ? await options.getOverviewCards(buildContext(req)) : [];\n res.json({ cards });\n });\n\n router.get(\"/api/home/categories\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const categories = options.home?.getCategories ? await options.home.getCategories(buildContext(req)) : [];\n res.json({ categories });\n });\n\n router.get(\"/api/home\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const sections = options.home?.getSections ? await options.home.getSections(buildContext(req)) : [];\n\n const categoryId = req.query.categoryId as string;\n const filtered = categoryId ? sections.filter((s) => s.categoryId === categoryId) : sections;\n res.json({ sections: filtered });\n });\n\n router.post(\"/api/home/:actionId\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const actionId = req.params.actionId;\n const actionFn = options.home?.actions?.[actionId];\n\n if (!actionFn) return res.status(404).json({ error: \"Action not found\" });\n\n try {\n const result = await actionFn(buildContext(req), req.body);\n res.json(result);\n } catch (error) {\n res.status(500).json({ error: error instanceof Error ? error.message : \"Action failed\" });\n }\n });\n\n router.get(\"/api/plugins\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n\n const context = buildContext(req);\n\n const resolvedPlugins = await Promise.all(\n (options.plugins || []).map(async (p) => {\n return {\n id: p.id,\n name: p.name,\n description: p.description,\n panels: p.getPanels ? await p.getPanels(context) : p.panels || [],\n };\n }),\n );\n\n res.json({ plugins: resolvedPlugins });\n });\n\n router.post(\"/api/plugins/:pluginId/:actionId\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n\n const { pluginId, actionId } = req.params;\n\n const plugin = options.plugins?.find((p) => p.id === pluginId);\n\n if (!plugin || !plugin.actions?.[actionId]) {\n return res.status(404).json({ error: \"Plugin or action not found\" });\n }\n\n try {\n const context = buildContext(req);\n const result = await plugin.actions[actionId](context, req.body);\n res.json(result);\n } catch (error) {\n console.error(`Action Error (${pluginId}/${actionId}):`, error);\n res.status(500).json({ error: \"Internal Server Error\" });\n }\n });\n\n app.use(basePath, router);\n return { app, engine };\n}\n","import jwt from \"@elysiajs/jwt\";\nimport node from \"@elysiajs/node\";\nimport { Elysia } from \"elysia\";\nimport { randomBytes } from \"node:crypto\";\nimport { DashboardEngine } from \"../core/DashboardEngine\";\nimport { SessionSchema, type DashboardContext, type DashboardOptions, type HomeActionPayload, type SessionData } from \"../Types\";\n\nexport function createElysiaAdapter(options: DashboardOptions) {\n const engine = new DashboardEngine(options);\n const basePath = options.basePath ?? \"/dashboard\";\n const sessionName = options.sessionName ?? \"discord_dashboard.sid\";\n\n const app = (options.app as Elysia) ?? new Elysia({ adapter: node() });\n\n const getSession = async (sessionSigner: any, cookie: any): Promise<SessionData | null> => {\n const cookieValue = cookie[sessionName]?.value as string | undefined;\n if (!cookieValue) return null;\n const session = await sessionSigner.verify(cookieValue);\n return session ?? null;\n };\n\n const setSession = async (sessionSigner: any, cookie: any, session: SessionData): Promise<void> => {\n const token = await sessionSigner.sign(session);\n cookie[sessionName].set({\n value: token,\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n });\n };\n\n const buildContext = (session: SessionData, query?: Record<string, unknown>): DashboardContext => ({\n user: session.discordAuth!.user,\n guilds: (session.discordAuth!.guilds || []).map((guild) => ({\n ...guild,\n botInGuild: guild.botInGuild ?? undefined,\n inviteUrl: guild.inviteUrl ?? undefined,\n })),\n accessToken: session.discordAuth!.accessToken || \"\",\n selectedGuildId: typeof query?.guildId === \"string\" ? query.guildId : undefined,\n helpers: engine.helpers,\n });\n\n const router = new Elysia({ prefix: basePath })\n .use(\n jwt({\n name: \"sessionSigner\",\n secret: options.sessionSecret,\n schema: SessionSchema,\n }),\n )\n .get(\"/\", async ({ sessionSigner, cookie, redirect }) => {\n const sessionData = await getSession(sessionSigner, cookie);\n if (!sessionData || !sessionData.discordAuth) return redirect(`${basePath}/login`);\n\n return new Response(engine.render(basePath), {\n headers: {\n \"content-type\": \"text/html; charset=utf-8\",\n },\n });\n })\n .get(\"/login\", async ({ sessionSigner, cookie, redirect }) => {\n const state = randomBytes(16).toString(\"hex\");\n\n await setSession(sessionSigner, cookie, { oauthState: state });\n\n return redirect(engine.getAuthUrl(state));\n })\n .get(\"/callback\", async ({ query, sessionSigner, cookie, redirect }) => {\n const { code, state } = query as { code?: string; state?: string; error?: string };\n const session = await getSession(sessionSigner, cookie);\n\n if (!code || !state || !session || state !== session.oauthState) return redirect(`${basePath}/login`);\n\n try {\n const tokens = await engine.exchangeCode(code);\n const [user, rawGuilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n const ADMIN = 8n;\n const MANAGE_GUILD = 32n;\n\n const processedGuilds = rawGuilds\n .filter((guild) => {\n const perms = BigInt(guild.permissions || \"0\");\n return (perms & ADMIN) === ADMIN || (perms & MANAGE_GUILD) === MANAGE_GUILD;\n })\n .map((guild) => ({\n id: guild.id,\n name: guild.name,\n icon: guild.icon,\n owner: guild.owner,\n permissions: guild.permissions,\n iconUrl: engine.helpers.getGuildIconUrl(guild.id, guild.icon),\n botInGuild: options.client.guilds.cache.has(guild.id),\n }));\n\n await setSession(sessionSigner, cookie, {\n discordAuth: {\n accessToken: tokens.access_token,\n user: {\n id: user.id,\n username: user.username,\n discriminator: user.discriminator,\n avatar: user.avatar,\n global_name: user.global_name,\n avatarUrl: engine.helpers.getUserAvatarUrl(user.id, user.avatar),\n },\n guilds: processedGuilds,\n },\n });\n\n return redirect(basePath);\n } catch (error) {\n console.error(\"Dashboard Auth Error:\", error);\n return redirect(`${basePath}/login`);\n }\n })\n .get(\"/api/session\", async ({ sessionSigner, cookie }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) return { authenticated: false };\n\n return {\n authenticated: true,\n user: session.discordAuth.user,\n guildCount: session.discordAuth.guilds.length,\n };\n })\n .get(\"/api/guilds\", async ({ sessionSigner, cookie, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n return { guilds: session.discordAuth.guilds };\n })\n .get(\"/api/overview\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const cards = options.getOverviewCards ? await options.getOverviewCards(buildContext(session, query as Record<string, unknown>)) : [];\n return { cards };\n })\n .get(\"/api/home/categories\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const categories = options.home?.getCategories ? await options.home.getCategories(buildContext(session, query as Record<string, unknown>)) : [];\n return { categories };\n })\n .get(\"/api/home\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const context = buildContext(session, query as Record<string, unknown>);\n const sections = options.home?.getSections ? await options.home.getSections(context) : [];\n const categoryId = typeof (query as Record<string, unknown>).categoryId === \"string\"\n ? ((query as Record<string, unknown>).categoryId as string)\n : undefined;\n const filteredSections = categoryId ? sections.filter((section) => section.categoryId === categoryId) : sections;\n\n return { sections: filteredSections };\n })\n .post(\"/api/home/:actionId\", async ({ params, body, sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const actionFn = options.home?.actions?.[params.actionId];\n if (!actionFn) {\n set.status = 404;\n return { error: \"Action not found\" };\n }\n\n try {\n return await actionFn(buildContext(session, query as Record<string, unknown>), body as HomeActionPayload);\n } catch (error) {\n set.status = 500;\n return { error: error instanceof Error ? error.message : \"Action failed\" };\n }\n })\n .get(\"/api/plugins\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const context = buildContext(session, query as Record<string, unknown>);\n const resolvedPlugins = await Promise.all(\n (options.plugins || []).map(async (plugin) => ({\n id: plugin.id,\n name: plugin.name,\n description: plugin.description,\n panels: plugin.getPanels ? await plugin.getPanels(context) : plugin.panels || [],\n })),\n );\n\n return { plugins: resolvedPlugins };\n })\n .post(\"/api/plugins/:pluginId/:actionId\", async ({ params, body, sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const plugin = options.plugins?.find((item) => item.id === params.pluginId);\n if (!plugin || !plugin.actions?.[params.actionId]) {\n set.status = 404;\n return { error: \"Plugin or action not found\" };\n }\n\n try {\n return await plugin.actions[params.actionId](buildContext(session, query as Record<string, unknown>), body);\n } catch (error) {\n console.error(`Action Error (${params.pluginId}/${params.actionId}):`, error);\n set.status = 500;\n return { error: \"Internal Server Error\" };\n }\n });\n\n app.use(router);\n\n return { app, engine };\n}\n","import { randomBytes } from \"crypto\";\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport type { DashboardContext, DashboardOptions, HomeActionPayload } from \"../Types\";\nimport { DashboardEngine } from \"../core/DashboardEngine\";\n\nexport function createFastifyAdapter(fastify: FastifyInstance, options: DashboardOptions) {\n const engine = new DashboardEngine(options);\n const basePath = options.basePath ?? \"/dashboard\";\n\n const buildContext = (request: FastifyRequest): DashboardContext => {\n const query = request.query as Record<string, unknown>;\n\n return {\n user: request.session.discordAuth!.user,\n guilds: request.session.discordAuth!.guilds || [],\n accessToken: request.session.discordAuth!.accessToken || \"\",\n selectedGuildId: typeof query.guildId === \"string\" ? query.guildId : undefined,\n helpers: engine.helpers,\n };\n };\n\n fastify.register(\n async (instance) => {\n instance.get(\"/\", async (request, reply) => {\n if (!request.session.discordAuth) return reply.redirect(`${basePath}/login`);\n return reply.type(\"html\").send(engine.render(basePath));\n });\n\n instance.get(\"/login\", async (request, reply) => {\n const state = randomBytes(16).toString(\"hex\");\n request.session.oauthState = state;\n return reply.redirect(engine.getAuthUrl(state));\n });\n\n instance.get(\"/callback\", async (request: FastifyRequest<{ Querystring: { code: string; state: string } }>, reply) => {\n const { code, state } = request.query;\n if (state !== request.session.oauthState) return reply.status(403).send(\"Invalid OAuth2 state\");\n\n try {\n const tokens = await engine.exchangeCode(code);\n const [user, rawGuilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n const ADMIN = 8n;\n const MANAGE_GUILD = 32n;\n\n const processedGuilds = rawGuilds\n .filter((guild) => {\n const perms = BigInt(guild.permissions || \"0\");\n return (perms & ADMIN) === ADMIN || (perms & MANAGE_GUILD) === MANAGE_GUILD;\n })\n .map((guild) => ({\n ...guild,\n iconUrl: engine.helpers.getGuildIconUrl(guild.id, guild.icon),\n botInGuild: options.client.guilds.cache.has(guild.id),\n }));\n\n request.session.discordAuth = {\n accessToken: tokens.access_token,\n user: {\n ...user,\n avatarUrl: engine.helpers.getUserAvatarUrl(user.id, user.avatar),\n },\n guilds: processedGuilds,\n };\n\n return reply.redirect(basePath);\n } catch (error) {\n console.error(\"Dashboard Auth Error:\", error);\n return reply.redirect(`${basePath}/login`);\n }\n });\n\n instance.get(\"/api/session\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.send({ authenticated: false });\n }\n\n return reply.send({\n authenticated: true,\n user: request.session.discordAuth.user,\n guildCount: request.session.discordAuth.guilds.length,\n });\n });\n\n instance.get(\"/api/guilds\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n return reply.send({ guilds: request.session.discordAuth.guilds });\n });\n\n instance.get(\"/api/overview\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const cards = options.getOverviewCards ? await options.getOverviewCards(buildContext(request)) : [];\n return reply.send({ cards });\n });\n\n instance.get(\"/api/home/categories\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const categories = options.home?.getCategories ? await options.home.getCategories(buildContext(request)) : [];\n return reply.send({ categories });\n });\n\n instance.get(\"/api/home\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const sections = options.home?.getSections ? await options.home.getSections(buildContext(request)) : [];\n const query = request.query as Record<string, unknown>;\n const categoryId = typeof query.categoryId === \"string\" ? query.categoryId : undefined;\n const filteredSections = categoryId ? sections.filter((section) => section.categoryId === categoryId) : sections;\n\n return reply.send({ sections: filteredSections });\n });\n\n instance.post(\"/api/home/:actionId\", async (request: FastifyRequest<{ Params: { actionId: string }; Body: unknown }>, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const actionFn = options.home?.actions?.[request.params.actionId];\n if (!actionFn) {\n return reply.status(404).send({ error: \"Action not found\" });\n }\n\n try {\n const result = await actionFn(buildContext(request), request.body as HomeActionPayload);\n return reply.send(result);\n } catch (error) {\n return reply.status(500).send({ error: error instanceof Error ? error.message : \"Action failed\" });\n }\n });\n\n instance.get(\"/api/plugins\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const context = buildContext(request);\n const resolvedPlugins = await Promise.all(\n (options.plugins || []).map(async (plugin) => ({\n id: plugin.id,\n name: plugin.name,\n description: plugin.description,\n panels: plugin.getPanels ? await plugin.getPanels(context) : plugin.panels || [],\n })),\n );\n\n return reply.send({ plugins: resolvedPlugins });\n });\n\n instance.post(\n \"/api/plugins/:pluginId/:actionId\",\n async (request: FastifyRequest<{ Params: { pluginId: string; actionId: string }; Body: unknown }>, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const { pluginId, actionId } = request.params;\n const plugin = options.plugins?.find((item) => item.id === pluginId);\n\n if (!plugin || !plugin.actions?.[actionId]) {\n return reply.status(404).send({ error: \"Plugin or action not found\" });\n }\n\n try {\n const result = await plugin.actions[actionId](buildContext(request), request.body as Record<string, unknown>);\n return reply.send(result);\n } catch (error) {\n console.error(`Action Error (${pluginId}/${actionId}):`, error);\n return reply.status(500).send({ error: \"Internal Server Error\" });\n }\n },\n );\n },\n { prefix: basePath },\n );\n\n return { engine };\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmkB1C;;;ACpkBA,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,QAAQ;AAC1I;AAEO,SAAS,oBAAoB,SAAiD;AACnF,QAAM,WAAW,WAAW,QAAQ,aAAa;AACjD,QAAM,SAAS,QAAQ,eAAe,CAAC;AAEvC,QAAM,eAAe,gBAAgB,QAAQ,UAAU,MAAM;AAE7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,QAAQ;AAAA;AAAA;AAAA;AAAA,cAIL,OAAO,MAAM,SAAS;AAAA,gBACpB,OAAO,QAAQ,SAAS;AAAA,sBAClB,OAAO,aAAa,SAAS;AAAA,iBAClC,OAAO,SAAS,SAAS;AAAA,mBACvB,OAAO,UAAU,SAAS;AAAA,gBAC7B,OAAO,QAAQ,SAAS;AAAA,iBACvB,OAAO,SAAS,SAAS;AAAA,mBACvB,OAAO,WAAW,SAAS;AAAA,mBAC3B,OAAO,WAAW,SAAS;AAAA,mBAC3B,OAAO,WAAW,SAAS;AAAA,kBAC5B,OAAO,UAAU,SAAS;AAAA,gBAC5B,OAAO,QAAQ,SAAS;AAAA,kBACtB,OAAO,UAAU,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA4ExD,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAMD,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAqCvB,YAAY;AAAA;AAAA;AAGxB;;;AC1JA,SAASA,YAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,QAAQ;AAC1I;AAEO,SAAS,oBAAoB,SAAiD;AACnF,QAAM,WAAWA,YAAW,QAAQ,aAAa;AACjD,QAAM,SAAS,QAAQ,eAAe,CAAC;AACvC,QAAM,aAAa,KAAK,UAAU,EAAE,UAAU,QAAQ,SAAS,CAAC;AAEhE,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,aAKI,QAAQ;AAAA;AAAA;AAAA;AAAA,gBAIL,OAAO,MAAM,SAAS;AAAA,wBACd,OAAO,aAAa,wDAAwD;AAAA,mBACjF,OAAO,SAAS,uBAAuB;AAAA,qBACrC,OAAO,UAAU,oBAAoB;AAAA,kBACxC,OAAO,QAAQ,SAAS;AAAA,mBACvB,OAAO,SAAS,SAAS;AAAA,qBACvB,OAAO,WAAW,SAAS;AAAA;AAAA,qBAE3B,OAAO,WAAW,SAAS;AAAA,oBAC5B,OAAO,UAAU,SAAS;AAAA,oBAC1B,OAAO,UAAU,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA8MxD,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDAUqB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAkD7B,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAqYxC,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;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","names":["escapeHtml","escapeHtml","express","compression","helmet","session","import_elysia","import_node_crypto","node","session","jwt"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/Types/elysia.ts","../src/handlers/DiscordHelpers.ts","../src/templates/scripts/client.ts","../src/templates/layouts/aurora.ts","../src/templates/layouts/compact.ts","../src/templates/layouts/cyberpunk.ts","../src/templates/layouts/default.ts","../src/templates/layouts/hologram.ts","../src/templates/layouts/nebula.ts","../src/templates/layouts/shadcn-magic.ts","../src/templates/index.ts","../src/templates/themes/index.ts","../src/handlers/TemplateManager.ts","../src/core/DashboardEngine.ts","../src/handlers/DashboardDesigner.ts","../src/adapters/express.ts","../src/adapters/elysia.ts","../src/adapters/fastify.ts"],"sourcesContent":["export * from \"./Types\";\n\nexport { DashboardEngine } from \"./core/DashboardEngine\";\nexport { DashboardDesigner } from \"./handlers/DashboardDesigner\";\n\nexport { createExpressAdapter } from \"./adapters/express\";\nexport { createElysiaAdapter } from \"./adapters/elysia\";\nexport { createFastifyAdapter } from \"./adapters/fastify\";\n","import { Static, t } from \"elysia\";\n\nconst UserSchema = t.Object({\n id: t.String(),\n username: t.String(),\n discriminator: t.String(),\n avatar: t.Nullable(t.String()),\n global_name: t.Optional(t.Nullable(t.String())),\n avatarUrl: t.Optional(t.Nullable(t.String())),\n});\n\nconst GuildSchema = t.Object({\n id: t.String(),\n name: t.String(),\n icon: t.Nullable(t.String()),\n owner: t.Boolean(),\n permissions: t.String(),\n iconUrl: t.Optional(t.Nullable(t.String())),\n botInGuild: t.Optional(t.Boolean()),\n inviteUrl: t.Optional(t.String()),\n});\n\nexport const SessionSchema = t.Object({\n oauthState: t.Optional(t.String()),\n discordAuth: t.Optional(\n t.Object({\n accessToken: t.String(),\n user: UserSchema,\n guilds: t.Array(GuildSchema),\n }),\n ),\n});\n\nexport type SessionData = Static<typeof SessionSchema>;\n","import type { DashboardDiscordHelpers, DiscordChannel, DiscordMember, DiscordRole } from \"../Types\";\n\nexport class DiscordHelpers implements DashboardDiscordHelpers {\n private readonly botToken: string;\n private readonly DISCORD_API = \"https://discord.com/api/v10\";\n\n constructor(botToken: string) {\n this.botToken = botToken;\n }\n\n private async fetchDiscordWithBot<T>(path: string): Promise<T | null> {\n const response = await fetch(`${this.DISCORD_API}${path}`, {\n headers: {\n Authorization: `Bot ${this.botToken}`,\n },\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as T;\n }\n\n public async getChannel(channelId: string): Promise<DiscordChannel | null> {\n return await this.fetchDiscordWithBot<DiscordChannel>(`/channels/${channelId}`);\n }\n\n public async getGuildChannels(guildId: string): Promise<DiscordChannel[]> {\n return (await this.fetchDiscordWithBot<DiscordChannel[]>(`/guilds/${guildId}/channels`)) ?? [];\n }\n\n public async searchGuildChannels(guildId: string, query: string, options?: { limit?: number; nsfw?: boolean; channelTypes?: number[] }): Promise<DiscordChannel[]> {\n const channels = (await this.fetchDiscordWithBot<DiscordChannel[]>(`/guilds/${guildId}/channels`)) ?? [];\n const normalizedQuery = query.trim().toLowerCase();\n const limit = Math.max(1, Math.min(options?.limit ?? 10, 50));\n\n return channels\n .filter((channel) => {\n if (options?.nsfw !== undefined && Boolean(channel.nsfw) !== options.nsfw) {\n return false;\n }\n\n if (options?.channelTypes && options.channelTypes.length > 0 && !options.channelTypes.includes(channel.type)) {\n return false;\n }\n\n if (!normalizedQuery) {\n return true;\n }\n\n return channel.name.toLowerCase().includes(normalizedQuery);\n })\n .slice(0, limit);\n }\n\n public async getRole(guildId: string, roleId: string): Promise<DiscordRole | null> {\n const roles = await this.fetchDiscordWithBot<DiscordRole[]>(`/guilds/${guildId}/roles`);\n if (!roles) {\n return null;\n }\n\n return roles.find((role) => role.id === roleId) ?? null;\n }\n\n public async getGuildRoles(guildId: string): Promise<DiscordRole[]> {\n return (await this.fetchDiscordWithBot<DiscordRole[]>(`/guilds/${guildId}/roles`)) ?? [];\n }\n\n public async searchGuildRoles(guildId: string, query: string, options?: { limit?: number; includeManaged?: boolean }): Promise<DiscordRole[]> {\n const roles = (await this.fetchDiscordWithBot<DiscordRole[]>(`/guilds/${guildId}/roles`)) ?? [];\n const normalizedQuery = query.trim().toLowerCase();\n const limit = Math.max(1, Math.min(options?.limit ?? 10, 50));\n\n return roles\n .filter((role) => {\n if (!options?.includeManaged && role.managed) {\n return false;\n }\n\n if (!normalizedQuery) {\n return true;\n }\n\n return role.name.toLowerCase().includes(normalizedQuery);\n })\n .sort((a, b) => b.position - a.position)\n .slice(0, limit);\n }\n\n public async searchGuildMembers(guildId: string, query: string, options?: { limit?: number }): Promise<DiscordMember[]> {\n const limit = Math.max(1, Math.min(options?.limit ?? 10, 1000));\n const params = new URLSearchParams({\n query: query.trim(),\n limit: String(limit),\n });\n\n return (await this.fetchDiscordWithBot<DiscordMember[]>(`/guilds/${guildId}/members/search?${params.toString()}`)) ?? [];\n }\n\n public async getGuildMember(guildId: string, userId: string): Promise<DiscordMember | null> {\n return await this.fetchDiscordWithBot<DiscordMember>(`/guilds/${guildId}/members/${userId}`);\n }\n\n public getGuildIconUrl(guildId: string, iconHash: string | null): string | null {\n if (!iconHash) return null;\n\n const extension = iconHash.startsWith(\"a_\") ? \"gif\" : \"png\";\n return `https://cdn.discordapp.com/icons/${guildId}/${iconHash}.${extension}?size=128`;\n }\n\n public getUserAvatarUrl(userId: string, avatarHash: string | null): string | null {\n if (!avatarHash) return null;\n const extension = avatarHash.startsWith(\"a_\") ? \"gif\" : \"png\";\n return `https://cdn.discordapp.com/avatars/${userId}/${avatarHash}.${extension}?size=128`;\n }\n}\n","export function getClientScript(basePath: string, setupDesign: any = {}): string {\n const scriptData = JSON.stringify({ basePath, setupDesign });\n\n return `\n const dashboardConfig = ${scriptData};\n const state = {\n session: null,\n guilds: [],\n selectedGuildId: null,\n homeCategories: [],\n selectedHomeCategoryId: null,\n activeMainTab: \"home\"\n };\n \n const el = {\n serverRail: document.getElementById(\"serverRail\"),\n userMeta: document.getElementById(\"userMeta\"),\n centerTitle: document.getElementById(\"centerTitle\"),\n tabHome: document.getElementById(\"tabHome\"),\n tabPlugins: document.getElementById(\"tabPlugins\"),\n homeArea: document.getElementById(\"homeArea\"),\n pluginsArea: document.getElementById(\"pluginsArea\"),\n homeCategories: document.getElementById(\"homeCategories\"),\n homeSections: document.getElementById(\"homeSections\"),\n overviewArea: document.getElementById(\"overviewArea\"),\n overviewCards: document.getElementById(\"overviewCards\"),\n plugins: document.getElementById(\"plugins\")\n };\n \n const fetchJson = async (url, init) => {\n const response = await fetch(url, init);\n if (!response.ok) throw new Error(await response.text());\n return response.json();\n };\n \n const buildApiUrl = (path) => {\n if (!state.selectedGuildId) return dashboardConfig.basePath + path;\n const separator = path.includes(\"?\") ? \"&\" : \"?\";\n return dashboardConfig.basePath + path + separator + \"guildId=\" + encodeURIComponent(state.selectedGuildId);\n };\n \n const escapeHtml = (value) => String(value)\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n \n const normalizeBoxWidth = (value) => {\n const numeric = Number(value);\n if (numeric === 50 || numeric === 33 || numeric === 20) return numeric;\n return 100;\n };\n \n const makeButton = (action, pluginId, panelId, panelElement) => {\n const button = document.createElement(\"button\");\n button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n \n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n let payload = {};\n if (action.collectFields && panelElement) {\n const values = {};\n const inputs = panelElement.querySelectorAll(\"[data-plugin-field-id]\");\n inputs.forEach((inputEl) => {\n const fieldId = inputEl.dataset.pluginFieldId;\n const fieldType = inputEl.dataset.pluginFieldType || \"text\";\n if (!fieldId) return;\n values[fieldId] = toFieldValue({ type: fieldType }, inputEl);\n });\n payload = { panelId, values };\n }\n \n const actionUrl = buildApiUrl(\"/api/plugins/\" + encodeURIComponent(pluginId) + \"/\" + encodeURIComponent(action.id));\n const result = await fetchJson(actionUrl, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(payload)\n });\n if (result.message) alert(result.message);\n if (result.refresh) await refreshContent();\n } catch (error) {\n alert(error instanceof Error ? error.message : \"Action failed\");\n } finally {\n button.disabled = false;\n }\n });\n return button;\n };\n \n const renderCards = (cards) => {\n if (!cards.length) {\n el.overviewCards.innerHTML = '<div class=\"empty\">No cards configured yet.</div>';\n return;\n }\n el.overviewCards.innerHTML = cards.map((card) => {\n const subtitle = card.subtitle ? '<div class=\"subtitle\">' + escapeHtml(card.subtitle) + '</div>' : \"\";\n return '<article class=\"panel\">'\n + '<div class=\"title\">' + escapeHtml(card.title) + '</div>'\n + '<div class=\"value\">' + escapeHtml(card.value) + '</div>'\n + subtitle\n + '</article>';\n }).join(\"\");\n };\n \n const shortName = (name) => {\n if (!name) return \"?\";\n const parts = String(name).trim().split(/\\\\s+/).filter(Boolean);\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return (parts[0][0] + parts[1][0]).toUpperCase();\n };\n \n const addAvatarOrFallback = (button, item) => {\n if (!item.avatarUrl) {\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n return;\n }\n const avatar = document.createElement(\"img\");\n avatar.className = \"server-avatar\";\n avatar.src = item.avatarUrl;\n avatar.alt = item.name;\n avatar.addEventListener(\"error\", () => {\n avatar.remove();\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n });\n button.appendChild(avatar);\n };\n \n const renderServerRail = () => {\n const items = [{ id: null, name: \"User Dashboard\", short: \"ME\", avatarUrl: state.session?.user?.avatarUrl ?? null, botInGuild: true }].concat(\n state.guilds.map((guild) => ({\n id: guild.id,\n name: guild.name,\n short: shortName(guild.name),\n avatarUrl: guild.iconUrl ?? null,\n botInGuild: guild.botInGuild !== false,\n inviteUrl: guild.inviteUrl\n }))\n );\n \n el.serverRail.innerHTML = \"\";\n items.forEach((item) => {\n const button = document.createElement(\"button\");\n button.className = \"server-item cursor-pointer\" + (item.id === state.selectedGuildId ? \" active\" : \"\");\n button.title = item.id && !item.botInGuild ? (item.name + \" • Invite bot\") : item.name;\n \n const activeIndicator = document.createElement(\"span\");\n activeIndicator.className = \"server-item-indicator\";\n button.appendChild(activeIndicator);\n \n addAvatarOrFallback(button, item);\n \n if (item.id) {\n const status = document.createElement(\"span\");\n status.className = \"server-status\" + (item.botInGuild ? \"\" : \" offline\");\n button.appendChild(status);\n }\n \n button.addEventListener(\"click\", async () => {\n if (item.id && !item.botInGuild && item.inviteUrl) {\n const opened = window.open(item.inviteUrl, \"_blank\", \"noopener,noreferrer\");\n if (!opened && typeof alert === \"function\") alert(\"Popup blocked. Please allow popups.\");\n return;\n }\n state.selectedGuildId = item.id;\n renderServerRail();\n updateContextLabel();\n await refreshContent();\n });\n el.serverRail.appendChild(button);\n });\n };\n \n const applyMainTab = () => {\n const homeActive = state.activeMainTab === \"home\";\n el.homeArea.style.display = homeActive ? \"block\" : \"none\";\n el.pluginsArea.style.display = homeActive ? \"none\" : \"block\";\n el.tabHome.className = \"main-tab cursor-pointer\" + (homeActive ? \" active\" : \"\");\n el.tabPlugins.className = \"main-tab cursor-pointer\" + (!homeActive ? \" active\" : \"\");\n };\n \n const updateContextLabel = () => {\n if (!state.selectedGuildId) {\n el.centerTitle.textContent = \"User Dashboard\";\n return;\n }\n const selectedGuild = state.guilds.find((guild) => guild.id === state.selectedGuildId);\n el.centerTitle.textContent = selectedGuild ? (selectedGuild.name + \" Dashboard\") : \"Server Dashboard\";\n };\n \n const renderHomeCategories = () => {\n if (!state.homeCategories.length) {\n el.homeCategories.innerHTML = \"\";\n return;\n }\n el.homeCategories.innerHTML = \"\";\n state.homeCategories.forEach((category) => {\n const button = document.createElement(\"button\");\n button.className = \"home-category-btn cursor-pointer\" + (state.selectedHomeCategoryId === category.id ? \" active\" : \"\");\n button.textContent = category.label;\n button.title = category.description || category.label;\n button.addEventListener(\"click\", async () => {\n state.selectedHomeCategoryId = category.id;\n renderHomeCategories();\n await refreshContent();\n });\n el.homeCategories.appendChild(button);\n });\n };\n \n const toFieldValue = (field, element) => {\n if (field.type === \"string-list\") {\n const raw = element.dataset.listValues;\n if (!raw) return [];\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch { return []; }\n }\n if (field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\") {\n const raw = element.dataset.selectedObject;\n if (!raw) return null;\n try { return JSON.parse(raw); } catch { return null; }\n }\n if (field.type === \"boolean\") return Boolean(element.checked);\n if (field.type === \"number\") return element.value === \"\" ? null : Number(element.value);\n return element.value;\n };\n \n const setupStringListField = (field, input, fieldWrap) => {\n const editor = document.createElement(\"div\");\n editor.className = \"list-editor\";\n const itemsWrap = document.createElement(\"div\");\n itemsWrap.className = \"list-items\";\n const addButton = document.createElement(\"button\");\n addButton.type = \"button\";\n addButton.className = \"list-add cursor-pointer\";\n addButton.textContent = \"Add Button\";\n \n const normalizeValues = () => {\n const values = Array.from(itemsWrap.querySelectorAll(\".list-input\")).map((item) => item.value.trim()).filter((item) => item.length > 0);\n input.dataset.listValues = JSON.stringify(values);\n };\n \n const makeRow = (value = \"\") => {\n const row = document.createElement(\"div\");\n row.className = \"list-item\";\n row.draggable = true;\n const handle = document.createElement(\"span\");\n handle.className = \"drag-handle cursor-pointer\";\n handle.textContent = \"⋮⋮\";\n const textInput = document.createElement(\"input\");\n textInput.type = \"text\";\n textInput.className = \"list-input\";\n textInput.value = value;\n textInput.placeholder = \"Button label\";\n textInput.addEventListener(\"input\", normalizeValues);\n const removeButton = document.createElement(\"button\");\n removeButton.type = \"button\";\n removeButton.className = \"cursor-pointer\";\n removeButton.textContent = \"×\";\n removeButton.addEventListener(\"click\", () => { row.remove(); normalizeValues(); });\n \n row.addEventListener(\"dragstart\", () => row.classList.add(\"dragging\"));\n row.addEventListener(\"dragend\", () => { row.classList.remove(\"dragging\"); normalizeValues(); });\n \n row.appendChild(handle); row.appendChild(textInput); row.appendChild(removeButton);\n return row;\n };\n \n itemsWrap.addEventListener(\"dragover\", (event) => {\n event.preventDefault();\n const dragging = itemsWrap.querySelector(\".dragging\");\n if (!dragging) return;\n const siblings = Array.from(itemsWrap.querySelectorAll(\".list-item:not(.dragging)\"));\n let inserted = false;\n for (const sibling of siblings) {\n const rect = sibling.getBoundingClientRect();\n if (event.clientY < rect.top + rect.height / 2) {\n itemsWrap.insertBefore(dragging, sibling);\n inserted = true;\n break;\n }\n }\n if (!inserted) itemsWrap.appendChild(dragging);\n });\n \n const initialValues = Array.isArray(field.value) ? field.value.map((item) => String(item)) : [];\n if (initialValues.length === 0) initialValues.push(\"Yes\", \"No\");\n initialValues.forEach((value) => itemsWrap.appendChild(makeRow(value)));\n \n addButton.addEventListener(\"click\", () => { itemsWrap.appendChild(makeRow(\"\")); normalizeValues(); });\n editor.appendChild(itemsWrap); editor.appendChild(addButton); fieldWrap.appendChild(editor);\n normalizeValues();\n };\n \n const showLookupResults = (container, items, labelResolver, onSelect) => {\n container.innerHTML = \"\";\n if (!items.length) { container.style.display = \"none\"; return; }\n items.forEach((item) => {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"lookup-item cursor-pointer\";\n btn.textContent = labelResolver(item);\n btn.addEventListener(\"click\", () => { onSelect(item); container.style.display = \"none\"; });\n container.appendChild(btn);\n });\n container.style.display = \"block\";\n };\n \n const setupLookupField = (field, input, fieldWrap) => {\n const wrap = document.createElement(\"div\");\n wrap.className = \"lookup-wrap\";\n const results = document.createElement(\"div\");\n results.className = \"lookup-results\";\n const selected = document.createElement(\"div\");\n selected.className = \"lookup-selected\";\n selected.textContent = \"No selection\";\n \n wrap.appendChild(input); wrap.appendChild(results); fieldWrap.appendChild(wrap); fieldWrap.appendChild(selected);\n \n const minQueryLength = Math.max(0, field.lookup?.minQueryLength ?? 1);\n const limit = Math.max(1, Math.min(field.lookup?.limit ?? 10, 50));\n \n const runSearch = async () => {\n const query = String(input.value || \"\");\n if (query.length < minQueryLength) { results.style.display = \"none\"; return; }\n try {\n if (field.type === \"role-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n if (field.lookup?.includeManaged !== undefined) params.set(\"includeManaged\", String(Boolean(field.lookup.includeManaged)));\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/roles?\" + params.toString()));\n showLookupResults(results, payload.roles || [], (item) => \"@\" + item.name, (item) => {\n input.value = item.name;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected role: @\" + item.name + \" (\" + item.id + \")\";\n });\n } else if (field.type === \"channel-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n if (field.lookup?.nsfw !== undefined) params.set(\"nsfw\", String(Boolean(field.lookup.nsfw)));\n if (field.lookup?.channelTypes && field.lookup.channelTypes.length > 0) params.set(\"channelTypes\", field.lookup.channelTypes.join(\",\"));\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/channels?\" + params.toString()));\n showLookupResults(results, payload.channels || [], (item) => \"#\" + item.name, (item) => {\n input.value = item.name;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected channel: #\" + item.name + \" (\" + item.id + \")\";\n });\n } else if (field.type === \"member-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/members?\" + params.toString()));\n showLookupResults(results, payload.members || [], \n (item) => (item?.user?.username || \"unknown\") + (item?.nick ? \" (\" + item.nick + \")\" : \"\"), \n (item) => {\n const username = item?.user?.username || \"unknown\";\n input.value = username;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected member: \" + username + \" (\" + (item?.user?.id || \"unknown\") + \")\";\n });\n }\n } catch { results.style.display = \"none\"; }\n };\n \n input.addEventListener(\"input\", () => { input.dataset.selectedObject = \"\"; selected.textContent = \"No selection\"; runSearch(); });\n input.addEventListener(\"blur\", () => setTimeout(() => { results.style.display = \"none\"; }, 120));\n };\n \n const renderHomeSections = (sections) => {\n if (!sections.length) { el.homeSections.innerHTML = '<div class=\"empty\">No home sections configured.</div>'; return; }\n el.homeSections.innerHTML = \"\";\n sections.forEach((section) => {\n const wrap = document.createElement(\"article\");\n wrap.className = \"panel home-section-panel home-width-\" + normalizeBoxWidth(section.width);\n \n const heading = document.createElement(\"h3\");\n heading.textContent = section.title; heading.style.margin = \"0\"; wrap.appendChild(heading);\n \n if (section.description) {\n const desc = document.createElement(\"div\");\n desc.className = \"subtitle\"; desc.textContent = section.description; wrap.appendChild(desc);\n }\n \n const message = document.createElement(\"div\");\n message.className = \"home-message\";\n \n if (section.fields?.length) {\n const fieldsWrap = document.createElement(\"div\");\n fieldsWrap.className = \"home-fields\";\n \n section.fields.forEach((field) => {\n const fieldWrap = document.createElement(\"div\");\n fieldWrap.className = \"home-field\";\n \n const label = document.createElement(\"label\");\n label.textContent = field.label; fieldWrap.appendChild(label);\n \n let input;\n if (field.type === \"textarea\") {\n input = document.createElement(\"textarea\"); input.className = \"home-textarea\"; input.value = field.value == null ? \"\" : String(field.value);\n } else if (field.type === \"select\") {\n input = document.createElement(\"select\"); input.className = \"home-select cursor-pointer\";\n (field.options || []).forEach((option) => {\n const optionEl = document.createElement(\"option\"); optionEl.value = option.value; optionEl.textContent = option.label;\n if (String(field.value ?? \"\") === option.value) optionEl.selected = true;\n input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\"); row.className = \"home-field-row\";\n input = document.createElement(\"input\"); input.type = \"checkbox\"; input.className = \"home-checkbox cursor-pointer\"; input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\"); stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\");\n row.appendChild(input); row.appendChild(stateText); fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\"); input.className = \"home-input\";\n input.type = field.type === \"number\" ? \"number\" : \"text\"; input.value = field.value == null ? \"\" : String(field.value);\n }\n \n if (input) {\n input.dataset.homeFieldId = field.id; input.dataset.homeFieldType = field.type;\n if (field.placeholder && \"placeholder\" in input) input.placeholder = field.placeholder;\n if (field.required && \"required\" in input) input.required = true;\n if (field.readOnly) { if (\"readOnly\" in input) input.readOnly = true; if (\"disabled\" in input) input.disabled = true; }\n \n if (field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\") { setupLookupField(field, input, fieldWrap); }\n else if (field.type !== \"boolean\") { fieldWrap.appendChild(input); }\n }\n fieldsWrap.appendChild(fieldWrap);\n });\n wrap.appendChild(fieldsWrap);\n }\n \n if (section.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n section.actions.forEach((action) => {\n const button = document.createElement(\"button\"); button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n \n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n const values = {};\n const inputs = wrap.querySelectorAll(\"[data-home-field-id]\");\n inputs.forEach((inputEl) => {\n const fieldId = inputEl.dataset.homeFieldId;\n const fieldType = inputEl.dataset.homeFieldType || \"text\";\n if (!fieldId) return;\n values[fieldId] = toFieldValue({ type: fieldType }, inputEl);\n });\n const result = await fetchJson(buildApiUrl(\"/api/home/\" + encodeURIComponent(action.id)), {\n method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ sectionId: section.id, values })\n });\n message.textContent = result.message || \"Saved.\";\n if (result.refresh) await refreshContent();\n } catch (error) { message.textContent = error instanceof Error ? error.message : \"Save failed\"; }\n finally { button.disabled = false; }\n });\n actions.appendChild(button);\n });\n wrap.appendChild(actions);\n }\n wrap.appendChild(message); el.homeSections.appendChild(wrap);\n });\n };\n \n const renderPlugins = (plugins) => {\n if (!plugins.length) { el.plugins.innerHTML = '<div class=\"empty\">No plugins configured yet.</div>'; return; }\n el.plugins.innerHTML = \"\";\n plugins.forEach((plugin) => {\n const wrap = document.createElement(\"article\"); wrap.className = \"panel\";\n const heading = document.createElement(\"div\"); heading.className = \"title\"; heading.textContent = plugin.name; wrap.appendChild(heading);\n if (plugin.description) { const desc = document.createElement(\"div\"); desc.className = \"subtitle\"; desc.textContent = plugin.description; wrap.appendChild(desc); }\n \n (plugin.panels || []).forEach((panel) => {\n const panelBody = document.createElement(\"div\");\n const panelTitle = document.createElement(\"h4\"); panelTitle.textContent = panel.title; panelTitle.style.marginBottom = \"4px\"; panelBody.appendChild(panelTitle);\n if (panel.description) { const p = document.createElement(\"div\"); p.className = \"subtitle\"; p.textContent = panel.description; panelBody.appendChild(p); }\n \n if (panel.fields?.length) {\n const fieldsWrap = document.createElement(\"div\"); fieldsWrap.className = \"plugin-fields\";\n panel.fields.forEach((field) => {\n const fieldWrap = document.createElement(\"div\"); fieldWrap.className = field.editable ? \"plugin-field\" : \"kv-item\";\n \n if (!field.editable) {\n const display = field.value == null ? \"\" : typeof field.value === \"object\" ? JSON.stringify(field.value) : String(field.value);\n fieldWrap.innerHTML = '<strong>' + escapeHtml(field.label) + '</strong><span>' + escapeHtml(display) + '</span>';\n fieldsWrap.appendChild(fieldWrap); return;\n }\n \n const label = document.createElement(\"label\"); label.textContent = field.label; fieldWrap.appendChild(label);\n \n let input;\n if (field.type === \"textarea\") { input = document.createElement(\"textarea\"); input.className = \"home-textarea\"; input.value = field.value == null ? \"\" : String(field.value); }\n else if (field.type === \"select\") {\n input = document.createElement(\"select\"); input.className = \"home-select cursor-pointer\";\n (field.options || []).forEach((option) => {\n const optionEl = document.createElement(\"option\"); optionEl.value = option.value; optionEl.textContent = option.label;\n if (String(field.value ?? \"\") === option.value) optionEl.selected = true;\n input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\"); row.className = \"home-field-row\";\n input = document.createElement(\"input\"); input.type = \"checkbox\"; input.className = \"home-checkbox cursor-pointer\"; input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\"); stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\");\n row.appendChild(input); row.appendChild(stateText); fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\"); input.className = \"home-input\";\n input.type = field.type === \"number\" ? \"number\" : field.type === \"url\" ? \"url\" : \"text\"; input.value = field.value == null ? \"\" : String(field.value);\n }\n \n if (input) {\n input.dataset.pluginFieldId = field.id || field.label; input.dataset.pluginFieldType = field.type || \"text\";\n if (field.placeholder && \"placeholder\" in input) input.placeholder = field.placeholder;\n if (field.required && \"required\" in input) input.required = true;\n \n const isLookup = field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\";\n if (isLookup) { setupLookupField(field, input, fieldWrap); }\n else if (field.type === \"string-list\") { setupStringListField(field, input, fieldWrap); }\n else if (field.type !== \"boolean\") { fieldWrap.appendChild(input); }\n }\n fieldsWrap.appendChild(fieldWrap);\n });\n panelBody.appendChild(fieldsWrap);\n }\n \n if (panel.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n panel.actions.forEach((action) => { actions.appendChild(makeButton(action, plugin.id, panel.id, panelBody)); });\n panelBody.appendChild(actions);\n }\n wrap.appendChild(panelBody);\n });\n el.plugins.appendChild(wrap);\n });\n };\n \n const refreshContent = async () => {\n const categoriesPayload = await fetchJson(buildApiUrl(\"/api/home/categories\"));\n state.homeCategories = categoriesPayload.categories || [];\n if (!state.selectedHomeCategoryId || !state.homeCategories.some((item) => item.id === state.selectedHomeCategoryId)) {\n const overviewCategory = state.homeCategories.find((item) => item.id === \"overview\");\n state.selectedHomeCategoryId = overviewCategory ? overviewCategory.id : (state.homeCategories[0]?.id ?? null);\n }\n renderHomeCategories();\n \n const homePath = state.selectedHomeCategoryId ? \"/api/home?categoryId=\" + encodeURIComponent(state.selectedHomeCategoryId) : \"/api/home\";\n const [home, overview, plugins] = await Promise.all([ fetchJson(buildApiUrl(homePath)), fetchJson(buildApiUrl(\"/api/overview\")), fetchJson(buildApiUrl(\"/api/plugins\")) ]);\n \n renderHomeSections(home.sections || []);\n const showOverviewArea = state.selectedHomeCategoryId === \"overview\";\n el.overviewArea.style.display = showOverviewArea ? \"block\" : \"none\";\n renderCards(overview.cards || []);\n renderPlugins(plugins.plugins || []);\n };\n \n const loadInitialData = async () => {\n const session = await fetchJson(dashboardConfig.basePath + \"/api/session\");\n if (!session.authenticated) { window.location.href = dashboardConfig.basePath + \"/login\"; return; }\n \n state.session = session;\n el.userMeta.textContent = session.user.username + \" • \" + session.guildCount + \" guild(s)\";\n const guilds = await fetchJson(dashboardConfig.basePath + \"/api/guilds\");\n state.guilds = guilds.guilds || [];\n state.selectedGuildId = null;\n renderServerRail(); updateContextLabel();\n \n el.tabHome.addEventListener(\"click\", () => { state.activeMainTab = \"home\"; applyMainTab(); });\n el.tabPlugins.addEventListener(\"click\", () => { state.activeMainTab = \"plugins\"; applyMainTab(); });\n \n applyMainTab(); await refreshContent();\n };\n \n loadInitialData().catch((error) => { el.userMeta.textContent = \"Load failed\"; console.error(error); });\n `;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderAuroraLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#050a0e\"};\n --rail: ${design.rail ?? \"#0a1018\"};\n --content-bg: ${design.contentBg ?? \"#070d14\"};\n --panel: ${design.panel ?? \"rgba(12, 22, 34, 0.7)\"};\n --panel-2: ${design.panel2 ?? \"rgba(8, 18, 28, 0.6)\"};\n --text: ${design.text ?? \"#e2f0e8\"};\n --muted: ${design.muted ?? \"#6d9886\"};\n --primary: ${design.primary ?? \"#38bdf8\"};\n --primary-glow: rgba(56, 189, 248, 0.3);\n --accent: ${design.accent ?? \"#34d399\"};\n --accent-glow: rgba(52, 211, 153, 0.25);\n --aurora-pink: rgba(236, 72, 153, 0.15);\n --success: ${design.success ?? \"#34d399\"};\n --warning: ${design.warning ?? \"#fbbf24\"};\n --danger: ${design.danger ?? \"#f87171\"};\n --border: ${design.border ?? \"rgba(56, 189, 248, 0.1)\"};\n }\n\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;\n background: var(--bg);\n color: var(--text);\n overflow: hidden;\n height: 100vh;\n }\n\n /* Animated aurora gradient background */\n body::before {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background:\n radial-gradient(ellipse 120% 60% at 20% 0%, rgba(56, 189, 248, 0.12), transparent),\n radial-gradient(ellipse 100% 50% at 60% 0%, rgba(52, 211, 153, 0.10), transparent),\n radial-gradient(ellipse 80% 40% at 80% 10%, var(--aurora-pink), transparent);\n animation: auroraShift 12s ease-in-out infinite alternate;\n }\n\n @keyframes auroraShift {\n 0% { opacity: 0.7; transform: translateX(0) scaleY(1); }\n 50% { opacity: 1; transform: translateX(-3%) scaleY(1.1); }\n 100% { opacity: 0.8; transform: translateX(2%) scaleY(0.95); }\n }\n\n /* ─── LAYOUT ─── */\n .layout {\n position: relative;\n z-index: 1;\n display: grid;\n grid-template-columns: 240px 1fr;\n height: 100vh;\n }\n\n /* ─── SIDEBAR ─── */\n .sidebar {\n background: rgba(5, 10, 14, 0.88);\n backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px);\n border-right: 1px solid var(--border);\n display: flex;\n flex-direction: column;\n overflow: hidden;\n }\n\n .sidebar-header {\n padding: 20px;\n border-bottom: 1px solid var(--border);\n }\n\n .brand {\n font-weight: 800;\n font-size: 16px;\n letter-spacing: 0.5px;\n background: linear-gradient(135deg, var(--primary), var(--accent));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n\n .brand-sub {\n font-size: 11px;\n color: var(--muted);\n margin-top: 4px;\n font-weight: 500;\n letter-spacing: 0.3px;\n }\n\n /* Server rail in sidebar */\n .server-section {\n padding: 14px 12px;\n border-bottom: 1px solid var(--border);\n max-height: 200px;\n overflow-y: auto;\n scrollbar-width: none;\n }\n .server-section::-webkit-scrollbar { display: none; }\n\n .server-section-label {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1.2px;\n color: var(--muted);\n padding: 0 8px;\n margin-bottom: 8px;\n }\n\n .server-rail {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .server-item {\n position: relative;\n width: 100%;\n height: 40px;\n border-radius: 10px;\n border: 1px solid transparent;\n background: transparent;\n color: var(--text);\n font-weight: 600;\n font-size: 13px;\n display: flex;\n align-items: center;\n padding: 0 12px 0 40px;\n transition: all 0.2s ease;\n text-align: left;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n }\n .server-item::before {\n content: attr(title);\n display: block;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n .server-item:hover {\n background: rgba(56, 189, 248, 0.08);\n border-color: rgba(56, 189, 248, 0.15);\n }\n .server-item.active {\n background: linear-gradient(90deg, rgba(56, 189, 248, 0.12), rgba(52, 211, 153, 0.08));\n border-color: rgba(56, 189, 248, 0.3);\n color: #fff;\n }\n\n .server-item-indicator {\n position: absolute; left: 0; top: 50%;\n transform: translateY(-50%);\n width: 3px; height: 0;\n border-radius: 0 4px 4px 0;\n background: linear-gradient(180deg, var(--primary), var(--accent));\n opacity: 0;\n transition: all 0.2s ease;\n }\n .server-item.active .server-item-indicator {\n opacity: 1; height: 20px;\n }\n\n .server-avatar {\n position: absolute; left: 10px; top: 50%;\n transform: translateY(-50%);\n width: 22px; height: 22px;\n border-radius: 6px;\n object-fit: cover;\n }\n .server-fallback {\n position: absolute; left: 10px; top: 50%;\n transform: translateY(-50%);\n width: 22px; height: 22px;\n border-radius: 6px;\n display: grid; place-items: center;\n background: rgba(56, 189, 248, 0.15);\n font-size: 9px; font-weight: 700;\n }\n .server-status {\n position: absolute; right: 10px; top: 50%;\n transform: translateY(-50%);\n width: 7px; height: 7px;\n border-radius: 999px;\n background: var(--accent);\n box-shadow: 0 0 6px var(--accent-glow);\n }\n .server-status.offline { background: var(--muted); box-shadow: none; }\n\n /* Nav links */\n .nav-section {\n flex: 1;\n padding: 14px 12px;\n overflow-y: auto;\n scrollbar-width: none;\n }\n .nav-section::-webkit-scrollbar { display: none; }\n\n .nav-label {\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1.2px;\n color: var(--muted);\n padding: 0 8px;\n margin-bottom: 8px;\n }\n\n .main-tabs {\n display: flex;\n flex-direction: column;\n gap: 4px;\n }\n\n .main-tab {\n width: 100%;\n text-align: left;\n padding: 9px 12px;\n border-radius: 8px;\n border: 1px solid transparent;\n background: transparent;\n color: var(--muted);\n font-size: 13px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.2s ease;\n display: flex;\n align-items: center;\n gap: 10px;\n }\n .main-tab:hover { background: rgba(56, 189, 248, 0.06); color: var(--text); }\n .main-tab.active {\n background: rgba(56, 189, 248, 0.1);\n color: var(--primary);\n border-color: rgba(56, 189, 248, 0.2);\n }\n\n /* User area at bottom */\n .user-area {\n padding: 14px 16px;\n border-top: 1px solid var(--border);\n background: rgba(0, 0, 0, 0.2);\n }\n\n /* ─── MAIN CONTENT ─── */\n .content {\n display: flex;\n flex-direction: column;\n min-width: 0;\n overflow: hidden;\n }\n\n .topbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 24px;\n height: 54px;\n border-bottom: 1px solid var(--border);\n background: rgba(5, 10, 14, 0.6);\n backdrop-filter: blur(12px);\n flex-shrink: 0;\n }\n\n .center-title {\n font-size: 14px;\n font-weight: 600;\n color: var(--text);\n letter-spacing: 0.2px;\n }\n\n .pill {\n display: flex; align-items: center; gap: 8px;\n padding: 5px 14px;\n border-radius: 999px;\n border: 1px solid var(--border);\n background: rgba(12, 22, 34, 0.6);\n font-size: 12px;\n font-weight: 500;\n color: var(--muted);\n }\n\n .main-area {\n flex: 1;\n padding: 24px;\n overflow-y: auto;\n background: radial-gradient(ellipse at 30% 0%, rgba(56, 189, 248, 0.04), transparent 50%),\n var(--content-bg);\n scrollbar-width: thin;\n scrollbar-color: rgba(56, 189, 248, 0.2) transparent;\n }\n .main-area::-webkit-scrollbar { width: 5px; }\n .main-area::-webkit-scrollbar-thumb { background: rgba(56, 189, 248, 0.2); border-radius: 3px; }\n\n /* ─── SECTION ─── */\n .section-title {\n font-size: 18px;\n font-weight: 700;\n margin: 0 0 14px;\n background: linear-gradient(135deg, var(--text), var(--primary));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n\n /* ─── GRID & CARDS ─── */\n .grid { display: grid; gap: 12px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }\n\n .panel {\n background: var(--panel);\n backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);\n border: 1px solid var(--border);\n border-radius: 14px;\n padding: 18px;\n transition: all 0.25s ease;\n position: relative;\n overflow: hidden;\n }\n .panel::after {\n content: \"\";\n position: absolute;\n top: 0; left: 0; right: 0;\n height: 2px;\n background: linear-gradient(90deg, var(--primary), var(--accent), transparent);\n opacity: 0;\n transition: opacity 0.25s ease;\n }\n .panel:hover {\n transform: translateY(-3px);\n border-color: rgba(56, 189, 248, 0.2);\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3), 0 0 16px rgba(56, 189, 248, 0.06);\n }\n .panel:hover::after { opacity: 1; }\n\n .title {\n color: var(--primary);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1.2px;\n }\n .value {\n font-size: 26px;\n font-weight: 800;\n margin-top: 6px;\n background: linear-gradient(135deg, #fff, var(--accent));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n .subtitle {\n margin-top: 6px;\n color: var(--muted);\n font-size: 12px;\n line-height: 1.5;\n }\n\n /* ─── CATEGORIES ─── */\n .home-categories {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-bottom: 14px;\n }\n\n /* ─── HOME SECTIONS ─── */\n .home-sections {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n margin-bottom: 20px;\n }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 6px); max-width: calc(50% - 6px); }\n .home-width-33 { flex-basis: calc(33.333% - 8px); max-width: calc(33.333% - 8px); }\n .home-width-20 { flex-basis: calc(20% - 9.6px); max-width: calc(20% - 9.6px); }\n\n /* ─── FORM ELEMENTS ─── */\n button {\n border: 1px solid var(--border);\n background: rgba(56, 189, 248, 0.06);\n color: var(--text);\n border-radius: 8px;\n padding: 8px 16px;\n font-size: 13px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n button:hover { background: rgba(56, 189, 248, 0.12); border-color: rgba(56, 189, 248, 0.25); }\n button.primary {\n background: linear-gradient(135deg, var(--primary), var(--accent));\n border: none; color: #050a0e;\n font-weight: 700;\n box-shadow: 0 2px 12px var(--primary-glow);\n }\n button.primary:hover { filter: brightness(1.1); box-shadow: 0 4px 20px var(--primary-glow); }\n button.danger {\n background: rgba(248, 113, 113, 0.12);\n border-color: rgba(248, 113, 113, 0.3);\n color: var(--danger);\n }\n\n .home-category-btn.active {\n background: rgba(56, 189, 248, 0.15);\n border-color: rgba(56, 189, 248, 0.4);\n color: var(--primary);\n }\n\n .home-fields, .plugin-fields { display: grid; gap: 10px; margin-top: 12px; }\n .home-field, .plugin-field { display: grid; gap: 5px; }\n .home-field label, .plugin-field > label {\n color: var(--muted);\n font-size: 11px; font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .home-input, .home-textarea, .home-select {\n width: 100%;\n border: 1px solid var(--border);\n background: var(--panel-2);\n color: var(--text);\n border-radius: 8px;\n padding: 10px 14px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n transition: border-color 0.2s, box-shadow 0.2s;\n }\n .home-input:focus, .home-textarea:focus, .home-select:focus {\n border-color: var(--primary);\n box-shadow: 0 0 0 2px var(--primary-glow);\n }\n .home-textarea { min-height: 80px; resize: vertical; }\n .home-checkbox { width: 16px; height: 16px; accent-color: var(--primary); }\n .home-field-row { display: flex; align-items: center; gap: 8px; }\n .home-message { margin-top: 6px; color: var(--muted); font-size: 12px; }\n\n .lookup-wrap { position: relative; }\n .lookup-results {\n position: absolute; left: 0; right: 0;\n top: calc(100% + 4px);\n z-index: 20;\n border: 1px solid var(--border);\n background: rgba(5, 10, 14, 0.97);\n backdrop-filter: blur(16px);\n border-radius: 10px;\n max-height: 200px;\n overflow: auto;\n display: none;\n box-shadow: 0 10px 28px rgba(0, 0, 0, 0.5);\n }\n .lookup-item {\n width: 100%; border: none; border-radius: 0;\n text-align: left; padding: 9px 12px;\n background: transparent; color: var(--text);\n font-size: 13px;\n }\n .lookup-item:hover { background: rgba(56, 189, 248, 0.1); }\n .lookup-selected { margin-top: 4px; font-size: 11px; color: var(--muted); }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }\n\n .kv-item {\n display: flex; justify-content: space-between;\n border: 1px solid var(--border); border-radius: 8px;\n padding: 8px 12px; background: var(--panel-2);\n font-size: 13px;\n }\n .list-editor {\n border: 1px solid var(--border); border-radius: 10px;\n background: var(--panel-2);\n padding: 10px; display: grid; gap: 8px;\n }\n .list-items { display: grid; gap: 6px; }\n .list-item {\n display: grid; grid-template-columns: auto 1fr auto;\n gap: 8px; align-items: center;\n border: 1px solid var(--border); border-radius: 8px;\n padding: 7px 10px; background: rgba(0, 0, 0, 0.2);\n transition: border-color 0.15s ease;\n }\n .list-item:hover { border-color: rgba(56, 189, 248, 0.2); }\n .list-item.dragging { opacity: 0.5; }\n .drag-handle { color: var(--muted); user-select: none; font-size: 14px; }\n .list-input { width: 100%; border: none; outline: none; background: transparent; color: var(--text); font-size: 13px; font-family: inherit; }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: 13px; padding: 8px 0; }\n .cursor-pointer { cursor: pointer; }\n\n @media (max-width: 900px) {\n .layout { grid-template-columns: 1fr; }\n .sidebar { display: none; }\n .home-width-50, .home-width-33, .home-width-20 { flex-basis: 100%; max-width: 100%; }\n }\n\n @keyframes fadeUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .main-area > * { animation: fadeUp 0.35s ease-out both; }\n\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"layout\">\n <aside class=\"sidebar\">\n <div class=\"sidebar-header\">\n <div class=\"brand\">${safeName}</div>\n <div class=\"brand-sub\">Control Panel</div>\n </div>\n\n <div class=\"server-section\">\n <div class=\"server-section-label\">Servers</div>\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </div>\n\n <div class=\"nav-section\">\n <div class=\"nav-label\">Navigation</div>\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"/><polyline points=\"9 22 9 12 15 12 15 22\"/></svg>\n Home\n </button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\"/></svg>\n Plugins\n </button>\n </div>\n\n <div class=\"nav-label\" style=\"margin-top: 16px;\">Categories</div>\n <div id=\"homeCategories\" class=\"main-tabs\"></div>\n </div>\n\n <div class=\"user-area\" id=\"userMeta\">\n <div class=\"pill\" style=\"width:100%;justify-content:center;\">Loading...</div>\n </div>\n </aside>\n\n <main class=\"content\">\n <header class=\"topbar\">\n <span id=\"centerTitle\" class=\"center-title\">Dashboard</span>\n <div id=\"userMetaTopbar\" class=\"pill\" style=\"display:none;\"></div>\n </header>\n\n <div class=\"main-area\">\n <section id=\"homeArea\">\n <div class=\"section-title\">Configuration</div>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">Overview</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Plugins</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderCompactLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#0a0c14\"};\n --rail: ${design.rail ?? \"#0e1120\"};\n --content-bg: ${design.contentBg ?? \"#0f1221\"};\n --panel: ${design.panel ?? \"rgba(22, 27, 48, 0.75)\"};\n --panel-2: ${design.panel2 ?? \"rgba(30, 37, 62, 0.6)\"};\n --text: ${design.text ?? \"#eaefff\"};\n --muted: ${design.muted ?? \"#8490b5\"};\n --primary: ${design.primary ?? \"#7c87ff\"};\n --primary-glow: rgba(124, 135, 255, 0.35);\n --success: ${design.success ?? \"#2bd4a6\"};\n --warning: ${design.warning ?? \"#ffd166\"};\n --danger: ${design.danger ?? \"#ff6f91\"};\n --info: ${design.info ?? \"#66d9ff\"};\n --border: ${design.border ?? \"rgba(255, 255, 255, 0.07)\"};\n }\n\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;\n background: var(--bg);\n color: var(--text);\n overflow: hidden;\n height: 100vh;\n }\n\n /* ─── SHELL ─── */\n .shell {\n height: 100vh;\n display: grid;\n grid-template-rows: auto 1fr;\n }\n\n /* ─── TOP BAR ─── */\n .topbar {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: 16px;\n padding: 0 20px;\n height: 52px;\n border-bottom: 1px solid var(--border);\n background: rgba(10, 12, 20, 0.85);\n backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);\n z-index: 50;\n }\n\n .brand {\n font-weight: 800;\n font-size: 14px;\n letter-spacing: 0.5px;\n text-transform: uppercase;\n background: linear-gradient(135deg, var(--primary), #a5b4fc);\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n\n .center-title {\n text-align: center;\n font-weight: 600;\n font-size: 13px;\n color: var(--muted);\n letter-spacing: 0.3px;\n }\n\n .pill {\n justify-self: end;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 5px 14px;\n border-radius: 999px;\n border: 1px solid var(--border);\n background: var(--panel);\n color: var(--muted);\n font-size: 12px;\n font-weight: 600;\n backdrop-filter: blur(8px);\n }\n\n /* ─── BODY LAYOUT ─── */\n .layout {\n display: grid;\n grid-template-columns: 64px 1fr;\n min-height: 0;\n overflow: hidden;\n }\n\n /* ─── SERVER RAIL ─── */\n .sidebar {\n background: linear-gradient(180deg, var(--rail), rgba(8, 10, 18, 0.95));\n border-right: 1px solid var(--border);\n padding: 12px 0;\n overflow-y: auto;\n scrollbar-width: none;\n }\n .sidebar::-webkit-scrollbar { display: none; }\n\n .server-rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n min-height: min-content;\n }\n\n .server-item {\n position: relative;\n width: 42px !important; height: 42px !important;\n min-width: 42px !important; min-height: 42px !important;\n flex: 0 0 42px !important;\n border-radius: 12px;\n border: 1px solid transparent;\n background: var(--panel-2);\n color: var(--text);\n font-weight: 700;\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: visible;\n padding: 0;\n }\n .server-item:hover {\n border-color: rgba(124, 135, 255, 0.5);\n background: rgba(124, 135, 255, 0.12);\n transform: translateY(-2px);\n }\n .server-item.active {\n background: var(--primary);\n border-color: var(--primary);\n box-shadow: 0 0 16px var(--primary-glow);\n color: #fff;\n }\n\n .server-item-indicator {\n position: absolute;\n left: -8px; top: 50%;\n transform: translateY(-50%) scaleY(0.5);\n width: 3px; height: 16px;\n background: var(--primary);\n border-radius: 999px;\n opacity: 0;\n transition: all 0.2s ease;\n }\n .server-item.active .server-item-indicator {\n opacity: 1;\n transform: translateY(-50%) scaleY(1);\n }\n\n .server-avatar {\n width: 100%; height: 100%;\n object-fit: cover;\n border-radius: inherit;\n }\n .server-fallback {\n display: grid;\n place-items: center;\n width: 100%; height: 100%;\n font-size: 12px;\n font-weight: 700;\n }\n\n .server-status {\n position: absolute;\n right: -3px; bottom: -3px;\n width: 10px; height: 10px;\n border-radius: 999px;\n border: 2px solid var(--rail);\n background: var(--success);\n }\n .server-status.offline { background: var(--muted); }\n\n /* Separator after \"ME\" tile */\n .server-rail > .server-item:first-child::after {\n display: none; /* separator handled via JS */\n }\n\n /* ─── CONTENT AREA ─── */\n .content {\n min-width: 0;\n padding: 16px;\n overflow-y: auto;\n background: radial-gradient(ellipse at 10% 0%, rgba(124, 135, 255, 0.06), transparent 60%),\n var(--content-bg);\n scrollbar-width: thin;\n scrollbar-color: rgba(124, 135, 255, 0.3) transparent;\n }\n .content::-webkit-scrollbar { width: 5px; }\n .content::-webkit-scrollbar-thumb { background: rgba(124, 135, 255, 0.3); border-radius: 3px; }\n\n .container {\n background: var(--panel);\n backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);\n border: 1px solid var(--border);\n border-radius: 16px;\n padding: 16px;\n }\n\n /* ─── TABS ─── */\n .main-tabs {\n display: inline-flex;\n gap: 4px;\n padding: 3px;\n border-radius: 10px;\n background: rgba(0, 0, 0, 0.25);\n border: 1px solid var(--border);\n margin-bottom: 16px;\n }\n\n button {\n border: 1px solid transparent;\n background: transparent;\n color: var(--muted);\n border-radius: 8px;\n padding: 7px 14px;\n font-size: 13px;\n font-weight: 600;\n font-family: inherit;\n transition: all 0.2s ease;\n cursor: pointer;\n }\n button:hover { background: rgba(255, 255, 255, 0.05); color: var(--text); }\n button.primary {\n background: linear-gradient(135deg, var(--primary), #a5b4fc);\n border: none; color: #fff;\n box-shadow: 0 2px 10px var(--primary-glow);\n font-weight: 700;\n }\n button.primary:hover { filter: brightness(1.1); box-shadow: 0 4px 16px var(--primary-glow); }\n button.danger {\n background: rgba(255, 111, 145, 0.15);\n border-color: rgba(255, 111, 145, 0.4);\n color: var(--danger);\n }\n button.danger:hover { background: rgba(255, 111, 145, 0.25); }\n\n .main-tab.active, .home-category-btn.active {\n background: var(--primary);\n color: #fff;\n border-color: transparent;\n box-shadow: 0 2px 8px var(--primary-glow);\n }\n\n /* ─── SECTION ─── */\n .section-title {\n font-size: 15px;\n font-weight: 700;\n color: var(--text);\n margin: 16px 0 10px;\n letter-spacing: 0.2px;\n }\n\n /* ─── GRID & CARDS ─── */\n .grid { display: grid; gap: 10px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }\n\n .panel {\n background: var(--panel-2);\n border: 1px solid var(--border);\n border-radius: 12px;\n padding: 14px;\n transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;\n }\n .panel:hover {\n transform: translateY(-2px);\n border-color: rgba(124, 135, 255, 0.25);\n box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);\n }\n\n .title {\n color: var(--primary);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1px;\n }\n .value {\n font-size: 22px;\n font-weight: 800;\n margin-top: 4px;\n background: linear-gradient(135deg, #fff, var(--primary));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n .subtitle {\n margin-top: 4px;\n color: var(--muted);\n font-size: 12px;\n line-height: 1.5;\n }\n\n /* ─── HOME CATEGORIES ─── */\n .home-categories {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-bottom: 12px;\n }\n\n /* ─── HOME SECTIONS ─── */\n .home-sections {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n margin-bottom: 12px;\n }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 6px); max-width: calc(50% - 6px); }\n .home-width-33 { flex-basis: calc(33.333% - 8px); max-width: calc(33.333% - 8px); }\n .home-width-20 { flex-basis: calc(20% - 9.6px); max-width: calc(20% - 9.6px); }\n\n /* ─── FIELDS ─── */\n .home-fields, .plugin-fields { display: grid; gap: 10px; margin-top: 10px; }\n .home-field, .plugin-field { display: grid; gap: 5px; }\n .home-field label, .plugin-field > label {\n color: var(--muted);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .home-input, .home-textarea, .home-select {\n width: 100%;\n border: 1px solid var(--border);\n background: rgba(0, 0, 0, 0.25);\n color: var(--text);\n border-radius: 8px;\n padding: 9px 12px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n }\n .home-input:focus, .home-textarea:focus, .home-select:focus {\n border-color: var(--primary);\n box-shadow: 0 0 0 2px var(--primary-glow);\n }\n .home-textarea { min-height: 80px; resize: vertical; }\n .home-checkbox { width: 16px; height: 16px; accent-color: var(--primary); }\n .home-field-row { display: flex; align-items: center; gap: 8px; }\n .home-message { margin-top: 6px; color: var(--muted); font-size: 12px; }\n\n /* ─── LOOKUP ─── */\n .lookup-wrap { position: relative; }\n .lookup-results {\n position: absolute; left: 0; right: 0;\n top: calc(100% + 4px);\n z-index: 20;\n border: 1px solid var(--border);\n background: rgba(14, 17, 32, 0.97);\n backdrop-filter: blur(12px);\n border-radius: 10px;\n max-height: 200px;\n overflow: auto;\n display: none;\n box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);\n }\n .lookup-item {\n width: 100%; border: none; border-radius: 0; text-align: left;\n padding: 9px 12px; background: transparent; color: var(--text);\n font-size: 13px;\n }\n .lookup-item:hover { background: rgba(124, 135, 255, 0.12); }\n .lookup-selected { margin-top: 4px; font-size: 11px; color: var(--muted); }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }\n\n /* ─── KV / LIST ─── */\n .kv-item {\n display: flex; justify-content: space-between;\n border: 1px solid var(--border); border-radius: 8px;\n padding: 8px 10px; background: var(--panel-2);\n font-size: 13px;\n }\n .list-editor {\n border: 1px solid var(--border); border-radius: 10px;\n background: rgba(0, 0, 0, 0.2);\n padding: 10px; display: grid; gap: 8px;\n }\n .list-items { display: grid; gap: 6px; }\n .list-item {\n display: grid; grid-template-columns: auto 1fr auto;\n gap: 8px; align-items: center;\n border: 1px solid var(--border); border-radius: 8px;\n padding: 7px 10px; background: var(--panel-2);\n transition: border-color 0.15s ease;\n }\n .list-item:hover { border-color: rgba(124, 135, 255, 0.25); }\n .list-item.dragging { opacity: 0.5; }\n .drag-handle { color: var(--muted); user-select: none; font-size: 14px; }\n .list-input {\n width: 100%; border: none; outline: none;\n background: transparent; color: var(--text);\n font-size: 13px; font-family: inherit;\n }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: 13px; padding: 8px 0; }\n .cursor-pointer { cursor: pointer; }\n\n /* ─── RESPONSIVE ─── */\n @media (max-width: 980px) {\n .layout { grid-template-columns: 56px 1fr; }\n .server-item {\n width: 36px !important; height: 36px !important;\n min-width: 36px !important; min-height: 36px !important;\n flex: 0 0 36px !important;\n border-radius: 10px;\n }\n .home-width-50, .home-width-33, .home-width-20 { flex-basis: 100%; max-width: 100%; }\n .content { padding: 12px; }\n }\n @media (max-width: 640px) {\n .layout { grid-template-columns: 1fr; }\n .sidebar { display: none; }\n .topbar { grid-template-columns: 1fr auto; }\n .center-title { display: none; }\n }\n\n /* Inject User Custom CSS Here */\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"shell\">\n <header class=\"topbar\">\n <div class=\"brand\">${safeName}</div>\n <div id=\"centerTitle\" class=\"center-title\">User Dashboard</div>\n <div id=\"userMeta\" class=\"pill\">Loading...</div>\n </header>\n\n <div class=\"layout\">\n <aside class=\"sidebar\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </aside>\n\n <main class=\"content\">\n <div class=\"container\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">Plugins</button>\n </div>\n\n <section id=\"homeArea\">\n <div class=\"section-title\">Home</div>\n <section id=\"homeCategories\" class=\"home-categories\"></section>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">Dashboard Stats</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Plugins</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderCyberpunkLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700;800&family=Inter:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#0a0a0f\"};\n --rail: ${design.rail ?? \"#0e0e15\"};\n --content-bg: ${design.contentBg ?? \"#0c0c12\"};\n --panel: ${design.panel ?? \"rgba(16, 16, 24, 0.9)\"};\n --panel-2: ${design.panel2 ?? \"rgba(22, 22, 32, 0.8)\"};\n --text: ${design.text ?? \"#e4e4ef\"};\n --muted: ${design.muted ?? \"#6b6b8a\"};\n --primary: ${design.primary ?? \"#ec4899\"};\n --primary-glow: rgba(236, 72, 153, 0.35);\n --accent: ${design.accent ?? \"#06b6d4\"};\n --accent-glow: rgba(6, 182, 212, 0.3);\n --success: ${design.success ?? \"#10b981\"};\n --warning: ${design.warning ?? \"#eab308\"};\n --danger: ${design.danger ?? \"#ef4444\"};\n --border: ${design.border ?? \"rgba(236, 72, 153, 0.12)\"};\n --border-accent: rgba(6, 182, 212, 0.15);\n --neon-pink: rgba(236, 72, 153, 0.7);\n --neon-cyan: rgba(6, 182, 212, 0.7);\n }\n\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;\n background: var(--bg);\n color: var(--text);\n overflow: hidden;\n height: 100vh;\n }\n\n /* Scanline overlay */\n body::before {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 100;\n background: repeating-linear-gradient(\n 0deg,\n transparent,\n transparent 2px,\n rgba(0, 0, 0, 0.04) 2px,\n rgba(0, 0, 0, 0.04) 4px\n );\n }\n\n /* Neon glow accents in corners */\n body::after {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background:\n radial-gradient(ellipse at 0% 0%, rgba(236, 72, 153, 0.08), transparent 35%),\n radial-gradient(ellipse at 100% 100%, rgba(6, 182, 212, 0.06), transparent 35%);\n }\n\n /* ─── SHELL ─── */\n .shell {\n position: relative;\n z-index: 1;\n height: 100vh;\n display: grid;\n grid-template-columns: 70px 1fr;\n grid-template-rows: auto 1fr;\n }\n\n /* ─── TOP BAR ─── */\n .topbar {\n grid-column: 1 / -1;\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 16px;\n height: 48px;\n background: rgba(10, 10, 15, 0.92);\n backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border);\n position: relative;\n }\n\n /* Pink neon line at top */\n .topbar::before {\n content: \"\";\n position: absolute;\n top: 0; left: 0; right: 0;\n height: 1px;\n background: linear-gradient(90deg, transparent, var(--neon-pink), var(--neon-cyan), transparent);\n }\n\n .brand {\n font-family: 'JetBrains Mono', monospace;\n font-weight: 800;\n font-size: 14px;\n letter-spacing: 2px;\n text-transform: uppercase;\n color: var(--primary);\n text-shadow: 0 0 12px var(--primary-glow);\n }\n\n .topbar-center {\n display: flex;\n align-items: center;\n gap: 8px;\n }\n\n .center-title {\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px;\n font-weight: 600;\n color: var(--accent);\n letter-spacing: 1px;\n text-transform: uppercase;\n padding: 4px 12px;\n border: 1px solid var(--border-accent);\n background: rgba(6, 182, 212, 0.06);\n }\n\n .main-tabs {\n display: flex;\n gap: 2px;\n }\n\n .main-tab {\n font-family: 'JetBrains Mono', monospace;\n padding: 5px 14px;\n border: 1px solid var(--border);\n background: transparent;\n color: var(--muted);\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 1px;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n .main-tab:first-child { border-radius: 4px 0 0 4px; }\n .main-tab:last-child { border-radius: 0 4px 4px 0; }\n .main-tab:hover { background: rgba(236, 72, 153, 0.06); color: var(--text); }\n .main-tab.active {\n background: var(--primary);\n border-color: var(--primary);\n color: #fff;\n text-shadow: 0 0 6px rgba(255, 255, 255, 0.4);\n box-shadow: 0 0 12px var(--primary-glow);\n }\n\n .pill {\n font-family: 'JetBrains Mono', monospace;\n font-size: 10px;\n font-weight: 600;\n color: var(--muted);\n padding: 4px 10px;\n border: 1px solid var(--border);\n letter-spacing: 0.5px;\n }\n\n /* ─── SERVER RAIL ─── */\n .sidebar {\n background: var(--rail);\n border-right: 1px solid var(--border);\n padding: 8px 0;\n overflow-y: auto;\n scrollbar-width: none;\n position: relative;\n }\n .sidebar::-webkit-scrollbar { display: none; }\n\n /* Cyan neon line on right edge */\n .sidebar::after {\n content: \"\";\n position: absolute;\n top: 0; right: 0; bottom: 0;\n width: 1px;\n background: linear-gradient(180deg, var(--neon-pink), transparent 30%, transparent 70%, var(--neon-cyan));\n }\n\n .server-rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n min-height: min-content;\n }\n\n .server-item {\n position: relative;\n width: 46px !important; height: 46px !important;\n min-width: 46px !important; min-height: 46px !important;\n flex: 0 0 46px !important;\n border-radius: 4px;\n border: 1px solid var(--border);\n background: var(--panel);\n color: var(--text);\n font-weight: 700;\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.15s ease;\n overflow: visible;\n padding: 0;\n }\n .server-item:hover {\n border-color: var(--primary);\n box-shadow: 0 0 10px var(--primary-glow), inset 0 0 10px rgba(236, 72, 153, 0.05);\n transform: scale(1.05);\n }\n .server-item.active {\n border-color: var(--accent);\n box-shadow: 0 0 14px var(--accent-glow), inset 0 0 14px rgba(6, 182, 212, 0.08);\n background: rgba(6, 182, 212, 0.08);\n }\n\n .server-item-indicator {\n position: absolute;\n left: -9px; top: 50%;\n transform: translateY(-50%);\n width: 3px; height: 0;\n background: var(--accent);\n box-shadow: 0 0 8px var(--accent-glow);\n opacity: 0;\n transition: all 0.2s ease;\n }\n .server-item.active .server-item-indicator {\n opacity: 1; height: 22px;\n }\n\n .server-avatar {\n width: 100%; height: 100%;\n object-fit: cover;\n border-radius: inherit;\n }\n .server-fallback {\n display: grid; place-items: center;\n width: 100%; height: 100%;\n font-family: 'JetBrains Mono', monospace;\n font-size: 11px; font-weight: 700;\n color: var(--primary);\n }\n .server-status {\n position: absolute;\n right: -3px; bottom: -3px;\n width: 9px; height: 9px;\n border-radius: 2px;\n border: 2px solid var(--rail);\n background: var(--success);\n box-shadow: 0 0 6px rgba(16, 185, 129, 0.5);\n }\n .server-status.offline { background: var(--muted); box-shadow: none; }\n\n /* ─── CONTENT ─── */\n .content {\n min-width: 0;\n padding: 14px;\n overflow-y: auto;\n background: var(--content-bg);\n scrollbar-width: thin;\n scrollbar-color: rgba(236, 72, 153, 0.2) transparent;\n }\n .content::-webkit-scrollbar { width: 4px; }\n .content::-webkit-scrollbar-thumb { background: rgba(236, 72, 153, 0.2); border-radius: 2px; }\n\n .container {\n border: 1px solid var(--border);\n background: var(--panel);\n padding: 16px;\n position: relative;\n }\n\n /* Corner accents on container */\n .container::before,\n .container::after {\n content: \"\";\n position: absolute;\n width: 12px; height: 12px;\n border-color: var(--accent);\n border-style: solid;\n }\n .container::before {\n top: -1px; left: -1px;\n border-width: 2px 0 0 2px;\n }\n .container::after {\n bottom: -1px; right: -1px;\n border-width: 0 2px 2px 0;\n }\n\n /* ─── SECTION ─── */\n .section-title {\n font-family: 'JetBrains Mono', monospace;\n font-size: 13px;\n font-weight: 700;\n color: var(--accent);\n text-transform: uppercase;\n letter-spacing: 2px;\n margin: 14px 0 10px;\n padding-bottom: 8px;\n border-bottom: 1px solid var(--border-accent);\n }\n\n /* ─── GRID & CARDS ─── */\n .grid { display: grid; gap: 10px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); }\n\n .panel {\n background: var(--panel-2);\n border: 1px solid var(--border);\n padding: 16px;\n transition: all 0.2s ease;\n position: relative;\n }\n .panel::before {\n content: \"\";\n position: absolute;\n top: 0; left: 0;\n width: 0; height: 2px;\n background: linear-gradient(90deg, var(--primary), var(--accent));\n transition: width 0.3s ease;\n }\n .panel:hover {\n border-color: rgba(236, 72, 153, 0.25);\n box-shadow: 0 0 16px rgba(236, 72, 153, 0.06), 0 4px 16px rgba(0, 0, 0, 0.3);\n }\n .panel:hover::before { width: 100%; }\n\n .title {\n font-family: 'JetBrains Mono', monospace;\n color: var(--primary);\n font-size: 10px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1.5px;\n }\n .value {\n font-family: 'JetBrains Mono', monospace;\n font-size: 24px;\n font-weight: 800;\n margin-top: 6px;\n color: var(--accent);\n text-shadow: 0 0 20px var(--accent-glow);\n }\n .subtitle {\n margin-top: 6px;\n color: var(--muted);\n font-size: 12px;\n line-height: 1.5;\n }\n\n /* ─── HOME SECTIONS ─── */\n .home-categories {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-bottom: 12px;\n }\n\n .home-category-btn.active {\n background: rgba(6, 182, 212, 0.1);\n border-color: rgba(6, 182, 212, 0.4);\n color: var(--accent);\n box-shadow: 0 0 8px rgba(6, 182, 212, 0.1);\n }\n\n .home-sections {\n display: flex;\n gap: 10px;\n flex-wrap: wrap;\n margin-bottom: 14px;\n }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 5px); max-width: calc(50% - 5px); }\n .home-width-33 { flex-basis: calc(33.333% - 6.67px); max-width: calc(33.333% - 6.67px); }\n .home-width-20 { flex-basis: calc(20% - 8px); max-width: calc(20% - 8px); }\n\n /* ─── FORM ELEMENTS ─── */\n button {\n font-family: 'JetBrains Mono', monospace;\n border: 1px solid var(--border);\n background: transparent;\n color: var(--text);\n border-radius: 2px;\n padding: 7px 14px;\n font-size: 11px;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n cursor: pointer;\n transition: all 0.15s ease;\n }\n button:hover {\n background: rgba(236, 72, 153, 0.08);\n border-color: var(--primary);\n box-shadow: 0 0 8px var(--primary-glow);\n }\n button.primary {\n background: var(--primary);\n border-color: var(--primary);\n color: #fff;\n box-shadow: 0 0 14px var(--primary-glow);\n }\n button.primary:hover {\n filter: brightness(1.1);\n box-shadow: 0 0 22px var(--primary-glow);\n }\n button.danger {\n background: rgba(239, 68, 68, 0.15);\n border-color: rgba(239, 68, 68, 0.4);\n color: var(--danger);\n }\n\n .home-fields, .plugin-fields { display: grid; gap: 10px; margin-top: 10px; }\n .home-field, .plugin-field { display: grid; gap: 5px; }\n .home-field label, .plugin-field > label {\n font-family: 'JetBrains Mono', monospace;\n color: var(--muted);\n font-size: 10px; font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 1px;\n }\n\n .home-input, .home-textarea, .home-select {\n width: 100%;\n border: 1px solid var(--border);\n background: rgba(0, 0, 0, 0.4);\n color: var(--text);\n border-radius: 2px;\n padding: 9px 12px;\n font-size: 13px;\n font-family: 'JetBrains Mono', monospace;\n outline: none;\n transition: border-color 0.15s, box-shadow 0.15s;\n }\n .home-input:focus, .home-textarea:focus, .home-select:focus {\n border-color: var(--accent);\n box-shadow: 0 0 0 1px var(--accent-glow), 0 0 12px var(--accent-glow);\n }\n .home-textarea { min-height: 80px; resize: vertical; }\n .home-checkbox { width: 16px; height: 16px; accent-color: var(--primary); }\n .home-field-row { display: flex; align-items: center; gap: 8px; }\n .home-message {\n font-family: 'JetBrains Mono', monospace;\n margin-top: 6px;\n color: var(--accent);\n font-size: 11px;\n }\n\n .lookup-wrap { position: relative; }\n .lookup-results {\n position: absolute; left: 0; right: 0;\n top: calc(100% + 4px);\n z-index: 20;\n border: 1px solid var(--border);\n background: rgba(10, 10, 15, 0.97);\n max-height: 200px;\n overflow: auto;\n display: none;\n box-shadow: 0 0 20px rgba(236, 72, 153, 0.1), 0 8px 24px rgba(0, 0, 0, 0.5);\n }\n .lookup-item {\n width: 100%; border: none; border-radius: 0;\n text-align: left; padding: 8px 12px;\n background: transparent; color: var(--text);\n font-size: 12px;\n }\n .lookup-item:hover { background: rgba(236, 72, 153, 0.1); }\n .lookup-selected { margin-top: 4px; font-size: 10px; color: var(--muted); font-family: 'JetBrains Mono', monospace; }\n .actions { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 12px; }\n\n .kv-item {\n display: flex; justify-content: space-between;\n border: 1px solid var(--border);\n padding: 7px 10px; background: var(--panel-2);\n font-size: 12px;\n }\n .list-editor {\n border: 1px solid var(--border);\n background: rgba(0, 0, 0, 0.3);\n padding: 8px; display: grid; gap: 6px;\n }\n .list-items { display: grid; gap: 4px; }\n .list-item {\n display: grid; grid-template-columns: auto 1fr auto;\n gap: 8px; align-items: center;\n border: 1px solid var(--border);\n padding: 6px 8px; background: var(--panel);\n transition: border-color 0.15s ease;\n }\n .list-item:hover { border-color: var(--primary); }\n .list-item.dragging { opacity: 0.5; }\n .drag-handle { color: var(--primary); user-select: none; font-size: 14px; }\n .list-input { width: 100%; border: none; outline: none; background: transparent; color: var(--text); font-size: 13px; font-family: 'JetBrains Mono', monospace; }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: 12px; font-family: 'JetBrains Mono', monospace; padding: 8px 0; }\n .cursor-pointer { cursor: pointer; }\n\n @media (max-width: 980px) {\n .shell { grid-template-columns: 60px 1fr; }\n .server-item {\n width: 38px !important; height: 38px !important;\n min-width: 38px !important; min-height: 38px !important;\n flex: 0 0 38px !important;\n }\n .home-width-50, .home-width-33, .home-width-20 { flex-basis: 100%; max-width: 100%; }\n }\n @media (max-width: 640px) {\n .shell { grid-template-columns: 1fr; grid-template-rows: auto auto 1fr; }\n .sidebar { flex-direction: row; border-right: none; border-bottom: 1px solid var(--border); padding: 6px 8px; overflow-x: auto; overflow-y: hidden; }\n .sidebar::after { display: none; }\n .server-rail { flex-direction: row; }\n .server-item-indicator { display: none; }\n .topbar-center { display: none; }\n }\n\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"shell\">\n <header class=\"topbar\">\n <div class=\"brand\">${safeName}</div>\n <div class=\"topbar-center\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">SYS</button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">EXT</button>\n </div>\n <div id=\"centerTitle\" class=\"center-title\">ONLINE</div>\n </div>\n <div id=\"userMeta\" class=\"pill\">INIT...</div>\n </header>\n\n <aside class=\"sidebar\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </aside>\n\n <main class=\"content\">\n <div class=\"container\">\n <section id=\"homeArea\">\n <div class=\"section-title\">// SYSTEM CONFIG</div>\n <section id=\"homeCategories\" class=\"home-categories\"></section>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">// TELEMETRY</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">// EXTENSIONS</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderDefaultLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n const scriptData = JSON.stringify({ basePath: context.basePath });\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#08090f\"};\n --content-bg: ${design.contentBg ?? \"radial-gradient(circle at top right, #171a2f, #08090f)\"};\n --panel: ${design.panel ?? \"rgba(20, 23, 43, 0.5)\"};\n --panel-2: ${design.panel2 ?? \"rgba(0, 0, 0, 0.4)\"};\n --text: ${design.text ?? \"#e0e6ff\"};\n --muted: ${design.muted ?? \"#8a93bc\"};\n --primary: ${design.primary ?? \"#5865F2\"};\n --primary-glow: rgba(88, 101, 242, 0.4);\n --success: ${design.success ?? \"#00E676\"};\n --danger: ${design.danger ?? \"#FF3D00\"};\n --border: ${design.border ?? \"rgba(255, 255, 255, 0.05)\"};\n }\n \n * { box-sizing: border-box; }\n \n body {\n margin: 0;\n padding: 24px; /* Creates the outer gap for the floating effect */\n height: 100vh;\n font-family: \"Inter\", \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n background: var(--content-bg); /* Applies gradient to entire screen */\n color: var(--text);\n overflow: hidden; \n }\n\n /* FLOATING ISLAND STRUCTURE */\n .layout { \n display: flex; \n gap: 24px; /* Gap between the floating islands */\n height: 100%; \n width: 100%; \n }\n \n /* Base styles for all floating windows */\n .floating-window {\n background: var(--panel);\n backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--border);\n border-radius: 24px;\n box-shadow: 0 12px 40px rgba(0, 0, 0, 0.3);\n }\n\n /* 1. SERVER RAIL */\n .sidebar { \n width: 84px; \n flex-shrink: 0; \n padding: 20px 0; \n z-index: 10;\n overflow-y: auto; \n scrollbar-width: none; \n /* Changed from flex to block to stop height crushing */\n display: block; \n }\n .sidebar::-webkit-scrollbar { display: none; } \n \n .server-rail { \n display: flex; \n flex-direction: column; \n align-items: center; \n gap: 16px; \n width: 100%; \n /* This tells the rail it is allowed to be taller than the screen */\n min-height: min-content; \n }\n \n .server-separator { \n width: 32px; \n height: 2px; \n min-height: 2px; /* Lock separator height */\n background: var(--border); \n border-radius: 2px; \n }\n \n .server-item { \n position: relative; \n /* The !important tags force the browser to respect the size */\n width: 54px !important; \n height: 54px !important; \n min-width: 54px !important; \n min-height: 54px !important; \n flex: 0 0 54px !important; \n \n border-radius: 16px; \n border: 1px solid transparent; \n background: var(--panel-2); \n color: var(--text); \n font-weight: 700; \n font-size: 16px; \n display: flex; \n align-items: center; \n justify-content: center; \n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n padding: 0;\n overflow: visible;\n }\n .server-item:hover { border-color: var(--primary); box-shadow: 0 0 15px var(--primary-glow); transform: translateY(-3px); }\n .server-item.active { background: var(--primary); border-color: var(--primary); box-shadow: 0 0 20px var(--primary-glow); color: #fff; }\n \n .server-item-indicator { position: absolute; left: -16px; width: 6px; height: 20px; border-radius: 4px; background: var(--primary); opacity: 0; transition: all 0.3s ease; box-shadow: 0 0 10px var(--primary-glow); }\n .server-item.active .server-item-indicator { opacity: 1; height: 32px; }\n \n .server-avatar { width: 100%; height: 100%; object-fit: cover; border-radius: inherit; }\n .server-fallback { font-weight: 700; font-size: 1rem; }\n .server-status { position: absolute; right: -4px; bottom: -4px; width: 16px; height: 16px; border-radius: 50%; border: 3px solid #141622; background: var(--success); z-index: 2; box-shadow: 0 0 10px var(--success); }\n .server-status.offline { background: var(--muted); box-shadow: none; border-color: transparent; }\n\n /* 2. SECONDARY SIDEBAR */\n .secondary-sidebar { \n width: 280px; flex-shrink: 0; \n display: flex; flex-direction: column; z-index: 9; \n overflow: hidden; /* Keeps user area inside border radius */\n }\n .sidebar-header { height: 72px; padding: 0 24px; display: flex; align-items: center; font-weight: 800; font-size: 18px; letter-spacing: 1px; text-transform: uppercase; background: linear-gradient(90deg, #fff, var(--primary)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; border-bottom: 1px solid var(--border); flex-shrink: 0; }\n .sidebar-content { flex: 1; overflow-y: auto; padding: 20px 16px; scrollbar-width: none; }\n .sidebar-content::-webkit-scrollbar { display: none; }\n \n .category-header { font-size: 11px; font-weight: 800; color: var(--primary); text-transform: uppercase; margin: 24px 8px 8px; letter-spacing: 1.5px; opacity: 0.8; }\n .category-header:first-child { margin-top: 0; }\n \n .channel-btn { \n width: 100%; text-align: left; padding: 12px 16px; border-radius: 12px; background: transparent; border: 1px solid transparent; \n color: var(--muted); display: flex; align-items: center; gap: 12px; font-size: 14px; font-weight: 600; margin-bottom: 6px; transition: all 0.2s; \n }\n .channel-btn:hover { background: var(--panel-2); color: var(--text); transform: translateX(4px); }\n .channel-btn.active { background: rgba(88, 101, 242, 0.15); border-left: 4px solid var(--primary); color: #fff; transform: translateX(4px); }\n \n .user-area { height: 80px; background: rgba(0,0,0,0.2); padding: 0 20px; display: flex; align-items: center; flex-shrink: 0; gap: 14px; border-top: 1px solid var(--border); }\n .user-avatar-small { \n width: 44px; \n height: 44px; \n min-width: 44px; \n min-height: 44px; \n flex: 0 0 44px;\n \n border-radius: 14px; \n background: var(--primary); \n border: 2px solid var(--border); \n padding: 2px; \n }\n .user-details { display: flex; flex-direction: column; line-height: 1.4; }\n .user-details .name { color: #fff; font-size: 15px; font-weight: 700; }\n .user-details .sub { color: var(--primary); font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; }\n\n /* 3. MAIN CONTENT */\n .content { flex: 1; display: flex; flex-direction: column; min-width: 0; gap: 24px; }\n \n /* Topbar is its own floating island now */\n .topbar { height: 72px; padding: 0 28px; display: flex; align-items: center; font-weight: 700; font-size: 20px; letter-spacing: 0.5px; z-index: 5; flex-shrink: 0; }\n \n /* Container background is transparent so inner panels float over the main gradient */\n .container { flex: 1; padding: 0 8px 0 0; overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--primary) transparent; }\n .container::-webkit-scrollbar { width: 6px; }\n .container::-webkit-scrollbar-thumb { background: var(--primary); border-radius: 3px; }\n \n .section-title { font-size: 26px; font-weight: 800; margin: 0 0 24px 0; color: #fff; letter-spacing: -0.5px; }\n .subtitle { margin-top: 6px; color: var(--muted); font-size: 14px; line-height: 1.5; }\n \n .grid { display: grid; gap: 24px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); }\n \n /* The content panels are also floating windows */\n .panel { \n background: var(--panel); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--border); border-radius: 20px; padding: 28px; \n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); transition: transform 0.3s, box-shadow 0.3s; \n }\n .panel:hover { transform: translateY(-4px); box-shadow: 0 12px 40px rgba(0,0,0,0.4), 0 0 20px rgba(88, 101, 242, 0.1); border-color: rgba(88, 101, 242, 0.3); }\n \n /* Inputs & Forms */\n .home-sections { display: flex; flex-wrap: wrap; gap: 24px; margin-bottom: 40px; }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 12px); max-width: calc(50% - 12px); }\n @media (max-width: 1300px) { .home-width-50 { flex-basis: 100%; max-width: 100%; } }\n \n .home-fields { display: grid; gap: 20px; margin-top: 24px; }\n .home-field { display: grid; gap: 10px; }\n .home-field label { color: var(--muted); font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; }\n \n .home-input, .home-textarea, .home-select { \n width: 100%; border: 1px solid var(--border); background: var(--panel-2); color: #fff; \n border-radius: 12px; padding: 14px 18px; font-size: 15px; outline: none; transition: all 0.3s ease; \n }\n .home-input:focus, .home-textarea:focus, .home-select:focus { \n border-color: var(--primary); box-shadow: 0 0 15px var(--primary-glow); background: rgba(0,0,0,0.6); \n }\n .home-textarea { min-height: 120px; resize: vertical; }\n \n .actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 28px; }\n button { \n border: 1px solid var(--border); background: rgba(255,255,255,0.05); color: #fff; \n border-radius: 10px; padding: 12px 24px; font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 1px; \n transition: all 0.2s; backdrop-filter: blur(4px);\n }\n button:hover { background: rgba(255,255,255,0.1); transform: translateY(-2px); }\n button.primary { background: linear-gradient(135deg, var(--primary), #7c88f9); border: none; box-shadow: 0 4px 15px var(--primary-glow); }\n button.primary:hover { box-shadow: 0 6px 20px var(--primary-glow); filter: brightness(1.1); }\n button.danger { background: linear-gradient(135deg, var(--danger), #ff7a59); border: none; box-shadow: 0 4px 15px rgba(255, 61, 0, 0.3); }\n .cursor-pointer { cursor: pointer; }\n\n .home-field-row { display: flex; align-items: center; gap: 12px; }\n .home-checkbox { width: 24px; height: 24px; accent-color: var(--primary); border-radius: 6px; }\n \n .lookup-wrap { position: relative; }\n .lookup-results { position: absolute; left: 0; right: 0; top: calc(100% + 8px); z-index: 20; background: #0e101a; border: 1px solid var(--primary); border-radius: 12px; max-height: 220px; overflow: auto; display: none; box-shadow: 0 10px 30px rgba(0,0,0,0.5), 0 0 20px var(--primary-glow); }\n .lookup-item { width: 100%; text-align: left; padding: 14px; background: transparent; border: none; border-bottom: 1px solid var(--border); color: var(--text); }\n .lookup-item:hover { background: var(--primary); color: #fff; }\n\n .value { font-size: 36px; font-weight: 800; background: linear-gradient(to right, #fff, #aab4ff); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-top: 10px; }\n .title { color: var(--primary); font-size: 13px; font-weight: 800; text-transform: uppercase; letter-spacing: 1.5px; }\n\n .list-editor { background: var(--panel-2); border: 1px solid var(--border); border-radius: 12px; padding: 16px; display: grid; gap: 12px; }\n .list-item { display: grid; grid-template-columns: auto 1fr auto; gap: 12px; align-items: center; background: rgba(0,0,0,0.5); border: 1px solid var(--border); border-radius: 8px; padding: 10px 14px; transition: border-color 0.2s; }\n .list-item:hover { border-color: var(--primary); }\n .list-input { width: 100%; border: none; background: transparent; color: #fff; outline: none; font-size: 15px; }\n .drag-handle { color: var(--primary); font-size: 18px; }\n \n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"layout\">\n <nav class=\"sidebar floating-window\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </nav>\n\n <aside class=\"secondary-sidebar floating-window\">\n <header class=\"sidebar-header brand\">${safeName}</header>\n <div class=\"sidebar-content\">\n <div class=\"category-header\">System</div>\n <button id=\"tabHome\" class=\"channel-btn cursor-pointer active\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z\"></path><polyline points=\"9 22 9 12 15 12 15 22\"></polyline></svg>\n Dashboard\n </button>\n <button id=\"tabPlugins\" class=\"channel-btn cursor-pointer\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><path d=\"M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z\"></path></svg>\n Extensions\n </button>\n \n <div class=\"category-header\" style=\"margin-top: 32px;\">Modules</div>\n <div id=\"homeCategories\"></div>\n </div>\n \n <div class=\"user-area\" id=\"userMeta\">\n <div class=\"user-avatar-small\"></div>\n <div class=\"user-details\">\n <span class=\"name\">Initializing...</span>\n <span class=\"sub\">Standby</span>\n </div>\n </div>\n </aside>\n\n <main class=\"content\">\n <header class=\"topbar floating-window\">\n <span id=\"centerTitle\" style=\"background: linear-gradient(90deg, #fff, var(--muted)); -webkit-background-clip: text; -webkit-text-fill-color: transparent;\">Interface Active</span>\n </header>\n\n <div class=\"container\">\n <section id=\"homeArea\">\n <div class=\"section-title\">Configuration Matrix</div>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\" style=\"margin-top: 24px;\">Telemetry Data</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Active Extensions</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n\n <script>\n const dashboardConfig = ${scriptData};\n const state = { session: null, guilds: [], selectedGuildId: null, homeCategories: [], selectedHomeCategoryId: null, activeMainTab: \"home\" };\n\n const el = {\n serverRail: document.getElementById(\"serverRail\"), userMeta: document.getElementById(\"userMeta\"),\n centerTitle: document.getElementById(\"centerTitle\"), tabHome: document.getElementById(\"tabHome\"),\n tabPlugins: document.getElementById(\"tabPlugins\"), homeArea: document.getElementById(\"homeArea\"),\n pluginsArea: document.getElementById(\"pluginsArea\"), homeCategories: document.getElementById(\"homeCategories\"),\n homeSections: document.getElementById(\"homeSections\"), overviewArea: document.getElementById(\"overviewArea\"),\n overviewCards: document.getElementById(\"overviewCards\"), plugins: document.getElementById(\"plugins\")\n };\n\n const fetchJson = async (url, init) => {\n const response = await fetch(url, init);\n if (!response.ok) throw new Error(await response.text());\n return response.json();\n };\n\n const buildApiUrl = (path) => {\n if (!state.selectedGuildId) return dashboardConfig.basePath + path;\n const separator = path.includes(\"?\") ? \"&\" : \"?\";\n return dashboardConfig.basePath + path + separator + \"guildId=\" + encodeURIComponent(state.selectedGuildId);\n };\n\n const escapeHtml = (value) => String(value).replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n const normalizeBoxWidth = (value) => [50, 33, 20].includes(Number(value)) ? Number(value) : 100;\n\n const makeButton = (action, pluginId, panelId, panelElement) => {\n const button = document.createElement(\"button\");\n button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n let payload = {};\n if (action.collectFields && panelElement) {\n const values = {};\n panelElement.querySelectorAll(\"[data-plugin-field-id]\").forEach((inputEl) => {\n const fieldId = inputEl.dataset.pluginFieldId;\n if (fieldId) values[fieldId] = toFieldValue({ type: inputEl.dataset.pluginFieldType || \"text\" }, inputEl);\n });\n payload = { panelId, values };\n }\n\n const result = await fetchJson(buildApiUrl(\"/api/plugins/\" + encodeURIComponent(pluginId) + \"/\" + encodeURIComponent(action.id)), {\n method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify(payload)\n });\n if (result.message) alert(result.message);\n if (result.refresh) await refreshContent();\n } catch (error) { alert(error instanceof Error ? error.message : \"Action failed\"); } \n finally { button.disabled = false; }\n });\n return button;\n };\n\n const renderCards = (cards) => {\n if (!cards.length) { el.overviewCards.innerHTML = '<div style=\"color:var(--muted)\">Awaiting telemetry...</div>'; return; }\n el.overviewCards.innerHTML = cards.map((card) => \n '<article class=\"panel\"><div class=\"title\">' + escapeHtml(card.title) + '</div><div class=\"value\">' + escapeHtml(card.value) + '</div>' + \n (card.subtitle ? '<div class=\"subtitle\">' + escapeHtml(card.subtitle) + '</div>' : \"\") + '</article>'\n ).join(\"\");\n };\n\n const shortName = (name) => {\n if (!name) return \"?\";\n const parts = String(name).trim().split(/\\\\s+/).filter(Boolean);\n return parts.length === 1 ? parts[0].slice(0, 2).toUpperCase() : (parts[0][0] + parts[1][0]).toUpperCase();\n };\n\n const addAvatarOrFallback = (button, item) => {\n if (!item.avatarUrl) {\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n return;\n }\n const avatar = document.createElement(\"img\");\n avatar.className = \"server-avatar\";\n avatar.src = item.avatarUrl;\n avatar.alt = item.name;\n avatar.addEventListener(\"error\", () => {\n avatar.remove();\n const fallback = document.createElement(\"span\");\n fallback.className = \"server-fallback\";\n fallback.textContent = item.short;\n button.appendChild(fallback);\n });\n button.appendChild(avatar);\n };\n\n const renderServerRail = () => {\n const meItem = { id: null, name: \"Global Control\", short: \"HQ\", avatarUrl: state.session?.user?.avatarUrl ?? null, botInGuild: true };\n el.serverRail.innerHTML = \"\";\n \n const meBtn = document.createElement(\"button\");\n meBtn.className = \"server-item cursor-pointer\" + (null === state.selectedGuildId ? \" active\" : \"\");\n meBtn.title = meItem.name;\n \n const meInd = document.createElement(\"span\");\n meInd.className = \"server-item-indicator\";\n meBtn.appendChild(meInd);\n addAvatarOrFallback(meBtn, meItem);\n \n meBtn.addEventListener(\"click\", async () => {\n state.selectedGuildId = null;\n renderServerRail();\n updateContextLabel();\n await refreshContent();\n });\n \n el.serverRail.appendChild(meBtn);\n \n const sep = document.createElement(\"div\");\n sep.className = \"server-separator\";\n el.serverRail.appendChild(sep);\n\n state.guilds.forEach((guild) => {\n const item = { id: guild.id, name: guild.name, short: shortName(guild.name), avatarUrl: guild.iconUrl ?? null, botInGuild: guild.botInGuild !== false, inviteUrl: guild.inviteUrl };\n const button = document.createElement(\"button\");\n button.className = \"server-item cursor-pointer\" + (item.id === state.selectedGuildId ? \" active\" : \"\");\n button.title = item.id && !item.botInGuild ? (item.name + \" • Deploy Bot\") : item.name;\n\n const activeIndicator = document.createElement(\"span\");\n activeIndicator.className = \"server-item-indicator\";\n button.appendChild(activeIndicator);\n addAvatarOrFallback(button, item);\n\n if (item.id) {\n const status = document.createElement(\"span\");\n status.className = \"server-status\" + (item.botInGuild ? \"\" : \" offline\");\n button.appendChild(status);\n }\n\n button.addEventListener(\"click\", async () => {\n if (item.id && !item.botInGuild && item.inviteUrl) {\n const opened = window.open(item.inviteUrl, \"_blank\", \"noopener,noreferrer\");\n if (!opened) alert(\"Popup blocked. Please allow popups to open the deployment page.\");\n return;\n }\n state.selectedGuildId = item.id;\n renderServerRail();\n updateContextLabel();\n await refreshContent();\n });\n el.serverRail.appendChild(button);\n });\n };\n\n const applyMainTab = () => {\n const homeActive = state.activeMainTab === \"home\";\n el.homeArea.style.display = homeActive ? \"block\" : \"none\";\n el.pluginsArea.style.display = homeActive ? \"none\" : \"block\";\n el.tabHome.classList.toggle(\"active\", homeActive);\n el.tabPlugins.classList.toggle(\"active\", !homeActive);\n };\n\n const updateContextLabel = () => {\n if (!state.selectedGuildId) { el.centerTitle.textContent = \"Global Dashboard\"; return; }\n const selectedGuild = state.guilds.find((guild) => guild.id === state.selectedGuildId);\n el.centerTitle.textContent = selectedGuild ? selectedGuild.name : \"System Interface\";\n };\n\n const renderUserBlock = () => {\n const user = state.session?.user;\n if (!user) return;\n const avatarHtml = user.avatarUrl ? '<img src=\"'+user.avatarUrl+'\" style=\"width:100%;height:100%;border-radius:12px;object-fit:cover;\">' : '';\n el.userMeta.innerHTML = \n '<div class=\"user-avatar-small\">'+avatarHtml+'</div>' +\n '<div class=\"user-details\"><span class=\"name\">' + escapeHtml(user.global_name || user.username) + '</span><span class=\"sub\">' + state.guilds.length + ' Nodes</span></div>';\n };\n\n const renderHomeCategories = () => {\n el.homeCategories.innerHTML = \"\";\n if (!state.homeCategories.length) return;\n state.homeCategories.forEach((category) => {\n const button = document.createElement(\"button\");\n button.className = \"channel-btn cursor-pointer\" + (state.selectedHomeCategoryId === category.id ? \" active\" : \"\");\n button.innerHTML = '<svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\"><rect x=\"3\" y=\"3\" width=\"7\" height=\"7\"></rect><rect x=\"14\" y=\"3\" width=\"7\" height=\"7\"></rect><rect x=\"14\" y=\"14\" width=\"7\" height=\"7\"></rect><rect x=\"3\" y=\"14\" width=\"7\" height=\"7\"></rect></svg> ' + escapeHtml(category.label);\n button.title = category.description || category.label;\n button.addEventListener(\"click\", async () => { state.selectedHomeCategoryId = category.id; renderHomeCategories(); await refreshContent(); });\n el.homeCategories.appendChild(button);\n });\n };\n\n const toFieldValue = (field, element) => {\n if (field.type === \"string-list\") { try { return JSON.parse(element.dataset.listValues || \"[]\"); } catch { return []; } }\n if ([\"role-search\", \"channel-search\", \"member-search\"].includes(field.type)) { try { return JSON.parse(element.dataset.selectedObject || \"null\"); } catch { return null; } }\n if (field.type === \"boolean\") return Boolean(element.checked);\n if (field.type === \"number\") return element.value === \"\" ? null : Number(element.value);\n return element.value;\n };\n\n const setupStringListField = (field, input, fieldWrap) => {\n const editor = document.createElement(\"div\"); editor.className = \"list-editor\";\n const itemsWrap = document.createElement(\"div\"); itemsWrap.className = \"list-items\";\n const addButton = document.createElement(\"button\"); addButton.type = \"button\"; addButton.className = \"list-add cursor-pointer\"; addButton.textContent = \"+ Add Sequence\";\n\n const normalizeValues = () => { input.dataset.listValues = JSON.stringify(Array.from(itemsWrap.querySelectorAll(\".list-input\")).map(i => i.value.trim()).filter(i => i.length > 0)); };\n\n const makeRow = (value = \"\") => {\n const row = document.createElement(\"div\"); row.className = \"list-item\"; row.draggable = true;\n const handle = document.createElement(\"span\"); handle.className = \"drag-handle cursor-pointer\"; handle.innerHTML = \"⣿\";\n const textInput = document.createElement(\"input\"); textInput.type = \"text\"; textInput.className = \"list-input\"; textInput.value = value;\n textInput.addEventListener(\"input\", normalizeValues);\n const removeBtn = document.createElement(\"button\"); removeBtn.type = \"button\"; removeBtn.className = \"cursor-pointer\"; removeBtn.textContent = \"×\"; removeBtn.style.padding = \"4px 8px\"; removeBtn.style.background = \"transparent\"; removeBtn.style.color = \"var(--danger)\";\n removeBtn.addEventListener(\"click\", () => { row.remove(); normalizeValues(); });\n row.append(handle, textInput, removeBtn);\n return row;\n };\n\n const initialValues = Array.isArray(field.value) ? field.value.map(String) : [];\n if (!initialValues.length) initialValues.push(\"Value_01\");\n initialValues.forEach(v => itemsWrap.appendChild(makeRow(v)));\n\n addButton.addEventListener(\"click\", () => { itemsWrap.appendChild(makeRow(\"\")); normalizeValues(); });\n editor.append(itemsWrap, addButton); fieldWrap.appendChild(editor); normalizeValues();\n };\n\n const showLookupResults = (container, items, labelResolver, onSelect) => {\n container.innerHTML = \"\";\n if (!items.length) { container.style.display = \"none\"; return; }\n items.forEach(item => {\n const btn = document.createElement(\"button\"); btn.type = \"button\"; btn.className = \"lookup-item cursor-pointer\"; btn.textContent = labelResolver(item);\n btn.addEventListener(\"click\", () => { onSelect(item); container.style.display = \"none\"; });\n container.appendChild(btn);\n });\n container.style.display = \"block\";\n };\n\n const setupLookupField = (field, input, fieldWrap) => {\n const wrap = document.createElement(\"div\"); wrap.className = \"lookup-wrap\";\n const results = document.createElement(\"div\"); results.className = \"lookup-results\";\n wrap.append(input, results); fieldWrap.appendChild(wrap);\n\n const runSearch = async () => {\n const query = String(input.value || \"\");\n if (query.length < 1) { results.style.display = \"none\"; return; }\n try {\n const params = new URLSearchParams({ q: query, limit: \"10\" });\n const ep = field.type === \"role-search\" ? \"roles\" : field.type === \"channel-search\" ? \"channels\" : \"members\";\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/\" + ep + \"?\" + params));\n \n showLookupResults(results, payload[ep] || [], \n (item) => field.type === \"channel-search\" ? \"#\"+item.name : (item.name || item.user?.username), \n (item) => { input.value = item.name || item.user?.username; input.dataset.selectedObject = JSON.stringify(item); }\n );\n } catch { results.style.display = \"none\"; }\n };\n input.addEventListener(\"input\", () => { input.dataset.selectedObject = \"\"; runSearch(); });\n input.addEventListener(\"blur\", () => setTimeout(() => { results.style.display = \"none\"; }, 150));\n };\n\n const renderHomeSections = (sections) => {\n el.homeSections.innerHTML = \"\";\n if (!sections.length) return;\n \n sections.forEach((section) => {\n const wrap = document.createElement(\"article\");\n wrap.className = \"panel home-section-panel home-width-\" + normalizeBoxWidth(section.width);\n\n const heading = document.createElement(\"h3\"); heading.textContent = section.title; heading.style.margin = \"0\"; wrap.appendChild(heading);\n if (section.description) { const desc = document.createElement(\"div\"); desc.className = \"subtitle\"; desc.textContent = section.description; wrap.appendChild(desc); }\n\n if (section.fields?.length) {\n const fieldsWrap = document.createElement(\"div\"); fieldsWrap.className = \"home-fields\";\n\n section.fields.forEach((field) => {\n const fieldWrap = document.createElement(\"div\"); fieldWrap.className = \"home-field\";\n const label = document.createElement(\"label\"); label.textContent = field.label; fieldWrap.appendChild(label);\n\n let input;\n if (field.type === \"textarea\") {\n input = document.createElement(\"textarea\"); input.className = \"home-textarea\"; input.value = field.value == null ? \"\" : String(field.value);\n } else if (field.type === \"select\") {\n input = document.createElement(\"select\"); input.className = \"home-select cursor-pointer\";\n (field.options || []).forEach((opt) => {\n const optionEl = document.createElement(\"option\"); optionEl.value = opt.value; optionEl.textContent = opt.label;\n if (String(field.value ?? \"\") === opt.value) optionEl.selected = true; input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\"); row.className = \"home-field-row\";\n input = document.createElement(\"input\"); input.type = \"checkbox\"; input.className = \"home-checkbox cursor-pointer\"; input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\"); stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\");\n row.append(input, stateText); fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\"); input.className = \"home-input\"; input.type = field.type === \"number\" ? \"number\" : field.type === \"url\" ? \"url\" : \"text\"; input.value = field.value == null ? \"\" : String(field.value);\n }\n\n if (input) {\n input.dataset.homeFieldId = field.id; input.dataset.homeFieldType = field.type;\n if (field.placeholder) input.placeholder = field.placeholder;\n if (field.readOnly) { input.readOnly = true; input.disabled = true; input.style.opacity = \"0.6\"; }\n if ([\"role-search\", \"channel-search\", \"member-search\"].includes(field.type)) setupLookupField(field, input, fieldWrap);\n else if (field.type === \"string-list\") setupStringListField(field, input, fieldWrap);\n else if (field.type !== \"boolean\") fieldWrap.appendChild(input);\n }\n fieldsWrap.appendChild(fieldWrap);\n });\n wrap.appendChild(fieldsWrap);\n }\n\n if (section.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n section.actions.forEach((action) => {\n const button = document.createElement(\"button\"); button.textContent = action.label;\n const variantClass = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\n button.className = [variantClass, \"cursor-pointer\"].filter(Boolean).join(\" \");\n button.addEventListener(\"click\", async () => {\n button.disabled = true;\n try {\n const values = {};\n wrap.querySelectorAll(\"[data-home-field-id]\").forEach((el) => { values[el.dataset.homeFieldId] = toFieldValue({ type: el.dataset.homeFieldType }, el); });\n const result = await fetchJson(buildApiUrl(\"/api/home/\" + encodeURIComponent(action.id)), { method: \"POST\", headers: { \"Content-Type\": \"application/json\" }, body: JSON.stringify({ sectionId: section.id, values }) });\n if (result.message) alert(result.message);\n if (result.refresh) await refreshContent();\n } catch (error) { alert(\"Execution Failed\"); } finally { button.disabled = false; }\n });\n actions.appendChild(button);\n });\n wrap.appendChild(actions);\n }\n el.homeSections.appendChild(wrap);\n });\n };\n\n const renderPlugins = (plugins) => {\n el.plugins.innerHTML = \"\";\n if (!plugins.length) { el.plugins.innerHTML = '<div style=\"color:var(--muted)\">No modules online.</div>'; return; }\n \n plugins.forEach((plugin) => {\n const wrap = document.createElement(\"article\"); wrap.className = \"panel\";\n const heading = document.createElement(\"h3\"); heading.textContent = plugin.name; heading.style.margin=\"0\"; wrap.appendChild(heading);\n if (plugin.description) { const desc = document.createElement(\"div\"); desc.className = \"subtitle\"; desc.textContent = plugin.description; wrap.appendChild(desc); }\n\n (plugin.panels || []).forEach((panel) => {\n const pBody = document.createElement(\"div\"); pBody.style.marginTop = \"24px\";\n const pTitle = document.createElement(\"h4\"); pTitle.textContent = panel.title; pTitle.style.margin = \"0 0 12px 0\"; pTitle.style.color = \"var(--primary)\"; pTitle.style.textTransform = \"uppercase\"; pBody.appendChild(pTitle);\n \n if (panel.fields?.length) {\n const fWrap = document.createElement(\"div\"); fWrap.className = \"home-fields\";\n panel.fields.forEach((field) => {\n const fw = document.createElement(\"div\"); fw.className = \"home-field\";\n if (!field.editable) {\n fw.innerHTML = '<label>' + escapeHtml(field.label) + '</label><div style=\"padding:14px 18px;background:var(--panel-2);border-radius:12px;border:1px solid var(--border);\">' + escapeHtml(field.value) + '</div>';\n } else {\n fw.innerHTML = '<label>' + escapeHtml(field.label) + '</label><input type=\"text\" class=\"home-input\" data-plugin-field-id=\"'+field.id+'\" value=\"'+escapeHtml(field.value)+'\">';\n }\n fWrap.appendChild(fw);\n });\n pBody.appendChild(fWrap);\n }\n\n if (panel.actions?.length) {\n const actions = document.createElement(\"div\"); actions.className = \"actions\";\n panel.actions.forEach((act) => actions.appendChild(makeButton(act, plugin.id, panel.id, pBody)));\n pBody.appendChild(actions);\n }\n wrap.appendChild(pBody);\n });\n el.plugins.appendChild(wrap);\n });\n };\n\n const refreshContent = async () => {\n const categoriesPayload = await fetchJson(buildApiUrl(\"/api/home/categories\"));\n state.homeCategories = categoriesPayload.categories || [];\n if (!state.selectedHomeCategoryId || !state.homeCategories.some(i => i.id === state.selectedHomeCategoryId)) {\n state.selectedHomeCategoryId = state.homeCategories.find(i => i.id === \"overview\")?.id || state.homeCategories[0]?.id || null;\n }\n renderHomeCategories();\n\n const homePath = state.selectedHomeCategoryId ? \"/api/home?categoryId=\" + encodeURIComponent(state.selectedHomeCategoryId) : \"/api/home\";\n const [home, overview, plugins] = await Promise.all([ fetchJson(buildApiUrl(homePath)), fetchJson(buildApiUrl(\"/api/overview\")), fetchJson(buildApiUrl(\"/api/plugins\")) ]);\n\n renderHomeSections(home.sections || []);\n el.overviewArea.style.display = state.selectedHomeCategoryId === \"overview\" ? \"block\" : \"none\";\n renderCards(overview.cards || []);\n renderPlugins(plugins.plugins || []);\n };\n\n const loadInitialData = async () => {\n const session = await fetchJson(dashboardConfig.basePath + \"/api/session\");\n if (!session.authenticated) { window.location.href = dashboardConfig.basePath + \"/login\"; return; }\n\n state.session = session;\n const guilds = await fetchJson(dashboardConfig.basePath + \"/api/guilds\");\n state.guilds = guilds.guilds || [];\n \n renderUserBlock();\n renderServerRail();\n updateContextLabel();\n\n el.tabHome.addEventListener(\"click\", () => { state.activeMainTab = \"home\"; applyMainTab(); });\n el.tabPlugins.addEventListener(\"click\", () => { state.activeMainTab = \"plugins\"; applyMainTab(); });\n\n applyMainTab();\n await refreshContent();\n };\n\n loadInitialData().catch(console.error);\n </script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderHologramLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#04060c\"};\n --rail: ${design.rail ?? \"#080b14\"};\n --content-bg: ${design.contentBg ?? \"#060912\"};\n --panel: ${design.panel ?? \"rgba(14, 20, 36, 0.45)\"};\n --panel-2: ${design.panel2 ?? \"rgba(10, 16, 30, 0.35)\"};\n --text: ${design.text ?? \"#e8edf6\"};\n --muted: ${design.muted ?? \"#7a8bb0\"};\n --primary: ${design.primary ?? \"#60a5fa\"};\n --primary-glow: rgba(96, 165, 250, 0.3);\n --accent: ${design.accent ?? \"#a78bfa\"};\n --accent-glow: rgba(167, 139, 250, 0.25);\n --holo-1: rgba(96, 165, 250, 0.12);\n --holo-2: rgba(167, 139, 250, 0.10);\n --holo-3: rgba(52, 211, 153, 0.08);\n --holo-4: rgba(244, 114, 182, 0.06);\n --success: ${design.success ?? \"#34d399\"};\n --warning: ${design.warning ?? \"#fbbf24\"};\n --danger: ${design.danger ?? \"#f87171\"};\n --border: ${design.border ?? \"rgba(96, 165, 250, 0.08)\"};\n }\n\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: 'Inter', ui-sans-serif, system-ui, sans-serif;\n background: var(--bg);\n color: var(--text);\n overflow: hidden;\n height: 100vh;\n }\n\n /* Holographic shimmer background */\n body::before {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background:\n radial-gradient(ellipse 80% 50% at 15% 20%, var(--holo-1), transparent),\n radial-gradient(ellipse 60% 40% at 75% 15%, var(--holo-2), transparent),\n radial-gradient(ellipse 70% 45% at 50% 80%, var(--holo-3), transparent),\n radial-gradient(ellipse 50% 30% at 90% 60%, var(--holo-4), transparent);\n animation: holoShift 16s ease-in-out infinite alternate;\n }\n\n @keyframes holoShift {\n 0% { opacity: 0.8; filter: hue-rotate(0deg); }\n 33% { opacity: 1; filter: hue-rotate(15deg); }\n 66% { opacity: 0.9; filter: hue-rotate(-10deg); }\n 100% { opacity: 0.85; filter: hue-rotate(5deg); }\n }\n\n /* Floating particle effect via dot grid */\n body::after {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background-image: radial-gradient(circle, rgba(96, 165, 250, 0.06) 1px, transparent 1px);\n background-size: 32px 32px;\n mask-image: radial-gradient(ellipse at center, rgba(0,0,0,0.5), transparent 70%);\n -webkit-mask-image: radial-gradient(ellipse at center, rgba(0,0,0,0.5), transparent 70%);\n }\n\n /* ─── SHELL ─── */\n .shell {\n position: relative;\n z-index: 1;\n height: 100vh;\n display: grid;\n grid-template-rows: auto 1fr;\n }\n\n /* ─── TOP BAR ─── */\n .topbar {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: 20px;\n padding: 0 24px;\n height: 54px;\n background: rgba(4, 6, 12, 0.65);\n backdrop-filter: blur(24px) saturate(1.2); -webkit-backdrop-filter: blur(24px) saturate(1.2);\n border-bottom: 1px solid var(--border);\n position: relative;\n }\n\n /* Holographic rainbow line */\n .topbar::after {\n content: \"\";\n position: absolute;\n bottom: -1px; left: 0; right: 0;\n height: 1px;\n background: linear-gradient(90deg,\n transparent,\n rgba(96, 165, 250, 0.5),\n rgba(167, 139, 250, 0.5),\n rgba(52, 211, 153, 0.4),\n rgba(244, 114, 182, 0.3),\n transparent\n );\n }\n\n .brand {\n font-weight: 800;\n font-size: 15px;\n letter-spacing: 0.4px;\n background: linear-gradient(135deg, var(--primary), var(--accent), #34d399);\n background-size: 200% 200%;\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n animation: brandShimmer 4s ease-in-out infinite;\n }\n\n @keyframes brandShimmer {\n 0%, 100% { background-position: 0% 50%; }\n 50% { background-position: 100% 50%; }\n }\n\n .center-title {\n text-align: center;\n font-weight: 600;\n font-size: 13px;\n color: var(--muted);\n letter-spacing: 0.3px;\n }\n\n .pill {\n display: flex; align-items: center; gap: 8px;\n padding: 5px 14px;\n border-radius: 999px;\n border: 1px solid var(--border);\n background: rgba(14, 20, 36, 0.4);\n backdrop-filter: blur(12px);\n font-size: 12px;\n font-weight: 600;\n color: var(--muted);\n }\n\n /* ─── BODY LAYOUT ─── */\n .layout {\n display: grid;\n grid-template-columns: 72px 1fr;\n min-height: 0;\n overflow: hidden;\n }\n\n /* ─── SERVER RAIL ─── */\n .sidebar {\n background: rgba(4, 6, 12, 0.5);\n backdrop-filter: blur(16px);\n border-right: 1px solid var(--border);\n padding: 14px 0;\n overflow-y: auto;\n scrollbar-width: none;\n }\n .sidebar::-webkit-scrollbar { display: none; }\n\n .server-rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n min-height: min-content;\n }\n\n .server-item {\n position: relative;\n width: 46px !important; height: 46px !important;\n min-width: 46px !important; min-height: 46px !important;\n flex: 0 0 46px !important;\n border-radius: 14px;\n border: 1px solid var(--border);\n background: rgba(14, 20, 36, 0.4);\n backdrop-filter: blur(8px);\n color: var(--text);\n font-weight: 700;\n font-size: 13px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: visible;\n padding: 0;\n }\n .server-item:hover {\n border-color: rgba(96, 165, 250, 0.35);\n background: rgba(96, 165, 250, 0.08);\n transform: translateY(-2px) scale(1.04);\n box-shadow: 0 4px 20px rgba(96, 165, 250, 0.1);\n }\n .server-item.active {\n border-color: rgba(167, 139, 250, 0.5);\n background: linear-gradient(135deg, rgba(96, 165, 250, 0.12), rgba(167, 139, 250, 0.1));\n box-shadow:\n 0 0 0 1px rgba(167, 139, 250, 0.15),\n 0 4px 20px rgba(167, 139, 250, 0.12),\n inset 0 0 20px rgba(96, 165, 250, 0.05);\n }\n\n .server-item-indicator {\n position: absolute;\n left: -10px; top: 50%;\n transform: translateY(-50%);\n width: 3px; height: 0;\n border-radius: 999px;\n background: linear-gradient(180deg, var(--primary), var(--accent));\n box-shadow: 0 0 8px var(--primary-glow);\n opacity: 0;\n transition: all 0.25s ease;\n }\n .server-item.active .server-item-indicator {\n opacity: 1; height: 24px;\n }\n\n .server-avatar {\n width: 100%; height: 100%;\n object-fit: cover;\n border-radius: inherit;\n }\n .server-fallback {\n display: grid; place-items: center;\n width: 100%; height: 100%;\n font-size: 12px; font-weight: 700;\n color: var(--primary);\n }\n .server-status {\n position: absolute;\n right: -3px; bottom: -3px;\n width: 10px; height: 10px;\n border-radius: 999px;\n border: 2px solid rgba(4, 6, 12, 0.8);\n background: var(--success);\n box-shadow: 0 0 6px rgba(52, 211, 153, 0.4);\n }\n .server-status.offline { background: var(--muted); box-shadow: none; }\n\n /* ─── CONTENT ─── */\n .content {\n min-width: 0;\n padding: 20px;\n overflow-y: auto;\n scrollbar-width: thin;\n scrollbar-color: rgba(96, 165, 250, 0.15) transparent;\n }\n .content::-webkit-scrollbar { width: 4px; }\n .content::-webkit-scrollbar-thumb { background: rgba(96, 165, 250, 0.15); border-radius: 2px; }\n\n .container {\n background: var(--panel);\n backdrop-filter: blur(20px) saturate(1.1); -webkit-backdrop-filter: blur(20px) saturate(1.1);\n border: 1px solid var(--border);\n border-radius: 20px;\n padding: 20px;\n position: relative;\n overflow: hidden;\n }\n\n /* Iridescent top border on container */\n .container::before {\n content: \"\";\n position: absolute;\n top: 0; left: 0; right: 0;\n height: 1px;\n background: linear-gradient(90deg,\n transparent 5%,\n rgba(96, 165, 250, 0.4),\n rgba(167, 139, 250, 0.4),\n rgba(52, 211, 153, 0.3),\n transparent 95%\n );\n }\n\n /* ─── TABS ─── */\n .main-tabs {\n display: inline-flex;\n gap: 4px;\n padding: 3px;\n border-radius: 12px;\n background: rgba(0, 0, 0, 0.25);\n border: 1px solid var(--border);\n margin-bottom: 18px;\n }\n\n button {\n border: 1px solid transparent;\n background: transparent;\n color: var(--muted);\n border-radius: 10px;\n padding: 7px 16px;\n font-size: 13px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n button:hover { color: var(--text); background: rgba(96, 165, 250, 0.06); }\n\n button.primary {\n background: linear-gradient(135deg, var(--primary), var(--accent));\n border: none; color: #fff;\n font-weight: 700;\n box-shadow: 0 2px 14px var(--primary-glow);\n }\n button.primary:hover {\n filter: brightness(1.1);\n box-shadow: 0 4px 22px var(--primary-glow);\n }\n button.danger {\n background: rgba(248, 113, 113, 0.1);\n border-color: rgba(248, 113, 113, 0.25);\n color: var(--danger);\n }\n\n .main-tab.active, .home-category-btn.active {\n background: linear-gradient(135deg, rgba(96, 165, 250, 0.15), rgba(167, 139, 250, 0.12));\n border-color: rgba(96, 165, 250, 0.3);\n color: var(--primary);\n box-shadow: 0 0 10px rgba(96, 165, 250, 0.08);\n }\n\n /* ─── SECTION ─── */\n .section-title {\n font-size: 16px;\n font-weight: 700;\n margin: 14px 0 12px;\n letter-spacing: 0.2px;\n background: linear-gradient(135deg, var(--text), var(--primary));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n\n /* ─── GRID & CARDS ─── */\n .grid { display: grid; gap: 12px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); }\n\n .panel {\n background: var(--panel);\n backdrop-filter: blur(16px) saturate(1.1); -webkit-backdrop-filter: blur(16px) saturate(1.1);\n border: 1px solid var(--border);\n border-radius: 16px;\n padding: 18px;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n overflow: hidden;\n }\n\n /* Holographic shimmer on panels */\n .panel::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: linear-gradient(135deg,\n rgba(96, 165, 250, 0.04),\n rgba(167, 139, 250, 0.03),\n rgba(52, 211, 153, 0.02)\n );\n opacity: 0;\n transition: opacity 0.3s ease;\n pointer-events: none;\n }\n .panel:hover {\n transform: translateY(-3px);\n border-color: rgba(96, 165, 250, 0.2);\n box-shadow:\n 0 8px 28px rgba(0, 0, 0, 0.25),\n 0 0 20px rgba(96, 165, 250, 0.05),\n 0 0 40px rgba(167, 139, 250, 0.03);\n }\n .panel:hover::before { opacity: 1; }\n\n .title {\n color: var(--primary);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1.2px;\n }\n .value {\n font-size: 24px;\n font-weight: 800;\n margin-top: 6px;\n background: linear-gradient(135deg, #fff, var(--primary), var(--accent));\n background-size: 200% 200%;\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n animation: valueShimmer 6s ease-in-out infinite;\n }\n\n @keyframes valueShimmer {\n 0%, 100% { background-position: 0% 50%; }\n 50% { background-position: 100% 50%; }\n }\n\n .subtitle {\n margin-top: 6px;\n color: var(--muted);\n font-size: 12px;\n line-height: 1.5;\n }\n\n /* ─── HOME SECTIONS ─── */\n .home-categories {\n display: flex;\n gap: 6px;\n flex-wrap: wrap;\n margin-bottom: 14px;\n }\n\n .home-sections {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n margin-bottom: 16px;\n }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 6px); max-width: calc(50% - 6px); }\n .home-width-33 { flex-basis: calc(33.333% - 8px); max-width: calc(33.333% - 8px); }\n .home-width-20 { flex-basis: calc(20% - 9.6px); max-width: calc(20% - 9.6px); }\n\n /* ─── FORM ELEMENTS ─── */\n .home-fields, .plugin-fields { display: grid; gap: 10px; margin-top: 12px; }\n .home-field, .plugin-field { display: grid; gap: 5px; }\n .home-field label, .plugin-field > label {\n color: var(--muted);\n font-size: 11px; font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .home-input, .home-textarea, .home-select {\n width: 100%;\n border: 1px solid var(--border);\n background: rgba(0, 0, 0, 0.2);\n backdrop-filter: blur(8px);\n color: var(--text);\n border-radius: 10px;\n padding: 10px 14px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n transition: all 0.2s ease;\n }\n .home-input:focus, .home-textarea:focus, .home-select:focus {\n border-color: var(--primary);\n box-shadow: 0 0 0 2px var(--primary-glow), 0 0 16px rgba(96, 165, 250, 0.06);\n background: rgba(0, 0, 0, 0.3);\n }\n .home-textarea { min-height: 80px; resize: vertical; }\n .home-checkbox { width: 16px; height: 16px; accent-color: var(--primary); }\n .home-field-row { display: flex; align-items: center; gap: 8px; }\n .home-message { margin-top: 6px; color: var(--muted); font-size: 12px; }\n\n .lookup-wrap { position: relative; }\n .lookup-results {\n position: absolute; left: 0; right: 0;\n top: calc(100% + 4px);\n z-index: 20;\n border: 1px solid var(--border);\n background: rgba(4, 6, 12, 0.95);\n backdrop-filter: blur(20px);\n border-radius: 12px;\n max-height: 200px;\n overflow: auto;\n display: none;\n box-shadow: 0 12px 36px rgba(0, 0, 0, 0.5), 0 0 20px rgba(96, 165, 250, 0.05);\n }\n .lookup-item {\n width: 100%; border: none; border-radius: 0;\n text-align: left; padding: 9px 14px;\n background: transparent; color: var(--text);\n font-size: 13px;\n }\n .lookup-item:hover { background: rgba(96, 165, 250, 0.08); }\n .lookup-selected { margin-top: 4px; font-size: 11px; color: var(--muted); }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }\n\n .kv-item {\n display: flex; justify-content: space-between;\n border: 1px solid var(--border); border-radius: 10px;\n padding: 8px 12px; background: var(--panel-2);\n font-size: 13px; backdrop-filter: blur(8px);\n }\n .list-editor {\n border: 1px solid var(--border); border-radius: 12px;\n background: rgba(0, 0, 0, 0.15);\n backdrop-filter: blur(8px);\n padding: 10px; display: grid; gap: 8px;\n }\n .list-items { display: grid; gap: 6px; }\n .list-item {\n display: grid; grid-template-columns: auto 1fr auto;\n gap: 8px; align-items: center;\n border: 1px solid var(--border); border-radius: 8px;\n padding: 7px 10px; background: rgba(14, 20, 36, 0.3);\n transition: border-color 0.2s ease;\n }\n .list-item:hover { border-color: rgba(96, 165, 250, 0.2); }\n .list-item.dragging { opacity: 0.5; }\n .drag-handle { color: var(--muted); user-select: none; font-size: 14px; }\n .list-input { width: 100%; border: none; outline: none; background: transparent; color: var(--text); font-size: 13px; font-family: inherit; }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: 13px; padding: 8px 0; }\n .cursor-pointer { cursor: pointer; }\n\n @media (max-width: 900px) {\n .layout { grid-template-columns: 60px 1fr; }\n .server-item {\n width: 38px !important; height: 38px !important;\n min-width: 38px !important; min-height: 38px !important;\n flex: 0 0 38px !important;\n border-radius: 12px;\n }\n .home-width-50, .home-width-33, .home-width-20 { flex-basis: 100%; max-width: 100%; }\n }\n @media (max-width: 640px) {\n .layout { grid-template-columns: 1fr; }\n .sidebar { display: none; }\n .topbar { grid-template-columns: 1fr auto; }\n .center-title { display: none; }\n }\n\n @keyframes fadeUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .container { animation: fadeUp 0.35s ease-out; }\n\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"shell\">\n <header class=\"topbar\">\n <div class=\"brand\">${safeName}</div>\n <div id=\"centerTitle\" class=\"center-title\">Holographic Interface</div>\n <div id=\"userMeta\" class=\"pill\">Loading...</div>\n </header>\n\n <div class=\"layout\">\n <aside class=\"sidebar\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </aside>\n\n <main class=\"content\">\n <div class=\"container\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">Plugins</button>\n </div>\n\n <section id=\"homeArea\">\n <div class=\"section-title\">System Interface</div>\n <section id=\"homeCategories\" class=\"home-categories\"></section>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">Telemetry</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Extensions</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderNebulaLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#080410\"};\n --rail: ${design.rail ?? \"#0c0816\"};\n --content-bg: ${design.contentBg ?? \"#0a0614\"};\n --panel: ${design.panel ?? \"rgba(20, 12, 36, 0.7)\"};\n --panel-2: ${design.panel2 ?? \"rgba(28, 16, 48, 0.5)\"};\n --text: ${design.text ?? \"#f0e8ff\"};\n --muted: ${design.muted ?? \"#9b8ab8\"};\n --primary: ${design.primary ?? \"#d946ef\"};\n --primary-glow: rgba(217, 70, 239, 0.35);\n --accent: ${design.accent ?? \"#818cf8\"};\n --accent-glow: rgba(129, 140, 248, 0.25);\n --success: ${design.success ?? \"#34d399\"};\n --warning: ${design.warning ?? \"#fbbf24\"};\n --danger: ${design.danger ?? \"#fb7185\"};\n --border: ${design.border ?? \"rgba(217, 70, 239, 0.1)\"};\n }\n\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n body {\n font-family: 'Outfit', ui-sans-serif, system-ui, sans-serif;\n background: var(--bg);\n color: var(--text);\n overflow: hidden;\n height: 100vh;\n }\n\n /* Nebula background — multiple cosmic clouds */\n body::before {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background:\n radial-gradient(ellipse 100% 60% at 20% 15%, rgba(217, 70, 239, 0.14), transparent),\n radial-gradient(ellipse 80% 50% at 75% 25%, rgba(129, 140, 248, 0.12), transparent),\n radial-gradient(ellipse 90% 70% at 50% 85%, rgba(236, 72, 153, 0.08), transparent),\n radial-gradient(ellipse 60% 40% at 90% 70%, rgba(99, 102, 241, 0.06), transparent);\n animation: nebulaFloat 20s ease-in-out infinite alternate;\n }\n\n @keyframes nebulaFloat {\n 0% { transform: scale(1) translate(0, 0); opacity: 0.8; }\n 50% { transform: scale(1.05) translate(-1%, 1%); opacity: 1; }\n 100% { transform: scale(0.98) translate(1%, -1%); opacity: 0.85; }\n }\n\n /* Star field overlay */\n body::after {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background-image:\n radial-gradient(1px 1px at 10% 20%, rgba(255, 255, 255, 0.3), transparent),\n radial-gradient(1px 1px at 30% 60%, rgba(255, 255, 255, 0.2), transparent),\n radial-gradient(1px 1px at 50% 10%, rgba(255, 255, 255, 0.25), transparent),\n radial-gradient(1px 1px at 70% 40%, rgba(255, 255, 255, 0.15), transparent),\n radial-gradient(1px 1px at 85% 75%, rgba(255, 255, 255, 0.2), transparent),\n radial-gradient(1.5px 1.5px at 15% 80%, rgba(217, 70, 239, 0.4), transparent),\n radial-gradient(1.5px 1.5px at 60% 85%, rgba(129, 140, 248, 0.3), transparent),\n radial-gradient(1px 1px at 40% 35%, rgba(255, 255, 255, 0.2), transparent),\n radial-gradient(1px 1px at 90% 15%, rgba(255, 255, 255, 0.15), transparent),\n radial-gradient(1.5px 1.5px at 25% 45%, rgba(236, 72, 153, 0.3), transparent);\n animation: starTwinkle 8s ease-in-out infinite alternate;\n }\n\n @keyframes starTwinkle {\n 0%, 100% { opacity: 0.6; }\n 50% { opacity: 1; }\n }\n\n /* ─── SHELL ─── */\n .shell {\n position: relative;\n z-index: 1;\n height: 100vh;\n display: grid;\n grid-template-columns: 80px 1fr;\n padding: 16px;\n gap: 16px;\n }\n\n /* ─── SERVER RAIL (floating) ─── */\n .sidebar {\n background: var(--panel);\n backdrop-filter: blur(20px) saturate(1.2); -webkit-backdrop-filter: blur(20px) saturate(1.2);\n border: 1px solid var(--border);\n border-radius: 20px;\n padding: 16px 0;\n overflow-y: auto;\n scrollbar-width: none;\n position: relative;\n }\n .sidebar::-webkit-scrollbar { display: none; }\n\n /* Glowing edge */\n .sidebar::before {\n content: \"\";\n position: absolute;\n top: 10%; bottom: 10%; right: -1px;\n width: 1px;\n background: linear-gradient(180deg, transparent, rgba(217, 70, 239, 0.4), rgba(129, 140, 248, 0.3), transparent);\n }\n\n .server-rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n min-height: min-content;\n }\n\n .server-item {\n position: relative;\n width: 50px !important; height: 50px !important;\n min-width: 50px !important; min-height: 50px !important;\n flex: 0 0 50px !important;\n border-radius: 16px;\n border: 1px solid rgba(217, 70, 239, 0.08);\n background: rgba(28, 16, 48, 0.5);\n color: var(--text);\n font-weight: 700;\n font-size: 14px;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n overflow: visible;\n padding: 0;\n }\n .server-item:hover {\n border-color: rgba(217, 70, 239, 0.4);\n background: rgba(217, 70, 239, 0.08);\n transform: translateY(-2px) scale(1.05);\n box-shadow: 0 6px 24px rgba(217, 70, 239, 0.15);\n }\n .server-item.active {\n border-color: var(--primary);\n background: linear-gradient(135deg, rgba(217, 70, 239, 0.15), rgba(129, 140, 248, 0.1));\n box-shadow:\n 0 0 0 1px rgba(217, 70, 239, 0.2),\n 0 6px 28px rgba(217, 70, 239, 0.18),\n 0 0 40px rgba(217, 70, 239, 0.06);\n }\n\n .server-item-indicator {\n position: absolute;\n left: -12px; top: 50%;\n transform: translateY(-50%);\n width: 4px; height: 0;\n border-radius: 0 4px 4px 0;\n background: linear-gradient(180deg, var(--primary), var(--accent));\n box-shadow: 0 0 10px var(--primary-glow);\n opacity: 0;\n transition: all 0.3s ease;\n }\n .server-item.active .server-item-indicator {\n opacity: 1; height: 28px;\n }\n\n .server-avatar {\n width: 100%; height: 100%;\n object-fit: cover;\n border-radius: inherit;\n }\n .server-fallback {\n display: grid; place-items: center;\n width: 100%; height: 100%;\n font-size: 12px; font-weight: 800;\n background: linear-gradient(135deg, rgba(217, 70, 239, 0.15), rgba(129, 140, 248, 0.1));\n border-radius: inherit;\n color: var(--primary);\n }\n .server-status {\n position: absolute;\n right: -3px; bottom: -3px;\n width: 11px; height: 11px;\n border-radius: 999px;\n border: 2px solid rgba(8, 4, 16, 0.8);\n background: var(--success);\n box-shadow: 0 0 8px rgba(52, 211, 153, 0.5);\n }\n .server-status.offline { background: var(--muted); box-shadow: none; }\n\n /* ─── CONTENT ─── */\n .content {\n display: flex;\n flex-direction: column;\n min-width: 0;\n gap: 16px;\n overflow: hidden;\n }\n\n /* Top bar — floating island */\n .topbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 0 24px;\n height: 60px;\n background: var(--panel);\n backdrop-filter: blur(20px) saturate(1.2); -webkit-backdrop-filter: blur(20px) saturate(1.2);\n border: 1px solid var(--border);\n border-radius: 18px;\n flex-shrink: 0;\n position: relative;\n overflow: hidden;\n }\n\n /* Gradient sweep across topbar */\n .topbar::before {\n content: \"\";\n position: absolute;\n inset: 0;\n background: linear-gradient(90deg, rgba(217, 70, 239, 0.04), transparent 30%, transparent 70%, rgba(129, 140, 248, 0.04));\n pointer-events: none;\n }\n\n .topbar-left {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n\n .brand {\n font-weight: 800;\n font-size: 17px;\n letter-spacing: 0.3px;\n background: linear-gradient(135deg, var(--primary), var(--accent));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n\n .center-title {\n font-weight: 600;\n font-size: 13px;\n color: var(--muted);\n }\n\n .pill {\n display: flex; align-items: center; gap: 8px;\n padding: 6px 16px;\n border-radius: 999px;\n border: 1px solid var(--border);\n background: rgba(28, 16, 48, 0.5);\n backdrop-filter: blur(8px);\n font-size: 12px;\n font-weight: 600;\n color: var(--muted);\n }\n\n /* Main container — floating island */\n .main-wrap {\n flex: 1;\n background: var(--panel);\n backdrop-filter: blur(20px) saturate(1.2); -webkit-backdrop-filter: blur(20px) saturate(1.2);\n border: 1px solid var(--border);\n border-radius: 20px;\n padding: 20px;\n overflow-y: auto;\n position: relative;\n scrollbar-width: thin;\n scrollbar-color: rgba(217, 70, 239, 0.2) transparent;\n }\n .main-wrap::-webkit-scrollbar { width: 4px; }\n .main-wrap::-webkit-scrollbar-thumb { background: rgba(217, 70, 239, 0.2); border-radius: 2px; }\n\n /* ─── TABS ─── */\n .main-tabs {\n display: inline-flex;\n gap: 4px;\n padding: 4px;\n border-radius: 14px;\n background: rgba(0, 0, 0, 0.3);\n border: 1px solid var(--border);\n margin-bottom: 20px;\n }\n\n button {\n border: 1px solid transparent;\n background: transparent;\n color: var(--muted);\n border-radius: 10px;\n padding: 8px 18px;\n font-size: 14px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n button:hover { color: var(--text); background: rgba(217, 70, 239, 0.06); }\n\n button.primary {\n background: linear-gradient(135deg, var(--primary), var(--accent));\n border: none; color: #fff;\n font-weight: 700;\n box-shadow: 0 2px 16px var(--primary-glow);\n }\n button.primary:hover {\n filter: brightness(1.1);\n box-shadow: 0 4px 24px var(--primary-glow), 0 0 40px rgba(217, 70, 239, 0.1);\n }\n button.danger {\n background: rgba(251, 113, 133, 0.12);\n border-color: rgba(251, 113, 133, 0.3);\n color: var(--danger);\n }\n\n .main-tab.active, .home-category-btn.active {\n background: linear-gradient(135deg, rgba(217, 70, 239, 0.15), rgba(129, 140, 248, 0.1));\n border-color: rgba(217, 70, 239, 0.4);\n color: var(--primary);\n box-shadow: 0 0 14px rgba(217, 70, 239, 0.08);\n }\n\n /* ─── SECTION ─── */\n .section-title {\n font-size: 18px;\n font-weight: 800;\n margin: 10px 0 14px;\n letter-spacing: -0.3px;\n background: linear-gradient(135deg, var(--text), var(--primary));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n\n /* ─── GRID & CARDS ─── */\n .grid { display: grid; gap: 14px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }\n\n .panel {\n background: rgba(20, 12, 36, 0.5);\n backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);\n border: 1px solid var(--border);\n border-radius: 16px;\n padding: 20px;\n transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n overflow: hidden;\n }\n\n /* Cosmic glow on hover */\n .panel::before {\n content: \"\";\n position: absolute;\n inset: -1px;\n border-radius: inherit;\n background: linear-gradient(135deg, rgba(217, 70, 239, 0.15), rgba(129, 140, 248, 0.1), transparent, transparent);\n opacity: 0;\n transition: opacity 0.3s ease;\n z-index: -1;\n pointer-events: none;\n }\n .panel:hover {\n transform: translateY(-4px);\n border-color: rgba(217, 70, 239, 0.25);\n box-shadow:\n 0 10px 32px rgba(0, 0, 0, 0.3),\n 0 0 24px rgba(217, 70, 239, 0.08),\n 0 0 48px rgba(129, 140, 248, 0.04);\n }\n .panel:hover::before { opacity: 1; }\n\n .title {\n color: var(--primary);\n font-size: 12px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1.5px;\n }\n .value {\n font-size: 28px;\n font-weight: 800;\n margin-top: 6px;\n color: #fff;\n text-shadow: 0 0 20px rgba(217, 70, 239, 0.3);\n }\n .subtitle {\n margin-top: 6px;\n color: var(--muted);\n font-size: 13px;\n line-height: 1.5;\n }\n\n /* ─── HOME SECTIONS ─── */\n .home-categories {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n margin-bottom: 16px;\n }\n\n .home-sections {\n display: flex;\n gap: 14px;\n flex-wrap: wrap;\n margin-bottom: 20px;\n }\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 7px); max-width: calc(50% - 7px); }\n .home-width-33 { flex-basis: calc(33.333% - 9.33px); max-width: calc(33.333% - 9.33px); }\n .home-width-20 { flex-basis: calc(20% - 11.2px); max-width: calc(20% - 11.2px); }\n\n /* ─── FORM ELEMENTS ─── */\n .home-fields, .plugin-fields { display: grid; gap: 12px; margin-top: 14px; }\n .home-field, .plugin-field { display: grid; gap: 6px; }\n .home-field label, .plugin-field > label {\n color: var(--muted);\n font-size: 12px; font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .home-input, .home-textarea, .home-select {\n width: 100%;\n border: 1px solid var(--border);\n background: rgba(0, 0, 0, 0.3);\n color: var(--text);\n border-radius: 12px;\n padding: 11px 16px;\n font-size: 14px;\n font-family: inherit;\n outline: none;\n transition: all 0.2s ease;\n }\n .home-input:focus, .home-textarea:focus, .home-select:focus {\n border-color: var(--primary);\n box-shadow: 0 0 0 2px var(--primary-glow), 0 0 20px rgba(217, 70, 239, 0.08);\n }\n .home-textarea { min-height: 90px; resize: vertical; }\n .home-checkbox { width: 18px; height: 18px; accent-color: var(--primary); }\n .home-field-row { display: flex; align-items: center; gap: 10px; }\n .home-message { margin-top: 6px; color: var(--muted); font-size: 12px; }\n\n .lookup-wrap { position: relative; }\n .lookup-results {\n position: absolute; left: 0; right: 0;\n top: calc(100% + 6px);\n z-index: 20;\n border: 1px solid var(--border);\n background: rgba(8, 4, 16, 0.96);\n backdrop-filter: blur(20px);\n border-radius: 12px;\n max-height: 200px;\n overflow: auto;\n display: none;\n box-shadow: 0 14px 40px rgba(0, 0, 0, 0.5), 0 0 24px rgba(217, 70, 239, 0.06);\n }\n .lookup-item {\n width: 100%; border: none; border-radius: 0;\n text-align: left; padding: 10px 16px;\n background: transparent; color: var(--text);\n font-size: 13px;\n }\n .lookup-item:hover { background: rgba(217, 70, 239, 0.08); }\n .lookup-selected { margin-top: 5px; font-size: 11px; color: var(--muted); }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 14px; }\n\n .kv-item {\n display: flex; justify-content: space-between;\n border: 1px solid var(--border); border-radius: 12px;\n padding: 9px 14px; background: var(--panel-2);\n font-size: 13px;\n }\n .list-editor {\n border: 1px solid var(--border); border-radius: 14px;\n background: rgba(0, 0, 0, 0.2);\n padding: 12px; display: grid; gap: 8px;\n }\n .list-items { display: grid; gap: 6px; }\n .list-item {\n display: grid; grid-template-columns: auto 1fr auto;\n gap: 8px; align-items: center;\n border: 1px solid var(--border); border-radius: 10px;\n padding: 8px 12px; background: rgba(20, 12, 36, 0.4);\n transition: border-color 0.2s ease;\n }\n .list-item:hover { border-color: rgba(217, 70, 239, 0.2); }\n .list-item.dragging { opacity: 0.5; }\n .drag-handle { color: var(--primary); user-select: none; font-size: 14px; }\n .list-input { width: 100%; border: none; outline: none; background: transparent; color: var(--text); font-size: 14px; font-family: inherit; }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: 13px; padding: 8px 0; }\n .cursor-pointer { cursor: pointer; }\n\n @media (max-width: 900px) {\n .shell { grid-template-columns: 1fr; padding: 10px; gap: 10px; }\n .sidebar { display: none; }\n .home-width-50, .home-width-33, .home-width-20 { flex-basis: 100%; max-width: 100%; }\n }\n\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(10px) scale(0.99); }\n to { opacity: 1; transform: translateY(0) scale(1); }\n }\n .main-wrap { animation: fadeIn 0.4s ease-out; }\n\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"shell\">\n <aside class=\"sidebar\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </aside>\n\n <main class=\"content\">\n <header class=\"topbar\">\n <div class=\"topbar-left\">\n <div class=\"brand\">${safeName}</div>\n <span id=\"centerTitle\" class=\"center-title\">Nebula Interface</span>\n </div>\n <div id=\"userMeta\" class=\"pill\">Loading...</div>\n </header>\n\n <div class=\"main-wrap\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">Plugins</button>\n </div>\n\n <section id=\"homeArea\">\n <div class=\"section-title\">Command Center</div>\n <section id=\"homeCategories\" class=\"home-categories\"></section>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">Stellar Metrics</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Modules</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </div>\n </main>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderContext } from \"../../Types\";\nimport { getClientScript } from \"../scripts/client\";\n\nfunction escapeHtml(value: string): string {\n return value.replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\").replaceAll('\"', \""\").replaceAll(\"'\", \"'\");\n}\n\nexport function renderShadcnMagicLayout(context: DashboardTemplateRenderContext): string {\n const safeName = escapeHtml(context.dashboardName);\n const design = context.setupDesign ?? {};\n\n const clientScript = getClientScript(context.basePath, design);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>${safeName}</title>\n <link rel=\"preconnect\" href=\"https://fonts.googleapis.com\">\n <link rel=\"preconnect\" href=\"https://fonts.gstatic.com\" crossorigin>\n <link href=\"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap\" rel=\"stylesheet\">\n <style>\n :root {\n color-scheme: dark;\n --bg: ${design.bg ?? \"#06070b\"};\n --surface: ${design.surface ?? \"rgba(10, 12, 18, 0.84)\"};\n --card: ${design.card ?? \"rgba(16, 20, 30, 0.82)\"};\n --card-2: ${design.card2 ?? \"rgba(23, 29, 42, 0.86)\"};\n --text: ${design.text ?? \"#f8fafc\"};\n --muted: ${design.muted ?? \"#94a3b8\"};\n --primary: ${design.primary ?? \"#c084fc\"};\n --primary-glow: rgba(192, 132, 252, 0.3);\n --accent: ${design.accent ?? \"#22d3ee\"};\n --accent-glow: rgba(34, 211, 238, 0.25);\n --border: ${design.border ?? \"rgba(148, 163, 184, 0.12)\"};\n --radius-lg: ${design.radiusLg ?? \"18px\"};\n --radius-md: ${design.radiusMd ?? \"12px\"};\n }\n\n * { box-sizing: border-box; margin: 0; padding: 0; }\n\n html, body { min-height: 100vh; }\n\n body {\n font-family: 'Inter', ui-sans-serif, system-ui, -apple-system, sans-serif;\n color: var(--text);\n background:\n radial-gradient(ellipse at 8% 12%, rgba(192, 132, 252, 0.18), transparent 40%),\n radial-gradient(ellipse at 88% 6%, rgba(34, 211, 238, 0.14), transparent 40%),\n radial-gradient(ellipse at 70% 90%, rgba(244, 114, 182, 0.10), transparent 38%),\n var(--bg);\n }\n\n /* ─── ANIMATED DOT GRID ─── */\n body::before {\n content: \"\";\n position: fixed;\n inset: 0;\n pointer-events: none;\n z-index: 0;\n background-image:\n radial-gradient(circle, rgba(148, 163, 184, 0.08) 1px, transparent 1px);\n background-size: 20px 20px;\n mask-image: radial-gradient(ellipse at center, rgba(0,0,0,0.7), transparent 75%);\n -webkit-mask-image: radial-gradient(ellipse at center, rgba(0,0,0,0.7), transparent 75%);\n }\n\n /* ─── GLOW ORB (decorative) ─── */\n body::after {\n content: \"\";\n position: fixed;\n top: -200px; right: -200px;\n width: 500px; height: 500px;\n border-radius: 50%;\n background: radial-gradient(circle, rgba(192, 132, 252, 0.08), transparent 65%);\n pointer-events: none;\n z-index: 0;\n }\n\n .page {\n position: relative;\n z-index: 1;\n min-height: 100vh;\n width: 100%;\n }\n\n .shell {\n min-height: 100vh;\n width: 100%;\n overflow: hidden;\n }\n\n /* ─── TOP BAR ─── */\n .topbar {\n display: grid;\n grid-template-columns: minmax(0, 1fr) auto auto;\n gap: 14px;\n align-items: center;\n padding: 0 20px;\n height: 56px;\n border-bottom: 1px solid var(--border);\n background: rgba(6, 8, 12, 0.72);\n backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px);\n position: sticky;\n top: 0;\n z-index: 50;\n }\n\n .brand {\n font-weight: 800;\n font-size: 15px;\n letter-spacing: 0.3px;\n background: linear-gradient(135deg, var(--primary), var(--accent));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n\n .center-title {\n border: 1px solid var(--border);\n background: rgba(15, 23, 42, 0.5);\n backdrop-filter: blur(8px);\n border-radius: 999px;\n padding: 6px 16px;\n font-size: 12px;\n font-weight: 600;\n color: #e2e8f0;\n letter-spacing: 0.2px;\n }\n\n .pill {\n border: 1px solid var(--border);\n border-radius: 999px;\n padding: 6px 14px;\n font-size: 12px;\n font-weight: 600;\n color: var(--muted);\n background: rgba(15, 23, 42, 0.5);\n backdrop-filter: blur(8px);\n }\n\n /* ─── SERVER STRIP ─── */\n .server-strip {\n padding: 14px 20px;\n border-bottom: 1px solid var(--border);\n background: rgba(6, 8, 14, 0.5);\n backdrop-filter: blur(12px);\n }\n\n .server-rail {\n display: flex;\n gap: 10px;\n overflow-x: auto;\n padding-bottom: 4px;\n scrollbar-width: none;\n }\n .server-rail::-webkit-scrollbar { display: none; }\n\n .server-item {\n position: relative;\n min-width: 200px;\n height: 56px;\n border-radius: var(--radius-md);\n border: 1px solid var(--border);\n background: linear-gradient(180deg, rgba(30, 41, 59, 0.5), rgba(15, 23, 42, 0.6));\n backdrop-filter: blur(8px);\n color: var(--text);\n font-weight: 600;\n display: flex;\n align-items: center;\n justify-content: flex-start;\n padding: 8px 56px 8px 52px;\n transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n flex-shrink: 0;\n }\n\n .server-item::before {\n content: attr(title);\n display: block;\n max-width: 100%;\n font-size: 13px;\n font-weight: 600;\n line-height: 1.2;\n color: #e2e8f0;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n }\n\n .server-item:hover {\n transform: translateY(-2px);\n border-color: rgba(192, 132, 252, 0.5);\n box-shadow: 0 4px 16px rgba(192, 132, 252, 0.12);\n }\n\n .server-item.active {\n border-color: var(--accent);\n box-shadow:\n 0 0 0 1px rgba(34, 211, 238, 0.2),\n 0 8px 24px rgba(34, 211, 238, 0.12),\n inset 0 1px 0 rgba(34, 211, 238, 0.1);\n background: linear-gradient(180deg, rgba(34, 211, 238, 0.08), rgba(15, 23, 42, 0.7));\n }\n\n .server-item-indicator {\n position: absolute;\n left: 8px; top: 50%;\n transform: translateY(-50%);\n width: 3px; height: 0;\n border-radius: 999px;\n background: linear-gradient(180deg, var(--primary), var(--accent));\n opacity: 0;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n .server-item.active .server-item-indicator {\n opacity: 1;\n height: 28px;\n }\n\n .server-avatar {\n position: absolute;\n left: 16px; top: 50%;\n transform: translateY(-50%);\n width: 26px; height: 26px;\n border-radius: 8px;\n object-fit: cover;\n }\n\n .server-fallback {\n position: absolute;\n left: 16px; top: 50%;\n transform: translateY(-50%);\n width: 26px; height: 26px;\n border-radius: 8px;\n display: grid;\n place-items: center;\n background: linear-gradient(135deg, rgba(192, 132, 252, 0.2), rgba(34, 211, 238, 0.15));\n font-size: 10px;\n font-weight: 700;\n }\n\n .server-status {\n position: absolute;\n right: 14px; top: 50%;\n transform: translateY(-50%);\n width: 8px; height: 8px;\n border-radius: 999px;\n background: #22c55e;\n box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);\n }\n\n .server-status::after {\n position: absolute;\n right: 16px; top: 50%;\n transform: translateY(-50%);\n font-size: 10px;\n font-weight: 600;\n letter-spacing: 0.2px;\n white-space: nowrap;\n }\n\n .server-status.offline {\n background: #94a3b8;\n box-shadow: none;\n }\n .server-status.offline::after {\n content: \"Invite Bot\";\n color: #fda4af;\n }\n\n /* ─── CONTENT ─── */\n .content {\n padding: 20px;\n min-height: calc(100vh - 56px - 85px);\n }\n\n .container {\n border: 1px solid var(--border);\n border-radius: var(--radius-lg);\n background: var(--surface);\n backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);\n padding: 18px;\n box-shadow: 0 4px 24px rgba(0, 0, 0, 0.15);\n }\n\n /* ─── TABS ─── */\n .main-tabs {\n display: inline-flex;\n gap: 4px;\n padding: 4px;\n border: 1px solid var(--border);\n border-radius: 999px;\n background: rgba(15, 23, 42, 0.4);\n margin-bottom: 18px;\n }\n\n button {\n border: 1px solid transparent;\n background: transparent;\n color: var(--muted);\n border-radius: 999px;\n padding: 7px 16px;\n font-size: 13px;\n font-weight: 600;\n font-family: inherit;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n button:hover { color: var(--text); background: rgba(255, 255, 255, 0.04); }\n\n button.primary {\n border: none;\n color: #0b0f1e;\n font-weight: 700;\n background: linear-gradient(135deg, var(--primary), var(--accent));\n box-shadow: 0 2px 12px var(--primary-glow);\n }\n button.primary:hover {\n filter: brightness(1.1);\n box-shadow: 0 4px 20px var(--primary-glow);\n }\n\n button.danger {\n background: rgba(190, 24, 93, 0.15);\n border-color: rgba(244, 114, 182, 0.35);\n color: #f9a8d4;\n }\n button.danger:hover { background: rgba(190, 24, 93, 0.25); }\n\n .main-tab.active, .home-category-btn.active {\n background: linear-gradient(135deg, rgba(34, 211, 238, 0.15), rgba(192, 132, 252, 0.12));\n border-color: rgba(34, 211, 238, 0.5);\n color: var(--accent);\n box-shadow: 0 0 12px rgba(34, 211, 238, 0.1);\n }\n\n /* ─── SECTION ─── */\n .section-title {\n margin: 14px 0 10px;\n font-size: 15px;\n font-weight: 700;\n color: #e2e8f0;\n letter-spacing: 0.2px;\n }\n\n /* ─── GRID & CARDS ─── */\n .grid { display: grid; gap: 12px; }\n .cards { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }\n\n .panel {\n border: 1px solid var(--border);\n border-radius: var(--radius-md);\n background: var(--card);\n backdrop-filter: blur(8px);\n padding: 16px;\n transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n position: relative;\n overflow: hidden;\n }\n .panel::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n background: linear-gradient(135deg, rgba(192, 132, 252, 0.03), rgba(34, 211, 238, 0.02));\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.25s ease;\n }\n .panel:hover {\n transform: translateY(-3px);\n border-color: rgba(192, 132, 252, 0.25);\n box-shadow: 0 8px 28px rgba(0, 0, 0, 0.3), 0 0 20px rgba(192, 132, 252, 0.06);\n }\n .panel:hover::before { opacity: 1; }\n\n .title {\n color: var(--muted);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 1px;\n }\n .value {\n margin-top: 6px;\n font-size: 24px;\n font-weight: 800;\n background: linear-gradient(135deg, #f8fafc, var(--primary));\n -webkit-background-clip: text; -webkit-text-fill-color: transparent;\n }\n .subtitle {\n margin-top: 6px;\n color: var(--muted);\n font-size: 12px;\n line-height: 1.5;\n }\n\n /* ─── HOME SECTIONS ─── */\n .home-categories,\n .home-sections {\n display: flex;\n gap: 10px;\n flex-wrap: wrap;\n margin-bottom: 12px;\n }\n\n .home-section-panel { flex: 0 0 100%; max-width: 100%; }\n .home-width-50 { flex-basis: calc(50% - 5px); max-width: calc(50% - 5px); }\n .home-width-33 { flex-basis: calc(33.333% - 6.67px); max-width: calc(33.333% - 6.67px); }\n .home-width-20 { flex-basis: calc(20% - 8px); max-width: calc(20% - 8px); }\n\n /* ─── FIELDS ─── */\n .home-fields,\n .plugin-fields { display: grid; gap: 10px; margin-top: 10px; }\n .home-field,\n .plugin-field { display: grid; gap: 5px; }\n .home-field label,\n .plugin-field > label {\n color: var(--muted);\n font-size: 11px;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: 0.8px;\n }\n\n .home-input,\n .home-textarea,\n .home-select {\n width: 100%;\n border: 1px solid var(--border);\n background: var(--card-2);\n color: var(--text);\n border-radius: 10px;\n padding: 10px 12px;\n font-size: 13px;\n font-family: inherit;\n outline: none;\n transition: border-color 0.2s ease, box-shadow 0.2s ease;\n }\n .home-input:focus,\n .home-textarea:focus,\n .home-select:focus {\n border-color: var(--primary);\n box-shadow: 0 0 0 2px var(--primary-glow), 0 0 16px rgba(192, 132, 252, 0.08);\n }\n\n .home-textarea { min-height: 80px; resize: vertical; }\n .home-checkbox { width: 17px; height: 17px; accent-color: var(--primary); }\n .home-field-row { display: flex; align-items: center; gap: 8px; }\n .home-message { margin-top: 6px; color: var(--muted); font-size: 12px; }\n\n /* ─── LOOKUP ─── */\n .lookup-wrap { position: relative; }\n .lookup-results {\n position: absolute; left: 0; right: 0;\n top: calc(100% + 6px);\n z-index: 20;\n border: 1px solid var(--border);\n background: rgba(10, 14, 22, 0.97);\n backdrop-filter: blur(16px);\n border-radius: 10px;\n max-height: 200px;\n overflow: auto;\n display: none;\n box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45), 0 0 0 1px rgba(148, 163, 184, 0.06);\n }\n .lookup-item {\n width: 100%; border: none; border-radius: 0; text-align: left;\n padding: 9px 12px; background: transparent; color: var(--text);\n font-size: 13px;\n }\n .lookup-item:hover { background: rgba(192, 132, 252, 0.1); }\n .lookup-selected { margin-top: 5px; font-size: 11px; color: var(--muted); }\n .actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }\n\n /* ─── KV / LIST ─── */\n .kv-item {\n display: flex; justify-content: space-between;\n border: 1px solid var(--border); border-radius: 10px;\n padding: 9px 12px; background: var(--card-2);\n font-size: 13px;\n }\n\n .list-editor {\n border: 1px solid var(--border); border-radius: 10px;\n background: var(--card-2);\n padding: 10px; display: grid; gap: 8px;\n }\n .list-items { display: grid; gap: 6px; }\n .list-item {\n display: grid; grid-template-columns: auto 1fr auto;\n gap: 8px; align-items: center;\n border: 1px solid var(--border); border-radius: 8px;\n padding: 7px 10px;\n background: rgba(23, 29, 42, 0.7);\n transition: border-color 0.15s ease;\n }\n .list-item:hover { border-color: rgba(192, 132, 252, 0.25); }\n .list-item.dragging { opacity: 0.5; }\n .drag-handle { color: var(--muted); user-select: none; font-size: 14px; }\n .list-input {\n width: 100%; border: none; outline: none;\n background: transparent; color: var(--text);\n font-size: 13px; font-family: inherit;\n }\n .list-add { justify-self: start; }\n .empty { color: var(--muted); font-size: 13px; padding: 8px 0; }\n .cursor-pointer { cursor: pointer; }\n\n /* ─── RESPONSIVE ─── */\n @media (max-width: 980px) {\n .topbar { grid-template-columns: 1fr auto; }\n .center-title { display: none; }\n .server-item { min-width: 170px; }\n .home-width-50, .home-width-33, .home-width-20 { flex-basis: 100%; max-width: 100%; }\n }\n @media (max-width: 640px) {\n .content { padding: 12px; }\n .container { padding: 14px; border-radius: 14px; }\n .server-item { min-width: 150px; height: 48px; }\n }\n\n /* ─── ANIMATIONS ─── */\n @keyframes fadeIn {\n from { opacity: 0; transform: translateY(6px); }\n to { opacity: 1; transform: translateY(0); }\n }\n .container { animation: fadeIn 0.3s ease-out; }\n\n /* Inject User Custom CSS Here */\n ${design.customCss ?? \"\"}\n </style>\n</head>\n<body>\n <div class=\"page\">\n <div class=\"shell\">\n <header class=\"topbar\">\n <div class=\"brand\">${safeName}</div>\n <div id=\"centerTitle\" class=\"center-title\">Workspace Hub</div>\n <div id=\"userMeta\" class=\"pill\">Loading...</div>\n </header>\n\n <section class=\"server-strip\">\n <div id=\"serverRail\" class=\"server-rail\"></div>\n </section>\n\n <main class=\"content\">\n <section class=\"container\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active cursor-pointer\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab cursor-pointer\">Plugins</button>\n </div>\n\n <section id=\"homeArea\">\n <div class=\"section-title\">Dashboard Workspace</div>\n <section id=\"homeCategories\" class=\"home-categories\"></section>\n <section id=\"homeSections\" class=\"home-sections\"></section>\n\n <section id=\"overviewArea\">\n <div class=\"section-title\">Overview Metrics</div>\n <section id=\"overviewCards\" class=\"grid cards\"></section>\n </section>\n </section>\n\n <section id=\"pluginsArea\" style=\"display:none;\">\n <div class=\"section-title\">Plugin Center</div>\n <section id=\"plugins\" class=\"grid\"></section>\n </section>\n </section>\n </main>\n </div>\n </div>\n\n <script>${clientScript}</script>\n</body>\n</html>`;\n}\n","import type { DashboardTemplateRenderer } from \"../Types\";\nimport { renderAuroraLayout } from \"./layouts/aurora\";\nimport { renderCompactLayout } from \"./layouts/compact\";\nimport { renderCyberpunkLayout } from \"./layouts/cyberpunk\";\nimport { renderDefaultLayout } from \"./layouts/default\";\nimport { renderHologramLayout } from \"./layouts/hologram\";\nimport { renderNebulaLayout } from \"./layouts/nebula\";\nimport { renderShadcnMagicLayout } from \"./layouts/shadcn-magic\";\n\nexport const BuiltinLayouts = {\n default: renderDefaultLayout,\n compact: renderCompactLayout,\n \"shadcn-magic\": renderShadcnMagicLayout,\n aurora: renderAuroraLayout,\n cyberpunk: renderCyberpunkLayout,\n hologram: renderHologramLayout,\n nebula: renderNebulaLayout,\n} satisfies Record<string, DashboardTemplateRenderer>;\n","import type { DashboardDesignConfig } from \"../../Types\";\n\nexport const BuiltinThemes = {\n default: {\n bg: \"#08090f\",\n rail: \"#0e101a\",\n contentBg: \"radial-gradient(circle at top right, #171a2f, #08090f)\",\n panel: \"rgba(20, 23, 43, 0.6)\",\n panel2: \"rgba(0, 0, 0, 0.3)\",\n text: \"#e0e6ff\",\n muted: \"#8a93bc\",\n primary: \"#5865F2\",\n success: \"#00E676\",\n warning: \"#FFD600\",\n danger: \"#FF3D00\",\n border: \"rgba(88, 101, 242, 0.2)\",\n },\n compact: {\n bg: \"#0a0c14\",\n rail: \"#0e1120\",\n contentBg: \"#0f1221\",\n panel: \"rgba(22, 27, 48, 0.75)\",\n panel2: \"rgba(30, 37, 62, 0.6)\",\n text: \"#eaefff\",\n muted: \"#8490b5\",\n primary: \"#7c87ff\",\n success: \"#2bd4a6\",\n warning: \"#ffd166\",\n danger: \"#ff6f91\",\n info: \"#66d9ff\",\n border: \"rgba(255, 255, 255, 0.07)\",\n },\n \"shadcn-magic\": {\n bg: \"#06070b\",\n surface: \"rgba(10, 12, 18, 0.84)\",\n card: \"rgba(16, 20, 30, 0.82)\",\n card2: \"rgba(23, 29, 42, 0.86)\",\n text: \"#f8fafc\",\n muted: \"#94a3b8\",\n primary: \"#c084fc\",\n accent: \"#22d3ee\",\n border: \"rgba(148, 163, 184, 0.12)\",\n radiusLg: \"18px\",\n radiusMd: \"12px\",\n },\n aurora: {\n bg: \"#050a0e\",\n rail: \"#0a1018\",\n contentBg: \"#070d14\",\n panel: \"rgba(12, 22, 34, 0.7)\",\n panel2: \"rgba(8, 18, 28, 0.6)\",\n text: \"#e2f0e8\",\n muted: \"#6d9886\",\n primary: \"#38bdf8\",\n accent: \"#34d399\",\n success: \"#34d399\",\n warning: \"#fbbf24\",\n danger: \"#f87171\",\n border: \"rgba(56, 189, 248, 0.1)\",\n },\n cyberpunk: {\n bg: \"#0a0a0f\",\n rail: \"#0e0e15\",\n contentBg: \"#0c0c12\",\n panel: \"rgba(16, 16, 24, 0.9)\",\n panel2: \"rgba(22, 22, 32, 0.8)\",\n text: \"#e4e4ef\",\n muted: \"#6b6b8a\",\n primary: \"#ec4899\",\n accent: \"#06b6d4\",\n success: \"#10b981\",\n warning: \"#eab308\",\n danger: \"#ef4444\",\n border: \"rgba(236, 72, 153, 0.12)\",\n },\n hologram: {\n bg: \"#04060c\",\n rail: \"#080b14\",\n contentBg: \"#060912\",\n panel: \"rgba(14, 20, 36, 0.45)\",\n panel2: \"rgba(10, 16, 30, 0.35)\",\n text: \"#e8edf6\",\n muted: \"#7a8bb0\",\n primary: \"#60a5fa\",\n accent: \"#a78bfa\",\n success: \"#34d399\",\n warning: \"#fbbf24\",\n danger: \"#f87171\",\n border: \"rgba(96, 165, 250, 0.08)\",\n },\n nebula: {\n bg: \"#080410\",\n rail: \"#0c0816\",\n contentBg: \"#0a0614\",\n panel: \"rgba(20, 12, 36, 0.7)\",\n panel2: \"rgba(28, 16, 48, 0.5)\",\n text: \"#f0e8ff\",\n muted: \"#9b8ab8\",\n primary: \"#d946ef\",\n accent: \"#818cf8\",\n success: \"#34d399\",\n warning: \"#fbbf24\",\n danger: \"#fb7185\",\n border: \"rgba(217, 70, 239, 0.1)\",\n },\n} satisfies Record<string, DashboardDesignConfig>;\n","import { BuiltinLayouts } from \"../templates\";\nimport { BuiltinThemes } from \"../templates/themes\";\nimport type { DashboardDesignConfig, DashboardOptions, DashboardTemplateRenderContext, DashboardTemplateRenderer } from \"../Types\";\n\nexport class TemplateManager {\n private layoutRenderer: DashboardTemplateRenderer;\n private resolvedDesign: DashboardDesignConfig;\n\n constructor(options: DashboardOptions) {\n this.layoutRenderer = this.resolveLayout(options.uiTemplate);\n this.resolvedDesign = this.resolveTheme(options.uiTheme, options.setupDesign);\n }\n\n private resolveLayout(layoutInput?: DashboardOptions[\"uiTemplate\"]): DashboardTemplateRenderer {\n if (typeof layoutInput === \"function\") {\n return layoutInput;\n }\n\n if (typeof layoutInput === \"string\" && BuiltinLayouts[layoutInput]) {\n return BuiltinLayouts[layoutInput];\n }\n\n return BuiltinLayouts[\"default\"];\n }\n\n private resolveTheme(themeInput?: DashboardOptions[\"uiTheme\"], customOverrides?: DashboardDesignConfig): DashboardDesignConfig {\n let baseTheme: DashboardDesignConfig = {};\n\n if (typeof themeInput === \"string\" && BuiltinThemes[themeInput]) {\n baseTheme = BuiltinThemes[themeInput];\n } else if (typeof themeInput === \"object\") {\n baseTheme = themeInput;\n }\n\n const merged: DashboardDesignConfig = {\n ...baseTheme,\n ...customOverrides,\n };\n\n const combinedCss = [baseTheme.customCss, customOverrides?.customCss].filter((css) => css && css.trim().length > 0).join(\"\\n\\n\");\n\n if (combinedCss) {\n merged.customCss = combinedCss;\n }\n\n return merged;\n }\n\n public render(contextBase: Omit<DashboardTemplateRenderContext, \"setupDesign\">): string {\n const finalContext: DashboardTemplateRenderContext = {\n ...contextBase,\n setupDesign: this.resolvedDesign,\n };\n\n return this.layoutRenderer(finalContext);\n }\n}\n","import { DiscordHelpers } from \"../handlers/DiscordHelpers\";\nimport { TemplateManager } from \"../handlers/TemplateManager\";\nimport type { DashboardGuild, DashboardOptions, DashboardTemplateRenderer, DashboardUser } from \"../Types\";\n\nexport class DashboardEngine {\n public helpers: DiscordHelpers;\n private templateManager: TemplateManager;\n private DISCORD_API = \"https://discord.com/api/v10\";\n\n constructor(public options: DashboardOptions) {\n this.helpers = new DiscordHelpers(options.botToken);\n this.templateManager = new TemplateManager(options);\n }\n\n public getAuthUrl(state: string): string {\n const scope = this.options.scopes && this.options.scopes.length > 0 ? this.options.scopes.join(\" \") : [\"identify\", \"guilds\"].join(\" \");\n const query = new URLSearchParams({\n client_id: this.options.clientId,\n redirect_uri: this.options.redirectUri,\n response_type: \"code\",\n scope,\n state,\n prompt: \"none\",\n });\n return `https://discord.com/oauth2/authorize?${query.toString()}`;\n }\n\n public async exchangeCode(code: string): Promise<{ access_token: string; refresh_token?: string; expires_in?: number }> {\n const response = await fetch(`${this.DISCORD_API}/oauth2/token`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/x-www-form-urlencoded\" },\n body: new URLSearchParams({\n client_id: this.options.clientId,\n client_secret: this.options.clientSecret,\n grant_type: \"authorization_code\",\n code,\n redirect_uri: this.options.redirectUri,\n }),\n });\n\n if (!response.ok) throw new Error(`Failed token exchange: ${response.status} ${await response.text()}`);\n return await response.json();\n }\n\n public async fetchUser(token: string): Promise<DashboardUser> {\n const res = await fetch(`${this.DISCORD_API}/users/@me`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) throw new Error(`Failed to fetch user: ${res.status} ${await res.text()}`);\n return await res.json();\n }\n\n public async fetchGuilds(token: string): Promise<DashboardGuild[]> {\n const res = await fetch(`${this.DISCORD_API}/users/@me/guilds`, {\n headers: { Authorization: `Bearer ${token}` },\n });\n\n if (!res.ok) throw new Error(`Failed to fetch guilds: ${res.status} ${await res.text()}`);\n return await res.json();\n }\n\n public render(basePath: string): string {\n return this.templateManager.render({\n dashboardName: this.options.dashboardName ?? \"Discord Dashboard\",\n basePath,\n });\n }\n}\n","import type { DashboardAction, DashboardCategory, DashboardDesignConfig, DashboardField, DashboardOptions, DashboardTemplateRenderer } from \"../Types\";\n\nexport class CategoryBuilder {\n public data: DashboardCategory;\n\n constructor(id: string, label: string) {\n this.data = { id, label };\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n}\n\nexport class SectionBuilder {\n public data: any = { fields: [], actions: [] };\n\n constructor(\n public id: string,\n public categoryId: string,\n public title: string,\n ) {\n this.data.id = id;\n this.data.categoryId = categoryId;\n this.data.title = title;\n this.data.width = 100;\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n\n public setWidth(width: 20 | 33 | 50 | 100) {\n this.data.width = width;\n return this;\n }\n\n public addField(field: DashboardField) {\n this.data.fields.push(field);\n return this;\n }\n\n public addAction(id: string, label: string, options?: { variant?: \"primary\" | \"danger\" | \"default\"; collectFields?: boolean }, handler?: DashboardAction[\"handler\"]) {\n this.data.actions.push({ id, label, ...options, handler });\n return this;\n }\n}\n\nexport class PanelBuilder {\n public data: any = { fields: [], actions: [] };\n\n constructor(\n public id: string,\n public title: string,\n ) {\n this.data.id = id;\n this.data.title = title;\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n\n public addField(field: DashboardField) {\n this.data.fields.push(field);\n return this;\n }\n\n public addAction(id: string, label: string, options?: { variant?: \"primary\" | \"danger\" | \"default\"; collectFields?: boolean }, handler?: DashboardAction[\"handler\"]) {\n this.data.actions.push({ id, label, ...options, handler });\n return this;\n }\n}\n\nexport class PluginBuilder {\n public data: any = { panels: [] };\n\n constructor(\n public id: string,\n public name: string,\n ) {\n this.data.id = id;\n this.data.name = name;\n }\n\n public setDescription(description: string) {\n this.data.description = description;\n return this;\n }\n\n public addPanel(panel: PanelBuilder) {\n this.data.panels.push(panel.data);\n return this;\n }\n}\n\nexport class DashboardDesigner {\n private config: Partial<DashboardOptions> = {\n setupDesign: {},\n uiTemplates: {},\n };\n\n private categories: DashboardCategory[] = [];\n private sections: any[] = [];\n private plugins: any[] = [];\n private overviewCards: { title: string; value: string; subtitle?: string }[] = [];\n\n constructor(dashboardName?: string) {\n if (dashboardName) {\n this.config.dashboardName = dashboardName;\n }\n }\n\n public setLayout(template: DashboardOptions[\"uiTemplate\"]) {\n this.config.uiTemplate = template;\n return this;\n }\n\n public setTheme(theme: DashboardOptions[\"uiTheme\"]) {\n this.config.uiTheme = theme;\n return this;\n }\n\n public setColors(colors: DashboardDesignConfig) {\n this.config.setupDesign = { ...this.config.setupDesign, ...colors };\n return this;\n }\n\n public setCustomCss(css: string) {\n this.config.setupDesign!.customCss = css;\n return this;\n }\n\n public registerCustomLayout(name: string, renderer: DashboardTemplateRenderer) {\n this.config.uiTemplates![name] = renderer;\n return this;\n }\n\n // --- Content Building Methods ---\n\n public addOverviewCard(title: string, value: string, subtitle?: string) {\n this.overviewCards.push({ title, value, subtitle });\n return this;\n }\n\n public addCategory(category: CategoryBuilder) {\n this.categories.push(category.data);\n return this;\n }\n\n public addSection(section: SectionBuilder) {\n this.sections.push(section.data);\n return this;\n }\n\n public addPlugin(plugin: PluginBuilder) {\n this.plugins.push(plugin.data);\n return this;\n }\n public build(): Partial<DashboardOptions> {\n return {\n ...this.config,\n dashboardData: {\n categories: this.categories,\n sections: this.sections,\n plugins: this.plugins,\n overviewCards: this.overviewCards,\n },\n };\n }\n}\n","import compression from \"compression\";\nimport express, { Router, type Express } from \"express\";\nimport session from \"express-session\";\nimport helmet from \"helmet\";\nimport { randomBytes } from \"node:crypto\";\nimport { DashboardEngine } from \"../core/DashboardEngine\";\nimport type { DashboardContext, DashboardOptions } from \"../Types\";\n\nexport function createExpressAdapter(options: DashboardOptions) {\n const engine = new DashboardEngine(options);\n const app = (options.app as Express) ?? express();\n const router = Router();\n const basePath = options.basePath ?? \"/dashboard\";\n\n router.use(compression());\n router.use(helmet({ contentSecurityPolicy: false }));\n router.use(express.json());\n router.use(\n session({\n secret: options.sessionSecret,\n resave: false,\n saveUninitialized: false,\n name: options.sessionName ?? \"discord_dashboard.sid\",\n }),\n );\n\n const buildContext = (req: express.Request): DashboardContext => ({\n user: req.session.discordAuth!.user,\n guilds: req.session.discordAuth!.guilds || [],\n accessToken: req.session.discordAuth!.accessToken || \"\",\n selectedGuildId: ((req.query as any).guildId as string) || undefined,\n helpers: engine.helpers,\n });\n\n router.get(\"/\", (req, res) => {\n if (!req.session.discordAuth) return res.redirect(`${basePath}/login`);\n res.type(\"html\").send(engine.render(basePath));\n });\n\n router.get(\"/login\", (req, res) => {\n const state = randomBytes(16).toString(\"hex\");\n req.session.oauthState = state;\n res.redirect(engine.getAuthUrl(state));\n });\n\n router.get(\"/callback\", async (req, res) => {\n const { code, state } = req.query;\n if (state !== req.session.oauthState) return res.status(403).send(\"Invalid OAuth2 state\");\n\n try {\n const tokens = await engine.exchangeCode(code as string);\n const [user, rawGuilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n const ADMIN = 8n;\n const MANAGE_GUILD = 32n;\n\n const processedGuilds = rawGuilds\n .filter((guild) => {\n const perms = BigInt(guild.permissions || \"0\");\n return (perms & ADMIN) === ADMIN || (perms & MANAGE_GUILD) === MANAGE_GUILD;\n })\n .map((guild) => ({\n ...guild,\n iconUrl: engine.helpers.getGuildIconUrl(guild.id, guild.icon),\n botInGuild: options.client.guilds.cache.has(guild.id),\n }));\n\n req.session.discordAuth = {\n accessToken: tokens.access_token,\n user: {\n ...user,\n avatarUrl: engine.helpers.getUserAvatarUrl(user.id, user.avatar),\n },\n guilds: processedGuilds,\n };\n\n res.redirect(basePath);\n } catch (error) {\n console.error(\"Dashboard Auth Error:\", error);\n res.redirect(`${basePath}/login`);\n }\n });\n\n router.get(\"/api/session\", (req, res) => {\n if (!req.session.discordAuth) return res.json({ authenticated: false });\n res.json({\n authenticated: true,\n user: req.session.discordAuth.user,\n guildCount: req.session.discordAuth.guilds.length,\n });\n });\n\n router.get(\"/api/guilds\", (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n res.json({ guilds: req.session.discordAuth.guilds });\n });\n\n router.get(\"/api/overview\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const cards = options.getOverviewCards ? await options.getOverviewCards(buildContext(req)) : [];\n res.json({ cards });\n });\n\n router.get(\"/api/home/categories\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const categories = options.home?.getCategories ? await options.home.getCategories(buildContext(req)) : [];\n res.json({ categories });\n });\n\n router.get(\"/api/home\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const sections = options.home?.getSections ? await options.home.getSections(buildContext(req)) : [];\n\n const categoryId = req.query.categoryId as string;\n const filtered = categoryId ? sections.filter((s) => s.categoryId === categoryId) : sections;\n res.json({ sections: filtered });\n });\n\n router.post(\"/api/home/:actionId\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n const actionId = req.params.actionId;\n const actionFn = options.home?.actions?.[actionId];\n\n if (!actionFn) return res.status(404).json({ error: \"Action not found\" });\n\n try {\n const result = await actionFn(buildContext(req), req.body);\n res.json(result);\n } catch (error) {\n res.status(500).json({ error: error instanceof Error ? error.message : \"Action failed\" });\n }\n });\n\n router.get(\"/api/plugins\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n\n const context = buildContext(req);\n\n const resolvedPlugins = await Promise.all(\n (options.plugins || []).map(async (p) => {\n return {\n id: p.id,\n name: p.name,\n description: p.description,\n panels: p.getPanels ? await p.getPanels(context) : p.panels || [],\n };\n }),\n );\n\n res.json({ plugins: resolvedPlugins });\n });\n\n router.post(\"/api/plugins/:pluginId/:actionId\", async (req, res) => {\n if (!req.session.discordAuth) return res.status(401).json({ error: \"Unauthorized\" });\n\n const { pluginId, actionId } = req.params;\n\n const plugin = options.plugins?.find((p) => p.id === pluginId);\n\n if (!plugin || !plugin.actions?.[actionId]) {\n return res.status(404).json({ error: \"Plugin or action not found\" });\n }\n\n try {\n const context = buildContext(req);\n const result = await plugin.actions[actionId](context, req.body);\n res.json(result);\n } catch (error) {\n console.error(`Action Error (${pluginId}/${actionId}):`, error);\n res.status(500).json({ error: \"Internal Server Error\" });\n }\n });\n\n app.use(basePath, router);\n return { app, engine };\n}\n","import jwt from \"@elysiajs/jwt\";\nimport node from \"@elysiajs/node\";\nimport { Elysia } from \"elysia\";\nimport { randomBytes } from \"node:crypto\";\nimport { DashboardEngine } from \"../core/DashboardEngine\";\nimport { SessionSchema, type DashboardContext, type DashboardOptions, type HomeActionPayload, type SessionData } from \"../Types\";\n\nexport function createElysiaAdapter(options: DashboardOptions) {\n const engine = new DashboardEngine(options);\n const basePath = options.basePath ?? \"/dashboard\";\n const sessionName = options.sessionName ?? \"discord_dashboard.sid\";\n\n const app = (options.app as Elysia) ?? new Elysia({ adapter: node() });\n\n const getSession = async (sessionSigner: any, cookie: any): Promise<SessionData | null> => {\n const cookieValue = cookie[sessionName]?.value as string | undefined;\n if (!cookieValue) return null;\n const session = await sessionSigner.verify(cookieValue);\n return session ?? null;\n };\n\n const setSession = async (sessionSigner: any, cookie: any, session: SessionData): Promise<void> => {\n const token = await sessionSigner.sign(session);\n cookie[sessionName].set({\n value: token,\n httpOnly: true,\n sameSite: \"lax\",\n path: \"/\",\n });\n };\n\n const buildContext = (session: SessionData, query?: Record<string, unknown>): DashboardContext => ({\n user: session.discordAuth!.user,\n guilds: (session.discordAuth!.guilds || []).map((guild) => ({\n ...guild,\n botInGuild: guild.botInGuild ?? undefined,\n inviteUrl: guild.inviteUrl ?? undefined,\n })),\n accessToken: session.discordAuth!.accessToken || \"\",\n selectedGuildId: typeof query?.guildId === \"string\" ? query.guildId : undefined,\n helpers: engine.helpers,\n });\n\n const router = new Elysia({ prefix: basePath })\n .use(\n jwt({\n name: \"sessionSigner\",\n secret: options.sessionSecret,\n schema: SessionSchema,\n }),\n )\n .get(\"/\", async ({ sessionSigner, cookie, redirect }) => {\n const sessionData = await getSession(sessionSigner, cookie);\n if (!sessionData || !sessionData.discordAuth) return redirect(`${basePath}/login`);\n\n return new Response(engine.render(basePath), {\n headers: {\n \"content-type\": \"text/html; charset=utf-8\",\n },\n });\n })\n .get(\"/login\", async ({ sessionSigner, cookie, redirect }) => {\n const state = randomBytes(16).toString(\"hex\");\n\n await setSession(sessionSigner, cookie, { oauthState: state });\n\n return redirect(engine.getAuthUrl(state));\n })\n .get(\"/callback\", async ({ query, sessionSigner, cookie, redirect }) => {\n const { code, state } = query as { code?: string; state?: string; error?: string };\n const session = await getSession(sessionSigner, cookie);\n\n if (!code || !state || !session || state !== session.oauthState) return redirect(`${basePath}/login`);\n\n try {\n const tokens = await engine.exchangeCode(code);\n const [user, rawGuilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n const ADMIN = 8n;\n const MANAGE_GUILD = 32n;\n\n const processedGuilds = rawGuilds\n .filter((guild) => {\n const perms = BigInt(guild.permissions || \"0\");\n return (perms & ADMIN) === ADMIN || (perms & MANAGE_GUILD) === MANAGE_GUILD;\n })\n .map((guild) => ({\n id: guild.id,\n name: guild.name,\n icon: guild.icon,\n owner: guild.owner,\n permissions: guild.permissions,\n iconUrl: engine.helpers.getGuildIconUrl(guild.id, guild.icon),\n botInGuild: options.client.guilds.cache.has(guild.id),\n }));\n\n await setSession(sessionSigner, cookie, {\n discordAuth: {\n accessToken: tokens.access_token,\n user: {\n id: user.id,\n username: user.username,\n discriminator: user.discriminator,\n avatar: user.avatar,\n global_name: user.global_name,\n avatarUrl: engine.helpers.getUserAvatarUrl(user.id, user.avatar),\n },\n guilds: processedGuilds,\n },\n });\n\n return redirect(basePath);\n } catch (error) {\n console.error(\"Dashboard Auth Error:\", error);\n return redirect(`${basePath}/login`);\n }\n })\n .get(\"/api/session\", async ({ sessionSigner, cookie }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) return { authenticated: false };\n\n return {\n authenticated: true,\n user: session.discordAuth.user,\n guildCount: session.discordAuth.guilds.length,\n };\n })\n .get(\"/api/guilds\", async ({ sessionSigner, cookie, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n return { guilds: session.discordAuth.guilds };\n })\n .get(\"/api/overview\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const cards = options.getOverviewCards ? await options.getOverviewCards(buildContext(session, query as Record<string, unknown>)) : [];\n return { cards };\n })\n .get(\"/api/home/categories\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const categories = options.home?.getCategories ? await options.home.getCategories(buildContext(session, query as Record<string, unknown>)) : [];\n return { categories };\n })\n .get(\"/api/home\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const context = buildContext(session, query as Record<string, unknown>);\n const sections = options.home?.getSections ? await options.home.getSections(context) : [];\n const categoryId = typeof (query as Record<string, unknown>).categoryId === \"string\"\n ? ((query as Record<string, unknown>).categoryId as string)\n : undefined;\n const filteredSections = categoryId ? sections.filter((section) => section.categoryId === categoryId) : sections;\n\n return { sections: filteredSections };\n })\n .post(\"/api/home/:actionId\", async ({ params, body, sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const actionFn = options.home?.actions?.[params.actionId];\n if (!actionFn) {\n set.status = 404;\n return { error: \"Action not found\" };\n }\n\n try {\n return await actionFn(buildContext(session, query as Record<string, unknown>), body as HomeActionPayload);\n } catch (error) {\n set.status = 500;\n return { error: error instanceof Error ? error.message : \"Action failed\" };\n }\n })\n .get(\"/api/plugins\", async ({ sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const context = buildContext(session, query as Record<string, unknown>);\n const resolvedPlugins = await Promise.all(\n (options.plugins || []).map(async (plugin) => ({\n id: plugin.id,\n name: plugin.name,\n description: plugin.description,\n panels: plugin.getPanels ? await plugin.getPanels(context) : plugin.panels || [],\n })),\n );\n\n return { plugins: resolvedPlugins };\n })\n .post(\"/api/plugins/:pluginId/:actionId\", async ({ params, body, sessionSigner, cookie, query, set }) => {\n const session = await getSession(sessionSigner, cookie);\n if (!session?.discordAuth) {\n set.status = 401;\n return { error: \"Unauthorized\" };\n }\n\n const plugin = options.plugins?.find((item) => item.id === params.pluginId);\n if (!plugin || !plugin.actions?.[params.actionId]) {\n set.status = 404;\n return { error: \"Plugin or action not found\" };\n }\n\n try {\n return await plugin.actions[params.actionId](buildContext(session, query as Record<string, unknown>), body);\n } catch (error) {\n console.error(`Action Error (${params.pluginId}/${params.actionId}):`, error);\n set.status = 500;\n return { error: \"Internal Server Error\" };\n }\n });\n\n app.use(router);\n\n return { app, engine };\n}\n","import { randomBytes } from \"crypto\";\nimport type { FastifyInstance, FastifyRequest } from \"fastify\";\nimport type { DashboardContext, DashboardOptions, HomeActionPayload } from \"../Types\";\nimport { DashboardEngine } from \"../core/DashboardEngine\";\n\nexport function createFastifyAdapter(fastify: FastifyInstance, options: DashboardOptions) {\n const engine = new DashboardEngine(options);\n const basePath = options.basePath ?? \"/dashboard\";\n\n const buildContext = (request: FastifyRequest): DashboardContext => {\n const query = request.query as Record<string, unknown>;\n\n return {\n user: request.session.discordAuth!.user,\n guilds: request.session.discordAuth!.guilds || [],\n accessToken: request.session.discordAuth!.accessToken || \"\",\n selectedGuildId: typeof query.guildId === \"string\" ? query.guildId : undefined,\n helpers: engine.helpers,\n };\n };\n\n fastify.register(\n async (instance) => {\n instance.get(\"/\", async (request, reply) => {\n if (!request.session.discordAuth) return reply.redirect(`${basePath}/login`);\n return reply.type(\"html\").send(engine.render(basePath));\n });\n\n instance.get(\"/login\", async (request, reply) => {\n const state = randomBytes(16).toString(\"hex\");\n request.session.oauthState = state;\n return reply.redirect(engine.getAuthUrl(state));\n });\n\n instance.get(\"/callback\", async (request: FastifyRequest<{ Querystring: { code: string; state: string } }>, reply) => {\n const { code, state } = request.query;\n if (state !== request.session.oauthState) return reply.status(403).send(\"Invalid OAuth2 state\");\n\n try {\n const tokens = await engine.exchangeCode(code);\n const [user, rawGuilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n const ADMIN = 8n;\n const MANAGE_GUILD = 32n;\n\n const processedGuilds = rawGuilds\n .filter((guild) => {\n const perms = BigInt(guild.permissions || \"0\");\n return (perms & ADMIN) === ADMIN || (perms & MANAGE_GUILD) === MANAGE_GUILD;\n })\n .map((guild) => ({\n ...guild,\n iconUrl: engine.helpers.getGuildIconUrl(guild.id, guild.icon),\n botInGuild: options.client.guilds.cache.has(guild.id),\n }));\n\n request.session.discordAuth = {\n accessToken: tokens.access_token,\n user: {\n ...user,\n avatarUrl: engine.helpers.getUserAvatarUrl(user.id, user.avatar),\n },\n guilds: processedGuilds,\n };\n\n return reply.redirect(basePath);\n } catch (error) {\n console.error(\"Dashboard Auth Error:\", error);\n return reply.redirect(`${basePath}/login`);\n }\n });\n\n instance.get(\"/api/session\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.send({ authenticated: false });\n }\n\n return reply.send({\n authenticated: true,\n user: request.session.discordAuth.user,\n guildCount: request.session.discordAuth.guilds.length,\n });\n });\n\n instance.get(\"/api/guilds\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n return reply.send({ guilds: request.session.discordAuth.guilds });\n });\n\n instance.get(\"/api/overview\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const cards = options.getOverviewCards ? await options.getOverviewCards(buildContext(request)) : [];\n return reply.send({ cards });\n });\n\n instance.get(\"/api/home/categories\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const categories = options.home?.getCategories ? await options.home.getCategories(buildContext(request)) : [];\n return reply.send({ categories });\n });\n\n instance.get(\"/api/home\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const sections = options.home?.getSections ? await options.home.getSections(buildContext(request)) : [];\n const query = request.query as Record<string, unknown>;\n const categoryId = typeof query.categoryId === \"string\" ? query.categoryId : undefined;\n const filteredSections = categoryId ? sections.filter((section) => section.categoryId === categoryId) : sections;\n\n return reply.send({ sections: filteredSections });\n });\n\n instance.post(\"/api/home/:actionId\", async (request: FastifyRequest<{ Params: { actionId: string }; Body: unknown }>, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const actionFn = options.home?.actions?.[request.params.actionId];\n if (!actionFn) {\n return reply.status(404).send({ error: \"Action not found\" });\n }\n\n try {\n const result = await actionFn(buildContext(request), request.body as HomeActionPayload);\n return reply.send(result);\n } catch (error) {\n return reply.status(500).send({ error: error instanceof Error ? error.message : \"Action failed\" });\n }\n });\n\n instance.get(\"/api/plugins\", async (request, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const context = buildContext(request);\n const resolvedPlugins = await Promise.all(\n (options.plugins || []).map(async (plugin) => ({\n id: plugin.id,\n name: plugin.name,\n description: plugin.description,\n panels: plugin.getPanels ? await plugin.getPanels(context) : plugin.panels || [],\n })),\n );\n\n return reply.send({ plugins: resolvedPlugins });\n });\n\n instance.post(\n \"/api/plugins/:pluginId/:actionId\",\n async (request: FastifyRequest<{ Params: { pluginId: string; actionId: string }; Body: unknown }>, reply) => {\n if (!request.session.discordAuth) {\n return reply.status(401).send({ error: \"Unauthorized\" });\n }\n\n const { pluginId, actionId } = request.params;\n const plugin = options.plugins?.find((item) => item.id === pluginId);\n\n if (!plugin || !plugin.actions?.[actionId]) {\n return reply.status(404).send({ error: \"Plugin or action not found\" });\n }\n\n try {\n const result = await plugin.actions[actionId](buildContext(request), request.body as Record<string, unknown>);\n return reply.send(result);\n } catch (error) {\n console.error(`Action Error (${pluginId}/${actionId}):`, error);\n return reply.status(500).send({ error: \"Internal Server Error\" });\n }\n },\n );\n },\n { prefix: basePath },\n );\n\n return { engine };\n}\n"],"mappings":"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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmkB1C;;;ACpkBA,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,QAAQ;AAC1I;AAEO,SAAS,mBAAmB,SAAiD;AAClF,QAAM,WAAW,WAAW,QAAQ,aAAa;AACjD,QAAM,SAAS,QAAQ,eAAe,CAAC;AAEvC,QAAM,eAAe,gBAAgB,QAAQ,UAAU,MAAM;AAE7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOL,OAAO,MAAM,SAAS;AAAA,gBACpB,OAAO,QAAQ,SAAS;AAAA,sBAClB,OAAO,aAAa,SAAS;AAAA,iBAClC,OAAO,SAAS,uBAAuB;AAAA,mBACrC,OAAO,UAAU,sBAAsB;AAAA,gBAC1C,OAAO,QAAQ,SAAS;AAAA,iBACvB,OAAO,SAAS,SAAS;AAAA,mBACvB,OAAO,WAAW,SAAS;AAAA;AAAA,kBAE5B,OAAO,UAAU,SAAS;AAAA;AAAA;AAAA,mBAGzB,OAAO,WAAW,SAAS;AAAA,mBAC3B,OAAO,WAAW,SAAS;AAAA,kBAC5B,OAAO,UAAU,SAAS;AAAA,kBAC1B,OAAO,UAAU,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAqetD,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAOC,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAwDzB,YAAY;AAAA;AAAA;AAGxB;;;AC5kBA,SAASA,YAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,QAAQ;AAC1I;AAEO,SAAS,oBAAoB,SAAiD;AACnF,QAAM,WAAWA,YAAW,QAAQ,aAAa;AACjD,QAAM,SAAS,QAAQ,eAAe,CAAC;AAEvC,QAAM,eAAe,gBAAgB,QAAQ,UAAU,MAAM;AAE7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOL,OAAO,MAAM,SAAS;AAAA,gBACpB,OAAO,QAAQ,SAAS;AAAA,sBAClB,OAAO,aAAa,SAAS;AAAA,iBAClC,OAAO,SAAS,wBAAwB;AAAA,mBACtC,OAAO,UAAU,uBAAuB;AAAA,gBAC3C,OAAO,QAAQ,SAAS;AAAA,iBACvB,OAAO,SAAS,SAAS;AAAA,mBACvB,OAAO,WAAW,SAAS;AAAA;AAAA,mBAE3B,OAAO,WAAW,SAAS;AAAA,mBAC3B,OAAO,WAAW,SAAS;AAAA,kBAC5B,OAAO,UAAU,SAAS;AAAA,gBAC5B,OAAO,QAAQ,SAAS;AAAA,kBACtB,OAAO,UAAU,2BAA2B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QA8MxD,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDAUqB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAkD7B,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuZxC;;;AC5rBA,SAASC,YAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,QAAQ;AAC1I;AAEO,SAAS,qBAAqB,SAAiD;AACpF,QAAM,WAAWA,YAAW,QAAQ,aAAa;AACjD,QAAM,SAAS,QAAQ,eAAe,CAAC;AAEvC,QAAM,eAAe,gBAAgB,QAAQ,UAAU,MAAM;AAE7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOL,OAAO,MAAM,SAAS;AAAA,gBACpB,OAAO,QAAQ,SAAS;AAAA,sBAClB,OAAO,aAAa,SAAS;AAAA,iBAClC,OAAO,SAAS,wBAAwB;AAAA,mBACtC,OAAO,UAAU,wBAAwB;AAAA,gBAC5C,OAAO,QAAQ,SAAS;AAAA,iBACvB,OAAO,SAAS,SAAS;AAAA,mBACvB,OAAO,WAAW,SAAS;AAAA;AAAA,kBAE5B,OAAO,UAAU,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAMzB,OAAO,WAAW,SAAS;AAAA,mBAC3B,OAAO,WAAW,SAAS;AAAA,kBAC5B,OAAO,UAAU,SAAS;AAAA,kBAC1B,OAAO,UAAU,0BAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MA8fvD,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2BAMD,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAqCvB,YAAY;AAAA;AAAA;AAGxB;;;ACplBA,SAASC,YAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,QAAQ;AAC1I;AAEO,SAAS,mBAAmB,SAAiD;AAClF,QAAM,WAAWA,YAAW,QAAQ,aAAa;AACjD,QAAM,SAAS,QAAQ,eAAe,CAAC;AAEvC,QAAM,eAAe,gBAAgB,QAAQ,UAAU,MAAM;AAE7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOL,OAAO,MAAM,SAAS;AAAA,gBACpB,OAAO,QAAQ,SAAS;AAAA,sBAClB,OAAO,aAAa,SAAS;AAAA,iBAClC,OAAO,SAAS,uBAAuB;AAAA,mBACrC,OAAO,UAAU,uBAAuB;AAAA,gBAC3C,OAAO,QAAQ,SAAS;AAAA,iBACvB,OAAO,SAAS,SAAS;AAAA,mBACvB,OAAO,WAAW,SAAS;AAAA;AAAA,kBAE5B,OAAO,UAAU,SAAS;AAAA;AAAA,mBAEzB,OAAO,WAAW,SAAS;AAAA,mBAC3B,OAAO,WAAW,SAAS;AAAA,kBAC5B,OAAO,UAAU,SAAS;AAAA,kBAC1B,OAAO,UAAU,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAoetD,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,+BAYG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YA+B3B,YAAY;AAAA;AAAA;AAGxB;;;ACtjBA,SAASC,YAAW,OAAuB;AACzC,SAAO,MAAM,WAAW,KAAK,OAAO,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,MAAM,EAAE,WAAW,KAAK,QAAQ,EAAE,WAAW,KAAK,QAAQ;AAC1I;AAEO,SAAS,wBAAwB,SAAiD;AACvF,QAAM,WAAWA,YAAW,QAAQ,aAAa;AACjD,QAAM,SAAS,QAAQ,eAAe,CAAC;AAEvC,QAAM,eAAe,gBAAgB,QAAQ,UAAU,MAAM;AAE7D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,cAOL,OAAO,MAAM,SAAS;AAAA,mBACjB,OAAO,WAAW,wBAAwB;AAAA,gBAC7C,OAAO,QAAQ,wBAAwB;AAAA,kBACrC,OAAO,SAAS,wBAAwB;AAAA,gBAC1C,OAAO,QAAQ,SAAS;AAAA,iBACvB,OAAO,SAAS,SAAS;AAAA,mBACvB,OAAO,WAAW,SAAS;AAAA;AAAA,kBAE5B,OAAO,UAAU,SAAS;AAAA;AAAA,kBAE1B,OAAO,UAAU,2BAA2B;AAAA,qBACzC,OAAO,YAAY,MAAM;AAAA,qBACzB,OAAO,YAAY,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["escapeHtml","escapeHtml","escapeHtml","escapeHtml","escapeHtml","escapeHtml","express","compression","helmet","session","import_elysia","import_node_crypto","node","session","jwt"]}
|