From 024fb32b115a23eba9afacd160a206d2fae51125 Mon Sep 17 00:00:00 2001 From: Daniel Roe Date: Mon, 25 May 2026 19:49:47 +0100 Subject: [PATCH] feat(cms): add support for multiple backgrounds --- cms/components/ImageGenerator.tsx | 7 +-- cms/components/VideoGenerator.tsx | 6 ++- cms/schemaTypes/ama.ts | 13 ++++++ cms/utils/backgrounds.ts | 71 +++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 5 deletions(-) create mode 100644 cms/utils/backgrounds.ts diff --git a/cms/components/ImageGenerator.tsx b/cms/components/ImageGenerator.tsx index 384ad90f..d8a06c6f 100644 --- a/cms/components/ImageGenerator.tsx +++ b/cms/components/ImageGenerator.tsx @@ -3,6 +3,7 @@ import { Stack, Text, Card, Button, Box, Flex } from '@sanity/ui' import { set, useFormValue, useClient } from 'sanity' import { domToBlob } from 'modern-screenshot' import { getHumanRelativeDate } from '../utils/date-formatting' +import { getBackgroundStyle } from '../utils/backgrounds' // Detect iOS devices const isIOS = () => { @@ -29,6 +30,8 @@ export function ImageGenerator (props: ImageGeneratorProps) { const relativeDate = getHumanRelativeDate(dateOfDocument) const documentContent = useFormValue(['content']) as string + const backgroundStyleId = useFormValue(['backgroundStyle']) as string | undefined + const backgroundStyle = getBackgroundStyle(backgroundStyleId) const client = useClient({ apiVersion: '2025-02-10' }) // Format text for display - now just clean whitespace @@ -346,12 +349,10 @@ export function ImageGenerator (props: ImageGeneratorProps) { }} >
diff --git a/cms/components/VideoGenerator.tsx b/cms/components/VideoGenerator.tsx index 7fe6f99b..2194f69d 100644 --- a/cms/components/VideoGenerator.tsx +++ b/cms/components/VideoGenerator.tsx @@ -7,6 +7,7 @@ import { calculateVideoDuration } from '../utils/video-animation-config' import type { GSAPTimeline } from '../utils/video-gsap-animations' import { createPreviewAnimation, stopAnimations } from '../utils/video-gsap-animations' import { getHumanRelativeDate } from '../utils/date-formatting' +import { getBackgroundStyle } from '../utils/backgrounds' import { fetchAudioTracks, selectAudioTrack, @@ -42,6 +43,8 @@ export function VideoContentGenerator (props: VideoGeneratorProps) { const documentContent = useFormValue(['content']) as string const posts = useFormValue(['posts']) as Array<{ content: PortableTextTextBlock[] }> const existingVideo = useFormValue(['video']) as { asset?: { _ref: string } } | undefined + const backgroundStyleId = useFormValue(['backgroundStyle']) as string | undefined + const backgroundStyle = getBackgroundStyle(backgroundStyleId) const client = useClient({ apiVersion: '2025-02-10' }) // Initialize video URL from existing asset @@ -635,8 +638,7 @@ export function VideoContentGenerator (props: VideoGeneratorProps) { style={{ position: 'absolute', inset: '-9em', - background: 'linear-gradient(yellow 5%, fuchsia, royalblue 95%)', - filter: 'url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMCI+IDxmaWx0ZXIgaWQ9Im15RmlsdGVyIj4gPGZlVHVyYnVsZW5jZSB0eXBlPSJmcmFjdGFsTm9pc2UiIGJhc2VGcmVxdWVuY3k9Ii4wMDUgLjAwMSIgbnVtT2N0YXZlcz0iMiIgLz4gPGZlRGlzcGxhY2VtZW50TWFwIHhDaGFubmVsU2VsZWN0b3I9IlIiIHNjYWxlPSI1MDAiIGluPSJTb3VyY2VHcmFwaGljIiByZXN1bHQ9ImJhbmRzIiAvPiA8ZmVUdXJidWxlbmNlIHR5cGU9ImZyYWN0YWxOb2lzZSIgYmFzZUZyZXF1ZW5jeT0iMy43MSIgLz4gPGZlRGlzcGxhY2VtZW50TWFwIGluPSJiYW5kcyIgc2NhbGU9IjMyIiB4Q2hhbm5lbFNlbGVjdG9yPSJSIiAvPiA8L2ZpbHRlcj4gPC9zdmc+#myFilter")', + ...backgroundStyle.style, }} /> diff --git a/cms/schemaTypes/ama.ts b/cms/schemaTypes/ama.ts index 1e7fe1f4..6c19651c 100644 --- a/cms/schemaTypes/ama.ts +++ b/cms/schemaTypes/ama.ts @@ -3,6 +3,7 @@ import { defineField, defineType } from 'sanity' import { CharacterCountInput } from '../components/CharacterCountInput' import { ImageGenerator } from '../components/ImageGenerator' import { VideoContentGenerator } from '../components/VideoGenerator' +import { BACKGROUND_STYLES, DEFAULT_BACKGROUND_STYLE_ID } from '../utils/backgrounds' export default defineType({ name: 'ama', @@ -177,6 +178,18 @@ export default defineType({ initialValue: 'draft', readOnly: ({ document }) => document?.publishStatus === 'ready' || document?.publishStatus === 'publishing' || document?.publishStatus === 'published', }), + defineField({ + name: 'backgroundStyle', + title: 'Background style', + type: 'string', + description: 'Background used behind the generated image and video.', + options: { + list: BACKGROUND_STYLES.map(({ id, title }) => ({ value: id, title })), + layout: 'dropdown', + }, + initialValue: DEFAULT_BACKGROUND_STYLE_ID, + readOnly: ({ document }) => document?.publishStatus === 'published', + }), defineField({ name: 'generatedImage', title: 'Generated image', diff --git a/cms/utils/backgrounds.ts b/cms/utils/backgrounds.ts new file mode 100644 index 00000000..9beaf342 --- /dev/null +++ b/cms/utils/backgrounds.ts @@ -0,0 +1,71 @@ +import type { CSSProperties } from 'react' + +export interface BackgroundStyle { + id: string + title: string + style: CSSProperties +} + +const NOISE_FILTER = 'url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMCI+IDxmaWx0ZXIgaWQ9Im15RmlsdGVyIj4gPGZlVHVyYnVsZW5jZSB0eXBlPSJmcmFjdGFsTm9pc2UiIGJhc2VGcmVxdWVuY3k9Ii4wMDUgLjAwMSIgbnVtT2N0YXZlcz0iMiIgLz4gPGZlRGlzcGxhY2VtZW50TWFwIHhDaGFubmVsU2VsZWN0b3I9IlIiIHNjYWxlPSI1MDAiIGluPSJTb3VyY2VHcmFwaGljIiByZXN1bHQ9ImJhbmRzIiAvPiA8ZmVUdXJidWxlbmNlIHR5cGU9ImZyYWN0YWxOb2lzZSIgYmFzZUZyZXF1ZW5jeT0iMy43MSIgLz4gPGZlRGlzcGxhY2VtZW50TWFwIGluPSJiYW5kcyIgc2NhbGU9IjMyIiB4Q2hhbm5lbFNlbGVjdG9yPSJSIiAvPiA8L2ZpbHRlcj4gPC9zdmc+#myFilter")' + +export const BACKGROUND_STYLES: readonly BackgroundStyle[] = [ + { + id: 'noise-gradient', + title: 'Noise gradient (default)', + style: { + background: 'linear-gradient(yellow 5%, fuchsia, royalblue 95%)', + filter: NOISE_FILTER, + }, + }, + { + id: 'aurora', + title: 'Aurora', + style: { + background: 'linear-gradient(135deg, #0f4c5c 0%, #14b8a6 40%, #84cc16 100%)', + }, + }, + { + id: 'sunset', + title: 'Sunset', + style: { + background: 'linear-gradient(135deg, #fbbf24 0%, #f97316 45%, #be185d 100%)', + }, + }, + { + id: 'mesh', + title: 'Mesh', + style: { + background: [ + 'radial-gradient(at 20% 20%, #6366f1 0px, transparent 50%)', + 'radial-gradient(at 80% 0%, #ec4899 0px, transparent 50%)', + 'radial-gradient(at 80% 80%, #14b8a6 0px, transparent 50%)', + 'radial-gradient(at 0% 100%, #f59e0b 0px, transparent 50%)', + '#0f172a', + ].join(', '), + }, + }, + { + id: 'solid-dark', + title: 'Solid dark', + style: { + background: '#0f172a', + }, + }, + { + id: 'solid-light', + title: 'Solid light', + style: { + background: '#f8fafc', + }, + }, +] as const + +export const DEFAULT_BACKGROUND_STYLE_ID = 'noise-gradient' + +export function getBackgroundStyle (id: string | undefined): BackgroundStyle { + return ( + BACKGROUND_STYLES.find(s => s.id === id) + ?? BACKGROUND_STYLES.find(s => s.id === DEFAULT_BACKGROUND_STYLE_ID) + ?? BACKGROUND_STYLES[0]! + ) +} -- 2.51.2