"use client"; import { Button } from "@prototype/components/ui/button"; import { Switch } from "@prototype/components/ui/switch"; import { PrototypeComponent, usePrototypeSlug, buildPrototypeTargetId, PROTOTYPE_TARGET_ATTR, } from "@prototype/components/prototypes/prototype-target"; import { PrototypeBriefTextarea } from "@prototype/components/prototypes/prototype-brief-field"; import { PrototypeDesignContextPanel } from "@prototype/components/prototypes/prototype-design-context-panel"; import { PrototypeMobbinGallery } from "@prototype/components/prototypes/prototype-mobbin-gallery"; import { buildMoreDesignExplorationVariantsCopyText, buildResetBriefDefaultsPrompt, buildSetDefaultVariantPrompt, getDesignExplorationDisplayOptions, getDesignExplorationVariantOptions, getMobbinReferencesForVariantLabel, resolveDesignExplorationBaselineOption, type DesignExplorationBaselineOption, type DesignExplorationBrief, type DesignExplorationContext, type DesignExplorationMobbin, type DesignExplorationVariantOption, type DesignExplorationVariantsSection, type MobbinReference, } from "@prototype/lib/prototypes/design-exploration-types"; import { usePersistedLocalBoolean, usePersistedLocalString, } from "@prototype/lib/prototypes/use-persisted-local-state"; import useCopyToClipboard from "@prototype/lib/use-copy-to-clipboard"; import { cn } from "@prototype/lib/utils"; import { useEffect, useState } from "react"; const MODAL_VARIANT_PREVIEW_SCALE = 0.75; const SIDEBAR_VARIANT_PREVIEW_SCALE = 0.42; const MODAL_OVERVIEW_TITLE_FIELD_CLASS = "tool-heading field-sizing-content w-full min-h-0 resize-none border-0 bg-transparent p-0 shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0"; const MODAL_OVERVIEW_DESCRIPTION_FIELD_CLASS = "tool-body field-sizing-content w-full min-h-0 resize-none border-0 bg-transparent p-0 shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0"; const MODAL_GALLERY_SECTION_TITLE_CLASS = "tool-heading"; const MODAL_GALLERY_CARD_TITLE_CLASS = "text-foreground font-medium"; const SIDEBAR_OVERVIEW_TITLE_FIELD_CLASS = "text-sm text-foreground field-sizing-content w-full min-h-0 resize-none border-0 bg-transparent p-0 font-medium leading-snug shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0"; const SIDEBAR_OVERVIEW_DESCRIPTION_FIELD_CLASS = "text-sm text-muted-foreground field-sizing-content w-full min-h-0 resize-none border-0 bg-transparent p-0 leading-relaxed shadow-none outline-none focus-visible:ring-0 focus-visible:ring-offset-0"; const SIDEBAR_GALLERY_SECTION_TITLE_CLASS = "tool-section-label"; const SIDEBAR_GALLERY_CARD_TITLE_CLASS = "text-sm text-foreground font-medium"; export type PrototypeDesignBriefPanelLayout = "default" | "sidebar"; export type PrototypeDesignBriefPanelProps = { componentIdPrefix: string; galleryId: string; storageKeyPrefix: string; brief: DesignExplorationBrief; context?: DesignExplorationContext; mobbin?: DesignExplorationMobbin; variantsSection?: DesignExplorationVariantsSection; variant: TVariant; onVariantChange: (variant: TVariant) => void; options: DesignExplorationVariantOption[]; renderers: Record React.ReactNode>; /** Pre-exploration UI — pinned at the bottom of the variant list. */ baseline: DesignExplorationBaselineOption; /** * Marked "Default" in the brief — the codebase-stored default variant * (`config.defaultVariant`). Non-default options (including Original) offer a "Make default" prompt. */ defaultVariantValue?: TVariant; /** Path to design exploration config — included in copy prompts. */ briefConfigFilePath?: string; /** Sidebar prompt copy — label from variant set or brief title. */ explorationLabel?: string; /** Sidebar prompt copy — variant set id when registered. */ explorationId?: string; layout?: PrototypeDesignBriefPanelLayout; className?: string; }; const SIDEBAR_OVERVIEW_COPY_BUTTON_CLASS = "cursor-pointer text-[var(--tool-chrome-icon)] hover:text-[var(--tool-chrome-icon-hover)] inline-flex size-7 shrink-0 items-center justify-center rounded-sm border-0 bg-transparent p-0 transition-colors duration-200 ease outline-none focus:outline-hidden focus-visible:ring-2 focus-visible:ring-focus-ring focus-visible:ring-offset-0 disabled:pointer-events-none disabled:opacity-40 motion-reduce:transition-none [&_svg]:pointer-events-none [&_svg]:shrink-0"; function VariantMobbinLinks({ componentIdPrefix, optionIdSuffix, references, }: { componentIdPrefix: string; optionIdSuffix: string; references: MobbinReference[]; }) { const slug = usePrototypeSlug(); if (references.length === 0) return null; const scrollToReference = (referenceId: string) => { if (!slug) return; const targetId = buildPrototypeTargetId( slug, `${componentIdPrefix}.mobbin-reference.${referenceId}`, ); const element = document.querySelector( `[${PROTOTYPE_TARGET_ATTR}="${targetId}"]`, ); if (element instanceof HTMLElement) { element.scrollIntoView({ behavior: "smooth", block: "start" }); } }; return ( <> · {references.map((reference, index) => ( {index > 0 ? ( , ) : null} ))} ); } function MakeDefaultButton({ componentId, optionLabel, optionValue, configFilePath, }: { componentId: string; optionLabel: string; optionValue: string; configFilePath?: string; }) { const { copy, isCopied } = useCopyToClipboard(); return ( ); } export function PrototypeDesignBriefPanel({ componentIdPrefix, galleryId, storageKeyPrefix, brief, context, mobbin, variantsSection, variant, onVariantChange, options, renderers, baseline, defaultVariantValue, briefConfigFilePath, explorationLabel, explorationId, layout = "default", className, }: PrototypeDesignBriefPanelProps) { const isSidebar = layout === "sidebar"; const slug = usePrototypeSlug(); const [variantPreviewsReady, setVariantPreviewsReady] = useState(false); useEffect(() => { setVariantPreviewsReady(true); }, []); const { copy: copyOverviewDefaultsPrompt, icon: copyOverviewDefaultsIcon } = useCopyToClipboard(); const { copy: copyMoreVariantsPrompt, icon: copyMoreVariantsIcon, isCopied: isMoreVariantsCopied, } = useCopyToClipboard(); const { value: title, updateValue: updateTitle } = usePersistedLocalString( `${storageKeyPrefix}-overview-title`, brief.titleDefault, ); const { value: description, updateValue: updateDescription } = usePersistedLocalString( `${storageKeyPrefix}-overview-description`, brief.descriptionDefault, ); const { value: contextExpanded, updateValue: updateContextExpanded, } = usePersistedLocalBoolean( `${storageKeyPrefix}-context-expanded`, context?.defaultExpanded ?? false, ); const { value: moreVariantsBrief, updateValue: updateMoreVariantsBrief } = usePersistedLocalString(`${storageKeyPrefix}-more-variants-brief`, ""); const { value: multipleVersions, updateValue: updateMultipleVersions, } = usePersistedLocalBoolean( `${storageKeyPrefix}-more-variants-multiple-versions`, true, ); const canCopyMoreVariants = moreVariantsBrief.trim().length > 0 && Boolean(slug); const variantsTitle = variantsSection?.title ?? "Design variants"; const effectiveTitle = title.trim() || brief.titleDefault; const effectiveDescription = description.trim() || brief.descriptionDefault; const overviewMatchesDefaults = effectiveTitle === brief.titleDefault && effectiveDescription === brief.descriptionDefault; const overviewTitleClass = isSidebar ? SIDEBAR_OVERVIEW_TITLE_FIELD_CLASS : MODAL_OVERVIEW_TITLE_FIELD_CLASS; const overviewDescriptionClass = isSidebar ? SIDEBAR_OVERVIEW_DESCRIPTION_FIELD_CLASS : MODAL_OVERVIEW_DESCRIPTION_FIELD_CLASS; const sectionTitleClass = isSidebar ? SIDEBAR_GALLERY_SECTION_TITLE_CLASS : MODAL_GALLERY_SECTION_TITLE_CLASS; const cardTitleClass = isSidebar ? SIDEBAR_GALLERY_CARD_TITLE_CLASS : MODAL_GALLERY_CARD_TITLE_CLASS; const variantPreviewScale = isSidebar ? SIDEBAR_VARIANT_PREVIEW_SCALE : MODAL_VARIANT_PREVIEW_SCALE; const overviewFields = (
updateTitle(event.target.value)} aria-label="Overview title" className={overviewTitleClass} />