feat: extend theme builder with block visibility, layout, effects, media, and custom CSS tabs
CI / check (push) Failing after 29s
CI / release (push) Skipped
CI / deploy (push) Skipped

- Add theme-blocks.ts registry with 24 themeable blocks across 4 categories
- Extend resolver to resolve blocks, layout, effects, media, and custom CSS per scope
- Add data-theme-block attributes to all site layout blocks
- Extend ScopedThemeVars to generate CSS for block visibility, layout vars, effect vars, media vars, and custom CSS
- Rewrite admin UI with 6 tabs: Colors, Blocks, Layout, Effects, Media, Custom
- Extend server actions to save/load all new setting types
- No database migration needed - uses existing theme_scope_values table with prefixed keys
This commit is contained in:
openhands committed 2026-08-31 17:44:29 +02:00
1 parent 3efd6c90f0
commit adf0658916
7 files changed
+1334 -75

No files matched your search

File diff suppressed because it is too large. Load diff
+22 -7
View File
@@ -22,20 +22,35 @@ export default async function SiteLayout({
<> <>
{session?.user?.id ? ( {session?.user?.id ? (
<> <>
<TopHeader session={session} /> <div data-theme-block="top_header">
<SiteHeader /> <TopHeader session={session} />
<Navigation session={session} /> </div>
<div data-theme-block="site_header">
<SiteHeader />
</div>
<div data-theme-block="navigation">
<Navigation session={session} />
</div>
</> </>
) : null} ) : null}
<main> <main>
<div className="mx-auto mt-4 grid max-w-7xl grid-cols-12 gap-x-3 gap-y-5 px-3 py-3 sm:px-4 sm:py-4 md:mt-0 md:px-6 md:py-6 md:gap-y-6"> <div
data-theme-block="content_grid"
className="mx-auto mt-4 grid max-w-7xl grid-cols-12 gap-x-3 gap-y-5 px-3 py-3 sm:px-4 sm:py-4 md:mt-0 md:px-6 md:py-6 md:gap-y-6"
>
<div className="col-span-12 min-w-0"> <div className="col-span-12 min-w-0">
<MotionPageWrapper>{children}</MotionPageWrapper> <div data-theme-block="page_transition">
<MotionPageWrapper>{children}</MotionPageWrapper>
</div>
</div> </div>
</div> </div>
</main> </main>
<SiteFooter /> <div data-theme-block="footer">
<RadioPlayerGate /> <SiteFooter />
</div>
<div data-theme-block="radio_player">
<RadioPlayerGate />
</div>
</> </>
); );
} }
+14 -1
View File
@@ -1,6 +1,7 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { getThemeBuilderData } from "@/actions/theme-builder"; import { getThemeBuilderData } from "@/actions/theme-builder";
import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions";
import { getFullScopeValues } from "@/lib/theme-resolver";
import { ScopeForm } from "./scope-form"; import { ScopeForm } from "./scope-form";
import { ScopeTree } from "./scope-tree"; import { ScopeTree } from "./scope-tree";
import { ThemeEditor } from "./theme-editor"; import { ThemeEditor } from "./theme-editor";
@@ -22,6 +23,18 @@ export default async function ThemeBuilderPage({
const selectedId = sp.scope ? Number(sp.scope) : (scopes[0]?.id ?? null); const selectedId = sp.scope ? Number(sp.scope) : (scopes[0]?.id ?? null);
const values = selectedId
? await getFullScopeValues(selectedId)
: {
light: {},
dark: {},
blocks: {},
layout: {},
effects: {},
media: {},
custom: {},
};
return ( return (
<div> <div>
<h1 className="admin-section-title mb-4">Theme Builder</h1> <h1 className="admin-section-title mb-4">Theme Builder</h1>
@@ -66,7 +79,7 @@ export default async function ThemeBuilderPage({
<div className="lg:col-span-9"> <div className="lg:col-span-9">
{selectedId ? ( {selectedId ? (
<ThemeEditor scopeId={selectedId} scopes={scopes} /> <ThemeEditor scopeId={selectedId} scopes={scopes} values={values} />
) : ( ) : (
<div className="admin-card p-8 text-center text-[var(--admin-text-muted)]"> <div className="admin-card p-8 text-center text-[var(--admin-text-muted)]">
Select or create a scope to start editing. Select or create a scope to start editing.
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff