{"version":3,"file":"color-schemes-D9FgmEbw.mjs","names":[],"sources":["../src/api/handlers/color-schemes.ts"],"sourcesContent":["/**\n * Color-scheme handlers.\n *\n * Proxy the marketplace catalogue (`${marketplace}/api/v1/color-schemes`) for\n * the admin browse page, and persist a site's chosen scheme as the option\n * `emdash:color_scheme` = { id, title, declarations }. `EmDashHead` reads that\n * option and injects the declarations into every page's <head>, so applying a\n * scheme recolors the live site with no rebuild.\n */\n\nimport type { Kysely } from \"kysely\";\n\nimport { OptionsRepository } from \"../../database/repositories/options.js\";\nimport type { Database } from \"../../database/types.js\";\nimport type { ApiResult } from \"../types.js\";\n\n/** The option key holding the applied scheme. */\nexport const COLOR_SCHEME_OPTION = \"emdash:color_scheme\";\n\nexport interface AppliedColorScheme {\n\tid: string;\n\ttitle: string;\n\tdeclarations: string;\n\t/** Kumo admin override CSS — recolors the admin panel to match. */\n\tadminCss: string;\n}\n\nfunction base(marketplaceUrl: string | undefined): string | null {\n\tconst u = (marketplaceUrl ?? \"\").trim().replace(/\\/$/, \"\");\n\treturn u ? u : null;\n}\n\n/** GET the catalogue list from the marketplace. */\nexport async function fetchColorSchemes(\n\tmarketplaceUrl: string | undefined,\n): Promise<ApiResult<unknown>> {\n\tconst b = base(marketplaceUrl);\n\tif (!b) {\n\t\treturn { success: false, error: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" } };\n\t}\n\ttry {\n\t\tconst res = await fetch(`${b}/api/v1/color-schemes`);\n\t\tif (!res.ok) throw new Error(`marketplace ${res.status}`);\n\t\treturn { success: true, data: await res.json() };\n\t} catch (err) {\n\t\treturn { success: false, error: { code: \"MARKETPLACE_UNAVAILABLE\", message: err instanceof Error ? err.message : \"Marketplace is unavailable\" } };\n\t}\n}\n\n/** GET one scheme (with declarations) from the marketplace. */\nexport async function fetchColorScheme(\n\tmarketplaceUrl: string | undefined,\n\tid: string,\n): Promise<ApiResult<{ id: string; title: string; declarations: string; adminCss?: string; [k: string]: unknown }>> {\n\tconst b = base(marketplaceUrl);\n\tif (!b) {\n\t\treturn { success: false, error: { code: \"MARKETPLACE_NOT_CONFIGURED\", message: \"Marketplace is not configured\" } };\n\t}\n\ttry {\n\t\tconst res = await fetch(`${b}/api/v1/color-schemes/${encodeURIComponent(id)}`);\n\t\tif (res.status === 404) {\n\t\t\treturn { success: false, error: { code: \"NOT_FOUND\", message: \"Color scheme not found\" } };\n\t\t}\n\t\tif (!res.ok) throw new Error(`marketplace ${res.status}`);\n\t\treturn { success: true, data: (await res.json()) as { id: string; title: string; declarations: string; adminCss?: string } };\n\t} catch (err) {\n\t\treturn { success: false, error: { code: \"MARKETPLACE_UNAVAILABLE\", message: err instanceof Error ? err.message : \"Marketplace is unavailable\" } };\n\t}\n}\n\n/** Read the applied scheme, or null when none is set. */\nexport async function getAppliedColorScheme(\n\tdb: Kysely<Database>,\n): Promise<AppliedColorScheme | null> {\n\tconst scheme = await new OptionsRepository(db).get<Partial<AppliedColorScheme>>(COLOR_SCHEME_OPTION);\n\tif (!scheme || typeof scheme.declarations !== \"string\" || !scheme.declarations) return null;\n\treturn {\n\t\tid: String(scheme.id ?? \"\"),\n\t\ttitle: String(scheme.title ?? \"\"),\n\t\tdeclarations: scheme.declarations,\n\t\tadminCss: typeof scheme.adminCss === \"string\" ? scheme.adminCss : \"\",\n\t};\n}\n\n/** Apply (persist) a scheme fetched from the marketplace. */\nexport async function applyColorScheme(\n\tdb: Kysely<Database>,\n\tmarketplaceUrl: string | undefined,\n\tid: string,\n): Promise<ApiResult<AppliedColorScheme>> {\n\tconst fetched = await fetchColorScheme(marketplaceUrl, id);\n\tif (!fetched.success) return fetched;\n\tconst { title, declarations, adminCss } = fetched.data;\n\tif (!declarations) {\n\t\treturn { success: false, error: { code: \"INVALID_SCHEME\", message: \"Scheme has no token declarations\" } };\n\t}\n\tconst applied: AppliedColorScheme = { id, title: title ?? id, declarations, adminCss: adminCss ?? \"\" };\n\tawait new OptionsRepository(db).set(COLOR_SCHEME_OPTION, applied);\n\treturn { success: true, data: applied };\n}\n\n/** Clear the applied scheme (revert to the theme's built-in tokens). */\nexport async function clearColorScheme(db: Kysely<Database>): Promise<ApiResult<{ cleared: true }>> {\n\tawait new OptionsRepository(db).delete(COLOR_SCHEME_OPTION);\n\treturn { success: true, data: { cleared: true } };\n}\n"],"mappings":";;;;AAiBA,MAAa,sBAAsB;AAUnC,SAAS,KAAK,gBAAmD;CAChE,MAAM,KAAK,kBAAkB,IAAI,MAAM,CAAC,QAAQ,OAAO,GAAG;AAC1D,QAAO,IAAI,IAAI;;;AAIhB,eAAsB,kBACrB,gBAC8B;CAC9B,MAAM,IAAI,KAAK,eAAe;AAC9B,KAAI,CAAC,EACJ,QAAO;EAAE,SAAS;EAAO,OAAO;GAAE,MAAM;GAA8B,SAAS;GAAiC;EAAE;AAEnH,KAAI;EACH,MAAM,MAAM,MAAM,MAAM,GAAG,EAAE,uBAAuB;AACpD,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,eAAe,IAAI,SAAS;AACzD,SAAO;GAAE,SAAS;GAAM,MAAM,MAAM,IAAI,MAAM;GAAE;UACxC,KAAK;AACb,SAAO;GAAE,SAAS;GAAO,OAAO;IAAE,MAAM;IAA2B,SAAS,eAAe,QAAQ,IAAI,UAAU;IAA8B;GAAE;;;;AAKnJ,eAAsB,iBACrB,gBACA,IACmH;CACnH,MAAM,IAAI,KAAK,eAAe;AAC9B,KAAI,CAAC,EACJ,QAAO;EAAE,SAAS;EAAO,OAAO;GAAE,MAAM;GAA8B,SAAS;GAAiC;EAAE;AAEnH,KAAI;EACH,MAAM,MAAM,MAAM,MAAM,GAAG,EAAE,wBAAwB,mBAAmB,GAAG,GAAG;AAC9E,MAAI,IAAI,WAAW,IAClB,QAAO;GAAE,SAAS;GAAO,OAAO;IAAE,MAAM;IAAa,SAAS;IAA0B;GAAE;AAE3F,MAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,eAAe,IAAI,SAAS;AACzD,SAAO;GAAE,SAAS;GAAM,MAAO,MAAM,IAAI,MAAM;GAA6E;UACpH,KAAK;AACb,SAAO;GAAE,SAAS;GAAO,OAAO;IAAE,MAAM;IAA2B,SAAS,eAAe,QAAQ,IAAI,UAAU;IAA8B;GAAE;;;;AAKnJ,eAAsB,sBACrB,IACqC;CACrC,MAAM,SAAS,MAAM,IAAI,kBAAkB,GAAG,CAAC,IAAiC,oBAAoB;AACpG,KAAI,CAAC,UAAU,OAAO,OAAO,iBAAiB,YAAY,CAAC,OAAO,aAAc,QAAO;AACvF,QAAO;EACN,IAAI,OAAO,OAAO,MAAM,GAAG;EAC3B,OAAO,OAAO,OAAO,SAAS,GAAG;EACjC,cAAc,OAAO;EACrB,UAAU,OAAO,OAAO,aAAa,WAAW,OAAO,WAAW;EAClE;;;AAIF,eAAsB,iBACrB,IACA,gBACA,IACyC;CACzC,MAAM,UAAU,MAAM,iBAAiB,gBAAgB,GAAG;AAC1D,KAAI,CAAC,QAAQ,QAAS,QAAO;CAC7B,MAAM,EAAE,OAAO,cAAc,aAAa,QAAQ;AAClD,KAAI,CAAC,aACJ,QAAO;EAAE,SAAS;EAAO,OAAO;GAAE,MAAM;GAAkB,SAAS;GAAoC;EAAE;CAE1G,MAAM,UAA8B;EAAE;EAAI,OAAO,SAAS;EAAI;EAAc,UAAU,YAAY;EAAI;AACtG,OAAM,IAAI,kBAAkB,GAAG,CAAC,IAAI,qBAAqB,QAAQ;AACjE,QAAO;EAAE,SAAS;EAAM,MAAM;EAAS;;;AAIxC,eAAsB,iBAAiB,IAA6D;AACnG,OAAM,IAAI,kBAAkB,GAAG,CAAC,OAAO,oBAAoB;AAC3D,QAAO;EAAE,SAAS;EAAM,MAAM,EAAE,SAAS,MAAM;EAAE"}