diff --git a/.changeset/add-og-image-field.md b/.changeset/add-og-image-field.md new file mode 100644 index 0000000..003e2a9 --- /dev/null +++ b/.changeset/add-og-image-field.md @@ -0,0 +1,5 @@ +--- +"@emdash-cms/admin": patch +--- + +Add OG Image field to content editor diff --git a/packages/admin/src/components/ContentEditor.tsx b/packages/admin/src/components/ContentEditor.tsx index 42e5ab7..1122e0a 100644 --- a/packages/admin/src/components/ContentEditor.tsx +++ b/packages/admin/src/components/ContentEditor.tsx @@ -66,6 +66,7 @@ import { } from "./PortableTextEditor"; import { RevisionHistory } from "./RevisionHistory"; import { SaveButton } from "./SaveButton"; +import { SeoImageField } from "./SeoImageField"; import { SeoPanel } from "./SeoPanel"; import { TaxonomySidebar } from "./TaxonomySidebar"; @@ -613,25 +614,46 @@ export function ContentEditor({ )} >
{description}
} {required && !displayUrl && (This field is required
)} diff --git a/packages/admin/src/components/SeoImageField.tsx b/packages/admin/src/components/SeoImageField.tsx new file mode 100644 index 0000000..69f5609 --- /dev/null +++ b/packages/admin/src/components/SeoImageField.tsx @@ -0,0 +1,84 @@ +/** + * SEO OG Image field for the content editor. + * + * Renders an image picker (reusing MediaPickerModal) that stores the + * selected image URL in `seo.image`. Designed to sit next to the + * Featured Image field in a two-column grid. + */ + +import { Button, Label } from "@cloudflare/kumo"; +import { Image as ImageIcon, X } from "@phosphor-icons/react"; +import * as React from "react"; + +import type { ContentSeo, ContentSeoInput, MediaItem } from "../lib/api"; +import { MediaPickerModal } from "./MediaPickerModal"; + +export interface SeoImageFieldProps { + seo?: ContentSeo; + onChange: (seo: ContentSeoInput) => void; +} + +export function SeoImageField({ seo, onChange }: SeoImageFieldProps) { + const [pickerOpen, setPickerOpen] = React.useState(false); + const imageUrl = seo?.image || null; + + const handleSelect = (item: MediaItem) => { + const isLocalProvider = !item.provider || item.provider === "local"; + const url = isLocalProvider + ? `/_emdash/api/media/file/${item.storageKey || item.id}` + : item.url; + onChange({ image: url }); + }; + + const handleRemove = () => { + onChange({ image: null }); + }; + + return ( ++ Image shown when this page is shared on social media +
+