feat: add scoped theme builder system with per-route, per-module, and multi-site support
- Add theme_scopes and theme_scope_values database tables for scoped themes - Implement theme resolver engine with inheritance: global > site > module > route - Add module detection for 20+ routes (shop, guilds, radio, news, etc.) - Create admin UI at /admin/theme-builder with scope tree and color editor - Add ScopedThemeVars component for injecting scoped CSS via data-attributes - Add ThemeScopeDetector client component for runtime module/route detection - Add site-resolver for multi-site domain detection - Add /api/themes/export endpoint (JSON, CSS, variables formats) - Add /api/themes/export/embed.js for external integration widget - Add server actions for full CRUD on scopes and theme values - Add admin nav link and EN/NL translations
This commit is contained in:
1 parent
b57697b2e0
commit
a98b194386
19 files changed
+1843
No files matched your search
@@ -2583,3 +2583,45 @@ export const YoutubePlaylists = mysqlTable(
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
// ─── Theme Builder: scoped theme system ──────────────────────────────────────
|
||||
|
||||
export const ThemeScope = mysqlTable("theme_scopes", {
|
||||
id: bigint("id", { mode: "bigint", unsigned: true })
|
||||
.autoincrement()
|
||||
.primaryKey()
|
||||
.notNull(),
|
||||
name: varchar("name", { length: 100 }).notNull(),
|
||||
type: mysqlEnum("type", ["global", "site", "module", "route"]).notNull(),
|
||||
parentId: bigint("parent_id", { mode: "bigint", unsigned: true }),
|
||||
siteDomain: varchar("site_domain", { length: 255 }),
|
||||
routePath: varchar("route_path", { length: 255 }),
|
||||
moduleId: varchar("module_id", { length: 100 }),
|
||||
isActive: boolean("is_active").notNull().default(true),
|
||||
sortOrder: int("sort_order").notNull().default(0),
|
||||
createdAt: datetime("created_at").notNull(),
|
||||
updatedAt: datetime("updated_at").notNull(),
|
||||
});
|
||||
|
||||
export const ThemeScopeValue = mysqlTable(
|
||||
"theme_scope_values",
|
||||
{
|
||||
id: bigint("id", { mode: "bigint", unsigned: true })
|
||||
.autoincrement()
|
||||
.primaryKey()
|
||||
.notNull(),
|
||||
scopeId: bigint("scope_id", { mode: "bigint", unsigned: true }).notNull(),
|
||||
settingKey: varchar("setting_key", { length: 100 }).notNull(),
|
||||
settingVal: varchar("setting_val", { length: 255 }).notNull(),
|
||||
mode: mysqlEnum("mode", ["light", "dark"]).notNull().default("light"),
|
||||
createdAt: datetime("created_at").notNull(),
|
||||
updatedAt: datetime("updated_at").notNull(),
|
||||
},
|
||||
(t) => [
|
||||
uniqueIndex("theme_scope_values_unique").on(
|
||||
t.scopeId,
|
||||
t.settingKey,
|
||||
t.mode,
|
||||
),
|
||||
],
|
||||
);
|
||||
Reference in new issue
Block a user