@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.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../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":["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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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,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;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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,MAqYxC,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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","names":["escapeHtml","escapeHtml","randomBytes","session","randomBytes","randomBytes","randomBytes"]}
|
|
1
|
+
{"version":3,"sources":["../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":["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;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;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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A;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;AAA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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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["escapeHtml","escapeHtml","escapeHtml","escapeHtml","escapeHtml","escapeHtml","randomBytes","session","randomBytes","randomBytes","randomBytes"]}
|