export declare const frameTemplate = "\"use client\";\nimport React from \"react\";\nimport Link from \"next/link\";\nimport styled from \"styled-components\";\nimport { styledSmall } from \"cherry-styled-components\";\nimport { Theme } from \"@/app/theme\";\n\n// Captions and hints accept a deliberately small slice of Markdown: links,\n// bold, italic and inline code. Everything is parsed into React elements -\n// never dangerouslySetInnerHTML - so author text can't inject raw markup.\nconst INLINE_MD =\n /\\[([^\\]]+)\\]\\(([^)\\s]+)\\)|\\*\\*([^*]+)\\*\\*|__([^_]+)__|`([^`]+)`|\\*([^*]+)\\*|_([^_]+)_/g;\n\n// Link targets are limited to schemes that can't execute script. Anything\n// else (javascript:, data:, vbscript:) falls back to literal text.\nconst SAFE_HREF = /^(?:https?:\\/\\/|mailto:|[/#]|\\.{1,2}\\/)/i;\n\n// Emphasis and link labels are parsed recursively, so the matcher must not\n// carry state between calls - matchAll() iterates over its own clone of the\n// pattern, leaving INLINE_MD.lastIndex untouched for the caller mid-loop.\nfunction renderInline(text: string, keyPrefix = \"\"): React.ReactNode[] {\n const nodes: React.ReactNode[] = [];\n let lastIndex = 0;\n\n for (const match of text.matchAll(INLINE_MD)) {\n const start = match.index ?? 0;\n if (start > lastIndex) {\n nodes.push(text.slice(lastIndex, start));\n }\n\n // RegExpExecArray types every group as string, but unmatched alternatives\n // are undefined at runtime - widen so the branch checks below are honest.\n const full = match[0];\n const groups: Array = match;\n const [, linkText, href, strong, strongAlt, code, em, emAlt] = groups;\n const key = `${keyPrefix}${start}`;\n\n if (href !== undefined) {\n const label = renderInline(linkText as string, `${key}-`);\n if (!SAFE_HREF.test(href)) {\n nodes.push(full);\n } else if (href.startsWith(\"/\")) {\n nodes.push(\n \n {label}\n ,\n );\n } else if (href.startsWith(\"#\")) {\n nodes.push(\n \n {label}\n ,\n );\n } else {\n nodes.push(\n \n {label}\n ,\n );\n }\n } else if (strong !== undefined || strongAlt !== undefined) {\n nodes.push(\n \n {renderInline((strong ?? strongAlt) as string, `${key}-`)}\n ,\n );\n } else if (code !== undefined) {\n nodes.push({code});\n } else {\n nodes.push(\n {renderInline((em ?? emAlt) as string, `${key}-`)},\n );\n }\n\n lastIndex = start + full.length;\n }\n\n if (lastIndex < text.length) {\n nodes.push(text.slice(lastIndex));\n }\n\n return nodes;\n}\n\n// A self-playing video only works across browsers when it is also muted and\n// inline, so the frame fills those in. Explicit author values always win, and\n// videos without autoPlay are left untouched. Recursion is capped and limited\n// to host elements so a markdown-wrapped

is still traversed while author\n// components are never re-cloned.\nfunction withVideoDefaults(\n children: React.ReactNode,\n depth = 0,\n): React.ReactNode {\n if (depth > 2) return children;\n\n return React.Children.map(children, (child) => {\n if (!React.isValidElement(child)) return child;\n\n if (child.type === \"video\") {\n const videoProps =\n child.props as React.VideoHTMLAttributes;\n if (!videoProps.autoPlay) return child;\n return React.cloneElement(\n child as React.ReactElement<\n React.VideoHTMLAttributes\n >,\n {\n playsInline: videoProps.playsInline ?? true,\n loop: videoProps.loop ?? true,\n muted: videoProps.muted ?? true,\n },\n );\n }\n\n if (typeof child.type !== \"string\") return child;\n\n const hostProps = child.props as { children?: React.ReactNode };\n if (hostProps.children == null) return child;\n\n return React.cloneElement(\n child as React.ReactElement<{ children?: React.ReactNode }>,\n { children: withVideoDefaults(hostProps.children, depth + 1) },\n );\n });\n}\n\nconst StyledFrameWrapper = styled.div`\n display: flex;\n flex-direction: column;\n gap: 10px;\n max-width: 100%;\n`;\n\nconst StyledFrameHint = styled.p<{ theme: Theme }>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme }) => theme.colors.grayDark};\n margin: 0;\n`;\n\nconst StyledFrame = styled.figure<{ theme: Theme }>`\n display: flex;\n flex-direction: column;\n gap: 8px;\n margin: 0;\n padding: 8px;\n border: solid 1px ${({ theme }) => theme.colors.grayLight};\n border-radius: ${({ theme }) => theme.spacing.radius.lg};\n background: ${({ theme }) =>\n `color-mix(in srgb, ${theme.colors.grayLight} 25%, transparent)`};\n`;\n\nconst StyledFrameContent = styled.div<{ theme: Theme }>`\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 15px;\n\n & > * {\n max-width: 100%;\n }\n\n & p {\n margin: 0;\n }\n\n & img,\n & video,\n & iframe {\n display: block;\n max-width: 100%;\n height: auto;\n border-radius: ${({ theme }) => theme.spacing.radius.xs};\n }\n`;\n\nconst StyledFrameCaption = styled.figcaption<{ theme: Theme }>`\n ${({ theme }) => styledSmall(theme)};\n color: ${({ theme }) => theme.colors.gray};\n text-align: center;\n margin: 0;\n`;\n\ninterface FrameProps extends React.HTMLAttributes {\n children: React.ReactNode;\n caption?: string;\n hint?: string;\n}\n\nfunction Frame({ children, caption, hint, ...props }: FrameProps) {\n return (\n \n {hint && {renderInline(hint)}}\n \n {withVideoDefaults(children)}\n {caption && (\n {renderInline(caption)}\n )}\n \n \n );\n}\n\nexport { Frame };\n";