@developer.krd/discord-dashboard 0.1.2 → 0.1.5

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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dashboard.ts","../src/discord-helpers.ts","../src/templates.ts","../src/templates/compact.ts","../src/templates/default.ts","../src/templates/index.ts","../src/designer.ts"],"sourcesContent":["import compression from \"compression\";\nimport express, { type Express, type NextFunction, type Request, type Response } from \"express\";\nimport session from \"express-session\";\nimport helmet from \"helmet\";\nimport { createServer, type Server } from \"node:http\";\nimport { randomBytes } from \"node:crypto\";\nimport { createDiscordHelpers } from \"./discord-helpers\";\nimport { renderDashboardHtml } from \"./templates\";\nimport { getBuiltinTemplateRenderer } from \"./templates/index\";\nimport type {\n DashboardCard,\n DashboardContext,\n DashboardGuild,\n DashboardTemplateRenderer,\n DashboardScope,\n HomeCategory,\n HomeSection,\n DashboardInstance,\n DashboardOptions,\n DashboardPlugin,\n DashboardUser,\n PluginActionResult\n} from \"./types\";\n\nconst DISCORD_API = \"https://discord.com/api/v10\";\nconst MANAGE_GUILD_PERMISSION = 0x20n;\nconst ADMIN_PERMISSION = 0x8n;\n\nfunction normalizeBasePath(basePath?: string): string {\n if (!basePath || basePath === \"/\") {\n return \"/dashboard\";\n }\n\n return basePath.startsWith(\"/\") ? basePath : `/${basePath}`;\n}\n\nfunction canManageGuild(permissions: string): boolean {\n const value = BigInt(permissions);\n return (value & MANAGE_GUILD_PERMISSION) === MANAGE_GUILD_PERMISSION || (value & ADMIN_PERMISSION) === ADMIN_PERMISSION;\n}\n\nfunction toQuery(params: Record<string, string>): string {\n const url = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n url.set(key, value);\n }\n return url.toString();\n}\n\nasync function fetchDiscord<T>(path: string, token: string): Promise<T> {\n const response = await fetch(`${DISCORD_API}${path}`, {\n headers: {\n Authorization: `Bearer ${token}`\n }\n });\n\n if (!response.ok) {\n throw new Error(`Discord API request failed (${response.status})`);\n }\n\n return (await response.json()) as T;\n}\n\nasync function exchangeCodeForToken(options: DashboardOptions, code: string): Promise<{\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n}> {\n const response = await fetch(`${DISCORD_API}/oauth2/token`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/x-www-form-urlencoded\"\n },\n body: toQuery({\n client_id: options.clientId,\n client_secret: options.clientSecret,\n grant_type: \"authorization_code\",\n code,\n redirect_uri: options.redirectUri\n })\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Failed token exchange: ${response.status} ${text}`);\n }\n\n return (await response.json()) as {\n access_token: string;\n refresh_token?: string;\n expires_in?: number;\n };\n}\n\nfunction createContext(req: Request, options: DashboardOptions): DashboardContext {\n const auth = req.session.discordAuth;\n if (!auth) {\n throw new Error(\"Not authenticated\");\n }\n\n const selectedGuildId = typeof req.query.guildId === \"string\" ? req.query.guildId : undefined;\n return {\n user: auth.user,\n guilds: auth.guilds,\n accessToken: auth.accessToken,\n selectedGuildId,\n helpers: createDiscordHelpers(options.botToken)\n };\n}\n\nfunction ensureAuthenticated(req: Request, res: Response, next: NextFunction): void {\n if (!req.session.discordAuth) {\n res.status(401).json({ authenticated: false, message: \"Authentication required\" });\n return;\n }\n\n next();\n}\n\nasync function resolveOverviewCards(options: DashboardOptions, context: DashboardContext): Promise<DashboardCard[]> {\n if (options.getOverviewCards) {\n return await options.getOverviewCards(context);\n }\n\n const manageableGuildCount = context.guilds.filter((guild) => guild.owner || canManageGuild(guild.permissions)).length;\n\n return [\n {\n id: \"user\",\n title: \"Logged-in User\",\n value: context.user.global_name || context.user.username,\n subtitle: `ID: ${context.user.id}`,\n intent: \"info\"\n },\n {\n id: \"guilds\",\n title: \"Manageable Guilds\",\n value: manageableGuildCount,\n subtitle: \"Owner or Manage Server permissions\",\n intent: \"success\"\n },\n {\n id: \"plugins\",\n title: \"Plugins Loaded\",\n value: options.plugins?.length ?? 0,\n subtitle: \"Dynamic server modules\",\n intent: \"neutral\"\n }\n ];\n}\n\nasync function resolveHomeSections(options: DashboardOptions, context: DashboardContext): Promise<HomeSection[]> {\n const customSections = options.home?.getSections ? await options.home.getSections(context) : [];\n const overviewSections = options.home?.getOverviewSections ? await options.home.getOverviewSections(context) : [];\n\n if (customSections.length > 0 || overviewSections.length > 0) {\n const normalizedOverview = overviewSections.map((section) => ({\n ...section,\n categoryId: section.categoryId ?? \"overview\"\n }));\n return [...normalizedOverview, ...customSections];\n }\n\n const selectedGuild = context.selectedGuildId\n ? context.guilds.find((guild) => guild.id === context.selectedGuildId)\n : undefined;\n\n return [\n {\n id: \"setup\",\n title: \"Setup Details\",\n description: \"Core dashboard setup information\",\n scope: \"setup\",\n categoryId: \"setup\",\n fields: [\n {\n id: \"dashboardName\",\n label: \"Dashboard Name\",\n type: \"text\",\n value: options.dashboardName ?? \"Discord Dashboard\",\n readOnly: true\n },\n {\n id: \"basePath\",\n label: \"Base Path\",\n type: \"text\",\n value: options.basePath ?? \"/dashboard\",\n readOnly: true\n }\n ]\n },\n {\n id: \"context\",\n title: \"Dashboard Context\",\n description: selectedGuild ? `Managing ${selectedGuild.name}` : \"Managing user dashboard\",\n scope: resolveScope(context),\n categoryId: \"overview\",\n fields: [\n {\n id: \"mode\",\n label: \"Mode\",\n type: \"text\",\n value: selectedGuild ? \"Guild\" : \"User\",\n readOnly: true\n },\n {\n id: \"target\",\n label: \"Target\",\n type: \"text\",\n value: selectedGuild ? selectedGuild.name : context.user.username,\n readOnly: true\n }\n ]\n }\n ];\n}\n\nfunction resolveScope(context: DashboardContext): DashboardScope {\n return context.selectedGuildId ? \"guild\" : \"user\";\n}\n\nasync function resolveHomeCategories(options: DashboardOptions, context: DashboardContext): Promise<HomeCategory[]> {\n if (options.home?.getCategories) {\n const categories = await options.home.getCategories(context);\n return [...categories].sort((a, b) => {\n if (a.id === \"overview\") return -1;\n if (b.id === \"overview\") return 1;\n return 0;\n });\n }\n\n return [\n { id: \"overview\", label: \"Overview\", scope: resolveScope(context) },\n { id: \"setup\", label: \"Setup\", scope: \"setup\" }\n ];\n}\n\nfunction getUserAvatarUrl(user: DashboardUser): string | null {\n if (user.avatar) {\n const ext = user.avatar.startsWith(\"a_\") ? \"gif\" : \"png\";\n return `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.${ext}?size=256`;\n }\n\n const fallbackIndex = Number((BigInt(user.id) >> 22n) % 6n);\n return `https://cdn.discordapp.com/embed/avatars/${fallbackIndex}.png`;\n}\n\nfunction getGuildIconUrl(guild: DashboardGuild): string | null {\n if (!guild.icon) {\n return null;\n }\n\n const ext = guild.icon.startsWith(\"a_\") ? \"gif\" : \"png\";\n return `https://cdn.discordapp.com/icons/${guild.id}/${guild.icon}.${ext}?size=128`;\n}\n\nfunction createGuildInviteUrl(options: DashboardOptions, guildId: string): string {\n const scopes = options.botInviteScopes && options.botInviteScopes.length > 0\n ? options.botInviteScopes\n : [\"bot\", \"applications.commands\"];\n\n return `https://discord.com/oauth2/authorize?${toQuery({\n client_id: options.clientId,\n scope: scopes.join(\" \"),\n permissions: options.botInvitePermissions ?? \"8\",\n guild_id: guildId,\n disable_guild_select: \"true\"\n })}`;\n}\n\nasync function fetchBotGuildIds(botToken: string): Promise<Set<string>> {\n type BotGuild = { id: string };\n\n const response = await fetch(`${DISCORD_API}/users/@me/guilds`, {\n headers: {\n Authorization: `Bot ${botToken}`\n }\n });\n\n if (!response.ok) {\n return new Set();\n }\n\n const guilds = (await response.json()) as BotGuild[];\n return new Set(guilds.map((guild) => guild.id));\n}\n\nfunction resolveTemplateRenderer(options: DashboardOptions): DashboardTemplateRenderer {\n const selectedTemplate = options.uiTemplate ?? \"default\";\n const defaultRenderer: DashboardTemplateRenderer = ({ dashboardName, basePath, setupDesign }) =>\n renderDashboardHtml(dashboardName, basePath, setupDesign);\n\n const customRenderer = options.uiTemplates?.[selectedTemplate];\n if (customRenderer) {\n return customRenderer;\n }\n\n const builtinRenderer = getBuiltinTemplateRenderer(selectedTemplate);\n if (builtinRenderer) {\n return builtinRenderer;\n }\n\n if (selectedTemplate !== \"default\") {\n throw new Error(`Unknown uiTemplate '${selectedTemplate}'. Register it in uiTemplates.`);\n }\n\n return defaultRenderer;\n}\n\nexport function createDashboard(options: DashboardOptions): DashboardInstance {\n const app = options.app ?? express();\n const basePath = normalizeBasePath(options.basePath);\n const dashboardName = options.dashboardName ?? \"Discord Dashboard\";\n const templateRenderer = resolveTemplateRenderer(options);\n const plugins = options.plugins ?? [];\n\n if (!options.botToken) throw new Error(\"botToken is required\");\n if (!options.clientId) throw new Error(\"clientId is required\");\n if (!options.clientSecret) throw new Error(\"clientSecret is required\");\n if (!options.redirectUri) throw new Error(\"redirectUri is required\");\n if (!options.sessionSecret) throw new Error(\"sessionSecret is required\");\n\n if (!options.app && options.trustProxy !== undefined) {\n app.set(\"trust proxy\", options.trustProxy);\n }\n\n const router = express.Router();\n const sessionMiddleware = session({\n name: options.sessionName ?? \"discord_dashboard.sid\",\n secret: options.sessionSecret,\n resave: false,\n saveUninitialized: false,\n cookie: {\n httpOnly: true,\n sameSite: \"lax\",\n maxAge: options.sessionMaxAgeMs ?? 1000 * 60 * 60 * 24 * 7\n }\n });\n\n router.use(compression());\n router.use(\n helmet({\n contentSecurityPolicy: false\n })\n );\n router.use(express.json());\n router.use(sessionMiddleware);\n\n router.get(\"/\", (req, res) => {\n if (!req.session.discordAuth) {\n res.redirect(`${basePath}/login`);\n return;\n }\n\n res.setHeader(\"Cache-Control\", \"no-store\");\n res.type(\"html\").send(templateRenderer({\n dashboardName,\n basePath,\n setupDesign: options.setupDesign\n }));\n });\n\n router.get(\"/login\", (req, res) => {\n const state = randomBytes(16).toString(\"hex\");\n req.session.oauthState = state;\n\n const scope = (options.scopes && options.scopes.length > 0 ? options.scopes : [\"identify\", \"guilds\"]).join(\" \");\n\n const query = toQuery({\n client_id: options.clientId,\n redirect_uri: options.redirectUri,\n response_type: \"code\",\n scope,\n state,\n prompt: \"none\"\n });\n\n res.redirect(`https://discord.com/oauth2/authorize?${query}`);\n });\n\n router.get(\"/callback\", async (req, res) => {\n try {\n const code = typeof req.query.code === \"string\" ? req.query.code : undefined;\n const state = typeof req.query.state === \"string\" ? req.query.state : undefined;\n\n if (!code || !state) {\n res.status(400).send(\"Missing OAuth2 code/state\");\n return;\n }\n\n if (!req.session.oauthState || req.session.oauthState !== state) {\n res.status(403).send(\"Invalid OAuth2 state\");\n return;\n }\n\n const tokenData = await exchangeCodeForToken(options, code);\n const [user, guilds] = await Promise.all([\n fetchDiscord<DashboardUser>(\"/users/@me\", tokenData.access_token),\n fetchDiscord<DashboardGuild[]>(\"/users/@me/guilds\", tokenData.access_token)\n ]);\n\n req.session.discordAuth = {\n accessToken: tokenData.access_token,\n refreshToken: tokenData.refresh_token,\n expiresAt: tokenData.expires_in ? Date.now() + tokenData.expires_in * 1000 : undefined,\n user,\n guilds\n };\n\n req.session.oauthState = undefined;\n res.redirect(basePath);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"OAuth callback failed\";\n res.status(500).send(message);\n }\n });\n\n router.post(\"/logout\", (req, res) => {\n req.session.destroy((sessionError) => {\n if (sessionError) {\n res.status(500).json({ ok: false, message: \"Failed to destroy session\" });\n return;\n }\n\n res.clearCookie(options.sessionName ?? \"discord_dashboard.sid\");\n res.json({ ok: true });\n });\n });\n\n router.get(\"/api/session\", (req, res) => {\n const auth = req.session.discordAuth;\n if (!auth) {\n res.status(200).json({ authenticated: false });\n return;\n }\n\n const manageableGuildCount = auth.guilds.filter((guild) => guild.owner || canManageGuild(guild.permissions)).length;\n\n res.json({\n authenticated: true,\n user: {\n ...auth.user,\n avatarUrl: getUserAvatarUrl(auth.user)\n },\n guildCount: manageableGuildCount,\n expiresAt: auth.expiresAt\n });\n });\n\n router.get(\"/api/guilds\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n\n if (options.ownerIds && options.ownerIds.length > 0 && !options.ownerIds.includes(context.user.id)) {\n res.status(403).json({ message: \"You are not allowed to access this dashboard.\" });\n return;\n }\n\n let manageableGuilds = context.guilds.filter((guild) => guild.owner || canManageGuild(guild.permissions));\n\n if (options.guildFilter) {\n const filtered: DashboardGuild[] = [];\n for (const guild of manageableGuilds) {\n const allowed = await options.guildFilter(guild, context);\n if (allowed) {\n filtered.push(guild);\n }\n }\n manageableGuilds = filtered;\n }\n\n const botGuildIds = await fetchBotGuildIds(options.botToken);\n const enrichedGuilds = manageableGuilds.map((guild) => {\n const botInGuild = botGuildIds.has(guild.id);\n return {\n ...guild,\n iconUrl: getGuildIconUrl(guild),\n botInGuild,\n inviteUrl: botInGuild ? undefined : createGuildInviteUrl(options, guild.id)\n };\n });\n\n res.json({ guilds: enrichedGuilds });\n });\n\n router.get(\"/api/overview\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const cards = await resolveOverviewCards(options, context);\n res.json({ cards });\n });\n\n router.get(\"/api/home/categories\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const activeScope = resolveScope(context);\n const categories = await resolveHomeCategories(options, context);\n const visible = categories.filter((item) => item.scope === \"setup\" || item.scope === activeScope);\n res.json({ categories: visible, activeScope });\n });\n\n router.get(\"/api/home\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const activeScope = resolveScope(context);\n const categoryId = typeof req.query.categoryId === \"string\" ? req.query.categoryId : undefined;\n let sections = await resolveHomeSections(options, context);\n\n sections = sections.filter((section) => {\n const sectionScope = section.scope ?? activeScope;\n if (sectionScope !== \"setup\" && sectionScope !== activeScope) {\n return false;\n }\n\n if (!categoryId) {\n return true;\n }\n\n return section.categoryId === categoryId;\n });\n\n res.json({ sections, activeScope });\n });\n\n router.get(\"/api/lookup/roles\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const guildId = typeof req.query.guildId === \"string\" && req.query.guildId.length > 0\n ? req.query.guildId\n : context.selectedGuildId;\n\n if (!guildId) {\n res.status(400).json({ message: \"guildId is required\" });\n return;\n }\n\n const query = typeof req.query.q === \"string\" ? req.query.q : \"\";\n const limit = typeof req.query.limit === \"string\" ? Number(req.query.limit) : undefined;\n const includeManaged = typeof req.query.includeManaged === \"string\"\n ? req.query.includeManaged === \"true\"\n : undefined;\n\n const roles = await context.helpers.searchGuildRoles(guildId, query, {\n limit: Number.isFinite(limit) ? limit : undefined,\n includeManaged\n });\n\n res.json({ roles });\n });\n\n router.get(\"/api/lookup/channels\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const guildId = typeof req.query.guildId === \"string\" && req.query.guildId.length > 0\n ? req.query.guildId\n : context.selectedGuildId;\n\n if (!guildId) {\n res.status(400).json({ message: \"guildId is required\" });\n return;\n }\n\n const query = typeof req.query.q === \"string\" ? req.query.q : \"\";\n const limit = typeof req.query.limit === \"string\" ? Number(req.query.limit) : undefined;\n const nsfw = typeof req.query.nsfw === \"string\"\n ? req.query.nsfw === \"true\"\n : undefined;\n const channelTypes = typeof req.query.channelTypes === \"string\"\n ? req.query.channelTypes\n .split(\",\")\n .map((item) => Number(item.trim()))\n .filter((item) => Number.isFinite(item))\n : undefined;\n\n const channels = await context.helpers.searchGuildChannels(guildId, query, {\n limit: Number.isFinite(limit) ? limit : undefined,\n nsfw,\n channelTypes\n });\n\n res.json({ channels });\n });\n\n router.get(\"/api/lookup/members\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const guildId = typeof req.query.guildId === \"string\" && req.query.guildId.length > 0\n ? req.query.guildId\n : context.selectedGuildId;\n\n if (!guildId) {\n res.status(400).json({ message: \"guildId is required\" });\n return;\n }\n\n const query = typeof req.query.q === \"string\" ? req.query.q : \"\";\n const limit = typeof req.query.limit === \"string\" ? Number(req.query.limit) : undefined;\n const members = await context.helpers.searchGuildMembers(guildId, query, {\n limit: Number.isFinite(limit) ? limit : undefined\n });\n\n res.json({ members });\n });\n\n router.post(\"/api/home/:actionId\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const action = options.home?.actions?.[req.params.actionId];\n\n if (!action) {\n res.status(404).json({ ok: false, message: \"Home action not found\" });\n return;\n }\n\n const payload = req.body as { sectionId?: string; values?: Record<string, unknown> };\n if (!payload || typeof payload.sectionId !== \"string\" || !payload.values || typeof payload.values !== \"object\") {\n res.status(400).json({ ok: false, message: \"Invalid home action payload\" });\n return;\n }\n\n let result: PluginActionResult;\n try {\n result = await action(context, {\n sectionId: payload.sectionId,\n values: payload.values\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Home action failed\";\n res.status(500).json({ ok: false, message });\n return;\n }\n\n res.json(result);\n });\n\n router.get(\"/api/plugins\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const activeScope = context.selectedGuildId ? \"guild\" : \"user\";\n const payload = [] as Array<{ id: string; name: string; description?: string; panels: unknown[] }>;\n\n for (const plugin of plugins) {\n const pluginScope = plugin.scope ?? \"both\";\n if (pluginScope !== \"both\" && pluginScope !== activeScope) {\n continue;\n }\n\n const panels = await plugin.getPanels(context);\n payload.push({\n id: plugin.id,\n name: plugin.name,\n description: plugin.description,\n panels\n });\n }\n\n res.json({ plugins: payload });\n });\n\n router.post(\"/api/plugins/:pluginId/:actionId\", ensureAuthenticated, async (req, res) => {\n const context = createContext(req, options);\n const plugin = plugins.find((item) => item.id === req.params.pluginId) as DashboardPlugin | undefined;\n\n if (!plugin) {\n res.status(404).json({ ok: false, message: \"Plugin not found\" });\n return;\n }\n\n const action = plugin.actions?.[req.params.actionId];\n if (!action) {\n res.status(404).json({ ok: false, message: \"Action not found\" });\n return;\n }\n\n let result: PluginActionResult;\n try {\n result = await action(context, req.body);\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Plugin action failed\";\n res.status(500).json({ ok: false, message });\n return;\n }\n\n res.json(result);\n });\n\n app.use(basePath, router);\n\n let server: Server | undefined;\n\n return {\n app,\n async start() {\n if (options.app) {\n return;\n }\n\n if (server) {\n return;\n }\n\n const port = options.port ?? 3000;\n const host = options.host ?? \"0.0.0.0\";\n\n server = createServer(app);\n await new Promise<void>((resolve) => {\n server!.listen(port, host, () => resolve());\n });\n },\n async stop() {\n if (!server) {\n return;\n }\n\n await new Promise<void>((resolve, reject) => {\n server!.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n\n resolve();\n });\n });\n\n server = undefined;\n }\n };\n}\n","import type {\n DashboardDiscordHelpers,\n DiscordChannel,\n DiscordMember,\n DiscordRole\n} from \"./types\";\n\nconst DISCORD_API = \"https://discord.com/api/v10\";\n\nasync function fetchDiscordWithBot<T>(botToken: string, path: string): Promise<T | null> {\n const response = await fetch(`${DISCORD_API}${path}`, {\n headers: {\n Authorization: `Bot ${botToken}`\n }\n });\n\n if (!response.ok) {\n return null;\n }\n\n return (await response.json()) as T;\n}\n\nexport function createDiscordHelpers(botToken: string): DashboardDiscordHelpers {\n return {\n async getChannel(channelId: string): Promise<DiscordChannel | null> {\n return await fetchDiscordWithBot<DiscordChannel>(botToken, `/channels/${channelId}`);\n },\n\n async getGuildChannels(guildId: string): Promise<DiscordChannel[]> {\n return (await fetchDiscordWithBot<DiscordChannel[]>(botToken, `/guilds/${guildId}/channels`)) ?? [];\n },\n\n async searchGuildChannels(\n guildId: string,\n query: string,\n options?: { limit?: number; nsfw?: boolean; channelTypes?: number[] }\n ): Promise<DiscordChannel[]> {\n const channels = (await fetchDiscordWithBot<DiscordChannel[]>(botToken, `/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 async getRole(guildId: string, roleId: string): Promise<DiscordRole | null> {\n const roles = await fetchDiscordWithBot<DiscordRole[]>(botToken, `/guilds/${guildId}/roles`);\n if (!roles) {\n return null;\n }\n\n return roles.find((role) => role.id === roleId) ?? null;\n },\n\n async getGuildRoles(guildId: string): Promise<DiscordRole[]> {\n return (await fetchDiscordWithBot<DiscordRole[]>(botToken, `/guilds/${guildId}/roles`)) ?? [];\n },\n\n async searchGuildRoles(\n guildId: string,\n query: string,\n options?: { limit?: number; includeManaged?: boolean }\n ): Promise<DiscordRole[]> {\n const roles = (await fetchDiscordWithBot<DiscordRole[]>(botToken, `/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 async searchGuildMembers(\n guildId: string,\n query: string,\n options?: { limit?: number }\n ): 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 fetchDiscordWithBot<DiscordMember[]>(botToken, `/guilds/${guildId}/members/search?${params.toString()}`)) ?? [];\n },\n\n async getGuildMember(guildId: string, userId: string): Promise<DiscordMember | null> {\n return await fetchDiscordWithBot<DiscordMember>(botToken, `/guilds/${guildId}/members/${userId}`);\n }\n };\n}\n","import type { DashboardDesignConfig } from \"./types\";\n\nconst appCss = `\n:root {\n color-scheme: dark;\n --bg: #13151a;\n --rail: #1e1f24;\n --content-bg: #2b2d31;\n --panel: #313338;\n --panel-2: #3a3d43;\n --text: #eef2ff;\n --muted: #b5bac1;\n --primary: #5865f2;\n --success: #20c997;\n --warning: #f4c95d;\n --danger: #ff6b6b;\n --info: #4dabf7;\n --border: rgba(255, 255, 255, 0.12);\n}\n* { box-sizing: border-box; }\nbody {\n margin: 0;\n font-family: Inter, ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif;\n background: var(--bg);\n color: var(--text);\n}\n.layout {\n display: flex;\n min-height: 100vh;\n}\n.sidebar {\n width: 76px;\n background: var(--rail);\n padding: 12px 0;\n border-right: 1px solid var(--border);\n}\n.server-rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.server-item {\n position: relative;\n width: 48px;\n height: 48px;\n border-radius: 50%;\n overflow: visible;\n border: none;\n padding: 0;\n background: var(--panel);\n color: #fff;\n font-weight: 700;\n display: grid;\n place-items: center;\n cursor: pointer;\n transition: border-radius .15s ease, background .15s ease, transform .15s ease;\n}\n.server-item:hover { border-radius: 16px; background: #404249; }\n.server-item.active {\n border-radius: 16px;\n background: var(--primary);\n transform: scale(1.1);\n}\n.server-item-indicator {\n position: absolute;\n left: -9px;\n width: 4px;\n height: 20px;\n border-radius: 999px;\n background: #fff;\n opacity: 0;\n transform: scaleY(0.5);\n transition: opacity .15s ease, transform .15s ease, height .15s ease;\n}\n.server-item.active .server-item-indicator {\n opacity: 1;\n transform: scaleY(1);\n height: 28px;\n}\n.server-avatar {\n width: 100%;\n height: 100%;\n object-fit: cover;\n object-position: center;\n display: block;\n border-radius: inherit;\n}\n.server-fallback {\n font-weight: 700;\n font-size: 0.8rem;\n}\n.main-tabs {\n display: flex;\n gap: 8px;\n margin-bottom: 14px;\n}\n.main-tab.active {\n background: var(--primary);\n border-color: transparent;\n}\n.server-status {\n position: absolute;\n right: -3px;\n bottom: -3px;\n width: 12px;\n height: 12px;\n border-radius: 999px;\n border: 2px solid var(--rail);\n background: #3ba55d;\n z-index: 2;\n}\n.server-status.offline {\n background: #747f8d;\n}\n.content {\n flex: 1;\n background: var(--content-bg);\n min-width: 0;\n}\n.topbar {\n display: grid;\n grid-template-columns: 1fr auto 1fr;\n align-items: center;\n padding: 14px 20px;\n border-bottom: 1px solid var(--border);\n}\n.brand {\n font-size: 1rem;\n font-weight: 700;\n}\n.center-title {\n text-align: center;\n font-weight: 700;\n font-size: 1rem;\n}\n.topbar-right {\n justify-self: end;\n}\n.container {\n padding: 22px;\n}\n.grid {\n display: grid;\n gap: 16px;\n}\n.cards { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }\n.panel {\n background: var(--panel);\n border: 1px solid var(--border);\n border-radius: 10px;\n padding: 16px;\n}\n.title { color: var(--muted); font-size: 0.9rem; }\n.value { font-size: 1.7rem; font-weight: 700; margin-top: 6px; }\n.subtitle { margin-top: 8px; color: var(--muted); font-size: 0.88rem; }\n.section-title { font-size: 1rem; margin: 20px 0 12px; color: #ffffff; }\n.pill {\n padding: 4px 9px;\n border-radius: 999px;\n font-size: 0.76rem;\n border: 1px solid var(--border);\n color: var(--muted);\n}\nbutton {\n border: 1px solid var(--border);\n background: var(--panel-2);\n color: var(--text);\n border-radius: 8px;\n padding: 8px 12px;\n cursor: pointer;\n}\nbutton.primary {\n background: var(--primary);\n border: none;\n}\nbutton.danger { background: #3a1e27; border-color: rgba(255,107,107,.45); }\n.actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }\n.home-categories {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n margin-bottom: 12px;\n}\n.home-category-btn.active {\n background: var(--primary);\n border-color: transparent;\n}\n.home-sections {\n display: flex;\n flex-wrap: wrap;\n gap: 12px;\n margin-bottom: 12px;\n}\n.home-section-panel {\n flex: 0 0 100%;\n max-width: 100%;\n}\n.home-width-50 {\n flex-basis: calc(50% - 6px);\n max-width: calc(50% - 6px);\n}\n.home-width-33 {\n flex-basis: calc(33.333333% - 8px);\n max-width: calc(33.333333% - 8px);\n}\n.home-width-20 {\n flex-basis: calc(20% - 9.6px);\n max-width: calc(20% - 9.6px);\n}\n@media (max-width: 980px) {\n .home-width-50,\n .home-width-33,\n .home-width-20 {\n flex-basis: 100%;\n max-width: 100%;\n }\n}\n.home-fields { display: grid; gap: 10px; margin-top: 10px; }\n.home-field { display: grid; gap: 6px; }\n.home-field label { color: var(--muted); font-size: 0.84rem; }\n.lookup-wrap { position: relative; }\n.home-input,\n.home-textarea,\n.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: 8px 10px;\n}\n.home-textarea { min-height: 92px; resize: vertical; }\n.home-checkbox {\n width: 18px;\n height: 18px;\n}\n.home-field-row {\n display: flex;\n align-items: center;\n gap: 8px;\n}\n.home-message {\n margin-top: 8px;\n color: var(--muted);\n font-size: 0.84rem;\n}\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: var(--panel);\n border-radius: 8px;\n max-height: 220px;\n overflow: auto;\n display: none;\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.lookup-item:hover {\n background: var(--panel-2);\n}\n.lookup-selected {\n margin-top: 6px;\n font-size: 0.82rem;\n color: var(--muted);\n}\n.kv { display: grid; gap: 8px; margin-top: 10px; }\n.kv-item {\n display: flex;\n justify-content: space-between;\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 8px 10px;\n background: var(--panel-2);\n}\n.plugin-fields {\n display: grid;\n gap: 10px;\n margin-top: 10px;\n}\n.plugin-field {\n display: grid;\n gap: 6px;\n}\n.plugin-field > label {\n color: var(--muted);\n font-size: 0.84rem;\n}\n.list-editor {\n border: 1px solid var(--border);\n border-radius: 8px;\n background: var(--panel-2);\n padding: 8px;\n display: grid;\n gap: 8px;\n}\n.list-items {\n display: grid;\n 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: var(--panel);\n}\n.list-item.dragging {\n opacity: .6;\n}\n.drag-handle {\n color: var(--muted);\n user-select: none;\n cursor: grab;\n font-size: 0.9rem;\n}\n.list-input {\n width: 100%;\n border: none;\n outline: none;\n background: transparent;\n color: var(--text);\n}\n.list-add {\n justify-self: start;\n}\n.empty { color: var(--muted); font-size: 0.9rem; }\n`;\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#039;\");\n}\n\nexport function renderDashboardHtml(name: string, basePath: string, setupDesign?: DashboardDesignConfig): string {\n const safeName = escapeHtml(name);\n const scriptData = JSON.stringify({ basePath, setupDesign: setupDesign ?? {} });\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>${appCss}</style>\n</head>\n<body>\n <div class=\"layout\">\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=\"brand\">${safeName}</div>\n <div id=\"centerTitle\" class=\"center-title\">User Dashboard</div>\n <div id=\"userMeta\" class=\"pill topbar-right\">Loading...</div>\n </header>\n\n <div class=\"container\">\n <div class=\"main-tabs\">\n <button id=\"tabHome\" class=\"main-tab active\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab\">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\n <script>\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) {\n throw new Error(await response.text());\n }\n return response.json();\n };\n\n const buildApiUrl = (path) => {\n if (!state.selectedGuildId) {\n return dashboardConfig.basePath + path;\n }\n\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(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#039;\");\n\n const normalizeBoxWidth = (value) => {\n const numeric = Number(value);\n if (numeric === 50 || numeric === 33 || numeric === 20) {\n return numeric;\n }\n\n return 100;\n };\n\n const applySetupDesign = () => {\n const root = document.documentElement;\n const design = dashboardConfig.setupDesign || {};\n const mappings = {\n bg: \"--bg\",\n rail: \"--rail\",\n contentBg: \"--content-bg\",\n panel: \"--panel\",\n panel2: \"--panel-2\",\n text: \"--text\",\n muted: \"--muted\",\n primary: \"--primary\",\n success: \"--success\",\n warning: \"--warning\",\n danger: \"--danger\",\n info: \"--info\",\n border: \"--border\"\n };\n\n Object.entries(mappings).forEach(([key, cssVar]) => {\n const value = design[key];\n if (typeof value === \"string\" && value.trim().length > 0) {\n root.style.setProperty(cssVar, value.trim());\n }\n });\n };\n\n const makeButton = (action, pluginId, panelId, panelElement) => {\n const button = document.createElement(\"button\");\n button.textContent = action.label;\n button.className = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\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) {\n return;\n }\n\n values[fieldId] = toFieldValue({ type: fieldType }, inputEl);\n });\n payload = {\n panelId,\n values\n };\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) {\n alert(result.message);\n }\n if (result.refresh) {\n await refreshContent();\n }\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\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\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\" + (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\") {\n alert(\"Popup blocked. Please allow popups to open the invite page.\");\n }\n return;\n }\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\" + (homeActive ? \" active\" : \"\");\n el.tabPlugins.className = \"main-tab\" + (!homeActive ? \" active\" : \"\");\n };\n\n const updateContextLabel = () => {\n if (!state.selectedGuildId) {\n el.centerTitle.textContent = \"User Dashboard\";\n return;\n }\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\n el.homeCategories.innerHTML = \"\";\n state.homeCategories.forEach((category) => {\n const button = document.createElement(\"button\");\n button.className = \"home-category-btn\" + (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) {\n return [];\n }\n\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch {\n return [];\n }\n }\n\n if (field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\") {\n const raw = element.dataset.selectedObject;\n if (!raw) {\n return null;\n }\n\n try {\n return JSON.parse(raw);\n } catch {\n return null;\n }\n }\n\n if (field.type === \"boolean\") {\n return Boolean(element.checked);\n }\n\n if (field.type === \"number\") {\n if (element.value === \"\") {\n return null;\n }\n return Number(element.value);\n }\n\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\";\n addButton.textContent = \"Add Button\";\n\n const normalizeValues = () => {\n const values = Array.from(itemsWrap.querySelectorAll(\".list-input\"))\n .map((item) => item.value.trim())\n .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\n const handle = document.createElement(\"span\");\n handle.className = \"drag-handle\";\n handle.textContent = \"⋮⋮\";\n\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\n const removeButton = document.createElement(\"button\");\n removeButton.type = \"button\";\n removeButton.textContent = \"×\";\n removeButton.addEventListener(\"click\", () => {\n row.remove();\n normalizeValues();\n });\n\n row.addEventListener(\"dragstart\", () => {\n row.classList.add(\"dragging\");\n });\n row.addEventListener(\"dragend\", () => {\n row.classList.remove(\"dragging\");\n normalizeValues();\n });\n\n row.appendChild(handle);\n row.appendChild(textInput);\n 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) {\n return;\n }\n\n const siblings = Array.from(itemsWrap.querySelectorAll(\".list-item:not(.dragging)\"));\n let inserted = false;\n\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\n if (!inserted) {\n itemsWrap.appendChild(dragging);\n }\n });\n\n const initialValues = Array.isArray(field.value)\n ? field.value.map((item) => String(item))\n : [];\n if (initialValues.length === 0) {\n initialValues.push(\"Yes\", \"No\");\n }\n\n initialValues.forEach((value) => {\n itemsWrap.appendChild(makeRow(value));\n });\n\n addButton.addEventListener(\"click\", () => {\n itemsWrap.appendChild(makeRow(\"\"));\n normalizeValues();\n });\n\n editor.appendChild(itemsWrap);\n editor.appendChild(addButton);\n fieldWrap.appendChild(editor);\n normalizeValues();\n };\n\n const showLookupResults = (container, items, labelResolver, onSelect) => {\n container.innerHTML = \"\";\n\n if (!items.length) {\n container.style.display = \"none\";\n return;\n }\n\n items.forEach((item) => {\n const btn = document.createElement(\"button\");\n btn.type = \"button\";\n btn.className = \"lookup-item\";\n btn.textContent = labelResolver(item);\n btn.addEventListener(\"click\", () => {\n onSelect(item);\n container.style.display = \"none\";\n });\n container.appendChild(btn);\n });\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);\n wrap.appendChild(results);\n fieldWrap.appendChild(wrap);\n 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) {\n results.style.display = \"none\";\n return;\n }\n\n try {\n if (field.type === \"role-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n if (field.lookup?.includeManaged !== undefined) {\n params.set(\"includeManaged\", String(Boolean(field.lookup.includeManaged)));\n }\n\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/roles?\" + params.toString()));\n showLookupResults(\n results,\n payload.roles || [],\n (item) => \"@\" + item.name,\n (item) => {\n input.value = item.name;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected role: @\" + item.name + \" (\" + item.id + \")\";\n }\n );\n } else if (field.type === \"channel-search\") {\n const params = new URLSearchParams({ q: query, limit: String(limit) });\n if (field.lookup?.nsfw !== undefined) {\n params.set(\"nsfw\", String(Boolean(field.lookup.nsfw)));\n }\n if (field.lookup?.channelTypes && field.lookup.channelTypes.length > 0) {\n params.set(\"channelTypes\", field.lookup.channelTypes.join(\",\"));\n }\n\n const payload = await fetchJson(buildApiUrl(\"/api/lookup/channels?\" + params.toString()));\n showLookupResults(\n results,\n payload.channels || [],\n (item) => \"#\" + item.name,\n (item) => {\n input.value = item.name;\n input.dataset.selectedObject = JSON.stringify(item);\n selected.textContent = \"Selected channel: #\" + item.name + \" (\" + item.id + \")\";\n }\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(\n results,\n payload.members || [],\n (item) => {\n const username = item?.user?.username || \"unknown\";\n const nick = item?.nick ? \" (\" + item.nick + \")\" : \"\";\n return username + nick;\n },\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 }\n } catch {\n results.style.display = \"none\";\n }\n };\n\n input.addEventListener(\"input\", () => {\n input.dataset.selectedObject = \"\";\n selected.textContent = \"No selection\";\n runSearch();\n });\n\n input.addEventListener(\"blur\", () => {\n setTimeout(() => {\n results.style.display = \"none\";\n }, 120);\n });\n };\n\n const renderHomeSections = (sections) => {\n if (!sections.length) {\n el.homeSections.innerHTML = '<div class=\"empty\">No home sections configured.</div>';\n return;\n }\n\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;\n heading.style.margin = \"0\";\n wrap.appendChild(heading);\n\n if (section.description) {\n const desc = document.createElement(\"div\");\n desc.className = \"subtitle\";\n desc.textContent = section.description;\n 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;\n fieldWrap.appendChild(label);\n\n let input;\n if (field.type === \"textarea\") {\n input = document.createElement(\"textarea\");\n input.className = \"home-textarea\";\n input.value = field.value == null ? \"\" : String(field.value);\n } else if (field.type === \"select\") {\n input = document.createElement(\"select\");\n input.className = \"home-select\";\n (field.options || []).forEach((option) => {\n const optionEl = document.createElement(\"option\");\n optionEl.value = option.value;\n optionEl.textContent = option.label;\n if (String(field.value ?? \"\") === option.value) {\n optionEl.selected = true;\n }\n input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\");\n row.className = \"home-field-row\";\n input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.className = \"home-checkbox\";\n input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\");\n stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => {\n stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n });\n row.appendChild(input);\n row.appendChild(stateText);\n fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\");\n input.className = \"home-input\";\n input.type = field.type === \"number\" ? \"number\" : \"text\";\n input.value = field.value == null ? \"\" : String(field.value);\n }\n\n if (input) {\n input.dataset.homeFieldId = field.id;\n input.dataset.homeFieldType = field.type;\n if (field.placeholder && \"placeholder\" in input) {\n input.placeholder = field.placeholder;\n }\n if (field.required && \"required\" in input) {\n input.required = true;\n }\n if (field.readOnly) {\n if (\"readOnly\" in input) {\n input.readOnly = true;\n }\n if (\"disabled\" in input) {\n input.disabled = true;\n }\n }\n\n if (field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\") {\n setupLookupField(field, input, fieldWrap);\n } else if (field.type !== \"boolean\") {\n fieldWrap.appendChild(input);\n }\n }\n\n fieldsWrap.appendChild(fieldWrap);\n });\n\n wrap.appendChild(fieldsWrap);\n }\n\n if (section.actions?.length) {\n const actions = document.createElement(\"div\");\n actions.className = \"actions\";\n\n section.actions.forEach((action) => {\n const button = document.createElement(\"button\");\n button.textContent = action.label;\n button.className = action.variant === \"primary\" ? \"primary\" : action.variant === \"danger\" ? \"danger\" : \"\";\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) {\n return;\n }\n\n values[fieldId] = toFieldValue({ type: fieldType }, inputEl);\n });\n\n const result = await fetchJson(buildApiUrl(\"/api/home/\" + encodeURIComponent(action.id)), {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n sectionId: section.id,\n values\n })\n });\n\n message.textContent = result.message || \"Saved.\";\n if (result.refresh) {\n await refreshContent();\n }\n } catch (error) {\n message.textContent = error instanceof Error ? error.message : \"Save failed\";\n } finally {\n button.disabled = false;\n }\n });\n actions.appendChild(button);\n });\n\n wrap.appendChild(actions);\n }\n\n wrap.appendChild(message);\n el.homeSections.appendChild(wrap);\n });\n };\n\n const renderPlugins = (plugins) => {\n if (!plugins.length) {\n el.plugins.innerHTML = '<div class=\"empty\">No plugins configured yet.</div>';\n return;\n }\n\n el.plugins.innerHTML = \"\";\n plugins.forEach((plugin) => {\n const wrap = document.createElement(\"article\");\n wrap.className = \"panel\";\n\n const heading = document.createElement(\"div\");\n heading.className = \"title\";\n heading.textContent = plugin.name;\n wrap.appendChild(heading);\n\n if (plugin.description) {\n const desc = document.createElement(\"div\");\n desc.className = \"subtitle\";\n desc.textContent = plugin.description;\n wrap.appendChild(desc);\n }\n\n (plugin.panels || []).forEach((panel) => {\n const panelBody = document.createElement(\"div\");\n\n const panelTitle = document.createElement(\"h4\");\n panelTitle.textContent = panel.title;\n panelTitle.style.marginBottom = \"4px\";\n panelBody.appendChild(panelTitle);\n\n if (panel.description) {\n const p = document.createElement(\"div\");\n p.className = \"subtitle\";\n p.textContent = panel.description;\n panelBody.appendChild(p);\n }\n\n if (panel.fields?.length) {\n const fieldsWrap = document.createElement(\"div\");\n fieldsWrap.className = \"plugin-fields\";\n\n panel.fields.forEach((field) => {\n const fieldWrap = document.createElement(\"div\");\n fieldWrap.className = field.editable ? \"plugin-field\" : \"kv-item\";\n\n if (!field.editable) {\n const display = field.value == null\n ? \"\"\n : typeof field.value === \"object\"\n ? JSON.stringify(field.value)\n : String(field.value);\n fieldWrap.innerHTML = '<strong>' + escapeHtml(field.label) + '</strong><span>' + escapeHtml(display) + '</span>';\n fieldsWrap.appendChild(fieldWrap);\n return;\n }\n\n const label = document.createElement(\"label\");\n label.textContent = field.label;\n fieldWrap.appendChild(label);\n\n let input;\n if (field.type === \"textarea\") {\n input = document.createElement(\"textarea\");\n input.className = \"home-textarea\";\n input.value = field.value == null ? \"\" : String(field.value);\n } else if (field.type === \"select\") {\n input = document.createElement(\"select\");\n input.className = \"home-select\";\n (field.options || []).forEach((option) => {\n const optionEl = document.createElement(\"option\");\n optionEl.value = option.value;\n optionEl.textContent = option.label;\n if (String(field.value ?? \"\") === option.value) {\n optionEl.selected = true;\n }\n input.appendChild(optionEl);\n });\n } else if (field.type === \"boolean\") {\n const row = document.createElement(\"div\");\n row.className = \"home-field-row\";\n input = document.createElement(\"input\");\n input.type = \"checkbox\";\n input.className = \"home-checkbox\";\n input.checked = Boolean(field.value);\n const stateText = document.createElement(\"span\");\n stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n input.addEventListener(\"change\", () => {\n stateText.textContent = input.checked ? \"Enabled\" : \"Disabled\";\n });\n row.appendChild(input);\n row.appendChild(stateText);\n fieldWrap.appendChild(row);\n } else {\n input = document.createElement(\"input\");\n input.className = \"home-input\";\n input.type = field.type === \"number\" ? \"number\" : field.type === \"url\" ? \"url\" : \"text\";\n input.value = field.value == null ? \"\" : String(field.value);\n }\n\n if (input) {\n input.dataset.pluginFieldId = field.id || field.label;\n input.dataset.pluginFieldType = field.type || \"text\";\n if (field.placeholder && \"placeholder\" in input) {\n input.placeholder = field.placeholder;\n }\n if (field.required && \"required\" in input) {\n input.required = true;\n }\n\n const isLookup = field.type === \"role-search\" || field.type === \"channel-search\" || field.type === \"member-search\";\n if (isLookup) {\n setupLookupField(field, input, fieldWrap);\n } else if (field.type === \"string-list\") {\n setupStringListField(field, input, fieldWrap);\n } else if (field.type !== \"boolean\") {\n fieldWrap.appendChild(input);\n }\n }\n\n fieldsWrap.appendChild(fieldWrap);\n });\n panelBody.appendChild(fieldsWrap);\n }\n\n if (panel.actions?.length) {\n const actions = document.createElement(\"div\");\n actions.className = \"actions\";\n panel.actions.forEach((action) => {\n actions.appendChild(makeButton(action, plugin.id, panel.id, panelBody));\n });\n panelBody.appendChild(actions);\n }\n\n wrap.appendChild(panelBody);\n });\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\n ? \"/api/home?categoryId=\" + encodeURIComponent(state.selectedHomeCategoryId)\n : \"/api/home\";\n\n const [home, overview, plugins] = await Promise.all([\n fetchJson(buildApiUrl(homePath)),\n fetchJson(buildApiUrl(\"/api/overview\")),\n fetchJson(buildApiUrl(\"/api/plugins\"))\n ]);\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 applySetupDesign();\n\n const session = await fetchJson(dashboardConfig.basePath + \"/api/session\");\n if (!session.authenticated) {\n window.location.href = dashboardConfig.basePath + \"/login\";\n return;\n }\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();\n updateContextLabel();\n\n el.tabHome.addEventListener(\"click\", () => {\n state.activeMainTab = \"home\";\n applyMainTab();\n });\n\n el.tabPlugins.addEventListener(\"click\", () => {\n state.activeMainTab = \"plugins\";\n applyMainTab();\n });\n\n applyMainTab();\n await refreshContent();\n };\n\n loadInitialData().catch((error) => {\n el.userMeta.textContent = \"Load failed\";\n console.error(error);\n });\n </script>\n</body>\n</html>`;\n}\n","import { renderDashboardHtml } from \"../templates\";\nimport type { DashboardTemplateRenderer } from \"../types\";\n\nfunction escapeHtml(value: string): string {\n return value\n .replaceAll(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#039;\");\n}\n\nfunction extractDashboardScript(html: string): string {\n const match = html.match(/<script>([\\s\\S]*)<\\/script>\\s*<\\/body>\\s*<\\/html>\\s*$/);\n if (!match) {\n throw new Error(\"Failed to resolve dashboard script for compact template.\");\n }\n\n return match[1];\n}\n\nconst compactCss = `\n:root {\n color-scheme: dark;\n --bg: #0f1221;\n --rail: #171a2d;\n --content-bg: #0f1426;\n --panel: #1f243b;\n --panel-2: #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* { box-sizing: border-box; }\nbody {\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 {\n min-height: 100vh;\n display: grid;\n grid-template-rows: auto 1fr;\n}\n.topbar {\n display: grid;\n grid-template-columns: auto 1fr auto;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--border);\n background: rgba(15, 20, 38, 0.7);\n backdrop-filter: blur(8px);\n}\n.brand { font-weight: 800; letter-spacing: .2px; }\n.center-title { text-align: center; font-weight: 700; color: #d8defc; }\n.pill {\n justify-self: end;\n padding: 4px 8px;\n border-radius: 999px;\n border: 1px solid var(--border);\n color: var(--muted);\n font-size: .75rem;\n}\n.layout {\n display: grid;\n grid-template-columns: 80px 1fr;\n min-height: 0;\n}\n.sidebar {\n border-right: 1px solid var(--border);\n background: linear-gradient(180deg, var(--rail), #121528);\n padding: 10px 0;\n}\n.server-rail {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 10px;\n}\n.server-item {\n position: relative;\n width: 46px;\n height: 46px;\n border: none;\n border-radius: 14px;\n overflow: visible;\n background: var(--panel);\n color: #fff;\n font-weight: 700;\n cursor: pointer;\n transition: transform .15s ease, background .15s ease;\n}\n.server-item:hover { transform: translateY(-1px); background: #323b5f; }\n.server-item.active { background: var(--primary); transform: translateY(-2px); }\n.server-item-indicator {\n position: absolute;\n left: -8px;\n top: 50%;\n transform: translateY(-50%) scaleY(.5);\n width: 3px;\n height: 18px;\n background: #fff;\n border-radius: 999px;\n opacity: 0;\n transition: opacity .15s ease, transform .15s ease;\n}\n.server-item.active .server-item-indicator {\n opacity: 1;\n transform: translateY(-50%) scaleY(1);\n}\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 {\n position: absolute;\n right: -3px;\n bottom: -3px;\n width: 11px;\n height: 11px;\n border-radius: 999px;\n border: 2px solid var(--rail);\n background: #35d489;\n}\n.server-status.offline { background: #7f8bb3; }\n.content {\n min-width: 0;\n padding: 12px;\n}\n.container {\n background: rgba(23, 28, 48, 0.6);\n border: 1px solid var(--border);\n border-radius: 12px;\n padding: 12px;\n}\n.main-tabs { display: flex; gap: 8px; margin-bottom: 10px; }\nbutton {\n border: 1px solid var(--border);\n background: var(--panel-2);\n color: var(--text);\n border-radius: 8px;\n padding: 7px 10px;\n cursor: pointer;\n}\nbutton.primary { background: var(--primary); border: none; }\nbutton.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 {\n background: linear-gradient(180deg, rgba(42,49,78,.7), rgba(31,36,59,.85));\n border: 1px solid var(--border);\n border-radius: 10px;\n padding: 12px;\n}\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 {\n width: 100%;\n border: 1px solid var(--border);\n background: var(--panel-2);\n color: var(--text);\n border-radius: 8px;\n padding: 7px 9px;\n}\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 {\n position: absolute;\n left: 0;\n right: 0;\n top: calc(100% + 5px);\n z-index: 20;\n border: 1px solid var(--border);\n background: #1f2742;\n border-radius: 8px;\n max-height: 220px;\n overflow: auto;\n display: none;\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.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 {\n display: flex;\n justify-content: space-between;\n border: 1px solid var(--border);\n border-radius: 8px;\n padding: 7px 9px;\n background: var(--panel-2);\n}\n.list-editor {\n border: 1px solid var(--border);\n border-radius: 8px;\n background: var(--panel-2);\n padding: 8px;\n display: grid;\n gap: 8px;\n}\n.list-items { display: grid; gap: 6px; }\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: var(--panel);\n}\n.list-item.dragging { opacity: .6; }\n.drag-handle { color: var(--muted); user-select: none; cursor: grab; 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@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\nexport const compactDashboardTemplateRenderer: DashboardTemplateRenderer = ({\n dashboardName,\n basePath,\n setupDesign\n}) => {\n const script = extractDashboardScript(renderDashboardHtml(dashboardName, basePath, setupDesign));\n const safeName = escapeHtml(dashboardName);\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>${compactCss}</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\">Home</button>\n <button id=\"tabPlugins\" class=\"main-tab\">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>${script}</script>\n</body>\n</html>`;\n};\n","import { renderDashboardHtml } from \"../templates\";\nimport type { DashboardTemplateRenderer } from \"../types\";\n\nexport const defaultDashboardTemplateRenderer: DashboardTemplateRenderer = ({\n dashboardName,\n basePath,\n setupDesign\n}) => renderDashboardHtml(dashboardName, basePath, setupDesign);\n","import type { DashboardTemplateRenderer } from \"../types\";\nimport { compactDashboardTemplateRenderer } from \"./compact\";\nimport { defaultDashboardTemplateRenderer } from \"./default\";\n\nexport const builtinTemplateRenderers: Record<string, DashboardTemplateRenderer> = {\n default: defaultDashboardTemplateRenderer,\n compact: compactDashboardTemplateRenderer\n};\n\nexport function getBuiltinTemplateRenderer(templateId: string): DashboardTemplateRenderer | undefined {\n return builtinTemplateRenderers[templateId];\n}\n","import type {\n DashboardHomeBuilder,\n DashboardContext,\n DashboardOptions,\n DashboardScope,\n DashboardTemplateRenderer,\n HomeActionPayload,\n HomeCategory,\n HomeSection,\n HomeSectionAction,\n HomeSectionField,\n PluginActionResult\n} from \"./types\";\n\ntype HomeActionHandler = (\n context: DashboardContext,\n payload: HomeActionPayload\n) => Promise<PluginActionResult> | PluginActionResult;\n\ntype HomeLoadHandler = (\n context: DashboardContext,\n section: HomeSection\n) => Promise<Partial<HomeSection> | HomeSection | void> | Partial<HomeSection> | HomeSection | void;\n\ninterface DesignerPageDefinition {\n pageId: string;\n category: HomeCategory;\n section: HomeSection;\n}\n\nclass CategoryBuilder {\n private sections: HomeSection[] = [];\n\n constructor(\n private readonly scope: DashboardScope,\n private readonly categoryId: string,\n private readonly categoryLabel: string\n ) {}\n\n section(input: {\n id: string;\n title: string;\n description?: string;\n width?: 100 | 50 | 33 | 20;\n fields?: HomeSectionField[];\n actions?: HomeSectionAction[];\n }): this {\n this.sections.push({\n id: input.id,\n title: input.title,\n description: input.description,\n width: input.width,\n fields: input.fields ?? [],\n actions: input.actions ?? [],\n scope: this.scope,\n categoryId: this.categoryId\n });\n\n return this;\n }\n\n buildCategory(): HomeCategory {\n return {\n id: this.categoryId,\n label: this.categoryLabel,\n scope: this.scope\n };\n }\n\n buildSections(): HomeSection[] {\n return [...this.sections];\n }\n}\n\nexport class DashboardDesigner {\n private readonly partialOptions: Partial<DashboardOptions>;\n private readonly categories: CategoryBuilder[] = [];\n private readonly pages: DesignerPageDefinition[] = [];\n private readonly homeActions: Record<string, HomeActionHandler> = {};\n private readonly loadHandlers: Record<string, HomeLoadHandler> = {};\n private readonly saveHandlers: Record<string, HomeActionHandler> = {};\n\n constructor(baseOptions: Omit<DashboardOptions, \"home\">) {\n this.partialOptions = { ...baseOptions };\n }\n\n setup(input: {\n ownerIds?: string[];\n botInvitePermissions?: string;\n botInviteScopes?: string[];\n dashboardName?: string;\n basePath?: string;\n uiTemplate?: string;\n }): this {\n if (input.ownerIds) this.partialOptions.ownerIds = input.ownerIds;\n if (input.botInvitePermissions) this.partialOptions.botInvitePermissions = input.botInvitePermissions;\n if (input.botInviteScopes) this.partialOptions.botInviteScopes = input.botInviteScopes;\n if (input.dashboardName) this.partialOptions.dashboardName = input.dashboardName;\n if (input.basePath) this.partialOptions.basePath = input.basePath;\n if (input.uiTemplate) this.partialOptions.uiTemplate = input.uiTemplate;\n return this;\n }\n\n useTemplate(templateId: string): this {\n this.partialOptions.uiTemplate = templateId;\n return this;\n }\n\n addTemplate(templateId: string, renderer: DashboardTemplateRenderer): this {\n this.partialOptions.uiTemplates = {\n ...(this.partialOptions.uiTemplates ?? {}),\n [templateId]: renderer\n };\n\n return this;\n }\n\n setupDesign(input: {\n bg?: string;\n rail?: string;\n contentBg?: string;\n panel?: string;\n panel2?: string;\n text?: string;\n muted?: string;\n primary?: string;\n success?: string;\n warning?: string;\n danger?: string;\n info?: string;\n border?: string;\n }): this {\n this.partialOptions.setupDesign = {\n ...(this.partialOptions.setupDesign ?? {}),\n ...input\n };\n\n return this;\n }\n\n userCategory(categoryId: string, categoryLabel: string, build: (builder: CategoryBuilder) => void): this {\n const builder = new CategoryBuilder(\"user\", categoryId, categoryLabel);\n build(builder);\n this.categories.push(builder);\n return this;\n }\n\n guildCategory(categoryId: string, categoryLabel: string, build: (builder: CategoryBuilder) => void): this {\n const builder = new CategoryBuilder(\"guild\", categoryId, categoryLabel);\n build(builder);\n this.categories.push(builder);\n return this;\n }\n\n setupCategory(categoryId: string, categoryLabel: string, build: (builder: CategoryBuilder) => void): this {\n const builder = new CategoryBuilder(\"setup\", categoryId, categoryLabel);\n build(builder);\n this.categories.push(builder);\n return this;\n }\n\n setupPage(input: {\n id: string;\n title: string;\n label?: string;\n scope?: DashboardScope;\n categoryId?: string;\n description?: string;\n width?: 100 | 50 | 33 | 20;\n fields?: HomeSectionField[];\n actions?: HomeSectionAction[];\n }): this {\n const scope = input.scope ?? \"user\";\n const categoryId = input.categoryId ?? input.id;\n\n this.pages.push({\n pageId: input.id,\n category: {\n id: categoryId,\n label: input.label ?? input.title,\n scope\n },\n section: {\n id: input.id,\n title: input.title,\n description: input.description,\n width: input.width,\n scope,\n categoryId,\n fields: input.fields ?? [],\n actions: input.actions ?? []\n }\n });\n\n return this;\n }\n\n onHomeAction(actionId: string, handler: HomeActionHandler): this {\n this.homeActions[actionId] = handler;\n return this;\n }\n\n onLoad(pageId: string, handler: HomeLoadHandler): this {\n this.loadHandlers[pageId] = handler;\n return this;\n }\n\n onload(pageId: string, handler: HomeLoadHandler): this {\n return this.onLoad(pageId, handler);\n }\n\n onSave(pageId: string, handler: HomeActionHandler): this {\n this.saveHandlers[pageId] = handler;\n return this;\n }\n\n onsave(pageId: string, handler: HomeActionHandler): this {\n return this.onSave(pageId, handler);\n }\n\n build(): DashboardOptions {\n const staticCategories = this.categories.map((item) => item.buildCategory());\n const staticSections = this.categories.flatMap((item) => item.buildSections());\n const pageCategories = this.pages.map((item) => item.category);\n const baseSections = [...staticSections, ...this.pages.map((item) => item.section)];\n\n const categoryMap = new Map<string, HomeCategory>();\n for (const category of [...staticCategories, ...pageCategories]) {\n const key = `${category.scope}:${category.id}`;\n if (!categoryMap.has(key)) {\n categoryMap.set(key, category);\n }\n }\n\n const categories = [...categoryMap.values()];\n const saveActionIds: Record<string, string> = {};\n for (const section of baseSections) {\n if (this.saveHandlers[section.id]) {\n saveActionIds[section.id] = `save:${section.id}`;\n }\n }\n\n const resolvedActions: Record<string, HomeActionHandler> = {\n ...this.homeActions\n };\n\n for (const [sectionId, handler] of Object.entries(this.saveHandlers)) {\n resolvedActions[saveActionIds[sectionId]] = handler;\n }\n\n const home: DashboardHomeBuilder = {\n getCategories: () => categories,\n getSections: async (context) => {\n const sections: HomeSection[] = [];\n\n for (const sourceSection of baseSections) {\n let section: HomeSection = {\n ...sourceSection,\n fields: sourceSection.fields ? [...sourceSection.fields] : [],\n actions: sourceSection.actions ? [...sourceSection.actions] : []\n };\n\n const saveActionId = saveActionIds[section.id];\n if (saveActionId && !section.actions?.some((action) => action.id === saveActionId)) {\n section.actions = [...(section.actions ?? []), {\n id: saveActionId,\n label: \"Save\",\n variant: \"primary\"\n }];\n }\n\n const loadHandler = this.loadHandlers[section.id];\n if (loadHandler) {\n const loaded = await loadHandler(context, section);\n if (loaded) {\n section = {\n ...section,\n ...loaded,\n fields: loaded.fields ?? section.fields,\n actions: loaded.actions ?? section.actions\n };\n }\n }\n\n sections.push(section);\n }\n\n return sections;\n },\n actions: resolvedActions as DashboardHomeBuilder[\"actions\"]\n };\n\n return {\n ...(this.partialOptions as DashboardOptions),\n home\n };\n }\n}\n\nexport function createDashboardDesigner(baseOptions: Omit<DashboardOptions, \"home\">): DashboardDesigner {\n return new DashboardDesigner(baseOptions);\n}\n"],"mappings":"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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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoVf,SAAS,WAAW,OAAuB;AACzC,SAAO,MACJ,WAAW,KAAK,OAAO,EACvB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,MAAM,EACtB,WAAW,KAAK,QAAQ,EACxB,WAAW,KAAK,QAAQ;AAC7B;AAEO,SAAS,oBAAoB,MAAc,UAAkB,aAA6C;AAC/G,QAAM,WAAW,WAAW,IAAI;AAChC,QAAM,aAAa,KAAK,UAAU,EAAE,UAAU,aAAa,eAAe,CAAC,EAAE,CAAC;AAE9E,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,QAAQ;AAAA,WACR,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAUY,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,8BA+BP,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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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","names":["escapeHtml","DISCORD_API"]}
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});\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.Nullable(t.Boolean())),\n inviteUrl: t.Optional(t.Nullable(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(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#039;\");\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(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\").replaceAll('\"', \"&quot;\").replaceAll(\"'\", \"&#039;\");\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(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\").replaceAll('\"', \"&quot;\").replaceAll(\"'\", \"&#039;\");\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(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\").replaceAll('\"', \"&quot;\").replaceAll(\"'\", \"&#039;\");\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(\"&\", \"&amp;\").replaceAll(\"<\", \"&lt;\").replaceAll(\">\", \"&gt;\").replaceAll('\"', \"&quot;\").replaceAll(\"'\", \"&#039;\");\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 DashboardOptions } 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 router = new Elysia({ prefix: basePath })\n .use(\n jwt({\n name: \"sessionSigner\",\n secret: options.sessionSecret,\n schema: SessionSchema,\n }),\n )\n .derive(({ set }) => ({\n html: (content: string) => {\n set.headers[\"Content-Type\"] = \"text/html; charset=utf8\";\n return content;\n },\n }))\n .get(\"/\", async ({ sessionSigner, cookie, redirect, html }) => {\n const cookieValue = cookie[sessionName].value as string | undefined;\n if (!cookieValue) return redirect(`${basePath}/login`);\n\n const sessionData = await sessionSigner.verify(cookieValue);\n if (!sessionData || !sessionData.discordAuth) return redirect(`${basePath}/login`);\n\n return html(engine.render(basePath));\n })\n .get(\"/login\", async ({ sessionSigner, cookie, redirect }) => {\n const state = randomBytes(16).toString(\"hex\");\n\n const token = await sessionSigner.sign({ oauthState: state });\n\n cookie[sessionName].set({\n value: token,\n httpOnly: true,\n path: \"/\",\n });\n\n return redirect(engine.getAuthUrl(state));\n })\n .get(\"/callback\", async ({ query, sessionSigner, cookie, redirect }) => {\n const { code, state } = query;\n const cookieValue = cookie[sessionName].value as string | undefined;\n if (!cookieValue) return redirect(`${basePath}/login`);\n\n const session = await sessionSigner.verify(cookieValue);\n\n if (!state || !session || state !== session.oauthState) return redirect(`${basePath}/login`);\n\n const tokens = await engine.exchangeCode(code as string);\n const [user, guilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n const token = await sessionSigner.sign({\n oauthState: undefined,\n discordAuth: {\n accessToken: tokens.access_token,\n user,\n guilds,\n },\n });\n\n cookie[sessionName].set({\n value: token,\n httpOnly: true,\n path: \"/\",\n });\n\n return redirect(basePath);\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 { DashboardOptions } 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 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 const tokens = await engine.exchangeCode(code);\n const [user, guilds] = await Promise.all([engine.fetchUser(tokens.access_token), engine.fetchGuilds(tokens.access_token)]);\n\n request.session.discordAuth = {\n accessToken: tokens.access_token,\n user,\n guilds,\n };\n\n return reply.redirect(basePath);\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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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,QA8MxD,OAAO,aAAa,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mDAUqB,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAkD7B,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;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,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;AAAA;AAAA;AAAA;AAAA;AAAA;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","names":["escapeHtml","escapeHtml","randomBytes","randomBytes","session","randomBytes","randomBytes"]}