@emdash-cms/admin 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2620 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25915 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins-XhZqfegd.js +109 -0
- package/dist/plugins-XhZqfegd.js.map +1 -0
- package/dist/styles.css +2 -0
- package/package.json +96 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["formatRelativeTime","PAGE_SIZE","useDebouncedValue","Image","Node","mergeAttributes","Node","useDebouncedValue","SectionCard","Suggestion","ImageIcon","LinkIcon","formatRelativeTime","fetchTaxonomyDefs","fetchTerms","ROLE_EDITOR","Link","ImageIcon","getItemTitle","Link","ImageIcon","Link","Link","Link","Link","Image","formatRelativeTime","isSafeUrl","BASE64URL_DASH_REGEX","BASE64URL_UNDERSCORE_REGEX","BASE64_PLUS_REGEX","BASE64_SLASH_REGEX","isWebAuthnSupported","base64urlToBuffer","bufferToBase64url","Link","isSortOption","SORT_LABELS","PluginCard","useNavigate","Link","Link","Link","isSafeUrl","Image","useDebouncedValue","useParams","useNavigate","FileIcon","LinkIcon","useNavigate","ListIcon","Link","useParams","useNavigate","Link","useNavigate","Link","ROLES","Link","Link","Link","Link","Link","Link","Link","StepIndicator","ROLE_ADMIN","ROLE_EDITOR","Image","useNavigate","ROLE_ADMIN","ROLE_EDITOR","Link","Image","Sidebar","Link","getRoleLabel","Sidebar","Link","Link","Link","Image"],"sources":["../src/components/ThemeProvider.tsx","../src/lib/plugin-context.tsx","../src/lib/utils.ts","../src/components/DialogError.tsx","../src/components/ConfirmDialog.tsx","../src/components/comments/CommentDetail.tsx","../src/components/comments/CommentInbox.tsx","../src/lib/api/content.ts","../src/lib/api/media.ts","../src/lib/api/schema.ts","../src/lib/api/settings.ts","../src/lib/api/users.ts","../src/lib/api/bylines.ts","../src/lib/api/menus.ts","../src/lib/api/widgets.ts","../src/lib/api/sections.ts","../src/lib/api/taxonomies.ts","../src/lib/api/import.ts","../src/lib/api/api-tokens.ts","../src/lib/api/comments.ts","../src/lib/api/dashboard.ts","../src/lib/api/search.ts","../src/lib/api/marketplace.ts","../src/lib/api/email-settings.ts","../src/lib/api/theme-marketplace.ts","../src/lib/api/redirects.ts","../src/lib/api/current-user.ts","../src/components/BlockKitFieldWidget.tsx","../src/components/editor/DocumentOutline.tsx","../src/components/PluginFieldErrorBoundary.tsx","../src/lib/hooks.ts","../src/lib/media-utils.ts","../src/components/MediaPickerModal.tsx","../src/components/editor/ImageDetailPanel.tsx","../../../node_modules/.pnpm/@tiptap+suggestion@3.20.0_@tiptap+core@3.20.0_@tiptap+pm@3.20.0__@tiptap+pm@3.20.0/node_modules/@tiptap/suggestion/dist/index.js","../src/components/editor/BlockMenu.tsx","../src/components/editor/DragHandleWrapper.tsx","../src/components/editor/ImageNode.tsx","../src/components/editor/MarkdownLinkExtension.tsx","../src/components/editor/PluginBlockNode.tsx","../src/components/SectionPickerModal.tsx","../src/components/PortableTextEditor.tsx","../src/components/RevisionHistory.tsx","../src/components/SaveButton.tsx","../src/components/SeoPanel.tsx","../src/components/TaxonomySidebar.tsx","../src/components/ContentEditor.tsx","../src/components/LocaleSwitcher.tsx","../src/components/ContentList.tsx","../src/components/FieldEditor.tsx","../src/components/ContentTypeEditor.tsx","../src/components/ContentTypeList.tsx","../src/components/SandboxedPluginWidget.tsx","../src/components/Dashboard.tsx","../src/components/DeviceAuthorizePage.tsx","../src/lib/url.ts","../src/components/auth/PasskeyLogin.tsx","../src/components/LoginPage.tsx","../src/components/MarketplaceBrowse.tsx","../src/components/CapabilityConsentDialog.tsx","../src/components/PluginManager.tsx","../src/components/MarketplacePluginDetail.tsx","../src/components/MediaDetailPanel.tsx","../src/components/MediaLibrary.tsx","../src/components/ContentPickerModal.tsx","../src/components/MenuEditor.tsx","../src/components/MenuList.tsx","../src/components/Redirects.tsx","../src/components/SandboxedPluginPage.tsx","../src/components/SectionEditor.tsx","../src/components/Sections.tsx","../src/components/Settings.tsx","../src/components/settings/AllowedDomainsSettings.tsx","../src/components/settings/ApiTokenSettings.tsx","../src/components/settings/EmailSettings.tsx","../src/components/settings/GeneralSettings.tsx","../src/components/auth/PasskeyRegistration.tsx","../src/components/settings/PasskeyItem.tsx","../src/components/settings/PasskeyList.tsx","../src/components/settings/SecuritySettings.tsx","../src/components/settings/SeoSettings.tsx","../src/components/settings/SocialSettings.tsx","../src/components/SetupWizard.tsx","../src/components/AdminCommandPalette.tsx","../src/components/Sidebar.tsx","../src/components/ThemeToggle.tsx","../src/components/Header.tsx","../src/components/WelcomeModal.tsx","../src/components/Shell.tsx","../src/components/SignupPage.tsx","../src/components/TaxonomyManager.tsx","../src/components/ThemeMarketplaceBrowse.tsx","../src/components/ThemeMarketplaceDetail.tsx","../src/components/Widgets.tsx","../src/components/WordPressImport.tsx","../src/routes/bylines.tsx","../src/components/users/RoleBadge.tsx","../src/components/users/UserList.tsx","../src/components/users/UserDetail.tsx","../src/components/users/InviteUserModal.tsx","../src/routes/users.tsx","../src/router.tsx","../src/App.tsx"],"sourcesContent":["import * as React from \"react\";\n\ntype Theme = \"light\" | \"dark\" | \"system\";\n\ninterface ThemeContextValue {\n\ttheme: Theme;\n\tsetTheme: (theme: Theme) => void;\n\t/** The resolved theme (always \"light\" or \"dark\") */\n\tresolvedTheme: \"light\" | \"dark\";\n}\n\nconst ThemeContext = React.createContext<ThemeContextValue | undefined>(undefined);\n\nconst STORAGE_KEY = \"emdash-theme\";\n\nfunction getSystemTheme(): \"light\" | \"dark\" {\n\tif (typeof window === \"undefined\") return \"light\";\n\treturn window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\nfunction getStoredTheme(): Theme {\n\tif (typeof window === \"undefined\") return \"system\";\n\tconst stored = localStorage.getItem(STORAGE_KEY);\n\tif (stored === \"light\" || stored === \"dark\" || stored === \"system\") {\n\t\treturn stored;\n\t}\n\treturn \"system\";\n}\n\nexport interface ThemeProviderProps {\n\tchildren: React.ReactNode;\n\t/** Default theme if none stored. Defaults to \"system\" */\n\tdefaultTheme?: Theme;\n}\n\nexport function ThemeProvider({ children, defaultTheme = \"system\" }: ThemeProviderProps) {\n\tconst [theme, setThemeState] = React.useState<Theme>(() => {\n\t\tconst stored = getStoredTheme();\n\t\treturn stored === \"system\" ? defaultTheme : stored;\n\t});\n\n\tconst [resolvedTheme, setResolvedTheme] = React.useState<\"light\" | \"dark\">(() => {\n\t\tif (theme === \"system\") return getSystemTheme();\n\t\treturn theme;\n\t});\n\n\t// Update DOM and resolved theme when theme changes.\n\t// Uses data-mode (not data-theme) for dark mode — kumo's convention.\n\t// data-theme is reserved for visual identity overrides.\n\t// Update DOM and resolved theme when theme changes.\n\t// Uses data-mode (not data-theme) for dark mode — kumo's convention.\n\t// data-theme is reserved for visual identity overrides (e.g. \"classic\").\n\tReact.useEffect(() => {\n\t\tconst root = document.documentElement;\n\n\t\t// Apply classic visual identity at the root level\n\t\t// so token overrides cascade to all kumo components including portals\n\t\troot.setAttribute(\"data-theme\", \"classic\");\n\n\t\tif (theme === \"system\") {\n\t\t\troot.removeAttribute(\"data-mode\");\n\t\t\tsetResolvedTheme(getSystemTheme());\n\t\t} else {\n\t\t\troot.setAttribute(\"data-mode\", theme);\n\t\t\tsetResolvedTheme(theme);\n\t\t}\n\t}, [theme]);\n\n\t// Listen for system theme changes when in system mode\n\tReact.useEffect(() => {\n\t\tif (theme !== \"system\") return;\n\n\t\tconst mediaQuery = window.matchMedia(\"(prefers-color-scheme: dark)\");\n\t\tconst handler = (e: MediaQueryListEvent) => {\n\t\t\tsetResolvedTheme(e.matches ? \"dark\" : \"light\");\n\t\t};\n\n\t\tmediaQuery.addEventListener(\"change\", handler);\n\t\treturn () => mediaQuery.removeEventListener(\"change\", handler);\n\t}, [theme]);\n\n\tconst setTheme = React.useCallback((newTheme: Theme) => {\n\t\tsetThemeState(newTheme);\n\t\tlocalStorage.setItem(STORAGE_KEY, newTheme);\n\t}, []);\n\n\tconst value = React.useMemo(\n\t\t() => ({ theme, setTheme, resolvedTheme }),\n\t\t[theme, setTheme, resolvedTheme],\n\t);\n\n\treturn <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\nexport function useTheme() {\n\tconst context = React.useContext(ThemeContext);\n\tif (context === undefined) {\n\t\tthrow new Error(\"useTheme must be used within a ThemeProvider\");\n\t}\n\treturn context;\n}\n","/**\n * Plugin Admin Context\n *\n * Provides plugin admin modules (widgets, pages, fields) to the admin UI\n * via React context. This avoids cross-module registry issues by keeping\n * everything in React's component tree.\n */\n\nimport * as React from \"react\";\nimport { createContext, useContext } from \"react\";\n\n/** Shape of a plugin's admin exports */\nexport interface PluginAdminModule {\n\twidgets?: Record<string, React.ComponentType>;\n\tpages?: Record<string, React.ComponentType>;\n\tfields?: Record<string, React.ComponentType>;\n}\n\n/** All plugin admin modules keyed by plugin ID */\nexport type PluginAdmins = Record<string, PluginAdminModule>;\n\nconst PluginAdminContext = createContext<PluginAdmins>({});\n\nexport interface PluginAdminProviderProps {\n\tchildren: React.ReactNode;\n\tpluginAdmins: PluginAdmins;\n}\n\n/**\n * Provider that makes plugin admin modules available to all descendants\n */\nexport function PluginAdminProvider({ children, pluginAdmins }: PluginAdminProviderProps) {\n\treturn <PluginAdminContext.Provider value={pluginAdmins}>{children}</PluginAdminContext.Provider>;\n}\n\n/**\n * Get all plugin admin modules\n */\nexport function usePluginAdmins(): PluginAdmins {\n\treturn useContext(PluginAdminContext);\n}\n\n/**\n * Get a dashboard widget component by plugin ID and widget ID\n */\nexport function usePluginWidget(pluginId: string, widgetId: string): React.ComponentType | null {\n\tconst admins = useContext(PluginAdminContext);\n\treturn admins[pluginId]?.widgets?.[widgetId] ?? null;\n}\n\n/**\n * Get a plugin page component by plugin ID and path\n */\nexport function usePluginPage(pluginId: string, path: string): React.ComponentType | null {\n\tconst admins = useContext(PluginAdminContext);\n\treturn admins[pluginId]?.pages?.[path] ?? null;\n}\n\n/**\n * Get a field widget component by plugin ID and field type\n */\nexport function usePluginField(pluginId: string, fieldType: string): React.ComponentType | null {\n\tconst admins = useContext(PluginAdminContext);\n\treturn admins[pluginId]?.fields?.[fieldType] ?? null;\n}\n\n/**\n * Check if a plugin has any registered admin pages\n */\nexport function usePluginHasPages(pluginId: string): boolean {\n\tconst admins = useContext(PluginAdminContext);\n\tconst pages = admins[pluginId]?.pages;\n\treturn pages !== undefined && Object.keys(pages).length > 0;\n}\n\n/**\n * Check if a plugin has any registered dashboard widgets\n */\nexport function usePluginHasWidgets(pluginId: string): boolean {\n\tconst admins = useContext(PluginAdminContext);\n\tconst widgets = admins[pluginId]?.widgets;\n\treturn widgets !== undefined && Object.keys(widgets).length > 0;\n}\n","import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n// Regex patterns for slugify\nconst DIACRITICS_PATTERN = /[\\u0300-\\u036f]/g;\nconst WHITESPACE_UNDERSCORE_PATTERN = /[\\s_]+/g;\nconst NON_ALPHANUMERIC_HYPHEN_PATTERN = /[^a-z0-9-]/g;\nconst MULTIPLE_HYPHENS_PATTERN = /-+/g;\nconst LEADING_TRAILING_HYPHEN_PATTERN = /^-|-$/g;\n\n/**\n * Merge class names with Tailwind CSS support\n */\nexport function cn(...inputs: ClassValue[]) {\n\treturn twMerge(clsx(inputs));\n}\n\n/**\n * Convert a string to a URL-friendly slug.\n *\n * Handles unicode by normalizing to NFD and stripping diacritics.\n */\nexport function formatRelativeTime(dateString: string): string {\n\tconst date = new Date(dateString);\n\tconst now = new Date();\n\tconst diffMs = now.getTime() - date.getTime();\n\tconst diffSecs = Math.floor(diffMs / 1000);\n\tconst diffMins = Math.floor(diffSecs / 60);\n\tconst diffHours = Math.floor(diffMins / 60);\n\tconst diffDays = Math.floor(diffHours / 24);\n\n\tif (diffSecs < 60) return \"just now\";\n\tif (diffMins < 60) return `${diffMins} min${diffMins === 1 ? \"\" : \"s\"} ago`;\n\tif (diffHours < 24) return `${diffHours} hour${diffHours === 1 ? \"\" : \"s\"} ago`;\n\tif (diffDays < 7) return `${diffDays} day${diffDays === 1 ? \"\" : \"s\"} ago`;\n\n\treturn date.toLocaleDateString(undefined, {\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t\tyear: date.getFullYear() !== now.getFullYear() ? \"numeric\" : undefined,\n\t});\n}\n\nexport function slugify(text: string): string {\n\treturn text\n\t\t.toLowerCase()\n\t\t.normalize(\"NFD\")\n\t\t.replace(DIACRITICS_PATTERN, \"\")\n\t\t.replace(WHITESPACE_UNDERSCORE_PATTERN, \"-\")\n\t\t.replace(NON_ALPHANUMERIC_HYPHEN_PATTERN, \"\")\n\t\t.replace(MULTIPLE_HYPHENS_PATTERN, \"-\")\n\t\t.replace(LEADING_TRAILING_HYPHEN_PATTERN, \"\");\n}\n","/**\n * Shared error display for dialogs and mutation error extraction.\n */\n\nimport { cn } from \"../lib/utils.js\";\n\n/** Extract a user-facing message from a mutation error value. */\nexport function getMutationError(error: unknown): string | null {\n\tif (!error) return null;\n\tif (error instanceof Error) return error.message;\n\treturn \"An error occurred\";\n}\n\n/** Inline error banner for use inside dialogs. */\nexport function DialogError({\n\tmessage,\n\tclassName,\n}: {\n\tmessage?: string | null;\n\tclassName?: string;\n}) {\n\tif (!message) return null;\n\treturn (\n\t\t<div className={cn(\"rounded-md bg-kumo-danger/10 p-3 text-sm text-kumo-danger\", className)}>\n\t\t\t{message}\n\t\t</div>\n\t);\n}\n","/**\n * Reusable confirmation dialog with inline error display.\n *\n * Handles the common pattern: title, description, optional error banner,\n * cancel/confirm buttons with pending state. Dialog stays open on error.\n */\n\nimport { Button, Dialog } from \"@cloudflare/kumo\";\nimport * as React from \"react\";\n\nimport { DialogError, getMutationError } from \"./DialogError.js\";\n\nexport interface ConfirmDialogProps {\n\topen: boolean;\n\tonClose: () => void;\n\ttitle: string;\n\t/** Static description or dynamic JSX content */\n\tdescription: React.ReactNode;\n\t/** Label for the confirm button (e.g. \"Delete\", \"Disable User\") */\n\tconfirmLabel: string;\n\t/** Label shown while the action is pending (e.g. \"Deleting...\") */\n\tpendingLabel: string;\n\t/** Button variant — defaults to \"destructive\" */\n\tvariant?: \"destructive\" | \"primary\";\n\tisPending: boolean;\n\t/** Error from a mutation — pass mutation.error directly */\n\terror: unknown;\n\tonConfirm: () => void;\n\t/** Extra content rendered between description and buttons (e.g. a checkbox) */\n\tchildren?: React.ReactNode;\n}\n\nexport function ConfirmDialog({\n\topen,\n\tonClose,\n\ttitle,\n\tdescription,\n\tconfirmLabel,\n\tpendingLabel,\n\tvariant = \"destructive\",\n\tisPending,\n\terror,\n\tonConfirm,\n\tchildren,\n}: ConfirmDialogProps) {\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={(o) => !o && onClose()} disablePointerDismissal>\n\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">{title}</Dialog.Title>\n\t\t\t\t<Dialog.Description className=\"text-kumo-subtle\">{description}</Dialog.Description>\n\t\t\t\t{children}\n\t\t\t\t<DialogError message={getMutationError(error)} className=\"mt-3\" />\n\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t<Button variant=\"secondary\" onClick={onClose}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button variant={variant} disabled={isPending} onClick={onConfirm}>\n\t\t\t\t\t\t{isPending ? pendingLabel : confirmLabel}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n","/**\n * Comment detail slide-over panel.\n *\n * Shows full comment body, author details, moderation metadata,\n * and status change buttons.\n */\n\nimport { Badge, Button } from \"@cloudflare/kumo\";\nimport { X, Check, Trash, Warning, UserCircle, EnvelopeSimple } from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport type { AdminComment, CommentStatus } from \"../../lib/api/comments.js\";\nimport { cn } from \"../../lib/utils.js\";\n\nexport interface CommentDetailProps {\n\tcomment: AdminComment;\n\tonClose: () => void;\n\tonStatusChange: (id: string, status: CommentStatus) => void;\n\tonDelete: (id: string) => void;\n\tisAdmin: boolean;\n\tisStatusPending: boolean;\n}\n\nexport function CommentDetail({\n\tcomment,\n\tonClose,\n\tonStatusChange,\n\tonDelete,\n\tisAdmin,\n\tisStatusPending,\n}: CommentDetailProps) {\n\t// Close on Escape\n\tReact.useEffect(() => {\n\t\tconst handler = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\" && !e.defaultPrevented) {\n\t\t\t\te.preventDefault();\n\t\t\t\tonClose();\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"keydown\", handler);\n\t\treturn () => document.removeEventListener(\"keydown\", handler);\n\t}, [onClose]);\n\n\tconst date = new Date(comment.createdAt);\n\n\treturn (\n\t\t<>\n\t\t\t{/* Backdrop */}\n\t\t\t<div className=\"fixed inset-0 z-40 bg-black/30\" onClick={onClose} aria-hidden=\"true\" />\n\n\t\t\t{/* Panel */}\n\t\t\t<div className=\"fixed inset-y-0 right-0 z-50 w-full max-w-lg overflow-y-auto bg-kumo-base border-l shadow-lg\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"flex items-center justify-between border-b px-6 py-4\">\n\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Comment Detail</h2>\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" onClick={onClose} aria-label=\"Close\">\n\t\t\t\t\t\t<X className=\"h-5 w-5\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Content */}\n\t\t\t\t<div className=\"space-y-6 p-6\">\n\t\t\t\t\t{/* Status */}\n\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t<CommentStatusBadge status={comment.status} />\n\t\t\t\t\t\t<span className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{date.toLocaleDateString()} {date.toLocaleTimeString()}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Author info */}\n\t\t\t\t\t<div className=\"rounded-lg border p-4 space-y-3\">\n\t\t\t\t\t\t<h3 className=\"text-sm font-semibold text-kumo-subtle uppercase tracking-wider\">\n\t\t\t\t\t\t\tAuthor\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<UserCircle className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">{comment.authorName}</span>\n\t\t\t\t\t\t\t\t{comment.authorUserId && <Badge variant=\"secondary\">Registered user</Badge>}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<EnvelopeSimple className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t\t<span className=\"text-sm text-kumo-subtle\">{comment.authorEmail}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Comment body */}\n\t\t\t\t\t<div className=\"rounded-lg border p-4 space-y-3\">\n\t\t\t\t\t\t<h3 className=\"text-sm font-semibold text-kumo-subtle uppercase tracking-wider\">\n\t\t\t\t\t\t\tComment\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<p className=\"text-sm whitespace-pre-wrap break-words\">{comment.body}</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Content reference */}\n\t\t\t\t\t<div className=\"rounded-lg border p-4 space-y-2\">\n\t\t\t\t\t\t<h3 className=\"text-sm font-semibold text-kumo-subtle uppercase tracking-wider\">\n\t\t\t\t\t\t\tContent\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<p className=\"text-sm\">\n\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Collection:</span>{\" \"}\n\t\t\t\t\t\t\t<span className=\"font-medium\">{comment.collection}</span>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p className=\"text-sm\">\n\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Content ID:</span>{\" \"}\n\t\t\t\t\t\t\t<code className=\"bg-kumo-tint px-1.5 py-0.5 rounded text-xs\">\n\t\t\t\t\t\t\t\t{comment.contentId}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{comment.parentId && (\n\t\t\t\t\t\t\t<p className=\"text-sm\">\n\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Reply to:</span>{\" \"}\n\t\t\t\t\t\t\t\t<code className=\"bg-kumo-tint px-1.5 py-0.5 rounded text-xs\">\n\t\t\t\t\t\t\t\t\t{comment.parentId}\n\t\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Moderation metadata */}\n\t\t\t\t\t{comment.moderationMetadata && Object.keys(comment.moderationMetadata).length > 0 && (\n\t\t\t\t\t\t<div className=\"rounded-lg border p-4 space-y-3\">\n\t\t\t\t\t\t\t<h3 className=\"text-sm font-semibold text-kumo-subtle uppercase tracking-wider\">\n\t\t\t\t\t\t\t\tModeration Signals\n\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t<pre className=\"text-xs bg-kumo-tint rounded p-3 overflow-x-auto\">\n\t\t\t\t\t\t\t\t{JSON.stringify(comment.moderationMetadata, null, 2)}\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Footer actions */}\n\t\t\t\t<div className=\"border-t px-6 py-4 space-y-3\">\n\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t{comment.status !== \"approved\" && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ticon={<Check />}\n\t\t\t\t\t\t\t\tonClick={() => onStatusChange(comment.id, \"approved\")}\n\t\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tApprove\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{comment.status !== \"spam\" && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\ticon={<Warning />}\n\t\t\t\t\t\t\t\tonClick={() => onStatusChange(comment.id, \"spam\")}\n\t\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tSpam\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{comment.status !== \"trash\" && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\ticon={<Trash />}\n\t\t\t\t\t\t\t\tonClick={() => onStatusChange(comment.id, \"trash\")}\n\t\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tTrash\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t{isAdmin && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\ticon={<Trash />}\n\t\t\t\t\t\t\tonClick={() => onDelete(comment.id)}\n\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tDelete Permanently\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</>\n\t);\n}\n\nexport function CommentStatusBadge({ status }: { status: CommentStatus }) {\n\treturn (\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"inline-flex items-center rounded-full px-2.5 py-1 text-xs font-medium\",\n\t\t\t\tstatus === \"approved\" &&\n\t\t\t\t\t\"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200\",\n\t\t\t\tstatus === \"pending\" &&\n\t\t\t\t\t\"bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200\",\n\t\t\t\tstatus === \"spam\" && \"bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200\",\n\t\t\t\tstatus === \"trash\" && \"bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200\",\n\t\t\t)}\n\t\t>\n\t\t\t{status}\n\t\t</span>\n\t);\n}\n","/**\n * Comment moderation inbox.\n *\n * Status tabs (Pending, Approved, Spam, Trash), search, collection filter,\n * table with row actions, bulk selection, and detail slide-over.\n */\n\nimport { Badge, Button, Checkbox, Input, Select, Tabs } from \"@cloudflare/kumo\";\nimport {\n\tMagnifyingGlass,\n\tCheck,\n\tTrash,\n\tWarning,\n\tCaretLeft,\n\tCaretRight,\n\tChatCircle,\n} from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport type {\n\tAdminComment,\n\tCommentCounts,\n\tCommentStatus,\n\tBulkAction,\n} from \"../../lib/api/comments.js\";\nimport { cn } from \"../../lib/utils.js\";\nimport { ConfirmDialog } from \"../ConfirmDialog.js\";\nimport { CommentDetail } from \"./CommentDetail.js\";\n\n// ---------------------------------------------------------------------------\n// Props\n// ---------------------------------------------------------------------------\n\nexport interface CommentInboxProps {\n\tcomments: AdminComment[];\n\tcounts: CommentCounts;\n\tisLoading: boolean;\n\tnextCursor?: string;\n\tcollections: Record<string, { label: string }>;\n\tactiveStatus: CommentStatus;\n\tonStatusChange: (status: CommentStatus) => void;\n\tcollectionFilter: string;\n\tonCollectionFilterChange: (collection: string) => void;\n\tsearchQuery: string;\n\tonSearchChange: (query: string) => void;\n\tonCommentStatusChange: (id: string, status: CommentStatus) => Promise<unknown>;\n\tonCommentDelete: (id: string) => Promise<unknown>;\n\tonBulkAction: (ids: string[], action: BulkAction) => Promise<unknown>;\n\tonLoadMore: () => void;\n\tisAdmin: boolean;\n\tisStatusPending: boolean;\n\tdeleteError: unknown;\n\tonDeleteErrorReset: () => void;\n}\n\n// ---------------------------------------------------------------------------\n// Component\n// ---------------------------------------------------------------------------\n\nconst PAGE_SIZE = 20;\n\nexport function CommentInbox({\n\tcomments,\n\tcounts,\n\tisLoading,\n\tnextCursor,\n\tcollections,\n\tactiveStatus,\n\tonStatusChange,\n\tcollectionFilter,\n\tonCollectionFilterChange,\n\tsearchQuery,\n\tonSearchChange,\n\tonCommentStatusChange,\n\tonCommentDelete,\n\tonBulkAction,\n\tonLoadMore,\n\tisAdmin,\n\tisStatusPending,\n\tdeleteError,\n\tonDeleteErrorReset,\n}: CommentInboxProps) {\n\t// Selection state\n\tconst [selected, setSelected] = React.useState<Set<string>>(new Set());\n\tconst [detailComment, setDetailComment] = React.useState<AdminComment | null>(null);\n\tconst [deleteId, setDeleteId] = React.useState<string | null>(null);\n\n\t// Pagination (client-side within loaded data)\n\tconst [page, setPage] = React.useState(0);\n\n\t// Reset selection and page when status tab or filters change\n\tReact.useEffect(() => {\n\t\tsetSelected(new Set());\n\t\tsetPage(0);\n\t}, [activeStatus, collectionFilter, searchQuery]);\n\n\tconst clearSelection = React.useCallback(() => setSelected(new Set()), []);\n\n\tconst totalPages = Math.max(1, Math.ceil(comments.length / PAGE_SIZE));\n\tconst paginatedComments = comments.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n\n\t// Bulk select\n\tconst allOnPageSelected =\n\t\tpaginatedComments.length > 0 && paginatedComments.every((c) => selected.has(c.id));\n\n\tconst toggleAll = () => {\n\t\tsetSelected((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (allOnPageSelected) {\n\t\t\t\tfor (const c of paginatedComments) next.delete(c.id);\n\t\t\t} else {\n\t\t\t\tfor (const c of paginatedComments) next.add(c.id);\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t};\n\n\tconst toggleOne = (id: string) => {\n\t\tsetSelected((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(id)) {\n\t\t\t\tnext.delete(id);\n\t\t\t} else {\n\t\t\t\tnext.add(id);\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t};\n\n\tconst handleBulk = (action: BulkAction) => {\n\t\tif (selected.size === 0) return;\n\t\tvoid onBulkAction([...selected], action).then(clearSelection);\n\t};\n\n\t// Collection filter items\n\tconst collectionItems: Record<string, string> = { \"\": \"All collections\" };\n\tfor (const [slug, config] of Object.entries(collections)) {\n\t\tcollectionItems[slug] = config.label;\n\t}\n\n\tconst total = counts.pending + counts.approved + counts.spam + counts.trash;\n\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<ChatCircle className=\"h-6 w-6\" />\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">Comments</h1>\n\t\t\t\t\t{total > 0 && <span className=\"text-sm text-kumo-subtle\">{total} total</span>}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Filters row */}\n\t\t\t<div className=\"flex items-center gap-3 flex-wrap\">\n\t\t\t\t{/* Search */}\n\t\t\t\t<div className=\"relative max-w-xs flex-1 min-w-[200px]\">\n\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tplaceholder=\"Search comments...\"\n\t\t\t\t\t\taria-label=\"Search comments\"\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={(e) => onSearchChange(e.target.value)}\n\t\t\t\t\t\tclassName=\"pl-9\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Collection filter */}\n\t\t\t\t{Object.keys(collections).length > 1 && (\n\t\t\t\t\t<div className=\"w-48\">\n\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\tvalue={collectionFilter}\n\t\t\t\t\t\t\tonValueChange={(v) => onCollectionFilterChange(v ?? \"\")}\n\t\t\t\t\t\t\titems={collectionItems}\n\t\t\t\t\t\t\taria-label=\"Filter by collection\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Tabs */}\n\t\t\t<Tabs\n\t\t\t\tvariant=\"underline\"\n\t\t\t\tvalue={activeStatus}\n\t\t\t\tonValueChange={(v) => {\n\t\t\t\t\tif (v === \"pending\" || v === \"approved\" || v === \"spam\" || v === \"trash\") {\n\t\t\t\t\t\tonStatusChange(v);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\ttabs={[\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"pending\",\n\t\t\t\t\t\tlabel: (\n\t\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\tPending\n\t\t\t\t\t\t\t\t{counts.pending > 0 && <Badge variant=\"secondary\">{counts.pending}</Badge>}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\t{ value: \"approved\", label: \"Approved\" },\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"spam\",\n\t\t\t\t\t\tlabel: (\n\t\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\tSpam\n\t\t\t\t\t\t\t\t{counts.spam > 0 && <Badge variant=\"secondary\">{counts.spam}</Badge>}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"trash\",\n\t\t\t\t\t\tlabel: (\n\t\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\tTrash\n\t\t\t\t\t\t\t\t{counts.trash > 0 && <Badge variant=\"secondary\">{counts.trash}</Badge>}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]}\n\t\t\t/>\n\n\t\t\t{/* Bulk action bar */}\n\t\t\t{selected.size > 0 && (\n\t\t\t\t<div className=\"flex items-center gap-3 rounded-lg border bg-kumo-tint/50 px-4 py-2\">\n\t\t\t\t\t<span className=\"text-sm font-medium\">{selected.size} selected</span>\n\t\t\t\t\t<div className=\"flex gap-2 ml-auto\">\n\t\t\t\t\t\t{activeStatus !== \"approved\" && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\ticon={<Check className=\"h-3.5 w-3.5\" />}\n\t\t\t\t\t\t\t\tonClick={() => handleBulk(\"approve\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tApprove\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{activeStatus !== \"spam\" && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\ticon={<Warning className=\"h-3.5 w-3.5\" />}\n\t\t\t\t\t\t\t\tonClick={() => handleBulk(\"spam\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tSpam\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{activeStatus !== \"trash\" && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\ticon={<Trash className=\"h-3.5 w-3.5\" />}\n\t\t\t\t\t\t\t\tonClick={() => handleBulk(\"trash\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tTrash\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{isAdmin && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\t\ticon={<Trash className=\"h-3.5 w-3.5\" />}\n\t\t\t\t\t\t\t\tonClick={() => handleBulk(\"delete\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Table */}\n\t\t\t<div className=\"rounded-md border overflow-x-auto\">\n\t\t\t\t<table className=\"w-full\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr className=\"border-b bg-kumo-tint/50\">\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"w-10 px-3 py-3\">\n\t\t\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\t\t\tchecked={allOnPageSelected}\n\t\t\t\t\t\t\t\t\tonChange={toggleAll}\n\t\t\t\t\t\t\t\t\taria-label=\"Select all\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tAuthor\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tComment\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tContent\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tDate\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-right text-sm font-medium\">\n\t\t\t\t\t\t\t\tActions\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{isLoading && comments.length === 0 ? (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={6} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\tLoading comments...\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t) : paginatedComments.length === 0 ? (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={6} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t<EmptyState status={activeStatus} hasSearch={!!searchQuery} />\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\tpaginatedComments.map((comment) => (\n\t\t\t\t\t\t\t\t<CommentRow\n\t\t\t\t\t\t\t\t\tkey={comment.id}\n\t\t\t\t\t\t\t\t\tcomment={comment}\n\t\t\t\t\t\t\t\t\tisSelected={selected.has(comment.id)}\n\t\t\t\t\t\t\t\t\tonToggle={() => toggleOne(comment.id)}\n\t\t\t\t\t\t\t\t\tonRowClick={() => setDetailComment(comment)}\n\t\t\t\t\t\t\t\t\tonStatusChange={(id, status) => {\n\t\t\t\t\t\t\t\t\t\tvoid onCommentStatusChange(id, status).then(clearSelection);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonDelete={(id) => {\n\t\t\t\t\t\t\t\t\t\tsetDeleteId(id);\n\t\t\t\t\t\t\t\t\t\tonDeleteErrorReset();\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tisAdmin={isAdmin}\n\t\t\t\t\t\t\t\t\tisStatusPending={isStatusPending}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\n\t\t\t{/* Pagination */}\n\t\t\t{(totalPages > 1 || nextCursor) && (\n\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t<span className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{comments.length} {comments.length === 1 ? \"comment\" : \"comments\"}\n\t\t\t\t\t</span>\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tdisabled={page === 0}\n\t\t\t\t\t\t\tonClick={() => setPage(page - 1)}\n\t\t\t\t\t\t\taria-label=\"Previous page\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CaretLeft className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<span className=\"text-sm\">\n\t\t\t\t\t\t\t{page + 1} / {totalPages}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tdisabled={page >= totalPages - 1 && !nextCursor}\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (page >= totalPages - 1 && nextCursor) {\n\t\t\t\t\t\t\t\t\tonLoadMore();\n\t\t\t\t\t\t\t\t\tsetPage(page + 1);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tsetPage(page + 1);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\taria-label=\"Next page\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CaretRight className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Detail slide-over */}\n\t\t\t{detailComment && (\n\t\t\t\t<CommentDetail\n\t\t\t\t\tcomment={detailComment}\n\t\t\t\t\tonClose={() => setDetailComment(null)}\n\t\t\t\t\tonStatusChange={(id, status) => {\n\t\t\t\t\t\tvoid onCommentStatusChange(id, status).then(clearSelection);\n\t\t\t\t\t\tsetDetailComment(null);\n\t\t\t\t\t}}\n\t\t\t\t\tonDelete={(id) => {\n\t\t\t\t\t\tsetDeleteId(id);\n\t\t\t\t\t\tonDeleteErrorReset();\n\t\t\t\t\t\tsetDetailComment(null);\n\t\t\t\t\t}}\n\t\t\t\t\tisAdmin={isAdmin}\n\t\t\t\t\tisStatusPending={isStatusPending}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Delete confirmation */}\n\t\t\t<ConfirmDialog\n\t\t\t\topen={!!deleteId}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetDeleteId(null);\n\t\t\t\t\tonDeleteErrorReset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Delete Comment?\"\n\t\t\t\tdescription=\"This will permanently delete this comment. This action cannot be undone.\"\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={isStatusPending}\n\t\t\t\terror={deleteError}\n\t\t\t\tonConfirm={() => {\n\t\t\t\t\tif (deleteId) {\n\t\t\t\t\t\tvoid onCommentDelete(deleteId).then(() => setDeleteId(null));\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\ninterface CommentRowProps {\n\tcomment: AdminComment;\n\tisSelected: boolean;\n\tonToggle: () => void;\n\tonRowClick: () => void;\n\tonStatusChange: (id: string, status: CommentStatus) => void;\n\tonDelete: (id: string) => void;\n\tisAdmin: boolean;\n\tisStatusPending: boolean;\n}\n\nfunction CommentRow({\n\tcomment,\n\tisSelected,\n\tonToggle,\n\tonRowClick,\n\tonStatusChange,\n\tonDelete,\n\tisAdmin,\n\tisStatusPending,\n}: CommentRowProps) {\n\tconst date = new Date(comment.createdAt);\n\tconst excerpt = comment.body.length > 120 ? comment.body.slice(0, 120) + \"...\" : comment.body;\n\n\treturn (\n\t\t<tr className={cn(\"border-b hover:bg-kumo-tint/25\", isSelected && \"bg-kumo-tint/40\")}>\n\t\t\t<td className=\"w-10 px-3 py-3\">\n\t\t\t\t<Checkbox\n\t\t\t\t\tchecked={isSelected}\n\t\t\t\t\tonChange={onToggle}\n\t\t\t\t\taria-label={`Select comment by ${comment.authorName}`}\n\t\t\t\t/>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<button type=\"button\" onClick={onRowClick} className=\"text-left\">\n\t\t\t\t\t<div className=\"font-medium text-sm\">{comment.authorName}</div>\n\t\t\t\t\t<div className=\"text-xs text-kumo-subtle\">{comment.authorEmail}</div>\n\t\t\t\t</button>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 max-w-xs\">\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={onRowClick}\n\t\t\t\t\tclassName=\"text-left text-sm text-kumo-subtle hover:text-kumo-default line-clamp-2\"\n\t\t\t\t>\n\t\t\t\t\t{excerpt}\n\t\t\t\t</button>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<div className=\"text-xs\">\n\t\t\t\t\t<span className=\"font-medium\">{comment.collection}</span>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle whitespace-nowrap\">\n\t\t\t\t{date.toLocaleDateString()}\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 text-right\">\n\t\t\t\t<div className=\"flex items-center justify-end gap-1\">\n\t\t\t\t\t{comment.status !== \"approved\" && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\taria-label=\"Approve\"\n\t\t\t\t\t\t\tonClick={() => onStatusChange(comment.id, \"approved\")}\n\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Check className=\"h-4 w-4 text-green-600\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t{comment.status !== \"spam\" && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\taria-label=\"Mark as spam\"\n\t\t\t\t\t\t\tonClick={() => onStatusChange(comment.id, \"spam\")}\n\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Warning className=\"h-4 w-4 text-orange-500\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t{comment.status !== \"trash\" && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\taria-label=\"Trash\"\n\t\t\t\t\t\t\tonClick={() => onStatusChange(comment.id, \"trash\")}\n\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t{isAdmin && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\taria-label=\"Delete permanently\"\n\t\t\t\t\t\t\tonClick={() => onDelete(comment.id)}\n\t\t\t\t\t\t\tdisabled={isStatusPending}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-danger\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\nfunction EmptyState({ status, hasSearch }: { status: CommentStatus; hasSearch: boolean }) {\n\tif (hasSearch) {\n\t\treturn <p>No comments match your search.</p>;\n\t}\n\n\tconst messages: Record<CommentStatus, string> = {\n\t\tpending: \"No comments awaiting moderation.\",\n\t\tapproved: \"No approved comments yet.\",\n\t\tspam: \"No spam comments.\",\n\t\ttrash: \"Trash is empty.\",\n\t};\n\n\treturn <p>{messages[status]}</p>;\n}\n","/**\n * Content CRUD and revision APIs\n */\n\nimport type { BylineCreditInput, BylineSummary } from \"./bylines.js\";\nimport {\n\tAPI_BASE,\n\tapiFetch,\n\tparseApiResponse,\n\tthrowResponseError,\n\ttype FindManyResult,\n} from \"./client.js\";\n\n/**\n * Derive draft status from a content item's revision pointers\n */\nexport function getDraftStatus(\n\titem: ContentItem,\n): \"unpublished\" | \"published\" | \"published_with_changes\" {\n\tif (!item.liveRevisionId) return \"unpublished\";\n\tif (item.draftRevisionId && item.draftRevisionId !== item.liveRevisionId)\n\t\treturn \"published_with_changes\";\n\treturn \"published\";\n}\n\n/** SEO metadata for a content item */\nexport interface ContentSeo {\n\ttitle: string | null;\n\tdescription: string | null;\n\timage: string | null;\n\tcanonical: string | null;\n\tnoIndex: boolean;\n}\n\nexport interface ContentItem {\n\tid: string;\n\ttype: string;\n\tslug: string | null;\n\tstatus: string;\n\tlocale: string;\n\ttranslationGroup: string | null;\n\tdata: Record<string, unknown>;\n\tauthorId: string | null;\n\tprimaryBylineId: string | null;\n\tbyline?: BylineSummary | null;\n\tbylines?: Array<{\n\t\tbyline: BylineSummary;\n\t\tsortOrder: number;\n\t\troleLabel: string | null;\n\t}>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tpublishedAt: string | null;\n\tscheduledAt: string | null;\n\tliveRevisionId: string | null;\n\tdraftRevisionId: string | null;\n\tseo?: ContentSeo;\n}\n\nexport interface CreateContentInput {\n\ttype: string;\n\tslug?: string;\n\tdata: Record<string, unknown>;\n\tstatus?: string;\n\tbylines?: BylineCreditInput[];\n\tlocale?: string;\n\ttranslationOf?: string;\n}\n\nexport interface TranslationSummary {\n\tid: string;\n\tlocale: string;\n\tslug: string | null;\n\tstatus: string;\n\tupdatedAt: string;\n}\n\nexport interface TranslationsResponse {\n\ttranslationGroup: string;\n\ttranslations: TranslationSummary[];\n}\n\n/**\n * Fetch translations for a content item\n */\nexport async function fetchTranslations(\n\tcollection: string,\n\tid: string,\n): Promise<TranslationsResponse> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/translations`);\n\treturn parseApiResponse<TranslationsResponse>(response, \"Failed to fetch translations\");\n}\n\n/** Input for updating SEO fields on content */\nexport interface ContentSeoInput {\n\ttitle?: string | null;\n\tdescription?: string | null;\n\timage?: string | null;\n\tcanonical?: string | null;\n\tnoIndex?: boolean;\n}\n\nexport interface UpdateContentInput {\n\tdata?: Record<string, unknown>;\n\tslug?: string;\n\tstatus?: string;\n\tauthorId?: string | null;\n\tbylines?: BylineCreditInput[];\n\t/** Skip revision creation (used by autosave) */\n\tskipRevision?: boolean;\n\tseo?: ContentSeoInput;\n}\n\n/**\n * Trashed content item with deletion timestamp\n */\nexport interface TrashedContentItem extends ContentItem {\n\tdeletedAt: string;\n}\n\n/**\n * Preview URL response\n */\nexport interface PreviewUrlResponse {\n\turl: string;\n\texpiresAt: number;\n}\n\n/**\n * Fetch content list\n */\nexport async function fetchContentList(\n\tcollection: string,\n\toptions?: {\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t\tstatus?: string;\n\t\tlocale?: string;\n\t},\n): Promise<FindManyResult<ContentItem>> {\n\tconst params = new URLSearchParams();\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\tif (options?.status) params.set(\"status\", options.status);\n\tif (options?.locale) params.set(\"locale\", options.locale);\n\n\tconst url = `${API_BASE}/content/${collection}${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<FindManyResult<ContentItem>>(response, \"Failed to fetch content\");\n}\n\n/**\n * Fetch single content item\n */\nexport async function fetchContent(collection: string, id: string): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}`);\n\tconst data = await parseApiResponse<{ item: ContentItem }>(response, \"Failed to fetch content\");\n\treturn data.item;\n}\n\n/**\n * Create content\n */\nexport async function createContent(\n\tcollection: string,\n\tinput: Omit<CreateContentInput, \"type\">,\n): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({\n\t\t\tdata: input.data,\n\t\t\tslug: input.slug,\n\t\t\tstatus: input.status,\n\t\t\tbylines: input.bylines,\n\t\t\tlocale: input.locale,\n\t\t\ttranslationOf: input.translationOf,\n\t\t}),\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(response, \"Failed to create content\");\n\treturn data.item;\n}\n\n/**\n * Update content\n */\nexport async function updateContent(\n\tcollection: string,\n\tid: string,\n\tinput: UpdateContentInput,\n): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(response, \"Failed to update content\");\n\treturn data.item;\n}\n\n/**\n * Delete content (moves to trash)\n */\nexport async function deleteContent(collection: string, id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete content\");\n}\n\n/**\n * Fetch trashed content list\n */\nexport async function fetchTrashedContent(\n\tcollection: string,\n\toptions?: {\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t},\n): Promise<FindManyResult<TrashedContentItem>> {\n\tconst params = new URLSearchParams();\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\n\tconst url = `${API_BASE}/content/${collection}/trash${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<FindManyResult<TrashedContentItem>>(\n\t\tresponse,\n\t\t\"Failed to fetch trashed content\",\n\t);\n}\n\n/**\n * Restore content from trash\n */\nexport async function restoreContent(collection: string, id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/restore`, {\n\t\tmethod: \"POST\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to restore content\");\n}\n\n/**\n * Permanently delete content (cannot be undone)\n */\nexport async function permanentDeleteContent(collection: string, id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/permanent`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to permanently delete content\");\n}\n\n/**\n * Duplicate content (creates a draft copy)\n */\nexport async function duplicateContent(collection: string, id: string): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/duplicate`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(\n\t\tresponse,\n\t\t\"Failed to duplicate content\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Schedule content for future publishing\n */\nexport async function scheduleContent(\n\tcollection: string,\n\tid: string,\n\tscheduledAt: string,\n): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/schedule`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ scheduledAt }),\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(\n\t\tresponse,\n\t\t\"Failed to schedule content\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Unschedule content (revert to draft)\n */\nexport async function unscheduleContent(collection: string, id: string): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/schedule`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(\n\t\tresponse,\n\t\t\"Failed to unschedule content\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Get a preview URL for content\n *\n * Returns a signed URL that allows viewing draft content.\n * Returns null if preview is not configured (missing EMDASH_PREVIEW_SECRET).\n */\nexport async function getPreviewUrl(\n\tcollection: string,\n\tid: string,\n\toptions?: {\n\t\texpiresIn?: string;\n\t\tpathPattern?: string;\n\t},\n): Promise<PreviewUrlResponse | null> {\n\ttry {\n\t\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/preview-url`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify(options || {}),\n\t\t});\n\n\t\tif (response.status === 500) {\n\t\t\t// Preview not configured — check error code without consuming body for parseApiResponse\n\t\t\tconst body: unknown = await response.json().catch(() => ({}));\n\t\t\tif (\n\t\t\t\ttypeof body === \"object\" &&\n\t\t\t\tbody !== null &&\n\t\t\t\t\"error\" in body &&\n\t\t\t\ttypeof body.error === \"object\" &&\n\t\t\t\tbody.error !== null &&\n\t\t\t\t\"code\" in body.error &&\n\t\t\t\tbody.error.code === \"NOT_CONFIGURED\"\n\t\t\t) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\t// Some other 500 error\n\t\t\tthrow new Error(\"Failed to get preview URL\");\n\t\t}\n\n\t\treturn parseApiResponse<PreviewUrlResponse>(response, \"Failed to get preview URL\");\n\t} catch {\n\t\t// If preview endpoint doesn't exist or fails, return null\n\t\treturn null;\n\t}\n}\n\n// =============================================================================\n// Publishing (Draft Revisions)\n// =============================================================================\n\n/**\n * Publish content - promotes current draft to live\n */\nexport async function publishContent(collection: string, id: string): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/publish`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(response, \"Failed to publish content\");\n\treturn data.item;\n}\n\n/**\n * Unpublish content - removes from public, preserves draft\n */\nexport async function unpublishContent(collection: string, id: string): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/unpublish`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(\n\t\tresponse,\n\t\t\"Failed to unpublish content\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Discard draft changes - reverts to live version\n */\nexport async function discardDraft(collection: string, id: string): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/discard-draft`, {\n\t\tmethod: \"POST\",\n\t});\n\tconst data = await parseApiResponse<{ item: ContentItem }>(response, \"Failed to discard draft\");\n\treturn data.item;\n}\n\n/**\n * Compare live and draft revisions\n */\nexport async function compareRevisions(\n\tcollection: string,\n\tid: string,\n): Promise<{\n\thasChanges: boolean;\n\tlive: Record<string, unknown> | null;\n\tdraft: Record<string, unknown> | null;\n}> {\n\tconst response = await apiFetch(`${API_BASE}/content/${collection}/${id}/compare`);\n\treturn parseApiResponse<{\n\t\thasChanges: boolean;\n\t\tlive: Record<string, unknown> | null;\n\t\tdraft: Record<string, unknown> | null;\n\t}>(response, \"Failed to compare revisions\");\n}\n\n// =============================================================================\n// Revision API\n// =============================================================================\n\nexport interface Revision {\n\tid: string;\n\tcollection: string;\n\tentryId: string;\n\tdata: Record<string, unknown>;\n\tauthorId: string | null;\n\tcreatedAt: string;\n}\n\nexport interface RevisionListResponse {\n\titems: Revision[];\n\ttotal: number;\n}\n\n/**\n * Fetch revisions for a content item\n */\nexport async function fetchRevisions(\n\tcollection: string,\n\tentryId: string,\n\toptions?: { limit?: number },\n): Promise<RevisionListResponse> {\n\tconst params = new URLSearchParams();\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\n\tconst url = `${API_BASE}/content/${collection}/${entryId}/revisions${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<RevisionListResponse>(response, \"Failed to fetch revisions\");\n}\n\n/**\n * Get a specific revision\n */\nexport async function fetchRevision(revisionId: string): Promise<Revision> {\n\tconst response = await apiFetch(`${API_BASE}/revisions/${revisionId}`);\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(`Revision not found: ${revisionId}`);\n\t\t}\n\t\tawait throwResponseError(response, \"Failed to fetch revision\");\n\t}\n\n\tconst data = await parseApiResponse<{ item: Revision }>(response, \"Failed to fetch revision\");\n\treturn data.item;\n}\n\n/**\n * Restore a revision (updates content to this revision's data)\n */\nexport async function restoreRevision(revisionId: string): Promise<ContentItem> {\n\tconst response = await apiFetch(`${API_BASE}/revisions/${revisionId}/restore`, {\n\t\tmethod: \"POST\",\n\t});\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(`Revision not found: ${revisionId}`);\n\t\t}\n\t\tawait throwResponseError(response, \"Failed to restore revision\");\n\t}\n\n\tconst data = await parseApiResponse<{ item: ContentItem }>(\n\t\tresponse,\n\t\t\"Failed to restore revision\",\n\t);\n\treturn data.item;\n}\n","/**\n * Media upload, list, delete, and provider APIs\n */\n\nimport {\n\tAPI_BASE,\n\tapiFetch,\n\tparseApiResponse,\n\tthrowResponseError,\n\ttype FindManyResult,\n} from \"./client.js\";\n\nexport interface MediaItem {\n\tid: string;\n\tfilename: string;\n\tmimeType: string;\n\turl: string;\n\t/** Storage key for local media (e.g., \"01ABC.jpg\"). Not present for external URLs. */\n\tstorageKey?: string;\n\tsize: number;\n\twidth?: number;\n\theight?: number;\n\talt?: string;\n\tcaption?: string;\n\tcreatedAt: string;\n\t/** Provider ID for external media (e.g., \"cloudflare-images\") */\n\tprovider?: string;\n\t/** Provider-specific metadata */\n\tmeta?: Record<string, unknown>;\n}\n\n/**\n * Fetch media list\n */\nexport async function fetchMediaList(options?: {\n\tcursor?: string;\n\tlimit?: number;\n\tmimeType?: string;\n}): Promise<FindManyResult<MediaItem>> {\n\tconst params = new URLSearchParams();\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\tif (options?.mimeType) params.set(\"mimeType\", options.mimeType);\n\n\tconst url = `${API_BASE}/media${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<FindManyResult<MediaItem>>(response, \"Failed to fetch media\");\n}\n\n/**\n * Upload URL response from the API\n */\ninterface UploadUrlResponse {\n\tuploadUrl: string;\n\tmethod: \"PUT\";\n\theaders: Record<string, string>;\n\tmediaId: string;\n\tstorageKey: string;\n\texpiresAt: string;\n}\n\n/**\n * Try to get a signed upload URL\n * Returns null if signed URLs are not supported (e.g., local storage)\n */\nasync function getUploadUrl(file: File): Promise<UploadUrlResponse | null> {\n\ttry {\n\t\tconst response = await apiFetch(`${API_BASE}/media/upload-url`, {\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({\n\t\t\t\tfilename: file.name,\n\t\t\t\tcontentType: file.type,\n\t\t\t\tsize: file.size,\n\t\t\t}),\n\t\t});\n\n\t\tif (response.status === 501) {\n\t\t\t// Not implemented - storage doesn't support signed URLs\n\t\t\treturn null;\n\t\t}\n\n\t\treturn parseApiResponse<UploadUrlResponse>(response, \"Failed to get upload URL\");\n\t} catch (error) {\n\t\t// If the endpoint doesn't exist, fall back to direct upload\n\t\tif (error instanceof TypeError && error.message.includes(\"fetch\")) {\n\t\t\treturn null;\n\t\t}\n\t\tthrow error;\n\t}\n}\n\n/**\n * Confirm upload after uploading to signed URL\n */\nasync function confirmUpload(\n\tmediaId: string,\n\tmetadata?: { width?: number; height?: number; size?: number },\n): Promise<MediaItem> {\n\tconst response = await apiFetch(`${API_BASE}/media/${mediaId}/confirm`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(metadata || {}),\n\t});\n\tconst data = await parseApiResponse<{ item: MediaItem }>(response, \"Failed to confirm upload\");\n\treturn data.item;\n}\n\n/**\n * Upload directly to signed URL\n */\nasync function uploadToSignedUrl(file: File, uploadInfo: UploadUrlResponse): Promise<void> {\n\tconst response = await fetch(uploadInfo.uploadUrl, {\n\t\tmethod: uploadInfo.method,\n\t\theaders: {\n\t\t\t...uploadInfo.headers,\n\t\t\t\"Content-Type\": file.type,\n\t\t},\n\t\tbody: file,\n\t});\n\n\tif (!response.ok) await throwResponseError(response, \"Failed to upload file\");\n}\n\n/**\n * Get image dimensions from a file\n */\nasync function getImageDimensions(file: File): Promise<{ width: number; height: number } | null> {\n\tif (!file.type.startsWith(\"image/\")) {\n\t\treturn null;\n\t}\n\n\treturn new Promise((resolve) => {\n\t\tconst img = new Image();\n\t\timg.onload = () => {\n\t\t\tresolve({ width: img.naturalWidth, height: img.naturalHeight });\n\t\t\tURL.revokeObjectURL(img.src);\n\t\t};\n\t\timg.onerror = () => {\n\t\t\tresolve(null);\n\t\t\tURL.revokeObjectURL(img.src);\n\t\t};\n\t\timg.src = URL.createObjectURL(file);\n\t});\n}\n\n/**\n * Upload media file via direct upload (legacy/local storage)\n */\nasync function uploadMediaDirect(file: File): Promise<MediaItem> {\n\t// Get image dimensions before upload\n\tconst dimensions = await getImageDimensions(file);\n\n\tconst formData = new FormData();\n\tformData.append(\"file\", file);\n\t// Send dimensions as form fields\n\tif (dimensions?.width) formData.append(\"width\", String(dimensions.width));\n\tif (dimensions?.height) formData.append(\"height\", String(dimensions.height));\n\n\tconst response = await apiFetch(`${API_BASE}/media`, {\n\t\tmethod: \"POST\",\n\t\tbody: formData,\n\t});\n\tconst data = await parseApiResponse<{ item: MediaItem }>(response, \"Failed to upload media\");\n\treturn data.item;\n}\n\n/**\n * Upload media file\n *\n * Tries signed URL upload first (for S3/R2 storage), falls back to direct upload\n * (for local storage) if signed URLs are not supported.\n */\nexport async function uploadMedia(file: File): Promise<MediaItem> {\n\t// Try to get a signed upload URL\n\tconst uploadInfo = await getUploadUrl(file);\n\n\tif (!uploadInfo) {\n\t\t// Signed URLs not supported, use direct upload\n\t\treturn uploadMediaDirect(file);\n\t}\n\n\t// Upload directly to storage via signed URL\n\tawait uploadToSignedUrl(file, uploadInfo);\n\n\t// Get image dimensions for confirmation\n\tconst dimensions = await getImageDimensions(file);\n\n\t// Confirm the upload\n\treturn confirmUpload(uploadInfo.mediaId, {\n\t\tsize: file.size,\n\t\twidth: dimensions?.width,\n\t\theight: dimensions?.height,\n\t});\n}\n\n/**\n * Delete media\n */\nexport async function deleteMedia(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/media/${id}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete media\");\n}\n\n/**\n * Update media metadata (dimensions, alt text, etc.)\n */\nexport async function updateMedia(\n\tid: string,\n\tinput: { alt?: string; caption?: string; width?: number; height?: number },\n): Promise<MediaItem> {\n\tconst response = await apiFetch(`${API_BASE}/media/${id}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ item: MediaItem }>(response, \"Failed to update media\");\n\treturn data.item;\n}\n\n// =============================================================================\n// Media Providers API\n// =============================================================================\n\n/** Media provider capabilities */\nexport interface MediaProviderCapabilities {\n\tbrowse: boolean;\n\tsearch: boolean;\n\tupload: boolean;\n\tdelete: boolean;\n}\n\n/** Media provider info from the API */\nexport interface MediaProviderInfo {\n\tid: string;\n\tname: string;\n\ticon?: string;\n\tcapabilities: MediaProviderCapabilities;\n}\n\n/** Media item from a provider */\nexport interface MediaProviderItem {\n\tid: string;\n\tfilename: string;\n\tmimeType: string;\n\tsize?: number;\n\twidth?: number;\n\theight?: number;\n\talt?: string;\n\tpreviewUrl?: string;\n\tmeta?: Record<string, unknown>;\n}\n\n/**\n * Fetch all configured media providers\n */\nexport async function fetchMediaProviders(): Promise<MediaProviderInfo[]> {\n\tconst response = await apiFetch(`${API_BASE}/media/providers`);\n\tconst data = await parseApiResponse<{ items: MediaProviderInfo[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch media providers\",\n\t);\n\treturn data.items;\n}\n\n/**\n * Fetch media items from a specific provider\n */\nexport async function fetchProviderMedia(\n\tproviderId: string,\n\toptions?: {\n\t\tcursor?: string;\n\t\tlimit?: number;\n\t\tquery?: string;\n\t\tmimeType?: string;\n\t},\n): Promise<FindManyResult<MediaProviderItem>> {\n\tconst params = new URLSearchParams();\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\tif (options?.query) params.set(\"query\", options.query);\n\tif (options?.mimeType) params.set(\"mimeType\", options.mimeType);\n\n\tconst url = `${API_BASE}/media/providers/${providerId}${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<FindManyResult<MediaProviderItem>>(\n\t\tresponse,\n\t\t\"Failed to fetch provider media\",\n\t);\n}\n\n/**\n * Upload media to a specific provider\n */\nexport async function uploadToProvider(\n\tproviderId: string,\n\tfile: File,\n\talt?: string,\n): Promise<MediaProviderItem> {\n\tconst formData = new FormData();\n\tformData.append(\"file\", file);\n\tif (alt) formData.append(\"alt\", alt);\n\n\tconst response = await apiFetch(`${API_BASE}/media/providers/${providerId}`, {\n\t\tmethod: \"POST\",\n\t\tbody: formData,\n\t});\n\tconst data = await parseApiResponse<{ item: MediaProviderItem }>(\n\t\tresponse,\n\t\t\"Failed to upload to provider\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Delete media from a specific provider\n */\nexport async function deleteFromProvider(providerId: string, itemId: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/media/providers/${providerId}/${itemId}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete from provider\");\n}\n","/**\n * Schema/collection/field management APIs (Content Type Builder)\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport type FieldType =\n\t| \"string\"\n\t| \"text\"\n\t| \"number\"\n\t| \"integer\"\n\t| \"boolean\"\n\t| \"datetime\"\n\t| \"select\"\n\t| \"multiSelect\"\n\t| \"portableText\"\n\t| \"image\"\n\t| \"file\"\n\t| \"reference\"\n\t| \"json\"\n\t| \"slug\";\n\nexport interface SchemaCollection {\n\tid: string;\n\tslug: string;\n\tlabel: string;\n\tlabelSingular?: string;\n\tdescription?: string;\n\ticon?: string;\n\tsupports: string[];\n\tsource?: string;\n\turlPattern?: string;\n\thasSeo: boolean;\n\tcommentsEnabled: boolean;\n\tcommentsModeration: \"all\" | \"first_time\" | \"none\";\n\tcommentsClosedAfterDays: number;\n\tcommentsAutoApproveUsers: boolean;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SchemaField {\n\tid: string;\n\tcollectionId: string;\n\tslug: string;\n\tlabel: string;\n\ttype: FieldType;\n\tcolumnType: string;\n\trequired: boolean;\n\tunique: boolean;\n\tsearchable: boolean;\n\tdefaultValue?: unknown;\n\tvalidation?: {\n\t\tmin?: number;\n\t\tmax?: number;\n\t\tminLength?: number;\n\t\tmaxLength?: number;\n\t\tpattern?: string;\n\t\toptions?: string[];\n\t};\n\twidget?: string;\n\toptions?: Record<string, unknown>;\n\tsortOrder: number;\n\tcreatedAt: string;\n}\n\nexport interface SchemaCollectionWithFields extends SchemaCollection {\n\tfields: SchemaField[];\n}\n\nexport interface CreateCollectionInput {\n\tslug: string;\n\tlabel: string;\n\tlabelSingular?: string;\n\tdescription?: string;\n\ticon?: string;\n\tsupports?: string[];\n\turlPattern?: string;\n\thasSeo?: boolean;\n}\n\nexport interface UpdateCollectionInput {\n\tlabel?: string;\n\tlabelSingular?: string;\n\tdescription?: string;\n\ticon?: string;\n\tsupports?: string[];\n\turlPattern?: string;\n\thasSeo?: boolean;\n\tcommentsEnabled?: boolean;\n\tcommentsModeration?: \"all\" | \"first_time\" | \"none\";\n\tcommentsClosedAfterDays?: number;\n\tcommentsAutoApproveUsers?: boolean;\n}\n\nexport interface CreateFieldInput {\n\tslug: string;\n\tlabel: string;\n\ttype: FieldType;\n\trequired?: boolean;\n\tunique?: boolean;\n\tsearchable?: boolean;\n\tdefaultValue?: unknown;\n\tvalidation?: {\n\t\tmin?: number;\n\t\tmax?: number;\n\t\tminLength?: number;\n\t\tmaxLength?: number;\n\t\tpattern?: string;\n\t\toptions?: string[];\n\t};\n\twidget?: string;\n\toptions?: Record<string, unknown>;\n}\n\nexport interface UpdateFieldInput {\n\tlabel?: string;\n\trequired?: boolean;\n\tunique?: boolean;\n\tsearchable?: boolean;\n\tdefaultValue?: unknown;\n\tvalidation?: {\n\t\tmin?: number;\n\t\tmax?: number;\n\t\tminLength?: number;\n\t\tmaxLength?: number;\n\t\tpattern?: string;\n\t\toptions?: string[];\n\t};\n\twidget?: string;\n\toptions?: Record<string, unknown>;\n\tsortOrder?: number;\n}\n\n/**\n * Fetch all collections\n */\nexport async function fetchCollections(): Promise<SchemaCollection[]> {\n\tconst response = await apiFetch(`${API_BASE}/schema/collections`);\n\tconst data = await parseApiResponse<{ items: SchemaCollection[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch collections\",\n\t);\n\treturn data.items;\n}\n\n/**\n * Fetch a single collection with fields\n */\nexport async function fetchCollection(\n\tslug: string,\n\tincludeFields = true,\n): Promise<SchemaCollectionWithFields> {\n\tconst url = includeFields\n\t\t? `${API_BASE}/schema/collections/${slug}?includeFields=true`\n\t\t: `${API_BASE}/schema/collections/${slug}`;\n\tconst response = await apiFetch(url);\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(`Collection \"${slug}\" not found`);\n\t\t}\n\t\tawait throwResponseError(response, \"Failed to fetch collection\");\n\t}\n\tconst data = await parseApiResponse<{ item: SchemaCollectionWithFields }>(\n\t\tresponse,\n\t\t\"Failed to fetch collection\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Create a collection\n */\nexport async function createCollection(input: CreateCollectionInput): Promise<SchemaCollection> {\n\tconst response = await apiFetch(`${API_BASE}/schema/collections`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ item: SchemaCollection }>(\n\t\tresponse,\n\t\t\"Failed to create collection\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Update a collection\n */\nexport async function updateCollection(\n\tslug: string,\n\tinput: UpdateCollectionInput,\n): Promise<SchemaCollection> {\n\tconst response = await apiFetch(`${API_BASE}/schema/collections/${slug}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ item: SchemaCollection }>(\n\t\tresponse,\n\t\t\"Failed to update collection\",\n\t);\n\treturn data.item;\n}\n\n/**\n * Delete a collection\n */\nexport async function deleteCollection(slug: string, force = false): Promise<void> {\n\tconst url = force\n\t\t? `${API_BASE}/schema/collections/${slug}?force=true`\n\t\t: `${API_BASE}/schema/collections/${slug}`;\n\tconst response = await apiFetch(url, { method: \"DELETE\" });\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete collection\");\n}\n\n/**\n * Fetch fields for a collection\n */\nexport async function fetchFields(collectionSlug: string): Promise<SchemaField[]> {\n\tconst response = await apiFetch(`${API_BASE}/schema/collections/${collectionSlug}/fields`);\n\tconst data = await parseApiResponse<{ items: SchemaField[] }>(response, \"Failed to fetch fields\");\n\treturn data.items;\n}\n\n/**\n * Create a field\n */\nexport async function createField(\n\tcollectionSlug: string,\n\tinput: CreateFieldInput,\n): Promise<SchemaField> {\n\tconst response = await apiFetch(`${API_BASE}/schema/collections/${collectionSlug}/fields`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ item: SchemaField }>(response, \"Failed to create field\");\n\treturn data.item;\n}\n\n/**\n * Update a field\n */\nexport async function updateField(\n\tcollectionSlug: string,\n\tfieldSlug: string,\n\tinput: UpdateFieldInput,\n): Promise<SchemaField> {\n\tconst response = await apiFetch(\n\t\t`${API_BASE}/schema/collections/${collectionSlug}/fields/${fieldSlug}`,\n\t\t{\n\t\t\tmethod: \"PUT\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify(input),\n\t\t},\n\t);\n\tconst data = await parseApiResponse<{ item: SchemaField }>(response, \"Failed to update field\");\n\treturn data.item;\n}\n\n/**\n * Delete a field\n */\nexport async function deleteField(collectionSlug: string, fieldSlug: string): Promise<void> {\n\tconst response = await apiFetch(\n\t\t`${API_BASE}/schema/collections/${collectionSlug}/fields/${fieldSlug}`,\n\t\t{ method: \"DELETE\" },\n\t);\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete field\");\n}\n\n/**\n * Reorder fields\n */\nexport async function reorderFields(collectionSlug: string, fieldSlugs: string[]): Promise<void> {\n\tconst response = await apiFetch(\n\t\t`${API_BASE}/schema/collections/${collectionSlug}/fields/reorder`,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ fieldSlugs }),\n\t\t},\n\t);\n\tif (!response.ok) await throwResponseError(response, \"Failed to reorder fields\");\n}\n\n// ============================================\n// Orphaned Tables\n// ============================================\n\nexport interface OrphanedTable {\n\tslug: string;\n\ttableName: string;\n\trowCount: number;\n}\n\n/**\n * Fetch orphaned content tables\n */\nexport async function fetchOrphanedTables(): Promise<OrphanedTable[]> {\n\tconst response = await apiFetch(`${API_BASE}/schema/orphans`);\n\tconst data = await parseApiResponse<{ items: OrphanedTable[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch orphaned tables\",\n\t);\n\treturn data.items;\n}\n\n/**\n * Register an orphaned table as a collection\n */\nexport async function registerOrphanedTable(\n\tslug: string,\n\toptions?: {\n\t\tlabel?: string;\n\t\tlabelSingular?: string;\n\t\tdescription?: string;\n\t},\n): Promise<SchemaCollection> {\n\tconst response = await apiFetch(`${API_BASE}/schema/orphans/${slug}`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(options || {}),\n\t});\n\tconst data = await parseApiResponse<{ item: SchemaCollection }>(\n\t\tresponse,\n\t\t\"Failed to register orphaned table\",\n\t);\n\treturn data.item;\n}\n","/**\n * Site settings APIs\n */\n\nimport { API_BASE, apiFetch, parseApiResponse } from \"./client.js\";\n\nexport interface SiteSettings {\n\t// Identity\n\ttitle: string;\n\ttagline?: string;\n\tlogo?: { mediaId: string; alt?: string; url?: string };\n\tfavicon?: { mediaId: string; url?: string };\n\n\t// URLs\n\turl?: string;\n\n\t// Display\n\tpostsPerPage: number;\n\tdateFormat: string;\n\ttimezone: string;\n\n\t// Social\n\tsocial?: {\n\t\ttwitter?: string;\n\t\tgithub?: string;\n\t\tfacebook?: string;\n\t\tinstagram?: string;\n\t\tlinkedin?: string;\n\t\tyoutube?: string;\n\t};\n\n\t// SEO\n\tseo?: {\n\t\ttitleSeparator?: string;\n\t\tdefaultOgImage?: { mediaId: string; alt?: string; url?: string };\n\t\trobotsTxt?: string;\n\t\tgoogleVerification?: string;\n\t\tbingVerification?: string;\n\t};\n}\n\n/**\n * Fetch site settings\n */\nexport async function fetchSettings(): Promise<Partial<SiteSettings>> {\n\tconst response = await apiFetch(`${API_BASE}/settings`);\n\treturn parseApiResponse<Partial<SiteSettings>>(response, \"Failed to fetch settings\");\n}\n\n/**\n * Update site settings\n */\nexport async function updateSettings(\n\tsettings: Partial<SiteSettings>,\n): Promise<Partial<SiteSettings>> {\n\tconst response = await apiFetch(`${API_BASE}/settings`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(settings),\n\t});\n\treturn parseApiResponse<Partial<SiteSettings>>(response, \"Failed to update settings\");\n}\n","/**\n * User management, passkeys, and allowed domains APIs\n */\n\nimport {\n\tAPI_BASE,\n\tapiFetch,\n\tparseApiResponse,\n\tthrowResponseError,\n\ttype FindManyResult,\n} from \"./client.js\";\n\n// =============================================================================\n// User Management API\n// =============================================================================\n\n/** User list item with computed fields */\nexport interface UserListItem {\n\tid: string;\n\temail: string;\n\tname: string | null;\n\tavatarUrl: string | null;\n\trole: number;\n\temailVerified: boolean;\n\tdisabled: boolean;\n\tcreatedAt: string;\n\tupdatedAt: string;\n\tlastLogin: string | null;\n\tcredentialCount: number;\n\toauthProviders: string[];\n}\n\n/** User detail with credentials and OAuth accounts */\nexport interface UserDetail extends UserListItem {\n\tcredentials: Array<{\n\t\tid: string;\n\t\tname: string | null;\n\t\tdeviceType: string;\n\t\tcreatedAt: string;\n\t\tlastUsedAt: string;\n\t}>;\n\toauthAccounts: Array<{\n\t\tprovider: string;\n\t\tcreatedAt: string;\n\t}>;\n}\n\n/** User update input */\nexport interface UpdateUserInput {\n\tname?: string;\n\temail?: string;\n\trole?: number;\n}\n\n/**\n * Fetch users with search, filter, and pagination\n */\nexport async function fetchUsers(options?: {\n\tsearch?: string;\n\trole?: number;\n\tcursor?: string;\n\tlimit?: number;\n}): Promise<FindManyResult<UserListItem>> {\n\tconst params = new URLSearchParams();\n\tif (options?.search) params.set(\"search\", options.search);\n\tif (options?.role !== undefined) params.set(\"role\", String(options.role));\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\n\tconst url = `${API_BASE}/admin/users${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<FindManyResult<UserListItem>>(response, \"Failed to fetch users\");\n}\n\n/**\n * Fetch a single user with details\n */\nexport async function fetchUser(id: string): Promise<UserDetail> {\n\tconst response = await apiFetch(`${API_BASE}/admin/users/${id}`);\n\n\tif (!response.ok) {\n\t\tif (response.status === 404) {\n\t\t\tthrow new Error(`User not found: ${id}`);\n\t\t}\n\t\tawait throwResponseError(response, \"Failed to fetch user\");\n\t}\n\n\tconst data = await parseApiResponse<{ item: UserDetail }>(response, \"Failed to fetch user\");\n\treturn data.item;\n}\n\n/**\n * Update a user\n */\nexport async function updateUser(id: string, input: UpdateUserInput): Promise<UserDetail> {\n\tconst response = await apiFetch(`${API_BASE}/admin/users/${id}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ item: UserDetail }>(response, \"Failed to update user\");\n\treturn data.item;\n}\n\n/**\n * Disable a user\n */\nexport async function disableUser(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/users/${id}/disable`, {\n\t\tmethod: \"POST\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to disable user\");\n}\n\n/**\n * Send a recovery magic link to a user\n */\nexport async function sendRecoveryLink(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/users/${id}/send-recovery`, {\n\t\tmethod: \"POST\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to send recovery link\");\n}\n\n/**\n * Enable a user\n */\nexport async function enableUser(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/users/${id}/enable`, {\n\t\tmethod: \"POST\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to enable user\");\n}\n\n/** Invite response -- includes inviteUrl when no email provider is configured */\nexport interface InviteResult {\n\tsuccess: true;\n\tmessage: string;\n\t/** Present when no email provider is configured (copy-link fallback) */\n\tinviteUrl?: string;\n}\n\n/**\n * Invite a new user\n *\n * Uses the existing /auth/invite endpoint.\n * When no email provider is configured, the response includes\n * an `inviteUrl` for manual sharing.\n */\nexport async function inviteUser(email: string, role?: number): Promise<InviteResult> {\n\tconst response = await apiFetch(`${API_BASE}/auth/invite`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ email, role }),\n\t});\n\treturn parseApiResponse<InviteResult>(response, \"Failed to invite user\");\n}\n\n// =============================================================================\n// Passkey Management API\n// =============================================================================\n\n/**\n * Passkey info returned from API\n */\nexport interface PasskeyInfo {\n\tid: string;\n\tname: string | null;\n\tdeviceType: \"singleDevice\" | \"multiDevice\";\n\tbackedUp: boolean;\n\tcreatedAt: string;\n\tlastUsedAt: string;\n}\n\n/**\n * List all passkeys for the current user\n */\nexport async function fetchPasskeys(): Promise<PasskeyInfo[]> {\n\tconst response = await apiFetch(`${API_BASE}/auth/passkey`);\n\tconst data = await parseApiResponse<{ items: PasskeyInfo[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch passkeys\",\n\t);\n\treturn data.items;\n}\n\n/**\n * Rename a passkey\n */\nexport async function renamePasskey(id: string, name: string): Promise<PasskeyInfo> {\n\tconst response = await apiFetch(`${API_BASE}/auth/passkey/${id}`, {\n\t\tmethod: \"PATCH\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ name }),\n\t});\n\tconst data = await parseApiResponse<{ passkey: PasskeyInfo }>(\n\t\tresponse,\n\t\t\"Failed to rename passkey\",\n\t);\n\treturn data.passkey;\n}\n\n/**\n * Delete a passkey\n */\nexport async function deletePasskey(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/auth/passkey/${id}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete passkey\");\n}\n\n// =============================================================================\n// Allowed Domains API (Self-Signup)\n// =============================================================================\n\n/** Allowed domain for self-signup */\nexport interface AllowedDomain {\n\tdomain: string;\n\tdefaultRole: number;\n\troleName: string;\n\tenabled: boolean;\n\tcreatedAt: string;\n}\n\n/** Create allowed domain input */\nexport interface CreateAllowedDomainInput {\n\tdomain: string;\n\tdefaultRole: number;\n}\n\n/** Update allowed domain input */\nexport interface UpdateAllowedDomainInput {\n\tenabled?: boolean;\n\tdefaultRole?: number;\n}\n\n/**\n * Fetch all allowed domains\n */\nexport async function fetchAllowedDomains(): Promise<AllowedDomain[]> {\n\tconst response = await apiFetch(`${API_BASE}/admin/allowed-domains`);\n\tconst data = await parseApiResponse<{ domains: AllowedDomain[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch allowed domains\",\n\t);\n\treturn data.domains;\n}\n\n/**\n * Create an allowed domain\n */\nexport async function createAllowedDomain(input: CreateAllowedDomainInput): Promise<AllowedDomain> {\n\tconst response = await apiFetch(`${API_BASE}/admin/allowed-domains`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ domain: AllowedDomain }>(\n\t\tresponse,\n\t\t\"Failed to create allowed domain\",\n\t);\n\treturn data.domain;\n}\n\n/**\n * Update an allowed domain\n */\nexport async function updateAllowedDomain(\n\tdomain: string,\n\tinput: UpdateAllowedDomainInput,\n): Promise<AllowedDomain> {\n\tconst response = await apiFetch(\n\t\t`${API_BASE}/admin/allowed-domains/${encodeURIComponent(domain)}`,\n\t\t{\n\t\t\tmethod: \"PATCH\",\n\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\tbody: JSON.stringify(input),\n\t\t},\n\t);\n\tconst data = await parseApiResponse<{ domain: AllowedDomain }>(\n\t\tresponse,\n\t\t\"Failed to update allowed domain\",\n\t);\n\treturn data.domain;\n}\n\n/**\n * Delete an allowed domain\n */\nexport async function deleteAllowedDomain(domain: string): Promise<void> {\n\tconst response = await apiFetch(\n\t\t`${API_BASE}/admin/allowed-domains/${encodeURIComponent(domain)}`,\n\t\t{\n\t\t\tmethod: \"DELETE\",\n\t\t},\n\t);\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete allowed domain\");\n}\n\n// =============================================================================\n// Self-Signup API\n// =============================================================================\n\n/** Signup verification result */\nexport interface SignupVerifyResult {\n\temail: string;\n\trole: number;\n\troleName: string;\n}\n\n/**\n * Request signup - send verification email\n * Always returns success to prevent enumeration\n */\nexport async function requestSignup(email: string): Promise<{ success: true; message: string }> {\n\tconst response = await apiFetch(`${API_BASE}/auth/signup/request`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ email }),\n\t});\n\treturn parseApiResponse<{ success: true; message: string }>(response, \"Signup request failed\");\n}\n\n/**\n * Verify signup token\n *\n * Uses custom error handling to preserve error codes for the UI.\n */\nexport async function verifySignupToken(token: string): Promise<SignupVerifyResult> {\n\tconst response = await apiFetch(\n\t\t`${API_BASE}/auth/signup/verify?token=${encodeURIComponent(token)}`,\n\t);\n\n\tif (!response.ok) {\n\t\tconst errorData: unknown = await response.json().catch(() => ({}));\n\t\tlet message = `Token verification failed: ${response.statusText}`;\n\t\tlet code: string | undefined;\n\t\tif (typeof errorData === \"object\" && errorData !== null && \"error\" in errorData) {\n\t\t\tconst err = errorData.error;\n\t\t\tif (typeof err === \"object\" && err !== null) {\n\t\t\t\tif (\"message\" in err && typeof err.message === \"string\") message = err.message;\n\t\t\t\tif (\"code\" in err && typeof err.code === \"string\") code = err.code;\n\t\t\t}\n\t\t}\n\t\tconst error: Error & { code?: string } = new Error(message);\n\t\terror.code = code;\n\t\tthrow error;\n\t}\n\n\treturn parseApiResponse<SignupVerifyResult>(response, \"Token verification failed\");\n}\n\n/**\n * Complete signup with passkey registration\n *\n * Uses custom error handling to preserve error codes for the UI.\n */\nexport async function completeSignup(\n\ttoken: string,\n\tcredential: unknown,\n\tname?: string,\n): Promise<{\n\tsuccess: true;\n\tuser: { id: string; email: string; name: string | null; role: number };\n}> {\n\tconst response = await apiFetch(`${API_BASE}/auth/signup/complete`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ token, credential, name }),\n\t});\n\n\tif (!response.ok) {\n\t\tconst errorData: unknown = await response.json().catch(() => ({}));\n\t\tlet message = `Signup completion failed: ${response.statusText}`;\n\t\tlet code: string | undefined;\n\t\tif (typeof errorData === \"object\" && errorData !== null && \"error\" in errorData) {\n\t\t\tconst err = errorData.error;\n\t\t\tif (typeof err === \"object\" && err !== null) {\n\t\t\t\tif (\"message\" in err && typeof err.message === \"string\") message = err.message;\n\t\t\t\tif (\"code\" in err && typeof err.code === \"string\") code = err.code;\n\t\t\t}\n\t\t}\n\t\tconst error: Error & { code?: string } = new Error(message);\n\t\terror.code = code;\n\t\tthrow error;\n\t}\n\n\treturn parseApiResponse<{\n\t\tsuccess: true;\n\t\tuser: { id: string; email: string; name: string | null; role: number };\n\t}>(response, \"Signup completion failed\");\n}\n\n/**\n * Check if any allowed domains exist (for showing signup link)\n */\nexport async function hasAllowedDomains(): Promise<boolean> {\n\ttry {\n\t\tconst domains = await fetchAllowedDomains();\n\t\treturn domains.some((d) => d.enabled);\n\t} catch {\n\t\t// If we can't fetch (e.g., not logged in), assume no domains\n\t\treturn false;\n\t}\n}\n","import {\n\tAPI_BASE,\n\tapiFetch,\n\tparseApiResponse,\n\tthrowResponseError,\n\ttype FindManyResult,\n} from \"./client.js\";\n\nexport interface BylineSummary {\n\tid: string;\n\tslug: string;\n\tdisplayName: string;\n\tbio: string | null;\n\tavatarMediaId: string | null;\n\twebsiteUrl: string | null;\n\tuserId: string | null;\n\tisGuest: boolean;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface BylineInput {\n\tslug: string;\n\tdisplayName: string;\n\tbio?: string | null;\n\tavatarMediaId?: string | null;\n\twebsiteUrl?: string | null;\n\tuserId?: string | null;\n\tisGuest?: boolean;\n}\n\nexport interface BylineCreditInput {\n\tbylineId: string;\n\troleLabel?: string | null;\n}\n\nexport async function fetchBylines(options?: {\n\tsearch?: string;\n\tisGuest?: boolean;\n\tuserId?: string;\n\tcursor?: string;\n\tlimit?: number;\n}): Promise<FindManyResult<BylineSummary>> {\n\tconst params = new URLSearchParams();\n\tif (options?.search) params.set(\"search\", options.search);\n\tif (options?.isGuest !== undefined) params.set(\"isGuest\", String(options.isGuest));\n\tif (options?.userId) params.set(\"userId\", options.userId);\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\n\tconst url = `${API_BASE}/admin/bylines${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<FindManyResult<BylineSummary>>(response, \"Failed to fetch bylines\");\n}\n\nexport async function fetchByline(id: string): Promise<BylineSummary> {\n\tconst response = await apiFetch(`${API_BASE}/admin/bylines/${id}`);\n\treturn parseApiResponse<BylineSummary>(response, \"Failed to fetch byline\");\n}\n\nexport async function createByline(input: BylineInput): Promise<BylineSummary> {\n\tconst response = await apiFetch(`${API_BASE}/admin/bylines`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<BylineSummary>(response, \"Failed to create byline\");\n}\n\nexport async function updateByline(\n\tid: string,\n\tinput: Partial<BylineInput>,\n): Promise<BylineSummary> {\n\tconst response = await apiFetch(`${API_BASE}/admin/bylines/${id}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<BylineSummary>(response, \"Failed to update byline\");\n}\n\nexport async function deleteByline(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/bylines/${id}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete byline\");\n}\n","/**\n * Menu management APIs\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport interface Menu {\n\tid: string;\n\tname: string;\n\tlabel: string;\n\tcreated_at: string;\n\tupdated_at: string;\n\titemCount?: number;\n}\n\nexport interface MenuItem {\n\tid: string;\n\tmenu_id: string;\n\tparent_id: string | null;\n\tsort_order: number;\n\ttype: string;\n\treference_collection: string | null;\n\treference_id: string | null;\n\tcustom_url: string | null;\n\tlabel: string;\n\ttitle_attr: string | null;\n\ttarget: string | null;\n\tcss_classes: string | null;\n\tcreated_at: string;\n}\n\nexport interface MenuWithItems extends Menu {\n\titems: MenuItem[];\n}\n\nexport interface CreateMenuInput {\n\tname: string;\n\tlabel: string;\n}\n\nexport interface UpdateMenuInput {\n\tlabel?: string;\n}\n\nexport interface CreateMenuItemInput {\n\ttype: string;\n\tlabel: string;\n\treferenceCollection?: string;\n\treferenceId?: string;\n\tcustomUrl?: string;\n\ttarget?: string;\n\ttitleAttr?: string;\n\tcssClasses?: string;\n\tparentId?: string;\n\tsortOrder?: number;\n}\n\nexport interface UpdateMenuItemInput {\n\tlabel?: string;\n\tcustomUrl?: string;\n\ttarget?: string;\n\ttitleAttr?: string;\n\tcssClasses?: string;\n\tparentId?: string | null;\n\tsortOrder?: number;\n}\n\nexport interface ReorderMenuItemsInput {\n\titems: Array<{\n\t\tid: string;\n\t\tparentId: string | null;\n\t\tsortOrder: number;\n\t}>;\n}\n\n/**\n * Fetch all menus\n */\nexport async function fetchMenus(): Promise<Menu[]> {\n\tconst response = await apiFetch(`${API_BASE}/menus`);\n\treturn parseApiResponse<Menu[]>(response, \"Failed to fetch menus\");\n}\n\n/**\n * Fetch a single menu with items\n */\nexport async function fetchMenu(name: string): Promise<MenuWithItems> {\n\tconst response = await apiFetch(`${API_BASE}/menus/${name}`);\n\treturn parseApiResponse<MenuWithItems>(response, \"Failed to fetch menu\");\n}\n\n/**\n * Create a menu\n */\nexport async function createMenu(input: CreateMenuInput): Promise<Menu> {\n\tconst response = await apiFetch(`${API_BASE}/menus`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Menu>(response, \"Failed to create menu\");\n}\n\n/**\n * Update a menu\n */\nexport async function updateMenu(name: string, input: UpdateMenuInput): Promise<Menu> {\n\tconst response = await apiFetch(`${API_BASE}/menus/${name}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Menu>(response, \"Failed to update menu\");\n}\n\n/**\n * Delete a menu\n */\nexport async function deleteMenu(name: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/menus/${name}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete menu\");\n}\n\n/**\n * Create a menu item\n */\nexport async function createMenuItem(\n\tmenuName: string,\n\tinput: CreateMenuItemInput,\n): Promise<MenuItem> {\n\tconst response = await apiFetch(`${API_BASE}/menus/${menuName}/items`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<MenuItem>(response, \"Failed to create menu item\");\n}\n\n/**\n * Update a menu item\n */\nexport async function updateMenuItem(\n\tmenuName: string,\n\titemId: string,\n\tinput: UpdateMenuItemInput,\n): Promise<MenuItem> {\n\tconst response = await apiFetch(`${API_BASE}/menus/${menuName}/items?id=${itemId}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<MenuItem>(response, \"Failed to update menu item\");\n}\n\n/**\n * Delete a menu item\n */\nexport async function deleteMenuItem(menuName: string, itemId: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/menus/${menuName}/items?id=${itemId}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete menu item\");\n}\n\n/**\n * Reorder menu items\n */\nexport async function reorderMenuItems(\n\tmenuName: string,\n\tinput: ReorderMenuItemsInput,\n): Promise<MenuItem[]> {\n\tconst response = await apiFetch(`${API_BASE}/menus/${menuName}/reorder`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<MenuItem[]>(response, \"Failed to reorder menu items\");\n}\n","/**\n * Widget areas APIs\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport interface WidgetArea {\n\tid: string;\n\tname: string;\n\tlabel: string;\n\tdescription?: string;\n\twidgets?: Widget[];\n\twidgetCount?: number;\n}\n\nexport interface Widget {\n\tid: string;\n\ttype: \"content\" | \"menu\" | \"component\";\n\ttitle?: string;\n\tcontent?: unknown[]; // Portable Text\n\tmenuName?: string;\n\tcomponentId?: string;\n\tcomponentProps?: Record<string, unknown>;\n\tsort_order?: number;\n}\n\nexport interface WidgetComponent {\n\tid: string;\n\tlabel: string;\n\tdescription?: string;\n\tprops: Record<\n\t\tstring,\n\t\t{\n\t\t\ttype: \"string\" | \"number\" | \"boolean\" | \"select\";\n\t\t\tlabel: string;\n\t\t\tdefault?: unknown;\n\t\t\toptions?: Array<{ value: string; label: string }>;\n\t\t}\n\t>;\n}\n\nexport interface CreateWidgetAreaInput {\n\tname: string;\n\tlabel: string;\n\tdescription?: string;\n}\n\nexport interface CreateWidgetInput {\n\ttype: \"content\" | \"menu\" | \"component\";\n\ttitle?: string;\n\tcontent?: unknown[];\n\tmenuName?: string;\n\tcomponentId?: string;\n\tcomponentProps?: Record<string, unknown>;\n}\n\nexport interface UpdateWidgetInput {\n\ttype?: \"content\" | \"menu\" | \"component\";\n\ttitle?: string;\n\tcontent?: unknown[];\n\tmenuName?: string;\n\tcomponentId?: string;\n\tcomponentProps?: Record<string, unknown>;\n}\n\n/**\n * Fetch all widget areas\n */\nexport async function fetchWidgetAreas(): Promise<WidgetArea[]> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas`);\n\tconst data = await parseApiResponse<{ items: WidgetArea[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch widget areas\",\n\t);\n\treturn data.items;\n}\n\n/**\n * Fetch a single widget area by name\n */\nexport async function fetchWidgetArea(name: string): Promise<WidgetArea> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas/${name}`);\n\treturn parseApiResponse<WidgetArea>(response, \"Failed to fetch widget area\");\n}\n\n/**\n * Create a widget area\n */\nexport async function createWidgetArea(input: CreateWidgetAreaInput): Promise<WidgetArea> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<WidgetArea>(response, \"Failed to create widget area\");\n}\n\n/**\n * Delete a widget area\n */\nexport async function deleteWidgetArea(name: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas/${name}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete widget area\");\n}\n\n/**\n * Add a widget to an area\n */\nexport async function createWidget(areaName: string, input: CreateWidgetInput): Promise<Widget> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas/${areaName}/widgets`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Widget>(response, \"Failed to create widget\");\n}\n\n/**\n * Update a widget\n */\nexport async function updateWidget(\n\tareaName: string,\n\twidgetId: string,\n\tinput: UpdateWidgetInput,\n): Promise<Widget> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas/${areaName}/widgets/${widgetId}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Widget>(response, \"Failed to update widget\");\n}\n\n/**\n * Delete a widget\n */\nexport async function deleteWidget(areaName: string, widgetId: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas/${areaName}/widgets/${widgetId}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete widget\");\n}\n\n/**\n * Reorder widgets in an area\n */\nexport async function reorderWidgets(areaName: string, widgetIds: string[]): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/widget-areas/${areaName}/reorder`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ widgetIds }),\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to reorder widgets\");\n}\n\n/**\n * Fetch available widget components\n */\nexport async function fetchWidgetComponents(): Promise<WidgetComponent[]> {\n\tconst response = await apiFetch(`${API_BASE}/widget-components`);\n\tconst data = await parseApiResponse<{ items: WidgetComponent[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch widget components\",\n\t);\n\treturn data.items;\n}\n","/**\n * Sections API (reusable content blocks)\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport type SectionSource = \"theme\" | \"user\" | \"import\";\n\nexport interface Section {\n\tid: string;\n\tslug: string;\n\ttitle: string;\n\tdescription?: string;\n\tkeywords: string[];\n\tcontent: unknown[]; // Portable Text\n\tpreviewUrl?: string;\n\tsource: SectionSource;\n\tthemeId?: string;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface CreateSectionInput {\n\tslug: string;\n\ttitle: string;\n\tdescription?: string;\n\tkeywords?: string[];\n\tcontent: unknown[];\n\tpreviewMediaId?: string;\n}\n\nexport interface UpdateSectionInput {\n\tslug?: string;\n\ttitle?: string;\n\tdescription?: string;\n\tkeywords?: string[];\n\tcontent?: unknown[];\n\tpreviewMediaId?: string | null;\n}\n\nexport interface GetSectionsOptions {\n\tsource?: SectionSource;\n\tsearch?: string;\n\tlimit?: number;\n\tcursor?: string;\n}\n\nexport interface SectionsResult {\n\titems: Section[];\n\tnextCursor?: string;\n}\n\n/**\n * Fetch all sections\n */\nexport async function fetchSections(options?: GetSectionsOptions): Promise<SectionsResult> {\n\tconst params = new URLSearchParams();\n\tif (options?.source) params.set(\"source\", options.source);\n\tif (options?.search) params.set(\"search\", options.search);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\n\tconst url = params.toString() ? `${API_BASE}/sections?${params}` : `${API_BASE}/sections`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<SectionsResult>(response, \"Failed to fetch sections\");\n}\n\n/**\n * Fetch a single section by slug\n */\nexport async function fetchSection(slug: string): Promise<Section> {\n\tconst response = await apiFetch(`${API_BASE}/sections/${slug}`);\n\treturn parseApiResponse<Section>(response, \"Failed to fetch section\");\n}\n\n/**\n * Create a section\n */\nexport async function createSection(input: CreateSectionInput): Promise<Section> {\n\tconst response = await apiFetch(`${API_BASE}/sections`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Section>(response, \"Failed to create section\");\n}\n\n/**\n * Update a section\n */\nexport async function updateSection(slug: string, input: UpdateSectionInput): Promise<Section> {\n\tconst response = await apiFetch(`${API_BASE}/sections/${slug}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Section>(response, \"Failed to update section\");\n}\n\n/**\n * Delete a section\n */\nexport async function deleteSection(slug: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/sections/${slug}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete section\");\n}\n","/**\n * Taxonomies API (categories, tags, custom taxonomies)\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport interface TaxonomyTerm {\n\tid: string;\n\tname: string;\n\tslug: string;\n\tlabel: string;\n\tparentId?: string;\n\tdescription?: string;\n\tchildren: TaxonomyTerm[];\n\tcount?: number;\n}\n\nexport interface TaxonomyDef {\n\tid: string;\n\tname: string;\n\tlabel: string;\n\tlabelSingular?: string;\n\thierarchical: boolean;\n\tcollections: string[];\n}\n\nexport interface CreateTaxonomyInput {\n\tname: string;\n\tlabel: string;\n\thierarchical?: boolean;\n\tcollections?: string[];\n}\n\nexport interface CreateTermInput {\n\tslug: string;\n\tlabel: string;\n\tparentId?: string;\n\tdescription?: string;\n}\n\nexport interface UpdateTermInput {\n\tslug?: string;\n\tlabel?: string;\n\tparentId?: string;\n\tdescription?: string;\n}\n\n/**\n * Fetch all taxonomy definitions\n */\nexport async function fetchTaxonomyDefs(): Promise<TaxonomyDef[]> {\n\tconst response = await apiFetch(`${API_BASE}/taxonomies`);\n\tconst data = await parseApiResponse<{ taxonomies: TaxonomyDef[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch taxonomies\",\n\t);\n\treturn data.taxonomies;\n}\n\n/**\n * Fetch taxonomy definition by name\n */\nexport async function fetchTaxonomyDef(name: string): Promise<TaxonomyDef | null> {\n\tconst defs = await fetchTaxonomyDefs();\n\treturn defs.find((t) => t.name === name) || null;\n}\n\n/**\n * Create a custom taxonomy definition\n */\nexport async function createTaxonomy(input: CreateTaxonomyInput): Promise<TaxonomyDef> {\n\tconst response = await apiFetch(`${API_BASE}/taxonomies`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ taxonomy: TaxonomyDef }>(\n\t\tresponse,\n\t\t\"Failed to create taxonomy\",\n\t);\n\treturn data.taxonomy;\n}\n\n/**\n * Fetch terms for a taxonomy\n */\nexport async function fetchTerms(taxonomyName: string): Promise<TaxonomyTerm[]> {\n\tconst response = await apiFetch(`${API_BASE}/taxonomies/${taxonomyName}/terms`);\n\tconst data = await parseApiResponse<{ terms: TaxonomyTerm[] }>(response, \"Failed to fetch terms\");\n\treturn data.terms;\n}\n\n/**\n * Create a term\n */\nexport async function createTerm(\n\ttaxonomyName: string,\n\tinput: CreateTermInput,\n): Promise<TaxonomyTerm> {\n\tconst response = await apiFetch(`${API_BASE}/taxonomies/${taxonomyName}/terms`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ term: TaxonomyTerm }>(response, \"Failed to create term\");\n\treturn data.term;\n}\n\n/**\n * Update a term\n */\nexport async function updateTerm(\n\ttaxonomyName: string,\n\tslug: string,\n\tinput: UpdateTermInput,\n): Promise<TaxonomyTerm> {\n\tconst response = await apiFetch(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\tconst data = await parseApiResponse<{ term: TaxonomyTerm }>(response, \"Failed to update term\");\n\treturn data.term;\n}\n\n/**\n * Delete a term\n */\nexport async function deleteTerm(taxonomyName: string, slug: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/taxonomies/${taxonomyName}/terms/${slug}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete term\");\n}\n","/**\n * WordPress import and source probing APIs\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\n// =============================================================================\n// WordPress Import API\n// =============================================================================\n\n/** Field compatibility status */\nexport type FieldCompatibility =\n\t| \"compatible\" // Field exists with compatible type\n\t| \"type_mismatch\" // Field exists but type differs\n\t| \"missing\"; // Field doesn't exist\n\n/** Single field definition for import */\nexport interface ImportFieldDef {\n\tslug: string;\n\tlabel: string;\n\ttype: string;\n\trequired: boolean;\n}\n\n/** Schema status for a collection */\nexport interface CollectionSchemaStatus {\n\texists: boolean;\n\tfieldStatus: Record<\n\t\tstring,\n\t\t{\n\t\t\tstatus: FieldCompatibility;\n\t\t\texistingType?: string;\n\t\t\trequiredType: string;\n\t\t}\n\t>;\n\tcanImport: boolean;\n\treason?: string;\n}\n\n/** Post type with full schema info */\nexport interface PostTypeAnalysis {\n\tname: string;\n\tcount: number;\n\tsuggestedCollection: string;\n\trequiredFields: ImportFieldDef[];\n\tschemaStatus: CollectionSchemaStatus;\n}\n\n/** Individual attachment info for media import */\nexport interface AttachmentInfo {\n\tid?: number;\n\ttitle?: string;\n\turl?: string;\n\tfilename?: string;\n\tmimeType?: string;\n}\n\n/** Navigation menu from WordPress */\nexport interface NavMenu {\n\tname: string;\n\tslug: string;\n\tcount: number;\n}\n\n/** Custom taxonomy from WordPress */\nexport interface CustomTaxonomy {\n\tname: string;\n\tslug: string;\n\tcount: number;\n\thierarchical: boolean;\n}\n\n/** Author info from WordPress */\nexport interface WpAuthorInfo {\n\tid?: number;\n\tlogin?: string;\n\temail?: string;\n\tdisplayName?: string;\n\tpostCount: number;\n}\n\nexport interface WxrAnalysis {\n\tsite: {\n\t\ttitle: string;\n\t\turl: string;\n\t};\n\tpostTypes: PostTypeAnalysis[];\n\tattachments: {\n\t\tcount: number;\n\t\titems: AttachmentInfo[];\n\t};\n\tcategories: number;\n\ttags: number;\n\tauthors: WpAuthorInfo[];\n\tcustomFields: Array<{\n\t\tkey: string;\n\t\tcount: number;\n\t\tsamples: string[];\n\t\tsuggestedField: string;\n\t\tsuggestedType: string;\n\t\tisInternal: boolean;\n\t}>;\n\t/** Navigation menus found in the export */\n\tnavMenus?: NavMenu[];\n\t/** Custom taxonomies found in the export */\n\tcustomTaxonomies?: CustomTaxonomy[];\n}\n\nexport interface PrepareRequest {\n\tpostTypes: Array<{\n\t\tname: string;\n\t\tcollection: string;\n\t\tfields: ImportFieldDef[];\n\t}>;\n}\n\nexport interface PrepareResult {\n\tsuccess: boolean;\n\tcollectionsCreated: string[];\n\tfieldsCreated: Array<{ collection: string; field: string }>;\n\terrors: Array<{ collection: string; error: string }>;\n}\n\n/** Author mapping from WP author login to EmDash user ID */\nexport interface AuthorMapping {\n\t/** WordPress author login */\n\twpLogin: string;\n\t/** WordPress author display name (for UI) */\n\twpDisplayName: string;\n\t/** WordPress author email (for matching) */\n\twpEmail?: string;\n\t/** EmDash user ID to assign (null = leave unassigned) */\n\temdashUserId: string | null;\n\t/** Number of posts by this author */\n\tpostCount: number;\n}\n\nexport interface ImportConfig {\n\tpostTypeMappings: Record<\n\t\tstring,\n\t\t{\n\t\t\tcollection: string;\n\t\t\tenabled: boolean;\n\t\t}\n\t>;\n\tskipExisting: boolean;\n\t/** Author mappings (WP author login -> EmDash user ID) */\n\tauthorMappings?: Record<string, string | null>;\n}\n\nexport interface ImportResult {\n\tsuccess: boolean;\n\timported: number;\n\tskipped: number;\n\terrors: Array<{ title: string; error: string }>;\n\tbyCollection: Record<string, number>;\n}\n\n/**\n * Analyze a WordPress WXR file\n */\nexport async function analyzeWxr(file: File): Promise<WxrAnalysis> {\n\tconst formData = new FormData();\n\tformData.append(\"file\", file);\n\n\tconst response = await apiFetch(`${API_BASE}/import/wordpress/analyze`, {\n\t\tmethod: \"POST\",\n\t\tbody: formData,\n\t});\n\treturn parseApiResponse<WxrAnalysis>(response, \"Failed to analyze file\");\n}\n\n/**\n * Prepare WordPress import (create collections/fields)\n */\nexport async function prepareWxrImport(request: PrepareRequest): Promise<PrepareResult> {\n\tconst response = await apiFetch(`${API_BASE}/import/wordpress/prepare`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(request),\n\t});\n\treturn parseApiResponse<PrepareResult>(response, \"Failed to prepare import\");\n}\n\n/**\n * Execute WordPress import\n */\nexport async function executeWxrImport(file: File, config: ImportConfig): Promise<ImportResult> {\n\tconst formData = new FormData();\n\tformData.append(\"file\", file);\n\tformData.append(\"config\", JSON.stringify(config));\n\n\tconst response = await apiFetch(`${API_BASE}/import/wordpress/execute`, {\n\t\tmethod: \"POST\",\n\t\tbody: formData,\n\t});\n\treturn parseApiResponse<ImportResult>(response, \"Failed to import\");\n}\n\n// =============================================================================\n// Media Import API\n// =============================================================================\n\nexport interface MediaImportResult {\n\timported: Array<{\n\t\twpId?: number;\n\t\toriginalUrl: string;\n\t\tnewUrl: string;\n\t\tmediaId: string;\n\t}>;\n\tfailed: Array<{\n\t\twpId?: number;\n\t\toriginalUrl: string;\n\t\terror: string;\n\t}>;\n\turlMap: Record<string, string>;\n}\n\n/** Progress update sent during streaming media import */\nexport interface MediaImportProgress {\n\ttype: \"progress\";\n\tcurrent: number;\n\ttotal: number;\n\tfilename?: string;\n\tstatus: \"downloading\" | \"uploading\" | \"done\" | \"skipped\" | \"failed\";\n\terror?: string;\n}\n\nexport interface RewriteUrlsResult {\n\tupdated: number;\n\tbyCollection: Record<string, number>;\n\turlsRewritten: number;\n\terrors: Array<{ collection: string; id: string; error: string }>;\n}\n\n/**\n * Import media from WordPress with streaming progress\n *\n * @param attachments - Array of attachments to import\n * @param onProgress - Callback for progress updates (optional)\n * @returns Final import result\n */\nexport async function importWxrMedia(\n\tattachments: AttachmentInfo[],\n\tonProgress?: (progress: MediaImportProgress) => void,\n): Promise<MediaImportResult> {\n\tconst response = await apiFetch(`${API_BASE}/import/wordpress/media`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ attachments, stream: !!onProgress }),\n\t});\n\n\tif (!response.ok) await throwResponseError(response, \"Failed to import media\");\n\n\t// If no progress callback, just parse as JSON (non-streaming mode)\n\t// Note: streaming NDJSON responses are excluded from the { data } envelope\n\tif (!onProgress) {\n\t\treturn parseApiResponse<MediaImportResult>(response, \"Failed to import media\");\n\t}\n\n\t// Streaming mode: read NDJSON line by line\n\tconst reader = response.body?.getReader();\n\tif (!reader) {\n\t\tthrow new Error(\"Response body is not readable\");\n\t}\n\n\tconst decoder = new TextDecoder();\n\tlet buffer = \"\";\n\tlet result: MediaImportResult | null = null;\n\n\twhile (true) {\n\t\tconst { done, value } = await reader.read();\n\n\t\tif (done) break;\n\n\t\tbuffer += decoder.decode(value, { stream: true });\n\n\t\t// Process complete lines\n\t\tconst lines = buffer.split(\"\\n\");\n\t\tbuffer = lines.pop() || \"\"; // Keep incomplete line in buffer\n\n\t\tfor (const line of lines) {\n\t\t\tif (!line.trim()) continue;\n\n\t\t\ttry {\n\t\t\t\tconst parsed: { type?: string; imported?: unknown } = JSON.parse(line);\n\t\t\t\tif (parsed.type === \"progress\") {\n\t\t\t\t\t// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- SSE event data is parsed JSON; discriminated by type === \"progress\"\n\t\t\t\t\tonProgress(parsed as MediaImportProgress);\n\t\t\t\t} else if (parsed.type === \"result\" || parsed.imported) {\n\t\t\t\t\t// Final result (has type: \"result\" or is the result object)\n\t\t\t\t\t// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- SSE event data is parsed JSON; discriminated by type === \"result\"\n\t\t\t\t\tresult = parsed as MediaImportResult;\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Ignore parse errors for incomplete JSON\n\t\t\t\tconsole.warn(\"Failed to parse NDJSON line:\", line);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Process any remaining data in buffer\n\tif (buffer.trim()) {\n\t\ttry {\n\t\t\tconst parsed: { type?: string; imported?: unknown } = JSON.parse(buffer);\n\t\t\tif (parsed.type === \"result\" || parsed.imported) {\n\t\t\t\t// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- SSE event data is parsed JSON; discriminated by type === \"result\"\n\t\t\t\tresult = parsed as MediaImportResult;\n\t\t\t}\n\t\t} catch {\n\t\t\tconsole.warn(\"Failed to parse final NDJSON:\", buffer);\n\t\t}\n\t}\n\n\tif (!result) {\n\t\tthrow new Error(\"No result received from media import\");\n\t}\n\n\treturn result;\n}\n\n// =============================================================================\n// Import Source Probing\n// =============================================================================\n\n/** Capabilities of an import source */\nexport interface SourceCapabilities {\n\tpublicContent: boolean;\n\tprivateContent: boolean;\n\tcustomPostTypes: boolean;\n\tallMeta: boolean;\n\tmediaStream: boolean;\n}\n\n/** Auth requirements for import */\nexport interface SourceAuth {\n\ttype: \"oauth\" | \"token\" | \"password\" | \"none\";\n\tprovider?: string;\n\toauthUrl?: string;\n\tinstructions?: string;\n}\n\n/** Suggested action after probing */\nexport type SuggestedAction =\n\t| { type: \"proceed\" }\n\t| { type: \"oauth\"; url: string; provider: string }\n\t| { type: \"upload\"; instructions: string }\n\t| { type: \"install-plugin\"; instructions: string };\n\n/** Result from probing a single source */\nexport interface SourceProbeResult {\n\tsourceId: string;\n\tconfidence: \"definite\" | \"likely\" | \"possible\";\n\tdetected: {\n\t\tplatform: string;\n\t\tversion?: string;\n\t\tsiteTitle?: string;\n\t\tsiteUrl?: string;\n\t};\n\tcapabilities: SourceCapabilities;\n\tauth?: SourceAuth;\n\tsuggestedAction: SuggestedAction;\n\tpreview?: {\n\t\tposts?: number;\n\t\tpages?: number;\n\t\tmedia?: number;\n\t};\n}\n\n/** Combined probe result */\nexport interface ProbeResult {\n\turl: string;\n\tisWordPress: boolean;\n\tbestMatch: SourceProbeResult | null;\n\tallMatches: SourceProbeResult[];\n}\n\n/**\n * Probe a URL to detect import source\n */\nexport async function probeImportUrl(url: string): Promise<ProbeResult> {\n\tconst response = await apiFetch(`${API_BASE}/import/probe`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ url }),\n\t});\n\tconst data = await parseApiResponse<{ result: ProbeResult }>(response, \"Failed to probe URL\");\n\treturn data.result;\n}\n\n/**\n * Rewrite URLs in content after media import\n */\nexport async function rewriteContentUrls(\n\turlMap: Record<string, string>,\n\tcollections?: string[],\n): Promise<RewriteUrlsResult> {\n\tconst response = await apiFetch(`${API_BASE}/import/wordpress/rewrite-urls`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ urlMap, collections }),\n\t});\n\treturn parseApiResponse<RewriteUrlsResult>(response, \"Failed to rewrite URLs\");\n}\n\n// =============================================================================\n// WordPress Plugin Direct Import API\n// =============================================================================\n\n/** WordPress Plugin analysis result */\nexport interface WpPluginAnalysis {\n\tsourceId: string;\n\tsite: {\n\t\ttitle: string;\n\t\turl: string;\n\t};\n\tpostTypes: PostTypeAnalysis[];\n\tattachments: {\n\t\tcount: number;\n\t\titems: AttachmentInfo[];\n\t};\n\tcategories: number;\n\ttags: number;\n\tauthors: WpAuthorInfo[];\n\t/** Navigation menus found via the plugin */\n\tnavMenus?: NavMenu[];\n\t/** Custom taxonomies found via the plugin */\n\tcustomTaxonomies?: CustomTaxonomy[];\n}\n\n/**\n * Analyze a WordPress site with EmDash Exporter plugin\n */\nexport async function analyzeWpPluginSite(url: string, token: string): Promise<WpPluginAnalysis> {\n\tconst response = await apiFetch(`${API_BASE}/import/wordpress-plugin/analyze`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ url, token }),\n\t});\n\tconst data = await parseApiResponse<{ analysis: WpPluginAnalysis }>(\n\t\tresponse,\n\t\t\"Failed to analyze WordPress site\",\n\t);\n\treturn data.analysis;\n}\n\n/**\n * Execute import from WordPress plugin API\n */\nexport async function executeWpPluginImport(\n\turl: string,\n\ttoken: string,\n\tconfig: ImportConfig,\n): Promise<ImportResult> {\n\tconst response = await apiFetch(`${API_BASE}/import/wordpress-plugin/execute`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ url, token, config }),\n\t});\n\tconst data = await parseApiResponse<{ result: ImportResult }>(\n\t\tresponse,\n\t\t\"Failed to import from WordPress\",\n\t);\n\treturn data.result;\n}\n","/**\n * API token management client functions\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\n/** API token info returned from the server */\nexport interface ApiTokenInfo {\n\tid: string;\n\tname: string;\n\tprefix: string;\n\tscopes: string[];\n\tuserId: string;\n\texpiresAt: string | null;\n\tlastUsedAt: string | null;\n\tcreatedAt: string;\n}\n\n/** Result from creating a new token */\nexport interface ApiTokenCreateResult {\n\t/** Raw token — shown once, never stored */\n\ttoken: string;\n\t/** Token metadata */\n\tinfo: ApiTokenInfo;\n}\n\n/** Input for creating a new token */\nexport interface CreateApiTokenInput {\n\tname: string;\n\tscopes: string[];\n\texpiresAt?: string;\n}\n\n/** Available scopes for API tokens */\nexport const API_TOKEN_SCOPES = [\n\t{ value: \"content:read\", label: \"Content Read\", description: \"Read content entries\" },\n\t{ value: \"content:write\", label: \"Content Write\", description: \"Create, update, delete content\" },\n\t{ value: \"media:read\", label: \"Media Read\", description: \"Read media files\" },\n\t{ value: \"media:write\", label: \"Media Write\", description: \"Upload and delete media\" },\n\t{ value: \"schema:read\", label: \"Schema Read\", description: \"Read collection schemas\" },\n\t{ value: \"schema:write\", label: \"Schema Write\", description: \"Modify collection schemas\" },\n\t{ value: \"admin\", label: \"Admin\", description: \"Full admin access\" },\n] as const;\n\n// =============================================================================\n// API Functions\n// =============================================================================\n\n/**\n * Fetch all API tokens for the current user\n */\nexport async function fetchApiTokens(): Promise<ApiTokenInfo[]> {\n\tconst response = await apiFetch(`${API_BASE}/admin/api-tokens`);\n\tconst result = await parseApiResponse<{ items: ApiTokenInfo[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch API tokens\",\n\t);\n\treturn result.items;\n}\n\n/**\n * Create a new API token\n */\nexport async function createApiToken(input: CreateApiTokenInput): Promise<ApiTokenCreateResult> {\n\tconst response = await apiFetch(`${API_BASE}/admin/api-tokens`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\n\treturn parseApiResponse<ApiTokenCreateResult>(response, \"Failed to create API token\");\n}\n\n/**\n * Revoke (delete) an API token\n */\nexport async function revokeApiToken(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/api-tokens/${id}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\n\tif (!response.ok) await throwResponseError(response, \"Failed to revoke API token\");\n}\n","/**\n * Comment moderation API client\n */\n\nimport {\n\tAPI_BASE,\n\tapiFetch,\n\tparseApiResponse,\n\tthrowResponseError,\n\ttype FindManyResult,\n} from \"./client.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CommentStatus = \"pending\" | \"approved\" | \"spam\" | \"trash\";\n\nexport interface AdminComment {\n\tid: string;\n\tcollection: string;\n\tcontentId: string;\n\tparentId: string | null;\n\tauthorName: string;\n\tauthorEmail: string;\n\tauthorUserId: string | null;\n\tbody: string;\n\tstatus: CommentStatus;\n\tipHash: string | null;\n\tuserAgent: string | null;\n\tmoderationMetadata: Record<string, unknown> | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport type CommentCounts = Record<CommentStatus, number>;\n\nexport type BulkAction = \"approve\" | \"spam\" | \"trash\" | \"delete\";\n\n// ---------------------------------------------------------------------------\n// Queries\n// ---------------------------------------------------------------------------\n\n/**\n * Fetch comments for the moderation inbox\n */\nexport async function fetchComments(options?: {\n\tstatus?: CommentStatus;\n\tcollection?: string;\n\tsearch?: string;\n\tlimit?: number;\n\tcursor?: string;\n}): Promise<FindManyResult<AdminComment>> {\n\tconst params = new URLSearchParams();\n\tif (options?.status) params.set(\"status\", options.status);\n\tif (options?.collection) params.set(\"collection\", options.collection);\n\tif (options?.search) params.set(\"search\", options.search);\n\tif (options?.limit) params.set(\"limit\", String(options.limit));\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\n\tconst url = `${API_BASE}/admin/comments${params.toString() ? `?${params}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<FindManyResult<AdminComment>>(response, \"Failed to fetch comments\");\n}\n\n/**\n * Fetch comment status counts for inbox badges\n */\nexport async function fetchCommentCounts(): Promise<CommentCounts> {\n\tconst response = await apiFetch(`${API_BASE}/admin/comments/counts`);\n\treturn parseApiResponse<CommentCounts>(response, \"Failed to fetch comment counts\");\n}\n\n/**\n * Fetch a single comment by ID\n */\nexport async function fetchComment(id: string): Promise<AdminComment> {\n\tconst response = await apiFetch(`${API_BASE}/admin/comments/${id}`);\n\treturn parseApiResponse<AdminComment>(response, \"Failed to fetch comment\");\n}\n\n// ---------------------------------------------------------------------------\n// Mutations\n// ---------------------------------------------------------------------------\n\n/**\n * Update a comment's status\n */\nexport async function updateCommentStatus(\n\tid: string,\n\tstatus: CommentStatus,\n): Promise<AdminComment> {\n\tconst response = await apiFetch(`${API_BASE}/admin/comments/${id}/status`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ status }),\n\t});\n\treturn parseApiResponse<AdminComment>(response, \"Failed to update comment status\");\n}\n\n/**\n * Hard delete a comment (ADMIN only)\n */\nexport async function deleteComment(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/comments/${id}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete comment\");\n}\n\n/**\n * Bulk status change or delete\n */\nexport async function bulkCommentAction(\n\tids: string[],\n\taction: BulkAction,\n): Promise<{ affected: number }> {\n\tconst response = await apiFetch(`${API_BASE}/admin/comments/bulk`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ ids, action }),\n\t});\n\treturn parseApiResponse<{ affected: number }>(response, \"Failed to perform bulk action\");\n}\n","/**\n * Dashboard stats API\n */\n\nimport { API_BASE, apiFetch, parseApiResponse } from \"./client.js\";\n\nexport interface CollectionStats {\n\tslug: string;\n\tlabel: string;\n\ttotal: number;\n\tpublished: number;\n\tdraft: number;\n}\n\nexport interface RecentItem {\n\tid: string;\n\tcollection: string;\n\tcollectionLabel: string;\n\ttitle: string;\n\tslug: string | null;\n\tstatus: string;\n\tupdatedAt: string;\n\tauthorId: string | null;\n}\n\nexport interface DashboardStats {\n\tcollections: CollectionStats[];\n\tmediaCount: number;\n\tuserCount: number;\n\trecentItems: RecentItem[];\n}\n\n/**\n * Fetch dashboard statistics\n */\nexport async function fetchDashboardStats(): Promise<DashboardStats> {\n\tconst response = await apiFetch(`${API_BASE}/dashboard`);\n\treturn parseApiResponse<DashboardStats>(response, \"Failed to fetch dashboard stats\");\n}\n","/**\n * Search enable/disable APIs\n */\n\nimport { API_BASE, apiFetch, parseApiResponse } from \"./client.js\";\n\nexport interface SearchEnableResult {\n\tsuccess: boolean;\n\tcollection: string;\n\tenabled: boolean;\n\tindexed?: number;\n}\n\n/**\n * Enable or disable search for a collection\n */\nexport async function setSearchEnabled(\n\tcollection: string,\n\tenabled: boolean,\n\tweights?: Record<string, number>,\n): Promise<SearchEnableResult> {\n\tconst response = await apiFetch(`${API_BASE}/search/enable`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ collection, enabled, weights }),\n\t});\n\treturn parseApiResponse<SearchEnableResult>(\n\t\tresponse,\n\t\t`Failed to ${enabled ? \"enable\" : \"disable\"} search`,\n\t);\n}\n","/**\n * Marketplace API client\n *\n * Calls the site-side proxy endpoints (/_emdash/api/admin/plugins/marketplace/*)\n * which forward to the marketplace Worker. This avoids CORS issues since the\n * admin UI doesn't need to know the marketplace URL.\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\n// ---------------------------------------------------------------------------\n// Types — matches the marketplace REST API response shapes\n// ---------------------------------------------------------------------------\n\nexport interface MarketplaceAuthor {\n\tname: string;\n\tverified: boolean;\n}\n\nexport interface MarketplaceAuditSummary {\n\tverdict: \"pass\" | \"warn\" | \"fail\";\n\triskScore: number;\n}\n\nexport interface MarketplaceImageAuditSummary {\n\tverdict: \"pass\" | \"warn\" | \"fail\";\n}\n\nexport interface MarketplaceVersion {\n\tversion: string;\n\tminEmDashVersion?: string;\n\tbundleSize: number;\n\tchangelog?: string;\n\treadme?: string;\n\tscreenshotUrls?: string[];\n\taudit?: MarketplaceAuditSummary;\n\timageAudit?: MarketplaceImageAuditSummary;\n\tpublishedAt: string;\n}\n\n/** Summary shown in browse cards */\nexport interface MarketplacePluginSummary {\n\tid: string;\n\tname: string;\n\tdescription?: string;\n\tauthor: MarketplaceAuthor;\n\tcapabilities: string[];\n\tkeywords?: string[];\n\tinstallCount: number;\n\ticonUrl?: string;\n\tlatestVersion?: {\n\t\tversion: string;\n\t\taudit?: MarketplaceAuditSummary;\n\t\timageAudit?: MarketplaceImageAuditSummary;\n\t};\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\n/** Full detail returned by GET /plugins/:id */\nexport interface MarketplacePluginDetail extends MarketplacePluginSummary {\n\tlicense?: string;\n\trepositoryUrl?: string;\n\thomepageUrl?: string;\n\tlatestVersion?: MarketplaceVersion;\n}\n\nexport interface MarketplaceSearchResult {\n\titems: MarketplacePluginSummary[];\n\tnextCursor?: string;\n}\n\nexport interface MarketplaceSearchOpts {\n\tq?: string;\n\tcapability?: string;\n\tsort?: \"installs\" | \"updated\" | \"created\" | \"name\";\n\tcursor?: string;\n\tlimit?: number;\n}\n\n/** Update check result per plugin */\nexport interface PluginUpdateInfo {\n\tpluginId: string;\n\tinstalled: string;\n\tlatest: string;\n\thasCapabilityChanges: boolean;\n}\n\n/** Install request body */\nexport interface InstallPluginOpts {\n\tversion?: string;\n}\n\n/** Update request body */\nexport interface UpdatePluginOpts {\n\t/** User has confirmed new capabilities */\n\tconfirmCapabilities?: boolean;\n}\n\n/** Uninstall request body */\nexport interface UninstallPluginOpts {\n\t/** Delete plugin storage data */\n\tdeleteData?: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// API functions — proxy through site endpoints\n// ---------------------------------------------------------------------------\n\nconst MARKETPLACE_BASE = `${API_BASE}/admin/plugins/marketplace`;\n\n/**\n * Search the marketplace catalog.\n * Proxied through /_emdash/api/admin/plugins/marketplace\n */\nexport async function searchMarketplace(\n\topts: MarketplaceSearchOpts = {},\n): Promise<MarketplaceSearchResult> {\n\tconst params = new URLSearchParams();\n\tif (opts.q) params.set(\"q\", opts.q);\n\tif (opts.capability) params.set(\"capability\", opts.capability);\n\tif (opts.sort) params.set(\"sort\", opts.sort);\n\tif (opts.cursor) params.set(\"cursor\", opts.cursor);\n\tif (opts.limit) params.set(\"limit\", String(opts.limit));\n\n\tconst qs = params.toString();\n\tconst url = `${MARKETPLACE_BASE}${qs ? `?${qs}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<MarketplaceSearchResult>(response, \"Marketplace search failed\");\n}\n\n/**\n * Get full plugin detail.\n * Proxied through /_emdash/api/admin/plugins/marketplace/:id\n */\nexport async function fetchMarketplacePlugin(id: string): Promise<MarketplacePluginDetail> {\n\tconst response = await apiFetch(`${MARKETPLACE_BASE}/${encodeURIComponent(id)}`);\n\tif (response.status === 404) {\n\t\tthrow new Error(`Plugin \"${id}\" not found in marketplace`);\n\t}\n\treturn parseApiResponse<MarketplacePluginDetail>(response, \"Failed to fetch plugin\");\n}\n\n/**\n * Install a plugin from the marketplace.\n * POST /_emdash/api/admin/plugins/marketplace/:id/install\n */\nexport async function installMarketplacePlugin(\n\tid: string,\n\topts: InstallPluginOpts = {},\n): Promise<void> {\n\tconst response = await apiFetch(`${MARKETPLACE_BASE}/${encodeURIComponent(id)}/install`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(opts),\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to install plugin\");\n}\n\n/**\n * Update a marketplace plugin to a newer version.\n * POST /_emdash/api/admin/plugins/:id/update\n */\nexport async function updateMarketplacePlugin(\n\tid: string,\n\topts: UpdatePluginOpts = {},\n): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${encodeURIComponent(id)}/update`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(opts),\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to update plugin\");\n}\n\n/**\n * Uninstall a marketplace plugin.\n * POST /_emdash/api/admin/plugins/:id/uninstall\n */\nexport async function uninstallMarketplacePlugin(\n\tid: string,\n\topts: UninstallPluginOpts = {},\n): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/${encodeURIComponent(id)}/uninstall`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(opts),\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to uninstall plugin\");\n}\n\n/**\n * Check all marketplace plugins for available updates.\n * GET /_emdash/api/admin/plugins/updates\n */\nexport async function checkPluginUpdates(): Promise<PluginUpdateInfo[]> {\n\tconst response = await apiFetch(`${API_BASE}/admin/plugins/updates`);\n\tconst result = await parseApiResponse<{ items: PluginUpdateInfo[] }>(\n\t\tresponse,\n\t\t\"Failed to check for updates\",\n\t);\n\treturn result.items;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/** Human-readable labels for plugin capabilities */\nexport const CAPABILITY_LABELS: Record<string, string> = {\n\t\"read:content\": \"Read your content\",\n\t\"write:content\": \"Create, update, and delete content\",\n\t\"read:media\": \"Access your media library\",\n\t\"write:media\": \"Upload and manage media\",\n\t\"network:fetch\": \"Make network requests\",\n\t\"network:fetch:any\": \"Make network requests to any host (unrestricted)\",\n};\n\n/**\n * Get a human-readable description for a capability.\n * For network:fetch, appends the allowed hosts if provided.\n */\nexport function describeCapability(capability: string, allowedHosts?: string[]): string {\n\tconst base = CAPABILITY_LABELS[capability] ?? capability;\n\tif (capability === \"network:fetch\" && allowedHosts && allowedHosts.length > 0) {\n\t\treturn `${base} to: ${allowedHosts.join(\", \")}`;\n\t}\n\treturn base;\n}\n","/**\n * Email settings API client functions\n */\n\nimport { API_BASE, apiFetch, parseApiResponse } from \"./client.js\";\n\n// =============================================================================\n// Types\n// =============================================================================\n\nexport interface EmailProvider {\n\tpluginId: string;\n}\n\nexport interface EmailSettings {\n\tavailable: boolean;\n\tproviders: EmailProvider[];\n\tselectedProviderId: string | null;\n\tmiddleware: {\n\t\tbeforeSend: string[];\n\t\tafterSend: string[];\n\t};\n}\n\n// =============================================================================\n// API functions\n// =============================================================================\n\nexport async function fetchEmailSettings(): Promise<EmailSettings> {\n\tconst res = await apiFetch(`${API_BASE}/settings/email`);\n\treturn parseApiResponse<EmailSettings>(res, \"Failed to fetch email settings\");\n}\n\nexport async function sendTestEmail(to: string): Promise<{ success: boolean; message: string }> {\n\tconst res = await apiFetch(`${API_BASE}/settings/email`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ to }),\n\t});\n\treturn parseApiResponse<{ success: boolean; message: string }>(res, \"Failed to send test email\");\n}\n","/**\n * Theme Marketplace API client\n *\n * Calls the site-side proxy endpoints (/_emdash/api/admin/themes/marketplace/*)\n * which forward to the marketplace Worker. The preview signing endpoint\n * is local (/_emdash/api/themes/preview).\n */\n\nimport { API_BASE, apiFetch, parseApiResponse } from \"./client.js\";\n\n// ---------------------------------------------------------------------------\n// Types — matches the marketplace REST API response shapes\n// ---------------------------------------------------------------------------\n\nexport interface ThemeAuthor {\n\tname: string;\n\tverified: boolean;\n\tavatarUrl: string | null;\n}\n\nexport interface ThemeAuthorDetail extends ThemeAuthor {\n\tid: string;\n}\n\n/** Summary shown in browse cards */\nexport interface ThemeSummary {\n\tid: string;\n\tname: string;\n\tdescription: string | null;\n\tauthor: ThemeAuthor;\n\tkeywords: string[];\n\tpreviewUrl: string;\n\tdemoUrl: string | null;\n\thasThumbnail: boolean;\n\tthumbnailUrl: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\n/** Full detail returned by GET /themes/:id */\nexport interface ThemeDetail extends Omit<ThemeSummary, \"author\"> {\n\tauthor: ThemeAuthorDetail;\n\trepositoryUrl: string | null;\n\thomepageUrl: string | null;\n\tlicense: string | null;\n\tscreenshotCount: number;\n\tscreenshotUrls: string[];\n}\n\nexport interface ThemeSearchResult {\n\titems: ThemeSummary[];\n\tnextCursor?: string;\n}\n\nexport interface ThemeSearchOpts {\n\tq?: string;\n\tkeyword?: string;\n\tsort?: \"name\" | \"created\" | \"updated\";\n\tcursor?: string;\n\tlimit?: number;\n}\n\n// ---------------------------------------------------------------------------\n// API functions\n// ---------------------------------------------------------------------------\n\nconst THEME_MARKETPLACE_BASE = `${API_BASE}/admin/themes/marketplace`;\n\n/**\n * Search theme listings.\n * Proxied through /_emdash/api/admin/themes/marketplace\n */\nexport async function searchThemes(opts: ThemeSearchOpts = {}): Promise<ThemeSearchResult> {\n\tconst params = new URLSearchParams();\n\tif (opts.q) params.set(\"q\", opts.q);\n\tif (opts.keyword) params.set(\"keyword\", opts.keyword);\n\tif (opts.sort) params.set(\"sort\", opts.sort);\n\tif (opts.cursor) params.set(\"cursor\", opts.cursor);\n\tif (opts.limit) params.set(\"limit\", String(opts.limit));\n\n\tconst qs = params.toString();\n\tconst url = `${THEME_MARKETPLACE_BASE}${qs ? `?${qs}` : \"\"}`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<ThemeSearchResult>(response, \"Theme search failed\");\n}\n\n/**\n * Get full theme detail.\n * Proxied through /_emdash/api/admin/themes/marketplace/:id\n */\nexport async function fetchTheme(id: string): Promise<ThemeDetail> {\n\tconst response = await apiFetch(`${THEME_MARKETPLACE_BASE}/${encodeURIComponent(id)}`);\n\tif (response.status === 404) {\n\t\tthrow new Error(`Theme \"${id}\" not found`);\n\t}\n\treturn parseApiResponse<ThemeDetail>(response, \"Failed to fetch theme\");\n}\n\n/**\n * Generate a signed preview URL for the \"Try with my data\" flow.\n * POST /_emdash/api/themes/preview (local, not proxied)\n */\nexport async function generatePreviewUrl(previewUrl: string): Promise<string> {\n\tconst response = await apiFetch(`${API_BASE}/themes/preview`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ previewUrl }),\n\t});\n\tconst result = await parseApiResponse<{ url: string }>(\n\t\tresponse,\n\t\t\"Failed to generate preview URL\",\n\t);\n\treturn result.url;\n}\n","/**\n * Redirects API client\n */\n\nimport { API_BASE, apiFetch, parseApiResponse, throwResponseError } from \"./client.js\";\n\nexport interface Redirect {\n\tid: string;\n\tsource: string;\n\tdestination: string;\n\ttype: number;\n\tisPattern: boolean;\n\tenabled: boolean;\n\thits: number;\n\tlastHitAt: string | null;\n\tgroupName: string | null;\n\tauto: boolean;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface NotFoundSummary {\n\tpath: string;\n\tcount: number;\n\tlastSeen: string;\n\ttopReferrer: string | null;\n}\n\nexport interface CreateRedirectInput {\n\tsource: string;\n\tdestination: string;\n\ttype?: number;\n\tenabled?: boolean;\n\tgroupName?: string | null;\n}\n\nexport interface UpdateRedirectInput {\n\tsource?: string;\n\tdestination?: string;\n\ttype?: number;\n\tenabled?: boolean;\n\tgroupName?: string | null;\n}\n\nexport interface RedirectListOptions {\n\tcursor?: string;\n\tlimit?: number;\n\tsearch?: string;\n\tgroup?: string;\n\tenabled?: boolean;\n\tauto?: boolean;\n}\n\nexport interface RedirectListResult {\n\titems: Redirect[];\n\tnextCursor?: string;\n}\n\n/**\n * List redirects with optional filters\n */\nexport async function fetchRedirects(options?: RedirectListOptions): Promise<RedirectListResult> {\n\tconst params = new URLSearchParams();\n\tif (options?.cursor) params.set(\"cursor\", options.cursor);\n\tif (options?.limit != null) params.set(\"limit\", String(options.limit));\n\tif (options?.search) params.set(\"search\", options.search);\n\tif (options?.group) params.set(\"group\", options.group);\n\tif (options?.enabled !== undefined) params.set(\"enabled\", String(options.enabled));\n\tif (options?.auto !== undefined) params.set(\"auto\", String(options.auto));\n\n\tconst url = params.toString() ? `${API_BASE}/redirects?${params}` : `${API_BASE}/redirects`;\n\tconst response = await apiFetch(url);\n\treturn parseApiResponse<RedirectListResult>(response, \"Failed to fetch redirects\");\n}\n\n/**\n * Create a redirect\n */\nexport async function createRedirect(input: CreateRedirectInput): Promise<Redirect> {\n\tconst response = await apiFetch(`${API_BASE}/redirects`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Redirect>(response, \"Failed to create redirect\");\n}\n\n/**\n * Update a redirect\n */\nexport async function updateRedirect(id: string, input: UpdateRedirectInput): Promise<Redirect> {\n\tconst response = await apiFetch(`${API_BASE}/redirects/${encodeURIComponent(id)}`, {\n\t\tmethod: \"PUT\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(input),\n\t});\n\treturn parseApiResponse<Redirect>(response, \"Failed to update redirect\");\n}\n\n/**\n * Delete a redirect\n */\nexport async function deleteRedirect(id: string): Promise<void> {\n\tconst response = await apiFetch(`${API_BASE}/redirects/${encodeURIComponent(id)}`, {\n\t\tmethod: \"DELETE\",\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to delete redirect\");\n}\n\n/**\n * Fetch 404 summary (grouped by path, sorted by count)\n */\nexport async function fetch404Summary(limit?: number): Promise<NotFoundSummary[]> {\n\tconst params = new URLSearchParams();\n\tif (limit != null) params.set(\"limit\", String(limit));\n\n\tconst url = params.toString()\n\t\t? `${API_BASE}/redirects/404s/summary?${params}`\n\t\t: `${API_BASE}/redirects/404s/summary`;\n\tconst response = await apiFetch(url);\n\tconst data = await parseApiResponse<{ items: NotFoundSummary[] }>(\n\t\tresponse,\n\t\t\"Failed to fetch 404 summary\",\n\t);\n\treturn data.items;\n}\n","/**\n * Current user query — shared across Shell, Header, Sidebar, and CommandPalette.\n */\n\nimport { useQuery } from \"@tanstack/react-query\";\n\nimport { apiFetch, parseApiResponse } from \"./client.js\";\n\nexport interface CurrentUser {\n\tid: string;\n\temail: string;\n\tname?: string;\n\trole: number;\n\tavatarUrl?: string;\n\tisFirstLogin?: boolean;\n}\n\nasync function fetchCurrentUser(): Promise<CurrentUser> {\n\tconst response = await apiFetch(\"/_emdash/api/auth/me\");\n\treturn parseApiResponse<CurrentUser>(response, \"Failed to fetch user\");\n}\n\nexport function useCurrentUser() {\n\treturn useQuery({\n\t\tqueryKey: [\"currentUser\"],\n\t\tqueryFn: fetchCurrentUser,\n\t\tstaleTime: 5 * 60 * 1000,\n\t\tretry: false,\n\t});\n}\n","import { Input, Switch } from \"@cloudflare/kumo\";\nimport type { Element } from \"@emdash-cms/blocks\";\nimport * as React from \"react\";\n\ninterface BlockKitFieldWidgetProps {\n\tlabel: string;\n\telements: Element[];\n\tvalue: unknown;\n\tonChange: (value: unknown) => void;\n}\n\n/**\n * Renders Block Kit elements as a field widget for sandboxed plugins.\n * Decomposes a JSON value into per-element values keyed by action_id,\n * and recomposes on change.\n */\nexport function BlockKitFieldWidget({\n\tlabel,\n\telements,\n\tvalue,\n\tonChange,\n}: BlockKitFieldWidgetProps) {\n\tconst obj = (value && typeof value === \"object\" ? value : {}) as Record<string, unknown>;\n\n\t// Use a ref to avoid stale closure -- rapid changes to different elements\n\t// would otherwise lose updates because each callback spreads from a stale obj.\n\tconst objRef = React.useRef(obj);\n\tobjRef.current = obj;\n\n\tconst handleElementChange = React.useCallback(\n\t\t(actionId: string, elementValue: unknown) => {\n\t\t\tonChange({ ...objRef.current, [actionId]: elementValue });\n\t\t},\n\t\t[onChange],\n\t);\n\n\t// Filter out elements without action_id -- they can't be mapped to values\n\tconst validElements = elements.filter((el) => el.action_id);\n\n\treturn (\n\t\t<div>\n\t\t\t<span className=\"text-sm font-medium leading-none\">{label}</span>\n\t\t\t<div className=\"mt-2 space-y-3\">\n\t\t\t\t{validElements.map((el) => (\n\t\t\t\t\t<BlockKitFieldElement\n\t\t\t\t\t\tkey={el.action_id}\n\t\t\t\t\t\telement={el}\n\t\t\t\t\t\tvalue={obj[el.action_id]}\n\t\t\t\t\t\tonChange={handleElementChange}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction BlockKitFieldElement({\n\telement,\n\tvalue,\n\tonChange,\n}: {\n\telement: Element;\n\tvalue: unknown;\n\tonChange: (actionId: string, value: unknown) => void;\n}) {\n\tswitch (element.type) {\n\t\tcase \"text_input\":\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={element.label}\n\t\t\t\t\tplaceholder={element.placeholder}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => onChange(element.action_id, e.target.value)}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"number_input\":\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={element.label}\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tvalue={typeof value === \"number\" ? String(value) : \"\"}\n\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\tconst n = Number(e.target.value);\n\t\t\t\t\t\tonChange(element.action_id, e.target.value && Number.isFinite(n) ? n : undefined);\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"toggle\":\n\t\t\treturn (\n\t\t\t\t<Switch\n\t\t\t\t\tlabel={element.label}\n\t\t\t\t\tchecked={!!value}\n\t\t\t\t\tonCheckedChange={(checked) => onChange(element.action_id, checked)}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"select\": {\n\t\t\tconst options = Array.isArray(element.options) ? element.options : [];\n\t\t\treturn (\n\t\t\t\t<div>\n\t\t\t\t\t<label className=\"text-sm font-medium mb-1.5 block\">{element.label}</label>\n\t\t\t\t\t<select\n\t\t\t\t\t\tclassName=\"flex w-full rounded-md border border-kumo-line bg-transparent px-3 py-2 text-sm\"\n\t\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\t\tonChange={(e) => onChange(element.action_id, e.target.value)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<option value=\"\">Select...</option>\n\t\t\t\t\t\t{options.map((opt) => (\n\t\t\t\t\t\t\t<option key={opt.value} value={opt.value}>\n\t\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\tdefault:\n\t\t\treturn (\n\t\t\t\t<div className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\tUnsupported widget element type: {(element as { type: string }).type}\n\t\t\t\t</div>\n\t\t\t);\n\t}\n}\n","/**\n * Document Outline\n *\n * Displays a tree structure of headings from the TipTap editor.\n * - Shows H1 at root, H2 indented, H3 further indented\n * - Click-to-navigate to heading position\n * - Highlights the current section based on cursor position\n */\n\nimport { Button } from \"@cloudflare/kumo\";\nimport { CaretDown, CaretRight, List } from \"@phosphor-icons/react\";\nimport type { Editor } from \"@tiptap/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\nfunction getIndentClass(level: number) {\n\tswitch (level) {\n\t\tcase 1:\n\t\t\treturn \"pl-0\";\n\t\tcase 2:\n\t\t\treturn \"pl-4\";\n\t\tcase 3:\n\t\t\treturn \"pl-8\";\n\t\tdefault:\n\t\t\treturn \"pl-0\";\n\t}\n}\n\nfunction getTextClass(level: number) {\n\tswitch (level) {\n\t\tcase 1:\n\t\t\treturn \"font-medium\";\n\t\tcase 2:\n\t\t\treturn \"font-normal\";\n\t\tcase 3:\n\t\t\treturn \"font-normal text-kumo-subtle\";\n\t\tdefault:\n\t\t\treturn \"font-normal\";\n\t}\n}\n\n/**\n * Heading item extracted from editor document\n */\nexport interface HeadingItem {\n\t/** Heading level (1-3) */\n\tlevel: number;\n\t/** Heading text content */\n\ttext: string;\n\t/** Position in document for navigation */\n\tpos: number;\n\t/** Unique key for React */\n\tkey: string;\n}\n\n/**\n * Extract headings from the TipTap editor document\n */\nexport function extractHeadings(editor: Editor | null): HeadingItem[] {\n\tif (!editor) return [];\n\n\tconst headings: HeadingItem[] = [];\n\tconst doc = editor.state.doc;\n\tlet key = 0;\n\n\tdoc.descendants((node, pos) => {\n\t\tif (node.type.name === \"heading\") {\n\t\t\tconst rawLevel = node.attrs.level;\n\t\t\tconst level = typeof rawLevel === \"number\" ? rawLevel : 1;\n\t\t\tconst text = node.textContent || \"\";\n\t\t\tif (text.trim()) {\n\t\t\t\theadings.push({\n\t\t\t\t\tlevel,\n\t\t\t\t\ttext,\n\t\t\t\t\tpos,\n\t\t\t\t\tkey: `heading-${key++}`,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t});\n\n\treturn headings;\n}\n\n/**\n * Find the current heading based on cursor position\n */\nexport function findCurrentHeading(headings: HeadingItem[], cursorPos: number): HeadingItem | null {\n\tif (headings.length === 0) return null;\n\n\t// Find the heading that contains or precedes the cursor\n\tlet current: HeadingItem | null = null;\n\tfor (const heading of headings) {\n\t\tif (heading.pos <= cursorPos) {\n\t\t\tcurrent = heading;\n\t\t} else {\n\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn current;\n}\n\nexport interface DocumentOutlineProps {\n\t/** TipTap editor instance */\n\teditor: Editor | null;\n\t/** Additional CSS classes */\n\tclassName?: string;\n}\n\n/**\n * Document outline component showing heading tree structure\n */\nexport function DocumentOutline({ editor, className }: DocumentOutlineProps) {\n\tconst [isExpanded, setIsExpanded] = React.useState(true);\n\tconst [headings, setHeadings] = React.useState<HeadingItem[]>([]);\n\tconst [currentPos, setCurrentPos] = React.useState(0);\n\n\t// Extract headings when editor content changes\n\tReact.useEffect(() => {\n\t\tif (!editor) return;\n\n\t\tconst updateHeadings = () => {\n\t\t\tsetHeadings(extractHeadings(editor));\n\t\t};\n\n\t\t// Initial extraction\n\t\tupdateHeadings();\n\n\t\t// Update on content changes\n\t\teditor.on(\"update\", updateHeadings);\n\n\t\treturn () => {\n\t\t\teditor.off(\"update\", updateHeadings);\n\t\t};\n\t}, [editor]);\n\n\t// Track cursor position for current section highlight\n\tReact.useEffect(() => {\n\t\tif (!editor) return;\n\n\t\tconst updatePosition = () => {\n\t\t\tconst { from } = editor.state.selection;\n\t\t\tsetCurrentPos(from);\n\t\t};\n\n\t\t// Initial position\n\t\tupdatePosition();\n\n\t\t// Update on selection changes\n\t\teditor.on(\"selectionUpdate\", updatePosition);\n\n\t\treturn () => {\n\t\t\teditor.off(\"selectionUpdate\", updatePosition);\n\t\t};\n\t}, [editor]);\n\n\tconst currentHeading = findCurrentHeading(headings, currentPos);\n\n\tconst handleHeadingClick = (heading: HeadingItem) => {\n\t\tif (!editor) return;\n\n\t\t// Navigate to heading and scroll into view\n\t\teditor.chain().focus().setTextSelection(heading.pos).scrollIntoView().run();\n\t};\n\n\treturn (\n\t\t<div className={cn(\"space-y-2\", className)}>\n\t\t\t<Button\n\t\t\t\tvariant=\"ghost\"\n\t\t\t\tsize=\"sm\"\n\t\t\t\tclassName=\"w-full justify-between px-2 h-8\"\n\t\t\t\tonClick={() => setIsExpanded(!isExpanded)}\n\t\t\t>\n\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t<List className=\"h-4 w-4\" />\n\t\t\t\t\t<span className=\"font-semibold\">Outline</span>\n\t\t\t\t</span>\n\t\t\t\t{isExpanded ? <CaretDown className=\"h-4 w-4\" /> : <CaretRight className=\"h-4 w-4\" />}\n\t\t\t</Button>\n\n\t\t\t{isExpanded && (\n\t\t\t\t<div className=\"space-y-0.5\">\n\t\t\t\t\t{headings.length === 0 ? (\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle px-2 py-1\">No headings in document</p>\n\t\t\t\t\t) : (\n\t\t\t\t\t\theadings.map((heading) => {\n\t\t\t\t\t\t\tconst isCurrent = currentHeading?.key === heading.key;\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={heading.key}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => handleHeadingClick(heading)}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"w-full text-left px-2 py-1 text-sm rounded transition-colors\",\n\t\t\t\t\t\t\t\t\t\t\"hover:bg-kumo-tint/50 cursor-pointer\",\n\t\t\t\t\t\t\t\t\t\t\"truncate\",\n\t\t\t\t\t\t\t\t\t\tgetIndentClass(heading.level),\n\t\t\t\t\t\t\t\t\t\tgetTextClass(heading.level),\n\t\t\t\t\t\t\t\t\t\tisCurrent && \"bg-kumo-tint text-kumo-default\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\ttitle={heading.text}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{heading.text}\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","import * as React from \"react\";\n\ninterface Props {\n\tchildren: React.ReactNode;\n\t/** The underlying field kind to show in the error message */\n\tfieldKind: string;\n}\n\ninterface State {\n\thasError: boolean;\n\terror?: Error;\n}\n\n/**\n * Error boundary that wraps trusted plugin field widgets.\n * On render error, shows a warning with a retry button instead of crashing the editor.\n */\nexport class PluginFieldErrorBoundary extends React.Component<Props, State> {\n\tconstructor(props: Props) {\n\t\tsuper(props);\n\t\tthis.state = { hasError: false };\n\t}\n\n\tstatic getDerivedStateFromError(error: Error): State {\n\t\treturn { hasError: true, error };\n\t}\n\n\toverride render() {\n\t\tif (this.state.hasError) {\n\t\t\treturn (\n\t\t\t\t<div className=\"rounded-md border border-kumo-danger/50 bg-kumo-danger/5 p-3\">\n\t\t\t\t\t<p className=\"text-sm font-medium text-kumo-danger\">Plugin widget error</p>\n\t\t\t\t\t<p className=\"mt-1 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t{this.state.error?.message || \"The plugin field widget failed to render.\"}\n\t\t\t\t\t</p>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"mt-2 text-xs font-medium text-kumo-brand underline\"\n\t\t\t\t\t\tonClick={() => this.setState({ hasError: false, error: undefined })}\n\t\t\t\t\t>\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\treturn this.props.children;\n\t}\n}\n","import * as React from \"react\";\n\n/**\n * Returns a stable function reference that always calls the latest version\n * of the provided callback. Useful for event listeners in effects where\n * you don't want the listener to be torn down and re-added when the\n * callback identity changes.\n */\nexport function useStableCallback<Args extends unknown[], Return>(\n\tcallback: (...args: Args) => Return,\n): (...args: Args) => Return {\n\tconst ref = React.useRef(callback);\n\tReact.useLayoutEffect(() => {\n\t\tref.current = callback;\n\t});\n\treturn React.useCallback((...args: Args) => ref.current(...args), []);\n}\n\n/**\n * Returns a debounced version of a value that only updates after the\n * specified delay has elapsed since the last change. Useful for search\n * inputs that trigger API calls.\n */\nexport function useDebouncedValue<T>(value: T, delay: number): T {\n\tconst [debouncedValue, setDebouncedValue] = React.useState(value);\n\tReact.useEffect(() => {\n\t\tconst timer = setTimeout(setDebouncedValue, delay, value);\n\t\treturn () => clearTimeout(timer);\n\t}, [value, delay]);\n\treturn debouncedValue;\n}\n","import type { MediaItem, MediaProviderItem } from \"./api/media.js\";\n\nexport function providerItemToMediaItem(\n\tproviderId: string,\n\titem: MediaProviderItem,\n): MediaItem & { provider: string; meta?: Record<string, unknown> } {\n\treturn {\n\t\tid: item.id,\n\t\tfilename: item.filename,\n\t\tmimeType: item.mimeType,\n\t\turl: item.previewUrl || \"\",\n\t\tsize: item.size || 0,\n\t\twidth: item.width,\n\t\theight: item.height,\n\t\talt: item.alt,\n\t\tcreatedAt: new Date().toISOString(),\n\t\tprovider: providerId,\n\t\tmeta: item.meta,\n\t} as MediaItem & { provider: string; meta?: Record<string, unknown> };\n}\n\nexport function getFileIcon(mimeType: string): string {\n\tif (mimeType.startsWith(\"video/\")) return \"🎬\";\n\tif (mimeType.startsWith(\"audio/\")) return \"🎵\";\n\tif (mimeType.includes(\"pdf\")) return \"📄\";\n\tif (mimeType.includes(\"document\") || mimeType.includes(\"word\")) return \"📝\";\n\tif (mimeType.includes(\"spreadsheet\") || mimeType.includes(\"excel\")) return \"📊\";\n\treturn \"📁\";\n}\n\nexport function formatFileSize(bytes: number): string {\n\tif (bytes === 0) return \"0 B\";\n\tconst k = 1024;\n\tconst sizes = [\"B\", \"KB\", \"MB\", \"GB\"];\n\tconst i = Math.floor(Math.log(bytes) / Math.log(k));\n\treturn parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + \" \" + sizes[i];\n}\n","/**\n * Media Picker Modal\n *\n * A modal dialog for selecting media from the library or uploading new files.\n * Supports multiple media providers with tabbed navigation.\n * Used by the rich text editor and image field components.\n */\n\nimport { Button, Dialog, Input, Label, Loader } from \"@cloudflare/kumo\";\nimport { Upload, Image, Check, Globe, MagnifyingGlass } from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport {\n\tfetchMediaList,\n\tfetchMediaProviders,\n\tfetchProviderMedia,\n\tuploadMedia,\n\tuploadToProvider,\n\tupdateMedia,\n\ttype MediaItem,\n\ttype MediaProviderInfo,\n\ttype MediaProviderItem,\n} from \"../lib/api\";\nimport { providerItemToMediaItem, getFileIcon } from \"../lib/media-utils\";\nimport { cn } from \"../lib/utils\";\nimport { DialogError } from \"./DialogError.js\";\n\n/** Selected item can be either a local MediaItem or a provider item with provider context */\ninterface SelectedMedia {\n\tproviderId: string;\n\titem: MediaItem | MediaProviderItem;\n}\n\nexport interface MediaPickerModalProps {\n\topen: boolean;\n\tonOpenChange: (open: boolean) => void;\n\tonSelect: (item: MediaItem) => void;\n\t/** Filter by mime type prefix, e.g. \"image/\" */\n\tmimeTypeFilter?: string;\n\ttitle?: string;\n}\n\n/**\n * Probe image URL to get dimensions\n */\nfunction probeImageDimensions(url: string): Promise<{ width: number; height: number }> {\n\treturn new Promise((resolve, reject) => {\n\t\tconst img = new window.Image();\n\t\timg.onload = () => {\n\t\t\tresolve({ width: img.naturalWidth, height: img.naturalHeight });\n\t\t};\n\t\timg.onerror = () => {\n\t\t\treject(new Error(\"Failed to load image\"));\n\t\t};\n\t\timg.src = url;\n\t});\n}\n\nexport function MediaPickerModal({\n\topen,\n\tonOpenChange,\n\tonSelect,\n\tmimeTypeFilter = \"image/\",\n\ttitle = \"Select Image\",\n}: MediaPickerModalProps) {\n\tconst queryClient = useQueryClient();\n\tconst [selectedItem, setSelectedItem] = React.useState<SelectedMedia | null>(null);\n\tconst [activeProvider, setActiveProvider] = React.useState<string>(\"local\");\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst fileInputRef = React.useRef<HTMLInputElement>(null);\n\n\t// URL input state\n\tconst [imageUrl, setImageUrl] = React.useState(\"\");\n\tconst [isProbing, setIsProbing] = React.useState(false);\n\tconst [urlError, setUrlError] = React.useState<string | null>(null);\n\n\t// Track loaded image dimensions for providers that don't return them (e.g., CF Images)\n\tconst [providerDimensions, setProviderDimensions] = React.useState<\n\t\tRecord<string, { width: number; height: number }>\n\t>({});\n\n\t// Reset state when modal opens\n\tReact.useEffect(() => {\n\t\tif (open) {\n\t\t\tsetSelectedItem(null);\n\t\t\tsetActiveProvider(\"local\");\n\t\t\tsetSearchQuery(\"\");\n\t\t\tsetImageUrl(\"\");\n\t\t\tsetUrlError(null);\n\t\t\tsetUploadError(null);\n\t\t\tsetProviderDimensions({});\n\t\t}\n\t}, [open]);\n\n\t// Fetch available providers\n\tconst { data: providers } = useQuery({\n\t\tqueryKey: [\"media-providers\"],\n\t\tqueryFn: fetchMediaProviders,\n\t\tenabled: open,\n\t\t// Default to just local if fetch fails\n\t\tplaceholderData: [],\n\t});\n\n\t// Get active provider info\n\tconst activeProviderInfo = React.useMemo(() => {\n\t\tif (activeProvider === \"local\") {\n\t\t\treturn {\n\t\t\t\tid: \"local\",\n\t\t\t\tname: \"Library\",\n\t\t\t\ticon: undefined,\n\t\t\t\tcapabilities: { browse: true, search: false, upload: true, delete: true },\n\t\t\t} as MediaProviderInfo;\n\t\t}\n\t\treturn providers?.find((p) => p.id === activeProvider);\n\t}, [activeProvider, providers]);\n\n\t// Fetch local media list\n\tconst { data: localData, isLoading: localLoading } = useQuery({\n\t\tqueryKey: [\"media\", mimeTypeFilter],\n\t\tqueryFn: () =>\n\t\t\tfetchMediaList({\n\t\t\t\tmimeType: mimeTypeFilter,\n\t\t\t\tlimit: 50,\n\t\t\t}),\n\t\tenabled: open && activeProvider === \"local\",\n\t});\n\n\t// Fetch provider media list\n\tconst { data: providerData, isLoading: providerLoading } = useQuery({\n\t\tqueryKey: [\"provider-media\", activeProvider, mimeTypeFilter, searchQuery],\n\t\tqueryFn: () =>\n\t\t\tfetchProviderMedia(activeProvider, {\n\t\t\t\tmimeType: mimeTypeFilter,\n\t\t\t\tlimit: 50,\n\t\t\t\tquery: searchQuery || undefined,\n\t\t\t}),\n\t\tenabled: open && activeProvider !== \"local\",\n\t});\n\n\tconst isLoading = activeProvider === \"local\" ? localLoading : providerLoading;\n\n\tconst [uploadError, setUploadError] = React.useState<string | null>(null);\n\n\t// Upload mutation for local provider\n\tconst uploadLocalMutation = useMutation({\n\t\tmutationFn: (file: File) => uploadMedia(file),\n\t\tonSuccess: (item) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"media\"] });\n\t\t\tsetSelectedItem({ providerId: \"local\", item });\n\t\t\tsetUploadError(null);\n\t\t},\n\t\tonError: (err: Error) => {\n\t\t\tsetUploadError(err.message);\n\t\t},\n\t});\n\n\t// Upload mutation for external providers\n\tconst uploadProviderMutation = useMutation({\n\t\tmutationFn: ({ providerId, file }: { providerId: string; file: File }) =>\n\t\t\tuploadToProvider(providerId, file),\n\t\tonSuccess: (item, { providerId }) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"provider-media\", providerId] });\n\t\t\tsetSelectedItem({ providerId, item });\n\t\t\tsetUploadError(null);\n\t\t},\n\t\tonError: (err: Error) => {\n\t\t\tsetUploadError(err.message);\n\t\t},\n\t});\n\n\tconst isUploading = uploadLocalMutation.isPending || uploadProviderMutation.isPending;\n\n\t// Track which items we've already updated dimensions for\n\tconst updatedDimensionsRef = React.useRef<Set<string>>(new Set());\n\n\t// Mutation for updating media dimensions\n\tconst dimensionsMutation = useMutation({\n\t\tmutationFn: ({ id, width, height }: { id: string; width: number; height: number }) =>\n\t\t\tupdateMedia(id, { width, height }),\n\t\tonSuccess: (_updated, { id, width, height }) => {\n\t\t\tqueryClient.setQueryData(\n\t\t\t\t[\"media\", mimeTypeFilter],\n\t\t\t\t(old: { items: MediaItem[]; nextCursor?: string } | undefined) => {\n\t\t\t\t\tif (!old) return old;\n\t\t\t\t\treturn {\n\t\t\t\t\t\t...old,\n\t\t\t\t\t\titems: old.items.map((item) => (item.id === id ? { ...item, width, height } : item)),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t);\n\n\t\t\tif (selectedItem?.providerId === \"local\" && selectedItem.item.id === id) {\n\t\t\t\tsetSelectedItem({\n\t\t\t\t\tproviderId: \"local\",\n\t\t\t\t\titem: { ...selectedItem.item, width, height },\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\tonError: (error) => {\n\t\t\tconsole.warn(\"Failed to update media dimensions:\", error);\n\t\t},\n\t});\n\n\t// Handle dimensions detected for local images missing them\n\tconst handleDimensionsDetected = React.useCallback(\n\t\t(id: string, width: number, height: number) => {\n\t\t\tif (updatedDimensionsRef.current.has(id)) return;\n\t\t\tupdatedDimensionsRef.current.add(id);\n\t\t\tdimensionsMutation.mutate({ id, width, height });\n\t\t},\n\t\t[dimensionsMutation],\n\t);\n\n\t// Get items for current view\n\tconst items = React.useMemo(() => {\n\t\tif (activeProvider === \"local\") {\n\t\t\tconst localItems = localData?.items || [];\n\t\t\tif (!mimeTypeFilter) return localItems;\n\t\t\treturn localItems.filter((item) => item.mimeType.startsWith(mimeTypeFilter));\n\t\t}\n\t\treturn providerData?.items || [];\n\t}, [activeProvider, localData?.items, providerData?.items, mimeTypeFilter]);\n\n\tconst handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst files = e.target.files;\n\t\tconst file = files?.[0];\n\t\tif (file) {\n\t\t\tif (activeProvider === \"local\") {\n\t\t\t\tuploadLocalMutation.mutate(file);\n\t\t\t} else if (activeProviderInfo?.capabilities.upload) {\n\t\t\t\tuploadProviderMutation.mutate({ providerId: activeProvider, file });\n\t\t\t}\n\t\t}\n\t\tif (fileInputRef.current) {\n\t\t\tfileInputRef.current.value = \"\";\n\t\t}\n\t};\n\n\tconst handleConfirm = () => {\n\t\tif (selectedItem) {\n\t\t\tif (selectedItem.providerId === \"local\") {\n\t\t\t\t// When providerId is \"local\", item is always MediaItem\n\t\t\t\tonSelect(selectedItem.item as MediaItem);\n\t\t\t} else {\n\t\t\t\t// When providerId is not \"local\", item is always MediaProviderItem\n\t\t\t\tconst providerItem = selectedItem.item as MediaProviderItem;\n\t\t\t\tconst dims = providerDimensions[providerItem.id];\n\t\t\t\tconst itemWithDims = dims\n\t\t\t\t\t? {\n\t\t\t\t\t\t\t...providerItem,\n\t\t\t\t\t\t\twidth: providerItem.width ?? dims.width,\n\t\t\t\t\t\t\theight: providerItem.height ?? dims.height,\n\t\t\t\t\t\t}\n\t\t\t\t\t: providerItem;\n\t\t\t\tconst mediaItem = providerItemToMediaItem(selectedItem.providerId, itemWithDims);\n\t\t\t\tonSelect(mediaItem);\n\t\t\t}\n\t\t\tonOpenChange(false);\n\t\t\tsetSelectedItem(null);\n\t\t\tsetImageUrl(\"\");\n\t\t}\n\t};\n\n\tconst handleClose = () => {\n\t\tonOpenChange(false);\n\t\tsetSelectedItem(null);\n\t\tsetImageUrl(\"\");\n\t\tsetUrlError(null);\n\t};\n\n\tconst handleUrlSubmit = async () => {\n\t\tif (!imageUrl.trim()) return;\n\n\t\tlet url: URL;\n\t\ttry {\n\t\t\turl = new URL(imageUrl.trim());\n\t\t} catch {\n\t\t\tsetUrlError(\"Please enter a valid URL\");\n\t\t\treturn;\n\t\t}\n\n\t\tsetIsProbing(true);\n\t\tsetUrlError(null);\n\n\t\ttry {\n\t\t\tconst dimensions = await probeImageDimensions(url.href);\n\t\t\tconst externalItem: MediaItem = {\n\t\t\t\tid: \"\",\n\t\t\t\tfilename: url.pathname.split(\"/\").pop() || \"external-image\",\n\t\t\t\tmimeType: \"image/unknown\",\n\t\t\t\turl: url.href,\n\t\t\t\tsize: 0,\n\t\t\t\twidth: dimensions.width,\n\t\t\t\theight: dimensions.height,\n\t\t\t\tcreatedAt: new Date().toISOString(),\n\t\t\t};\n\n\t\t\tonSelect(externalItem);\n\t\t\tonOpenChange(false);\n\t\t\tsetImageUrl(\"\");\n\t\t} catch {\n\t\t\tsetUrlError(\"Could not load image from URL\");\n\t\t} finally {\n\t\t\tsetIsProbing(false);\n\t\t}\n\t};\n\n\tconst handleUrlKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\") {\n\t\t\te.preventDefault();\n\t\t\tvoid handleUrlSubmit();\n\t\t}\n\t};\n\n\tconst canUpload =\n\t\tactiveProvider === \"local\" || (activeProviderInfo?.capabilities.upload ?? false);\n\tconst canSearch = activeProviderInfo?.capabilities.search ?? false;\n\n\t// Build provider tabs - always show local first, then add external providers\n\t// Filter out \"local\" from API response since we add it manually\n\tconst providerTabs = React.useMemo(() => {\n\t\tconst tabs: Array<{ id: string; name: string; icon?: string }> = [\n\t\t\t{ id: \"local\", name: \"Library\", icon: undefined },\n\t\t];\n\t\tif (providers) {\n\t\t\tfor (const p of providers) {\n\t\t\t\tif (p.id !== \"local\") {\n\t\t\t\t\ttabs.push({ id: p.id, name: p.name, icon: p.icon });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn tabs;\n\t}, [providers]);\n\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={handleClose}>\n\t\t\t<Dialog className=\"p-6 max-w-4xl max-h-[80vh] flex flex-col\" size=\"xl\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t{title}\n\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{/* URL Input */}\n\t\t\t\t<div className=\"border-b pb-4\">\n\t\t\t\t\t<Label>Insert from URL</Label>\n\t\t\t\t\t<div className=\"flex gap-2 mt-1.5\">\n\t\t\t\t\t\t<div className=\"flex-1 relative\">\n\t\t\t\t\t\t\t<Globe className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\t\tplaceholder=\"https://example.com/image.jpg\"\n\t\t\t\t\t\t\t\taria-label=\"Image URL\"\n\t\t\t\t\t\t\t\tvalue={imageUrl}\n\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\tsetImageUrl(e.target.value);\n\t\t\t\t\t\t\t\t\tsetUrlError(null);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonKeyDown={handleUrlKeyDown}\n\t\t\t\t\t\t\t\tclassName=\"pl-9\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Button onClick={handleUrlSubmit} disabled={!imageUrl.trim() || isProbing}>\n\t\t\t\t\t\t\t{isProbing ? <Loader size=\"sm\" /> : \"Insert\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t\t{urlError && <p className=\"text-sm text-kumo-danger mt-1\">{urlError}</p>}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Divider with \"or\" */}\n\t\t\t\t<div className=\"relative py-2\">\n\t\t\t\t\t<div className=\"absolute inset-0 flex items-center\">\n\t\t\t\t\t\t<span className=\"w-full border-t\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"relative flex justify-center text-xs uppercase\">\n\t\t\t\t\t\t<span className=\"bg-kumo-base px-2 text-kumo-subtle\">or choose from library</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Provider Tabs */}\n\t\t\t\t{providerTabs.length > 1 && (\n\t\t\t\t\t<div className=\"flex gap-2 border-b pb-3 flex-wrap\">\n\t\t\t\t\t\t{providerTabs.map((tab) => (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tkey={tab.id}\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tsetActiveProvider(tab.id);\n\t\t\t\t\t\t\t\t\tsetSelectedItem(null);\n\t\t\t\t\t\t\t\t\tsetSearchQuery(\"\");\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 px-4 h-9 text-sm font-medium rounded-md transition-colors whitespace-nowrap\",\n\t\t\t\t\t\t\t\t\tactiveProvider === tab.id\n\t\t\t\t\t\t\t\t\t\t? \"bg-kumo-brand text-white\"\n\t\t\t\t\t\t\t\t\t\t: \"bg-kumo-tint hover:bg-kumo-tint/80 text-kumo-subtle\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{tab.icon &&\n\t\t\t\t\t\t\t\t\t(tab.icon.startsWith(\"data:\") ? (\n\t\t\t\t\t\t\t\t\t\t<img src={tab.icon} alt=\"\" className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<span aria-hidden=\"true\">{tab.icon}</span>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t{tab.name}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Toolbar */}\n\t\t\t\t<div className=\"flex items-center justify-between pb-3 gap-4\">\n\t\t\t\t\t{/* Search (if provider supports it) */}\n\t\t\t\t\t{canSearch ? (\n\t\t\t\t\t\t<div className=\"relative flex-1 max-w-xs\">\n\t\t\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\t\t\tplaceholder=\"Search...\"\n\t\t\t\t\t\t\t\taria-label=\"Search media\"\n\t\t\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\t\t\tclassName=\"pl-9\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{items.length} item{items.length !== 1 ? \"s\" : \"\"}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Upload button (if provider supports it) */}\n\t\t\t\t\t{canUpload && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\ticon={<Upload />}\n\t\t\t\t\t\t\t\tonClick={() => fileInputRef.current?.click()}\n\t\t\t\t\t\t\t\tdisabled={isUploading}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isUploading ? \"Uploading...\" : \"Upload\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\taccept={mimeTypeFilter ? `${mimeTypeFilter}*` : undefined}\n\t\t\t\t\t\t\t\tclassName=\"sr-only\"\n\t\t\t\t\t\t\t\tonChange={handleFileSelect}\n\t\t\t\t\t\t\t\taria-label=\"Upload file\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Upload error */}\n\t\t\t\t<DialogError\n\t\t\t\t\tmessage={uploadError ? `Upload failed: ${uploadError}` : null}\n\t\t\t\t\tclassName=\"mb-3\"\n\t\t\t\t/>\n\n\t\t\t\t{/* Media Grid */}\n\t\t\t\t<div className=\"flex-1 overflow-y-auto min-h-[300px]\">\n\t\t\t\t\t{isLoading ? (\n\t\t\t\t\t\t<div className=\"flex items-center justify-center h-full\">\n\t\t\t\t\t\t\t<Loader />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : items.length === 0 ? (\n\t\t\t\t\t\t<div className=\"flex flex-col items-center justify-center h-full text-center p-8\">\n\t\t\t\t\t\t\t<Image className=\"h-12 w-12 text-kumo-subtle mb-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t<h3 className=\"text-lg font-medium\">No media found</h3>\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\t{canSearch && searchQuery\n\t\t\t\t\t\t\t\t\t? \"Try a different search term\"\n\t\t\t\t\t\t\t\t\t: canUpload\n\t\t\t\t\t\t\t\t\t\t? \"Upload an image to get started\"\n\t\t\t\t\t\t\t\t\t\t: \"No media available from this provider\"}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t{canUpload && !searchQuery && (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tclassName=\"mt-4\"\n\t\t\t\t\t\t\t\t\ticon={<Upload />}\n\t\t\t\t\t\t\t\t\tonClick={() => fileInputRef.current?.click()}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tUpload Image\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<ul\n\t\t\t\t\t\t\tclassName=\"grid grid-cols-[repeat(auto-fill,minmax(120px,1fr))] gap-3 p-1\"\n\t\t\t\t\t\t\trole=\"listbox\"\n\t\t\t\t\t\t\taria-label=\"Available media\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{activeProvider === \"local\"\n\t\t\t\t\t\t\t\t? (items as MediaItem[]).map((item) => (\n\t\t\t\t\t\t\t\t\t\t<MediaPickerItem\n\t\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\t\t\tselected={\n\t\t\t\t\t\t\t\t\t\t\t\tselectedItem?.providerId === \"local\" && selectedItem.item.id === item.id\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => setSelectedItem({ providerId: \"local\", item })}\n\t\t\t\t\t\t\t\t\t\t\tonDoubleClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tonSelect(item);\n\t\t\t\t\t\t\t\t\t\t\t\tonOpenChange(false);\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tonDimensionsDetected={handleDimensionsDetected}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t: (items as MediaProviderItem[]).map((item) => (\n\t\t\t\t\t\t\t\t\t\t<ProviderMediaItem\n\t\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\t\t\tselected={\n\t\t\t\t\t\t\t\t\t\t\t\tselectedItem?.providerId === activeProvider &&\n\t\t\t\t\t\t\t\t\t\t\t\tselectedItem.item.id === item.id\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => setSelectedItem({ providerId: activeProvider, item })}\n\t\t\t\t\t\t\t\t\t\t\tonDoubleClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t// Merge loaded dimensions for double-click select\n\t\t\t\t\t\t\t\t\t\t\t\tconst dims = providerDimensions[item.id];\n\t\t\t\t\t\t\t\t\t\t\t\tconst itemWithDims = dims\n\t\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t...item,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: item.width ?? dims.width,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: item.height ?? dims.height,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t: item;\n\t\t\t\t\t\t\t\t\t\t\t\tconst mediaItem = providerItemToMediaItem(activeProvider, itemWithDims);\n\t\t\t\t\t\t\t\t\t\t\t\tonSelect(mediaItem);\n\t\t\t\t\t\t\t\t\t\t\t\tonOpenChange(false);\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tonDimensionsLoaded={(width, height) => {\n\t\t\t\t\t\t\t\t\t\t\t\tsetProviderDimensions((prev) => ({\n\t\t\t\t\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\t\t\t\t\t[item.id]: { width, height },\n\t\t\t\t\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</ul>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Footer */}\n\t\t\t\t<div className=\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2 border-t pt-4\">\n\t\t\t\t\t<div className=\"flex-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{selectedItem && (\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\tSelected: <strong>{selectedItem.item.filename}</strong>\n\t\t\t\t\t\t\t\t{selectedItem.providerId !== \"local\" && (\n\t\t\t\t\t\t\t\t\t<span className=\"ml-2 text-xs\">\n\t\t\t\t\t\t\t\t\t\t(from {providers?.find((p) => p.id === selectedItem.providerId)?.name})\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<Button variant=\"outline\" onClick={handleClose}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button onClick={handleConfirm} disabled={!selectedItem}>\n\t\t\t\t\t\tInsert\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n\ninterface MediaPickerItemProps {\n\titem: MediaItem;\n\tselected: boolean;\n\tonClick: () => void;\n\tonDoubleClick: () => void;\n\tonDimensionsDetected?: (id: string, width: number, height: number) => void;\n}\n\nfunction MediaPickerItem({\n\titem,\n\tselected,\n\tonClick,\n\tonDoubleClick,\n\tonDimensionsDetected,\n}: MediaPickerItemProps) {\n\tconst isImage = item.mimeType.startsWith(\"image/\");\n\tconst needsDimensions = isImage && (!item.width || !item.height);\n\n\tconst handleImageLoad = React.useCallback(\n\t\t(e: React.SyntheticEvent<HTMLImageElement>) => {\n\t\t\tif (needsDimensions && onDimensionsDetected) {\n\t\t\t\tconst img = e.currentTarget;\n\t\t\t\tif (img.naturalWidth && img.naturalHeight) {\n\t\t\t\t\tonDimensionsDetected(item.id, img.naturalWidth, img.naturalHeight);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[needsDimensions, onDimensionsDetected, item.id],\n\t);\n\n\treturn (\n\t\t<li role=\"option\" aria-selected={selected}>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative aspect-square w-full rounded-lg border-2 overflow-hidden transition-all\",\n\t\t\t\t\t\"hover:border-kumo-brand/50 focus:outline-none focus:ring-2 focus:ring-kumo-ring\",\n\t\t\t\t\tselected ? \"border-kumo-brand ring-2 ring-kumo-brand/20\" : \"border-transparent\",\n\t\t\t\t)}\n\t\t\t\tonClick={onClick}\n\t\t\t\tonDoubleClick={onDoubleClick}\n\t\t\t\taria-label={`${item.filename}${selected ? \" (selected)\" : \"\"}`}\n\t\t\t>\n\t\t\t\t{isImage ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={item.url}\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tclassName=\"h-full w-full object-cover\"\n\t\t\t\t\t\tonLoad={handleImageLoad}\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"flex h-full w-full items-center justify-center bg-kumo-tint\">\n\t\t\t\t\t\t<span className=\"text-3xl\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t{getFileIcon(item.mimeType)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{selected && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"absolute inset-0 bg-kumo-brand/20 flex items-center justify-center\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"bg-kumo-brand text-white rounded-full p-1\">\n\t\t\t\t\t\t\t<Check className=\"h-4 w-4\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/60 to-transparent p-2\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t>\n\t\t\t\t\t<p className=\"text-xs text-white truncate\">{item.filename}</p>\n\t\t\t\t</div>\n\t\t\t</button>\n\t\t</li>\n\t);\n}\n\ninterface ProviderMediaItemProps {\n\titem: MediaProviderItem;\n\tselected: boolean;\n\tonClick: () => void;\n\tonDoubleClick: () => void;\n\t/** Callback when image dimensions are loaded (for providers that don't return dimensions) */\n\tonDimensionsLoaded?: (width: number, height: number) => void;\n}\n\nfunction ProviderMediaItem({\n\titem,\n\tselected,\n\tonClick,\n\tonDoubleClick,\n\tonDimensionsLoaded,\n}: ProviderMediaItemProps) {\n\tconst isImage = item.mimeType.startsWith(\"image/\");\n\tconst needsDimensions = isImage && (!item.width || !item.height);\n\n\tconst handleImageLoad = React.useCallback(\n\t\t(e: React.SyntheticEvent<HTMLImageElement>) => {\n\t\t\tif (needsDimensions && onDimensionsLoaded) {\n\t\t\t\tconst img = e.currentTarget;\n\t\t\t\tif (img.naturalWidth && img.naturalHeight) {\n\t\t\t\t\tonDimensionsLoaded(img.naturalWidth, img.naturalHeight);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[needsDimensions, onDimensionsLoaded],\n\t);\n\n\treturn (\n\t\t<li role=\"option\" aria-selected={selected}>\n\t\t\t<button\n\t\t\t\ttype=\"button\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"relative aspect-square w-full rounded-lg border-2 overflow-hidden transition-all\",\n\t\t\t\t\t\"hover:border-kumo-brand/50 focus:outline-none focus:ring-2 focus:ring-kumo-ring\",\n\t\t\t\t\tselected ? \"border-kumo-brand ring-2 ring-kumo-brand/20\" : \"border-transparent\",\n\t\t\t\t)}\n\t\t\t\tonClick={onClick}\n\t\t\t\tonDoubleClick={onDoubleClick}\n\t\t\t\taria-label={`${item.filename}${selected ? \" (selected)\" : \"\"}`}\n\t\t\t>\n\t\t\t\t{isImage && item.previewUrl ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={item.previewUrl}\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tclassName=\"h-full w-full object-cover\"\n\t\t\t\t\t\tonLoad={handleImageLoad}\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"flex h-full w-full items-center justify-center bg-kumo-tint\">\n\t\t\t\t\t\t<span className=\"text-3xl\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t{getFileIcon(item.mimeType)}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{selected && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"absolute inset-0 bg-kumo-brand/20 flex items-center justify-center\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"bg-kumo-brand text-white rounded-full p-1\">\n\t\t\t\t\t\t\t<Check className=\"h-4 w-4\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t<div\n\t\t\t\t\tclassName=\"absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/60 to-transparent p-2\"\n\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t>\n\t\t\t\t\t<p className=\"text-xs text-white truncate\">{item.filename}</p>\n\t\t\t\t</div>\n\t\t\t</button>\n\t\t</li>\n\t);\n}\n\nexport default MediaPickerModal;\n","/**\n * Image Detail Panel for Editor\n *\n * A slide-out panel for editing image properties in the rich text editor.\n * Shows preview and allows editing alt text, caption, and link settings.\n */\n\nimport { Button, Input, InputArea, Label, LinkButton } from \"@cloudflare/kumo\";\nimport {\n\tX,\n\tArrowSquareOut,\n\tRuler,\n\tSlidersHorizontal,\n\tImageSquare,\n\tLinkSimple,\n\tLinkBreak,\n} from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport type { MediaItem } from \"../../lib/api\";\nimport { useStableCallback } from \"../../lib/hooks\";\nimport { ConfirmDialog } from \"../ConfirmDialog\";\nimport { MediaPickerModal } from \"../MediaPickerModal\";\n\nexport interface ImageAttributes {\n\tsrc: string;\n\talt?: string;\n\ttitle?: string;\n\tcaption?: string;\n\tmediaId?: string;\n\t/** Original image width */\n\twidth?: number;\n\t/** Original image height */\n\theight?: number;\n\t/** Display width for this instance (defaults to original) */\n\tdisplayWidth?: number;\n\t/** Display height for this instance (defaults to original) */\n\tdisplayHeight?: number;\n}\n\nexport interface ImageDetailPanelProps {\n\tattributes: ImageAttributes;\n\tonUpdate: (attrs: Partial<ImageAttributes>) => void;\n\tonReplace: (attrs: ImageAttributes) => void;\n\tonDelete: () => void;\n\tonClose: () => void;\n\t/** When true, renders inline within the sidebar column instead of as a fixed overlay */\n\tinline?: boolean;\n}\n\n/**\n * Panel for editing image properties in the editor.\n * Renders as a fixed slide-out overlay by default, or inline within\n * the content sidebar when `inline` is true.\n */\nexport function ImageDetailPanel({\n\tattributes,\n\tonUpdate,\n\tonReplace,\n\tonDelete,\n\tonClose,\n\tinline = false,\n}: ImageDetailPanelProps) {\n\t// Form state\n\tconst [alt, setAlt] = React.useState(attributes.alt ?? \"\");\n\tconst [caption, setCaption] = React.useState(attributes.caption ?? \"\");\n\tconst [title, setTitle] = React.useState(attributes.title ?? \"\");\n\tconst [showMediaPicker, setShowMediaPicker] = React.useState(false);\n\n\t// Dimension state - default to display dimensions, fall back to original\n\tconst [displayWidth, setDisplayWidth] = React.useState<number | undefined>(\n\t\tattributes.displayWidth ?? attributes.width,\n\t);\n\tconst [displayHeight, setDisplayHeight] = React.useState<number | undefined>(\n\t\tattributes.displayHeight ?? attributes.height,\n\t);\n\tconst [lockAspectRatio, setLockAspectRatio] = React.useState(true);\n\n\t// Calculate aspect ratio from original dimensions\n\tconst aspectRatio =\n\t\tattributes.width && attributes.height ? attributes.width / attributes.height : undefined;\n\n\tconst handleWidthChange = (value: string) => {\n\t\tconst newWidth = value ? parseInt(value, 10) : undefined;\n\t\tsetDisplayWidth(newWidth);\n\t\tif (lockAspectRatio && aspectRatio && newWidth) {\n\t\t\tsetDisplayHeight(Math.round(newWidth / aspectRatio));\n\t\t}\n\t};\n\n\tconst handleHeightChange = (value: string) => {\n\t\tconst newHeight = value ? parseInt(value, 10) : undefined;\n\t\tsetDisplayHeight(newHeight);\n\t\tif (lockAspectRatio && aspectRatio && newHeight) {\n\t\t\tsetDisplayWidth(Math.round(newHeight * aspectRatio));\n\t\t}\n\t};\n\n\tconst handleResetDimensions = () => {\n\t\tsetDisplayWidth(attributes.width);\n\t\tsetDisplayHeight(attributes.height);\n\t};\n\n\tconst handleMediaSelect = (item: MediaItem) => {\n\t\tonReplace({\n\t\t\tsrc: item.url,\n\t\t\talt: item.alt || item.filename,\n\t\t\tmediaId: item.id,\n\t\t\twidth: item.width,\n\t\t\theight: item.height,\n\t\t\t// Clear caption/title since it's a new image\n\t\t\tcaption: undefined,\n\t\t\ttitle: undefined,\n\t\t});\n\t\tsetShowMediaPicker(false);\n\t\tonClose();\n\t};\n\n\t// Track if form has unsaved changes\n\tconst hasChanges = React.useMemo(() => {\n\t\tconst originalDisplayWidth = attributes.displayWidth ?? attributes.width;\n\t\tconst originalDisplayHeight = attributes.displayHeight ?? attributes.height;\n\t\treturn (\n\t\t\talt !== (attributes.alt ?? \"\") ||\n\t\t\tcaption !== (attributes.caption ?? \"\") ||\n\t\t\ttitle !== (attributes.title ?? \"\") ||\n\t\t\tdisplayWidth !== originalDisplayWidth ||\n\t\t\tdisplayHeight !== originalDisplayHeight\n\t\t);\n\t}, [attributes, alt, caption, title, displayWidth, displayHeight]);\n\n\tconst handleSave = () => {\n\t\tonUpdate({\n\t\t\talt: alt || undefined,\n\t\t\tcaption: caption || undefined,\n\t\t\ttitle: title || undefined,\n\t\t\tdisplayWidth,\n\t\t\tdisplayHeight,\n\t\t});\n\t\tonClose();\n\t};\n\n\tconst [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);\n\n\tconst handleDelete = () => {\n\t\tsetShowDeleteConfirm(true);\n\t};\n\n\tconst stableOnClose = useStableCallback(onClose);\n\tconst stableHandleSave = useStableCallback(handleSave);\n\n\t// Handle keyboard shortcuts\n\tReact.useEffect(() => {\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\") {\n\t\t\t\tstableOnClose();\n\t\t\t}\n\t\t\tif ((e.metaKey || e.ctrlKey) && e.key === \"s\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tstableHandleSave();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => window.removeEventListener(\"keydown\", handleKeyDown);\n\t}, [stableOnClose, stableHandleSave]);\n\n\tconst dialogs = (\n\t\t<>\n\t\t\t<ConfirmDialog\n\t\t\t\topen={showDeleteConfirm}\n\t\t\t\tonClose={() => setShowDeleteConfirm(false)}\n\t\t\t\ttitle=\"Remove Image?\"\n\t\t\t\tdescription=\"Remove this image from the document?\"\n\t\t\t\tconfirmLabel=\"Remove\"\n\t\t\t\tpendingLabel=\"Removing...\"\n\t\t\t\tisPending={false}\n\t\t\t\terror={null}\n\t\t\t\tonConfirm={() => {\n\t\t\t\t\tonDelete();\n\t\t\t\t\tonClose();\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t<MediaPickerModal\n\t\t\t\topen={showMediaPicker}\n\t\t\t\tonOpenChange={setShowMediaPicker}\n\t\t\t\tonSelect={handleMediaSelect}\n\t\t\t\tmimeTypeFilter=\"image/\"\n\t\t\t\ttitle=\"Replace Image\"\n\t\t\t/>\n\t\t</>\n\t);\n\n\tif (inline) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border bg-kumo-base flex flex-col animate-in fade-in duration-200\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"flex items-center justify-between p-4 border-b\">\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<SlidersHorizontal className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t<h3 className=\"text-sm font-semibold\">Image Settings</h3>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Close\" onClick={onClose}>\n\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Preview */}\n\t\t\t\t<div className=\"p-4 border-b\">\n\t\t\t\t\t<div className=\"aspect-video bg-kumo-tint rounded-lg overflow-hidden flex items-center justify-center relative group\">\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={attributes.src}\n\t\t\t\t\t\t\talt={attributes.alt || \"\"}\n\t\t\t\t\t\t\tclassName=\"max-h-full max-w-full object-contain\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<div className=\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\ticon={<ImageSquare />}\n\t\t\t\t\t\t\t\tonClick={() => setShowMediaPicker(true)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tReplace Image\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Original dimensions */}\n\t\t\t\t\t{(attributes.width || attributes.height) && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm mt-3\">\n\t\t\t\t\t\t\t<Ruler className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Original:</span>\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t{attributes.width} × {attributes.height}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Display Size */}\n\t\t\t\t{attributes.width && attributes.height && (\n\t\t\t\t\t<div className=\"p-4 border-b space-y-3\">\n\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t<Label>Display Size</Label>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tonClick={handleResetDimensions}\n\t\t\t\t\t\t\t\tclassName=\"h-auto py-1 px-2 text-xs\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tReset to original\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Width\"\n\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\tvalue={displayWidth ?? \"\"}\n\t\t\t\t\t\t\t\t\tonChange={(e) => handleWidthChange(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\tclassName=\"mt-5\"\n\t\t\t\t\t\t\t\tonClick={() => setLockAspectRatio(!lockAspectRatio)}\n\t\t\t\t\t\t\t\ttitle={lockAspectRatio ? \"Unlock aspect ratio\" : \"Lock aspect ratio\"}\n\t\t\t\t\t\t\t\taria-label={lockAspectRatio ? \"Unlock aspect ratio\" : \"Lock aspect ratio\"}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{lockAspectRatio ? (\n\t\t\t\t\t\t\t\t\t<LinkSimple className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<LinkBreak className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Height\"\n\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\tvalue={displayHeight ?? \"\"}\n\t\t\t\t\t\t\t\t\tonChange={(e) => handleHeightChange(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\tSet a custom display size for this image instance.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Editable Fields */}\n\t\t\t\t<div className=\"p-4 space-y-4\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Alt Text\"\n\t\t\t\t\t\tvalue={alt}\n\t\t\t\t\t\tonChange={(e) => setAlt(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"Describe this image for accessibility\"\n\t\t\t\t\t\tdescription=\"Required for accessibility. Describes the image for screen readers.\"\n\t\t\t\t\t/>\n\n\t\t\t\t\t<InputArea\n\t\t\t\t\t\tlabel=\"Caption\"\n\t\t\t\t\t\tvalue={caption}\n\t\t\t\t\t\tonChange={(e) => setCaption(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"Optional caption displayed below the image\"\n\t\t\t\t\t\tdescription=\"Displayed below the image as a visible caption.\"\n\t\t\t\t\t\trows={2}\n\t\t\t\t\t/>\n\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Title (Tooltip)\"\n\t\t\t\t\t\tvalue={title}\n\t\t\t\t\t\tonChange={(e) => setTitle(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"Optional tooltip on hover\"\n\t\t\t\t\t\tdescription=\"Shown when hovering over the image.\"\n\t\t\t\t\t/>\n\n\t\t\t\t\t{/* Source URL - only show for external images (no mediaId) */}\n\t\t\t\t\t{!attributes.mediaId && attributes.src && (\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Label>Source</Label>\n\t\t\t\t\t\t\t<div className=\"mt-1.5 flex gap-2\">\n\t\t\t\t\t\t\t\t<Input value={attributes.src} readOnly className=\"text-xs font-mono flex-1\" />\n\t\t\t\t\t\t\t\t<LinkButton\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\thref={attributes.src}\n\t\t\t\t\t\t\t\t\texternal\n\t\t\t\t\t\t\t\t\ttitle=\"Open in new tab\"\n\t\t\t\t\t\t\t\t\taria-label=\"Open in new tab\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<ArrowSquareOut className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</LinkButton>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Actions */}\n\t\t\t\t<div className=\"p-4 border-t flex items-center justify-between gap-2\">\n\t\t\t\t\t<Button variant=\"destructive\" size=\"sm\" onClick={handleDelete}>\n\t\t\t\t\t\tRemove Image\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button size=\"sm\" onClick={handleSave} disabled={!hasChanges}>\n\t\t\t\t\t\tSave\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{dialogs}\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"fixed inset-y-0 right-0 w-96 bg-kumo-base border-l shadow-xl z-50 flex flex-col animate-in slide-in-from-right duration-200\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between border-b p-4\">\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<SlidersHorizontal className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t<h2 className=\"font-semibold\">Image Settings</h2>\n\t\t\t\t</div>\n\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Close\" onClick={onClose}>\n\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t{/* Content */}\n\t\t\t<div className=\"flex-1 overflow-y-auto\">\n\t\t\t\t{/* Preview */}\n\t\t\t\t<div className=\"p-4 border-b\">\n\t\t\t\t\t<div className=\"aspect-video bg-kumo-tint rounded-lg overflow-hidden flex items-center justify-center relative group\">\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={attributes.src}\n\t\t\t\t\t\t\talt={attributes.alt || \"\"}\n\t\t\t\t\t\t\tclassName=\"max-h-full max-w-full object-contain\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<div className=\"absolute inset-0 bg-black/50 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\ticon={<ImageSquare />}\n\t\t\t\t\t\t\t\tonClick={() => setShowMediaPicker(true)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tReplace Image\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Image Info - original dimensions */}\n\t\t\t\t{(attributes.width || attributes.height) && (\n\t\t\t\t\t<div className=\"p-4 border-b\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm\">\n\t\t\t\t\t\t\t<Ruler className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Original:</span>\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t{attributes.width} × {attributes.height}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Display Size */}\n\t\t\t\t{attributes.width && attributes.height && (\n\t\t\t\t\t<div className=\"p-4 border-b space-y-3\">\n\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t<Label>Display Size</Label>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tonClick={handleResetDimensions}\n\t\t\t\t\t\t\t\tclassName=\"h-auto py-1 px-2 text-xs\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tReset to original\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Width\"\n\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\tvalue={displayWidth ?? \"\"}\n\t\t\t\t\t\t\t\t\tonChange={(e) => handleWidthChange(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\tclassName=\"mt-5\"\n\t\t\t\t\t\t\t\tonClick={() => setLockAspectRatio(!lockAspectRatio)}\n\t\t\t\t\t\t\t\ttitle={lockAspectRatio ? \"Unlock aspect ratio\" : \"Lock aspect ratio\"}\n\t\t\t\t\t\t\t\taria-label={lockAspectRatio ? \"Unlock aspect ratio\" : \"Lock aspect ratio\"}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{lockAspectRatio ? (\n\t\t\t\t\t\t\t\t\t<LinkSimple className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<LinkBreak className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Height\"\n\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\tvalue={displayHeight ?? \"\"}\n\t\t\t\t\t\t\t\t\tonChange={(e) => handleHeightChange(e.target.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\tSet a custom display size for this image instance.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Editable Fields */}\n\t\t\t\t<div className=\"p-4 space-y-4\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Alt Text\"\n\t\t\t\t\t\tvalue={alt}\n\t\t\t\t\t\tonChange={(e) => setAlt(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"Describe this image for accessibility\"\n\t\t\t\t\t\tdescription=\"Required for accessibility. Describes the image for screen readers.\"\n\t\t\t\t\t/>\n\n\t\t\t\t\t<InputArea\n\t\t\t\t\t\tlabel=\"Caption\"\n\t\t\t\t\t\tvalue={caption}\n\t\t\t\t\t\tonChange={(e) => setCaption(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"Optional caption displayed below the image\"\n\t\t\t\t\t\tdescription=\"Displayed below the image as a visible caption.\"\n\t\t\t\t\t\trows={2}\n\t\t\t\t\t/>\n\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Title (Tooltip)\"\n\t\t\t\t\t\tvalue={title}\n\t\t\t\t\t\tonChange={(e) => setTitle(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"Optional tooltip on hover\"\n\t\t\t\t\t\tdescription=\"Shown when hovering over the image.\"\n\t\t\t\t\t/>\n\n\t\t\t\t\t{/* Source URL - only show for external images (no mediaId) */}\n\t\t\t\t\t{!attributes.mediaId && attributes.src && (\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Label>Source</Label>\n\t\t\t\t\t\t\t<div className=\"mt-1.5 flex gap-2\">\n\t\t\t\t\t\t\t\t<Input value={attributes.src} readOnly className=\"text-xs font-mono flex-1\" />\n\t\t\t\t\t\t\t\t<LinkButton\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\thref={attributes.src}\n\t\t\t\t\t\t\t\t\texternal\n\t\t\t\t\t\t\t\t\ttitle=\"Open in new tab\"\n\t\t\t\t\t\t\t\t\taria-label=\"Open in new tab\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<ArrowSquareOut className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</LinkButton>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Footer */}\n\t\t\t<div className=\"p-4 border-t flex items-center justify-between gap-2\">\n\t\t\t\t<Button variant=\"destructive\" size=\"sm\" onClick={handleDelete}>\n\t\t\t\t\tRemove Image\n\t\t\t\t</Button>\n\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={onClose}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button size=\"sm\" onClick={handleSave} disabled={!hasChanges}>\n\t\t\t\t\t\tSave\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{dialogs}\n\t\t</div>\n\t);\n}\n\nexport default ImageDetailPanel;\n","// src/suggestion.ts\nimport { Plugin, PluginKey } from \"@tiptap/pm/state\";\nimport { Decoration, DecorationSet } from \"@tiptap/pm/view\";\n\n// src/findSuggestionMatch.ts\nimport { escapeForRegEx } from \"@tiptap/core\";\nfunction findSuggestionMatch(config) {\n var _a;\n const { char, allowSpaces: allowSpacesOption, allowToIncludeChar, allowedPrefixes, startOfLine, $position } = config;\n const allowSpaces = allowSpacesOption && !allowToIncludeChar;\n const escapedChar = escapeForRegEx(char);\n const suffix = new RegExp(`\\\\s${escapedChar}$`);\n const prefix = startOfLine ? \"^\" : \"\";\n const finalEscapedChar = allowToIncludeChar ? \"\" : escapedChar;\n const regexp = allowSpaces ? new RegExp(`${prefix}${escapedChar}.*?(?=\\\\s${finalEscapedChar}|$)`, \"gm\") : new RegExp(`${prefix}(?:^)?${escapedChar}[^\\\\s${finalEscapedChar}]*`, \"gm\");\n const text = ((_a = $position.nodeBefore) == null ? void 0 : _a.isText) && $position.nodeBefore.text;\n if (!text) {\n return null;\n }\n const textFrom = $position.pos - text.length;\n const match = Array.from(text.matchAll(regexp)).pop();\n if (!match || match.input === void 0 || match.index === void 0) {\n return null;\n }\n const matchPrefix = match.input.slice(Math.max(0, match.index - 1), match.index);\n const matchPrefixIsAllowed = new RegExp(`^[${allowedPrefixes == null ? void 0 : allowedPrefixes.join(\"\")}\\0]?$`).test(matchPrefix);\n if (allowedPrefixes !== null && !matchPrefixIsAllowed) {\n return null;\n }\n const from = textFrom + match.index;\n let to = from + match[0].length;\n if (allowSpaces && suffix.test(text.slice(to - 1, to + 1))) {\n match[0] += \" \";\n to += 1;\n }\n if (from < $position.pos && to >= $position.pos) {\n return {\n range: {\n from,\n to\n },\n query: match[0].slice(char.length),\n text: match[0]\n };\n }\n return null;\n}\n\n// src/suggestion.ts\nvar SuggestionPluginKey = new PluginKey(\"suggestion\");\nfunction Suggestion({\n pluginKey = SuggestionPluginKey,\n editor,\n char = \"@\",\n allowSpaces = false,\n allowToIncludeChar = false,\n allowedPrefixes = [\" \"],\n startOfLine = false,\n decorationTag = \"span\",\n decorationClass = \"suggestion\",\n decorationContent = \"\",\n decorationEmptyClass = \"is-empty\",\n command = () => null,\n items = () => [],\n render = () => ({}),\n allow = () => true,\n findSuggestionMatch: findSuggestionMatch2 = findSuggestionMatch,\n shouldShow\n}) {\n let props;\n const renderer = render == null ? void 0 : render();\n const getAnchorClientRect = () => {\n const pos = editor.state.selection.$anchor.pos;\n const coords = editor.view.coordsAtPos(pos);\n const { top, right, bottom, left } = coords;\n try {\n return new DOMRect(left, top, right - left, bottom - top);\n } catch {\n return null;\n }\n };\n const clientRectFor = (view, decorationNode) => {\n if (!decorationNode) {\n return getAnchorClientRect;\n }\n return () => {\n const state = pluginKey.getState(editor.state);\n const decorationId = state == null ? void 0 : state.decorationId;\n const currentDecorationNode = view.dom.querySelector(`[data-decoration-id=\"${decorationId}\"]`);\n return (currentDecorationNode == null ? void 0 : currentDecorationNode.getBoundingClientRect()) || null;\n };\n };\n function dispatchExit(view, pluginKeyRef) {\n var _a;\n try {\n const state = pluginKey.getState(view.state);\n const decorationNode = (state == null ? void 0 : state.decorationId) ? view.dom.querySelector(`[data-decoration-id=\"${state.decorationId}\"]`) : null;\n const exitProps = {\n // @ts-ignore editor is available in closure\n editor,\n range: (state == null ? void 0 : state.range) || { from: 0, to: 0 },\n query: (state == null ? void 0 : state.query) || null,\n text: (state == null ? void 0 : state.text) || null,\n items: [],\n command: (commandProps) => {\n return command({ editor, range: (state == null ? void 0 : state.range) || { from: 0, to: 0 }, props: commandProps });\n },\n decorationNode,\n clientRect: clientRectFor(view, decorationNode)\n };\n (_a = renderer == null ? void 0 : renderer.onExit) == null ? void 0 : _a.call(renderer, exitProps);\n } catch {\n }\n const tr = view.state.tr.setMeta(pluginKeyRef, { exit: true });\n view.dispatch(tr);\n }\n const plugin = new Plugin({\n key: pluginKey,\n view() {\n return {\n update: async (view, prevState) => {\n var _a, _b, _c, _d, _e, _f, _g;\n const prev = (_a = this.key) == null ? void 0 : _a.getState(prevState);\n const next = (_b = this.key) == null ? void 0 : _b.getState(view.state);\n const moved = prev.active && next.active && prev.range.from !== next.range.from;\n const started = !prev.active && next.active;\n const stopped = prev.active && !next.active;\n const changed = !started && !stopped && prev.query !== next.query;\n const handleStart = started || moved && changed;\n const handleChange = changed || moved;\n const handleExit = stopped || moved && changed;\n if (!handleStart && !handleChange && !handleExit) {\n return;\n }\n const state = handleExit && !handleStart ? prev : next;\n const decorationNode = view.dom.querySelector(`[data-decoration-id=\"${state.decorationId}\"]`);\n props = {\n editor,\n range: state.range,\n query: state.query,\n text: state.text,\n items: [],\n command: (commandProps) => {\n return command({\n editor,\n range: state.range,\n props: commandProps\n });\n },\n decorationNode,\n clientRect: clientRectFor(view, decorationNode)\n };\n if (handleStart) {\n (_c = renderer == null ? void 0 : renderer.onBeforeStart) == null ? void 0 : _c.call(renderer, props);\n }\n if (handleChange) {\n (_d = renderer == null ? void 0 : renderer.onBeforeUpdate) == null ? void 0 : _d.call(renderer, props);\n }\n if (handleChange || handleStart) {\n props.items = await items({\n editor,\n query: state.query\n });\n }\n if (handleExit) {\n (_e = renderer == null ? void 0 : renderer.onExit) == null ? void 0 : _e.call(renderer, props);\n }\n if (handleChange) {\n (_f = renderer == null ? void 0 : renderer.onUpdate) == null ? void 0 : _f.call(renderer, props);\n }\n if (handleStart) {\n (_g = renderer == null ? void 0 : renderer.onStart) == null ? void 0 : _g.call(renderer, props);\n }\n },\n destroy: () => {\n var _a;\n if (!props) {\n return;\n }\n (_a = renderer == null ? void 0 : renderer.onExit) == null ? void 0 : _a.call(renderer, props);\n }\n };\n },\n state: {\n // Initialize the plugin's internal state.\n init() {\n const state = {\n active: false,\n range: {\n from: 0,\n to: 0\n },\n query: null,\n text: null,\n composing: false\n };\n return state;\n },\n // Apply changes to the plugin state from a view transaction.\n apply(transaction, prev, _oldState, state) {\n const { isEditable } = editor;\n const { composing } = editor.view;\n const { selection } = transaction;\n const { empty, from } = selection;\n const next = { ...prev };\n const meta = transaction.getMeta(pluginKey);\n if (meta && meta.exit) {\n next.active = false;\n next.decorationId = null;\n next.range = { from: 0, to: 0 };\n next.query = null;\n next.text = null;\n return next;\n }\n next.composing = composing;\n if (isEditable && (empty || editor.view.composing)) {\n if ((from < prev.range.from || from > prev.range.to) && !composing && !prev.composing) {\n next.active = false;\n }\n const match = findSuggestionMatch2({\n char,\n allowSpaces,\n allowToIncludeChar,\n allowedPrefixes,\n startOfLine,\n $position: selection.$from\n });\n const decorationId = `id_${Math.floor(Math.random() * 4294967295)}`;\n if (match && allow({\n editor,\n state,\n range: match.range,\n isActive: prev.active\n }) && (!shouldShow || shouldShow({\n editor,\n range: match.range,\n query: match.query,\n text: match.text,\n transaction\n }))) {\n next.active = true;\n next.decorationId = prev.decorationId ? prev.decorationId : decorationId;\n next.range = match.range;\n next.query = match.query;\n next.text = match.text;\n } else {\n next.active = false;\n }\n } else {\n next.active = false;\n }\n if (!next.active) {\n next.decorationId = null;\n next.range = { from: 0, to: 0 };\n next.query = null;\n next.text = null;\n }\n return next;\n }\n },\n props: {\n // Call the keydown hook if suggestion is active.\n handleKeyDown(view, event) {\n var _a, _b, _c, _d;\n const { active, range } = plugin.getState(view.state);\n if (!active) {\n return false;\n }\n if (event.key === \"Escape\" || event.key === \"Esc\") {\n const state = plugin.getState(view.state);\n const cachedNode = (_a = props == null ? void 0 : props.decorationNode) != null ? _a : null;\n const decorationNode = cachedNode != null ? cachedNode : (state == null ? void 0 : state.decorationId) ? view.dom.querySelector(`[data-decoration-id=\"${state.decorationId}\"]`) : null;\n const handledByKeyDown = ((_b = renderer == null ? void 0 : renderer.onKeyDown) == null ? void 0 : _b.call(renderer, { view, event, range: state.range })) || false;\n if (handledByKeyDown) {\n return true;\n }\n const exitProps = {\n editor,\n range: state.range,\n query: state.query,\n text: state.text,\n items: [],\n command: (commandProps) => {\n return command({ editor, range: state.range, props: commandProps });\n },\n decorationNode,\n // If we have a cached decoration node, use it for the clientRect\n // to avoid another DOM lookup. If not, leave clientRect null and\n // let consumer decide if they want to query.\n clientRect: decorationNode ? () => {\n return decorationNode.getBoundingClientRect() || null;\n } : null\n };\n (_c = renderer == null ? void 0 : renderer.onExit) == null ? void 0 : _c.call(renderer, exitProps);\n dispatchExit(view, pluginKey);\n return true;\n }\n const handled = ((_d = renderer == null ? void 0 : renderer.onKeyDown) == null ? void 0 : _d.call(renderer, { view, event, range })) || false;\n return handled;\n },\n // Setup decorator on the currently active suggestion.\n decorations(state) {\n const { active, range, decorationId, query } = plugin.getState(state);\n if (!active) {\n return null;\n }\n const isEmpty = !(query == null ? void 0 : query.length);\n const classNames = [decorationClass];\n if (isEmpty) {\n classNames.push(decorationEmptyClass);\n }\n return DecorationSet.create(state.doc, [\n Decoration.inline(range.from, range.to, {\n nodeName: decorationTag,\n class: classNames.join(\" \"),\n \"data-decoration-id\": decorationId,\n \"data-decoration-content\": decorationContent\n })\n ]);\n }\n }\n });\n return plugin;\n}\nfunction exitSuggestion(view, pluginKeyRef = SuggestionPluginKey) {\n const tr = view.state.tr.setMeta(pluginKeyRef, { exit: true });\n view.dispatch(tr);\n}\n\n// src/index.ts\nvar index_default = Suggestion;\nexport {\n Suggestion,\n SuggestionPluginKey,\n index_default as default,\n exitSuggestion,\n findSuggestionMatch\n};\n//# sourceMappingURL=index.js.map","/**\n * Block Menu Component\n *\n * Floating menu that appears when a block is selected via drag handle click.\n * Provides block actions:\n * - Turn into (transform to different block type)\n * - Duplicate\n * - Delete\n *\n * Uses Floating UI for positioning relative to the selected block.\n */\n\nimport { Button } from \"@cloudflare/kumo\";\nimport { useFloating, offset, flip, shift, autoUpdate } from \"@floating-ui/react\";\nimport {\n\tDotsSixVertical,\n\tParagraph,\n\tTextHOne,\n\tTextHTwo,\n\tTextHThree,\n\tQuotes,\n\tCode,\n\tList,\n\tListNumbers,\n\tCopy,\n\tTrash,\n\tCaretRight,\n\ttype Icon as PhosphorIcon,\n} from \"@phosphor-icons/react\";\nimport type { Editor } from \"@tiptap/react\";\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport { useStableCallback } from \"../../lib/hooks\";\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Block transform options\n */\ninterface BlockTransform {\n\tid: string;\n\tlabel: string;\n\ticon: PhosphorIcon;\n\ttransform: (editor: Editor) => void;\n}\n\nconst blockTransforms: BlockTransform[] = [\n\t{\n\t\tid: \"paragraph\",\n\t\tlabel: \"Paragraph\",\n\t\ticon: Paragraph,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().setNode(\"paragraph\").run();\n\t\t},\n\t},\n\t{\n\t\tid: \"heading1\",\n\t\tlabel: \"Heading 1\",\n\t\ticon: TextHOne,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().setNode(\"heading\", { level: 1 }).run();\n\t\t},\n\t},\n\t{\n\t\tid: \"heading2\",\n\t\tlabel: \"Heading 2\",\n\t\ticon: TextHTwo,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().setNode(\"heading\", { level: 2 }).run();\n\t\t},\n\t},\n\t{\n\t\tid: \"heading3\",\n\t\tlabel: \"Heading 3\",\n\t\ticon: TextHThree,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().setNode(\"heading\", { level: 3 }).run();\n\t\t},\n\t},\n\t{\n\t\tid: \"blockquote\",\n\t\tlabel: \"Quote\",\n\t\ticon: Quotes,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().toggleBlockquote().run();\n\t\t},\n\t},\n\t{\n\t\tid: \"codeBlock\",\n\t\tlabel: \"Code Block\",\n\t\ticon: Code,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().toggleCodeBlock().run();\n\t\t},\n\t},\n\t{\n\t\tid: \"bulletList\",\n\t\tlabel: \"Bullet List\",\n\t\ticon: List,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().toggleBulletList().run();\n\t\t},\n\t},\n\t{\n\t\tid: \"orderedList\",\n\t\tlabel: \"Numbered List\",\n\t\ticon: ListNumbers,\n\t\ttransform: (editor) => {\n\t\t\teditor.chain().focus().toggleOrderedList().run();\n\t\t},\n\t},\n];\n\ninterface BlockMenuProps {\n\teditor: Editor;\n\t/** The DOM element of the selected block (for positioning) */\n\tanchorElement: HTMLElement | null;\n\t/** Whether the menu is open */\n\tisOpen: boolean;\n\t/** Callback to close the menu */\n\tonClose: () => void;\n}\n\n/**\n * Block Menu - floating menu for block-level actions\n */\nexport function BlockMenu({ editor, anchorElement, isOpen, onClose }: BlockMenuProps) {\n\tconst [showTransforms, setShowTransforms] = React.useState(false);\n\tconst menuRef = React.useRef<HTMLDivElement>(null);\n\tconst stableOnClose = useStableCallback(onClose);\n\n\tconst { refs, floatingStyles } = useFloating({\n\t\topen: isOpen,\n\t\tplacement: \"left-start\",\n\t\tmiddleware: [offset({ mainAxis: 8, crossAxis: 0 }), flip(), shift({ padding: 8 })],\n\t\twhileElementsMounted: autoUpdate,\n\t});\n\n\t// Sync the anchor element\n\tReact.useEffect(() => {\n\t\tif (anchorElement) {\n\t\t\trefs.setReference(anchorElement);\n\t\t}\n\t}, [anchorElement, refs]);\n\n\t// Close on escape\n\tReact.useEffect(() => {\n\t\tif (!isOpen) return;\n\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (showTransforms) {\n\t\t\t\t\tsetShowTransforms(false);\n\t\t\t\t} else {\n\t\t\t\t\tstableOnClose();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => document.removeEventListener(\"keydown\", handleKeyDown);\n\t}, [isOpen, stableOnClose, showTransforms]);\n\n\t// Close on click outside\n\tReact.useEffect(() => {\n\t\tif (!isOpen) return;\n\n\t\tconst handleClickOutside = (e: MouseEvent) => {\n\t\t\tconst target = e.target;\n\t\t\t// Don't close if clicking on the drag handle or menu itself\n\t\t\tif (target instanceof Node && menuRef.current?.contains(target)) return;\n\t\t\tif (target instanceof Element && target.closest(\"[data-block-handle]\")) return;\n\n\t\t\tstableOnClose();\n\t\t};\n\n\t\t// Delay to avoid immediate close from the click that opened it\n\t\tconst timer = setTimeout(() => {\n\t\t\tdocument.addEventListener(\"mousedown\", handleClickOutside);\n\t\t}, 0);\n\n\t\treturn () => {\n\t\t\tclearTimeout(timer);\n\t\t\tdocument.removeEventListener(\"mousedown\", handleClickOutside);\n\t\t};\n\t}, [isOpen, stableOnClose]);\n\n\t// Reset submenu state when menu closes\n\tReact.useEffect(() => {\n\t\tif (!isOpen) {\n\t\t\tsetShowTransforms(false);\n\t\t}\n\t}, [isOpen]);\n\n\tconst handleDuplicate = () => {\n\t\tconst { selection } = editor.state;\n\t\tconst { $from, $to } = selection;\n\n\t\t// Get the block node at current position\n\t\tconst blockStart = $from.start($from.depth);\n\t\tconst blockEnd = $to.end($to.depth);\n\n\t\t// Get the content to duplicate\n\t\tconst slice = editor.state.doc.slice(blockStart, blockEnd);\n\n\t\t// Insert after current block\n\t\teditor\n\t\t\t.chain()\n\t\t\t.focus()\n\t\t\t.command(({ tr }) => {\n\t\t\t\ttr.insert(blockEnd + 1, slice.content);\n\t\t\t\treturn true;\n\t\t\t})\n\t\t\t.run();\n\n\t\tonClose();\n\t};\n\n\tconst handleDelete = () => {\n\t\teditor.chain().focus().deleteNode(editor.state.selection.$from.parent.type.name).run();\n\t\tonClose();\n\t};\n\n\tconst handleTransform = (transform: BlockTransform) => {\n\t\ttransform.transform(editor);\n\t\tonClose();\n\t};\n\n\tif (!isOpen) return null;\n\n\treturn createPortal(\n\t\t<div\n\t\t\tref={(node) => {\n\t\t\t\tmenuRef.current = node;\n\t\t\t\trefs.setFloating(node);\n\t\t\t}}\n\t\t\tstyle={floatingStyles}\n\t\t\tclassName=\"z-[100] rounded-lg border bg-kumo-overlay shadow-lg min-w-[180px] overflow-hidden\"\n\t\t>\n\t\t\t{showTransforms ? (\n\t\t\t\t// Transform submenu\n\t\t\t\t<div className=\"py-1\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex items-center gap-2 w-full px-3 py-2 text-sm hover:bg-kumo-tint text-left\"\n\t\t\t\t\t\tonClick={() => setShowTransforms(false)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<CaretRight className=\"h-4 w-4 rotate-180\" />\n\t\t\t\t\t\t<span>Back</span>\n\t\t\t\t\t</button>\n\t\t\t\t\t<div className=\"h-px bg-kumo-line my-1\" />\n\t\t\t\t\t{blockTransforms.map((transform) => (\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tkey={transform.id}\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 w-full px-3 py-2 text-sm hover:bg-kumo-tint text-left\"\n\t\t\t\t\t\t\tonClick={() => handleTransform(transform)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<transform.icon className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<span>{transform.label}</span>\n\t\t\t\t\t\t</button>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t// Main menu\n\t\t\t\t<div className=\"py-1\">\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex items-center justify-between w-full px-3 py-2 text-sm hover:bg-kumo-tint text-left\"\n\t\t\t\t\t\tonClick={() => setShowTransforms(true)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<Paragraph className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<span>Turn into</span>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<CaretRight className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t</button>\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex items-center gap-2 w-full px-3 py-2 text-sm hover:bg-kumo-tint text-left\"\n\t\t\t\t\t\tonClick={handleDuplicate}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Copy className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t<span>Duplicate</span>\n\t\t\t\t\t</button>\n\t\t\t\t\t<div className=\"h-px bg-kumo-line my-1\" />\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tclassName=\"flex items-center gap-2 w-full px-3 py-2 text-sm hover:bg-kumo-tint text-left text-kumo-danger\"\n\t\t\t\t\t\tonClick={handleDelete}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t\t\t<span>Delete</span>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>,\n\t\tdocument.body,\n\t);\n}\n\n/**\n * Block Drag Handle Component\n *\n * Shown in the left gutter of each block. Clicking opens the block menu,\n * dragging reorders blocks.\n */\ninterface BlockHandleProps {\n\tonClick: (e: React.MouseEvent) => void;\n\tonDragStart?: (e: React.DragEvent) => void;\n\tselected?: boolean;\n}\n\nexport function BlockHandle({ onClick, onDragStart, selected }: BlockHandleProps) {\n\treturn (\n\t\t<Button\n\t\t\ttype=\"button\"\n\t\t\tvariant=\"ghost\"\n\t\t\tshape=\"square\"\n\t\t\tclassName={cn(\n\t\t\t\t\"h-6 w-6 cursor-grab active:cursor-grabbing\",\n\t\t\t\t\"text-kumo-subtle/50 hover:text-kumo-subtle\",\n\t\t\t\tselected && \"text-kumo-subtle\",\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t\tonDragStart={onDragStart}\n\t\t\tdraggable\n\t\t\tdata-block-handle\n\t\t\taria-label=\"Drag to reorder block\"\n\t\t>\n\t\t\t<DotsSixVertical className=\"h-4 w-4\" />\n\t\t</Button>\n\t);\n}\n\nexport { blockTransforms };\nexport type { BlockTransform };\n","/**\n * Drag Handle Wrapper Component\n *\n * Wraps TipTap's official DragHandle React component with our BlockMenu.\n * This component provides:\n * - Drag handles that appear on block hover\n * - Actual drag-and-drop block reordering (handled by TipTap)\n * - Block menu integration for transforms, duplicate, delete\n */\n\nimport { DotsSixVertical } from \"@phosphor-icons/react\";\nimport type { Editor } from \"@tiptap/core\";\nimport { DragHandle } from \"@tiptap/extension-drag-handle-react\";\nimport type { Node as PMNode } from \"@tiptap/pm/model\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\nimport { BlockMenu } from \"./BlockMenu\";\n\ninterface DragHandleWrapperProps {\n\teditor: Editor;\n}\n\ninterface HoveredNode {\n\tnode: PMNode;\n\tpos: number;\n}\n\n// Extend Editor commands type to include DragHandle commands\ndeclare module \"@tiptap/core\" {\n\tinterface Commands<ReturnType> {\n\t\tdragHandle: {\n\t\t\tlockDragHandle: () => ReturnType;\n\t\t\tunlockDragHandle: () => ReturnType;\n\t\t\ttoggleDragHandle: () => ReturnType;\n\t\t};\n\t}\n}\n\n/**\n * DragHandleWrapper - Official TipTap drag handle with BlockMenu integration\n */\nexport function DragHandleWrapper({ editor }: DragHandleWrapperProps) {\n\tconst [hoveredNode, setHoveredNode] = React.useState<HoveredNode | null>(null);\n\tconst [menuOpen, setMenuOpen] = React.useState(false);\n\tconst [menuAnchor, setMenuAnchor] = React.useState<HTMLElement | null>(null);\n\tconst handleRef = React.useRef<HTMLButtonElement>(null);\n\n\t// Handle click on drag handle to open menu\n\tconst handleClick = React.useCallback(\n\t\t(e: React.MouseEvent) => {\n\t\t\te.preventDefault();\n\t\t\te.stopPropagation();\n\n\t\t\tif (!hoveredNode) return;\n\n\t\t\t// Select the block in the editor\n\t\t\teditor.chain().setNodeSelection(hoveredNode.pos).run();\n\n\t\t\t// Open the menu\n\t\t\tsetMenuAnchor(handleRef.current);\n\t\t\tsetMenuOpen(true);\n\n\t\t\t// Lock the drag handle so it stays visible while menu is open\n\t\t\teditor.commands.lockDragHandle();\n\t\t},\n\t\t[editor, hoveredNode],\n\t);\n\n\t// Close the menu\n\tconst handleCloseMenu = React.useCallback(() => {\n\t\tsetMenuOpen(false);\n\t\tsetMenuAnchor(null);\n\t\teditor.commands.unlockDragHandle();\n\t}, [editor]);\n\n\t// Handle node change from drag handle\n\tconst handleNodeChange = React.useCallback(\n\t\t(data: { node: PMNode | null; editor: Editor; pos: number }) => {\n\t\t\tif (data.node) {\n\t\t\t\tsetHoveredNode({ node: data.node, pos: data.pos });\n\t\t\t} else {\n\t\t\t\t// Only clear if menu is not open\n\t\t\t\tif (!menuOpen) {\n\t\t\t\t\tsetHoveredNode(null);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\t[menuOpen],\n\t);\n\n\t// Stable reference — DragHandle's useEffect depends on this by reference.\n\t// An inline object causes plugin unregister/register every render, which\n\t// tears down the Suggestion plugin view (calling onExit → setState → loop).\n\tconst computePositionConfig = React.useMemo(\n\t\t() => ({\n\t\t\tplacement: \"left-start\" as const,\n\t\t\tstrategy: \"absolute\" as const,\n\t\t}),\n\t\t[],\n\t);\n\n\treturn (\n\t\t<>\n\t\t\t<DragHandle\n\t\t\t\teditor={editor}\n\t\t\t\tonNodeChange={handleNodeChange}\n\t\t\t\tcomputePositionConfig={computePositionConfig}\n\t\t\t>\n\t\t\t\t<button\n\t\t\t\t\tref={handleRef}\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex items-center justify-center\",\n\t\t\t\t\t\t\"w-6 h-6 rounded select-none\",\n\t\t\t\t\t\t\"text-kumo-subtle/50 hover:text-kumo-subtle\",\n\t\t\t\t\t\t\"hover:bg-kumo-tint/80 cursor-grab active:cursor-grabbing\",\n\t\t\t\t\t\t\"transition-colors duration-100\",\n\t\t\t\t\t\tmenuOpen && \"text-kumo-subtle bg-kumo-tint\",\n\t\t\t\t\t)}\n\t\t\t\t\tonClick={handleClick}\n\t\t\t\t\tdata-block-handle\n\t\t\t\t\taria-label=\"Block actions - drag to reorder, click for menu\"\n\t\t\t\t>\n\t\t\t\t\t<DotsSixVertical className=\"h-4 w-4\" />\n\t\t\t\t</button>\n\t\t\t</DragHandle>\n\n\t\t\t{/* Block menu */}\n\t\t\t<BlockMenu\n\t\t\t\teditor={editor}\n\t\t\t\tanchorElement={menuAnchor}\n\t\t\t\tisOpen={menuOpen}\n\t\t\t\tonClose={handleCloseMenu}\n\t\t\t/>\n\t\t</>\n\t);\n}\n","/**\n * Custom Image Node for TipTap\n *\n * Provides a selectable, editable image with:\n * - Click to select\n * - Visual selection indicator\n * - Quick inline alt text editing\n * - Full detail panel for advanced settings\n * - Delete/replace options\n */\n\nimport { Button, Input } from \"@cloudflare/kumo\";\nimport { Trash, Pencil, X, Check, SlidersHorizontal } from \"@phosphor-icons/react\";\nimport type { NodeViewProps } from \"@tiptap/react\";\nimport { Node, mergeAttributes } from \"@tiptap/react\";\nimport { ReactNodeViewRenderer, NodeViewWrapper } from \"@tiptap/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\nimport type { ImageAttributes } from \"./ImageDetailPanel\";\n\n// Extend the Commands interface to include setImage\ndeclare module \"@tiptap/react\" {\n\tinterface Commands<ReturnType> {\n\t\timage: {\n\t\t\tsetImage: (options: {\n\t\t\t\tsrc: string;\n\t\t\t\talt?: string;\n\t\t\t\ttitle?: string;\n\t\t\t\tcaption?: string;\n\t\t\t\tmediaId?: string;\n\t\t\t\t/** Provider ID for external media (e.g., \"cloudflare-images\") */\n\t\t\t\tprovider?: string;\n\t\t\t\twidth?: number;\n\t\t\t\theight?: number;\n\t\t\t\tdisplayWidth?: number;\n\t\t\t\tdisplayHeight?: number;\n\t\t\t}) => ReturnType;\n\t\t};\n\t}\n}\n\n// React component for the image node view\nfunction ImageNodeView({ node, updateAttributes, selected, deleteNode, editor }: NodeViewProps) {\n\tconst [isEditingAlt, setIsEditingAlt] = React.useState(false);\n\tconst [altText, setAltText] = React.useState(node.attrs.alt || \"\");\n\n\t/** Whether this node currently has its sidebar panel open */\n\tconst sidebarOpenRef = React.useRef(false);\n\n\tconst handleSaveAlt = () => {\n\t\tupdateAttributes({ alt: altText });\n\t\tsetIsEditingAlt(false);\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\") {\n\t\t\te.preventDefault();\n\t\t\thandleSaveAlt();\n\t\t} else if (e.key === \"Escape\") {\n\t\t\tsetAltText(node.attrs.alt || \"\");\n\t\t\tsetIsEditingAlt(false);\n\t\t}\n\t};\n\n\t// Sync local alt text state when node attributes change\n\tReact.useEffect(() => {\n\t\tsetAltText(node.attrs.alt || \"\");\n\t}, [node.attrs.alt]);\n\n\tconst getImageAttrs = (): ImageAttributes => ({\n\t\tsrc: node.attrs.src,\n\t\talt: node.attrs.alt,\n\t\ttitle: node.attrs.title,\n\t\tcaption: node.attrs.caption,\n\t\tmediaId: node.attrs.mediaId,\n\t\twidth: node.attrs.width,\n\t\theight: node.attrs.height,\n\t\tdisplayWidth: node.attrs.displayWidth,\n\t\tdisplayHeight: node.attrs.displayHeight,\n\t});\n\n\tconst openSidebar = () => {\n\t\tconst storage = (editor.storage as unknown as Record<string, Record<string, unknown>>).image;\n\t\tconst onOpen = storage?.onOpenBlockSidebar as\n\t\t\t| ((panel: {\n\t\t\t\t\ttype: \"image\";\n\t\t\t\t\tattrs: ImageAttributes;\n\t\t\t\t\tonUpdate: (attrs: Partial<ImageAttributes>) => void;\n\t\t\t\t\tonReplace: (attrs: ImageAttributes) => void;\n\t\t\t\t\tonDelete: () => void;\n\t\t\t\t\tonClose: () => void;\n\t\t\t }) => void)\n\t\t\t| null;\n\t\tif (onOpen) {\n\t\t\tsidebarOpenRef.current = true;\n\t\t\tonOpen({\n\t\t\t\ttype: \"image\",\n\t\t\t\tattrs: getImageAttrs(),\n\t\t\t\tonUpdate: (attrs: Partial<ImageAttributes>) => updateAttributes(attrs),\n\t\t\t\tonReplace: (attrs: ImageAttributes) => updateAttributes(attrs),\n\t\t\t\tonDelete: () => deleteNode(),\n\t\t\t\tonClose: () => {\n\t\t\t\t\tsidebarOpenRef.current = false;\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\t};\n\n\tconst closeSidebar = () => {\n\t\tif (!sidebarOpenRef.current) return;\n\t\tconst storage = (editor.storage as unknown as Record<string, Record<string, unknown>>).image;\n\t\tconst onClose = storage?.onCloseBlockSidebar as (() => void) | null;\n\t\tif (onClose) {\n\t\t\tonClose();\n\t\t\tsidebarOpenRef.current = false;\n\t\t}\n\t};\n\n\tconst toggleSidebar = () => {\n\t\tif (sidebarOpenRef.current) {\n\t\t\tcloseSidebar();\n\t\t} else {\n\t\t\topenSidebar();\n\t\t}\n\t};\n\n\t// Close sidebar when this node is deselected\n\tReact.useEffect(() => {\n\t\tif (!selected) {\n\t\t\tcloseSidebar();\n\t\t}\n\t}, [selected]);\n\n\treturn (\n\t\t<NodeViewWrapper\n\t\t\tclassName={cn(\n\t\t\t\t\"relative my-4 group\",\n\t\t\t\tselected && \"ring-2 ring-kumo-brand ring-offset-2 rounded-lg\",\n\t\t\t)}\n\t\t>\n\t\t\t<figure className=\"relative\">\n\t\t\t\t<img\n\t\t\t\t\tsrc={node.attrs.src}\n\t\t\t\t\talt={node.attrs.alt || \"\"}\n\t\t\t\t\ttitle={node.attrs.title || \"\"}\n\t\t\t\t\tclassName=\"rounded-lg max-w-full mx-auto\"\n\t\t\t\t\tstyle={{\n\t\t\t\t\t\twidth: node.attrs.displayWidth ? `${node.attrs.displayWidth}px` : undefined,\n\t\t\t\t\t\theight: node.attrs.displayHeight ? `${node.attrs.displayHeight}px` : undefined,\n\t\t\t\t\t}}\n\t\t\t\t\tdraggable={false}\n\t\t\t\t/>\n\n\t\t\t\t{/* Selection overlay with actions */}\n\t\t\t\t{selected && (\n\t\t\t\t\t<div className=\"absolute top-2 right-2 flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity\">\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\tonMouseDown={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonClick={() => setIsEditingAlt(true)}\n\t\t\t\t\t\t\ttitle=\"Quick edit alt text\"\n\t\t\t\t\t\t\taria-label=\"Quick edit alt text\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Pencil className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\tonMouseDown={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonClick={toggleSidebar}\n\t\t\t\t\t\t\ttitle=\"Image settings\"\n\t\t\t\t\t\t\taria-label=\"Image settings\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<SlidersHorizontal className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\tonMouseDown={(e) => e.preventDefault()}\n\t\t\t\t\t\t\tonClick={() => deleteNode()}\n\t\t\t\t\t\t\ttitle=\"Delete image\"\n\t\t\t\t\t\t\taria-label=\"Delete image\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Quick alt text editor (inline) */}\n\t\t\t\t{isEditingAlt && (\n\t\t\t\t\t<div className=\"absolute bottom-0 left-0 right-0 bg-kumo-base/95 backdrop-blur p-3 rounded-b-lg border-t\">\n\t\t\t\t\t\t<label className=\"text-xs font-medium text-kumo-subtle mb-1 block\">Alt text</label>\n\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tvalue={altText}\n\t\t\t\t\t\t\t\tonChange={(e) => setAltText(e.target.value)}\n\t\t\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\t\t\tplaceholder=\"Describe the image...\"\n\t\t\t\t\t\t\t\tclassName=\"flex-1 h-8 text-sm\"\n\t\t\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\t\tonMouseDown={(e) => e.preventDefault()}\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tsetAltText(node.attrs.alt || \"\");\n\t\t\t\t\t\t\t\t\tsetIsEditingAlt(false);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\ttitle=\"Cancel\"\n\t\t\t\t\t\t\t\taria-label=\"Cancel\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\t\tonMouseDown={(e) => e.preventDefault()}\n\t\t\t\t\t\t\t\tonClick={handleSaveAlt}\n\t\t\t\t\t\t\t\ttitle=\"Save\"\n\t\t\t\t\t\t\t\taria-label=\"Save alt text\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Check className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Caption display (shows caption if set, falls back to alt) */}\n\t\t\t\t{!isEditingAlt && (node.attrs.caption || node.attrs.alt) && (\n\t\t\t\t\t<figcaption className=\"text-center text-sm text-kumo-subtle mt-2\">\n\t\t\t\t\t\t{node.attrs.caption || node.attrs.alt}\n\t\t\t\t\t</figcaption>\n\t\t\t\t)}\n\t\t\t</figure>\n\t\t</NodeViewWrapper>\n\t);\n}\n\n// Custom Image extension with React NodeView\nexport const ImageExtension = Node.create({\n\tname: \"image\",\n\n\taddOptions() {\n\t\treturn {\n\t\t\tinline: false,\n\t\t\tallowBase64: false,\n\t\t\tHTMLAttributes: {},\n\t\t};\n\t},\n\n\taddStorage() {\n\t\treturn {\n\t\t\t/** Callback set by PortableTextEditor to open image settings in the content sidebar */\n\t\t\tonOpenBlockSidebar: null as\n\t\t\t\t| ((panel: {\n\t\t\t\t\t\ttype: \"image\";\n\t\t\t\t\t\tattrs: import(\"./ImageDetailPanel\").ImageAttributes;\n\t\t\t\t\t\tonUpdate: (attrs: Partial<import(\"./ImageDetailPanel\").ImageAttributes>) => void;\n\t\t\t\t\t\tonReplace: (attrs: import(\"./ImageDetailPanel\").ImageAttributes) => void;\n\t\t\t\t\t\tonDelete: () => void;\n\t\t\t\t\t\tonClose: () => void;\n\t\t\t\t }) => void)\n\t\t\t\t| null,\n\t\t\t/** Callback set by PortableTextEditor to close the sidebar */\n\t\t\tonCloseBlockSidebar: null as (() => void) | null,\n\t\t};\n\t},\n\n\tinline() {\n\t\treturn this.options.inline;\n\t},\n\n\tgroup() {\n\t\treturn this.options.inline ? \"inline\" : \"block\";\n\t},\n\n\tdraggable: true,\n\n\taddAttributes() {\n\t\treturn {\n\t\t\tsrc: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\talt: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\ttitle: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\tcaption: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\tmediaId: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\t/** Provider ID for external media (e.g., \"cloudflare-images\") */\n\t\t\tprovider: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\twidth: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\theight: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\tdisplayWidth: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\tdisplayHeight: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t};\n\t},\n\n\tparseHTML() {\n\t\treturn [\n\t\t\t{\n\t\t\t\ttag: \"img[src]\",\n\t\t\t},\n\t\t];\n\t},\n\n\trenderHTML({ HTMLAttributes }: { HTMLAttributes: Record<string, unknown> }) {\n\t\treturn [\"img\", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)];\n\t},\n\n\taddNodeView() {\n\t\treturn ReactNodeViewRenderer(ImageNodeView);\n\t},\n\n\taddCommands() {\n\t\treturn {\n\t\t\tsetImage:\n\t\t\t\t(options: {\n\t\t\t\t\tsrc: string;\n\t\t\t\t\talt?: string;\n\t\t\t\t\ttitle?: string;\n\t\t\t\t\tcaption?: string;\n\t\t\t\t\tmediaId?: string;\n\t\t\t\t\tprovider?: string;\n\t\t\t\t\twidth?: number;\n\t\t\t\t\theight?: number;\n\t\t\t\t\tdisplayWidth?: number;\n\t\t\t\t\tdisplayHeight?: number;\n\t\t\t\t}) =>\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any\n\t\t\t\t({ commands }: any) => {\n\t\t\t\t\treturn commands.insertContent({\n\t\t\t\t\t\ttype: this.name,\n\t\t\t\t\t\tattrs: options,\n\t\t\t\t\t});\n\t\t\t\t},\n\t\t};\n\t},\n});\n","/**\n * Markdown Link Extension for TipTap\n *\n * Converts markdown link syntax into proper link marks:\n * - Typing `[text](url)` converts on closing paren\n * - Pasting text containing `[text](url)` converts inline\n * - Rejects disallowed protocols (e.g. `javascript:`) via Link's allowlist\n *\n * Augments the existing Link mark from StarterKit — no new marks added.\n */\n\nimport { Extension, InputRule, PasteRule } from \"@tiptap/core\";\nimport { isAllowedUri } from \"@tiptap/extension-link\";\nimport type { EditorState } from \"@tiptap/pm/state\";\n\n// Matches [link text](https://url.com) — typed (input rule, end-anchored)\n// match[1] = link text, match[2] = href\nconst MARKDOWN_LINK_INPUT_REGEX = /\\[([^\\]]+)\\]\\(([^)]+)\\)$/;\n\n// Matches [link text](https://url.com) — pasted (paste rule, global)\n// match[1] = link text, match[2] = href\nconst MARKDOWN_LINK_PASTE_REGEX = /\\[([^\\]]+)\\]\\(([^)]+)\\)/g;\n\n/** Shared handler context — InputRule and PasteRule use the same shape. */\ninterface RuleMatch {\n\tstate: EditorState;\n\trange: { from: number; to: number };\n\tmatch: RegExpMatchArray;\n}\n\n/**\n * Replace a `[text](url)` match with `text` carrying the link mark.\n * Returns null (no-op) if the URL fails the protocol allowlist.\n *\n * Shared by both the input rule and paste rule — the handler signature\n * for InputRule and PasteRule is identical.\n */\nfunction handleMarkdownLink({ state, range, match }: RuleMatch): null | void {\n\tconst linkType = state.schema.marks[\"link\"];\n\tconst linkText = match[1];\n\tconst href = match[2]?.trim();\n\n\tif (!linkType || !linkText || !href || !isAllowedUri(href)) return null;\n\n\tconst { tr } = state;\n\tconst mark = linkType.create({ href });\n\n\ttr.replaceWith(range.from, range.to, state.schema.text(linkText, [mark]));\n\ttr.removeStoredMark(linkType);\n}\n\n/**\n * Adds markdown link syntax support to the TipTap editor.\n *\n * Typing `[text](url)` and completing the closing `)` converts the syntax\n * into a proper link mark. Pasting text containing `[text](url)` patterns\n * also converts them. URLs that fail the protocol allowlist (e.g. `javascript:`)\n * are silently ignored, leaving the markdown syntax as literal text.\n *\n * Uses raw InputRule/PasteRule rather than the markInputRule/markPasteRule\n * helpers because those helpers unconditionally use the last capture group as\n * the replacement text — we need group 1 (text) as content and group 2 (href)\n * as the attribute, so we write the transaction by hand.\n *\n * This augments the Link mark already provided by StarterKit — no new\n * dependencies required.\n */\nexport const MarkdownLinkExtension = Extension.create({\n\tname: \"markdownLink\",\n\n\taddInputRules() {\n\t\treturn [\n\t\t\tnew InputRule({\n\t\t\t\tfind: MARKDOWN_LINK_INPUT_REGEX,\n\t\t\t\thandler: handleMarkdownLink,\n\t\t\t}),\n\t\t];\n\t},\n\n\taddPasteRules() {\n\t\treturn [\n\t\t\tnew PasteRule({\n\t\t\t\tfind: MARKDOWN_LINK_PASTE_REGEX,\n\t\t\t\thandler: handleMarkdownLink,\n\t\t\t}),\n\t\t];\n\t},\n});\n","/**\n * Plugin Block Node for TipTap\n *\n * Renders embed blocks (YouTube, Vimeo, tweets, etc.) with:\n * - Selection indicator with ring\n * - Inline URL editing via popover\n * - Drag handle in left gutter\n * - Action buttons on hover/selection\n * - Keyboard support\n */\n\nimport { Button, Input } from \"@cloudflare/kumo\";\nimport {\n\tDotsSixVertical,\n\tTrash,\n\tPencil,\n\tX,\n\tCheck,\n\tArrowSquareOut,\n\tYoutubeLogo,\n\tLinkSimple,\n\tCode,\n\tCopy,\n\tCube,\n\tListBullets,\n} from \"@phosphor-icons/react\";\nimport type { Element } from \"@emdash-cms/blocks\";\nimport { Node, mergeAttributes } from \"@tiptap/core\";\nimport type { NodeViewProps } from \"@tiptap/react\";\nimport { ReactNodeViewRenderer, NodeViewWrapper } from \"@tiptap/react\";\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\n/**\n * Plugin block definition for slash commands\n */\nexport interface PluginBlockDef {\n\ttype: string;\n\tpluginId: string;\n\tlabel: string;\n\ticon?: string;\n\tdescription?: string;\n\tplaceholder?: string;\n\t/** Block Kit form fields. If declared, replaces the simple URL input. */\n\tfields?: Element[];\n}\n\n// =============================================================================\n// Plugin Block Registry (stored per-editor instance via TipTap extension storage)\n// =============================================================================\n\n/** Register plugin block definitions into editor storage so the node view can look up metadata */\nexport function registerPluginBlocks(\n\teditor: { storage: Record<string, Record<string, unknown>> },\n\tblocks: PluginBlockDef[],\n): void {\n\tconst registry = new Map<string, PluginBlockDef>();\n\tfor (const block of blocks) {\n\t\tregistry.set(block.type, block);\n\t}\n\tconst storage = editor.storage.pluginBlock as Record<string, unknown> | undefined;\n\tif (storage) {\n\t\tstorage.registry = registry;\n\t}\n}\n\n/** Read the registry from editor storage */\nfunction getRegistry(editor: {\n\tstorage: Record<string, Record<string, unknown>>;\n}): Map<string, PluginBlockDef> {\n\tconst storage = editor.storage.pluginBlock as Record<string, unknown> | undefined;\n\treturn (storage?.registry as Map<string, PluginBlockDef>) ?? new Map();\n}\n\n/** Named icon map: icon key → React component */\nconst ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {\n\tvideo: YoutubeLogo,\n\tcode: Code,\n\tlink: LinkSimple,\n\t\"link-external\": ArrowSquareOut,\n\tform: ListBullets,\n};\n\n/** Resolve an icon key to a React component */\nfunction resolveIcon(iconKey?: string): React.ComponentType<{ className?: string }> {\n\tif (iconKey && ICON_MAP[iconKey]) {\n\t\treturn ICON_MAP[iconKey];\n\t}\n\treturn Cube;\n}\n\n/**\n * Get icon component and metadata for embed block types.\n * Reads from the plugin block registry in editor storage.\n */\nfunction getEmbedMeta(\n\tblockType: string,\n\tregistry: Map<string, PluginBlockDef>,\n): {\n\tIcon: React.ComponentType<{ className?: string }>;\n\tlabel: string;\n\tcolor: string;\n\tplaceholder: string;\n} {\n\tconst def = registry.get(blockType);\n\tif (def) {\n\t\treturn {\n\t\t\tIcon: resolveIcon(def.icon),\n\t\t\tlabel: def.label,\n\t\t\tcolor: \"text-kumo-subtle\",\n\t\t\tplaceholder: def.placeholder || \"Enter URL...\",\n\t\t};\n\t}\n\n\t// Fallback for unregistered block types\n\treturn {\n\t\tIcon: Cube,\n\t\tlabel: blockType.charAt(0).toUpperCase() + blockType.slice(1),\n\t\tcolor: \"text-kumo-subtle\",\n\t\tplaceholder: \"Enter URL...\",\n\t};\n}\n\n/**\n * Extract display ID from URL for cleaner presentation\n */\nfunction getDisplayId(id: string, blockType: string): string {\n\ttry {\n\t\tconst url = new URL(id);\n\n\t\tswitch (blockType) {\n\t\t\tcase \"youtube\": {\n\t\t\t\t// youtube.com/watch?v=VIDEO_ID or youtu.be/VIDEO_ID\n\t\t\t\tconst videoId = url.searchParams.get(\"v\") || url.pathname.split(\"/\").pop();\n\t\t\t\treturn videoId || id;\n\t\t\t}\n\t\t\tcase \"vimeo\": {\n\t\t\t\t// vimeo.com/VIDEO_ID\n\t\t\t\treturn url.pathname.split(\"/\").find(Boolean) || id;\n\t\t\t}\n\t\t\tcase \"tweet\": {\n\t\t\t\t// twitter.com/user/status/TWEET_ID\n\t\t\t\tconst parts = url.pathname.split(\"/\");\n\t\t\t\tconst statusIndex = parts.indexOf(\"status\");\n\t\t\t\tconst tweetId = parts[statusIndex + 1];\n\t\t\t\tif (statusIndex !== -1 && tweetId) {\n\t\t\t\t\treturn `@${parts[1]}/${tweetId.slice(0, 8)}...`;\n\t\t\t\t}\n\t\t\t\treturn id;\n\t\t\t}\n\t\t\tcase \"gist\": {\n\t\t\t\t// gist.github.com/user/GIST_ID\n\t\t\t\tconst parts = url.pathname.split(\"/\").filter(Boolean);\n\t\t\t\tif (parts.length >= 2 && parts[0] && parts[1]) {\n\t\t\t\t\treturn `${parts[0]}/${parts[1].slice(0, 8)}...`;\n\t\t\t\t}\n\t\t\t\treturn id;\n\t\t\t}\n\t\t\tdefault:\n\t\t\t\t// Show hostname + truncated path\n\t\t\t\treturn url.hostname + (url.pathname.length > 20 ? \"...\" : url.pathname);\n\t\t}\n\t} catch {\n\t\t// Not a valid URL, show as-is but truncated\n\t\treturn id.length > 30 ? id.slice(0, 27) + \"...\" : id;\n\t}\n}\n\n/**\n * React component for the plugin block node view\n */\nfunction PluginBlockNodeView({\n\tnode,\n\tupdateAttributes,\n\tselected,\n\tdeleteNode,\n\teditor,\n\tgetPos,\n}: NodeViewProps) {\n\tconst blockType = typeof node.attrs.blockType === \"string\" ? node.attrs.blockType : \"\";\n\tconst id = typeof node.attrs.id === \"string\" ? node.attrs.id : \"\";\n\tconst data =\n\t\ttypeof node.attrs.data === \"object\" && node.attrs.data !== null\n\t\t\t? (node.attrs.data as Record<string, unknown>)\n\t\t\t: {};\n\tconst registry = getRegistry(\n\t\teditor as unknown as { storage: Record<string, Record<string, unknown>> },\n\t);\n\tconst { Icon, label, color, placeholder } = getEmbedMeta(blockType, registry);\n\n\t// Check if this block type has fields defined in the registry\n\tconst blockDef = registry.get(blockType);\n\tconst hasFields = blockDef?.fields && blockDef.fields.length > 0;\n\n\tconst [isEditing, setIsEditing] = React.useState(false);\n\tconst [editValue, setEditValue] = React.useState(id || \"\");\n\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\t// Focus input when editing starts\n\tReact.useEffect(() => {\n\t\tif (isEditing) {\n\t\t\tsetEditValue(id || \"\");\n\t\t\tsetTimeout(() => inputRef.current?.focus(), 0);\n\t\t}\n\t}, [isEditing, id]);\n\n\tconst handleSave = () => {\n\t\tif (editValue.trim()) {\n\t\t\tupdateAttributes({ id: editValue.trim() });\n\t\t}\n\t\tsetIsEditing(false);\n\t};\n\n\tconst handleCancel = () => {\n\t\tsetEditValue(id || \"\");\n\t\tsetIsEditing(false);\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\") {\n\t\t\te.preventDefault();\n\t\t\thandleSave();\n\t\t} else if (e.key === \"Escape\") {\n\t\t\te.preventDefault();\n\t\t\thandleCancel();\n\t\t}\n\t};\n\n\tconst handleCopyUrl = () => {\n\t\tvoid navigator.clipboard.writeText(id);\n\t};\n\n\tconst handleOpenExternal = () => {\n\t\twindow.open(id, \"_blank\", \"noopener,noreferrer\");\n\t};\n\n\tconst displayId = id\n\t\t? getDisplayId(id, blockType)\n\t\t: Object.values(data)\n\t\t\t\t.filter((v) => typeof v === \"string\" && v.length > 0)\n\t\t\t\t.join(\", \") || blockType;\n\n\treturn (\n\t\t<NodeViewWrapper\n\t\t\tclassName={cn(\n\t\t\t\t\"plugin-block relative my-3\",\n\t\t\t\tselected && \"ring-2 ring-kumo-brand ring-offset-2 rounded-lg\",\n\t\t\t)}\n\t\t\tcontentEditable={false}\n\t\t\tdata-drag-handle\n\t\t>\n\t\t\t<div className=\"relative group\">\n\t\t\t\t{/* Drag handle - appears in left gutter */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"absolute -left-8 top-1/2 -translate-y-1/2 opacity-0 group-hover:opacity-100 transition-opacity cursor-grab active:cursor-grabbing\",\n\t\t\t\t\t\tselected && \"opacity-100\",\n\t\t\t\t\t)}\n\t\t\t\t\tdata-drag-handle\n\t\t\t\t>\n\t\t\t\t\t<DotsSixVertical className=\"h-5 w-5 text-kumo-subtle/50\" />\n\t\t\t\t</div>\n\n\t\t\t\t{/* Main block content */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"rounded-lg border bg-kumo-base transition-colors\",\n\t\t\t\t\t\tselected ? \"border-kumo-brand/50 bg-kumo-tint/30\" : \"hover:border-kumo-line\",\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{/* Header with icon, label, and actions */}\n\t\t\t\t\t<div className=\"flex items-center gap-3 px-4 py-3\">\n\t\t\t\t\t\t{/* Icon */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex-shrink-0 w-10 h-10 rounded-lg bg-kumo-tint flex items-center justify-center\",\n\t\t\t\t\t\t\t\tcolor,\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Icon className=\"h-5 w-5\" />\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Label and ID */}\n\t\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t\t<div className=\"text-sm font-medium\">{label}</div>\n\t\t\t\t\t\t\t{!isEditing && (\n\t\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle truncate font-mono\">{displayId}</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Action buttons - visible on hover or when selected */}\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex items-center gap-1 transition-opacity\",\n\t\t\t\t\t\t\t\tselected ? \"opacity-100\" : \"opacity-0 group-hover:opacity-100\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{id && (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\t\t\t\tonClick={handleCopyUrl}\n\t\t\t\t\t\t\t\t\t\ttitle=\"Copy URL\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Copy URL\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Copy className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\t\t\t\tonClick={handleOpenExternal}\n\t\t\t\t\t\t\t\t\t\ttitle=\"Open in new tab\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Open in new tab\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<ArrowSquareOut className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tif (hasFields) {\n\t\t\t\t\t\t\t\t\t\t// Open Block Kit modal via editor storage callback\n\t\t\t\t\t\t\t\t\t\tconst storage = (\n\t\t\t\t\t\t\t\t\t\t\teditor.storage as unknown as Record<string, Record<string, unknown>>\n\t\t\t\t\t\t\t\t\t\t).pluginBlock;\n\t\t\t\t\t\t\t\t\t\tconst onEdit = storage?.onEditBlock as\n\t\t\t\t\t\t\t\t\t\t\t| ((attrs: {\n\t\t\t\t\t\t\t\t\t\t\t\t\tblockType: string;\n\t\t\t\t\t\t\t\t\t\t\t\t\tid: string;\n\t\t\t\t\t\t\t\t\t\t\t\t\tdata: Record<string, unknown>;\n\t\t\t\t\t\t\t\t\t\t\t\t\tpos: number;\n\t\t\t\t\t\t\t\t\t\t\t }) => void)\n\t\t\t\t\t\t\t\t\t\t\t| null;\n\t\t\t\t\t\t\t\t\t\tif (onEdit) {\n\t\t\t\t\t\t\t\t\t\t\tconst pos = (typeof getPos === \"function\" ? getPos() : 0) ?? 0;\n\t\t\t\t\t\t\t\t\t\t\tonEdit({ blockType, id, data, pos });\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tsetIsEditing(true);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\ttitle={hasFields ? \"Edit\" : \"Edit URL\"}\n\t\t\t\t\t\t\t\taria-label={hasFields ? \"Edit\" : \"Edit URL\"}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Pencil className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\tclassName=\"h-8 w-8 text-kumo-danger hover:text-kumo-danger hover:bg-kumo-danger/10\"\n\t\t\t\t\t\t\t\tonClick={() => deleteNode()}\n\t\t\t\t\t\t\t\ttitle=\"Delete\"\n\t\t\t\t\t\t\t\taria-label=\"Delete embed\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Inline URL editor - slides down when editing */}\n\t\t\t\t\t{isEditing && (\n\t\t\t\t\t\t<div className=\"px-4 pb-3 pt-0\">\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\t\t\tvalue={editValue}\n\t\t\t\t\t\t\t\t\tonChange={(e) => setEditValue(e.target.value)}\n\t\t\t\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\t\t\t\tclassName=\"flex-1 h-9 text-sm font-mono\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\tclassName=\"h-9 w-9\"\n\t\t\t\t\t\t\t\t\tonClick={handleCancel}\n\t\t\t\t\t\t\t\t\ttitle=\"Cancel (Esc)\"\n\t\t\t\t\t\t\t\t\taria-label=\"Cancel\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\tclassName=\"h-9 w-9\"\n\t\t\t\t\t\t\t\t\tonClick={handleSave}\n\t\t\t\t\t\t\t\t\ttitle=\"Save (Enter)\"\n\t\t\t\t\t\t\t\t\taria-label=\"Save\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Check className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</NodeViewWrapper>\n\t);\n}\n\n/**\n * TipTap Node extension for plugin blocks (embeds)\n */\nexport const PluginBlockExtension = Node.create({\n\tname: \"pluginBlock\",\n\tgroup: \"block\",\n\tatom: true,\n\tdraggable: true,\n\tselectable: true,\n\n\taddAttributes() {\n\t\treturn {\n\t\t\tblockType: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\tid: {\n\t\t\t\tdefault: null,\n\t\t\t},\n\t\t\tdata: {\n\t\t\t\tdefault: {},\n\t\t\t\tparseHTML: (el: HTMLElement) => JSON.parse(el.getAttribute(\"data-plugin-data\") || \"{}\"),\n\t\t\t\trenderHTML: (attrs: Record<string, unknown>) => ({\n\t\t\t\t\t\"data-plugin-data\": JSON.stringify(attrs.data),\n\t\t\t\t}),\n\t\t\t},\n\t\t};\n\t},\n\n\taddStorage() {\n\t\treturn {\n\t\t\t/** Per-editor registry of plugin block definitions */\n\t\t\tregistry: new Map<string, PluginBlockDef>(),\n\t\t\t/** Callback set by PortableTextEditor to open the Block Kit modal for editing */\n\t\t\tonEditBlock: null as\n\t\t\t\t| ((attrs: {\n\t\t\t\t\t\tblockType: string;\n\t\t\t\t\t\tid: string;\n\t\t\t\t\t\tdata: Record<string, unknown>;\n\t\t\t\t\t\tpos: number;\n\t\t\t\t }) => void)\n\t\t\t\t| null,\n\t\t};\n\t},\n\n\tparseHTML() {\n\t\treturn [\n\t\t\t{\n\t\t\t\ttag: \"div[data-plugin-block]\",\n\t\t\t},\n\t\t];\n\t},\n\n\trenderHTML({ HTMLAttributes }) {\n\t\treturn [\"div\", mergeAttributes(HTMLAttributes, { \"data-plugin-block\": \"\" })];\n\t},\n\n\taddNodeView() {\n\t\treturn ReactNodeViewRenderer(PluginBlockNodeView);\n\t},\n\n\taddKeyboardShortcuts() {\n\t\treturn {\n\t\t\t// Delete block on backspace when selected (not editing)\n\t\t\tBackspace: () => {\n\t\t\t\tconst { selection } = this.editor.state;\n\t\t\t\tconst node = this.editor.state.doc.nodeAt(selection.from);\n\t\t\t\tif (node?.type.name === \"pluginBlock\") {\n\t\t\t\t\tthis.editor.commands.deleteSelection();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t},\n\t\t\t// Also handle Delete key\n\t\t\tDelete: () => {\n\t\t\t\tconst { selection } = this.editor.state;\n\t\t\t\tconst node = this.editor.state.doc.nodeAt(selection.from);\n\t\t\t\tif (node?.type.name === \"pluginBlock\") {\n\t\t\t\t\tthis.editor.commands.deleteSelection();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t},\n\t\t};\n\t},\n});\n\n// Re-export helpers for use elsewhere\nexport { getEmbedMeta, resolveIcon };\n","/**\n * Section Picker Modal\n *\n * A modal for selecting and inserting sections into content.\n */\n\nimport { Button, Dialog, Input } from \"@cloudflare/kumo\";\nimport { MagnifyingGlass, Stack, FolderOpen } from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { fetchSections, type Section } from \"../lib/api\";\nimport { useDebouncedValue } from \"../lib/hooks\";\nimport { cn } from \"../lib/utils\";\n\ninterface SectionPickerModalProps {\n\topen: boolean;\n\tonOpenChange: (open: boolean) => void;\n\tonSelect: (section: Section) => void;\n}\n\nexport function SectionPickerModal({ open, onOpenChange, onSelect }: SectionPickerModalProps) {\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst debouncedSearch = useDebouncedValue(searchQuery, 300);\n\n\tconst { data: sectionsData, isLoading: sectionsLoading } = useQuery({\n\t\tqueryKey: [\"sections\", { search: debouncedSearch }],\n\t\tqueryFn: () =>\n\t\t\tfetchSections({\n\t\t\t\tsearch: debouncedSearch || undefined,\n\t\t\t}),\n\t\tenabled: open,\n\t});\n\tconst sections = sectionsData?.items ?? [];\n\n\t// Reset search when modal opens\n\tReact.useEffect(() => {\n\t\tif (open) {\n\t\t\tsetSearchQuery(\"\");\n\t\t}\n\t}, [open]);\n\n\tconst handleSelect = (section: Section) => {\n\t\tonSelect(section);\n\t\tonOpenChange(false);\n\t};\n\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={onOpenChange}>\n\t\t\t<Dialog className=\"p-6 max-w-3xl max-h-[80vh] flex flex-col\" size=\"lg\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight flex items-center gap-2\">\n\t\t\t\t\t\t<Stack className=\"h-5 w-5\" />\n\t\t\t\t\t\tInsert Section\n\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Search */}\n\t\t\t\t<div className=\"flex items-center gap-4 py-4 border-b\">\n\t\t\t\t\t<div className=\"relative flex-1\">\n\t\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tplaceholder=\"Search sections...\"\n\t\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\t\tclassName=\"pl-10\"\n\t\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Section grid */}\n\t\t\t\t<div className=\"flex-1 overflow-y-auto py-4\">\n\t\t\t\t\t{sectionsLoading ? (\n\t\t\t\t\t\t<div className=\"flex items-center justify-center h-32\">\n\t\t\t\t\t\t\t<div className=\"text-kumo-subtle\">Loading sections...</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : sections.length === 0 ? (\n\t\t\t\t\t\t<div className=\"flex flex-col items-center justify-center h-32 text-center\">\n\t\t\t\t\t\t\t{searchQuery ? (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<MagnifyingGlass className=\"h-8 w-8 text-kumo-subtle mb-2\" />\n\t\t\t\t\t\t\t\t\t<p className=\"text-kumo-subtle\">No sections found</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">Try adjusting your search</p>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<FolderOpen className=\"h-8 w-8 text-kumo-subtle mb-2\" />\n\t\t\t\t\t\t\t\t\t<p className=\"text-kumo-subtle\">No sections available</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\tCreate sections in the Sections library to use them here\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n\t\t\t\t\t\t\t{sections.map((section) => (\n\t\t\t\t\t\t\t\t<SectionCard\n\t\t\t\t\t\t\t\t\tkey={section.id}\n\t\t\t\t\t\t\t\t\tsection={section}\n\t\t\t\t\t\t\t\t\tonSelect={() => handleSelect(section)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Footer */}\n\t\t\t\t<div className=\"flex justify-end gap-2 pt-4 border-t\">\n\t\t\t\t\t<Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n\nfunction SectionCard({ section, onSelect }: { section: Section; onSelect: () => void }) {\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={onSelect}\n\t\t\tclassName={cn(\n\t\t\t\t\"text-left rounded-lg border bg-kumo-base overflow-hidden transition-colors\",\n\t\t\t\t\"hover:border-kumo-brand hover:bg-kumo-tint/50\",\n\t\t\t\t\"focus:outline-none focus:ring-2 focus:ring-kumo-ring focus:ring-offset-2\",\n\t\t\t)}\n\t\t>\n\t\t\t{/* Preview */}\n\t\t\t<div className=\"aspect-video bg-kumo-tint flex items-center justify-center\">\n\t\t\t\t{section.previewUrl ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={section.previewUrl}\n\t\t\t\t\t\talt={section.title}\n\t\t\t\t\t\tclassName=\"w-full h-full object-cover\"\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<Stack className=\"h-8 w-8 text-kumo-subtle\" />\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Content */}\n\t\t\t<div className=\"p-3\">\n\t\t\t\t<h4 className=\"font-medium truncate\">{section.title}</h4>\n\t\t\t\t{section.description && (\n\t\t\t\t\t<p className=\"text-xs text-kumo-subtle line-clamp-2 mt-1\">{section.description}</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</button>\n\t);\n}\n","/**\n * Portable Text Editor\n *\n * TipTap-based rich text editor that stores content as Portable Text.\n * Handles conversion between ProseMirror JSON and Portable Text automatically.\n *\n * Features:\n * - BubbleMenu for inline formatting\n * - Link popover for editing URLs (no window.prompt)\n * - Slash commands for block insertion\n * - Floating menu on empty lines\n */\n\nimport { Button, Dialog, Input } from \"@cloudflare/kumo\";\nimport { useFloating, offset, flip, shift, autoUpdate } from \"@floating-ui/react\";\nimport {\n\tTextB,\n\tTextItalic,\n\tTextUnderline,\n\tTextStrikethrough,\n\tCode,\n\tTextHOne,\n\tTextHTwo,\n\tTextHThree,\n\tList,\n\tListNumbers,\n\tQuotes,\n\tLink as LinkIcon,\n\tImage as ImageIcon,\n\tArrowUUpLeft,\n\tArrowUUpRight,\n\tTextAlignLeft,\n\tTextAlignCenter,\n\tTextAlignRight,\n\tMinus,\n\tLinkBreak,\n\tArrowSquareOut,\n\tCodeBlock,\n\tStack,\n\tEye,\n\ttype Icon,\n} from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport type { Element } from \"@emdash-cms/blocks\";\nimport { Extension, type Range } from \"@tiptap/core\";\nimport CharacterCount from \"@tiptap/extension-character-count\";\nimport Focus from \"@tiptap/extension-focus\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport TextAlign from \"@tiptap/extension-text-align\";\nimport Typography from \"@tiptap/extension-typography\";\nimport { useEditor, EditorContent, useEditorState, type Editor } from \"@tiptap/react\";\nimport { BubbleMenu } from \"@tiptap/react/menus\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport Suggestion from \"@tiptap/suggestion\";\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\n\nimport type { MediaItem } from \"../lib/api\";\nimport type { Section } from \"../lib/api\";\nimport { cn } from \"../lib/utils\";\nimport { DragHandleWrapper } from \"./editor/DragHandleWrapper\";\nimport { ImageExtension } from \"./editor/ImageNode\";\nimport { MarkdownLinkExtension } from \"./editor/MarkdownLinkExtension\";\nimport {\n\ttype PluginBlockDef,\n\tPluginBlockExtension,\n\tregisterPluginBlocks,\n\tresolveIcon,\n} from \"./editor/PluginBlockNode\";\nimport { MediaPickerModal } from \"./MediaPickerModal\";\nimport { SectionPickerModal } from \"./SectionPickerModal\";\n\n// Import converters from inline module since we can't import from emdash package\n// These will be duplicated here until we set up proper package exports\n\ninterface PortableTextSpan {\n\t_type: \"span\";\n\t_key: string;\n\ttext: string;\n\tmarks?: string[];\n}\n\ninterface PortableTextMarkDef {\n\t_type: string;\n\t_key: string;\n\t[key: string]: unknown;\n}\n\ninterface PortableTextTextBlock {\n\t_type: \"block\";\n\t_key: string;\n\tstyle?: \"normal\" | \"h1\" | \"h2\" | \"h3\" | \"h4\" | \"h5\" | \"h6\" | \"blockquote\";\n\tlistItem?: \"bullet\" | \"number\";\n\tlevel?: number;\n\tchildren: PortableTextSpan[];\n\tmarkDefs?: PortableTextMarkDef[];\n}\n\ninterface PortableTextImageBlock {\n\t_type: \"image\";\n\t_key: string;\n\tasset: { _ref: string; url?: string };\n\talt?: string;\n\tcaption?: string;\n\twidth?: number;\n\theight?: number;\n\tdisplayWidth?: number;\n\tdisplayHeight?: number;\n}\n\ninterface PortableTextCodeBlock {\n\t_type: \"code\";\n\t_key: string;\n\tcode: string;\n\tlanguage?: string;\n}\n\ntype PortableTextBlock =\n\t| PortableTextTextBlock\n\t| PortableTextImageBlock\n\t| PortableTextCodeBlock\n\t| { _type: string; _key: string; [key: string]: unknown };\n\n// Generate unique key\nfunction generateKey(): string {\n\treturn Math.random().toString(36).substring(2, 11);\n}\n\n// Helpers for safely extracting typed values from ProseMirror attrs (Record<string, any>)\nconst attrStr = (v: unknown): string | undefined => (typeof v === \"string\" && v ? v : undefined);\nconst attrNum = (v: unknown): number | undefined => (typeof v === \"number\" && v ? v : undefined);\n\n// ProseMirror to Portable Text converter\nfunction prosemirrorToPortableText(doc: {\n\ttype: string;\n\tcontent?: Array<{\n\t\ttype: string;\n\t\tattrs?: Record<string, unknown>;\n\t\tcontent?: unknown[];\n\t\tmarks?: unknown[];\n\t\ttext?: string;\n\t}>;\n}): PortableTextBlock[] {\n\tif (!doc || doc.type !== \"doc\" || !doc.content) {\n\t\treturn [];\n\t}\n\n\tconst blocks: PortableTextBlock[] = [];\n\n\tfor (const node of doc.content) {\n\t\tconst converted = convertPMNode(node);\n\t\tif (converted) {\n\t\t\tif (Array.isArray(converted)) {\n\t\t\t\tblocks.push(...converted);\n\t\t\t} else {\n\t\t\t\tblocks.push(converted);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\nfunction convertPMNode(node: {\n\ttype: string;\n\tattrs?: Record<string, unknown>;\n\tcontent?: unknown[];\n\tmarks?: unknown[];\n\ttext?: string;\n}): PortableTextBlock | PortableTextBlock[] | null {\n\tswitch (node.type) {\n\t\tcase \"paragraph\": {\n\t\t\tconst { children, markDefs } = convertInlineContent(node.content || []);\n\t\t\tif (children.length === 0) return null;\n\t\t\treturn {\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tstyle: \"normal\",\n\t\t\t\tchildren,\n\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t};\n\t\t}\n\n\t\tcase \"heading\": {\n\t\t\tconst { children, markDefs } = convertInlineContent(node.content || []);\n\t\t\tconst rawLevel = node.attrs?.level;\n\t\t\tconst level = typeof rawLevel === \"number\" ? rawLevel : 1;\n\t\t\tif (children.length === 0) return null;\n\t\t\tconst headingStyle =\n\t\t\t\tlevel >= 1 && level <= 6\n\t\t\t\t\t? (`h${level}` as PortableTextTextBlock[\"style\"])\n\t\t\t\t\t: (\"h1\" as PortableTextTextBlock[\"style\"]);\n\t\t\treturn {\n\t\t\t\t_type: \"block\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tstyle: headingStyle,\n\t\t\t\tchildren,\n\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t};\n\t\t}\n\n\t\tcase \"bulletList\":\n\t\t\treturn convertList(node.content || [], \"bullet\");\n\n\t\tcase \"orderedList\":\n\t\t\treturn convertList(node.content || [], \"number\");\n\n\t\tcase \"blockquote\": {\n\t\t\tconst blocks: PortableTextTextBlock[] = [];\n\t\t\tconst blockquoteContent = (node.content || []) as Array<{\n\t\t\t\ttype: string;\n\t\t\t\tcontent?: unknown[];\n\t\t\t}>;\n\t\t\tfor (const child of blockquoteContent) {\n\t\t\t\tif (child.type === \"paragraph\") {\n\t\t\t\t\tconst { children, markDefs } = convertInlineContent(child.content || []);\n\t\t\t\t\tif (children.length > 0) {\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tstyle: \"blockquote\",\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (blocks.length === 1) {\n\t\t\t\treturn blocks[0]!;\n\t\t\t}\n\t\t\treturn blocks.length > 0 ? blocks : null;\n\t\t}\n\n\t\tcase \"codeBlock\": {\n\t\t\tconst codeContent = (node.content || []) as Array<{ text?: string }>;\n\t\t\tconst code = codeContent.map((n) => n.text || \"\").join(\"\");\n\t\t\tconst rawLanguage = node.attrs?.language;\n\t\t\treturn {\n\t\t\t\t_type: \"code\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tcode,\n\t\t\t\tlanguage: typeof rawLanguage === \"string\" ? rawLanguage : undefined,\n\t\t\t};\n\t\t}\n\n\t\tcase \"image\": {\n\t\t\tconst attrs = node.attrs ?? {};\n\t\t\tconst provider = attrStr(attrs.provider);\n\t\t\treturn {\n\t\t\t\t_type: \"image\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tasset: {\n\t\t\t\t\t_ref: attrStr(attrs.mediaId) ?? \"\",\n\t\t\t\t\turl: attrStr(attrs.src) ?? \"\",\n\t\t\t\t\tprovider: provider && provider !== \"local\" ? provider : undefined,\n\t\t\t\t},\n\t\t\t\talt: attrStr(attrs.alt),\n\t\t\t\tcaption: attrStr(attrs.caption) ?? attrStr(attrs.title),\n\t\t\t\twidth: attrNum(attrs.width),\n\t\t\t\theight: attrNum(attrs.height),\n\t\t\t\tdisplayWidth: attrNum(attrs.displayWidth),\n\t\t\t\tdisplayHeight: attrNum(attrs.displayHeight),\n\t\t\t};\n\t\t}\n\n\t\tcase \"horizontalRule\":\n\t\t\treturn {\n\t\t\t\t_type: \"break\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tstyle: \"lineBreak\",\n\t\t\t};\n\n\t\tcase \"pluginBlock\": {\n\t\t\tconst { blockType, id: pluginId, data } = node.attrs ?? {};\n\t\t\treturn {\n\t\t\t\t...(data && typeof data === \"object\" ? data : {}),\n\t\t\t\t_type: typeof blockType === \"string\" ? blockType : \"embed\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\tid: typeof pluginId === \"string\" ? pluginId : \"\",\n\t\t\t};\n\t\t}\n\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nfunction convertList(items: unknown[], listItem: \"bullet\" | \"number\"): PortableTextTextBlock[] {\n\tconst blocks: PortableTextTextBlock[] = [];\n\tconst typedItems = items as Array<{ type: string; content?: unknown[] }>;\n\n\tfor (const item of typedItems) {\n\t\tif (item.type === \"listItem\") {\n\t\t\tconst listItemContent = (item.content || []) as Array<{\n\t\t\t\ttype: string;\n\t\t\t\tcontent?: unknown[];\n\t\t\t}>;\n\t\t\tfor (const child of listItemContent) {\n\t\t\t\tif (child.type === \"paragraph\") {\n\t\t\t\t\tconst { children, markDefs } = convertInlineContent(child.content || []);\n\t\t\t\t\tif (children.length > 0) {\n\t\t\t\t\t\tblocks.push({\n\t\t\t\t\t\t\t_type: \"block\",\n\t\t\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\t\t\tstyle: \"normal\",\n\t\t\t\t\t\t\tlistItem,\n\t\t\t\t\t\t\tlevel: 1,\n\t\t\t\t\t\t\tchildren,\n\t\t\t\t\t\t\tmarkDefs: markDefs.length > 0 ? markDefs : undefined,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn blocks;\n}\n\nfunction convertInlineContent(nodes: unknown[]): {\n\tchildren: PortableTextSpan[];\n\tmarkDefs: PortableTextMarkDef[];\n} {\n\tconst children: PortableTextSpan[] = [];\n\tconst markDefs: PortableTextMarkDef[] = [];\n\tconst markDefMap = new Map<string, string>();\n\n\tconst typedNodes = nodes as Array<{\n\t\ttype: string;\n\t\ttext?: string;\n\t\tmarks?: Array<{ type: string; attrs?: Record<string, unknown> }>;\n\t}>;\n\tfor (const node of typedNodes) {\n\t\tif (node.type === \"text\" && node.text) {\n\t\t\tconst marks: string[] = [];\n\n\t\t\tfor (const mark of node.marks || []) {\n\t\t\t\tconst markType = convertMark(mark, markDefs, markDefMap);\n\t\t\t\tif (markType) {\n\t\t\t\t\tmarks.push(markType);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tchildren.push({\n\t\t\t\t_type: \"span\",\n\t\t\t\t_key: generateKey(),\n\t\t\t\ttext: node.text,\n\t\t\t\tmarks: marks.length > 0 ? marks : undefined,\n\t\t\t});\n\t\t} else if (node.type === \"hardBreak\") {\n\t\t\tif (children.length > 0) {\n\t\t\t\tconst last = children.at(-1);\n\t\t\t\tif (last) last.text += \"\\n\";\n\t\t\t} else {\n\t\t\t\tchildren.push({\n\t\t\t\t\t_type: \"span\",\n\t\t\t\t\t_key: generateKey(),\n\t\t\t\t\ttext: \"\\n\",\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tif (children.length === 0) {\n\t\tchildren.push({\n\t\t\t_type: \"span\",\n\t\t\t_key: generateKey(),\n\t\t\ttext: \"\",\n\t\t});\n\t}\n\n\treturn { children, markDefs };\n}\n\nfunction convertMark(\n\tmark: { type: string; attrs?: Record<string, unknown> },\n\tmarkDefs: PortableTextMarkDef[],\n\tmarkDefMap: Map<string, string>,\n): string | null {\n\tswitch (mark.type) {\n\t\tcase \"bold\":\n\t\tcase \"strong\":\n\t\t\treturn \"strong\";\n\t\tcase \"italic\":\n\t\tcase \"em\":\n\t\t\treturn \"em\";\n\t\tcase \"underline\":\n\t\t\treturn \"underline\";\n\t\tcase \"strike\":\n\t\tcase \"strikethrough\":\n\t\t\treturn \"strike-through\";\n\t\tcase \"code\":\n\t\t\treturn \"code\";\n\t\tcase \"link\": {\n\t\t\tconst rawHref = mark.attrs?.href;\n\t\t\tconst href = typeof rawHref === \"string\" ? rawHref : \"\";\n\t\t\tif (markDefMap.has(href)) {\n\t\t\t\treturn markDefMap.get(href)!;\n\t\t\t}\n\t\t\tconst key = generateKey();\n\t\t\tmarkDefs.push({\n\t\t\t\t_type: \"link\",\n\t\t\t\t_key: key,\n\t\t\t\thref,\n\t\t\t\tblank: mark.attrs?.target === \"_blank\",\n\t\t\t});\n\t\t\tmarkDefMap.set(href, key);\n\t\t\treturn key;\n\t\t}\n\t\tdefault:\n\t\t\treturn mark.type;\n\t}\n}\n\n// Type guards for PortableText block variants\nfunction isTextBlock(block: PortableTextBlock): block is PortableTextTextBlock {\n\treturn block._type === \"block\";\n}\n\nfunction isImageBlock(block: PortableTextBlock): block is PortableTextImageBlock {\n\treturn block._type === \"image\";\n}\n\nfunction isCodeBlock(block: PortableTextBlock): block is PortableTextCodeBlock {\n\treturn block._type === \"code\";\n}\n\n// Portable Text to ProseMirror converter\nfunction portableTextToProsemirror(blocks: PortableTextBlock[]): {\n\ttype: \"doc\";\n\tcontent: unknown[];\n} {\n\tif (!blocks || blocks.length === 0) {\n\t\treturn {\n\t\t\ttype: \"doc\",\n\t\t\tcontent: [{ type: \"paragraph\" }],\n\t\t};\n\t}\n\n\tconst content: unknown[] = [];\n\tlet i = 0;\n\n\twhile (i < blocks.length) {\n\t\tconst block = blocks[i]!;\n\n\t\tif (isTextBlock(block) && block.listItem) {\n\t\t\tconst listBlocks: PortableTextTextBlock[] = [];\n\t\t\tconst listType = block.listItem;\n\n\t\t\twhile (i < blocks.length) {\n\t\t\t\tconst current = blocks[i]!;\n\t\t\t\tif (isTextBlock(current) && current.listItem === listType) {\n\t\t\t\t\tlistBlocks.push(current);\n\t\t\t\t\ti++;\n\t\t\t\t} else {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tcontent.push(convertPTList(listBlocks, listType));\n\t\t} else {\n\t\t\tconst converted = convertPTBlock(block);\n\t\t\tif (converted) {\n\t\t\t\tcontent.push(converted);\n\t\t\t}\n\t\t\ti++;\n\t\t}\n\t}\n\n\treturn {\n\t\ttype: \"doc\",\n\t\tcontent: content.length > 0 ? content : [{ type: \"paragraph\" }],\n\t};\n}\n\nfunction convertPTBlock(block: PortableTextBlock): unknown {\n\tswitch (block._type) {\n\t\tcase \"block\": {\n\t\t\tif (!isTextBlock(block)) return null;\n\t\t\tconst { style = \"normal\", children, markDefs = [] } = block;\n\t\t\tconst pmContent = convertPTSpans(children, markDefs);\n\n\t\t\tswitch (style) {\n\t\t\t\tcase \"h1\":\n\t\t\t\tcase \"h2\":\n\t\t\t\tcase \"h3\":\n\t\t\t\tcase \"h4\":\n\t\t\t\tcase \"h5\":\n\t\t\t\tcase \"h6\": {\n\t\t\t\t\tconst level = parseInt(style.substring(1), 10);\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: \"heading\",\n\t\t\t\t\t\tattrs: { level },\n\t\t\t\t\t\tcontent: pmContent.length > 0 ? pmContent : undefined,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tcase \"blockquote\":\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: \"blockquote\",\n\t\t\t\t\t\tcontent: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\ttype: \"paragraph\",\n\t\t\t\t\t\t\t\tcontent: pmContent.length > 0 ? pmContent : undefined,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t\t\t};\n\t\t\t\tdefault:\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttype: \"paragraph\",\n\t\t\t\t\t\tcontent: pmContent.length > 0 ? pmContent : undefined,\n\t\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\tcase \"image\": {\n\t\t\tif (!isImageBlock(block)) return null;\n\t\t\tconst imageBlock = block;\n\t\t\treturn {\n\t\t\t\ttype: \"image\",\n\t\t\t\tattrs: {\n\t\t\t\t\tsrc: imageBlock.asset.url || `/_emdash/api/media/file/${imageBlock.asset._ref}`,\n\t\t\t\t\talt: imageBlock.alt || \"\",\n\t\t\t\t\ttitle: imageBlock.caption || \"\",\n\t\t\t\t\tcaption: imageBlock.caption || \"\",\n\t\t\t\t\tmediaId: imageBlock.asset._ref,\n\t\t\t\t\twidth: imageBlock.width,\n\t\t\t\t\theight: imageBlock.height,\n\t\t\t\t\tdisplayWidth: imageBlock.displayWidth,\n\t\t\t\t\tdisplayHeight: imageBlock.displayHeight,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tcase \"code\": {\n\t\t\tif (!isCodeBlock(block)) return null;\n\t\t\tconst codeBlock = block;\n\t\t\treturn {\n\t\t\t\ttype: \"codeBlock\",\n\t\t\t\tattrs: { language: codeBlock.language || null },\n\t\t\t\tcontent: codeBlock.code ? [{ type: \"text\", text: codeBlock.code }] : undefined,\n\t\t\t};\n\t\t}\n\n\t\tcase \"break\":\n\t\t\treturn { type: \"horizontalRule\" };\n\n\t\tdefault: {\n\t\t\t// Treat unknown block types as plugin blocks (embeds)\n\t\t\t// These have an id field (or url for backwards compat) for the embed source,\n\t\t\t// OR Block Kit field data stored as top-level keys (e.g., formId for forms plugin)\n\t\t\tconst { _type, _key, id, url, ...rest } = block as Record<string, unknown>;\n\t\t\t// Filter out _-prefixed keys to prevent accumulation across edit cycles\n\t\t\tconst data = Object.fromEntries(Object.entries(rest).filter(([k]) => !k.startsWith(\"_\")));\n\t\t\tconst hasFieldData = Object.keys(data).length > 0;\n\t\t\tif (id || url || hasFieldData) {\n\t\t\t\treturn {\n\t\t\t\t\ttype: \"pluginBlock\",\n\t\t\t\t\tattrs: {\n\t\t\t\t\t\tblockType: _type,\n\t\t\t\t\t\tid: id || url || \"\",\n\t\t\t\t\t\tdata,\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t\t// Truly unknown blocks with no data at all\n\t\t\treturn {\n\t\t\t\ttype: \"paragraph\",\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `[Unknown block type: ${block._type}]`,\n\t\t\t\t\t\tmarks: [{ type: \"code\" }],\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t};\n\t\t}\n\t}\n}\n\nfunction convertPTList(items: PortableTextTextBlock[], listType: \"bullet\" | \"number\"): unknown {\n\tconst listItems = items.map((item) => {\n\t\tconst pmContent = convertPTSpans(item.children, item.markDefs || []);\n\t\treturn {\n\t\t\ttype: \"listItem\",\n\t\t\tcontent: [\n\t\t\t\t{\n\t\t\t\t\ttype: \"paragraph\",\n\t\t\t\t\tcontent: pmContent.length > 0 ? pmContent : undefined,\n\t\t\t\t},\n\t\t\t],\n\t\t};\n\t});\n\n\treturn {\n\t\ttype: listType === \"bullet\" ? \"bulletList\" : \"orderedList\",\n\t\tcontent: listItems,\n\t};\n}\n\nfunction convertPTSpans(spans: PortableTextSpan[], markDefs: PortableTextMarkDef[]): unknown[] {\n\tconst nodes: unknown[] = [];\n\tconst markDefsMap = new Map(markDefs.map((md) => [md._key, md]));\n\n\tfor (const span of spans) {\n\t\tif (span._type !== \"span\") continue;\n\n\t\tconst parts = span.text.split(\"\\n\");\n\n\t\tfor (let i = 0; i < parts.length; i++) {\n\t\t\tconst text = parts[i]!;\n\n\t\t\tif (text.length > 0) {\n\t\t\t\tconst marks = convertPTMarks(span.marks || [], markDefsMap);\n\t\t\t\tconst node: { type: string; text: string; marks?: unknown[] } = {\n\t\t\t\t\ttype: \"text\",\n\t\t\t\t\ttext,\n\t\t\t\t};\n\t\t\t\tif (marks.length > 0) {\n\t\t\t\t\tnode.marks = marks;\n\t\t\t\t}\n\t\t\t\tnodes.push(node);\n\t\t\t}\n\n\t\t\tif (i < parts.length - 1) {\n\t\t\t\tnodes.push({ type: \"hardBreak\" });\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nodes;\n}\n\nfunction convertPTMarks(marks: string[], markDefs: Map<string, PortableTextMarkDef>): unknown[] {\n\tconst pmMarks: unknown[] = [];\n\n\tfor (const mark of marks) {\n\t\tswitch (mark) {\n\t\t\tcase \"strong\":\n\t\t\t\tpmMarks.push({ type: \"bold\" });\n\t\t\t\tbreak;\n\t\t\tcase \"em\":\n\t\t\t\tpmMarks.push({ type: \"italic\" });\n\t\t\t\tbreak;\n\t\t\tcase \"underline\":\n\t\t\t\tpmMarks.push({ type: \"underline\" });\n\t\t\t\tbreak;\n\t\t\tcase \"strike-through\":\n\t\t\t\tpmMarks.push({ type: \"strike\" });\n\t\t\t\tbreak;\n\t\t\tcase \"code\":\n\t\t\t\tpmMarks.push({ type: \"code\" });\n\t\t\t\tbreak;\n\t\t\tdefault: {\n\t\t\t\tconst markDef = markDefs.get(mark);\n\t\t\t\tif (markDef && markDef._type === \"link\") {\n\t\t\t\t\tpmMarks.push({\n\t\t\t\t\t\ttype: \"link\",\n\t\t\t\t\t\tattrs: {\n\t\t\t\t\t\t\thref: markDef.href,\n\t\t\t\t\t\t\ttarget: markDef.blank ? \"_blank\" : null,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\treturn pmMarks;\n}\n\n// =============================================================================\n// Slash Commands\n// =============================================================================\n\n/**\n * Slash command item definition\n */\ninterface SlashCommandItem {\n\tid: string;\n\ttitle: string;\n\tdescription: string;\n\ticon: Icon | React.ComponentType<{ className?: string }>;\n\tcommand: (props: { editor: Editor; range: Range }) => void;\n\taliases?: string[];\n\tcategory?: string;\n}\n\n/**\n * Default slash commands for built-in block types\n */\nconst defaultSlashCommands: SlashCommandItem[] = [\n\t{\n\t\tid: \"heading1\",\n\t\ttitle: \"Heading 1\",\n\t\tdescription: \"Large section heading\",\n\t\ticon: TextHOne,\n\t\taliases: [\"h1\", \"title\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).setNode(\"heading\", { level: 1 }).run();\n\t\t},\n\t},\n\t{\n\t\tid: \"heading2\",\n\t\ttitle: \"Heading 2\",\n\t\tdescription: \"Medium section heading\",\n\t\ticon: TextHTwo,\n\t\taliases: [\"h2\", \"subtitle\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).setNode(\"heading\", { level: 2 }).run();\n\t\t},\n\t},\n\t{\n\t\tid: \"heading3\",\n\t\ttitle: \"Heading 3\",\n\t\tdescription: \"Small section heading\",\n\t\ticon: TextHThree,\n\t\taliases: [\"h3\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).setNode(\"heading\", { level: 3 }).run();\n\t\t},\n\t},\n\t{\n\t\tid: \"bulletList\",\n\t\ttitle: \"Bullet List\",\n\t\tdescription: \"Create a bullet list\",\n\t\ticon: List,\n\t\taliases: [\"ul\", \"unordered\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).toggleBulletList().run();\n\t\t},\n\t},\n\t{\n\t\tid: \"numberedList\",\n\t\ttitle: \"Numbered List\",\n\t\tdescription: \"Create a numbered list\",\n\t\ticon: ListNumbers,\n\t\taliases: [\"ol\", \"ordered\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).toggleOrderedList().run();\n\t\t},\n\t},\n\t{\n\t\tid: \"quote\",\n\t\ttitle: \"Quote\",\n\t\tdescription: \"Insert a blockquote\",\n\t\ticon: Quotes,\n\t\taliases: [\"blockquote\", \"cite\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).toggleBlockquote().run();\n\t\t},\n\t},\n\t{\n\t\tid: \"codeBlock\",\n\t\ttitle: \"Code Block\",\n\t\tdescription: \"Insert a code block\",\n\t\ticon: CodeBlock,\n\t\taliases: [\"code\", \"pre\", \"```\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).toggleCodeBlock().run();\n\t\t},\n\t},\n\t{\n\t\tid: \"divider\",\n\t\ttitle: \"Divider\",\n\t\tdescription: \"Insert a horizontal rule\",\n\t\ticon: Minus,\n\t\taliases: [\"hr\", \"---\", \"separator\"],\n\t\tcommand: ({ editor, range }) => {\n\t\t\teditor.chain().focus().deleteRange(range).setHorizontalRule().run();\n\t\t},\n\t},\n];\n\n/**\n * Slash menu state\n */\ninterface SlashMenuState {\n\tisOpen: boolean;\n\titems: SlashCommandItem[];\n\tselectedIndex: number;\n\tclientRect: (() => DOMRect | null) | null;\n\trange: Range | null;\n}\n\n/**\n * Create the slash commands TipTap extension\n */\nfunction createSlashCommandsExtension(options: {\n\tfilterCommands: (query: string) => SlashCommandItem[];\n\tonStateChange: React.Dispatch<React.SetStateAction<SlashMenuState>>;\n\tgetState: () => SlashMenuState;\n}) {\n\tconst { filterCommands, onStateChange, getState } = options;\n\n\treturn Extension.create({\n\t\tname: \"slashCommands\",\n\n\t\taddProseMirrorPlugins() {\n\t\t\treturn [\n\t\t\t\tSuggestion({\n\t\t\t\t\teditor: this.editor,\n\t\t\t\t\tchar: \"/\",\n\t\t\t\t\tstartOfLine: true,\n\t\t\t\t\tcommand: ({ editor, range, props }) => {\n\t\t\t\t\t\tconst item = props as SlashCommandItem;\n\t\t\t\t\t\titem.command({ editor, range });\n\t\t\t\t\t},\n\t\t\t\t\titems: ({ query }) => filterCommands(query),\n\t\t\t\t\trender: () => {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tonStart: (props) => {\n\t\t\t\t\t\t\t\tonStateChange({\n\t\t\t\t\t\t\t\t\tisOpen: true,\n\t\t\t\t\t\t\t\t\titems: props.items,\n\t\t\t\t\t\t\t\t\tselectedIndex: 0,\n\t\t\t\t\t\t\t\t\tclientRect: props.clientRect ?? null,\n\t\t\t\t\t\t\t\t\trange: props.range,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonUpdate: (props) => {\n\t\t\t\t\t\t\t\tonStateChange((prev) => ({\n\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\titems: props.items,\n\t\t\t\t\t\t\t\t\tselectedIndex: 0,\n\t\t\t\t\t\t\t\t\tclientRect: props.clientRect ?? null,\n\t\t\t\t\t\t\t\t\trange: props.range,\n\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonKeyDown: (props) => {\n\t\t\t\t\t\t\t\tif (props.event.key === \"Escape\") {\n\t\t\t\t\t\t\t\t\tonStateChange((prev) => ({ ...prev, isOpen: false }));\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (props.event.key === \"ArrowUp\") {\n\t\t\t\t\t\t\t\t\tonStateChange((prev) => ({\n\t\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\t\tselectedIndex: (prev.selectedIndex - 1 + prev.items.length) % prev.items.length,\n\t\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (props.event.key === \"ArrowDown\") {\n\t\t\t\t\t\t\t\t\tonStateChange((prev) => ({\n\t\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\t\tselectedIndex: (prev.selectedIndex + 1) % prev.items.length,\n\t\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\tif (props.event.key === \"Enter\") {\n\t\t\t\t\t\t\t\t\tconst state = getState();\n\t\t\t\t\t\t\t\t\tif (state.items.length > 0 && state.range) {\n\t\t\t\t\t\t\t\t\t\tconst item = state.items[state.selectedIndex];\n\t\t\t\t\t\t\t\t\t\tif (item) {\n\t\t\t\t\t\t\t\t\t\t\titem.command({ editor: this.editor, range: state.range });\n\t\t\t\t\t\t\t\t\t\t\tonStateChange((prev) => ({ ...prev, isOpen: false }));\n\t\t\t\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tonExit: () => {\n\t\t\t\t\t\t\t\tonStateChange((prev) => ({ ...prev, isOpen: false }));\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t];\n\t\t},\n\t});\n}\n\n/**\n * Slash command menu component using Floating UI\n */\nfunction SlashCommandMenu({\n\tstate,\n\tonCommand,\n\tonClose: _onClose,\n\tsetSelectedIndex,\n}: {\n\tstate: SlashMenuState;\n\tonCommand: (item: SlashCommandItem) => void;\n\tonClose: () => void;\n\tsetSelectedIndex: (index: number) => void;\n}) {\n\tconst containerRef = React.useRef<HTMLDivElement>(null);\n\n\tconst { refs, floatingStyles } = useFloating({\n\t\topen: state.isOpen,\n\t\tplacement: \"bottom-start\",\n\t\tmiddleware: [offset(8), flip(), shift({ padding: 8 })],\n\t\twhileElementsMounted: autoUpdate,\n\t});\n\n\t// Sync virtual reference from TipTap's clientRect\n\tReact.useEffect(() => {\n\t\tif (state.clientRect) {\n\t\t\tconst clientRectFn = state.clientRect;\n\t\t\trefs.setReference({\n\t\t\t\tgetBoundingClientRect: () => clientRectFn() ?? new DOMRect(),\n\t\t\t});\n\t\t}\n\t}, [state.clientRect, refs]);\n\n\t// Scroll selected item into view\n\tReact.useEffect(() => {\n\t\tif (!state.isOpen) return;\n\t\tconst container = containerRef.current;\n\t\tif (!container) return;\n\n\t\tconst selected = container.querySelector<HTMLElement>(`[data-index=\"${state.selectedIndex}\"]`);\n\t\tif (selected) {\n\t\t\tselected.scrollIntoView({ block: \"nearest\" });\n\t\t}\n\t}, [state.selectedIndex, state.isOpen]);\n\n\tif (!state.isOpen) return null;\n\n\treturn createPortal(\n\t\t<div\n\t\t\tref={(node) => {\n\t\t\t\tcontainerRef.current = node;\n\t\t\t\trefs.setFloating(node);\n\t\t\t}}\n\t\t\tstyle={floatingStyles}\n\t\t\tclassName=\"z-[100] rounded-lg border bg-kumo-overlay p-1 shadow-lg min-w-[220px] max-h-[300px] overflow-y-auto\"\n\t\t>\n\t\t\t{state.items.length === 0 ? (\n\t\t\t\t<p className=\"text-sm text-kumo-subtle px-3 py-2\">No results</p>\n\t\t\t) : (\n\t\t\t\tstate.items.map((item, index) => (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tdata-index={index}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex items-center gap-3 w-full px-3 py-2 text-sm rounded text-left\",\n\t\t\t\t\t\t\tindex === state.selectedIndex\n\t\t\t\t\t\t\t\t? \"bg-kumo-tint text-kumo-default\"\n\t\t\t\t\t\t\t\t: \"hover:bg-kumo-tint/50\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t\tonClick={() => onCommand(item)}\n\t\t\t\t\t\tonMouseEnter={() => setSelectedIndex(index)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<item.icon className=\"h-4 w-4 text-kumo-subtle flex-shrink-0\" />\n\t\t\t\t\t\t<div className=\"flex flex-col\">\n\t\t\t\t\t\t\t<span className=\"font-medium\">{item.title}</span>\n\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">{item.description}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</button>\n\t\t\t\t))\n\t\t\t)}\n\t\t</div>,\n\t\tdocument.body,\n\t);\n}\n\n/**\n * Plugin block insertion/editing modal.\n * When the block has `fields`, renders Block Kit elements.\n * Otherwise falls back to a simple URL input.\n */\nfunction PluginBlockModal({\n\tblock,\n\tinitialValues,\n\tonClose,\n\tonInsert,\n}: {\n\tblock: PluginBlockDef | null;\n\t/** Pre-populated values when editing an existing block */\n\tinitialValues?: Record<string, unknown>;\n\tonClose: () => void;\n\tonInsert: (values: Record<string, unknown>) => void;\n}) {\n\tconst [formValues, setFormValues] = React.useState<Record<string, unknown>>({});\n\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\tReact.useEffect(() => {\n\t\tif (block) {\n\t\t\tif (initialValues) {\n\t\t\t\tsetFormValues({ ...initialValues });\n\t\t\t} else {\n\t\t\t\tsetFormValues({});\n\t\t\t}\n\t\t\tif (!block.fields || block.fields.length === 0) {\n\t\t\t\tsetTimeout(() => inputRef.current?.focus(), 0);\n\t\t\t}\n\t\t}\n\t}, [block, initialValues]);\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (block?.fields && block.fields.length > 0) {\n\t\t\tonInsert(formValues);\n\t\t} else {\n\t\t\tconst url = typeof formValues.id === \"string\" ? formValues.id.trim() : \"\";\n\t\t\tif (url) {\n\t\t\t\tonInsert({ id: url });\n\t\t\t}\n\t\t}\n\t};\n\n\tconst handleFieldChange = (actionId: string, value: unknown) => {\n\t\tsetFormValues((prev) => ({ ...prev, [actionId]: value }));\n\t};\n\n\tconst isEditing = !!initialValues;\n\tconst hasFields = block?.fields && block.fields.length > 0;\n\n\t// For simple URL mode, check if the URL is non-empty\n\t// For Block Kit fields, require at least one field to have a value\n\tconst canSubmit = hasFields\n\t\t? Object.values(formValues).some((v) => v !== undefined && v !== null && v !== \"\")\n\t\t: typeof formValues.id === \"string\" && formValues.id.trim().length > 0;\n\n\treturn (\n\t\t<Dialog.Root open={!!block} onOpenChange={(open: boolean) => !open && onClose()}>\n\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t{isEditing ? \"Edit\" : \"Insert\"} {block?.label || \"\"}\n\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t<div className=\"py-4 space-y-4\">\n\t\t\t\t\t\t{hasFields ? (\n\t\t\t\t\t\t\tblock.fields!.map((field) => (\n\t\t\t\t\t\t\t\t<BlockKitField\n\t\t\t\t\t\t\t\t\tkey={field.action_id}\n\t\t\t\t\t\t\t\t\tfield={field}\n\t\t\t\t\t\t\t\t\tpluginId={block.pluginId}\n\t\t\t\t\t\t\t\t\tvalue={formValues[field.action_id]}\n\t\t\t\t\t\t\t\t\tonChange={handleFieldChange}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\t\tplaceholder={block?.placeholder || \"Enter URL...\"}\n\t\t\t\t\t\t\t\tvalue={typeof formValues.id === \"string\" ? formValues.id : \"\"}\n\t\t\t\t\t\t\t\tonChange={(e) => handleFieldChange(\"id\", e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\">\n\t\t\t\t\t\t<Button type=\"button\" variant=\"ghost\" onClick={onClose}>\n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button type=\"submit\" disabled={!canSubmit}>\n\t\t\t\t\t\t\t{isEditing ? \"Save\" : \"Insert\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n\n/**\n * Renders a single Block Kit field element.\n * Supports text_input, number_input, select (with optional async options), and toggle.\n */\nfunction BlockKitField({\n\tfield,\n\tpluginId,\n\tvalue,\n\tonChange,\n}: {\n\tfield: Element;\n\tpluginId?: string;\n\tvalue: unknown;\n\tonChange: (actionId: string, value: unknown) => void;\n}) {\n\tswitch (field.type) {\n\t\tcase \"text_input\": {\n\t\t\tconst multiline = !!field.multiline;\n\t\t\tconst placeholder = typeof field.placeholder === \"string\" ? field.placeholder : undefined;\n\t\t\tconst Tag = multiline ? \"textarea\" : \"input\";\n\t\t\treturn (\n\t\t\t\t<div>\n\t\t\t\t\t<label className=\"text-sm font-medium mb-1.5 block\">{field.label}</label>\n\t\t\t\t\t{multiline ? (\n\t\t\t\t\t\t<Tag\n\t\t\t\t\t\t\tclassName=\"flex w-full rounded-md border border-kumo-line bg-transparent px-3 py-2 text-sm ring-offset-background placeholder:text-kumo-subtle focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kumo-ring focus-visible:ring-offset-2 min-h-[80px]\"\n\t\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\t\t\tonChange={(e) => onChange(field.action_id, e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tplaceholder={placeholder}\n\t\t\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\t\t\tonChange={(e) => onChange(field.action_id, e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\tcase \"number_input\": {\n\t\t\tconst min = typeof field.min === \"number\" ? field.min : undefined;\n\t\t\tconst max = typeof field.max === \"number\" ? field.max : undefined;\n\t\t\treturn (\n\t\t\t\t<div>\n\t\t\t\t\t<label className=\"text-sm font-medium mb-1.5 block\">{field.label}</label>\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\tmin={min}\n\t\t\t\t\t\tmax={max}\n\t\t\t\t\t\tvalue={typeof value === \"number\" ? String(value) : \"\"}\n\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\tonChange(field.action_id, e.target.value ? Number(e.target.value) : undefined)\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\tcase \"select\": {\n\t\t\treturn <DynamicSelect field={field} pluginId={pluginId} value={value} onChange={onChange} />;\n\t\t}\n\t\tcase \"toggle\": {\n\t\t\treturn (\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\tchecked={!!value}\n\t\t\t\t\t\tonChange={(e) => onChange(field.action_id, e.target.checked)}\n\t\t\t\t\t\tclassName=\"h-4 w-4\"\n\t\t\t\t\t/>\n\t\t\t\t\t<label className=\"text-sm font-medium\">{field.label}</label>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\t\tdefault:\n\t\t\treturn <div className=\"text-sm text-kumo-subtle\">Unknown field type: {field.type}</div>;\n\t}\n}\n\n/**\n * Select field that supports loading options dynamically via `optionsRoute`.\n * When `optionsRoute` is set, fetches `{ items: [{ id, name }] }` from the plugin route.\n */\nfunction DynamicSelect({\n\tfield,\n\tpluginId,\n\tvalue,\n\tonChange,\n}: {\n\tfield: Extract<Element, { type: \"select\" }>;\n\tpluginId?: string;\n\tvalue: unknown;\n\tonChange: (actionId: string, value: unknown) => void;\n}) {\n\tconst [dynamicOptions, setDynamicOptions] = React.useState<Array<{\n\t\tlabel: string;\n\t\tvalue: string;\n\t}> | null>(null);\n\tconst [loading, setLoading] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tif (!field.optionsRoute || !pluginId) return;\n\t\tconst controller = new AbortController();\n\t\tsetLoading(true);\n\t\tvoid (async () => {\n\t\t\ttry {\n\t\t\t\tconst res = await fetch(`/_emdash/api/plugins/${pluginId}/${field.optionsRoute}`, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: {\n\t\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\t\t\"X-EmDash-Request\": \"1\",\n\t\t\t\t\t},\n\t\t\t\t\tbody: JSON.stringify({}),\n\t\t\t\t\tsignal: controller.signal,\n\t\t\t\t});\n\t\t\t\tif (res.ok) {\n\t\t\t\t\tconst body = (await res.json()) as {\n\t\t\t\t\t\tdata: { items?: Array<{ id: string; name: string }> };\n\t\t\t\t\t};\n\t\t\t\t\tif (body.data?.items) {\n\t\t\t\t\t\tsetDynamicOptions(\n\t\t\t\t\t\t\tbody.data.items.map((item) => ({ label: item.name, value: item.id })),\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Failed to load options or aborted — static options will be used\n\t\t\t} finally {\n\t\t\t\tif (!controller.signal.aborted) {\n\t\t\t\t\tsetLoading(false);\n\t\t\t\t}\n\t\t\t}\n\t\t})();\n\t\treturn () => controller.abort();\n\t}, [field.optionsRoute, pluginId]);\n\n\tconst options = dynamicOptions ?? field.options;\n\n\treturn (\n\t\t<div>\n\t\t\t<label className=\"text-sm font-medium mb-1.5 block\">{field.label}</label>\n\t\t\t{loading ? (\n\t\t\t\t<div className=\"flex h-10 items-center px-3 text-sm text-kumo-subtle\">Loading...</div>\n\t\t\t) : (\n\t\t\t\t<select\n\t\t\t\t\tclassName=\"flex h-10 w-full rounded-md border border-kumo-line bg-transparent px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-kumo-ring focus-visible:ring-offset-2\"\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => onChange(field.action_id, e.target.value)}\n\t\t\t\t>\n\t\t\t\t\t<option value=\"\">Select...</option>\n\t\t\t\t\t{options.map((opt) => (\n\t\t\t\t\t\t<option key={opt.value} value={opt.value}>\n\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t</option>\n\t\t\t\t\t))}\n\t\t\t\t</select>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// Re-export for consumers\nexport type { PluginBlockDef } from \"./editor/PluginBlockNode\";\n\n// Exported for unit testing (pure functions, no React dependencies)\nexport { prosemirrorToPortableText as _prosemirrorToPortableText };\nexport { portableTextToProsemirror as _portableTextToProsemirror };\n\n// =============================================================================\n// Editor Footer with Writing Metrics\n// =============================================================================\n\n/**\n * Calculate reading time in minutes based on word count\n * Uses a standard reading speed of 200 words per minute\n */\nexport function calculateReadingTime(words: number): number {\n\treturn Math.ceil(words / 200);\n}\n\n/**\n * Editor footer showing writing metrics (word count, character count, reading time)\n */\nfunction EditorFooter({ editor }: { editor: Editor }) {\n\tconst { words, characters } = useEditorState({\n\t\teditor,\n\t\tselector: (ctx) => {\n\t\t\tconst storage: { words: () => number; characters: () => number } =\n\t\t\t\tctx.editor.storage.characterCount;\n\t\t\treturn {\n\t\t\t\twords: storage.words(),\n\t\t\t\tcharacters: storage.characters(),\n\t\t\t};\n\t\t},\n\t});\n\n\tconst readingTime = calculateReadingTime(words);\n\n\treturn (\n\t\t<div className=\"border-t px-4 py-2 flex items-center gap-4 text-xs text-kumo-subtle\">\n\t\t\t<span>\n\t\t\t\t{words} {words === 1 ? \"word\" : \"words\"}\n\t\t\t</span>\n\t\t\t<span>\n\t\t\t\t{characters} {characters === 1 ? \"character\" : \"characters\"}\n\t\t\t</span>\n\t\t\t<span>{readingTime} min read</span>\n\t\t</div>\n\t);\n}\n\n/** Focus mode state for the editor */\nexport type FocusMode = \"normal\" | \"spotlight\";\n\n/** Describes a block sidebar panel request from a node view */\nexport interface BlockSidebarPanel {\n\ttype: string;\n\tattrs: Record<string, unknown>;\n\tonUpdate: (attrs: Record<string, unknown>) => void;\n\tonReplace: (attrs: Record<string, unknown>) => void;\n\tonDelete: () => void;\n\tonClose: () => void;\n}\n\n// Editor Props\nexport interface PortableTextEditorProps {\n\tvalue?: PortableTextBlock[];\n\tonChange?: (value: PortableTextBlock[]) => void;\n\tplaceholder?: string;\n\tclassName?: string;\n\teditable?: boolean;\n\t/** ID of label element for accessibility */\n\t\"aria-labelledby\"?: string;\n\t/** Plugin blocks available for insertion via slash commands */\n\tpluginBlocks?: PluginBlockDef[];\n\t/** Focus mode - controlled from parent for distraction-free mode coordination */\n\tfocusMode?: FocusMode;\n\t/** Callback when focus mode changes */\n\tonFocusModeChange?: (mode: FocusMode) => void;\n\t/** Callback to receive the editor instance for external integrations */\n\tonEditorReady?: (editor: Editor) => void;\n\t/** Minimal chrome - hides toolbar, border, footer (distraction-free mode) */\n\tminimal?: boolean;\n\t/** Callback when a block node requests sidebar space (e.g. image settings) */\n\tonBlockSidebarOpen?: (panel: BlockSidebarPanel) => void;\n\t/** Callback when a block node closes its sidebar */\n\tonBlockSidebarClose?: () => void;\n}\n\n/**\n * Portable Text Editor Component\n */\nexport function PortableTextEditor({\n\tvalue,\n\tonChange,\n\tplaceholder = \"Start writing...\",\n\tclassName,\n\teditable = true,\n\t\"aria-labelledby\": ariaLabelledby,\n\tpluginBlocks = [],\n\tfocusMode: controlledFocusMode,\n\tonFocusModeChange,\n\tonEditorReady,\n\tminimal = false,\n\tonBlockSidebarOpen,\n\tonBlockSidebarClose,\n}: PortableTextEditorProps) {\n\t// Use a ref for onChange to avoid recreating the editor when the callback changes\n\tconst onChangeRef = React.useRef(onChange);\n\tReact.useEffect(() => {\n\t\tonChangeRef.current = onChange;\n\t}, [onChange]);\n\n\t// Focus mode state - support both controlled and uncontrolled modes\n\tconst [internalFocusMode, setInternalFocusMode] = React.useState<FocusMode>(\"normal\");\n\tconst focusMode = controlledFocusMode ?? internalFocusMode;\n\tconst setFocusMode = (mode: FocusMode) => {\n\t\tif (onFocusModeChange) {\n\t\t\tonFocusModeChange(mode);\n\t\t} else {\n\t\t\tsetInternalFocusMode(mode);\n\t\t}\n\t};\n\n\t// Media picker state (for image insertion)\n\tconst [mediaPickerOpen, setMediaPickerOpen] = React.useState(false);\n\n\t// Plugin block insertion/editing state\n\tconst [pluginBlockModal, setPluginBlockModal] = React.useState<PluginBlockDef | null>(null);\n\tconst [pluginBlockInitialValues, setPluginBlockInitialValues] = React.useState<\n\t\tRecord<string, unknown> | undefined\n\t>(undefined);\n\t/** When editing an existing block, store the node position for updateAttributes */\n\tconst editingBlockPosRef = React.useRef<number | null>(null);\n\n\t// Section picker state (for inserting sections)\n\tconst [sectionPickerOpen, setSectionPickerOpen] = React.useState(false);\n\n\t// Slash commands state\n\tconst [slashMenuState, setSlashMenuState] = React.useState<SlashMenuState>({\n\t\tisOpen: false,\n\t\titems: [],\n\t\tselectedIndex: 0,\n\t\tclientRect: null,\n\t\trange: null,\n\t});\n\n\t// Ref to access current state synchronously in keyboard handlers\n\tconst slashMenuStateRef = React.useRef(slashMenuState);\n\tReact.useEffect(() => {\n\t\tslashMenuStateRef.current = slashMenuState;\n\t}, [slashMenuState]);\n\n\t// Build slash commands\n\tconst slashCommands = React.useMemo(() => {\n\t\tconst cmds: SlashCommandItem[] = [...defaultSlashCommands];\n\n\t\t// Add image command\n\t\tcmds.push({\n\t\t\tid: \"image\",\n\t\t\ttitle: \"Image\",\n\t\t\tdescription: \"Insert an image\",\n\t\t\ticon: ImageIcon,\n\t\t\taliases: [\"img\", \"photo\", \"picture\", \"url\"],\n\t\t\tcategory: \"Media\",\n\t\t\tcommand: ({ editor, range }) => {\n\t\t\t\teditor.chain().focus().deleteRange(range).run();\n\t\t\t\tsetMediaPickerOpen(true);\n\t\t\t},\n\t\t});\n\n\t\t// Add section command\n\t\tcmds.push({\n\t\t\tid: \"section\",\n\t\t\ttitle: \"Section\",\n\t\t\tdescription: \"Insert a reusable section\",\n\t\t\ticon: Stack,\n\t\t\taliases: [\"pattern\", \"block\", \"template\"],\n\t\t\tcategory: \"Content\",\n\t\t\tcommand: ({ editor, range }) => {\n\t\t\t\teditor.chain().focus().deleteRange(range).run();\n\t\t\t\tsetSectionPickerOpen(true);\n\t\t\t},\n\t\t});\n\n\t\t// Add plugin block commands\n\t\tfor (const block of pluginBlocks) {\n\t\t\tcmds.push({\n\t\t\t\tid: `plugin-${block.pluginId}-${block.type}`,\n\t\t\t\ttitle: block.label,\n\t\t\t\tdescription: block.description || `Embed a ${block.label.toLowerCase()}`,\n\t\t\t\ticon: resolveIcon(block.icon),\n\t\t\t\taliases: [block.type],\n\t\t\t\tcategory: \"Embeds\",\n\t\t\t\tcommand: ({ editor, range }) => {\n\t\t\t\t\teditor.chain().focus().deleteRange(range).run();\n\t\t\t\t\tsetPluginBlockModal(block);\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn cmds;\n\t}, [pluginBlocks]);\n\n\t// Filter commands by query — accessed via ref so the Suggestion plugin\n\t// (created once) always sees the latest command list without needing\n\t// the extension to be recreated.\n\tconst filterCommandsRef = React.useRef((_q: string): SlashCommandItem[] => []);\n\tfilterCommandsRef.current = (query: string) => {\n\t\tif (!query) return slashCommands;\n\t\tconst searchText = query.toLowerCase();\n\t\tconst titleMatches: SlashCommandItem[] = [];\n\t\tconst otherMatches: SlashCommandItem[] = [];\n\t\tfor (const item of slashCommands) {\n\t\t\tif (item.title.toLowerCase().includes(searchText)) {\n\t\t\t\ttitleMatches.push(item);\n\t\t\t} else if (\n\t\t\t\titem.description.toLowerCase().includes(searchText) ||\n\t\t\t\titem.aliases?.some((alias) => alias.toLowerCase().includes(searchText))\n\t\t\t) {\n\t\t\t\totherMatches.push(item);\n\t\t\t}\n\t\t}\n\t\treturn [...titleMatches, ...otherMatches];\n\t};\n\n\t// Convert initial value to ProseMirror format\n\tconst initialContent = React.useMemo(\n\t\t() => portableTextToProsemirror(value || []),\n\t\t[], // Only compute once on mount\n\t);\n\n\t// Memoize the entire extensions array so TipTap never diffs/replaces\n\t// plugins on re-render. The loop was: extension array changes → useEditor\n\t// calls setOptions → old Suggestion plugin destroyed → onExit fires\n\t// setSlashMenuState → re-render → new extension array → repeat.\n\t// All mutable state (filterCommands, onChange) is accessed via refs.\n\tconst extensions = React.useMemo(\n\t\t() => [\n\t\t\tStarterKit.configure({\n\t\t\t\theading: {\n\t\t\t\t\tlevels: [1, 2, 3],\n\t\t\t\t},\n\t\t\t\tdropcursor: {\n\t\t\t\t\tcolor: \"#3b82f6\",\n\t\t\t\t\twidth: 2,\n\t\t\t\t},\n\t\t\t\t// StarterKit v3 includes Link and Underline\n\t\t\t\tlink: {\n\t\t\t\t\topenOnClick: false,\n\t\t\t\t\tenableClickSelection: true,\n\t\t\t\t\tHTMLAttributes: {\n\t\t\t\t\t\tclass: \"text-kumo-brand underline\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tunderline: {},\n\t\t\t}),\n\t\t\tImageExtension,\n\t\t\tMarkdownLinkExtension,\n\t\t\tPluginBlockExtension,\n\t\t\tPlaceholder.configure({\n\t\t\t\tincludeChildren: true,\n\t\t\t\tplaceholder: ({ node }) => {\n\t\t\t\t\tif (node.type.name === \"paragraph\") {\n\t\t\t\t\t\treturn placeholder;\n\t\t\t\t\t}\n\t\t\t\t\treturn placeholder;\n\t\t\t\t},\n\t\t\t}),\n\t\t\tTextAlign.configure({\n\t\t\t\ttypes: [\"heading\", \"paragraph\"],\n\t\t\t}),\n\t\t\tcreateSlashCommandsExtension({\n\t\t\t\tfilterCommands: (query: string) => filterCommandsRef.current(query),\n\t\t\t\tonStateChange: setSlashMenuState,\n\t\t\t\tgetState: () => slashMenuStateRef.current,\n\t\t\t}),\n\t\t\tCharacterCount,\n\t\t\tFocus.configure({\n\t\t\t\tclassName: \"has-focus\",\n\t\t\t\tmode: \"all\",\n\t\t\t}),\n\t\t\tTypography,\n\t\t],\n\t\t[], // Created once — all mutable state accessed via refs\n\t);\n\n\t// Stable editorProps reference — a new object every render would cause\n\t// compareOptions to call setOptions → updateState → plugin teardown →\n\t// Suggestion onExit → setSlashMenuState → re-render → infinite loop.\n\tconst editorProps = React.useMemo(\n\t\t() => ({\n\t\t\tattributes: {\n\t\t\t\tclass:\n\t\t\t\t\t\"prose prose-sm sm:prose-base dark:prose-invert max-w-none focus:outline-none min-h-[200px] p-4\",\n\t\t\t},\n\t\t}),\n\t\t[],\n\t);\n\n\tconst editor = useEditor({\n\t\textensions,\n\t\tcontent: initialContent as Parameters<typeof useEditor>[0][\"content\"],\n\t\teditable,\n\t\timmediatelyRender: true,\n\t\teditorProps,\n\t\tonUpdate: ({ editor: updatedEditor }) => {\n\t\t\tconst cb = onChangeRef.current;\n\t\t\tif (cb) {\n\t\t\t\tconst doc = updatedEditor.getJSON();\n\t\t\t\t// TipTap's getJSON() returns JSONContent which is structurally compatible\n\t\t\t\tconst pmDoc = doc as Parameters<typeof prosemirrorToPortableText>[0];\n\t\t\t\tconst portableText = prosemirrorToPortableText(pmDoc);\n\t\t\t\tcb(portableText);\n\t\t\t}\n\t\t},\n\t});\n\n\t// Notify when editor is ready\n\tReact.useEffect(() => {\n\t\tif (editor && onEditorReady) {\n\t\t\tonEditorReady(editor);\n\t\t}\n\t}, [editor, onEditorReady]);\n\n\t// Register plugin blocks into editor storage so the node view can look up metadata\n\tReact.useEffect(() => {\n\t\tif (editor) {\n\t\t\tregisterPluginBlocks(\n\t\t\t\teditor as unknown as { storage: Record<string, Record<string, unknown>> },\n\t\t\t\tpluginBlocks,\n\t\t\t);\n\t\t}\n\t}, [editor, pluginBlocks]);\n\n\t// Wire up the onEditBlock callback so the node view can open the Block Kit modal\n\tReact.useEffect(() => {\n\t\tif (!editor) return;\n\t\tconst storage = (editor.storage as unknown as Record<string, Record<string, unknown>>)\n\t\t\t.pluginBlock;\n\t\tif (!storage) return;\n\t\tstorage.onEditBlock = (attrs: {\n\t\t\tblockType: string;\n\t\t\tid: string;\n\t\t\tdata: Record<string, unknown>;\n\t\t\tpos: number;\n\t\t}) => {\n\t\t\tconst blockDef = pluginBlocks.find((b) => b.type === attrs.blockType);\n\t\t\tif (!blockDef) return;\n\t\t\teditingBlockPosRef.current = attrs.pos;\n\t\t\tsetPluginBlockInitialValues({ id: attrs.id, ...attrs.data });\n\t\t\tsetPluginBlockModal(blockDef);\n\t\t};\n\t\treturn () => {\n\t\t\tstorage.onEditBlock = null;\n\t\t};\n\t}, [editor, pluginBlocks]);\n\n\t// Wire up block sidebar callbacks so node views (e.g. ImageNode) can request sidebar space\n\tconst onBlockSidebarOpenRef = React.useRef(onBlockSidebarOpen);\n\tonBlockSidebarOpenRef.current = onBlockSidebarOpen;\n\tconst onBlockSidebarCloseRef = React.useRef(onBlockSidebarClose);\n\tonBlockSidebarCloseRef.current = onBlockSidebarClose;\n\n\tReact.useEffect(() => {\n\t\tif (!editor) return;\n\t\tconst storage = (editor.storage as unknown as Record<string, Record<string, unknown>>).image;\n\t\tif (!storage) return;\n\t\tstorage.onOpenBlockSidebar = (panel: BlockSidebarPanel) => {\n\t\t\tonBlockSidebarOpenRef.current?.(panel);\n\t\t};\n\t\tstorage.onCloseBlockSidebar = () => {\n\t\t\tonBlockSidebarCloseRef.current?.();\n\t\t};\n\t\treturn () => {\n\t\t\tstorage.onOpenBlockSidebar = null;\n\t\t\tstorage.onCloseBlockSidebar = null;\n\t\t};\n\t}, [editor]);\n\n\t// Handle image selection from media picker\n\tconst handleImageSelect = React.useCallback(\n\t\t(item: MediaItem) => {\n\t\t\tif (editor) {\n\t\t\t\t// For external providers, src is only used for admin preview\n\t\t\t\t// The frontend Image component uses provider + mediaId to generate proper URLs\n\t\t\t\teditor\n\t\t\t\t\t.chain()\n\t\t\t\t\t.focus()\n\t\t\t\t\t.setImage({\n\t\t\t\t\t\tsrc: item.url,\n\t\t\t\t\t\talt: item.alt || item.filename,\n\t\t\t\t\t\tmediaId: item.id,\n\t\t\t\t\t\tprovider: item.provider || \"local\",\n\t\t\t\t\t\twidth: item.width,\n\t\t\t\t\t\theight: item.height,\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t}\n\t\t\tsetMediaPickerOpen(false);\n\t\t},\n\t\t[editor],\n\t);\n\n\t// Handle plugin block insertion or update\n\tconst handlePluginBlockInsert = React.useCallback(\n\t\t(values: Record<string, unknown>) => {\n\t\t\tif (!editor || !pluginBlockModal) return;\n\n\t\t\tconst { id, ...data } = values;\n\t\t\tconst editPos = editingBlockPosRef.current;\n\n\t\t\tif (editPos !== null) {\n\t\t\t\t// Editing an existing block — update its attributes in place\n\t\t\t\tconst { tr } = editor.state;\n\t\t\t\tconst node = tr.doc.nodeAt(editPos);\n\t\t\t\tif (node?.type.name === \"pluginBlock\") {\n\t\t\t\t\ttr.setNodeMarkup(editPos, undefined, {\n\t\t\t\t\t\t...node.attrs,\n\t\t\t\t\t\tid: typeof id === \"string\" ? id : node.attrs.id,\n\t\t\t\t\t\tdata,\n\t\t\t\t\t});\n\t\t\t\t\teditor.view.dispatch(tr);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Inserting a new block\n\t\t\t\teditor\n\t\t\t\t\t.chain()\n\t\t\t\t\t.focus()\n\t\t\t\t\t.insertContent({\n\t\t\t\t\t\ttype: \"pluginBlock\",\n\t\t\t\t\t\tattrs: {\n\t\t\t\t\t\t\tblockType: pluginBlockModal.type,\n\t\t\t\t\t\t\tid: typeof id === \"string\" ? id : \"\",\n\t\t\t\t\t\t\tdata,\n\t\t\t\t\t\t},\n\t\t\t\t\t})\n\t\t\t\t\t.run();\n\t\t\t}\n\n\t\t\tsetPluginBlockModal(null);\n\t\t\tsetPluginBlockInitialValues(undefined);\n\t\t\teditingBlockPosRef.current = null;\n\t\t},\n\t\t[editor, pluginBlockModal],\n\t);\n\n\t// Handle slash menu command execution\n\tconst handleSlashCommand = React.useCallback(\n\t\t(item: SlashCommandItem) => {\n\t\t\tif (editor && slashMenuState.range) {\n\t\t\t\titem.command({ editor, range: slashMenuState.range });\n\t\t\t\tsetSlashMenuState((prev) => ({ ...prev, isOpen: false }));\n\t\t\t}\n\t\t},\n\t\t[editor, slashMenuState.range],\n\t);\n\n\t// Handle section selection - insert section content at cursor\n\tconst handleSectionSelect = React.useCallback(\n\t\t(section: Section) => {\n\t\t\tif (!editor || !section.content || section.content.length === 0) return;\n\n\t\t\t// Convert Portable Text to ProseMirror format\n\t\t\tconst ptContent = Array.isArray(section.content)\n\t\t\t\t? (section.content as PortableTextBlock[])\n\t\t\t\t: [];\n\t\t\tconst { content: prosemirrorContent } = portableTextToProsemirror(ptContent);\n\n\t\t\t// Insert the content at current cursor position\n\t\t\teditor.chain().focus().insertContent(prosemirrorContent).run();\n\t\t},\n\t\t[editor],\n\t);\n\n\tif (!editor) {\n\t\treturn (\n\t\t\t<div className={cn(\"border rounded-lg\", className)}>\n\t\t\t\t<div className=\"p-4 text-kumo-subtle\">Loading editor...</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"border rounded-lg overflow-hidden\",\n\t\t\t\tminimal && \"border-0 rounded-none -mx-4\",\n\t\t\t\tfocusMode === \"spotlight\" && \"spotlight-mode\",\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\taria-labelledby={ariaLabelledby}\n\t\t>\n\t\t\t{!minimal && (\n\t\t\t\t<EditorToolbar editor={editor} focusMode={focusMode} onFocusModeChange={setFocusMode} />\n\t\t\t)}\n\t\t\t<EditorBubbleMenu editor={editor} />\n\t\t\t<div className=\"relative overflow-visible\">\n\t\t\t\t<EditorContent editor={editor} />\n\t\t\t\t{editable && <DragHandleWrapper editor={editor} />}\n\t\t\t</div>\n\t\t\t{!minimal && <EditorFooter editor={editor} />}\n\n\t\t\t{/* Slash command menu */}\n\t\t\t<SlashCommandMenu\n\t\t\t\tstate={slashMenuState}\n\t\t\t\tonCommand={handleSlashCommand}\n\t\t\t\tonClose={() => setSlashMenuState((prev) => ({ ...prev, isOpen: false }))}\n\t\t\t\tsetSelectedIndex={(index) =>\n\t\t\t\t\tsetSlashMenuState((prev) => ({ ...prev, selectedIndex: index }))\n\t\t\t\t}\n\t\t\t/>\n\n\t\t\t{/* Media picker for image insertion */}\n\t\t\t<MediaPickerModal\n\t\t\t\topen={mediaPickerOpen}\n\t\t\t\tonOpenChange={setMediaPickerOpen}\n\t\t\t\tonSelect={handleImageSelect}\n\t\t\t\tmimeTypeFilter=\"image/\"\n\t\t\t\ttitle=\"Select Image\"\n\t\t\t/>\n\n\t\t\t{/* Plugin block insertion/editing modal */}\n\t\t\t<PluginBlockModal\n\t\t\t\tblock={pluginBlockModal}\n\t\t\t\tinitialValues={pluginBlockInitialValues}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetPluginBlockModal(null);\n\t\t\t\t\tsetPluginBlockInitialValues(undefined);\n\t\t\t\t\teditingBlockPosRef.current = null;\n\t\t\t\t}}\n\t\t\t\tonInsert={handlePluginBlockInsert}\n\t\t\t/>\n\n\t\t\t{/* Section picker modal */}\n\t\t\t<SectionPickerModal\n\t\t\t\topen={sectionPickerOpen}\n\t\t\t\tonOpenChange={setSectionPickerOpen}\n\t\t\t\tonSelect={handleSectionSelect}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\n/**\n * Bubble Menu - appears when text is selected\n * Shows inline formatting options and link editing\n */\nfunction EditorBubbleMenu({ editor }: { editor: Editor }) {\n\tconst [showLinkInput, setShowLinkInput] = React.useState(false);\n\tconst [linkUrl, setLinkUrl] = React.useState(\"\");\n\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\t// When bubble menu opens with link input, populate the URL\n\tReact.useEffect(() => {\n\t\tif (showLinkInput) {\n\t\t\tconst existingUrl = editor.getAttributes(\"link\").href || \"\";\n\t\t\tsetLinkUrl(existingUrl);\n\t\t\t// Focus input after state update\n\t\t\tsetTimeout(() => inputRef.current?.focus(), 0);\n\t\t}\n\t}, [showLinkInput, editor]);\n\n\tconst handleSetLink = () => {\n\t\tif (linkUrl.trim() === \"\") {\n\t\t\teditor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n\t\t} else {\n\t\t\teditor.chain().focus().extendMarkRange(\"link\").setLink({ href: linkUrl.trim() }).run();\n\t\t}\n\t\tsetShowLinkInput(false);\n\t\tsetLinkUrl(\"\");\n\t};\n\n\tconst handleRemoveLink = () => {\n\t\teditor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n\t\tsetShowLinkInput(false);\n\t\tsetLinkUrl(\"\");\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\") {\n\t\t\te.preventDefault();\n\t\t\thandleSetLink();\n\t\t} else if (e.key === \"Escape\") {\n\t\t\tsetShowLinkInput(false);\n\t\t\tsetLinkUrl(\"\");\n\t\t\teditor.commands.focus();\n\t\t}\n\t};\n\n\treturn (\n\t\t<BubbleMenu\n\t\t\teditor={editor}\n\t\t\toptions={{\n\t\t\t\tplacement: \"top\",\n\t\t\t\toffset: 8,\n\t\t\t\tflip: true,\n\t\t\t\tshift: true,\n\t\t\t}}\n\t\t\tclassName=\"z-[100] flex items-center gap-0.5 rounded-lg border bg-kumo-base p-1 shadow-lg\"\n\t\t>\n\t\t\t{showLinkInput ? (\n\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\tplaceholder=\"https://...\"\n\t\t\t\t\t\tvalue={linkUrl}\n\t\t\t\t\t\tonChange={(e) => setLinkUrl(e.target.value)}\n\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\tclassName=\"h-8 w-48 text-sm\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\tonClick={handleSetLink}\n\t\t\t\t\t\ttitle=\"Apply link\"\n\t\t\t\t\t\taria-label=\"Apply link\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<ArrowSquareOut className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t{editor.isActive(\"link\") && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tclassName=\"h-8 w-8 text-kumo-danger\"\n\t\t\t\t\t\t\tonClick={handleRemoveLink}\n\t\t\t\t\t\t\ttitle=\"Remove link\"\n\t\t\t\t\t\t\taria-label=\"Remove link\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<LinkBreak className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t<BubbleButton\n\t\t\t\t\t\tonClick={() => editor.chain().focus().toggleBold().run()}\n\t\t\t\t\t\tactive={editor.isActive(\"bold\")}\n\t\t\t\t\t\ttitle=\"Bold\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<TextB className=\"h-4 w-4\" />\n\t\t\t\t\t</BubbleButton>\n\t\t\t\t\t<BubbleButton\n\t\t\t\t\t\tonClick={() => editor.chain().focus().toggleItalic().run()}\n\t\t\t\t\t\tactive={editor.isActive(\"italic\")}\n\t\t\t\t\t\ttitle=\"Italic\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<TextItalic className=\"h-4 w-4\" />\n\t\t\t\t\t</BubbleButton>\n\t\t\t\t\t<BubbleButton\n\t\t\t\t\t\tonClick={() => editor.chain().focus().toggleUnderline().run()}\n\t\t\t\t\t\tactive={editor.isActive(\"underline\")}\n\t\t\t\t\t\ttitle=\"Underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<TextUnderline className=\"h-4 w-4\" />\n\t\t\t\t\t</BubbleButton>\n\t\t\t\t\t<BubbleButton\n\t\t\t\t\t\tonClick={() => editor.chain().focus().toggleStrike().run()}\n\t\t\t\t\t\tactive={editor.isActive(\"strike\")}\n\t\t\t\t\t\ttitle=\"Strikethrough\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<TextStrikethrough className=\"h-4 w-4\" />\n\t\t\t\t\t</BubbleButton>\n\t\t\t\t\t<BubbleButton\n\t\t\t\t\t\tonClick={() => editor.chain().focus().toggleCode().run()}\n\t\t\t\t\t\tactive={editor.isActive(\"code\")}\n\t\t\t\t\t\ttitle=\"Code\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Code className=\"h-4 w-4\" />\n\t\t\t\t\t</BubbleButton>\n\t\t\t\t\t<div className=\"w-px h-6 bg-kumo-line mx-1\" />\n\t\t\t\t\t<BubbleButton\n\t\t\t\t\t\tonClick={() => setShowLinkInput(true)}\n\t\t\t\t\t\tactive={editor.isActive(\"link\")}\n\t\t\t\t\t\ttitle={editor.isActive(\"link\") ? \"Edit link\" : \"Add link\"}\n\t\t\t\t\t>\n\t\t\t\t\t\t<LinkIcon className=\"h-4 w-4\" />\n\t\t\t\t\t</BubbleButton>\n\t\t\t\t</>\n\t\t\t)}\n\t\t</BubbleMenu>\n\t);\n}\n\nfunction BubbleButton({\n\tonClick,\n\tactive,\n\ttitle,\n\tchildren,\n}: {\n\tonClick: () => void;\n\tactive?: boolean;\n\ttitle: string;\n\tchildren: React.ReactNode;\n}) {\n\treturn (\n\t\t<Button\n\t\t\ttype=\"button\"\n\t\t\tvariant=\"ghost\"\n\t\t\tshape=\"square\"\n\t\t\tclassName={cn(\"h-8 w-8\", active && \"bg-kumo-tint text-kumo-default\")}\n\t\t\tonClick={onClick}\n\t\t\ttitle={title}\n\t\t\taria-label={title}\n\t\t>\n\t\t\t{children}\n\t\t</Button>\n\t);\n}\n\n/**\n * Editor Toolbar\n *\n * Implements WAI-ARIA toolbar pattern with proper keyboard navigation.\n * Arrow keys move focus between buttons, Home/End jump to first/last.\n */\nfunction EditorToolbar({\n\teditor,\n\tfocusMode,\n\tonFocusModeChange,\n}: {\n\teditor: Editor;\n\tfocusMode: FocusMode;\n\tonFocusModeChange: (mode: FocusMode) => void;\n}) {\n\tconst [mediaPickerOpen, setMediaPickerOpen] = React.useState(false);\n\tconst [showLinkPopover, setShowLinkPopover] = React.useState(false);\n\tconst [linkUrl, setLinkUrl] = React.useState(\"\");\n\tconst toolbarRef = React.useRef<HTMLDivElement>(null);\n\tconst linkInputRef = React.useRef<HTMLInputElement>(null);\n\n\t// Subscribe to editor state changes for reactive button states\n\tconst editorState = useEditorState({\n\t\teditor,\n\t\tselector: (ctx) => ({\n\t\t\tisBold: ctx.editor.isActive(\"bold\"),\n\t\t\tisItalic: ctx.editor.isActive(\"italic\"),\n\t\t\tisUnderline: ctx.editor.isActive(\"underline\"),\n\t\t\tisStrike: ctx.editor.isActive(\"strike\"),\n\t\t\tisCode: ctx.editor.isActive(\"code\"),\n\t\t\tisHeading1: ctx.editor.isActive(\"heading\", { level: 1 }),\n\t\t\tisHeading2: ctx.editor.isActive(\"heading\", { level: 2 }),\n\t\t\tisHeading3: ctx.editor.isActive(\"heading\", { level: 3 }),\n\t\t\tisBulletList: ctx.editor.isActive(\"bulletList\"),\n\t\t\tisOrderedList: ctx.editor.isActive(\"orderedList\"),\n\t\t\tisBlockquote: ctx.editor.isActive(\"blockquote\"),\n\t\t\tisCodeBlock: ctx.editor.isActive(\"codeBlock\"),\n\t\t\tisAlignLeft: ctx.editor.isActive({ textAlign: \"left\" }),\n\t\t\tisAlignCenter: ctx.editor.isActive({ textAlign: \"center\" }),\n\t\t\tisAlignRight: ctx.editor.isActive({ textAlign: \"right\" }),\n\t\t\tisLink: ctx.editor.isActive(\"link\"),\n\t\t\tcanUndo: ctx.editor.can().undo(),\n\t\t\tcanRedo: ctx.editor.can().redo(),\n\t\t}),\n\t});\n\n\t// Populate link URL when opening popover\n\tReact.useEffect(() => {\n\t\tif (showLinkPopover) {\n\t\t\tconst existingUrl = editor.getAttributes(\"link\").href || \"\";\n\t\t\tsetLinkUrl(existingUrl);\n\t\t\tsetTimeout(() => linkInputRef.current?.focus(), 0);\n\t\t}\n\t}, [showLinkPopover, editor]);\n\n\tconst handleSetLink = () => {\n\t\tif (linkUrl.trim() === \"\") {\n\t\t\teditor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n\t\t} else {\n\t\t\teditor.chain().focus().extendMarkRange(\"link\").setLink({ href: linkUrl.trim() }).run();\n\t\t}\n\t\tsetShowLinkPopover(false);\n\t\tsetLinkUrl(\"\");\n\t};\n\n\tconst handleRemoveLink = () => {\n\t\teditor.chain().focus().extendMarkRange(\"link\").unsetLink().run();\n\t\tsetShowLinkPopover(false);\n\t\tsetLinkUrl(\"\");\n\t};\n\n\tconst handleLinkKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\") {\n\t\t\te.preventDefault();\n\t\t\thandleSetLink();\n\t\t} else if (e.key === \"Escape\") {\n\t\t\tsetShowLinkPopover(false);\n\t\t\tsetLinkUrl(\"\");\n\t\t\teditor.commands.focus();\n\t\t}\n\t};\n\n\tconst handleImageSelect = React.useCallback(\n\t\t(item: MediaItem) => {\n\t\t\teditor\n\t\t\t\t.chain()\n\t\t\t\t.focus()\n\t\t\t\t.setImage({\n\t\t\t\t\tsrc: item.url,\n\t\t\t\t\talt: item.alt || item.filename,\n\t\t\t\t\tmediaId: item.id,\n\t\t\t\t\twidth: item.width,\n\t\t\t\t\theight: item.height,\n\t\t\t\t})\n\t\t\t\t.run();\n\t\t},\n\t\t[editor],\n\t);\n\n\t// Keyboard navigation for toolbar (WAI-ARIA toolbar pattern)\n\tconst handleKeyDown = React.useCallback((e: React.KeyboardEvent<HTMLDivElement>) => {\n\t\tconst toolbar = toolbarRef.current;\n\t\tif (!toolbar) return;\n\n\t\tconst buttons = [\n\t\t\t...toolbar.querySelectorAll<HTMLButtonElement>(\n\t\t\t\t'button:not([disabled]), [role=\"button\"]:not([disabled])',\n\t\t\t),\n\t\t];\n\t\tconst currentIndex = buttons.findIndex((btn) => btn === document.activeElement);\n\t\tif (currentIndex === -1) return;\n\n\t\tlet nextIndex: number | null = null;\n\n\t\tswitch (e.key) {\n\t\t\tcase \"ArrowRight\":\n\t\t\tcase \"ArrowDown\":\n\t\t\t\tnextIndex = (currentIndex + 1) % buttons.length;\n\t\t\t\tbreak;\n\t\t\tcase \"ArrowLeft\":\n\t\t\tcase \"ArrowUp\":\n\t\t\t\tnextIndex = (currentIndex - 1 + buttons.length) % buttons.length;\n\t\t\t\tbreak;\n\t\t\tcase \"Home\":\n\t\t\t\tnextIndex = 0;\n\t\t\t\tbreak;\n\t\t\tcase \"End\":\n\t\t\t\tnextIndex = buttons.length - 1;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\treturn;\n\t\t}\n\n\t\tif (nextIndex !== null) {\n\t\t\te.preventDefault();\n\t\t\tbuttons[nextIndex]?.focus();\n\t\t}\n\t}, []);\n\n\treturn (\n\t\t<div\n\t\t\tref={toolbarRef}\n\t\t\trole=\"toolbar\"\n\t\t\taria-label=\"Text formatting\"\n\t\t\tclassName=\"border-b bg-kumo-tint/50 p-1 flex flex-wrap gap-0.5\"\n\t\t\tonKeyDown={handleKeyDown}\n\t\t>\n\t\t\t{/* Text formatting */}\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleBold().run()}\n\t\t\t\t\tactive={editorState.isBold}\n\t\t\t\t\ttitle=\"Bold\"\n\t\t\t\t>\n\t\t\t\t\t<TextB className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleItalic().run()}\n\t\t\t\t\tactive={editorState.isItalic}\n\t\t\t\t\ttitle=\"Italic\"\n\t\t\t\t>\n\t\t\t\t\t<TextItalic className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleUnderline().run()}\n\t\t\t\t\tactive={editorState.isUnderline}\n\t\t\t\t\ttitle=\"Underline\"\n\t\t\t\t>\n\t\t\t\t\t<TextUnderline className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleStrike().run()}\n\t\t\t\t\tactive={editorState.isStrike}\n\t\t\t\t\ttitle=\"Strikethrough\"\n\t\t\t\t>\n\t\t\t\t\t<TextStrikethrough className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleCode().run()}\n\t\t\t\t\tactive={editorState.isCode}\n\t\t\t\t\ttitle=\"Inline Code\"\n\t\t\t\t>\n\t\t\t\t\t<Code className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t</ToolbarGroup>\n\n\t\t\t<ToolbarSeparator />\n\n\t\t\t{/* Headings */}\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}\n\t\t\t\t\tactive={editorState.isHeading1}\n\t\t\t\t\ttitle=\"Heading 1\"\n\t\t\t\t>\n\t\t\t\t\t<TextHOne className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}\n\t\t\t\t\tactive={editorState.isHeading2}\n\t\t\t\t\ttitle=\"Heading 2\"\n\t\t\t\t>\n\t\t\t\t\t<TextHTwo className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleHeading({ level: 3 }).run()}\n\t\t\t\t\tactive={editorState.isHeading3}\n\t\t\t\t\ttitle=\"Heading 3\"\n\t\t\t\t>\n\t\t\t\t\t<TextHThree className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t</ToolbarGroup>\n\n\t\t\t<ToolbarSeparator />\n\n\t\t\t{/* Lists and blocks */}\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleBulletList().run()}\n\t\t\t\t\tactive={editorState.isBulletList}\n\t\t\t\t\ttitle=\"Bullet List\"\n\t\t\t\t>\n\t\t\t\t\t<List className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleOrderedList().run()}\n\t\t\t\t\tactive={editorState.isOrderedList}\n\t\t\t\t\ttitle=\"Numbered List\"\n\t\t\t\t>\n\t\t\t\t\t<ListNumbers className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleBlockquote().run()}\n\t\t\t\t\tactive={editorState.isBlockquote}\n\t\t\t\t\ttitle=\"Quote\"\n\t\t\t\t>\n\t\t\t\t\t<Quotes className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().toggleCodeBlock().run()}\n\t\t\t\t\tactive={editorState.isCodeBlock}\n\t\t\t\t\ttitle=\"Code Block\"\n\t\t\t\t>\n\t\t\t\t\t<CodeBlock className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t</ToolbarGroup>\n\n\t\t\t<ToolbarSeparator />\n\n\t\t\t{/* Text alignment */}\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().setTextAlign(\"left\").run()}\n\t\t\t\t\tactive={editorState.isAlignLeft}\n\t\t\t\t\ttitle=\"Align Left\"\n\t\t\t\t>\n\t\t\t\t\t<TextAlignLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().setTextAlign(\"center\").run()}\n\t\t\t\t\tactive={editorState.isAlignCenter}\n\t\t\t\t\ttitle=\"Align Center\"\n\t\t\t\t>\n\t\t\t\t\t<TextAlignCenter className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().setTextAlign(\"right\").run()}\n\t\t\t\t\tactive={editorState.isAlignRight}\n\t\t\t\t\ttitle=\"Align Right\"\n\t\t\t\t>\n\t\t\t\t\t<TextAlignRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t</ToolbarGroup>\n\n\t\t\t<ToolbarSeparator />\n\n\t\t\t{/* Insert */}\n\t\t\t<ToolbarGroup>\n\t\t\t\t{/* Link with popover */}\n\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t<ToolbarButton\n\t\t\t\t\t\tonClick={() => setShowLinkPopover(!showLinkPopover)}\n\t\t\t\t\t\tactive={editorState.isLink}\n\t\t\t\t\t\ttitle=\"Insert Link\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<LinkIcon className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t</ToolbarButton>\n\t\t\t\t\t{showLinkPopover && (\n\t\t\t\t\t\t<div className=\"absolute top-full left-0 mt-1 z-50 rounded-md border bg-kumo-overlay p-3 shadow-lg\">\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t<label className=\"text-xs font-medium text-kumo-subtle\">URL</label>\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tref={linkInputRef}\n\t\t\t\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"https://...\"\n\t\t\t\t\t\t\t\t\t\tvalue={linkUrl}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setLinkUrl(e.target.value)}\n\t\t\t\t\t\t\t\t\t\tonKeyDown={handleLinkKeyDown}\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-8 w-52 text-sm\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\tsetShowLinkPopover(false);\n\t\t\t\t\t\t\t\t\t\t\tsetLinkUrl(\"\");\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t<div className=\"flex gap-1\">\n\t\t\t\t\t\t\t\t\t\t{editorState.isLink && (\n\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-kumo-danger\"\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={handleRemoveLink}\n\t\t\t\t\t\t\t\t\t\t\t\ticon={<LinkBreak />}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tRemove\n\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"primary\" size=\"sm\" onClick={handleSetLink}>\n\t\t\t\t\t\t\t\t\t\t\tApply\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<ToolbarButton onClick={() => setMediaPickerOpen(true)} title=\"Insert Image\">\n\t\t\t\t\t<ImageIcon className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().setHorizontalRule().run()}\n\t\t\t\t\ttitle=\"Insert Horizontal Rule\"\n\t\t\t\t>\n\t\t\t\t\t<Minus className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t</ToolbarGroup>\n\n\t\t\t<ToolbarSeparator aria-hidden=\"true\" />\n\n\t\t\t{/* History */}\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().undo().run()}\n\t\t\t\t\tdisabled={!editorState.canUndo}\n\t\t\t\t\ttitle=\"Undo\"\n\t\t\t\t>\n\t\t\t\t\t<ArrowUUpLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => editor.chain().focus().redo().run()}\n\t\t\t\t\tdisabled={!editorState.canRedo}\n\t\t\t\t\ttitle=\"Redo\"\n\t\t\t\t>\n\t\t\t\t\t<ArrowUUpRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t</ToolbarGroup>\n\n\t\t\t<ToolbarSeparator aria-hidden=\"true\" />\n\n\t\t\t{/* Focus mode */}\n\t\t\t<ToolbarGroup>\n\t\t\t\t<ToolbarButton\n\t\t\t\t\tonClick={() => onFocusModeChange(focusMode === \"spotlight\" ? \"normal\" : \"spotlight\")}\n\t\t\t\t\tactive={focusMode === \"spotlight\"}\n\t\t\t\t\ttitle={focusMode === \"spotlight\" ? \"Exit Spotlight Mode\" : \"Spotlight Mode\"}\n\t\t\t\t>\n\t\t\t\t\t<Eye className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t</ToolbarButton>\n\t\t\t</ToolbarGroup>\n\n\t\t\t{/* Media Picker Modal */}\n\t\t\t<MediaPickerModal\n\t\t\t\topen={mediaPickerOpen}\n\t\t\t\tonOpenChange={setMediaPickerOpen}\n\t\t\t\tonSelect={handleImageSelect}\n\t\t\t\tmimeTypeFilter=\"image/\"\n\t\t\t\ttitle=\"Select Image\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nfunction ToolbarGroup({ children }: { children: React.ReactNode }) {\n\treturn <div className=\"flex gap-0.5\">{children}</div>;\n}\n\nfunction ToolbarSeparator() {\n\treturn <div className=\"w-px bg-kumo-line mx-1\" />;\n}\n\ninterface ToolbarButtonProps {\n\tonClick?: () => void;\n\tactive?: boolean;\n\tdisabled?: boolean;\n\ttitle: string; // Required for accessibility\n\tchildren: React.ReactNode;\n}\n\nfunction ToolbarButton({ onClick, active, disabled, title, children }: ToolbarButtonProps) {\n\treturn (\n\t\t<Button\n\t\t\ttype=\"button\"\n\t\t\tvariant=\"ghost\"\n\t\t\tshape=\"square\"\n\t\t\tclassName={cn(\"h-8 w-8\", active && \"bg-kumo-tint text-kumo-default\")}\n\t\t\tonMouseDown={(e) => e.preventDefault()}\n\t\t\tonClick={onClick}\n\t\t\tdisabled={disabled}\n\t\t\taria-label={title}\n\t\t\taria-pressed={active}\n\t\t\ttabIndex={0}\n\t\t>\n\t\t\t{children}\n\t\t</Button>\n\t);\n}\n\nexport default PortableTextEditor;\n","import { Badge, Button, Loader, Toast } from \"@cloudflare/kumo\";\nimport {\n\tClockCounterClockwise,\n\tArrowCounterClockwise,\n\tCaretDown,\n\tCaretUp,\n\tPlus,\n\tMinus,\n\tPencilSimple,\n} from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { fetchRevisions, restoreRevision, type Revision } from \"../lib/api\";\nimport { formatRelativeTime } from \"../lib/utils\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\n// =============================================================================\n// Diff utilities\n// =============================================================================\n\ntype DiffKind = \"added\" | \"removed\" | \"changed\" | \"unchanged\";\n\ninterface FieldDiff {\n\tfield: string;\n\tkind: DiffKind;\n\toldValue?: unknown;\n\tnewValue?: unknown;\n}\n\n/**\n * Compute field-level diff between two revision data snapshots.\n * `older` is the revision being viewed, `newer` is the next revision after it.\n */\nfunction computeFieldDiff(\n\tolder: Record<string, unknown>,\n\tnewer: Record<string, unknown>,\n): FieldDiff[] {\n\tconst allKeys = new Set([...Object.keys(older), ...Object.keys(newer)]);\n\tconst diffs: FieldDiff[] = [];\n\n\tfor (const key of allKeys) {\n\t\tconst inOlder = key in older;\n\t\tconst inNewer = key in newer;\n\n\t\tif (inOlder && !inNewer) {\n\t\t\tdiffs.push({ field: key, kind: \"removed\", oldValue: older[key] });\n\t\t} else if (!inOlder && inNewer) {\n\t\t\tdiffs.push({ field: key, kind: \"added\", newValue: newer[key] });\n\t\t} else {\n\t\t\tconst oldJson = JSON.stringify(older[key]);\n\t\t\tconst newJson = JSON.stringify(newer[key]);\n\t\t\tif (oldJson !== newJson) {\n\t\t\t\tdiffs.push({ field: key, kind: \"changed\", oldValue: older[key], newValue: newer[key] });\n\t\t\t} else {\n\t\t\t\tdiffs.push({ field: key, kind: \"unchanged\", oldValue: older[key], newValue: newer[key] });\n\t\t\t}\n\t\t}\n\t}\n\n\t// Sort: changes first, then added, removed, unchanged\n\tconst kindOrder: Record<DiffKind, number> = { changed: 0, added: 1, removed: 2, unchanged: 3 };\n\tdiffs.sort((a, b) => kindOrder[a.kind] - kindOrder[b.kind]);\n\n\treturn diffs;\n}\n\n/** Format a value for display in the diff view */\nfunction formatDiffValue(value: unknown): string {\n\tif (value === null || value === undefined) return \"—\";\n\tif (typeof value === \"string\") return value;\n\treturn JSON.stringify(value, null, 2);\n}\n\ninterface RevisionHistoryProps {\n\tcollection: string;\n\tentryId: string;\n\t/** Called when a revision is successfully restored */\n\tonRestored?: () => void;\n}\n\n/**\n * Format a date as a full timestamp\n */\nfunction formatFullDate(dateString: string): string {\n\treturn new Date(dateString).toLocaleString(undefined, {\n\t\tweekday: \"short\",\n\t\tyear: \"numeric\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t});\n}\n\n/**\n * RevisionHistory component - displays revision history for a content item\n * with ability to restore previous versions.\n */\nexport function RevisionHistory({ collection, entryId, onRestored }: RevisionHistoryProps) {\n\tconst [isExpanded, setIsExpanded] = React.useState(false);\n\tconst [selectedRevision, setSelectedRevision] = React.useState<Revision | null>(null);\n\tconst [restoreTarget, setRestoreTarget] = React.useState<Revision | null>(null);\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\n\tconst { data, isLoading, error } = useQuery({\n\t\tqueryKey: [\"revisions\", collection, entryId],\n\t\tqueryFn: () => fetchRevisions(collection, entryId, { limit: 20 }),\n\t\tenabled: isExpanded, // Only fetch when expanded\n\t});\n\n\tconst restoreMutation = useMutation({\n\t\tmutationFn: (revisionId: string) => restoreRevision(revisionId),\n\t\tonSuccess: () => {\n\t\t\t// Invalidate content and revisions queries\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"content\", collection, entryId],\n\t\t\t});\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"revisions\", collection, entryId],\n\t\t\t});\n\t\t\tsetSelectedRevision(null);\n\t\t\tsetRestoreTarget(null);\n\t\t\tonRestored?.();\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Revision restored\",\n\t\t\t\tdescription: \"Content has been updated to the selected revision.\",\n\t\t\t});\n\t\t},\n\t\tonError: (err: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Restore failed\",\n\t\t\t\tdescription: err.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleRestore = (revision: Revision) => {\n\t\tsetRestoreTarget(revision);\n\t};\n\n\tconst revisions = data?.items ?? [];\n\tconst total = data?.total ?? 0;\n\n\treturn (\n\t\t<>\n\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t{/* Header - always visible */}\n\t\t\t\t<button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tonClick={() => setIsExpanded(!isExpanded)}\n\t\t\t\t\tclassName=\"flex w-full items-center justify-between p-4 text-left hover:bg-kumo-tint/50 transition-colors\"\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<ClockCounterClockwise className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t<span className=\"font-semibold\">Revisions</span>\n\t\t\t\t\t\t{total > 0 && <span className=\"text-xs text-kumo-subtle\">({total})</span>}\n\t\t\t\t\t</div>\n\t\t\t\t\t{isExpanded ? (\n\t\t\t\t\t\t<CaretUp className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<CaretDown className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t)}\n\t\t\t\t</button>\n\n\t\t\t\t{/* Content - shown when expanded */}\n\t\t\t\t{isExpanded && (\n\t\t\t\t\t<div className=\"border-t px-4 pb-4\">\n\t\t\t\t\t\t{isLoading ? (\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-center py-6\">\n\t\t\t\t\t\t\t\t<Loader />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : error ? (\n\t\t\t\t\t\t\t<div className=\"py-4 text-center text-sm text-kumo-danger\">\n\t\t\t\t\t\t\t\tFailed to load revisions\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : revisions.length === 0 ? (\n\t\t\t\t\t\t\t<div className=\"py-4 text-center text-sm text-kumo-subtle\">No revisions yet</div>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<div className=\"space-y-1 pt-2\">\n\t\t\t\t\t\t\t\t{revisions.map((revision, index) => (\n\t\t\t\t\t\t\t\t\t<RevisionItem\n\t\t\t\t\t\t\t\t\t\tkey={revision.id}\n\t\t\t\t\t\t\t\t\t\trevision={revision}\n\t\t\t\t\t\t\t\t\t\tcompareRevision={index > 0 ? revisions[index - 1] : undefined}\n\t\t\t\t\t\t\t\t\t\tisLatest={index === 0}\n\t\t\t\t\t\t\t\t\t\tisRestoring={\n\t\t\t\t\t\t\t\t\t\t\trestoreMutation.isPending && restoreMutation.variables === revision.id\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tonRestore={() => handleRestore(revision)}\n\t\t\t\t\t\t\t\t\t\tonSelect={() =>\n\t\t\t\t\t\t\t\t\t\t\tsetSelectedRevision(selectedRevision?.id === revision.id ? null : revision)\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tisSelected={selectedRevision?.id === revision.id}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={!!restoreTarget}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetRestoreTarget(null);\n\t\t\t\t\trestoreMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Restore Revision?\"\n\t\t\t\tdescription={\n\t\t\t\t\trestoreTarget\n\t\t\t\t\t\t? `Restore this version from ${formatFullDate(restoreTarget.createdAt)}? This will update the current content to this revision's data.`\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}\n\t\t\t\tconfirmLabel=\"Restore\"\n\t\t\t\tpendingLabel=\"Restoring...\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\tisPending={restoreMutation.isPending}\n\t\t\t\terror={restoreMutation.error}\n\t\t\t\tonConfirm={() => {\n\t\t\t\t\tif (restoreTarget) restoreMutation.mutate(restoreTarget.id);\n\t\t\t\t}}\n\t\t\t/>\n\t\t</>\n\t);\n}\n\ninterface RevisionItemProps {\n\trevision: Revision;\n\t/** The next newer revision to compare against (undefined for the latest) */\n\tcompareRevision?: Revision;\n\tisLatest: boolean;\n\tisRestoring: boolean;\n\tisSelected: boolean;\n\tonRestore: () => void;\n\tonSelect: () => void;\n}\n\nfunction RevisionItem({\n\trevision,\n\tcompareRevision,\n\tisLatest,\n\tisRestoring,\n\tisSelected,\n\tonRestore,\n\tonSelect,\n}: RevisionItemProps) {\n\treturn (\n\t\t<div\n\t\t\tclassName={`rounded-md border p-3 transition-colors ${\n\t\t\t\tisSelected ? \"border-kumo-brand bg-kumo-brand/5\" : \"hover:bg-kumo-tint/50\"\n\t\t\t}`}\n\t\t>\n\t\t\t<div className=\"flex items-start justify-between gap-2\">\n\t\t\t\t<button type=\"button\" onClick={onSelect} className=\"flex-1 text-left\">\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<span className=\"text-sm font-medium\">{formatRelativeTime(revision.createdAt)}</span>\n\t\t\t\t\t\t{isLatest && <Badge variant=\"outline\">Current</Badge>}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-xs text-kumo-subtle mt-0.5\">\n\t\t\t\t\t\t{formatFullDate(revision.createdAt)}\n\t\t\t\t\t</div>\n\t\t\t\t</button>\n\n\t\t\t\t{!isLatest && (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\tonRestore();\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tdisabled={isRestoring}\n\t\t\t\t\t\tclassName=\"shrink-0\"\n\t\t\t\t\t\ttitle=\"Restore this version\"\n\t\t\t\t\t\taria-label=\"Restore this version\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{isRestoring ? <Loader size=\"sm\" /> : <ArrowCounterClockwise className=\"h-4 w-4\" />}\n\t\t\t\t\t</Button>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Diff view or snapshot - shown when selected */}\n\t\t\t{isSelected && (\n\t\t\t\t<div className=\"mt-3 pt-3 border-t\">\n\t\t\t\t\t{compareRevision ? (\n\t\t\t\t\t\t<RevisionDiffView older={revision.data} newer={compareRevision.data} />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<div className=\"text-xs font-medium text-kumo-subtle mb-2\">Content snapshot:</div>\n\t\t\t\t\t\t\t<pre className=\"text-xs bg-kumo-tint p-2 rounded overflow-auto max-h-48\">\n\t\t\t\t\t\t\t\t{JSON.stringify(revision.data, null, 2)}\n\t\t\t\t\t\t\t</pre>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// =============================================================================\n// Diff view component\n// =============================================================================\n\ninterface RevisionDiffViewProps {\n\tolder: Record<string, unknown>;\n\tnewer: Record<string, unknown>;\n}\n\nfunction RevisionDiffView({ older, newer }: RevisionDiffViewProps) {\n\tconst [showUnchanged, setShowUnchanged] = React.useState(false);\n\tconst diffs = React.useMemo(() => computeFieldDiff(older, newer), [older, newer]);\n\n\tconst changedCount = diffs.filter((d) => d.kind !== \"unchanged\").length;\n\tconst unchangedCount = diffs.length - changedCount;\n\n\tif (diffs.length === 0) {\n\t\treturn <div className=\"text-xs text-kumo-subtle text-center py-2\">No fields to compare</div>;\n\t}\n\n\tconst visibleDiffs = showUnchanged ? diffs : diffs.filter((d) => d.kind !== \"unchanged\");\n\n\treturn (\n\t\t<div className=\"space-y-2\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"text-xs font-medium text-kumo-subtle\">\n\t\t\t\t\t{changedCount} change{changedCount === 1 ? \"\" : \"s\"} from next revision\n\t\t\t\t</div>\n\t\t\t\t{unchangedCount > 0 && (\n\t\t\t\t\t<button\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => setShowUnchanged(!showUnchanged)}\n\t\t\t\t\t\tclassName=\"text-xs text-kumo-brand hover:underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{showUnchanged ? \"Hide\" : \"Show\"} {unchangedCount} unchanged\n\t\t\t\t\t</button>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<div className=\"space-y-1.5\">\n\t\t\t\t{visibleDiffs.map((diff) => (\n\t\t\t\t\t<DiffFieldRow key={diff.field} diff={diff} />\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nconst DIFF_STYLES: Record<DiffKind, { bg: string; icon: React.ReactNode; label: string }> = {\n\tadded: {\n\t\tbg: \"bg-green-50 dark:bg-green-950/30 border-green-200 dark:border-green-800\",\n\t\ticon: <Plus className=\"h-3 w-3 text-green-600 dark:text-green-400\" aria-hidden=\"true\" />,\n\t\tlabel: \"Added\",\n\t},\n\tremoved: {\n\t\tbg: \"bg-red-50 dark:bg-red-950/30 border-red-200 dark:border-red-800\",\n\t\ticon: <Minus className=\"h-3 w-3 text-red-600 dark:text-red-400\" aria-hidden=\"true\" />,\n\t\tlabel: \"Removed\",\n\t},\n\tchanged: {\n\t\tbg: \"bg-amber-50 dark:bg-amber-950/30 border-amber-200 dark:border-amber-800\",\n\t\ticon: (\n\t\t\t<PencilSimple className=\"h-3 w-3 text-amber-600 dark:text-amber-400\" aria-hidden=\"true\" />\n\t\t),\n\t\tlabel: \"Changed\",\n\t},\n\tunchanged: {\n\t\tbg: \"bg-kumo-tint/50 border-kumo-line\",\n\t\ticon: null,\n\t\tlabel: \"Unchanged\",\n\t},\n};\n\nfunction DiffFieldRow({ diff }: { diff: FieldDiff }) {\n\tconst style = DIFF_STYLES[diff.kind];\n\n\treturn (\n\t\t<div className={`rounded border px-3 py-2 text-xs ${style.bg}`}>\n\t\t\t<div className=\"flex items-center gap-1.5 mb-1\">\n\t\t\t\t{style.icon}\n\t\t\t\t<span className=\"font-medium\">{diff.field}</span>\n\t\t\t</div>\n\n\t\t\t{diff.kind === \"changed\" && (\n\t\t\t\t<div className=\"space-y-1 mt-1.5\">\n\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t<span className=\"text-red-600 dark:text-red-400 shrink-0\">−</span>\n\t\t\t\t\t\t<pre className=\"whitespace-pre-wrap break-all font-mono\">\n\t\t\t\t\t\t\t{formatDiffValue(diff.oldValue)}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t<span className=\"text-green-600 dark:text-green-400 shrink-0\">+</span>\n\t\t\t\t\t\t<pre className=\"whitespace-pre-wrap break-all font-mono\">\n\t\t\t\t\t\t\t{formatDiffValue(diff.newValue)}\n\t\t\t\t\t\t</pre>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{diff.kind === \"added\" && (\n\t\t\t\t<pre className=\"whitespace-pre-wrap break-all font-mono mt-1\">\n\t\t\t\t\t{formatDiffValue(diff.newValue)}\n\t\t\t\t</pre>\n\t\t\t)}\n\n\t\t\t{diff.kind === \"removed\" && (\n\t\t\t\t<pre className=\"whitespace-pre-wrap break-all font-mono mt-1\">\n\t\t\t\t\t{formatDiffValue(diff.oldValue)}\n\t\t\t\t</pre>\n\t\t\t)}\n\n\t\t\t{diff.kind === \"unchanged\" && (\n\t\t\t\t<pre className=\"whitespace-pre-wrap break-all font-mono mt-1 text-kumo-subtle\">\n\t\t\t\t\t{formatDiffValue(diff.oldValue)}\n\t\t\t\t</pre>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * Save Button with inline feedback\n *\n * Shows state based on whether there are unsaved changes:\n * - \"Saved\" when clean (no unsaved changes)\n * - \"Save\" when dirty (has unsaved changes)\n * - \"Saving...\" while saving\n */\n\nimport { Button, Loader } from \"@cloudflare/kumo\";\nimport { FloppyDisk, Check } from \"@phosphor-icons/react\";\nimport type { ComponentProps } from \"react\";\nimport * as React from \"react\";\n\nimport { cn } from \"../lib/utils\";\n\nexport interface SaveButtonProps extends Omit<ComponentProps<typeof Button>, \"children\" | \"shape\"> {\n\t/** Whether there are unsaved changes */\n\tisDirty: boolean;\n\t/** Whether currently saving */\n\tisSaving: boolean;\n}\n\n/**\n * Button that reflects save state\n */\nexport function SaveButton({ isDirty, isSaving, className, disabled, ...props }: SaveButtonProps) {\n\tconst isSaved = !isDirty && !isSaving;\n\n\treturn (\n\t\t<Button\n\t\t\tclassName={cn(\"min-w-[100px] transition-all\", className)}\n\t\t\tdisabled={disabled || isSaving || isSaved}\n\t\t\tvariant={isSaved ? \"secondary\" : \"primary\"}\n\t\t\ticon={isSaving ? <Loader size=\"sm\" /> : isSaved ? <Check /> : <FloppyDisk />}\n\t\t\taria-live=\"polite\"\n\t\t\taria-busy={isSaving}\n\t\t\t{...props}\n\t\t>\n\t\t\t{isSaving ? \"Saving...\" : isSaved ? \"Saved\" : \"Save\"}\n\t\t</Button>\n\t);\n}\n\nexport default SaveButton;\n","/**\n * SEO Panel for Content Editor Sidebar\n *\n * Shows SEO metadata fields (title, description, OG image, canonical URL,\n * noIndex) when the collection has `hasSeo` enabled. Changes are sent\n * alongside content updates via the `seo` field on the update body.\n */\n\nimport { Input, InputArea, Label, Switch } from \"@cloudflare/kumo\";\nimport * as React from \"react\";\n\nimport type { ContentSeo, ContentSeoInput } from \"../lib/api\";\n\nexport interface SeoPanelProps {\n\tseo?: ContentSeo;\n\tonChange: (seo: ContentSeoInput) => void;\n}\n\n/**\n * Compact SEO metadata editor for the content sidebar.\n */\nexport function SeoPanel({ seo, onChange }: SeoPanelProps) {\n\tconst [title, setTitle] = React.useState(seo?.title ?? \"\");\n\tconst [description, setDescription] = React.useState(seo?.description ?? \"\");\n\tconst [canonical, setCanonical] = React.useState(seo?.canonical ?? \"\");\n\tconst [noIndex, setNoIndex] = React.useState(seo?.noIndex ?? false);\n\n\t// Keep local state in sync when the prop changes (e.g. after save)\n\tReact.useEffect(() => {\n\t\tsetTitle(seo?.title ?? \"\");\n\t\tsetDescription(seo?.description ?? \"\");\n\t\tsetCanonical(seo?.canonical ?? \"\");\n\t\tsetNoIndex(seo?.noIndex ?? false);\n\t}, [seo]);\n\n\tconst emitChange = (patch: Partial<ContentSeoInput>) => {\n\t\tonChange({\n\t\t\ttitle: title || null,\n\t\t\tdescription: description || null,\n\t\t\tcanonical: canonical || null,\n\t\t\tnoIndex,\n\t\t\t...patch,\n\t\t});\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-3\">\n\t\t\t<Input\n\t\t\t\tlabel=\"SEO Title\"\n\t\t\t\tdescription=\"Overrides the page title in search engine results\"\n\t\t\t\tvalue={title}\n\t\t\t\tonChange={(e) => {\n\t\t\t\t\tsetTitle(e.target.value);\n\t\t\t\t\temitChange({ title: e.target.value || null });\n\t\t\t\t}}\n\t\t\t/>\n\n\t\t\t<div>\n\t\t\t\t<InputArea\n\t\t\t\t\tlabel=\"Meta Description\"\n\t\t\t\t\tdescription={\n\t\t\t\t\t\tdescription\n\t\t\t\t\t\t\t? `${description.length}/160 characters`\n\t\t\t\t\t\t\t: \"Brief summary shown below the title in search results\"\n\t\t\t\t\t}\n\t\t\t\t\tvalue={description}\n\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\tsetDescription(e.target.value);\n\t\t\t\t\t\temitChange({ description: e.target.value || null });\n\t\t\t\t\t}}\n\t\t\t\t\trows={3}\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t<Input\n\t\t\t\tlabel=\"Canonical URL\"\n\t\t\t\tdescription=\"Points search engines to the original version of this page, if it's duplicated from another URL\"\n\t\t\t\tvalue={canonical}\n\t\t\t\tonChange={(e) => {\n\t\t\t\t\tsetCanonical(e.target.value);\n\t\t\t\t\temitChange({ canonical: e.target.value || null });\n\t\t\t\t}}\n\t\t\t/>\n\n\t\t\t<div className=\"flex items-center justify-between pt-1\">\n\t\t\t\t<div>\n\t\t\t\t\t<Label>Hide from search engines</Label>\n\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">Add noindex meta tag</p>\n\t\t\t\t</div>\n\t\t\t\t<Switch\n\t\t\t\t\tchecked={noIndex}\n\t\t\t\t\tonCheckedChange={(checked) => {\n\t\t\t\t\t\tsetNoIndex(checked);\n\t\t\t\t\t\temitChange({ noIndex: checked });\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * Taxonomy Sidebar for Content Editor\n *\n * Shows taxonomy selection UI in the content editor sidebar.\n * - Checkbox tree for hierarchical taxonomies (categories)\n * - Tag input for flat taxonomies (tags)\n */\n\nimport { Input, Label } from \"@cloudflare/kumo\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { apiFetch, parseApiResponse, throwResponseError } from \"../lib/api/client.js\";\n\ninterface TaxonomyTerm {\n\tid: string;\n\tname: string;\n\tslug: string;\n\tlabel: string;\n\tparentId?: string;\n\tchildren: TaxonomyTerm[];\n}\n\ninterface TaxonomyDef {\n\tid: string;\n\tname: string;\n\tlabel: string;\n\tlabelSingular?: string;\n\thierarchical: boolean;\n\tcollections: string[];\n}\n\ninterface TaxonomySidebarProps {\n\tcollection: string;\n\tentryId?: string;\n\tonChange?: (taxonomyName: string, termIds: string[]) => void;\n}\n\n/**\n * Fetch taxonomy definitions\n */\nasync function fetchTaxonomyDefs(): Promise<TaxonomyDef[]> {\n\tconst res = await apiFetch(`/_emdash/api/taxonomies`);\n\tconst data = await parseApiResponse<{ taxonomies: TaxonomyDef[] }>(\n\t\tres,\n\t\t\"Failed to fetch taxonomies\",\n\t);\n\treturn data.taxonomies;\n}\n\n/**\n * Fetch terms for a taxonomy\n */\nasync function fetchTerms(taxonomyName: string): Promise<TaxonomyTerm[]> {\n\tconst res = await apiFetch(`/_emdash/api/taxonomies/${taxonomyName}/terms`);\n\tconst data = await parseApiResponse<{ terms: TaxonomyTerm[] }>(res, \"Failed to fetch terms\");\n\treturn data.terms;\n}\n\n/**\n * Fetch entry terms\n */\nasync function fetchEntryTerms(\n\tcollection: string,\n\tentryId: string,\n\ttaxonomy: string,\n): Promise<TaxonomyTerm[]> {\n\tconst res = await apiFetch(`/_emdash/api/content/${collection}/${entryId}/terms/${taxonomy}`);\n\tconst data = await parseApiResponse<{ terms: TaxonomyTerm[] }>(\n\t\tres,\n\t\t\"Failed to fetch entry terms\",\n\t);\n\treturn data.terms;\n}\n\n/**\n * Set entry terms\n */\nasync function setEntryTerms(\n\tcollection: string,\n\tentryId: string,\n\ttaxonomy: string,\n\ttermIds: string[],\n): Promise<void> {\n\tconst res = await apiFetch(`/_emdash/api/content/${collection}/${entryId}/terms/${taxonomy}`, {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ termIds }),\n\t});\n\tif (!res.ok) await throwResponseError(res, \"Failed to set entry terms\");\n}\n\n/**\n * Checkbox tree for hierarchical taxonomies\n */\nfunction CategoryCheckboxTree({\n\tterm,\n\tlevel = 0,\n\tselectedIds,\n\tonToggle,\n}: {\n\tterm: TaxonomyTerm;\n\tlevel?: number;\n\tselectedIds: Set<string>;\n\tonToggle: (termId: string) => void;\n}) {\n\tconst isChecked = selectedIds.has(term.id);\n\n\treturn (\n\t\t<div>\n\t\t\t<label\n\t\t\t\tclassName=\"flex items-center py-1 cursor-pointer hover:bg-kumo-tint/50 rounded px-2\"\n\t\t\t\tstyle={{ marginLeft: `${level}rem` }}\n\t\t\t>\n\t\t\t\t<input\n\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\tchecked={isChecked}\n\t\t\t\t\tonChange={() => onToggle(term.id)}\n\t\t\t\t\tclassName=\"mr-2\"\n\t\t\t\t/>\n\t\t\t\t<span className=\"text-sm\">{term.label}</span>\n\t\t\t</label>\n\t\t\t{term.children.map((child) => (\n\t\t\t\t<CategoryCheckboxTree\n\t\t\t\t\tkey={child.id}\n\t\t\t\t\tterm={child}\n\t\t\t\t\tlevel={level + 1}\n\t\t\t\t\tselectedIds={selectedIds}\n\t\t\t\t\tonToggle={onToggle}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n\n/**\n * Tag input for flat taxonomies\n */\nfunction TagInput({\n\tterms,\n\tselectedIds,\n\tonAdd,\n\tonRemove,\n\tlabel,\n}: {\n\tterms: TaxonomyTerm[];\n\tselectedIds: Set<string>;\n\tonAdd: (termId: string) => void;\n\tonRemove: (termId: string) => void;\n\tlabel: string;\n}) {\n\tconst [input, setInput] = React.useState(\"\");\n\n\tconst selectedTerms = terms.filter((t) => selectedIds.has(t.id));\n\n\tconst suggestions = React.useMemo(() => {\n\t\tif (!input) return [];\n\t\treturn terms\n\t\t\t.filter((t) => t.label.toLowerCase().includes(input.toLowerCase()) && !selectedIds.has(t.id))\n\t\t\t.slice(0, 5);\n\t}, [input, terms, selectedIds]);\n\n\tconst handleSelect = (term: TaxonomyTerm) => {\n\t\tonAdd(term.id);\n\t\tsetInput(\"\");\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-2\">\n\t\t\t{/* Selected tags */}\n\t\t\t{selectedTerms.length > 0 && (\n\t\t\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t\t\t{selectedTerms.map((term) => (\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tkey={term.id}\n\t\t\t\t\t\t\tclassName=\"inline-flex items-center gap-1 px-2 py-1 text-sm bg-kumo-tint rounded\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{term.label}\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => onRemove(term.id)}\n\t\t\t\t\t\t\t\tclassName=\"hover:text-kumo-danger\"\n\t\t\t\t\t\t\t\taria-label={`Remove ${term.label}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"w-3 h-3\" />\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</span>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Input with autocomplete */}\n\t\t\t<div className=\"relative\">\n\t\t\t\t<Input\n\t\t\t\t\tvalue={input}\n\t\t\t\t\tonChange={(e) => setInput(e.target.value)}\n\t\t\t\t\tplaceholder=\"Add tags...\"\n\t\t\t\t\taria-label={`Add ${label}`}\n\t\t\t\t\tclassName=\"text-sm\"\n\t\t\t\t/>\n\n\t\t\t\t{/* Suggestions dropdown */}\n\t\t\t\t{suggestions.length > 0 && (\n\t\t\t\t\t<div className=\"absolute top-full left-0 right-0 mt-1 bg-kumo-overlay border rounded-md shadow-lg z-10\">\n\t\t\t\t\t\t{suggestions.map((term) => (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tkey={term.id}\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => handleSelect(term)}\n\t\t\t\t\t\t\t\tclassName=\"w-full text-left px-3 py-2 text-sm hover:bg-kumo-tint\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{term.label}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n/**\n * Single taxonomy section\n */\nfunction TaxonomySection({\n\ttaxonomy,\n\tcollection,\n\tentryId,\n\tonChange,\n}: {\n\ttaxonomy: TaxonomyDef;\n\tcollection: string;\n\tentryId?: string;\n\tonChange?: (termIds: string[]) => void;\n}) {\n\tconst queryClient = useQueryClient();\n\n\tconst { data: terms = [] } = useQuery({\n\t\tqueryKey: [\"taxonomy-terms\", taxonomy.name],\n\t\tqueryFn: () => fetchTerms(taxonomy.name),\n\t});\n\n\tconst { data: entryTerms = [] } = useQuery({\n\t\tqueryKey: [\"entry-terms\", collection, entryId, taxonomy.name],\n\t\tqueryFn: () => {\n\t\t\tif (!entryId) return [];\n\t\t\treturn fetchEntryTerms(collection, entryId, taxonomy.name);\n\t\t},\n\t\tenabled: !!entryId,\n\t});\n\n\tconst saveMutation = useMutation({\n\t\tmutationFn: (termIds: string[]) => {\n\t\t\tif (!entryId) throw new Error(\"No entry ID\");\n\t\t\treturn setEntryTerms(collection, entryId, taxonomy.name, termIds);\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"entry-terms\", collection, entryId, taxonomy.name],\n\t\t\t});\n\t\t},\n\t});\n\n\tconst [selectedIds, setSelectedIds] = React.useState<Set<string>>(new Set());\n\n\t// Sync selected IDs from entry terms\n\tReact.useEffect(() => {\n\t\tsetSelectedIds(new Set(entryTerms.map((t) => t.id)));\n\t}, [entryTerms]);\n\n\tconst handleToggle = (termId: string) => {\n\t\tconst newSelected = new Set(selectedIds);\n\t\tif (newSelected.has(termId)) {\n\t\t\tnewSelected.delete(termId);\n\t\t} else {\n\t\t\tnewSelected.add(termId);\n\t\t}\n\t\tsetSelectedIds(newSelected);\n\n\t\t// Notify parent of change\n\t\tconst termIdsArray = [...newSelected];\n\t\tonChange?.(termIdsArray);\n\n\t\t// Auto-save if entry exists\n\t\tif (entryId) {\n\t\t\tsaveMutation.mutate(termIdsArray);\n\t\t}\n\t};\n\n\tconst handleAdd = (termId: string) => {\n\t\thandleToggle(termId);\n\t};\n\n\tconst handleRemove = (termId: string) => {\n\t\thandleToggle(termId);\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-2\">\n\t\t\t<Label className=\"text-sm font-medium\">{taxonomy.label}</Label>\n\n\t\t\t{terms.length === 0 ? (\n\t\t\t\t<p className=\"text-sm text-kumo-subtle\">No {taxonomy.label.toLowerCase()} available.</p>\n\t\t\t) : taxonomy.hierarchical ? (\n\t\t\t\t<div className=\"border rounded-md p-2 max-h-64 overflow-y-auto\">\n\t\t\t\t\t{terms.map((term) => (\n\t\t\t\t\t\t<CategoryCheckboxTree\n\t\t\t\t\t\t\tkey={term.id}\n\t\t\t\t\t\t\tterm={term}\n\t\t\t\t\t\t\tselectedIds={selectedIds}\n\t\t\t\t\t\t\tonToggle={handleToggle}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<TagInput\n\t\t\t\t\tterms={terms}\n\t\t\t\t\tselectedIds={selectedIds}\n\t\t\t\t\tonAdd={handleAdd}\n\t\t\t\t\tonRemove={handleRemove}\n\t\t\t\t\tlabel={taxonomy.label}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n/**\n * Main TaxonomySidebar component\n */\nexport function TaxonomySidebar({ collection, entryId, onChange }: TaxonomySidebarProps) {\n\tconst { data: taxonomies = [] } = useQuery({\n\t\tqueryKey: [\"taxonomy-defs\"],\n\t\tqueryFn: fetchTaxonomyDefs,\n\t});\n\n\t// Filter to taxonomies that apply to this collection\n\tconst applicableTaxonomies = taxonomies.filter((t) => t.collections.includes(collection));\n\n\tif (applicableTaxonomies.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div>\n\t\t\t\t<h3 className=\"font-semibold mb-4\">Taxonomies</h3>\n\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t{applicableTaxonomies.map((taxonomy) => (\n\t\t\t\t\t\t<TaxonomySection\n\t\t\t\t\t\t\tkey={taxonomy.name}\n\t\t\t\t\t\t\ttaxonomy={taxonomy}\n\t\t\t\t\t\t\tcollection={collection}\n\t\t\t\t\t\t\tentryId={entryId}\n\t\t\t\t\t\t\tonChange={(termIds) => onChange?.(taxonomy.name, termIds)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","import {\n\tBadge,\n\tButton,\n\tDialog,\n\tInput,\n\tInputArea,\n\tLabel,\n\tLoader,\n\tSelect,\n\tSwitch,\n\tbuttonVariants,\n} from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tCheck,\n\tEye,\n\tImage as ImageIcon,\n\tMagnifyingGlass,\n\tX,\n\tTrash,\n\tArrowsInSimple,\n\tArrowsOutSimple,\n} from \"@phosphor-icons/react\";\nimport { Link } from \"@tanstack/react-router\";\nimport type { Editor } from \"@tiptap/react\";\nimport * as React from \"react\";\n\nimport type {\n\tBylineCreditInput,\n\tBylineSummary,\n\tContentItem,\n\tMediaItem,\n\tUserListItem,\n\tTranslationSummary,\n} from \"../lib/api\";\nimport { getPreviewUrl, getDraftStatus } from \"../lib/api\";\nimport { usePluginAdmins } from \"../lib/plugin-context.js\";\nimport { cn, slugify } from \"../lib/utils\";\nimport { BlockKitFieldWidget } from \"./BlockKitFieldWidget.js\";\nimport { DocumentOutline } from \"./editor/DocumentOutline\";\nimport { PluginFieldErrorBoundary } from \"./PluginFieldErrorBoundary.js\";\n\n/** Autosave debounce delay in milliseconds */\nconst AUTOSAVE_DELAY = 2000;\n\nfunction serializeEditorState(input: {\n\tdata: Record<string, unknown>;\n\tslug: string;\n\tbylines: BylineCreditInput[];\n}) {\n\treturn JSON.stringify({\n\t\tdata: input.data,\n\t\tslug: input.slug,\n\t\tbylines: input.bylines,\n\t});\n}\n\nimport type { ContentSeoInput } from \"../lib/api\";\nimport { ImageDetailPanel } from \"./editor/ImageDetailPanel\";\nimport type { ImageAttributes } from \"./editor/ImageDetailPanel\";\nimport { MediaPickerModal } from \"./MediaPickerModal\";\nimport {\n\tPortableTextEditor,\n\ttype PluginBlockDef,\n\ttype BlockSidebarPanel,\n} from \"./PortableTextEditor\";\nimport { RevisionHistory } from \"./RevisionHistory\";\nimport { SaveButton } from \"./SaveButton\";\nimport { SeoPanel } from \"./SeoPanel\";\nimport { TaxonomySidebar } from \"./TaxonomySidebar\";\n\n// Editor role level (40) from @emdash-cms/auth\nconst ROLE_EDITOR = 40;\n\nexport interface FieldDescriptor {\n\tkind: string;\n\tlabel?: string;\n\trequired?: boolean;\n\toptions?: Array<{ value: string; label: string }>;\n\twidget?: string;\n}\n\n/** Simplified user info for current user context */\nexport interface CurrentUserInfo {\n\tid: string;\n\trole: number;\n}\n\nexport interface ContentEditorProps {\n\tcollection: string;\n\tcollectionLabel: string;\n\titem?: ContentItem | null;\n\tfields: Record<string, FieldDescriptor>;\n\tisNew?: boolean;\n\tisSaving?: boolean;\n\tonSave?: (payload: {\n\t\tdata: Record<string, unknown>;\n\t\tslug?: string;\n\t\tbylines?: BylineCreditInput[];\n\t}) => void;\n\t/** Callback for autosave (debounced, skips revision creation) */\n\tonAutosave?: (payload: {\n\t\tdata: Record<string, unknown>;\n\t\tslug?: string;\n\t\tbylines?: BylineCreditInput[];\n\t}) => void;\n\t/** Whether autosave is in progress */\n\tisAutosaving?: boolean;\n\t/** Last autosave timestamp (for UI indicator) */\n\tlastAutosaveAt?: Date | null;\n\tonPublish?: () => void;\n\tonUnpublish?: () => void;\n\t/** Callback to discard draft changes (revert to published version) */\n\tonDiscardDraft?: () => void;\n\t/** Callback to schedule for future publishing */\n\tonSchedule?: (scheduledAt: string) => void;\n\t/** Callback to cancel scheduling (revert to draft) */\n\tonUnschedule?: () => void;\n\t/** Whether scheduling is in progress */\n\tisScheduling?: boolean;\n\t/** Whether this collection supports drafts */\n\tsupportsDrafts?: boolean;\n\t/** Whether this collection supports revisions */\n\tsupportsRevisions?: boolean;\n\t/** Current user (for permission checks) */\n\tcurrentUser?: CurrentUserInfo;\n\t/** Available users for author selection (only shown to editors+) */\n\tusers?: UserListItem[];\n\t/** Callback when author is changed */\n\tonAuthorChange?: (authorId: string | null) => void;\n\t/** Available byline profiles */\n\tavailableBylines?: BylineSummary[];\n\t/** Selected byline credits (controlled for new entries) */\n\tselectedBylines?: BylineCreditInput[];\n\t/** Callback when byline credits are changed */\n\tonBylinesChange?: (bylines: BylineCreditInput[]) => void;\n\t/** Callback for creating a byline inline from the editor */\n\tonQuickCreateByline?: (input: { slug: string; displayName: string }) => Promise<BylineSummary>;\n\t/** Callback for updating a byline inline from the editor */\n\tonQuickEditByline?: (\n\t\tbylineId: string,\n\t\tinput: { slug: string; displayName: string },\n\t) => Promise<BylineSummary>;\n\t/** Callback when item is deleted (moved to trash) */\n\tonDelete?: () => void;\n\t/** Whether delete is in progress */\n\tisDeleting?: boolean;\n\t/** i18n config — present when multiple locales are configured */\n\ti18n?: { defaultLocale: string; locales: string[] };\n\t/** Existing translations for this content item */\n\ttranslations?: TranslationSummary[];\n\t/** Callback to create a translation for a locale */\n\tonTranslate?: (locale: string) => void;\n\t/** Plugin block types available for insertion in Portable Text fields */\n\tpluginBlocks?: PluginBlockDef[];\n\t/** Whether this collection has SEO fields enabled */\n\thasSeo?: boolean;\n\t/** Callback when SEO fields change */\n\tonSeoChange?: (seo: ContentSeoInput) => void;\n\t/** Admin manifest for resolving plugin field widgets */\n\tmanifest?: import(\"../lib/api/client.js\").AdminManifest | null;\n}\n\n/** Format scheduled date for display */\nfunction formatScheduledDate(dateStr: string | null) {\n\tif (!dateStr) return null;\n\tconst date = new Date(dateStr);\n\treturn date.toLocaleString();\n}\n\n/**\n * Content editor with dynamic field rendering\n */\nexport function ContentEditor({\n\tcollection,\n\tcollectionLabel,\n\titem,\n\tfields,\n\tisNew,\n\tisSaving,\n\tonSave,\n\tonAutosave,\n\tisAutosaving,\n\tlastAutosaveAt,\n\tonPublish,\n\tonUnpublish,\n\tonDiscardDraft,\n\tonSchedule,\n\tonUnschedule,\n\tisScheduling,\n\tsupportsDrafts = false,\n\tsupportsRevisions = false,\n\tcurrentUser,\n\tusers,\n\tonAuthorChange,\n\tavailableBylines,\n\tselectedBylines,\n\tonBylinesChange,\n\tonQuickCreateByline,\n\tonQuickEditByline,\n\tonDelete,\n\tisDeleting,\n\ti18n,\n\ttranslations,\n\tonTranslate,\n\tpluginBlocks,\n\thasSeo = false,\n\tonSeoChange,\n\tmanifest,\n}: ContentEditorProps) {\n\tconst [formData, setFormData] = React.useState<Record<string, unknown>>(item?.data || {});\n\tconst [slug, setSlug] = React.useState(item?.slug || \"\");\n\tconst [slugTouched, setSlugTouched] = React.useState(!!item?.slug);\n\tconst [status, setStatus] = React.useState(item?.status || \"draft\");\n\tconst [internalBylines, setInternalBylines] = React.useState<BylineCreditInput[]>(\n\t\titem?.bylines?.map((entry) => ({ bylineId: entry.byline.id, roleLabel: entry.roleLabel })) ??\n\t\t\t[],\n\t);\n\n\t// Track portableText editor for document outline\n\tconst [portableTextEditor, setPortableTextEditor] = React.useState<Editor | null>(null);\n\n\t// Block sidebar state – when a block (e.g. image) requests sidebar space, this holds\n\t// the panel data. When non-null the sidebar shows the block panel instead of the\n\t// default content settings sections.\n\tconst [blockSidebarPanel, setBlockSidebarPanel] = React.useState<BlockSidebarPanel | null>(null);\n\n\tconst handleBlockSidebarOpen = React.useCallback((panel: BlockSidebarPanel) => {\n\t\tsetBlockSidebarPanel(panel);\n\t}, []);\n\n\tconst handleBlockSidebarClose = React.useCallback(() => {\n\t\tsetBlockSidebarPanel((prev) => {\n\t\t\tprev?.onClose();\n\t\t\treturn null;\n\t\t});\n\t}, []);\n\n\t// Track the last saved state to determine if dirty\n\tconst [lastSavedData, setLastSavedData] = React.useState<string>(\n\t\tserializeEditorState({\n\t\t\tdata: item?.data || {},\n\t\t\tslug: item?.slug || \"\",\n\t\t\tbylines:\n\t\t\t\titem?.bylines?.map((entry) => ({\n\t\t\t\t\tbylineId: entry.byline.id,\n\t\t\t\t\troleLabel: entry.roleLabel,\n\t\t\t\t})) ?? [],\n\t\t}),\n\t);\n\n\t// Update form and last saved state when item changes (e.g., after save or restore)\n\t// Stringify the data for comparison since objects are compared by reference\n\tconst itemDataString = React.useMemo(() => (item ? JSON.stringify(item.data) : \"\"), [item?.data]);\n\tReact.useEffect(() => {\n\t\tif (item) {\n\t\t\tsetFormData(item.data);\n\t\t\tsetSlug(item.slug || \"\");\n\t\t\tsetSlugTouched(!!item.slug);\n\t\t\tsetStatus(item.status);\n\t\t\tsetInternalBylines(\n\t\t\t\titem.bylines?.map((entry) => ({ bylineId: entry.byline.id, roleLabel: entry.roleLabel })) ??\n\t\t\t\t\t[],\n\t\t\t);\n\t\t\tsetLastSavedData(\n\t\t\t\tserializeEditorState({\n\t\t\t\t\tdata: item.data,\n\t\t\t\t\tslug: item.slug || \"\",\n\t\t\t\t\tbylines:\n\t\t\t\t\t\titem.bylines?.map((entry) => ({\n\t\t\t\t\t\t\tbylineId: entry.byline.id,\n\t\t\t\t\t\t\troleLabel: entry.roleLabel,\n\t\t\t\t\t\t})) ?? [],\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\t}, [item?.updatedAt, itemDataString, item?.slug, item?.status]);\n\n\tconst activeBylines = isNew ? (selectedBylines ?? []) : internalBylines;\n\n\tconst handleBylinesChange = React.useCallback(\n\t\t(next: BylineCreditInput[]) => {\n\t\t\tif (isNew) {\n\t\t\t\tonBylinesChange?.(next);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tsetInternalBylines(next);\n\t\t\tonBylinesChange?.(next);\n\t\t},\n\t\t[isNew, onBylinesChange],\n\t);\n\n\t// Check if form has unsaved changes\n\tconst currentData = React.useMemo(\n\t\t() =>\n\t\t\tserializeEditorState({\n\t\t\t\tdata: formData,\n\t\t\t\tslug,\n\t\t\t\tbylines: activeBylines,\n\t\t\t}),\n\t\t[formData, slug, activeBylines],\n\t);\n\tconst isDirty = isNew || currentData !== lastSavedData;\n\n\t// Autosave with debounce\n\t// Track pending autosave to cancel on manual save\n\tconst autosaveTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | null>(null);\n\tconst formDataRef = React.useRef(formData);\n\tformDataRef.current = formData;\n\tconst slugRef = React.useRef(slug);\n\tslugRef.current = slug;\n\n\tReact.useEffect(() => {\n\t\t// Don't autosave for new items (no ID yet) or if autosave isn't configured\n\t\tif (isNew || !onAutosave || !item?.id) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Don't autosave if not dirty or already saving\n\t\tif (!isDirty || isSaving || isAutosaving) {\n\t\t\treturn;\n\t\t}\n\n\t\t// Clear any pending autosave\n\t\tif (autosaveTimeoutRef.current) {\n\t\t\tclearTimeout(autosaveTimeoutRef.current);\n\t\t}\n\n\t\t// Schedule autosave\n\t\tautosaveTimeoutRef.current = setTimeout(() => {\n\t\t\tonAutosave({\n\t\t\t\tdata: formDataRef.current,\n\t\t\t\tslug: slugRef.current || undefined,\n\t\t\t\tbylines: activeBylines,\n\t\t\t});\n\t\t}, AUTOSAVE_DELAY);\n\n\t\treturn () => {\n\t\t\tif (autosaveTimeoutRef.current) {\n\t\t\t\tclearTimeout(autosaveTimeoutRef.current);\n\t\t\t}\n\t\t};\n\t}, [currentData, isNew, onAutosave, item?.id, isDirty, isSaving, isAutosaving, activeBylines]);\n\n\t// Cancel pending autosave on manual save\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\t// Cancel pending autosave\n\t\tif (autosaveTimeoutRef.current) {\n\t\t\tclearTimeout(autosaveTimeoutRef.current);\n\t\t\tautosaveTimeoutRef.current = null;\n\t\t}\n\t\tonSave?.({\n\t\t\tdata: formData,\n\t\t\tslug: slug || undefined,\n\t\t\tbylines: activeBylines,\n\t\t});\n\t};\n\n\t// Preview URL state\n\tconst [isLoadingPreview, setIsLoadingPreview] = React.useState(false);\n\n\tconst handlePreview = async () => {\n\t\tif (!item?.id) return;\n\n\t\tsetIsLoadingPreview(true);\n\t\ttry {\n\t\t\tconst result = await getPreviewUrl(collection, item.id);\n\t\t\tif (result?.url) {\n\t\t\t\t// Open preview in new tab\n\t\t\t\twindow.open(result.url, \"_blank\", \"noopener,noreferrer\");\n\t\t\t} else {\n\t\t\t\t// Fallback to direct URL if preview not configured\n\t\t\t\twindow.open(`/${collection}/${slug || item.id}`, \"_blank\", \"noopener,noreferrer\");\n\t\t\t}\n\t\t} catch {\n\t\t\t// Fallback to direct URL on error\n\t\t\twindow.open(`/${collection}/${slug || item?.id}`, \"_blank\", \"noopener,noreferrer\");\n\t\t} finally {\n\t\t\tsetIsLoadingPreview(false);\n\t\t}\n\t};\n\n\tconst handleFieldChange = React.useCallback(\n\t\t(name: string, value: unknown) => {\n\t\t\tsetFormData((prev) => ({ ...prev, [name]: value }));\n\t\t\tif (name === \"title\" && !slugTouched && typeof value === \"string\" && value) {\n\t\t\t\tsetSlug(slugify(value));\n\t\t\t}\n\t\t},\n\t\t[slugTouched],\n\t);\n\n\tconst handleSlugChange = (value: string) => {\n\t\tsetSlug(value);\n\t\tsetSlugTouched(true);\n\t};\n\n\tconst isPublished = status === \"published\";\n\n\t// Draft revision status (only meaningful when supportsDrafts is on)\n\tconst draftStatus = item ? getDraftStatus(item) : \"unpublished\";\n\tconst hasPendingChanges = draftStatus === \"published_with_changes\";\n\tconst isLive = draftStatus === \"published\" || draftStatus === \"published_with_changes\";\n\n\t// Scheduling — keyed off scheduledAt rather than status, since published\n\t// posts can now have a pending schedule without changing status.\n\tconst hasSchedule = Boolean(item?.scheduledAt);\n\tconst canSchedule =\n\t\t!isNew && !hasSchedule && Boolean(onSchedule) && (!isPublished || hasPendingChanges);\n\n\t// Schedule datetime state\n\tconst [scheduleDate, setScheduleDate] = React.useState<string>(\"\");\n\tconst [showScheduler, setShowScheduler] = React.useState(false);\n\n\t// Distraction-free mode state\n\tconst [isDistractionFree, setIsDistractionFree] = React.useState(false);\n\n\t// Escape exits distraction-free mode\n\tReact.useEffect(() => {\n\t\tif (!isDistractionFree) return;\n\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\") {\n\t\t\t\te.preventDefault();\n\t\t\t\te.stopPropagation();\n\t\t\t\tsetIsDistractionFree(false);\n\t\t\t}\n\t\t};\n\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown, { capture: true });\n\t\treturn () => document.removeEventListener(\"keydown\", handleKeyDown, { capture: true });\n\t}, [isDistractionFree]);\n\n\tconst handleScheduleSubmit = () => {\n\t\tif (scheduleDate && onSchedule) {\n\t\t\t// Convert local datetime to ISO string\n\t\t\tconst date = new Date(scheduleDate);\n\t\t\tonSchedule(date.toISOString());\n\t\t\tsetShowScheduler(false);\n\t\t\tsetScheduleDate(\"\");\n\t\t}\n\t};\n\n\treturn (\n\t\t<form\n\t\t\tonSubmit={handleSubmit}\n\t\t\tclassName={cn(\n\t\t\t\t\"space-y-6 transition-all duration-300\",\n\t\t\t\tisDistractionFree && \"fixed inset-0 z-50 bg-kumo-base p-8 overflow-auto\",\n\t\t\t)}\n\t\t>\n\t\t\t{/* Header - show on hover in distraction-free mode */}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex flex-wrap items-center justify-between gap-y-2\",\n\t\t\t\t\tisDistractionFree &&\n\t\t\t\t\t\t\"opacity-0 hover:opacity-100 transition-opacity duration-200 fixed top-0 left-0 right-0 bg-kumo-base/95 backdrop-blur p-4 z-10\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<div className=\"flex items-center space-x-4\">\n\t\t\t\t\t{!isDistractionFree && (\n\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\tto=\"/content/$collection\"\n\t\t\t\t\t\t\tparams={{ collection }}\n\t\t\t\t\t\t\tsearch={{ locale: undefined }}\n\t\t\t\t\t\t\taria-label={`Back to ${collectionLabel} list`}\n\t\t\t\t\t\t\tclassName={buttonVariants({ variant: \"ghost\", shape: \"square\" })}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowLeft className=\"h-5 w-5\" aria-hidden=\"true\" />\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t)}\n\t\t\t\t\t{isDistractionFree && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tonClick={() => setIsDistractionFree(false)}\n\t\t\t\t\t\t\taria-label=\"Exit distraction-free mode\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowsInSimple className=\"h-5 w-5\" aria-hidden=\"true\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">\n\t\t\t\t\t\t{isNew ? `New ${collectionLabel}` : `Edit ${collectionLabel}`}\n\t\t\t\t\t</h1>\n\t\t\t\t\t{i18n && item?.locale && (\n\t\t\t\t\t\t<Badge variant=\"outline\" className=\"uppercase text-xs\">\n\t\t\t\t\t\t\t{item.locale}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex items-center space-x-2\">\n\t\t\t\t\t{/* Autosave indicator */}\n\t\t\t\t\t{!isNew && onAutosave && (\n\t\t\t\t\t\t<div className=\"flex items-center text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t{isAutosaving ? (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<Loader size=\"sm\" />\n\t\t\t\t\t\t\t\t\t<span className=\"ml-1\">Saving...</span>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t) : lastAutosaveAt ? (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<Check className=\"mr-1 h-3 w-3 text-green-600\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t<span>Saved</span>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{!isDistractionFree && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={() => setIsDistractionFree(true)}\n\t\t\t\t\t\t\taria-label=\"Enter distraction-free mode\"\n\t\t\t\t\t\t\ttitle=\"Distraction-free mode (⌘⇧\\)\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowsOutSimple className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t{!isNew && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tonClick={handlePreview}\n\t\t\t\t\t\t\tdisabled={isLoadingPreview}\n\t\t\t\t\t\t\ticon={isLoadingPreview ? <Loader size=\"sm\" /> : <Eye />}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{hasPendingChanges ? \"Preview draft\" : \"Preview\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t<SaveButton type=\"submit\" isDirty={isDirty} isSaving={isSaving || false} />\n\t\t\t\t\t{!isNew && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t{supportsDrafts && hasPendingChanges && onDiscardDraft && (\n\t\t\t\t\t\t\t\t<Dialog.Root disablePointerDismissal>\n\t\t\t\t\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t\t<Button {...p} type=\"button\" variant=\"outline\" size=\"sm\" icon={<X />}>\n\t\t\t\t\t\t\t\t\t\t\t\tDiscard changes\n\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\tDiscard draft changes?\n\t\t\t\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t\t\t\t<Dialog.Description className=\"text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\tThis will revert to the published version. Your draft changes will be lost.\n\t\t\t\t\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t\t\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"secondary\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"destructive\" onClick={onDiscardDraft}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tDiscard changes\n\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</Dialog>\n\t\t\t\t\t\t\t\t</Dialog.Root>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{isLive ? (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t{hasPendingChanges ? (\n\t\t\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"primary\" onClick={onPublish}>\n\t\t\t\t\t\t\t\t\t\t\tPublish changes\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={onUnpublish}>\n\t\t\t\t\t\t\t\t\t\t\tUnpublish\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"secondary\" onClick={onPublish}>\n\t\t\t\t\t\t\t\t\tPublish\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Main content area */}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"grid gap-6 lg:grid-cols-3\",\n\t\t\t\t\tisDistractionFree && \"lg:grid-cols-1 max-w-4xl mx-auto pt-16\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{/* Editor fields */}\n\t\t\t\t<div className=\"space-y-6 lg:col-span-2\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"rounded-lg border bg-kumo-base p-6\",\n\t\t\t\t\t\t\tisDistractionFree && \"border-0 bg-transparent p-0\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t\t{Object.entries(fields).map(([name, field]) => (\n\t\t\t\t\t\t\t\t<FieldRenderer\n\t\t\t\t\t\t\t\t\tkey={name}\n\t\t\t\t\t\t\t\t\tname={name}\n\t\t\t\t\t\t\t\t\tfield={field}\n\t\t\t\t\t\t\t\t\tvalue={formData[name]}\n\t\t\t\t\t\t\t\t\tonChange={handleFieldChange}\n\t\t\t\t\t\t\t\t\tonEditorReady={field.kind === \"portableText\" ? setPortableTextEditor : undefined}\n\t\t\t\t\t\t\t\t\tminimal={isDistractionFree}\n\t\t\t\t\t\t\t\t\tpluginBlocks={pluginBlocks}\n\t\t\t\t\t\t\t\t\tonBlockSidebarOpen={\n\t\t\t\t\t\t\t\t\t\tfield.kind === \"portableText\" ? handleBlockSidebarOpen : undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tonBlockSidebarClose={\n\t\t\t\t\t\t\t\t\t\tfield.kind === \"portableText\" ? handleBlockSidebarClose : undefined\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tmanifest={manifest}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Sidebar - hidden in distraction-free mode */}\n\t\t\t\t<div className={cn(\"space-y-6\", isDistractionFree && \"hidden\")}>\n\t\t\t\t\t{blockSidebarPanel ? (\n\t\t\t\t\t\t/* Block sidebar panel – replaces default sections when a block requests it */\n\t\t\t\t\t\tblockSidebarPanel.type === \"image\" ? (\n\t\t\t\t\t\t\t<ImageDetailPanel\n\t\t\t\t\t\t\t\tattributes={blockSidebarPanel.attrs as unknown as ImageAttributes}\n\t\t\t\t\t\t\t\tonUpdate={(attrs) =>\n\t\t\t\t\t\t\t\t\tblockSidebarPanel.onUpdate(attrs as unknown as Record<string, unknown>)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tonReplace={(attrs) =>\n\t\t\t\t\t\t\t\t\tblockSidebarPanel.onReplace(attrs as unknown as Record<string, unknown>)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tonDelete={() => {\n\t\t\t\t\t\t\t\t\tblockSidebarPanel.onDelete();\n\t\t\t\t\t\t\t\t\tsetBlockSidebarPanel(null);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tonClose={handleBlockSidebarClose}\n\t\t\t\t\t\t\t\tinline\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t) : null\n\t\t\t\t\t) : (\n\t\t\t\t\t\t/* Default content settings sections – single card with dividers */\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base flex flex-col\">\n\t\t\t\t\t\t\t{/* Publish settings */}\n\t\t\t\t\t\t\t<div className=\"p-4\">\n\t\t\t\t\t\t\t\t<h3 className=\"mb-4 font-semibold\">Publish</h3>\n\t\t\t\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\t\t\t\t\tvalue={slug}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => handleSlugChange(e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"my-post-slug\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<Label>Status</Label>\n\t\t\t\t\t\t\t\t\t\t<div className=\"mt-1 flex flex-wrap items-center gap-1.5\">\n\t\t\t\t\t\t\t\t\t\t\t{supportsDrafts ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{isLive && <Badge variant=\"primary\">Published</Badge>}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{hasPendingChanges && <Badge variant=\"secondary\">Pending changes</Badge>}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{!isLive && !hasSchedule && <Badge variant=\"secondary\">Draft</Badge>}\n\t\t\t\t\t\t\t\t\t\t\t\t\t{hasSchedule && <Badge variant=\"outline\">Scheduled</Badge>}\n\t\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t{status.charAt(0).toUpperCase() + status.slice(1)}\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t{item?.scheduledAt && (\n\t\t\t\t\t\t\t\t\t\t\t<div className=\"mt-2 flex items-center justify-between gap-2 rounded-md border px-3 py-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tScheduled for: {formatScheduledDate(item.scheduledAt)}\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onUnschedule}>\n\t\t\t\t\t\t\t\t\t\t\t\t\tUnschedule\n\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t\t{canSchedule && (\n\t\t\t\t\t\t\t\t\t\t<div className=\"pt-2\">\n\t\t\t\t\t\t\t\t\t\t\t{showScheduler ? (\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tlabel=\"Schedule for\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"datetime-local\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tvalue={scheduleDate}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => setScheduleDate(e.target.value)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tmin={new Date().toISOString().slice(0, 16)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={handleScheduleSubmit}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={!scheduleDate || isScheduling}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ticon={isScheduling ? <Loader size=\"sm\" /> : undefined}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSchedule\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetShowScheduler(false);\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsetScheduleDate(\"\");\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => setShowScheduler(true)}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\tSchedule for later\n\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t\t\t{item && (\n\t\t\t\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t<p>Created: {new Date(item.createdAt).toLocaleString()}</p>\n\t\t\t\t\t\t\t\t\t\t\t<p>Updated: {new Date(item.updatedAt).toLocaleString()}</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{!isNew && onDelete && (\n\t\t\t\t\t\t\t\t\t\t<div className=\"pt-4 border-t\">\n\t\t\t\t\t\t\t\t\t\t\t<Dialog.Root disablePointerDismissal>\n\t\t\t\t\t\t\t\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{...p}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"w-full text-kumo-danger hover:text-kumo-danger\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tdisabled={isDeleting}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ticon={isDeleting ? <Loader size=\"sm\" /> : <Trash />}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMove to Trash\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tMove to Trash?\n\t\t\t\t\t\t\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<Dialog.Description className=\"text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tThis will move the item to trash. You can restore it later from the\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttrash.\n\t\t\t\t\t\t\t\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"secondary\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"destructive\" onClick={onDelete}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tMove to Trash\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</Dialog>\n\t\t\t\t\t\t\t\t\t\t\t</Dialog.Root>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Ownership selector - shown only to editors and above */}\n\t\t\t\t\t\t\t{currentUser && currentUser.role >= ROLE_EDITOR && users && users.length > 0 && (\n\t\t\t\t\t\t\t\t<div className=\"p-4 border-t\">\n\t\t\t\t\t\t\t\t\t<h3 className=\"mb-4 font-semibold\">Ownership</h3>\n\t\t\t\t\t\t\t\t\t<AuthorSelector\n\t\t\t\t\t\t\t\t\t\tauthorId={item?.authorId || null}\n\t\t\t\t\t\t\t\t\t\tusers={users}\n\t\t\t\t\t\t\t\t\t\tonChange={onAuthorChange}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Byline credits */}\n\t\t\t\t\t\t\t{currentUser && currentUser.role >= ROLE_EDITOR && (\n\t\t\t\t\t\t\t\t<div className=\"p-4 border-t\">\n\t\t\t\t\t\t\t\t\t<h3 className=\"mb-4 font-semibold\">Bylines</h3>\n\t\t\t\t\t\t\t\t\t<BylineCreditsEditor\n\t\t\t\t\t\t\t\t\t\tcredits={activeBylines}\n\t\t\t\t\t\t\t\t\t\tbylines={availableBylines ?? []}\n\t\t\t\t\t\t\t\t\t\tonChange={handleBylinesChange}\n\t\t\t\t\t\t\t\t\t\tonQuickCreate={onQuickCreateByline}\n\t\t\t\t\t\t\t\t\t\tonQuickEdit={onQuickEditByline}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Translations sidebar - shown when i18n is enabled */}\n\t\t\t\t\t\t\t{i18n && item && !isNew && (\n\t\t\t\t\t\t\t\t<div className=\"p-4 border-t\">\n\t\t\t\t\t\t\t\t\t<h3 className=\"mb-4 font-semibold\">Translations</h3>\n\t\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t\t{i18n.locales.map((locale) => {\n\t\t\t\t\t\t\t\t\t\t\tconst translation = translations?.find((t) => t.locale === locale);\n\t\t\t\t\t\t\t\t\t\t\tconst isCurrent = locale === item.locale;\n\t\t\t\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={locale}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\"flex items-center justify-between rounded-md px-3 py-2 text-sm\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tisCurrent\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-kumo-brand/10 font-medium\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: translation\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"hover:bg-kumo-tint/50\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"text-kumo-subtle\",\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-xs font-semibold uppercase\">{locale}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{locale === i18n.defaultLocale && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-[10px] text-kumo-subtle\">(default)</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{isCurrent && (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-[10px] text-kumo-brand\">current</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t{translation && !isCurrent ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tto=\"/content/$collection/$id\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tparams={{ collection, id: translation.id }}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-xs text-kumo-brand hover:underline\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tEdit\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t\t\t\t\t\t) : !translation && onTranslate ? (\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"h-auto px-2 py-1 text-xs\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => onTranslate(locale)}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tTranslate\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Taxonomy selector */}\n\t\t\t\t\t\t\t{item && (\n\t\t\t\t\t\t\t\t<div className=\"p-4 border-t\">\n\t\t\t\t\t\t\t\t\t<TaxonomySidebar collection={collection} entryId={item.id} />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* SEO panel - shown for collections with hasSeo enabled */}\n\t\t\t\t\t\t\t{hasSeo && !isNew && onSeoChange && (\n\t\t\t\t\t\t\t\t<div className=\"p-4 border-t\">\n\t\t\t\t\t\t\t\t\t<h3 className=\"mb-4 font-semibold flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<MagnifyingGlass className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\tSEO\n\t\t\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t\t\t<SeoPanel seo={item?.seo} onChange={onSeoChange} />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Document outline - shown when editing content with portableText */}\n\t\t\t\t\t\t\t{portableTextEditor && (\n\t\t\t\t\t\t\t\t<div className=\"p-4 border-t\">\n\t\t\t\t\t\t\t\t\t<DocumentOutline editor={portableTextEditor} />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Revision history - shown for existing items in collections that support it */}\n\t\t\t\t\t\t\t{!isNew && item && supportsRevisions && (\n\t\t\t\t\t\t\t\t<div className=\"p-4 border-t\">\n\t\t\t\t\t\t\t\t\t<RevisionHistory collection={collection} entryId={item.id} />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n\ninterface FieldRendererProps {\n\tname: string;\n\tfield: FieldDescriptor;\n\tvalue: unknown;\n\tonChange: (name: string, value: unknown) => void;\n\t/** Callback when a portableText editor is ready */\n\tonEditorReady?: (editor: Editor) => void;\n\t/** Minimal chrome - hides toolbar, fades labels, removes borders (distraction-free mode) */\n\tminimal?: boolean;\n\t/** Plugin block types available for insertion in Portable Text fields */\n\tpluginBlocks?: PluginBlockDef[];\n\t/** Callback when a block node requests sidebar space */\n\tonBlockSidebarOpen?: (panel: BlockSidebarPanel) => void;\n\t/** Callback when a block node closes its sidebar */\n\tonBlockSidebarClose?: () => void;\n\t/** Admin manifest for resolving sandboxed field widget elements */\n\tmanifest?: import(\"../lib/api/client.js\").AdminManifest | null;\n}\n\n/**\n * Render field based on type\n */\nfunction FieldRenderer({\n\tname,\n\tfield,\n\tvalue,\n\tonChange,\n\tonEditorReady,\n\tminimal,\n\tpluginBlocks,\n\tonBlockSidebarOpen,\n\tonBlockSidebarClose,\n\tmanifest,\n}: FieldRendererProps) {\n\tconst pluginAdmins = usePluginAdmins();\n\tconst label = field.label || name.charAt(0).toUpperCase() + name.slice(1);\n\tconst id = `field-${name}`;\n\tconst labelClass = minimal ? \"text-kumo-subtle/50 text-xs font-normal\" : undefined;\n\n\tconst handleChange = React.useCallback((v: unknown) => onChange(name, v), [onChange, name]);\n\n\t// Check for plugin field widget override\n\tif (field.widget) {\n\t\tconst sepIdx = field.widget.indexOf(\":\");\n\t\tif (sepIdx <= 0) {\n\t\t\tconsole.warn(\n\t\t\t\t`[emdash] Field \"${name}\" has widget \"${field.widget}\" but it should use the format \"pluginId:widgetName\". Falling back to default editor.`,\n\t\t\t);\n\t\t}\n\t\tif (sepIdx > 0) {\n\t\t\tconst pluginId = field.widget.slice(0, sepIdx);\n\t\t\tconst widgetName = field.widget.slice(sepIdx + 1);\n\t\t\t// Trusted plugin: React component\n\t\t\tconst PluginField = pluginAdmins[pluginId]?.fields?.[widgetName] as\n\t\t\t\t| React.ComponentType<{\n\t\t\t\t\t\tvalue: unknown;\n\t\t\t\t\t\tonChange: (value: unknown) => void;\n\t\t\t\t\t\tlabel: string;\n\t\t\t\t\t\tid: string;\n\t\t\t\t\t\trequired?: boolean;\n\t\t\t\t\t\toptions?: Array<{ value: string; label: string }>;\n\t\t\t\t\t\tminimal?: boolean;\n\t\t\t\t }>\n\t\t\t\t| undefined;\n\t\t\tif (typeof PluginField === \"function\") {\n\t\t\t\treturn (\n\t\t\t\t\t<PluginFieldErrorBoundary fieldKind={field.kind}>\n\t\t\t\t\t\t<PluginField\n\t\t\t\t\t\t\tvalue={value}\n\t\t\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\t\t\tlabel={label}\n\t\t\t\t\t\t\tid={id}\n\t\t\t\t\t\t\trequired={field.required}\n\t\t\t\t\t\t\toptions={field.options}\n\t\t\t\t\t\t\tminimal={minimal}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</PluginFieldErrorBoundary>\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Sandboxed plugin: Block Kit elements from manifest\n\t\t\tif (manifest) {\n\t\t\t\tconst pluginManifest = manifest.plugins[pluginId];\n\t\t\t\tconst widgetDef = pluginManifest?.fieldWidgets?.find((w) => w.name === widgetName);\n\t\t\t\tif (widgetDef?.elements && widgetDef.elements.length > 0) {\n\t\t\t\t\treturn (\n\t\t\t\t\t\t<PluginFieldErrorBoundary fieldKind={field.kind}>\n\t\t\t\t\t\t\t<BlockKitFieldWidget\n\t\t\t\t\t\t\t\tlabel={label}\n\t\t\t\t\t\t\t\telements={widgetDef.elements}\n\t\t\t\t\t\t\t\tvalue={value}\n\t\t\t\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</PluginFieldErrorBoundary>\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Widget declared but plugin not found/active -- fall through to default\n\t\t}\n\t}\n\n\tswitch (field.kind) {\n\t\tcase \"string\":\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={<span className={labelClass}>{label}</span>}\n\t\t\t\t\tid={id}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => handleChange(e.target.value)}\n\t\t\t\t\trequired={field.required}\n\t\t\t\t\tclassName={\n\t\t\t\t\t\tminimal\n\t\t\t\t\t\t\t? \"border-0 bg-transparent px-0 text-lg font-medium focus-visible:ring-0 focus-visible:ring-offset-0\"\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t);\n\n\t\tcase \"number\":\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={<span className={labelClass}>{label}</span>}\n\t\t\t\t\tid={id}\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tvalue={typeof value === \"number\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => handleChange(Number(e.target.value))}\n\t\t\t\t\trequired={field.required}\n\t\t\t\t/>\n\t\t\t);\n\n\t\tcase \"boolean\":\n\t\t\treturn (\n\t\t\t\t<Switch\n\t\t\t\t\tlabel={label}\n\t\t\t\t\tchecked={typeof value === \"boolean\" ? value : false}\n\t\t\t\t\tonCheckedChange={handleChange}\n\t\t\t\t/>\n\t\t\t);\n\n\t\tcase \"portableText\": {\n\t\t\tconst labelId = `${id}-label`;\n\t\t\treturn (\n\t\t\t\t<div>\n\t\t\t\t\t{!minimal && (\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tid={labelId}\n\t\t\t\t\t\t\tclassName={cn(\"text-sm font-medium leading-none text-kumo-default\", labelClass)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t)}\n\t\t\t\t\t<PortableTextEditor\n\t\t\t\t\t\tvalue={Array.isArray(value) ? value : []}\n\t\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\t\tplaceholder={`Enter ${label.toLowerCase()}...`}\n\t\t\t\t\t\taria-labelledby={labelId}\n\t\t\t\t\t\tpluginBlocks={pluginBlocks}\n\t\t\t\t\t\tonEditorReady={onEditorReady}\n\t\t\t\t\t\tminimal={minimal}\n\t\t\t\t\t\tonBlockSidebarOpen={onBlockSidebarOpen}\n\t\t\t\t\t\tonBlockSidebarClose={onBlockSidebarClose}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t);\n\t\t}\n\n\t\tcase \"richText\":\n\t\t\t// For richText (markdown), use InputArea\n\t\t\treturn (\n\t\t\t\t<InputArea\n\t\t\t\t\tlabel={label}\n\t\t\t\t\tid={id}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => handleChange(e.target.value)}\n\t\t\t\t\trows={10}\n\t\t\t\t\tplaceholder=\"Enter markdown content...\"\n\t\t\t\t/>\n\t\t\t);\n\n\t\tcase \"select\": {\n\t\t\tconst selectItems: Record<string, string> = {};\n\t\t\tfor (const opt of field.options ?? []) {\n\t\t\t\tselectItems[opt.value] = opt.label;\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t<Select\n\t\t\t\t\tlabel={label}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonValueChange={(v) => handleChange(v ?? \"\")}\n\t\t\t\t\titems={selectItems}\n\t\t\t\t>\n\t\t\t\t\t{field.options?.map((opt) => (\n\t\t\t\t\t\t<Select.Option key={opt.value} value={opt.value}>\n\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t))}\n\t\t\t\t</Select>\n\t\t\t);\n\t\t}\n\n\t\tcase \"datetime\":\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={label}\n\t\t\t\t\tid={id}\n\t\t\t\t\ttype=\"datetime-local\"\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => handleChange(e.target.value)}\n\t\t\t\t\trequired={field.required}\n\t\t\t\t/>\n\t\t\t);\n\n\t\tcase \"image\": {\n\t\t\t// value is either an ImageFieldValue object, a legacy string URL, or undefined\n\t\t\tconst imageValue =\n\t\t\t\tvalue != null && typeof value === \"object\" ? (value as ImageFieldValue) : undefined;\n\t\t\treturn (\n\t\t\t\t<ImageFieldRenderer\n\t\t\t\t\tlabel={label}\n\t\t\t\t\tvalue={imageValue}\n\t\t\t\t\tonChange={handleChange}\n\t\t\t\t\trequired={field.required}\n\t\t\t\t/>\n\t\t\t);\n\t\t}\n\n\t\tdefault:\n\t\t\t// Default to text input\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={label}\n\t\t\t\t\tid={id}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => handleChange(e.target.value)}\n\t\t\t\t\trequired={field.required}\n\t\t\t\t/>\n\t\t\t);\n\t}\n}\n\n/**\n * Image field value - matches emdash's MediaValue type\n */\ninterface ImageFieldValue {\n\tid: string;\n\t/** Provider ID (e.g., \"local\", \"cloudflare-images\") */\n\tprovider?: string;\n\t/** Direct URL for local media or legacy data */\n\tsrc?: string;\n\t/** Preview URL for admin display (separate from src used for rendering) */\n\tpreviewUrl?: string;\n\talt?: string;\n\twidth?: number;\n\theight?: number;\n\t/** Provider-specific metadata */\n\tmeta?: Record<string, unknown>;\n}\n\n/**\n * Image field with media picker\n *\n * Stores full image metadata including dimensions for responsive images.\n * Handles backwards compatibility with legacy string URLs.\n */\ninterface ImageFieldRendererProps {\n\tlabel: string;\n\tvalue: ImageFieldValue | string | undefined;\n\tonChange: (value: ImageFieldValue | undefined) => void;\n\trequired?: boolean;\n}\n\nfunction ImageFieldRenderer({ label, value, onChange, required }: ImageFieldRendererProps) {\n\tconst [pickerOpen, setPickerOpen] = React.useState(false);\n\t// Normalize value to get display URL (handles both object and legacy string)\n\t// Prefer previewUrl for admin display, fall back to src, then derive from storageKey/id\n\tconst displayUrl =\n\t\ttypeof value === \"string\"\n\t\t\t? value\n\t\t\t: value?.previewUrl ||\n\t\t\t\tvalue?.src ||\n\t\t\t\t(value && (!value.provider || value.provider === \"local\")\n\t\t\t\t\t? `/_emdash/api/media/file/${typeof value.meta?.storageKey === \"string\" ? value.meta.storageKey : value.id}`\n\t\t\t\t\t: undefined);\n\n\tconst handleSelect = (item: MediaItem) => {\n\t\tconst isLocalProvider = !item.provider || item.provider === \"local\";\n\n\t\tonChange({\n\t\t\tid: item.id,\n\t\t\tprovider: item.provider || \"local\",\n\t\t\t// Local media derives URLs from meta.storageKey at display time — no src needed\n\t\t\t// External providers cache a preview URL for admin display\n\t\t\tpreviewUrl: isLocalProvider ? undefined : item.url,\n\t\t\talt: item.alt || \"\",\n\t\t\twidth: item.width,\n\t\t\theight: item.height,\n\t\t\tmeta: isLocalProvider ? { ...item.meta, storageKey: item.storageKey } : item.meta,\n\t\t});\n\t};\n\n\tconst handleRemove = () => {\n\t\tonChange(undefined);\n\t};\n\n\treturn (\n\t\t<div>\n\t\t\t<Label>{label}</Label>\n\t\t\t{displayUrl ? (\n\t\t\t\t<div className=\"mt-2 relative group\">\n\t\t\t\t\t<img src={displayUrl} alt=\"\" className=\"max-h-48 rounded-lg border object-cover\" />\n\t\t\t\t\t<div className=\"absolute top-2 right-2 opacity-0 group-hover:opacity-100 transition-opacity flex gap-1\">\n\t\t\t\t\t\t<Button type=\"button\" size=\"sm\" variant=\"secondary\" onClick={() => setPickerOpen(true)}>\n\t\t\t\t\t\t\tChange\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\tclassName=\"h-8 w-8\"\n\t\t\t\t\t\t\tonClick={handleRemove}\n\t\t\t\t\t\t\taria-label=\"Remove image\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<Button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\tclassName=\"mt-2 w-full h-32 border-dashed\"\n\t\t\t\t\tonClick={() => setPickerOpen(true)}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex flex-col items-center gap-2 text-kumo-subtle\">\n\t\t\t\t\t\t<ImageIcon className=\"h-8 w-8\" />\n\t\t\t\t\t\t<span>Select image</span>\n\t\t\t\t\t</div>\n\t\t\t\t</Button>\n\t\t\t)}\n\t\t\t<MediaPickerModal\n\t\t\t\topen={pickerOpen}\n\t\t\t\tonOpenChange={setPickerOpen}\n\t\t\t\tonSelect={handleSelect}\n\t\t\t\tmimeTypeFilter=\"image/\"\n\t\t\t\ttitle={`Select ${label}`}\n\t\t\t/>\n\t\t\t{required && !displayUrl && (\n\t\t\t\t<p className=\"text-sm text-kumo-danger mt-1\">This field is required</p>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n/**\n * Author selector component for editors and above\n */\ninterface AuthorSelectorProps {\n\tauthorId: string | null;\n\tusers: UserListItem[];\n\tonChange?: (authorId: string | null) => void;\n}\n\ninterface BylineCreditsEditorProps {\n\tcredits: BylineCreditInput[];\n\tbylines: BylineSummary[];\n\tonChange: (bylines: BylineCreditInput[]) => void;\n\tonQuickCreate?: (input: { slug: string; displayName: string }) => Promise<BylineSummary>;\n\tonQuickEdit?: (\n\t\tbylineId: string,\n\t\tinput: { slug: string; displayName: string },\n\t) => Promise<BylineSummary>;\n}\n\nfunction BylineCreditsEditor({\n\tcredits,\n\tbylines,\n\tonChange,\n\tonQuickCreate,\n\tonQuickEdit,\n}: BylineCreditsEditorProps) {\n\tconst [selectedBylineId, setSelectedBylineId] = React.useState(\"\");\n\tconst [quickName, setQuickName] = React.useState(\"\");\n\tconst [quickSlug, setQuickSlug] = React.useState(\"\");\n\tconst [quickError, setQuickError] = React.useState<string | null>(null);\n\tconst [isCreating, setIsCreating] = React.useState(false);\n\tconst [editBylineId, setEditBylineId] = React.useState<string | null>(null);\n\tconst [editName, setEditName] = React.useState(\"\");\n\tconst [editSlug, setEditSlug] = React.useState(\"\");\n\tconst [editError, setEditError] = React.useState<string | null>(null);\n\tconst [isEditing, setIsEditing] = React.useState(false);\n\n\tconst bylineMap = React.useMemo(() => new Map(bylines.map((b) => [b.id, b])), [bylines]);\n\n\tconst availableToAdd = bylines.filter((b) => !credits.some((c) => c.bylineId === b.id));\n\n\tconst move = (index: number, direction: -1 | 1) => {\n\t\tconst target = index + direction;\n\t\tif (target < 0 || target >= credits.length) return;\n\t\tconst next = [...credits];\n\t\tconst [moved] = next.splice(index, 1);\n\t\tif (!moved) return;\n\t\tnext.splice(target, 0, moved);\n\t\tonChange(next);\n\t};\n\n\tconst resetQuickCreate = () => {\n\t\tsetQuickName(\"\");\n\t\tsetQuickSlug(\"\");\n\t\tsetQuickError(null);\n\t};\n\n\tconst openEditByline = (byline: BylineSummary) => {\n\t\tsetEditBylineId(byline.id);\n\t\tsetEditName(byline.displayName);\n\t\tsetEditSlug(byline.slug);\n\t\tsetEditError(null);\n\t};\n\n\tconst resetQuickEdit = () => {\n\t\tsetEditBylineId(null);\n\t\tsetEditName(\"\");\n\t\tsetEditSlug(\"\");\n\t\tsetEditError(null);\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-3\">\n\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t<select\n\t\t\t\t\tvalue={selectedBylineId}\n\t\t\t\t\tonChange={(e) => setSelectedBylineId(e.target.value)}\n\t\t\t\t\tclassName=\"w-full rounded border bg-kumo-base px-3 py-2 text-sm\"\n\t\t\t\t>\n\t\t\t\t\t<option value=\"\">Select byline...</option>\n\t\t\t\t\t{availableToAdd.map((b) => (\n\t\t\t\t\t\t<option key={b.id} value={b.id}>\n\t\t\t\t\t\t\t{b.displayName}\n\t\t\t\t\t\t</option>\n\t\t\t\t\t))}\n\t\t\t\t</select>\n\t\t\t\t<Button\n\t\t\t\t\ttype=\"button\"\n\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\tif (!selectedBylineId) return;\n\t\t\t\t\t\tonChange([...credits, { bylineId: selectedBylineId, roleLabel: null }]);\n\t\t\t\t\t\tsetSelectedBylineId(\"\");\n\t\t\t\t\t}}\n\t\t\t\t\tdisabled={!selectedBylineId}\n\t\t\t\t>\n\t\t\t\t\tAdd\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t{credits.length > 0 ? (\n\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t{credits.map((credit, index) => {\n\t\t\t\t\t\tconst byline = bylineMap.get(credit.bylineId);\n\t\t\t\t\t\tif (!byline) return null;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div key={`${credit.bylineId}-${index}`} className=\"rounded border p-2 space-y-2\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center justify-between gap-2\">\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">{byline.displayName}</p>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">{byline.slug}</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex gap-1\">\n\t\t\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => move(index, -1)}>\n\t\t\t\t\t\t\t\t\t\t\tUp\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"ghost\" size=\"sm\" onClick={() => move(index, 1)}>\n\t\t\t\t\t\t\t\t\t\t\tDown\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t{onQuickEdit && (\n\t\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => openEditByline(byline)}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tEdit\n\t\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onChange(credits.filter((_, i) => i !== index))}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tRemove\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Role label\"\n\t\t\t\t\t\t\t\t\tvalue={credit.roleLabel ?? \"\"}\n\t\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\t\tconst next = [...credits];\n\t\t\t\t\t\t\t\t\t\tconst current = next[index];\n\t\t\t\t\t\t\t\t\t\tif (!current) return;\n\t\t\t\t\t\t\t\t\t\tnext[index] = {\n\t\t\t\t\t\t\t\t\t\t\t...current,\n\t\t\t\t\t\t\t\t\t\t\troleLabel: e.target.value || null,\n\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\tonChange(next);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<p className=\"text-sm text-kumo-subtle\">No bylines selected.</p>\n\t\t\t)}\n\n\t\t\t{onQuickCreate && (\n\t\t\t\t<Dialog.Root>\n\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t<Button {...p} type=\"button\" variant=\"secondary\">\n\t\t\t\t\t\t\t\tQuick create byline\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">Create byline</Dialog.Title>\n\t\t\t\t\t\t<div className=\"mt-4 space-y-3\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Display name\"\n\t\t\t\t\t\t\t\tvalue={quickName}\n\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\tsetQuickName(e.target.value);\n\t\t\t\t\t\t\t\t\tif (!quickSlug) setQuickSlug(slugify(e.target.value));\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\t\t\tvalue={quickSlug}\n\t\t\t\t\t\t\t\tonChange={(e) => setQuickSlug(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t{quickError && <p className=\"text-sm text-kumo-danger\">{quickError}</p>}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"secondary\" onClick={resetQuickCreate}>\n\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tdisabled={!quickName || !quickSlug || isCreating}\n\t\t\t\t\t\t\t\tonClick={async () => {\n\t\t\t\t\t\t\t\t\tsetQuickError(null);\n\t\t\t\t\t\t\t\t\tsetIsCreating(true);\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tconst created = await onQuickCreate({\n\t\t\t\t\t\t\t\t\t\t\tdisplayName: quickName,\n\t\t\t\t\t\t\t\t\t\t\tslug: quickSlug,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\tonChange([...credits, { bylineId: created.id, roleLabel: null }]);\n\t\t\t\t\t\t\t\t\t\tresetQuickCreate();\n\t\t\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\t\t\tsetQuickError(err instanceof Error ? err.message : \"Failed to create byline\");\n\t\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\t\tsetIsCreating(false);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isCreating ? \"Creating...\" : \"Create\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Dialog>\n\t\t\t\t</Dialog.Root>\n\t\t\t)}\n\n\t\t\t{onQuickEdit && editBylineId && (\n\t\t\t\t<Dialog.Root open onOpenChange={(open) => (!open ? resetQuickEdit() : undefined)}>\n\t\t\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">Edit byline</Dialog.Title>\n\t\t\t\t\t\t<div className=\"mt-4 space-y-3\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Display name\"\n\t\t\t\t\t\t\t\tvalue={editName}\n\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\tsetEditName(e.target.value);\n\t\t\t\t\t\t\t\t\tif (!editSlug) setEditSlug(slugify(e.target.value));\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<Input label=\"Slug\" value={editSlug} onChange={(e) => setEditSlug(e.target.value)} />\n\t\t\t\t\t\t\t{editError && <p className=\"text-sm text-kumo-danger\">{editError}</p>}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t\t\t<Button type=\"button\" variant=\"secondary\" onClick={resetQuickEdit}>\n\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tdisabled={!editName || !editSlug || isEditing}\n\t\t\t\t\t\t\t\tonClick={async () => {\n\t\t\t\t\t\t\t\t\tsetEditError(null);\n\t\t\t\t\t\t\t\t\tsetIsEditing(true);\n\t\t\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\t\t\tawait onQuickEdit(editBylineId, {\n\t\t\t\t\t\t\t\t\t\t\tdisplayName: editName,\n\t\t\t\t\t\t\t\t\t\t\tslug: editSlug,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t\tresetQuickEdit();\n\t\t\t\t\t\t\t\t\t} catch (err) {\n\t\t\t\t\t\t\t\t\t\tsetEditError(err instanceof Error ? err.message : \"Failed to update byline\");\n\t\t\t\t\t\t\t\t\t} finally {\n\t\t\t\t\t\t\t\t\t\tsetIsEditing(false);\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isEditing ? \"Saving...\" : \"Save\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Dialog>\n\t\t\t\t</Dialog.Root>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction AuthorSelector({ authorId, users, onChange }: AuthorSelectorProps) {\n\tconst currentAuthor = users.find((u) => u.id === authorId);\n\n\tconst authorItems: Record<string, string> = { unassigned: \"Unassigned\" };\n\tfor (const user of users) {\n\t\tauthorItems[user.id] = user.name || user.email;\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-2\">\n\t\t\t<Select\n\t\t\t\tvalue={authorId || \"unassigned\"}\n\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\tonChange?.(value === \"unassigned\" || value === null ? null : value)\n\t\t\t\t}\n\t\t\t\titems={authorItems}\n\t\t\t>\n\t\t\t\t<Select.Option value=\"unassigned\">\n\t\t\t\t\t<span className=\"text-kumo-subtle\">Unassigned</span>\n\t\t\t\t</Select.Option>\n\t\t\t\t{users.map((user) => (\n\t\t\t\t\t<Select.Option key={user.id} value={user.id}>\n\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t{user.name || user.email}\n\t\t\t\t\t\t\t{user.name && <span className=\"text-xs text-kumo-subtle\">({user.email})</span>}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</Select.Option>\n\t\t\t\t))}\n\t\t\t</Select>\n\t\t\t{currentAuthor && <p className=\"text-xs text-kumo-subtle\">{currentAuthor.email}</p>}\n\t\t</div>\n\t);\n}\n","/**\n * Locale switcher component for i18n-enabled sites.\n *\n * Used in both the content list (to filter by locale) and the content editor\n * (to switch between locale versions of a content item).\n *\n * Only renders when i18n is configured (manifest.i18n is present).\n */\n\nimport { GlobeSimple } from \"@phosphor-icons/react\";\nimport React from \"react\";\n\nimport { cn } from \"../lib/utils.js\";\n\ninterface LocaleSwitcherProps {\n\tlocales: string[];\n\tdefaultLocale: string;\n\tvalue: string;\n\tonChange: (locale: string) => void;\n\t/** Show \"All locales\" option (for list filtering) */\n\tshowAll?: boolean;\n\tclassName?: string;\n\t/** Size variant */\n\tsize?: \"sm\" | \"md\";\n}\n\n/**\n * Get a display label for a locale code.\n * Uses Intl.DisplayNames when available, falls back to uppercase code.\n */\nfunction getLocaleLabel(code: string): string {\n\ttry {\n\t\tconst names = new Intl.DisplayNames([\"en\"], { type: \"language\" });\n\t\treturn names.of(code) ?? code.toUpperCase();\n\t} catch {\n\t\treturn code.toUpperCase();\n\t}\n}\n\nexport function LocaleSwitcher({\n\tlocales,\n\tdefaultLocale,\n\tvalue,\n\tonChange,\n\tshowAll = false,\n\tclassName,\n\tsize = \"md\",\n}: LocaleSwitcherProps) {\n\treturn (\n\t\t<div className={cn(\"flex items-center gap-1.5\", className)}>\n\t\t\t<GlobeSimple\n\t\t\t\tclassName={cn(\"text-kumo-subtle shrink-0\", size === \"sm\" ? \"size-3.5\" : \"size-4\")}\n\t\t\t\tweight=\"bold\"\n\t\t\t/>\n\t\t\t<select\n\t\t\t\tvalue={value}\n\t\t\t\tonChange={(e) => onChange(e.target.value)}\n\t\t\t\taria-label=\"Locale\"\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"rounded-md border bg-transparent font-medium transition-colors\",\n\t\t\t\t\t\"focus:ring-kumo-ring focus:outline-none focus:ring-2 focus:ring-offset-1\",\n\t\t\t\t\t\"hover:bg-kumo-tint/50 cursor-pointer\",\n\t\t\t\t\tsize === \"sm\" ? \"px-1.5 py-0.5 text-xs\" : \"px-2 py-1 text-sm\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{showAll && <option value=\"\">All locales</option>}\n\t\t\t\t{locales.map((locale) => (\n\t\t\t\t\t<option key={locale} value={locale}>\n\t\t\t\t\t\t{locale.toUpperCase()}\n\t\t\t\t\t\t{locale === defaultLocale ? \" (default)\" : \"\"}\n\t\t\t\t\t</option>\n\t\t\t\t))}\n\t\t\t</select>\n\t\t</div>\n\t);\n}\n\n/**\n * Compact locale badges showing which translations exist for a content item.\n * Renders as a row of small locale codes, with existing translations highlighted.\n */\nexport function LocaleBadges({\n\tlocales,\n\texistingLocales,\n\tonLocaleClick,\n}: {\n\tlocales: string[];\n\texistingLocales: string[];\n\tonLocaleClick?: (locale: string) => void;\n}) {\n\tconst existingSet = new Set(existingLocales);\n\n\treturn (\n\t\t<div className=\"flex items-center gap-0.5\">\n\t\t\t{locales.map((locale) => {\n\t\t\t\tconst exists = existingSet.has(locale);\n\t\t\t\treturn (\n\t\t\t\t\t<button\n\t\t\t\t\t\tkey={locale}\n\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\tonClick={() => onLocaleClick?.(locale)}\n\t\t\t\t\t\tdisabled={!onLocaleClick}\n\t\t\t\t\t\ttitle={\n\t\t\t\t\t\t\texists\n\t\t\t\t\t\t\t\t? `${getLocaleLabel(locale)} \\u2014 view translation`\n\t\t\t\t\t\t\t\t: `${getLocaleLabel(locale)} \\u2014 no translation`\n\t\t\t\t\t\t}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"rounded px-1 py-0.5 text-[10px] font-semibold uppercase leading-none transition-colors\",\n\t\t\t\t\t\t\texists\n\t\t\t\t\t\t\t\t? \"bg-kumo-brand/10 text-kumo-brand hover:bg-kumo-brand/20\"\n\t\t\t\t\t\t\t\t: \"bg-kumo-tint text-kumo-subtle/50\",\n\t\t\t\t\t\t\tonLocaleClick && exists && \"cursor-pointer\",\n\t\t\t\t\t\t\t(!onLocaleClick || !exists) && \"cursor-default\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{locale}\n\t\t\t\t\t</button>\n\t\t\t\t);\n\t\t\t})}\n\t\t</div>\n\t);\n}\n\n/**\n * Hook to get i18n config from the manifest query.\n * Returns null if i18n is not configured.\n */\nexport function useI18nConfig(\n\tmanifest: { i18n?: { defaultLocale: string; locales: string[] } } | undefined,\n) {\n\treturn React.useMemo(() => {\n\t\tif (!manifest?.i18n) return null;\n\t\treturn manifest.i18n;\n\t}, [manifest?.i18n]);\n}\n","import { Badge, Button, buttonVariants, Dialog, Input, Tabs } from \"@cloudflare/kumo\";\nimport {\n\tPlus,\n\tPencil,\n\tTrash,\n\tArrowCounterClockwise,\n\tCopy,\n\tMagnifyingGlass,\n\tCaretLeft,\n\tCaretRight,\n} from \"@phosphor-icons/react\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport type { ContentItem, TrashedContentItem } from \"../lib/api\";\nimport { cn } from \"../lib/utils\";\nimport { LocaleSwitcher } from \"./LocaleSwitcher\";\n\nexport interface ContentListProps {\n\tcollection: string;\n\tcollectionLabel: string;\n\titems: ContentItem[];\n\ttrashedItems?: TrashedContentItem[];\n\tisLoading?: boolean;\n\tisTrashedLoading?: boolean;\n\tonDelete?: (id: string) => void;\n\tonDuplicate?: (id: string) => void;\n\tonRestore?: (id: string) => void;\n\tonPermanentDelete?: (id: string) => void;\n\tonLoadMore?: () => void;\n\tonLoadMoreTrashed?: () => void;\n\thasMore?: boolean;\n\thasMoreTrashed?: boolean;\n\ttrashedCount?: number;\n\t/** i18n config — present when multiple locales are configured */\n\ti18n?: { defaultLocale: string; locales: string[] };\n\t/** Currently active locale filter */\n\tactiveLocale?: string;\n\t/** Callback when locale filter changes */\n\tonLocaleChange?: (locale: string) => void;\n}\n\ntype ViewTab = \"all\" | \"trash\";\n\nconst PAGE_SIZE = 20;\n\nfunction getItemTitle(item: { data: Record<string, unknown>; slug: string | null; id: string }) {\n\tconst rawTitle = item.data.title;\n\tconst rawName = item.data.name;\n\treturn (\n\t\t(typeof rawTitle === \"string\" ? rawTitle : \"\") ||\n\t\t(typeof rawName === \"string\" ? rawName : \"\") ||\n\t\titem.slug ||\n\t\titem.id\n\t);\n}\n\n/**\n * Content list view with table display and trash tab\n */\nexport function ContentList({\n\tcollection,\n\tcollectionLabel,\n\titems,\n\ttrashedItems = [],\n\tisLoading,\n\tisTrashedLoading,\n\tonDelete,\n\tonDuplicate,\n\tonRestore,\n\tonPermanentDelete,\n\tonLoadMore,\n\tonLoadMoreTrashed,\n\thasMore,\n\thasMoreTrashed,\n\ttrashedCount = 0,\n\ti18n,\n\tactiveLocale,\n\tonLocaleChange,\n}: ContentListProps) {\n\tconst [activeTab, setActiveTab] = React.useState<ViewTab>(\"all\");\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst [page, setPage] = React.useState(0);\n\n\t// Reset page when search changes\n\tconst handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tsetSearchQuery(e.target.value);\n\t\tsetPage(0);\n\t};\n\n\tconst filteredItems = React.useMemo(() => {\n\t\tif (!searchQuery) return items;\n\t\tconst query = searchQuery.toLowerCase();\n\t\treturn items.filter((item) => getItemTitle(item).toLowerCase().includes(query));\n\t}, [items, searchQuery]);\n\n\tconst totalPages = Math.max(1, Math.ceil(filteredItems.length / PAGE_SIZE));\n\tconst paginatedItems = filteredItems.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE);\n\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">{collectionLabel}</h1>\n\t\t\t\t\t{i18n && activeLocale && onLocaleChange && (\n\t\t\t\t\t\t<LocaleSwitcher\n\t\t\t\t\t\t\tlocales={i18n.locales}\n\t\t\t\t\t\t\tdefaultLocale={i18n.defaultLocale}\n\t\t\t\t\t\t\tvalue={activeLocale}\n\t\t\t\t\t\t\tonChange={onLocaleChange}\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t\t<Link to=\"/content/$collection/new\" params={{ collection }} className={buttonVariants()}>\n\t\t\t\t\t<Plus className=\"mr-2 h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\tAdd New\n\t\t\t\t</Link>\n\t\t\t</div>\n\n\t\t\t{/* Search */}\n\t\t\t{items.length > 0 && (\n\t\t\t\t<div className=\"relative max-w-sm\">\n\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tplaceholder={`Search ${collectionLabel.toLowerCase()}...`}\n\t\t\t\t\t\taria-label={`Search ${collectionLabel.toLowerCase()}`}\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={handleSearchChange}\n\t\t\t\t\t\tclassName=\"pl-9\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Tabs */}\n\t\t\t<Tabs\n\t\t\t\tvariant=\"underline\"\n\t\t\t\tvalue={activeTab}\n\t\t\t\tonValueChange={(v) => {\n\t\t\t\t\tif (v === \"all\" || v === \"trash\") setActiveTab(v);\n\t\t\t\t}}\n\t\t\t\ttabs={[\n\t\t\t\t\t{ value: \"all\", label: \"All\" },\n\t\t\t\t\t{\n\t\t\t\t\t\tvalue: \"trash\",\n\t\t\t\t\t\tlabel: (\n\t\t\t\t\t\t\t<span className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\tTrash\n\t\t\t\t\t\t\t\t{trashedCount > 0 && <Badge variant=\"secondary\">{trashedCount}</Badge>}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t),\n\t\t\t\t\t},\n\t\t\t\t]}\n\t\t\t/>\n\n\t\t\t{/* Content based on active tab */}\n\t\t\t{activeTab === \"all\" ? (\n\t\t\t\t<>\n\t\t\t\t\t{/* Table */}\n\t\t\t\t\t<div className=\"rounded-md border overflow-x-auto\">\n\t\t\t\t\t\t<table className=\"w-full\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr className=\"border-b bg-kumo-tint/50\">\n\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\tTitle\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\tStatus\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t{i18n && (\n\t\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\t\tLocale\n\t\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\tDate\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-right text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\tActions\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{items.length === 0 && !isLoading ? (\n\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t<td colSpan={i18n ? 5 : 4} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\tNo {collectionLabel.toLowerCase()} yet.{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\t\t\t\t\tto=\"/content/$collection/new\"\n\t\t\t\t\t\t\t\t\t\t\t\tparams={{ collection }}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-kumo-brand underline\"\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tCreate your first one\n\t\t\t\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t) : paginatedItems.length === 0 ? (\n\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t<td colSpan={i18n ? 5 : 4} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\tNo results for “{searchQuery}”\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\tpaginatedItems.map((item) => (\n\t\t\t\t\t\t\t\t\t\t<ContentListItem\n\t\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\t\t\tcollection={collection}\n\t\t\t\t\t\t\t\t\t\t\tonDelete={onDelete}\n\t\t\t\t\t\t\t\t\t\t\tonDuplicate={onDuplicate}\n\t\t\t\t\t\t\t\t\t\t\tshowLocale={!!i18n}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Pagination */}\n\t\t\t\t\t{totalPages > 1 && (\n\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t<span className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t{filteredItems.length} {filteredItems.length === 1 ? \"item\" : \"items\"}\n\t\t\t\t\t\t\t\t{searchQuery && ` matching \"${searchQuery}\"`}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\tdisabled={page === 0}\n\t\t\t\t\t\t\t\t\tonClick={() => setPage(page - 1)}\n\t\t\t\t\t\t\t\t\taria-label=\"Previous page\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<CaretLeft className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<span className=\"text-sm\">\n\t\t\t\t\t\t\t\t\t{page + 1} / {totalPages}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\tdisabled={page >= totalPages - 1}\n\t\t\t\t\t\t\t\t\tonClick={() => setPage(page + 1)}\n\t\t\t\t\t\t\t\t\taria-label=\"Next page\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<CaretRight className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Load more */}\n\t\t\t\t\t{hasMore && (\n\t\t\t\t\t\t<div className=\"flex justify-center\">\n\t\t\t\t\t\t\t<Button variant=\"outline\" onClick={onLoadMore} disabled={isLoading}>\n\t\t\t\t\t\t\t\t{isLoading ? \"Loading...\" : \"Load More\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<>\n\t\t\t\t\t{/* Trash Table */}\n\t\t\t\t\t<div className=\"rounded-md border overflow-x-auto\">\n\t\t\t\t\t\t<table className=\"w-full\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr className=\"border-b bg-kumo-tint/50\">\n\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\tTitle\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\tDeleted\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-right text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\tActions\n\t\t\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t{trashedItems.length === 0 && !isTrashedLoading ? (\n\t\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t\t<td colSpan={3} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\tTrash is empty\n\t\t\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\ttrashedItems.map((item) => (\n\t\t\t\t\t\t\t\t\t\t<TrashedListItem\n\t\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\t\t\tonRestore={onRestore}\n\t\t\t\t\t\t\t\t\t\t\tonPermanentDelete={onPermanentDelete}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</tbody>\n\t\t\t\t\t\t</table>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Load more trashed */}\n\t\t\t\t\t{hasMoreTrashed && (\n\t\t\t\t\t\t<div className=\"flex justify-center\">\n\t\t\t\t\t\t\t<Button variant=\"outline\" onClick={onLoadMoreTrashed} disabled={isTrashedLoading}>\n\t\t\t\t\t\t\t\t{isTrashedLoading ? \"Loading...\" : \"Load More\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\ninterface ContentListItemProps {\n\titem: ContentItem;\n\tcollection: string;\n\tonDelete?: (id: string) => void;\n\tonDuplicate?: (id: string) => void;\n\tshowLocale?: boolean;\n}\n\nfunction ContentListItem({\n\titem,\n\tcollection,\n\tonDelete,\n\tonDuplicate,\n\tshowLocale,\n}: ContentListItemProps) {\n\tconst title = getItemTitle(item);\n\tconst date = new Date(item.updatedAt || item.createdAt);\n\n\treturn (\n\t\t<tr className=\"border-b hover:bg-kumo-tint/25\">\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<Link\n\t\t\t\t\tto=\"/content/$collection/$id\"\n\t\t\t\t\tparams={{ collection, id: item.id }}\n\t\t\t\t\tclassName=\"font-medium hover:text-kumo-brand\"\n\t\t\t\t>\n\t\t\t\t\t{title}\n\t\t\t\t</Link>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<StatusBadge\n\t\t\t\t\tstatus={item.status}\n\t\t\t\t\thasPendingChanges={!!item.draftRevisionId && item.draftRevisionId !== item.liveRevisionId}\n\t\t\t\t/>\n\t\t\t</td>\n\t\t\t{showLocale && (\n\t\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t\t<span className=\"bg-kumo-tint rounded px-1.5 py-0.5 text-xs font-semibold uppercase\">\n\t\t\t\t\t\t{item.locale}\n\t\t\t\t\t</span>\n\t\t\t\t</td>\n\t\t\t)}\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle\">{date.toLocaleDateString()}</td>\n\t\t\t<td className=\"px-4 py-3 text-right\">\n\t\t\t\t<div className=\"flex items-center justify-end space-x-1\">\n\t\t\t\t\t<Link\n\t\t\t\t\t\tto=\"/content/$collection/$id\"\n\t\t\t\t\t\tparams={{ collection, id: item.id }}\n\t\t\t\t\t\taria-label={`Edit ${title}`}\n\t\t\t\t\t\tclassName={buttonVariants({ variant: \"ghost\", shape: \"square\" })}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Pencil className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t</Link>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\taria-label={`Duplicate ${title}`}\n\t\t\t\t\t\tonClick={() => onDuplicate?.(item.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Copy className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Dialog.Root disablePointerDismissal>\n\t\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t<Button {...p} variant=\"ghost\" shape=\"square\" aria-label={`Move ${title} to trash`}>\n\t\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-danger\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">Move to Trash?</Dialog.Title>\n\t\t\t\t\t\t\t<Dialog.Description className=\"text-kumo-subtle\">\n\t\t\t\t\t\t\t\tMove \"{title}\" to trash? You can restore it later.\n\t\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"secondary\">\n\t\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"destructive\" onClick={() => onDelete?.(item.id)}>\n\t\t\t\t\t\t\t\t\t\t\tMove to Trash\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</Dialog>\n\t\t\t\t\t</Dialog.Root>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\ninterface TrashedListItemProps {\n\titem: TrashedContentItem;\n\tonRestore?: (id: string) => void;\n\tonPermanentDelete?: (id: string) => void;\n}\n\nfunction TrashedListItem({ item, onRestore, onPermanentDelete }: TrashedListItemProps) {\n\tconst title = getItemTitle(item);\n\tconst deletedDate = new Date(item.deletedAt);\n\n\treturn (\n\t\t<tr className=\"border-b hover:bg-kumo-tint/25\">\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<span className=\"font-medium text-kumo-subtle\">{title}</span>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle\">{deletedDate.toLocaleDateString()}</td>\n\t\t\t<td className=\"px-4 py-3 text-right\">\n\t\t\t\t<div className=\"flex items-center justify-end space-x-1\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\taria-label={`Restore ${title}`}\n\t\t\t\t\t\tonClick={() => onRestore?.(item.id)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ArrowCounterClockwise className=\"h-4 w-4 text-kumo-brand\" aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Dialog.Root disablePointerDismissal>\n\t\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t{...p}\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\taria-label={`Permanently delete ${title}`}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-danger\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">Delete Permanently?</Dialog.Title>\n\t\t\t\t\t\t\t<Dialog.Description className=\"text-kumo-subtle\">\n\t\t\t\t\t\t\t\tPermanently delete \"{title}\"? This cannot be undone.\n\t\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t<Button {...p} variant=\"secondary\">\n\t\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t{...p}\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onPermanentDelete?.(item.id)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tDelete Permanently\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</Dialog>\n\t\t\t\t\t</Dialog.Root>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\nfunction StatusBadge({\n\tstatus,\n\thasPendingChanges,\n}: {\n\tstatus: string;\n\thasPendingChanges?: boolean;\n}) {\n\treturn (\n\t\t<span className=\"inline-flex items-center gap-1.5\">\n\t\t\t<span\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"inline-flex items-center rounded-full px-2 py-1 text-xs font-medium\",\n\t\t\t\t\tstatus === \"published\" &&\n\t\t\t\t\t\t\"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200\",\n\t\t\t\t\tstatus === \"draft\" &&\n\t\t\t\t\t\t\"bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200\",\n\t\t\t\t\tstatus === \"scheduled\" &&\n\t\t\t\t\t\t\"bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200\",\n\t\t\t\t\tstatus === \"archived\" && \"bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{status}\n\t\t\t</span>\n\t\t\t{hasPendingChanges && <Badge variant=\"secondary\">pending</Badge>}\n\t\t</span>\n\t);\n}\n","import { Button, Dialog, Input, InputArea } from \"@cloudflare/kumo\";\nimport {\n\tTextT,\n\tTextAlignLeft,\n\tHash,\n\tToggleLeft,\n\tCalendar,\n\tList,\n\tListChecks,\n\tFileText,\n\tImage as ImageIcon,\n\tFile,\n\tLinkSimple,\n\tBracketsCurly,\n\tLink,\n} from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport type { FieldType, CreateFieldInput, SchemaField } from \"../lib/api\";\nimport { cn } from \"../lib/utils\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst SLUG_INVALID_CHARS_REGEX = /[^a-z0-9]+/g;\nconst SLUG_LEADING_TRAILING_REGEX = /^_|_$/g;\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface FieldEditorProps {\n\topen: boolean;\n\tonOpenChange: (open: boolean) => void;\n\tfield?: SchemaField;\n\tonSave: (input: CreateFieldInput) => void;\n\tisSaving?: boolean;\n}\n\nconst FIELD_TYPES: {\n\ttype: FieldType;\n\tlabel: string;\n\tdescription: string;\n\ticon: React.ElementType;\n}[] = [\n\t{\n\t\ttype: \"string\",\n\t\tlabel: \"Short Text\",\n\t\tdescription: \"Single line text input\",\n\t\ticon: TextT,\n\t},\n\t{\n\t\ttype: \"text\",\n\t\tlabel: \"Long Text\",\n\t\tdescription: \"Multi-line plain text\",\n\t\ticon: TextAlignLeft,\n\t},\n\t{\n\t\ttype: \"number\",\n\t\tlabel: \"Number\",\n\t\tdescription: \"Decimal number\",\n\t\ticon: Hash,\n\t},\n\t{\n\t\ttype: \"integer\",\n\t\tlabel: \"Integer\",\n\t\tdescription: \"Whole number\",\n\t\ticon: Hash,\n\t},\n\t{\n\t\ttype: \"boolean\",\n\t\tlabel: \"Boolean\",\n\t\tdescription: \"True/false toggle\",\n\t\ticon: ToggleLeft,\n\t},\n\t{\n\t\ttype: \"datetime\",\n\t\tlabel: \"Date & Time\",\n\t\tdescription: \"Date and time picker\",\n\t\ticon: Calendar,\n\t},\n\t{\n\t\ttype: \"select\",\n\t\tlabel: \"Select\",\n\t\tdescription: \"Single choice from options\",\n\t\ticon: List,\n\t},\n\t{\n\t\ttype: \"multiSelect\",\n\t\tlabel: \"Multi Select\",\n\t\tdescription: \"Multiple choices from options\",\n\t\ticon: ListChecks,\n\t},\n\t{\n\t\ttype: \"portableText\",\n\t\tlabel: \"Rich Text\",\n\t\tdescription: \"Rich text editor\",\n\t\ticon: FileText,\n\t},\n\t{\n\t\ttype: \"image\",\n\t\tlabel: \"Image\",\n\t\tdescription: \"Image from media library\",\n\t\ticon: ImageIcon,\n\t},\n\t{\n\t\ttype: \"file\",\n\t\tlabel: \"File\",\n\t\tdescription: \"File from media library\",\n\t\ticon: File,\n\t},\n\t{\n\t\ttype: \"reference\",\n\t\tlabel: \"Reference\",\n\t\tdescription: \"Link to another content item\",\n\t\ticon: LinkSimple,\n\t},\n\t{\n\t\ttype: \"json\",\n\t\tlabel: \"JSON\",\n\t\tdescription: \"Arbitrary JSON data\",\n\t\ticon: BracketsCurly,\n\t},\n\t{\n\t\ttype: \"slug\",\n\t\tlabel: \"Slug\",\n\t\tdescription: \"URL-friendly identifier\",\n\t\ticon: Link,\n\t},\n];\n\ninterface FieldFormState {\n\tstep: \"type\" | \"config\";\n\tselectedType: FieldType | null;\n\tslug: string;\n\tlabel: string;\n\trequired: boolean;\n\tunique: boolean;\n\tsearchable: boolean;\n\tminLength: string;\n\tmaxLength: string;\n\tmin: string;\n\tmax: string;\n\tpattern: string;\n\toptions: string;\n}\n\nfunction getInitialFormState(field?: SchemaField): FieldFormState {\n\tif (field) {\n\t\treturn {\n\t\t\tstep: \"config\",\n\t\t\tselectedType: field.type,\n\t\t\tslug: field.slug,\n\t\t\tlabel: field.label,\n\t\t\trequired: field.required,\n\t\t\tunique: field.unique,\n\t\t\tsearchable: field.searchable,\n\t\t\tminLength: field.validation?.minLength?.toString() ?? \"\",\n\t\t\tmaxLength: field.validation?.maxLength?.toString() ?? \"\",\n\t\t\tmin: field.validation?.min?.toString() ?? \"\",\n\t\t\tmax: field.validation?.max?.toString() ?? \"\",\n\t\t\tpattern: field.validation?.pattern ?? \"\",\n\t\t\toptions: field.validation?.options?.join(\"\\n\") ?? \"\",\n\t\t};\n\t}\n\treturn {\n\t\tstep: \"type\",\n\t\tselectedType: null,\n\t\tslug: \"\",\n\t\tlabel: \"\",\n\t\trequired: false,\n\t\tunique: false,\n\t\tsearchable: false,\n\t\tminLength: \"\",\n\t\tmaxLength: \"\",\n\t\tmin: \"\",\n\t\tmax: \"\",\n\t\tpattern: \"\",\n\t\toptions: \"\",\n\t};\n}\n\n/**\n * Field editor dialog for creating/editing fields\n */\nexport function FieldEditor({ open, onOpenChange, field, onSave, isSaving }: FieldEditorProps) {\n\tconst [formState, setFormState] = React.useState(() => getInitialFormState(field));\n\n\t// Reset state when dialog opens\n\tReact.useEffect(() => {\n\t\tif (open) {\n\t\t\tsetFormState(getInitialFormState(field));\n\t\t}\n\t}, [open, field]);\n\n\tconst { step, selectedType, slug, label, required, unique, searchable } = formState;\n\tconst { minLength, maxLength, min, max, pattern, options } = formState;\n\tconst setField = <K extends keyof FieldFormState>(key: K, value: FieldFormState[K]) =>\n\t\tsetFormState((prev) => ({ ...prev, [key]: value }));\n\n\t// Auto-generate slug from label\n\tconst handleLabelChange = (value: string) => {\n\t\tsetField(\"label\", value);\n\t\tif (!field) {\n\t\t\t// Only auto-generate for new fields\n\t\t\tsetField(\n\t\t\t\t\"slug\",\n\t\t\t\tvalue\n\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t.replace(SLUG_INVALID_CHARS_REGEX, \"_\")\n\t\t\t\t\t.replace(SLUG_LEADING_TRAILING_REGEX, \"\"),\n\t\t\t);\n\t\t}\n\t};\n\n\tconst handleTypeSelect = (type: FieldType) => {\n\t\tsetFormState((prev) => ({ ...prev, selectedType: type, step: \"config\" }));\n\t};\n\n\tconst handleSave = () => {\n\t\tif (!selectedType || !slug || !label) return;\n\n\t\tconst validation: CreateFieldInput[\"validation\"] = {};\n\n\t\t// Build validation based on field type\n\t\tif (selectedType === \"string\" || selectedType === \"text\" || selectedType === \"slug\") {\n\t\t\tif (minLength) validation.minLength = parseInt(minLength, 10);\n\t\t\tif (maxLength) validation.maxLength = parseInt(maxLength, 10);\n\t\t\tif (pattern) validation.pattern = pattern;\n\t\t}\n\n\t\tif (selectedType === \"number\" || selectedType === \"integer\") {\n\t\t\tif (min) validation.min = parseFloat(min);\n\t\t\tif (max) validation.max = parseFloat(max);\n\t\t}\n\n\t\tif (selectedType === \"select\" || selectedType === \"multiSelect\") {\n\t\t\tconst optionList = options\n\t\t\t\t.split(\"\\n\")\n\t\t\t\t.map((o) => o.trim())\n\t\t\t\t.filter(Boolean);\n\t\t\tif (optionList.length > 0) {\n\t\t\t\tvalidation.options = optionList;\n\t\t\t}\n\t\t}\n\n\t\t// Only include searchable for text-based fields\n\t\tconst isSearchableType =\n\t\t\tselectedType === \"string\" ||\n\t\t\tselectedType === \"text\" ||\n\t\t\tselectedType === \"portableText\" ||\n\t\t\tselectedType === \"slug\";\n\n\t\tconst input: CreateFieldInput = {\n\t\t\tslug,\n\t\t\tlabel,\n\t\t\ttype: selectedType,\n\t\t\trequired,\n\t\t\tunique,\n\t\t\tsearchable: isSearchableType ? searchable : undefined,\n\t\t\tvalidation: Object.keys(validation).length > 0 ? validation : undefined,\n\t\t};\n\n\t\tonSave(input);\n\t};\n\n\tconst typeConfig = FIELD_TYPES.find((t) => t.type === selectedType);\n\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={onOpenChange}>\n\t\t\t<Dialog className=\"p-6 max-w-2xl\" size=\"lg\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t{field ? \"Edit Field\" : step === \"type\" ? \"Add Field\" : \"Configure Field\"}\n\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{step === \"type\" ? (\n\t\t\t\t\t<div className=\"grid grid-cols-2 gap-3 max-h-[60vh] overflow-y-auto\">\n\t\t\t\t\t\t{FIELD_TYPES.map((ft) => {\n\t\t\t\t\t\t\tconst Icon = ft.icon;\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\tkey={ft.type}\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tonClick={() => handleTypeSelect(ft.type)}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex items-start space-x-3 p-4 rounded-lg border text-left transition-colors hover:border-kumo-brand hover:bg-kumo-tint/50\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div className=\"flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-kumo-tint\">\n\t\t\t\t\t\t\t\t\t\t<Icon className=\"h-5 w-5\" />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">{ft.label}</p>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">{ft.description}</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"space-y-6\">\n\t\t\t\t\t\t{/* Type indicator */}\n\t\t\t\t\t\t{typeConfig && (\n\t\t\t\t\t\t\t<div className=\"flex items-center space-x-3 p-3 bg-kumo-tint/50 rounded-lg\">\n\t\t\t\t\t\t\t\t<typeConfig.icon className=\"h-5 w-5\" />\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">{typeConfig.label}</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">{typeConfig.description}</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{!field && (\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"ml-auto\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => setField(\"step\", \"type\")}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tChange\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Basic info */}\n\t\t\t\t\t\t<div className=\"grid grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Label\"\n\t\t\t\t\t\t\t\tvalue={label}\n\t\t\t\t\t\t\t\tonChange={(e) => handleLabelChange(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"Field Label\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\t\t\t\tvalue={slug}\n\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"slug\", e.target.value)}\n\t\t\t\t\t\t\t\t\tplaceholder=\"field_slug\"\n\t\t\t\t\t\t\t\t\tdisabled={!!field}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{field && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-2\">\n\t\t\t\t\t\t\t\t\t\tField slugs cannot be changed after creation\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Toggles */}\n\t\t\t\t\t\t<div className=\"flex items-center space-x-6\">\n\t\t\t\t\t\t\t<label className=\"flex items-center space-x-2\">\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\tchecked={required}\n\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"required\", e.target.checked)}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded border-kumo-line\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<span className=\"text-sm\">Required</span>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<label className=\"flex items-center space-x-2\">\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\tchecked={unique}\n\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"unique\", e.target.checked)}\n\t\t\t\t\t\t\t\t\tclassName=\"rounded border-kumo-line\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<span className=\"text-sm\">Unique</span>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t{(selectedType === \"string\" ||\n\t\t\t\t\t\t\t\tselectedType === \"text\" ||\n\t\t\t\t\t\t\t\tselectedType === \"portableText\" ||\n\t\t\t\t\t\t\t\tselectedType === \"slug\") && (\n\t\t\t\t\t\t\t\t<label className=\"flex items-center space-x-2\">\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\tchecked={searchable}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"searchable\", e.target.checked)}\n\t\t\t\t\t\t\t\t\t\tclassName=\"rounded border-kumo-line\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<span className=\"text-sm\">Searchable</span>\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Type-specific validation */}\n\t\t\t\t\t\t{(selectedType === \"string\" || selectedType === \"text\" || selectedType === \"slug\") && (\n\t\t\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t\t\t<h4 className=\"font-medium text-sm\">Validation</h4>\n\t\t\t\t\t\t\t\t<div className=\"grid grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Min Length\"\n\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\tvalue={minLength}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"minLength\", e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"No minimum\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Max Length\"\n\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\tvalue={maxLength}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"maxLength\", e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"No maximum\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{selectedType === \"string\" && (\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Pattern (Regex)\"\n\t\t\t\t\t\t\t\t\t\tvalue={pattern}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"pattern\", e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"^[a-z]+$\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{(selectedType === \"number\" || selectedType === \"integer\") && (\n\t\t\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t\t\t<h4 className=\"font-medium text-sm\">Validation</h4>\n\t\t\t\t\t\t\t\t<div className=\"grid grid-cols-2 gap-4\">\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Min Value\"\n\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\tvalue={min}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"min\", e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"No minimum\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Max Value\"\n\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\tvalue={max}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setField(\"max\", e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"No maximum\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{(selectedType === \"select\" || selectedType === \"multiSelect\") && (\n\t\t\t\t\t\t\t<InputArea\n\t\t\t\t\t\t\t\tlabel=\"Options (one per line)\"\n\t\t\t\t\t\t\t\tvalue={options}\n\t\t\t\t\t\t\t\tonChange={(e) => setField(\"options\", e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder={\"Option 1\\nOption 2\\nOption 3\"}\n\t\t\t\t\t\t\t\trows={5}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{step === \"config\" && (\n\t\t\t\t\t<div className=\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\">\n\t\t\t\t\t\t<Button variant=\"outline\" onClick={() => onOpenChange(false)} disabled={isSaving}>\n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button onClick={handleSave} disabled={!slug || !label || isSaving}>\n\t\t\t\t\t\t\t{isSaving ? \"Saving...\" : field ? \"Update Field\" : \"Add Field\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n","import { Badge, Button, Input, InputArea, Label, Select, buttonVariants } from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tPlus,\n\tDotsSixVertical,\n\tPencil,\n\tTrash,\n\tDatabase,\n\tFileText,\n} from \"@phosphor-icons/react\";\nimport { Link, useNavigate } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport type {\n\tSchemaCollectionWithFields,\n\tSchemaField,\n\tCreateFieldInput,\n\tCreateCollectionInput,\n\tUpdateCollectionInput,\n} from \"../lib/api\";\nimport { cn } from \"../lib/utils\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\nimport { FieldEditor } from \"./FieldEditor\";\n\n// Regex patterns for slug generation\nconst SLUG_INVALID_CHARS_PATTERN = /[^a-z0-9]+/g;\nconst SLUG_LEADING_TRAILING_PATTERN = /^_|_$/g;\n\nexport interface ContentTypeEditorProps {\n\tcollection?: SchemaCollectionWithFields;\n\tisNew?: boolean;\n\tisSaving?: boolean;\n\tonSave: (input: CreateCollectionInput | UpdateCollectionInput) => void;\n\tonAddField?: (input: CreateFieldInput) => void;\n\tonUpdateField?: (fieldSlug: string, input: CreateFieldInput) => void;\n\tonDeleteField?: (fieldSlug: string) => void;\n\tonReorderFields?: (fieldSlugs: string[]) => void;\n}\n\nconst SUPPORT_OPTIONS = [\n\t{\n\t\tvalue: \"drafts\",\n\t\tlabel: \"Drafts\",\n\t\tdescription: \"Save content as draft before publishing\",\n\t},\n\t{\n\t\tvalue: \"revisions\",\n\t\tlabel: \"Revisions\",\n\t\tdescription: \"Track content history\",\n\t},\n\t{\n\t\tvalue: \"preview\",\n\t\tlabel: \"Preview\",\n\t\tdescription: \"Preview content before publishing\",\n\t},\n\t{\n\t\tvalue: \"search\",\n\t\tlabel: \"Search\",\n\t\tdescription: \"Enable full-text search on this collection\",\n\t},\n];\n\n/**\n * System fields that exist on every collection\n * These are created automatically and cannot be modified\n */\nconst SYSTEM_FIELDS = [\n\t{\n\t\tslug: \"id\",\n\t\tlabel: \"ID\",\n\t\ttype: \"text\",\n\t\tdescription: \"Unique identifier (ULID)\",\n\t},\n\t{\n\t\tslug: \"slug\",\n\t\tlabel: \"Slug\",\n\t\ttype: \"text\",\n\t\tdescription: \"URL-friendly identifier\",\n\t},\n\t{\n\t\tslug: \"status\",\n\t\tlabel: \"Status\",\n\t\ttype: \"text\",\n\t\tdescription: \"draft, published, or archived\",\n\t},\n\t{\n\t\tslug: \"created_at\",\n\t\tlabel: \"Created At\",\n\t\ttype: \"datetime\",\n\t\tdescription: \"When the entry was created\",\n\t},\n\t{\n\t\tslug: \"updated_at\",\n\t\tlabel: \"Updated At\",\n\t\ttype: \"datetime\",\n\t\tdescription: \"When the entry was last modified\",\n\t},\n\t{\n\t\tslug: \"published_at\",\n\t\tlabel: \"Published At\",\n\t\ttype: \"datetime\",\n\t\tdescription: \"When the entry was published\",\n\t},\n];\n\n/**\n * Content Type editor for creating/editing collections\n */\nexport function ContentTypeEditor({\n\tcollection,\n\tisNew,\n\tisSaving,\n\tonSave,\n\tonAddField,\n\tonUpdateField,\n\tonDeleteField,\n\tonReorderFields: _onReorderFields,\n}: ContentTypeEditorProps) {\n\tconst _navigate = useNavigate();\n\n\t// Form state\n\tconst [slug, setSlug] = React.useState(collection?.slug ?? \"\");\n\tconst [label, setLabel] = React.useState(collection?.label ?? \"\");\n\tconst [labelSingular, setLabelSingular] = React.useState(collection?.labelSingular ?? \"\");\n\tconst [description, setDescription] = React.useState(collection?.description ?? \"\");\n\tconst [urlPattern, setUrlPattern] = React.useState(collection?.urlPattern ?? \"\");\n\tconst [supports, setSupports] = React.useState<string[]>(collection?.supports ?? [\"drafts\"]);\n\n\t// SEO state\n\tconst [hasSeo, setHasSeo] = React.useState(collection?.hasSeo ?? false);\n\n\t// Comment settings state\n\tconst [commentsEnabled, setCommentsEnabled] = React.useState(\n\t\tcollection?.commentsEnabled ?? false,\n\t);\n\tconst [commentsModeration, setCommentsModeration] = React.useState<\"all\" | \"first_time\" | \"none\">(\n\t\tcollection?.commentsModeration ?? \"first_time\",\n\t);\n\tconst [commentsClosedAfterDays, setCommentsClosedAfterDays] = React.useState(\n\t\tcollection?.commentsClosedAfterDays ?? 90,\n\t);\n\tconst [commentsAutoApproveUsers, setCommentsAutoApproveUsers] = React.useState(\n\t\tcollection?.commentsAutoApproveUsers ?? true,\n\t);\n\n\t// Field editor state\n\tconst [fieldEditorOpen, setFieldEditorOpen] = React.useState(false);\n\tconst [editingField, setEditingField] = React.useState<SchemaField | undefined>();\n\tconst [fieldSaving, setFieldSaving] = React.useState(false);\n\tconst [deleteFieldTarget, setDeleteFieldTarget] = React.useState<SchemaField | null>(null);\n\n\tconst urlPatternValid = !urlPattern || urlPattern.includes(\"{slug}\");\n\n\t// Track whether form has unsaved changes\n\tconst hasChanges = React.useMemo(() => {\n\t\tif (isNew) return slug && label;\n\t\tif (!collection) return false;\n\t\treturn (\n\t\t\tlabel !== collection.label ||\n\t\t\tlabelSingular !== (collection.labelSingular ?? \"\") ||\n\t\t\tdescription !== (collection.description ?? \"\") ||\n\t\t\turlPattern !== (collection.urlPattern ?? \"\") ||\n\t\t\tJSON.stringify([...supports].toSorted()) !==\n\t\t\t\tJSON.stringify([...collection.supports].toSorted()) ||\n\t\t\thasSeo !== collection.hasSeo ||\n\t\t\tcommentsEnabled !== collection.commentsEnabled ||\n\t\t\tcommentsModeration !== collection.commentsModeration ||\n\t\t\tcommentsClosedAfterDays !== collection.commentsClosedAfterDays ||\n\t\t\tcommentsAutoApproveUsers !== collection.commentsAutoApproveUsers\n\t\t);\n\t}, [\n\t\tisNew,\n\t\tcollection,\n\t\tslug,\n\t\tlabel,\n\t\tlabelSingular,\n\t\tdescription,\n\t\turlPattern,\n\t\tsupports,\n\t\thasSeo,\n\t\tcommentsEnabled,\n\t\tcommentsModeration,\n\t\tcommentsClosedAfterDays,\n\t\tcommentsAutoApproveUsers,\n\t]);\n\n\t// Auto-generate slug from plural label\n\tconst handleLabelChange = (value: string) => {\n\t\tsetLabel(value);\n\t\tif (isNew) {\n\t\t\tsetSlug(\n\t\t\t\tvalue\n\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t.replace(SLUG_INVALID_CHARS_PATTERN, \"_\")\n\t\t\t\t\t.replace(SLUG_LEADING_TRAILING_PATTERN, \"\"),\n\t\t\t);\n\t\t}\n\t};\n\n\t// Auto-generate plural label (and slug) from singular label\n\tconst handleSingularLabelChange = (value: string) => {\n\t\tsetLabelSingular(value);\n\t\tif (isNew) {\n\t\t\tconst plural = value ? `${value}s` : \"\";\n\t\t\thandleLabelChange(plural);\n\t\t}\n\t};\n\n\tconst handleSupportToggle = (value: string) => {\n\t\tsetSupports((prev) =>\n\t\t\tprev.includes(value) ? prev.filter((s) => s !== value) : [...prev, value],\n\t\t);\n\t};\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (isNew) {\n\t\t\tonSave({\n\t\t\t\tslug,\n\t\t\t\tlabel,\n\t\t\t\tlabelSingular: labelSingular || undefined,\n\t\t\t\tdescription: description || undefined,\n\t\t\t\turlPattern: urlPattern || undefined,\n\t\t\t\tsupports,\n\t\t\t\thasSeo,\n\t\t\t});\n\t\t} else {\n\t\t\tonSave({\n\t\t\t\tlabel,\n\t\t\t\tlabelSingular: labelSingular || undefined,\n\t\t\t\tdescription: description || undefined,\n\t\t\t\turlPattern: urlPattern || undefined,\n\t\t\t\tsupports,\n\t\t\t\thasSeo,\n\t\t\t\tcommentsEnabled,\n\t\t\t\tcommentsModeration,\n\t\t\t\tcommentsClosedAfterDays,\n\t\t\t\tcommentsAutoApproveUsers,\n\t\t\t});\n\t\t}\n\t};\n\n\tconst handleFieldSave = async (input: CreateFieldInput) => {\n\t\tsetFieldSaving(true);\n\t\ttry {\n\t\t\tif (editingField) {\n\t\t\t\tonUpdateField?.(editingField.slug, input);\n\t\t\t} else {\n\t\t\t\tonAddField?.(input);\n\t\t\t}\n\t\t\tsetFieldEditorOpen(false);\n\t\t\tsetEditingField(undefined);\n\t\t} finally {\n\t\t\tsetFieldSaving(false);\n\t\t}\n\t};\n\n\tconst handleEditField = (field: SchemaField) => {\n\t\tsetEditingField(field);\n\t\tsetFieldEditorOpen(true);\n\t};\n\n\tconst handleAddField = () => {\n\t\tsetEditingField(undefined);\n\t\tsetFieldEditorOpen(true);\n\t};\n\n\tconst isFromCode = collection?.source === \"code\";\n\tconst fields = collection?.fields ?? [];\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center space-x-4\">\n\t\t\t\t<Link\n\t\t\t\t\tto=\"/content-types\"\n\t\t\t\t\taria-label=\"Back to Content Types\"\n\t\t\t\t\tclassName={buttonVariants({ variant: \"ghost\", shape: \"square\" })}\n\t\t\t\t>\n\t\t\t\t\t<ArrowLeft className=\"h-5 w-5\" />\n\t\t\t\t</Link>\n\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">{isNew ? \"New Content Type\" : collection?.label}</h1>\n\t\t\t\t\t{!isNew && (\n\t\t\t\t\t\t<p className=\"text-kumo-subtle text-sm\">\n\t\t\t\t\t\t\t<code className=\"bg-kumo-tint px-1.5 py-0.5 rounded\">{collection?.slug}</code>\n\t\t\t\t\t\t\t{isFromCode && (\n\t\t\t\t\t\t\t\t<span className=\"ml-2 text-purple-600 dark:text-purple-400\">Defined in code</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{isFromCode && (\n\t\t\t\t<div className=\"rounded-lg border border-purple-200 dark:border-purple-800 bg-purple-50 dark:bg-purple-950 p-4\">\n\t\t\t\t\t<div className=\"flex items-center space-x-2\">\n\t\t\t\t\t\t<FileText className=\"h-5 w-5 text-purple-600 dark:text-purple-400\" />\n\t\t\t\t\t\t<p className=\"text-sm text-purple-700 dark:text-purple-300\">\n\t\t\t\t\t\t\tThis collection is defined in code. Some settings cannot be changed here. Edit your\n\t\t\t\t\t\t\tlive.config.ts file to modify the schema.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n\t\t\t\t{/* Settings form */}\n\t\t\t\t<div className=\"lg:col-span-1\">\n\t\t\t\t\t<form onSubmit={handleSubmit} className=\"space-y-4\">\n\t\t\t\t\t\t<div className=\"rounded-lg border p-4 space-y-4\">\n\t\t\t\t\t\t\t<h2 className=\"font-semibold\">Settings</h2>\n\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Label (Singular)\"\n\t\t\t\t\t\t\t\tvalue={labelSingular}\n\t\t\t\t\t\t\t\tonChange={(e) => handleSingularLabelChange(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"Post\"\n\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Label (Plural)\"\n\t\t\t\t\t\t\t\tvalue={label}\n\t\t\t\t\t\t\t\tonChange={(e) => handleLabelChange(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"Posts\"\n\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t{isNew && (\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\t\t\t\t\tvalue={slug}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setSlug(e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"posts\"\n\t\t\t\t\t\t\t\t\t\tdisabled={!isNew}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-2\">Used in URLs and API endpoints</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t<InputArea\n\t\t\t\t\t\t\t\tlabel=\"Description\"\n\t\t\t\t\t\t\t\tvalue={description}\n\t\t\t\t\t\t\t\tonChange={(e) => setDescription(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"A brief description of this content type\"\n\t\t\t\t\t\t\t\trows={3}\n\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"URL Pattern\"\n\t\t\t\t\t\t\t\t\tvalue={urlPattern}\n\t\t\t\t\t\t\t\t\tonChange={(e) => setUrlPattern(e.target.value)}\n\t\t\t\t\t\t\t\t\tplaceholder={`/${slug === \"pages\" ? \"\" : `${slug}/`}{slug}`}\n\t\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t{urlPattern && !urlPattern.includes(\"{slug}\") && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-danger mt-2\">\n\t\t\t\t\t\t\t\t\t\tPattern must include a {\"{slug}\"} placeholder\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\t\tPattern for generating URLs, e.g. /blog/{\"{slug}\"}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<div className=\"space-y-3\">\n\t\t\t\t\t\t\t\t<Label>Features</Label>\n\t\t\t\t\t\t\t\t{SUPPORT_OPTIONS.map((option) => (\n\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\tkey={option.value}\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"flex items-start space-x-3 p-2 rounded-md cursor-pointer hover:bg-kumo-tint/50\",\n\t\t\t\t\t\t\t\t\t\t\tisFromCode && \"opacity-60 cursor-not-allowed\",\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\tchecked={supports.includes(option.value)}\n\t\t\t\t\t\t\t\t\t\t\tonChange={() => handleSupportToggle(option.value)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"mt-1 rounded border-kumo-line\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">{option.label}</span>\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">{option.description}</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* SEO toggle */}\n\t\t\t\t\t\t\t<div className=\"pt-2 border-t\">\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex items-start space-x-3 p-2 rounded-md cursor-pointer hover:bg-kumo-tint/50\",\n\t\t\t\t\t\t\t\t\t\tisFromCode && \"opacity-60 cursor-not-allowed\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\tchecked={hasSeo}\n\t\t\t\t\t\t\t\t\t\tonChange={() => setHasSeo(!hasSeo)}\n\t\t\t\t\t\t\t\t\t\tclassName=\"mt-1 rounded border-kumo-line\"\n\t\t\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">SEO</span>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\tAdd SEO metadata fields (title, description, image) and include in sitemap\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Comments settings — only for existing collections */}\n\t\t\t\t\t\t{!isNew && (\n\t\t\t\t\t\t\t<div className=\"rounded-lg border p-4 space-y-4\">\n\t\t\t\t\t\t\t\t<h2 className=\"font-semibold\">Comments</h2>\n\n\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex items-start space-x-3 p-2 rounded-md cursor-pointer hover:bg-kumo-tint/50\",\n\t\t\t\t\t\t\t\t\t\tisFromCode && \"opacity-60 cursor-not-allowed\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\tchecked={commentsEnabled}\n\t\t\t\t\t\t\t\t\t\tonChange={() => setCommentsEnabled(!commentsEnabled)}\n\t\t\t\t\t\t\t\t\t\tclassName=\"mt-1 rounded border-kumo-line\"\n\t\t\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">Enable comments</span>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\tAllow visitors to leave comments on this collection's content\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</label>\n\n\t\t\t\t\t\t\t\t{commentsEnabled && (\n\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\t\t\tlabel=\"Moderation\"\n\t\t\t\t\t\t\t\t\t\t\tvalue={commentsModeration}\n\t\t\t\t\t\t\t\t\t\t\tonValueChange={(v) =>\n\t\t\t\t\t\t\t\t\t\t\t\tsetCommentsModeration((v as \"all\" | \"first_time\" | \"none\") ?? \"first_time\")\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\titems={{\n\t\t\t\t\t\t\t\t\t\t\t\tall: \"All comments require approval\",\n\t\t\t\t\t\t\t\t\t\t\t\tfirst_time: \"First-time commenters only\",\n\t\t\t\t\t\t\t\t\t\t\t\tnone: \"No moderation (auto-approve all)\",\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\t\tlabel=\"Close comments after (days)\"\n\t\t\t\t\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\t\t\t\t\tmin={0}\n\t\t\t\t\t\t\t\t\t\t\tvalue={String(commentsClosedAfterDays)}\n\t\t\t\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst parsed = Number.parseInt(e.target.value, 10);\n\t\t\t\t\t\t\t\t\t\t\t\tsetCommentsClosedAfterDays(Number.isNaN(parsed) ? 0 : Math.max(0, parsed));\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle -mt-2\">\n\t\t\t\t\t\t\t\t\t\t\tSet to 0 to never close comments automatically.\n\t\t\t\t\t\t\t\t\t\t</p>\n\n\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\"flex items-start space-x-3 p-2 rounded-md cursor-pointer hover:bg-kumo-tint/50\",\n\t\t\t\t\t\t\t\t\t\t\t\tisFromCode && \"opacity-60 cursor-not-allowed\",\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\tchecked={commentsAutoApproveUsers}\n\t\t\t\t\t\t\t\t\t\t\t\tonChange={() => setCommentsAutoApproveUsers(!commentsAutoApproveUsers)}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"mt-1 rounded border-kumo-line\"\n\t\t\t\t\t\t\t\t\t\t\t\tdisabled={isFromCode}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tAuto-approve authenticated users\n\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t\tComments from logged-in CMS users are approved automatically\n\t\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{!isFromCode && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tdisabled={!hasChanges || !urlPatternValid || isSaving}\n\t\t\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? \"Saving...\" : isNew ? \"Create Content Type\" : \"Save Changes\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</form>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Fields section - only show for existing collections */}\n\t\t\t\t{!isNew && (\n\t\t\t\t\t<div className=\"lg:col-span-2\">\n\t\t\t\t\t\t<div className=\"rounded-lg border\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-between p-4 border-b\">\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<h2 className=\"font-semibold\">Fields</h2>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t{SYSTEM_FIELDS.length} system + {fields.length} custom field\n\t\t\t\t\t\t\t\t\t\t{fields.length !== 1 ? \"s\" : \"\"}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t{!isFromCode && (\n\t\t\t\t\t\t\t\t\t<Button icon={<Plus />} onClick={handleAddField}>\n\t\t\t\t\t\t\t\t\t\tAdd Field\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* System fields - always shown */}\n\t\t\t\t\t\t\t<div className=\"border-b bg-kumo-tint/30\">\n\t\t\t\t\t\t\t\t<div className=\"px-4 py-2 text-xs font-medium text-kumo-subtle uppercase tracking-wider\">\n\t\t\t\t\t\t\t\t\tSystem Fields\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"divide-y divide-kumo-line/50\">\n\t\t\t\t\t\t\t\t\t{SYSTEM_FIELDS.map((field) => (\n\t\t\t\t\t\t\t\t\t\t<SystemFieldRow key={field.slug} field={field} />\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Custom fields */}\n\t\t\t\t\t\t\t{fields.length === 0 ? (\n\t\t\t\t\t\t\t\t<div className=\"p-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t<Database className=\"mx-auto h-12 w-12 mb-4 opacity-50\" />\n\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">No custom fields yet</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm\">Add fields to define the structure of your content</p>\n\t\t\t\t\t\t\t\t\t{!isFromCode && (\n\t\t\t\t\t\t\t\t\t\t<Button className=\"mt-4\" icon={<Plus />} onClick={handleAddField}>\n\t\t\t\t\t\t\t\t\t\t\tAdd First Field\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<div className=\"px-4 py-2 text-xs font-medium text-kumo-subtle uppercase tracking-wider border-b\">\n\t\t\t\t\t\t\t\t\t\tCustom Fields\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"divide-y\">\n\t\t\t\t\t\t\t\t\t\t{fields.map((field) => (\n\t\t\t\t\t\t\t\t\t\t\t<FieldRow\n\t\t\t\t\t\t\t\t\t\t\t\tkey={field.id}\n\t\t\t\t\t\t\t\t\t\t\t\tfield={field}\n\t\t\t\t\t\t\t\t\t\t\t\tisFromCode={isFromCode}\n\t\t\t\t\t\t\t\t\t\t\t\tonEdit={() => handleEditField(field)}\n\t\t\t\t\t\t\t\t\t\t\t\tonDelete={() => setDeleteFieldTarget(field)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Field editor dialog */}\n\t\t\t<FieldEditor\n\t\t\t\topen={fieldEditorOpen}\n\t\t\t\tonOpenChange={setFieldEditorOpen}\n\t\t\t\tfield={editingField}\n\t\t\t\tonSave={handleFieldSave}\n\t\t\t\tisSaving={fieldSaving}\n\t\t\t/>\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={!!deleteFieldTarget}\n\t\t\t\tonClose={() => setDeleteFieldTarget(null)}\n\t\t\t\ttitle=\"Delete Field?\"\n\t\t\t\tdescription={\n\t\t\t\t\tdeleteFieldTarget\n\t\t\t\t\t\t? `Are you sure you want to delete the \"${deleteFieldTarget.label}\" field?`\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={false}\n\t\t\t\terror={null}\n\t\t\t\tonConfirm={() => {\n\t\t\t\t\tif (deleteFieldTarget) {\n\t\t\t\t\t\tonDeleteField?.(deleteFieldTarget.slug);\n\t\t\t\t\t\tsetDeleteFieldTarget(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\ninterface FieldRowProps {\n\tfield: SchemaField;\n\tisFromCode?: boolean;\n\tonEdit: () => void;\n\tonDelete: () => void;\n}\n\nfunction FieldRow({ field, isFromCode, onEdit, onDelete }: FieldRowProps) {\n\treturn (\n\t\t<div className=\"flex items-center px-4 py-3 hover:bg-kumo-tint/25\">\n\t\t\t{!isFromCode && <DotsSixVertical className=\"h-5 w-5 mr-3 text-kumo-subtle cursor-grab\" />}\n\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t<div className=\"flex items-center space-x-2\">\n\t\t\t\t\t<span className=\"font-medium\">{field.label}</span>\n\t\t\t\t\t<code className=\"text-xs bg-kumo-tint px-1.5 py-0.5 rounded text-kumo-subtle\">\n\t\t\t\t\t\t{field.slug}\n\t\t\t\t\t</code>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex items-center space-x-2 mt-1\">\n\t\t\t\t\t<span className=\"text-xs text-kumo-subtle capitalize\">{field.type}</span>\n\t\t\t\t\t{field.required && <Badge variant=\"secondary\">Required</Badge>}\n\t\t\t\t\t{field.unique && <Badge variant=\"secondary\">Unique</Badge>}\n\t\t\t\t\t{field.searchable && <Badge variant=\"secondary\">Searchable</Badge>}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{!isFromCode && (\n\t\t\t\t<div className=\"flex items-center space-x-1\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\tonClick={onEdit}\n\t\t\t\t\t\taria-label={`Edit ${field.label} field`}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Pencil className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\tonClick={onDelete}\n\t\t\t\t\t\taria-label={`Delete ${field.label} field`}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-danger\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\ninterface SystemFieldInfo {\n\tslug: string;\n\tlabel: string;\n\ttype: string;\n\tdescription: string;\n}\n\nfunction SystemFieldRow({ field }: { field: SystemFieldInfo }) {\n\treturn (\n\t\t<div className=\"flex items-center px-4 py-2 opacity-75\">\n\t\t\t<div className=\"w-8\" /> {/* Spacer for alignment with draggable fields */}\n\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t<div className=\"flex items-center space-x-2\">\n\t\t\t\t\t<span className=\"font-medium text-sm\">{field.label}</span>\n\t\t\t\t\t<code className=\"text-xs bg-kumo-tint px-1.5 py-0.5 rounded text-kumo-subtle\">\n\t\t\t\t\t\t{field.slug}\n\t\t\t\t\t</code>\n\t\t\t\t\t<Badge variant=\"secondary\">System</Badge>\n\t\t\t\t</div>\n\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-0.5\">{field.description}</p>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","import { Badge, Button, buttonVariants } from \"@cloudflare/kumo\";\nimport { Plus, Pencil, Trash, Database, FileText, Warning, Check } from \"@phosphor-icons/react\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport type { SchemaCollection, OrphanedTable } from \"../lib/api\";\nimport { cn } from \"../lib/utils\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\nexport interface ContentTypeListProps {\n\tcollections: SchemaCollection[];\n\torphanedTables?: OrphanedTable[];\n\tisLoading?: boolean;\n\tonDelete?: (slug: string) => void;\n\tonRegisterOrphan?: (slug: string) => void;\n}\n\n/**\n * Content Type list view - shows all collections in the schema registry\n */\nexport function ContentTypeList({\n\tcollections,\n\torphanedTables,\n\tisLoading,\n\tonDelete,\n\tonRegisterOrphan,\n}: ContentTypeListProps) {\n\tconst [deleteTarget, setDeleteTarget] = React.useState<SchemaCollection | null>(null);\n\tconst hasOrphans = orphanedTables && orphanedTables.length > 0;\n\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">Content Types</h1>\n\t\t\t\t\t<p className=\"text-kumo-subtle text-sm\">Define the structure of your content</p>\n\t\t\t\t</div>\n\t\t\t\t<Link to=\"/content-types/new\" className={buttonVariants()}>\n\t\t\t\t\t<Plus className=\"mr-2 h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\tNew Content Type\n\t\t\t\t</Link>\n\t\t\t</div>\n\n\t\t\t{/* Orphaned Tables Warning */}\n\t\t\t{hasOrphans && (\n\t\t\t\t<div className=\"rounded-md border border-amber-200 bg-amber-50 dark:border-amber-900 dark:bg-amber-950 p-4\">\n\t\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t\t<Warning className=\"h-5 w-5 text-amber-600 dark:text-amber-400 mt-0.5\" />\n\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t<h3 className=\"font-medium text-amber-800 dark:text-amber-200\">\n\t\t\t\t\t\t\t\tUnregistered Content Tables Found\n\t\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t\t<p className=\"text-sm text-amber-700 dark:text-amber-300 mt-1\">\n\t\t\t\t\t\t\t\tThe following tables contain content but aren't registered as collections. Register\n\t\t\t\t\t\t\t\tthem to manage this content in the admin.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<div className=\"mt-3 space-y-2\">\n\t\t\t\t\t\t\t\t{orphanedTables.map((orphan) => (\n\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\tkey={orphan.slug}\n\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center justify-between bg-white dark:bg-amber-900/50 rounded-md px-3 py-2\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<code className=\"text-sm font-medium\">{orphan.slug}</code>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle ml-2\">\n\t\t\t\t\t\t\t\t\t\t\t\t({orphan.rowCount} items)\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\ticon={<Check />}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => onRegisterOrphan?.(orphan.slug)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tRegister\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Table */}\n\t\t\t<div className=\"rounded-md border overflow-x-auto\">\n\t\t\t\t<table className=\"w-full\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr className=\"border-b bg-kumo-tint/50\">\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tName\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tSlug\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tSource\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tFeatures\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-right text-sm font-medium\">\n\t\t\t\t\t\t\t\tActions\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{isLoading ? (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={5} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\tLoading collections...\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t) : collections.length === 0 && !hasOrphans ? (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={5} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\tNo content types yet.{\" \"}\n\t\t\t\t\t\t\t\t\t<Link to=\"/content-types/new\" className=\"text-kumo-brand underline\">\n\t\t\t\t\t\t\t\t\t\tCreate your first one\n\t\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\tcollections.map((collection) => (\n\t\t\t\t\t\t\t\t<ContentTypeRow\n\t\t\t\t\t\t\t\t\tkey={collection.id}\n\t\t\t\t\t\t\t\t\tcollection={collection}\n\t\t\t\t\t\t\t\t\tonRequestDelete={setDeleteTarget}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={!!deleteTarget}\n\t\t\t\tonClose={() => setDeleteTarget(null)}\n\t\t\t\ttitle=\"Delete Content Type?\"\n\t\t\t\tdescription={\n\t\t\t\t\tdeleteTarget\n\t\t\t\t\t\t? `Are you sure you want to delete \"${deleteTarget.label}\"? This will also delete all content in this collection.`\n\t\t\t\t\t\t: \"\"\n\t\t\t\t}\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={false}\n\t\t\t\terror={null}\n\t\t\t\tonConfirm={() => {\n\t\t\t\t\tif (deleteTarget) {\n\t\t\t\t\t\tonDelete?.(deleteTarget.slug);\n\t\t\t\t\t\tsetDeleteTarget(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\ninterface ContentTypeRowProps {\n\tcollection: SchemaCollection;\n\tonRequestDelete?: (collection: SchemaCollection) => void;\n}\n\nfunction ContentTypeRow({ collection, onRequestDelete }: ContentTypeRowProps) {\n\tconst isFromCode = collection.source === \"code\";\n\n\treturn (\n\t\t<tr className=\"border-b hover:bg-kumo-tint/25\">\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<div className=\"flex items-center space-x-3\">\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex h-8 w-8 items-center justify-center rounded-lg\",\n\t\t\t\t\t\t\tisFromCode\n\t\t\t\t\t\t\t\t? \"bg-purple-100 text-purple-600 dark:bg-purple-900 dark:text-purple-300\"\n\t\t\t\t\t\t\t\t: \"bg-blue-100 text-blue-600 dark:bg-blue-900 dark:text-blue-300\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{isFromCode ? <FileText className=\"h-4 w-4\" /> : <Database className=\"h-4 w-4\" />}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\tto=\"/content-types/$slug\"\n\t\t\t\t\t\t\tparams={{ slug: collection.slug }}\n\t\t\t\t\t\t\tclassName=\"font-medium hover:text-kumo-brand\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{collection.label}\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t{collection.description && (\n\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">{collection.description}</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<code className=\"text-sm bg-kumo-tint px-1.5 py-0.5 rounded\">{collection.slug}</code>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<SourceBadge source={collection.source} />\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t{collection.supports.map((feature) => (\n\t\t\t\t\t\t<Badge key={feature} variant=\"secondary\">\n\t\t\t\t\t\t\t{feature}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 text-right\">\n\t\t\t\t<div className=\"flex items-center justify-end space-x-1\">\n\t\t\t\t\t<Link\n\t\t\t\t\t\tto=\"/content-types/$slug\"\n\t\t\t\t\t\tparams={{ slug: collection.slug }}\n\t\t\t\t\t\taria-label={`Edit ${collection.label}`}\n\t\t\t\t\t\tclassName={buttonVariants({ variant: \"ghost\", shape: \"square\" })}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Pencil className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t</Link>\n\t\t\t\t\t{!isFromCode && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\taria-label={`Delete ${collection.label}`}\n\t\t\t\t\t\t\tonClick={() => onRequestDelete?.(collection)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-danger\" aria-hidden=\"true\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\nfunction SourceBadge({ source }: { source?: string }) {\n\tif (source === \"code\") {\n\t\treturn <Badge variant=\"secondary\">Code</Badge>;\n\t}\n\treturn <Badge variant=\"secondary\">Dashboard</Badge>;\n}\n","/**\n * SandboxedPluginWidget\n *\n * Renders a plugin's dashboard widget using Block Kit. Sends a page_load\n * interaction with page=\"widget:<widgetId>\" to the plugin's admin route.\n */\n\nimport { CircleNotch } from \"@phosphor-icons/react\";\nimport { BlockRenderer } from \"@emdash-cms/blocks\";\nimport type { Block, BlockInteraction, BlockResponse } from \"@emdash-cms/blocks\";\nimport { useCallback, useEffect, useState } from \"react\";\n\nimport { apiFetch, API_BASE } from \"../lib/api/client.js\";\n\ninterface SandboxedPluginWidgetProps {\n\tpluginId: string;\n\twidgetId: string;\n}\n\nexport function SandboxedPluginWidget({ pluginId, widgetId }: SandboxedPluginWidgetProps) {\n\tconst [blocks, setBlocks] = useState<Block[]>([]);\n\tconst [loading, setLoading] = useState(true);\n\tconst [error, setError] = useState<string | null>(null);\n\n\tconst sendInteraction = useCallback(\n\t\tasync (interaction: BlockInteraction) => {\n\t\t\ttry {\n\t\t\t\tconst response = await apiFetch(`${API_BASE}/plugins/${pluginId}/admin`, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify(interaction),\n\t\t\t\t});\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tsetError(`Plugin error (${response.status})`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst body = (await response.json()) as { data: BlockResponse };\n\t\t\t\tconst data = body.data;\n\t\t\t\tsetBlocks(data.blocks);\n\t\t\t\tsetError(null);\n\t\t\t} catch {\n\t\t\t\tsetError(\"Failed to load widget\");\n\t\t\t}\n\t\t},\n\t\t[pluginId],\n\t);\n\n\t// Initial widget load\n\tuseEffect(() => {\n\t\tsetLoading(true);\n\t\tvoid sendInteraction({ type: \"page_load\", page: `widget:${widgetId}` }).finally(() =>\n\t\t\tsetLoading(false),\n\t\t);\n\t}, [sendInteraction, widgetId]);\n\n\tconst handleAction = useCallback(\n\t\t(interaction: BlockInteraction) => {\n\t\t\tvoid sendInteraction(interaction);\n\t\t},\n\t\t[sendInteraction],\n\t);\n\n\tif (loading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center py-6\">\n\t\t\t\t<CircleNotch className=\"h-5 w-5 animate-spin text-kumo-subtle\" />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error) {\n\t\treturn <p className=\"text-sm text-kumo-subtle\">{error}</p>;\n\t}\n\n\tif (blocks.length === 0) {\n\t\treturn <p className=\"text-sm text-kumo-subtle\">No content</p>;\n\t}\n\n\treturn <BlockRenderer blocks={blocks} onAction={handleAction} />;\n}\n","import {\n\tPlus,\n\tUpload,\n\tArrowRight,\n\tCircleDashed,\n\tCheckCircle,\n\tPencilSimple,\n\tCalendarBlank,\n\tImage,\n\tUsers,\n} from \"@phosphor-icons/react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\n\nimport type { AdminManifest } from \"../lib/api\";\nimport type { CollectionStats, DashboardStats, RecentItem } from \"../lib/api/dashboard\";\nimport { fetchDashboardStats } from \"../lib/api/dashboard\";\nimport { usePluginWidget } from \"../lib/plugin-context\";\nimport { formatRelativeTime } from \"../lib/utils\";\nimport { SandboxedPluginWidget } from \"./SandboxedPluginWidget\";\n\nexport interface DashboardProps {\n\tmanifest: AdminManifest;\n}\n\n/**\n * Admin dashboard — quick actions, status, collections, recent activity.\n */\nexport function Dashboard({ manifest }: DashboardProps) {\n\tconst { data: stats, isLoading } = useQuery({\n\t\tqueryKey: [\"dashboard-stats\"],\n\t\tqueryFn: fetchDashboardStats,\n\t\trefetchOnWindowFocus: true,\n\t});\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between\">\n\t\t\t\t<h1 className=\"text-3xl font-bold\">Dashboard</h1>\n\t\t\t\t<QuickActions manifest={manifest} />\n\t\t\t</div>\n\n\t\t\t<StatusBar stats={stats} loading={isLoading} />\n\n\t\t\t{/* Collections + Recent activity */}\n\t\t\t<div className=\"grid gap-6 lg:grid-cols-2\">\n\t\t\t\t<CollectionList\n\t\t\t\t\tcollections={stats?.collections ?? []}\n\t\t\t\t\tmanifest={manifest}\n\t\t\t\t\tloading={isLoading}\n\t\t\t\t/>\n\t\t\t\t<RecentActivity items={stats?.recentItems ?? []} loading={isLoading} />\n\t\t\t</div>\n\n\t\t\t{/* Plugin widgets */}\n\t\t\t<PluginWidgets manifest={manifest} />\n\t\t</div>\n\t);\n}\n\n// --- Quick actions ---\n\nfunction QuickActions({ manifest }: { manifest: AdminManifest }) {\n\tconst collections = Object.entries(manifest.collections);\n\n\treturn (\n\t\t<div className=\"flex flex-wrap gap-2\">\n\t\t\t{collections.map(([slug, config]) => (\n\t\t\t\t<Link\n\t\t\t\t\tkey={slug}\n\t\t\t\t\tto=\"/content/$collection\"\n\t\t\t\t\tparams={{ collection: slug }}\n\t\t\t\t\tsearch={{ locale: undefined }}\n\t\t\t\t\tclassName=\"inline-flex items-center gap-1.5 rounded-md border bg-kumo-base px-3 py-1.5 text-sm font-medium transition-colors hover:bg-kumo-tint\"\n\t\t\t\t>\n\t\t\t\t\t<Plus className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n\t\t\t\t\t{config.labelSingular ?? config.label}\n\t\t\t\t</Link>\n\t\t\t))}\n\t\t\t<Link\n\t\t\t\tto=\"/media\"\n\t\t\t\tclassName=\"inline-flex items-center gap-1.5 rounded-md border bg-kumo-base px-3 py-1.5 text-sm font-medium transition-colors hover:bg-kumo-tint\"\n\t\t\t>\n\t\t\t\t<Upload className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n\t\t\t\tUpload Media\n\t\t\t</Link>\n\t\t</div>\n\t);\n}\n\n// --- Status bar ---\n\nfunction StatusBar({ stats, loading }: { stats?: DashboardStats; loading: boolean }) {\n\tif (loading) {\n\t\treturn <div className=\"flex h-9 animate-pulse rounded-lg border bg-kumo-tint\" />;\n\t}\n\n\tif (!stats) return null;\n\n\tconst totalDrafts = stats.collections.reduce((sum, c) => sum + c.draft, 0);\n\tconst totalScheduled = stats.collections.reduce(\n\t\t(sum, c) => sum + (c.total - c.published - c.draft),\n\t\t0,\n\t);\n\n\tconst indicators = [\n\t\ttotalDrafts > 0 && {\n\t\t\ticon: PencilSimple,\n\t\t\tlabel: `${totalDrafts} draft${totalDrafts !== 1 ? \"s\" : \"\"}`,\n\t\t\tclassName: \"text-amber-700 dark:text-amber-400\",\n\t\t},\n\t\ttotalScheduled > 0 && {\n\t\t\ticon: CalendarBlank,\n\t\t\tlabel: `${totalScheduled} scheduled`,\n\t\t\tclassName: \"text-blue-600 dark:text-blue-400\",\n\t\t},\n\t\t{\n\t\t\ticon: Image,\n\t\t\tlabel: `${stats.mediaCount} media`,\n\t\t\tclassName: \"text-kumo-subtle\",\n\t\t},\n\t\t{\n\t\t\ticon: Users,\n\t\t\tlabel: `${stats.userCount} user${stats.userCount !== 1 ? \"s\" : \"\"}`,\n\t\t\tclassName: \"text-kumo-subtle\",\n\t\t},\n\t].filter(Boolean) as Array<{\n\t\ticon: React.ElementType;\n\t\tlabel: string;\n\t\tclassName: string;\n\t}>;\n\n\treturn (\n\t\t<div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 rounded-lg border bg-kumo-base px-4 py-2 text-sm\">\n\t\t\t{indicators.map((ind) => (\n\t\t\t\t<span key={ind.label} className={`inline-flex items-center gap-1.5 ${ind.className}`}>\n\t\t\t\t\t<ind.icon className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n\t\t\t\t\t{ind.label}\n\t\t\t\t</span>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n\n// --- Collection list with counts ---\n\nfunction CollectionList({\n\tcollections,\n\tmanifest,\n\tloading,\n}: {\n\tcollections: CollectionStats[];\n\tmanifest: AdminManifest;\n\tloading: boolean;\n}) {\n\treturn (\n\t\t<div className=\"rounded-lg border bg-kumo-base p-4 sm:p-6\">\n\t\t\t<h2 className=\"mb-4 text-lg font-semibold\">Content</h2>\n\t\t\t{loading ? (\n\t\t\t\t<div className=\"space-y-3\">\n\t\t\t\t\t{[1, 2, 3].map((i) => (\n\t\t\t\t\t\t<div key={i} className=\"h-10 animate-pulse rounded-md bg-kumo-tint\" />\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t) : collections.length === 0 ? (\n\t\t\t\t<p className=\"text-sm text-kumo-subtle\">No collections configured</p>\n\t\t\t) : (\n\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t{collections.map((col) => {\n\t\t\t\t\t\tconst config = manifest.collections[col.slug];\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tkey={col.slug}\n\t\t\t\t\t\t\t\tto=\"/content/$collection\"\n\t\t\t\t\t\t\t\tparams={{ collection: col.slug }}\n\t\t\t\t\t\t\t\tsearch={{ locale: undefined }}\n\t\t\t\t\t\t\t\tclassName=\"group flex items-center justify-between rounded-md px-3 py-2 hover:bg-kumo-tint\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">{config?.label ?? col.label}</span>\n\t\t\t\t\t\t\t\t<span className=\"flex items-center gap-3 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t<CountBadge icon={CheckCircle} count={col.published} title=\"Published\" />\n\t\t\t\t\t\t\t\t\t<CountBadge icon={PencilSimple} count={col.draft} title=\"Drafts\" />\n\t\t\t\t\t\t\t\t\t<ArrowRight\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-3.5 w-3.5 opacity-0 transition-opacity group-hover:opacity-100\"\n\t\t\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction CountBadge({\n\ticon: Icon,\n\tcount,\n\ttitle,\n}: {\n\ticon: React.ElementType;\n\tcount: number;\n\ttitle: string;\n}) {\n\tif (count === 0) return null;\n\treturn (\n\t\t<span className=\"inline-flex items-center gap-1\" title={title}>\n\t\t\t<Icon className=\"h-3 w-3\" aria-hidden=\"true\" />\n\t\t\t{count}\n\t\t</span>\n\t);\n}\n\n// --- Recent activity ---\n\nfunction RecentActivity({ items, loading }: { items: RecentItem[]; loading: boolean }) {\n\treturn (\n\t\t<div className=\"rounded-lg border bg-kumo-base p-4 sm:p-6\">\n\t\t\t<h2 className=\"mb-4 text-lg font-semibold\">Recent Activity</h2>\n\t\t\t{loading ? (\n\t\t\t\t<div className=\"space-y-3\">\n\t\t\t\t\t{[1, 2, 3, 4, 5].map((i) => (\n\t\t\t\t\t\t<div key={i} className=\"h-10 animate-pulse rounded-md bg-kumo-tint\" />\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t) : items.length === 0 ? (\n\t\t\t\t<p className=\"text-sm text-kumo-subtle\">No recent activity</p>\n\t\t\t) : (\n\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t{items.map((item) => (\n\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\tkey={`${item.collection}-${item.id}`}\n\t\t\t\t\t\t\tto=\"/content/$collection/$id\"\n\t\t\t\t\t\t\tparams={{ collection: item.collection, id: item.id }}\n\t\t\t\t\t\t\tclassName=\"group flex items-center justify-between gap-2 rounded-md px-3 py-2 hover:bg-kumo-tint\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<div className=\"flex min-w-0 items-center gap-2\">\n\t\t\t\t\t\t\t\t<StatusDot status={item.status} />\n\t\t\t\t\t\t\t\t<span className=\"truncate font-medium\">\n\t\t\t\t\t\t\t\t\t{item.title || item.slug || \"Untitled\"}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t<span className=\"hidden shrink-0 text-xs text-kumo-subtle sm:inline\">\n\t\t\t\t\t\t\t\t\t{item.collectionLabel}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<span className=\"shrink-0 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t{formatRelativeTime(item.updatedAt)}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction StatusDot({ status }: { status: string }) {\n\tconst colors: Record<string, string> = {\n\t\tpublished: \"text-green-500\",\n\t\tdraft: \"text-amber-500\",\n\t\tscheduled: \"text-blue-500\",\n\t};\n\tconst Icon = status === \"published\" ? CheckCircle : CircleDashed;\n\treturn (\n\t\t<Icon\n\t\t\tclassName={`h-3.5 w-3.5 shrink-0 ${colors[status] ?? \"text-kumo-subtle\"}`}\n\t\t\taria-label={status}\n\t\t/>\n\t);\n}\n\n// --- Plugin widgets ---\n\nfunction PluginWidgets({ manifest }: { manifest: AdminManifest }) {\n\tconst widgets: Array<{\n\t\tid: string;\n\t\tpluginId: string;\n\t\ttitle?: string;\n\t\tsize?: \"full\" | \"half\" | \"third\";\n\t}> = [];\n\n\tfor (const [pluginId, plugin] of Object.entries(manifest.plugins || {})) {\n\t\tif (plugin.enabled === false) continue;\n\n\t\tif (\"dashboardWidgets\" in plugin && Array.isArray(plugin.dashboardWidgets)) {\n\t\t\tfor (const widget of plugin.dashboardWidgets) {\n\t\t\t\twidgets.push({\n\t\t\t\t\tid: widget.id,\n\t\t\t\t\tpluginId,\n\t\t\t\t\ttitle: widget.title,\n\t\t\t\t\tsize: widget.size,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\tif (widgets.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn (\n\t\t<div className=\"grid gap-6 lg:grid-cols-2\">\n\t\t\t{widgets.map((widget) => (\n\t\t\t\t<PluginWidgetCard key={`${widget.pluginId}:${widget.id}`} widget={widget} />\n\t\t\t))}\n\t\t</div>\n\t);\n}\n\nfunction PluginWidgetCard({\n\twidget,\n}: {\n\twidget: { id: string; pluginId: string; title?: string; size?: string };\n}) {\n\tconst WidgetComponent = usePluginWidget(widget.pluginId, widget.id);\n\n\treturn (\n\t\t<div className=\"rounded-lg border bg-kumo-base p-4 sm:p-6\">\n\t\t\t<h2 className=\"text-lg font-semibold mb-4\">{widget.title || widget.id}</h2>\n\t\t\t{WidgetComponent ? (\n\t\t\t\t<WidgetComponent />\n\t\t\t) : (\n\t\t\t\t<SandboxedPluginWidget pluginId={widget.pluginId} widgetId={widget.id} />\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * Device Authorization Page\n *\n * Standalone page where users enter the code displayed by `emdash login`\n * to authorize a CLI or agent to access their account.\n *\n * Flow:\n * 1. User runs `emdash login` → sees a code like ABCD-1234\n * 2. User opens this page in their browser (already logged in)\n * 3. User enters the code → clicks Authorize\n * 4. CLI receives tokens and saves them\n */\n\nimport { Button, Input } from \"@cloudflare/kumo\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { apiFetch, API_BASE, parseApiResponse } from \"../lib/api\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface UserInfo {\n\tid: string;\n\temail: string;\n\tname: string | null;\n\trole: number;\n}\n\ntype PageState = \"input\" | \"submitting\" | \"success\" | \"denied\" | \"error\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst ROLE_NAMES: Record<number, string> = {\n\t10: \"Subscriber\",\n\t20: \"Contributor\",\n\t30: \"Author\",\n\t40: \"Editor\",\n\t50: \"Admin\",\n};\n\nconst DEVICE_CODE_INVALID_CHARS_REGEX = /[^A-Z0-9-]/g;\nconst DEVICE_CODE_HYPHEN_REGEX = /-/g;\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function DeviceAuthorizePage() {\n\tconst [code, setCode] = React.useState(\"\");\n\tconst [pageState, setPageState] = React.useState<PageState>(\"input\");\n\tconst [errorMessage, setErrorMessage] = React.useState(\"\");\n\n\t// Check if user is logged in\n\tconst {\n\t\tdata: user,\n\t\tisLoading,\n\t\terror: authError,\n\t} = useQuery<UserInfo>({\n\t\tqueryKey: [\"auth-me\"],\n\t\tqueryFn: async () => {\n\t\t\tconst res = await apiFetch(`${API_BASE}/auth/me`);\n\t\t\treturn parseApiResponse<UserInfo>(res, \"Not authenticated\");\n\t\t},\n\t\tretry: false,\n\t});\n\n\t// Pre-populate from URL query param (?code=ABCD-1234)\n\tReact.useEffect(() => {\n\t\tconst params = new URLSearchParams(window.location.search);\n\t\tconst urlCode = params.get(\"code\");\n\t\tif (urlCode) {\n\t\t\tsetCode(urlCode);\n\t\t}\n\t}, []);\n\n\t// Not authenticated — redirect to login\n\tReact.useEffect(() => {\n\t\tif (!isLoading && (authError || !user)) {\n\t\t\tconst returnUrl = encodeURIComponent(window.location.pathname + window.location.search);\n\t\t\twindow.location.href = `/_emdash/admin/login?redirect=${returnUrl}`;\n\t\t}\n\t}, [isLoading, authError, user]);\n\n\tasync function handleSubmit(e: React.FormEvent) {\n\t\te.preventDefault();\n\n\t\tconst trimmed = code.trim();\n\t\tif (!trimmed) return;\n\n\t\tsetPageState(\"submitting\");\n\t\tsetErrorMessage(\"\");\n\n\t\ttry {\n\t\t\tconst res = await apiFetch(`${API_BASE}/oauth/device/authorize`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ user_code: trimmed, action: \"approve\" }),\n\t\t\t});\n\n\t\t\tconst data = await parseApiResponse<{ authorized: boolean }>(res, \"Authorization failed\");\n\t\t\tsetPageState(data.authorized ? \"success\" : \"denied\");\n\t\t} catch (err) {\n\t\t\tsetErrorMessage(err instanceof Error ? err.message : \"Network error\");\n\t\t\tsetPageState(\"error\");\n\t\t}\n\t}\n\n\tasync function handleDeny(e: React.FormEvent) {\n\t\te.preventDefault();\n\n\t\tconst trimmed = code.trim();\n\t\tif (!trimmed) return;\n\n\t\tsetPageState(\"submitting\");\n\n\t\ttry {\n\t\t\tawait apiFetch(`${API_BASE}/oauth/device/authorize`, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ user_code: trimmed, action: \"deny\" }),\n\t\t\t});\n\t\t\tsetPageState(\"denied\");\n\t\t} catch {\n\t\t\tsetPageState(\"denied\");\n\t\t}\n\t}\n\n\t// Format code as user types (insert hyphen after 4 chars)\n\tfunction handleCodeChange(e: React.ChangeEvent<HTMLInputElement>) {\n\t\tlet value = e.target.value.toUpperCase().replace(DEVICE_CODE_INVALID_CHARS_REGEX, \"\");\n\n\t\t// Auto-insert hyphen after 4 chars if not already present\n\t\tif (value.length === 4 && !value.includes(\"-\")) {\n\t\t\tvalue = value + \"-\";\n\t\t}\n\n\t\t// Limit to 9 chars (XXXX-XXXX)\n\t\tif (value.length > 9) {\n\t\t\tvalue = value.slice(0, 9);\n\t\t}\n\n\t\tsetCode(value);\n\t}\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<PageWrapper>\n\t\t\t\t<p className=\"text-kumo-subtle text-sm\">Checking authentication...</p>\n\t\t\t</PageWrapper>\n\t\t);\n\t}\n\n\tif (!user) {\n\t\treturn (\n\t\t\t<PageWrapper>\n\t\t\t\t<p className=\"text-kumo-subtle text-sm\">Redirecting to login...</p>\n\t\t\t</PageWrapper>\n\t\t);\n\t}\n\n\treturn (\n\t\t<PageWrapper>\n\t\t\t<div className=\"w-full max-w-sm\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"text-center mb-8\">\n\t\t\t\t\t<div className=\"inline-flex items-center justify-center w-12 h-12 rounded-xl bg-kumo-brand/10 mb-4\">\n\t\t\t\t\t\t<TerminalIcon className=\"w-6 h-6 text-kumo-brand\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<h1 className=\"text-xl font-semibold tracking-tight\">Authorize Device</h1>\n\t\t\t\t\t<p className=\"text-kumo-subtle text-sm mt-1.5\">Enter the code from your terminal</p>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Success state */}\n\t\t\t\t{pageState === \"success\" && (\n\t\t\t\t\t<div className=\"rounded-lg border border-green-200 bg-green-50 dark:border-green-900 dark:bg-green-950/50 p-6 text-center\">\n\t\t\t\t\t\t<div className=\"inline-flex items-center justify-center w-10 h-10 rounded-full bg-green-100 dark:bg-green-900/50 mb-3\">\n\t\t\t\t\t\t\t<CheckIcon className=\"w-5 h-5 text-green-600 dark:text-green-400\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<h2 className=\"font-medium text-green-900 dark:text-green-100\">Device authorized</h2>\n\t\t\t\t\t\t<p className=\"text-sm text-green-700 dark:text-green-300 mt-1\">\n\t\t\t\t\t\t\tYou can close this page and return to your terminal.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-3\">Signed in as {user.email}</p>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Denied state */}\n\t\t\t\t{pageState === \"denied\" && (\n\t\t\t\t\t<div className=\"rounded-lg border border-kumo-line p-6 text-center\">\n\t\t\t\t\t\t<h2 className=\"font-medium\">Authorization denied</h2>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">The device will not be granted access.</p>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tclassName=\"mt-4\"\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tsetPageState(\"input\");\n\t\t\t\t\t\t\t\tsetCode(\"\");\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tTry another code\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Input / Error state */}\n\t\t\t\t{(pageState === \"input\" || pageState === \"submitting\" || pageState === \"error\") && (\n\t\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t\t<div className=\"rounded-lg border border-kumo-line bg-kumo-base p-6\">\n\t\t\t\t\t\t\t{/* User badge */}\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 mb-5 pb-4 border-b border-kumo-line\">\n\t\t\t\t\t\t\t\t<div className=\"w-8 h-8 rounded-full bg-kumo-tint flex items-center justify-center text-xs font-medium\">\n\t\t\t\t\t\t\t\t\t{(user.name || user.email).charAt(0).toUpperCase()}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium truncate\">{user.name || user.email}</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">{ROLE_NAMES[user.role] || \"User\"}</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Code input */}\n\t\t\t\t\t\t\t<label className=\"block text-sm font-medium mb-2\" htmlFor=\"user-code\">\n\t\t\t\t\t\t\t\tDevice code\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tid=\"user-code\"\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tvalue={code}\n\t\t\t\t\t\t\t\tonChange={handleCodeChange}\n\t\t\t\t\t\t\t\tplaceholder=\"XXXX-XXXX\"\n\t\t\t\t\t\t\t\tclassName=\"text-center text-lg font-mono tracking-widest\"\n\t\t\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t\tspellCheck={false}\n\t\t\t\t\t\t\t\tdisabled={pageState === \"submitting\"}\n\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t{/* Error message */}\n\t\t\t\t\t\t\t{pageState === \"error\" && errorMessage && (\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-danger mt-2\">{errorMessage}</p>\n\t\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t\t{/* Actions */}\n\t\t\t\t\t\t\t<div className=\"flex gap-2 mt-4\">\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tcode.replace(DEVICE_CODE_HYPHEN_REGEX, \"\").length < 8 ||\n\t\t\t\t\t\t\t\t\t\tpageState === \"submitting\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{pageState === \"submitting\" ? \"Authorizing...\" : \"Authorize\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tonClick={handleDeny}\n\t\t\t\t\t\t\t\t\tdisabled={\n\t\t\t\t\t\t\t\t\t\tcode.replace(DEVICE_CODE_HYPHEN_REGEX, \"\").length < 8 ||\n\t\t\t\t\t\t\t\t\t\tpageState === \"submitting\"\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tDeny\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle text-center mt-4\">\n\t\t\t\t\t\t\tThis will grant CLI access with your permissions.\n\t\t\t\t\t\t\t<br />\n\t\t\t\t\t\t\tOnly authorize codes you recognize.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</PageWrapper>\n\t);\n}\n\n// ============================================================================\n// Layout wrapper\n// ============================================================================\n\nfunction PageWrapper({ children }: { children: React.ReactNode }) {\n\treturn (\n\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base p-4\">\n\t\t\t<div className=\"w-full max-w-sm\">{children}</div>\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Icons (inline SVG to avoid dependency on icon library for this simple page)\n// ============================================================================\n\nfunction TerminalIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth={2}\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t>\n\t\t\t<polyline points=\"4 17 10 11 4 5\" />\n\t\t\t<line x1=\"12\" y1=\"19\" x2=\"20\" y2=\"19\" />\n\t\t</svg>\n\t);\n}\n\nfunction CheckIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg\n\t\t\tclassName={className}\n\t\t\tviewBox=\"0 0 24 24\"\n\t\t\tfill=\"none\"\n\t\t\tstroke=\"currentColor\"\n\t\t\tstrokeWidth={2}\n\t\t\tstrokeLinecap=\"round\"\n\t\t\tstrokeLinejoin=\"round\"\n\t\t>\n\t\t\t<polyline points=\"20 6 9 17 4 12\" />\n\t\t</svg>\n\t);\n}\n","/**\n * Shared URL validation and transformation utilities\n */\n\nconst DEFAULT_REDIRECT = \"/_emdash/admin\";\n\n/**\n * Sanitize a redirect URL to prevent open-redirect and javascript: XSS attacks.\n *\n * Only allows relative paths starting with `/`. Rejects protocol-relative\n * URLs (`//evil.com`), backslash tricks (`/\\evil.com`), and non-path schemes\n * like `javascript:`.\n *\n * Returns the default admin URL when the input is unsafe.\n */\nexport function sanitizeRedirectUrl(raw: string): string {\n\tif (raw.startsWith(\"/\") && !raw.startsWith(\"//\") && !raw.includes(\"\\\\\")) {\n\t\treturn raw;\n\t}\n\treturn DEFAULT_REDIRECT;\n}\n\n/** Matches http:// or https:// URLs */\nexport const SAFE_URL_RE = /^https?:\\/\\//i;\n\n/** Returns true if the URL uses a safe scheme (http/https) */\nexport function isSafeUrl(url: string): boolean {\n\treturn SAFE_URL_RE.test(url);\n}\n\n/**\n * Build an icon URL with a width query param, or return null for unsafe URLs.\n * Validates the URL scheme and appends `?w=<width>` for image resizing.\n */\nexport function safeIconUrl(url: string, width: number): string | null {\n\tif (!SAFE_URL_RE.test(url)) return null;\n\ttry {\n\t\tconst u = new URL(url);\n\t\tu.searchParams.set(\"w\", String(width));\n\t\treturn u.href;\n\t} catch {\n\t\treturn null;\n\t}\n}\n","/**\n * PasskeyLogin - WebAuthn authentication component\n *\n * Handles the passkey login flow:\n * 1. Fetches authentication options from server\n * 2. Triggers browser's WebAuthn credential assertion\n * 3. Sends assertion back to server for verification\n *\n * Supports:\n * - Discoverable credentials (passkey autofill)\n * - Non-discoverable credentials (email-first flow)\n */\n\nimport { Button, Input } from \"@cloudflare/kumo\";\nimport * as React from \"react\";\n\nimport { apiFetch, parseApiResponse } from \"../../lib/api/client\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst BASE64URL_DASH_REGEX = /-/g;\nconst BASE64URL_UNDERSCORE_REGEX = /_/g;\nconst BASE64_PLUS_REGEX = /\\+/g;\nconst BASE64_SLASH_REGEX = /\\//g;\n\n// ============================================================================\n// WebAuthn types\n// ============================================================================\ninterface PublicKeyCredentialRequestOptionsJSON {\n\tchallenge: string;\n\trpId: string;\n\ttimeout?: number;\n\tuserVerification?: \"discouraged\" | \"preferred\" | \"required\";\n\tallowCredentials?: Array<{\n\t\ttype: \"public-key\";\n\t\tid: string;\n\t\ttransports?: AuthenticatorTransport[];\n\t}>;\n}\n\ninterface AuthenticationResponse {\n\tid: string;\n\trawId: string;\n\ttype: \"public-key\";\n\tresponse: {\n\t\tclientDataJSON: string;\n\t\tauthenticatorData: string;\n\t\tsignature: string;\n\t\tuserHandle?: string;\n\t};\n\tauthenticatorAttachment?: \"platform\" | \"cross-platform\";\n}\n\nexport interface PasskeyLoginProps {\n\t/** Endpoint to get authentication options */\n\toptionsEndpoint: string;\n\t/** Endpoint to verify authentication */\n\tverifyEndpoint: string;\n\t/** Called on successful authentication */\n\tonSuccess: (response: unknown) => void;\n\t/** Called on error */\n\tonError?: (error: Error) => void;\n\t/** Show email input for non-discoverable flow */\n\tshowEmailInput?: boolean;\n\t/** Button text */\n\tbuttonText?: string;\n}\n\ntype LoginState =\n\t| { status: \"idle\" }\n\t| { status: \"loading\"; message: string }\n\t| { status: \"error\"; message: string }\n\t| { status: \"success\" };\n\n/**\n * Check if WebAuthn is supported in the current browser\n */\nfunction isWebAuthnSupported(): boolean {\n\treturn (\n\t\ttypeof window !== \"undefined\" &&\n\t\twindow.PublicKeyCredential !== undefined &&\n\t\ttypeof window.PublicKeyCredential === \"function\"\n\t);\n}\n\n/**\n * Check if conditional mediation (autofill) is supported\n */\nasync function isConditionalMediationSupported(): Promise<boolean> {\n\tif (!isWebAuthnSupported()) return false;\n\ttry {\n\t\treturn (await PublicKeyCredential.isConditionalMediationAvailable?.()) ?? false;\n\t} catch {\n\t\treturn false;\n\t}\n}\n\n/**\n * Convert base64url to ArrayBuffer\n */\nfunction base64urlToBuffer(base64url: string): ArrayBuffer {\n\tconst base64 = base64url\n\t\t.replace(BASE64URL_DASH_REGEX, \"+\")\n\t\t.replace(BASE64URL_UNDERSCORE_REGEX, \"/\");\n\tconst padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n\tconst binary = atob(base64 + padding);\n\tconst bytes = new Uint8Array(binary.length);\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i);\n\t}\n\treturn bytes.buffer;\n}\n\n/**\n * Convert ArrayBuffer to base64url (with padding for @oslojs/encoding compatibility)\n */\nfunction bufferToBase64url(buffer: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(buffer);\n\tlet binary = \"\";\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbinary += String.fromCharCode(bytes[i]!);\n\t}\n\tconst base64 = btoa(binary);\n\t// Convert to base64url but keep padding (required by @oslojs/encoding)\n\treturn base64.replace(BASE64_PLUS_REGEX, \"-\").replace(BASE64_SLASH_REGEX, \"_\");\n}\n\n/**\n * PasskeyLogin Component\n */\nexport function PasskeyLogin({\n\toptionsEndpoint,\n\tverifyEndpoint,\n\tonSuccess,\n\tonError,\n\tshowEmailInput = false,\n\tbuttonText = \"Sign in with Passkey\",\n}: PasskeyLoginProps) {\n\tconst [state, setState] = React.useState<LoginState>({ status: \"idle\" });\n\tconst [email, setEmail] = React.useState(\"\");\n\tconst [supportsConditional, setSupportsConditional] = React.useState(false);\n\n\t// Check WebAuthn support on mount\n\tconst isSupported = React.useMemo(() => isWebAuthnSupported(), []);\n\n\t// Check conditional mediation support\n\tReact.useEffect(() => {\n\t\tvoid isConditionalMediationSupported().then(setSupportsConditional);\n\t}, []);\n\n\tconst handleLogin = React.useCallback(\n\t\tasync (useConditional = false) => {\n\t\t\tif (!isSupported) {\n\t\t\t\tsetState({\n\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\tmessage: \"WebAuthn is not supported in this browser\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\t// Step 1: Get authentication options from server\n\t\t\t\tsetState({ status: \"loading\", message: \"Preparing...\" });\n\n\t\t\t\tconst optionsResponse = await apiFetch(optionsEndpoint, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify({ email: email || undefined }),\n\t\t\t\t});\n\n\t\t\t\tconst optionsData = await parseApiResponse<{\n\t\t\t\t\toptions: PublicKeyCredentialRequestOptionsJSON;\n\t\t\t\t}>(optionsResponse, \"Failed to get authentication options\");\n\t\t\t\tconst { options } = optionsData;\n\n\t\t\t\t// Step 2: Get assertion from browser\n\t\t\t\tsetState({ status: \"loading\", message: \"Waiting for passkey...\" });\n\n\t\t\t\t// Convert options to the format expected by the browser\n\t\t\t\tconst publicKeyOptions: PublicKeyCredentialRequestOptions = {\n\t\t\t\t\tchallenge: base64urlToBuffer(options.challenge),\n\t\t\t\t\trpId: options.rpId,\n\t\t\t\t\ttimeout: options.timeout,\n\t\t\t\t\tuserVerification: options.userVerification,\n\t\t\t\t\tallowCredentials: options.allowCredentials?.map((cred) => ({\n\t\t\t\t\t\ttype: cred.type,\n\t\t\t\t\t\tid: base64urlToBuffer(cred.id),\n\t\t\t\t\t\ttransports: cred.transports,\n\t\t\t\t\t})),\n\t\t\t\t};\n\n\t\t\t\tconst credentialOptions: CredentialRequestOptions = {\n\t\t\t\t\tpublicKey: publicKeyOptions,\n\t\t\t\t\t// Use conditional mediation if supported and requested\n\t\t\t\t\t...(useConditional && supportsConditional\n\t\t\t\t\t\t? { mediation: \"conditional\" as CredentialMediationRequirement }\n\t\t\t\t\t\t: {}),\n\t\t\t\t};\n\n\t\t\t\tconst rawCredential = await navigator.credentials.get(credentialOptions);\n\n\t\t\t\tif (!rawCredential) {\n\t\t\t\t\tthrow new Error(\"No credential returned from authenticator\");\n\t\t\t\t}\n\n\t\t\t\t// Step 3: Send credential to server for verification\n\t\t\t\tsetState({ status: \"loading\", message: \"Verifying...\" });\n\n\t\t\t\t// navigator.credentials.get() with publicKey returns PublicKeyCredential\n\t\t\t\tconst credential = rawCredential as PublicKeyCredential;\n\t\t\t\tconst assertionResponse = credential.response as AuthenticatorAssertionResponse;\n\n\t\t\t\t// authenticatorAttachment exists at runtime on PublicKeyCredential but isn't in the base type definition\n\t\t\t\tconst rawAttachment =\n\t\t\t\t\t\"authenticatorAttachment\" in credential ? credential.authenticatorAttachment : undefined;\n\t\t\t\tconst authenticatorAttachment =\n\t\t\t\t\trawAttachment === \"platform\" || rawAttachment === \"cross-platform\"\n\t\t\t\t\t\t? rawAttachment\n\t\t\t\t\t\t: undefined;\n\n\t\t\t\tconst authenticationResponse: AuthenticationResponse = {\n\t\t\t\t\tid: credential.id,\n\t\t\t\t\trawId: bufferToBase64url(credential.rawId),\n\t\t\t\t\ttype: \"public-key\",\n\t\t\t\t\tresponse: {\n\t\t\t\t\t\tclientDataJSON: bufferToBase64url(assertionResponse.clientDataJSON),\n\t\t\t\t\t\tauthenticatorData: bufferToBase64url(assertionResponse.authenticatorData),\n\t\t\t\t\t\tsignature: bufferToBase64url(assertionResponse.signature),\n\t\t\t\t\t\tuserHandle: assertionResponse.userHandle\n\t\t\t\t\t\t\t? bufferToBase64url(assertionResponse.userHandle)\n\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t},\n\t\t\t\t\tauthenticatorAttachment,\n\t\t\t\t};\n\n\t\t\t\tconst verifyResponse = await apiFetch(verifyEndpoint, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify({ credential: authenticationResponse }),\n\t\t\t\t});\n\n\t\t\t\tconst result = await parseApiResponse<unknown>(\n\t\t\t\t\tverifyResponse,\n\t\t\t\t\t\"Failed to verify authentication\",\n\t\t\t\t);\n\n\t\t\t\tsetState({ status: \"success\" });\n\t\t\t\tonSuccess(result);\n\t\t\t} catch (error) {\n\t\t\t\tconst message = error instanceof Error ? error.message : \"Authentication failed\";\n\n\t\t\t\t// Handle specific WebAuthn errors\n\t\t\t\tlet userMessage = message;\n\t\t\t\tif (error instanceof DOMException) {\n\t\t\t\t\tswitch (error.name) {\n\t\t\t\t\t\tcase \"NotAllowedError\":\n\t\t\t\t\t\t\tuserMessage = \"Authentication was cancelled or timed out. Please try again.\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"InvalidStateError\":\n\t\t\t\t\t\t\tuserMessage = \"No matching passkey found for this account.\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"NotSupportedError\":\n\t\t\t\t\t\t\tuserMessage = \"Your device doesn't support the required security features.\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"SecurityError\":\n\t\t\t\t\t\t\tuserMessage = \"Security error. Make sure you're on a secure connection.\";\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"AbortError\":\n\t\t\t\t\t\t\t// User cancelled - don't show error\n\t\t\t\t\t\t\tsetState({ status: \"idle\" });\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\tdefault:\n\t\t\t\t\t\t\tuserMessage = `Authentication error: ${error.message}`;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tsetState({ status: \"error\", message: userMessage });\n\t\t\t\tonError?.(new Error(userMessage));\n\t\t\t}\n\t\t},\n\t\t[isSupported, optionsEndpoint, verifyEndpoint, email, supportsConditional, onSuccess, onError],\n\t);\n\n\t// Not supported message\n\tif (!isSupported) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-4\">\n\t\t\t\t<h3 className=\"font-medium text-kumo-danger\">Passkeys Not Supported</h3>\n\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\tYour browser doesn't support passkeys. Please use a modern browser like Chrome, Safari,\n\t\t\t\t\tFirefox, or Edge.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Email input (optional - for non-discoverable credentials) */}\n\t\t\t{showEmailInput && (\n\t\t\t\t<div>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Email (optional)\"\n\t\t\t\t\t\ttype=\"email\"\n\t\t\t\t\t\tvalue={email}\n\t\t\t\t\t\tonChange={(e) => setEmail(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"you@example.com\"\n\t\t\t\t\t\tdisabled={state.status === \"loading\"}\n\t\t\t\t\t\tautoComplete=\"username webauthn\"\n\t\t\t\t\t/>\n\t\t\t\t\t<p className=\"mt-1 text-xs text-kumo-subtle\">\n\t\t\t\t\t\tLeave blank to use a discoverable passkey.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Error message */}\n\t\t\t{state.status === \"error\" && (\n\t\t\t\t<div className=\"rounded-lg bg-kumo-danger/10 p-4 text-sm text-kumo-danger\">\n\t\t\t\t\t{state.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Login button */}\n\t\t\t<Button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={() => handleLogin(false)}\n\t\t\t\tloading={state.status === \"loading\"}\n\t\t\t\tclassName=\"w-full justify-center\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t>\n\t\t\t\t{state.status === \"loading\" ? <>{state.message}</> : buttonText}\n\t\t\t</Button>\n\n\t\t\t{/* Help text */}\n\t\t\t<p className=\"text-xs text-kumo-subtle text-center\">\n\t\t\t\tUse your device's biometric authentication, security key, or PIN to sign in.\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n","/**\n * Login Page - Standalone login page for the admin\n *\n * This component is NOT wrapped in the admin Shell.\n * It's a standalone page for authentication.\n *\n * Supports:\n * - Passkey authentication (primary)\n * - OAuth (GitHub, Google) when configured\n * - Magic link (email) when configured\n *\n * When external auth (e.g., Cloudflare Access) is configured, this page\n * redirects to the admin dashboard since authentication is handled externally.\n */\n\nimport { Button, Input, Loader } from \"@cloudflare/kumo\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { apiFetch, fetchManifest } from \"../lib/api\";\nimport { sanitizeRedirectUrl } from \"../lib/url\";\nimport { PasskeyLogin } from \"./auth/PasskeyLogin\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface LoginPageProps {\n\t/** URL to redirect to after successful login */\n\tredirectUrl?: string;\n}\n\ntype LoginMethod = \"passkey\" | \"magic-link\";\n\ninterface OAuthProvider {\n\tid: string;\n\tname: string;\n\ticon: React.ReactNode;\n}\n\n// ============================================================================\n// OAuth Icons\n// ============================================================================\n\nfunction GitHubIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg className={className} viewBox=\"0 0 24 24\" fill=\"currentColor\">\n\t\t\t<path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\" />\n\t\t</svg>\n\t);\n}\n\nfunction GoogleIcon({ className }: { className?: string }) {\n\treturn (\n\t\t<svg className={className} viewBox=\"0 0 24 24\">\n\t\t\t<path\n\t\t\t\tfill=\"#4285F4\"\n\t\t\t\td=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"#34A853\"\n\t\t\t\td=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"#FBBC05\"\n\t\t\t\td=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\"\n\t\t\t/>\n\t\t\t<path\n\t\t\t\tfill=\"#EA4335\"\n\t\t\t\td=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\"\n\t\t\t/>\n\t\t</svg>\n\t);\n}\n\n// ============================================================================\n// OAuth Providers\n// ============================================================================\n\nconst OAUTH_PROVIDERS: OAuthProvider[] = [\n\t{\n\t\tid: \"github\",\n\t\tname: \"GitHub\",\n\t\ticon: <GitHubIcon className=\"h-5 w-5\" />,\n\t},\n\t{\n\t\tid: \"google\",\n\t\tname: \"Google\",\n\t\ticon: <GoogleIcon className=\"h-5 w-5\" />,\n\t},\n];\n\n// ============================================================================\n// Components\n// ============================================================================\n\ninterface MagicLinkFormProps {\n\tonBack: () => void;\n}\n\nfunction MagicLinkForm({ onBack }: MagicLinkFormProps) {\n\tconst [email, setEmail] = React.useState(\"\");\n\tconst [isLoading, setIsLoading] = React.useState(false);\n\tconst [error, setError] = React.useState<string | null>(null);\n\tconst [sent, setSent] = React.useState(false);\n\n\tconst handleSubmit = async (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tsetIsLoading(true);\n\n\t\ttry {\n\t\t\tconst response = await apiFetch(\"/_emdash/api/auth/magic-link/send\", {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({ email: email.trim().toLowerCase() }),\n\t\t\t});\n\n\t\t\tif (!response.ok) {\n\t\t\t\tconst body: { error?: { message?: string } } = await response.json().catch(() => ({}));\n\t\t\t\tthrow new Error(body?.error?.message || \"Failed to send magic link\");\n\t\t\t}\n\n\t\t\tsetSent(true);\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof Error ? err.message : \"Failed to send magic link\");\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t};\n\n\tif (sent) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6 text-center\">\n\t\t\t\t<div className=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-kumo-brand/10 mx-auto\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\tclassName=\"w-8 h-8 text-kumo-brand\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\td=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t</div>\n\n\t\t\t\t<div>\n\t\t\t\t\t<h2 className=\"text-xl font-semibold\">Check your email</h2>\n\t\t\t\t\t<p className=\"text-kumo-subtle mt-2\">\n\t\t\t\t\t\tIf an account exists for <span className=\"font-medium text-kumo-default\">{email}</span>,\n\t\t\t\t\t\twe've sent a sign-in link.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t<p>Click the link in the email to sign in.</p>\n\t\t\t\t\t<p className=\"mt-2\">The link will expire in 15 minutes.</p>\n\t\t\t\t</div>\n\n\t\t\t\t<Button variant=\"outline\" onClick={onBack} className=\"mt-4 w-full justify-center\">\n\t\t\t\t\tBack to login\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} className=\"space-y-4\">\n\t\t\t<Input\n\t\t\t\tlabel=\"Email address\"\n\t\t\t\ttype=\"email\"\n\t\t\t\tvalue={email}\n\t\t\t\tonChange={(e) => setEmail(e.target.value)}\n\t\t\t\tplaceholder=\"you@example.com\"\n\t\t\t\tclassName={error ? \"border-kumo-danger\" : \"\"}\n\t\t\t\tdisabled={isLoading}\n\t\t\t\tautoComplete=\"email\"\n\t\t\t\tautoFocus\n\t\t\t\trequired\n\t\t\t/>\n\n\t\t\t{error && (\n\t\t\t\t<div className=\"rounded-lg bg-kumo-danger/10 p-3 text-sm text-kumo-danger\">{error}</div>\n\t\t\t)}\n\n\t\t\t<Button\n\t\t\t\ttype=\"submit\"\n\t\t\t\tclassName=\"w-full justify-center\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t\tloading={isLoading}\n\t\t\t\tdisabled={!email}\n\t\t\t>\n\t\t\t\t{isLoading ? \"Sending...\" : \"Send magic link\"}\n\t\t\t</Button>\n\n\t\t\t<Button type=\"button\" variant=\"ghost\" className=\"w-full justify-center\" onClick={onBack}>\n\t\t\t\tBack to login\n\t\t\t</Button>\n\t\t</form>\n\t);\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nfunction handleOAuthClick(providerId: string) {\n\t// Redirect to OAuth endpoint\n\twindow.location.href = `/_emdash/api/auth/oauth/${providerId}`;\n}\n\nexport function LoginPage({ redirectUrl = \"/_emdash/admin\" }: LoginPageProps) {\n\t// Defense-in-depth: sanitize even if the caller already validated\n\tconst safeRedirectUrl = sanitizeRedirectUrl(redirectUrl);\n\tconst [method, setMethod] = React.useState<LoginMethod>(\"passkey\");\n\tconst [urlError, setUrlError] = React.useState<string | null>(null);\n\n\t// Fetch manifest to check auth mode\n\tconst { data: manifest, isLoading: manifestLoading } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\t// Redirect to admin when using external auth (authentication is handled externally)\n\tReact.useEffect(() => {\n\t\tif (manifest?.authMode && manifest.authMode !== \"passkey\") {\n\t\t\twindow.location.href = safeRedirectUrl;\n\t\t}\n\t}, [manifest, safeRedirectUrl]);\n\n\t// Check for error in URL (from OAuth redirect)\n\tReact.useEffect(() => {\n\t\tconst params = new URLSearchParams(window.location.search);\n\t\tconst error = params.get(\"error\");\n\t\tconst message = params.get(\"message\");\n\n\t\tif (error) {\n\t\t\tsetUrlError(message || `Authentication error: ${error}`);\n\t\t\t// Clean up URL\n\t\t\twindow.history.replaceState({}, \"\", window.location.pathname);\n\t\t}\n\t}, []);\n\n\tconst handleSuccess = () => {\n\t\t// Redirect after successful login\n\t\twindow.location.href = safeRedirectUrl;\n\t};\n\n\t// Show loading state while checking auth mode\n\tif (manifestLoading || (manifest?.authMode && manifest.authMode !== \"passkey\")) {\n\t\treturn (\n\t\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base p-4\">\n\t\t\t\t<div className=\"text-center\">\n\t\t\t\t\t<div className=\"text-4xl font-bold mb-4\">💫 EmDash</div>\n\t\t\t\t\t<Loader />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base p-4\">\n\t\t\t<div className=\"w-full max-w-md\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"text-center mb-8\">\n\t\t\t\t\t<div className=\"text-4xl font-bold mb-2\">💫 EmDash</div>\n\t\t\t\t\t<h1 className=\"text-2xl font-semibold text-kumo-default\">\n\t\t\t\t\t\t{method === \"passkey\" && \"Sign in to your site\"}\n\t\t\t\t\t\t{method === \"magic-link\" && \"Sign in with email\"}\n\t\t\t\t\t</h1>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Error from URL (OAuth failure) */}\n\t\t\t\t{urlError && (\n\t\t\t\t\t<div className=\"mb-6 rounded-lg bg-kumo-danger/10 border border-kumo-danger/20 p-4 text-sm text-kumo-danger\">\n\t\t\t\t\t\t{urlError}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Login Card */}\n\t\t\t\t<div className=\"bg-kumo-base border rounded-lg shadow-sm p-6\">\n\t\t\t\t\t{method === \"passkey\" && (\n\t\t\t\t\t\t<div className=\"space-y-6\">\n\t\t\t\t\t\t\t{/* Passkey Login */}\n\t\t\t\t\t\t\t<PasskeyLogin\n\t\t\t\t\t\t\t\toptionsEndpoint=\"/_emdash/api/auth/passkey/options\"\n\t\t\t\t\t\t\t\tverifyEndpoint=\"/_emdash/api/auth/passkey/verify\"\n\t\t\t\t\t\t\t\tonSuccess={handleSuccess}\n\t\t\t\t\t\t\t\tbuttonText=\"Sign in with Passkey\"\n\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t{/* Divider */}\n\t\t\t\t\t\t\t<div className=\"relative\">\n\t\t\t\t\t\t\t\t<div className=\"absolute inset-0 flex items-center\">\n\t\t\t\t\t\t\t\t\t<span className=\"w-full border-t\" />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"relative flex justify-center text-xs uppercase\">\n\t\t\t\t\t\t\t\t\t<span className=\"bg-kumo-base px-2 text-kumo-subtle\">Or continue with</span>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* OAuth Providers */}\n\t\t\t\t\t\t\t<div className=\"grid grid-cols-2 gap-3\">\n\t\t\t\t\t\t\t\t{OAUTH_PROVIDERS.map((provider) => (\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tkey={provider.id}\n\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => handleOAuthClick(provider.id)}\n\t\t\t\t\t\t\t\t\t\tclassName=\"w-full justify-center\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{provider.icon}\n\t\t\t\t\t\t\t\t\t\t<span className=\"ml-2\">{provider.name}</span>\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Magic Link Option */}\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tclassName=\"w-full justify-center\"\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => setMethod(\"magic-link\")}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tSign in with email link\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{method === \"magic-link\" && <MagicLinkForm onBack={() => setMethod(\"passkey\")} />}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Help text */}\n\t\t\t\t<p className=\"text-center mt-6 text-sm text-kumo-subtle\">\n\t\t\t\t\t{method === \"passkey\"\n\t\t\t\t\t\t? \"Use your registered passkey to sign in securely.\"\n\t\t\t\t\t\t: \"We'll send you a link to sign in without a password.\"}\n\t\t\t\t</p>\n\n\t\t\t\t{/* Signup link — only shown when self-signup is enabled */}\n\t\t\t\t{manifest?.signupEnabled && (\n\t\t\t\t\t<p className=\"text-center mt-4 text-sm text-kumo-subtle\">\n\t\t\t\t\t\tDon't have an account?{\" \"}\n\t\t\t\t\t\t<Link to=\"/signup\" className=\"text-kumo-brand hover:underline font-medium\">\n\t\t\t\t\t\t\tSign up\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * Marketplace Browse\n *\n * Grid of plugin cards with search and sorting.\n * Navigates to plugin detail on card click.\n */\n\nimport { Badge, Button } from \"@cloudflare/kumo\";\nimport {\n\tMagnifyingGlass,\n\tPuzzlePiece,\n\tDownloadSimple,\n\tShieldCheck,\n\tShieldWarning,\n\tWarning,\n\tArrowsClockwise,\n} from \"@phosphor-icons/react\";\nimport { useInfiniteQuery } from \"@tanstack/react-query\";\nimport { Link, useNavigate } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tCAPABILITY_LABELS,\n\tsearchMarketplace,\n\ttype MarketplacePluginSummary,\n\ttype MarketplaceSearchOpts,\n} from \"../lib/api/marketplace.js\";\nimport { safeIconUrl } from \"../lib/url.js\";\n\ntype SortOption = \"installs\" | \"updated\" | \"created\" | \"name\";\n\nconst SORT_OPTIONS = new Set<string>([\"installs\", \"updated\", \"created\", \"name\"]);\n\nfunction isSortOption(value: string): value is SortOption {\n\treturn SORT_OPTIONS.has(value);\n}\n\nconst SORT_LABELS: Record<SortOption, string> = {\n\tinstalls: \"Most Popular\",\n\tupdated: \"Recently Updated\",\n\tcreated: \"Newest\",\n\tname: \"Name\",\n};\n\nexport interface MarketplaceBrowseProps {\n\t/** IDs of plugins already installed on this site */\n\tinstalledPluginIds?: Set<string>;\n}\n\nexport function MarketplaceBrowse({ installedPluginIds = new Set() }: MarketplaceBrowseProps) {\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst [sort, setSort] = React.useState<SortOption>(\"installs\");\n\tconst [capability, setCapability] = React.useState<string>(\"\");\n\tconst [debouncedQuery, setDebouncedQuery] = React.useState(\"\");\n\n\t// Debounce search input\n\tReact.useEffect(() => {\n\t\tconst timer = setTimeout(setDebouncedQuery, 300, searchQuery);\n\t\treturn () => clearTimeout(timer);\n\t}, [searchQuery]);\n\n\tconst searchOpts: MarketplaceSearchOpts = {\n\t\tq: debouncedQuery || undefined,\n\t\tcapability: capability || undefined,\n\t\tsort,\n\t\tlimit: 20,\n\t};\n\n\tconst { data, isLoading, error, refetch, fetchNextPage, hasNextPage, isFetchingNextPage } =\n\t\tuseInfiniteQuery({\n\t\t\tqueryKey: [\"marketplace\", \"search\", searchOpts],\n\t\t\tqueryFn: ({ pageParam }) => searchMarketplace({ ...searchOpts, cursor: pageParam }),\n\t\t\tinitialPageParam: undefined as string | undefined,\n\t\t\tgetNextPageParam: (lastPage) => lastPage.nextCursor,\n\t\t});\n\n\tconst plugins = data?.pages.flatMap((p) => p.items);\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div>\n\t\t\t\t<h1 className=\"text-3xl font-bold\">Marketplace</h1>\n\t\t\t\t<p className=\"mt-1 text-kumo-subtle\">Browse and install plugins to extend your site.</p>\n\t\t\t</div>\n\n\t\t\t{/* Search + Sort */}\n\t\t\t<div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n\t\t\t\t<div className=\"relative flex-1\">\n\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-kumo-subtle\" />\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tplaceholder=\"Search plugins...\"\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\tclassName=\"w-full rounded-md border bg-kumo-base px-3 py-2 pl-9 text-sm placeholder:text-kumo-subtle focus:outline-none focus:ring-2 focus:ring-kumo-ring\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<select\n\t\t\t\t\tvalue={capability}\n\t\t\t\t\tonChange={(e) => setCapability(e.target.value)}\n\t\t\t\t\tclassName=\"rounded-md border bg-kumo-base px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-kumo-ring\"\n\t\t\t\t\taria-label=\"Filter by capability\"\n\t\t\t\t>\n\t\t\t\t\t<option value=\"\">All capabilities</option>\n\t\t\t\t\t{Object.entries(CAPABILITY_LABELS).map(([value, label]) => (\n\t\t\t\t\t\t<option key={value} value={value}>\n\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t</option>\n\t\t\t\t\t))}\n\t\t\t\t</select>\n\t\t\t\t<select\n\t\t\t\t\tvalue={sort}\n\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\tconst v = e.target.value;\n\t\t\t\t\t\tif (isSortOption(v)) setSort(v);\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"rounded-md border bg-kumo-base px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-kumo-ring\"\n\t\t\t\t\taria-label=\"Sort plugins\"\n\t\t\t\t>\n\t\t\t\t\t{Object.entries(SORT_LABELS).map(([value, label]) => (\n\t\t\t\t\t\t<option key={value} value={value}>\n\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t</option>\n\t\t\t\t\t))}\n\t\t\t\t</select>\n\t\t\t</div>\n\n\t\t\t{/* Error state */}\n\t\t\t{error && (\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-6 text-center\">\n\t\t\t\t\t<Warning className=\"mx-auto h-8 w-8 text-kumo-danger\" />\n\t\t\t\t\t<h3 className=\"mt-3 font-medium text-kumo-danger\">Unable to reach marketplace</h3>\n\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{error instanceof Error ? error.message : \"An error occurred\"}\n\t\t\t\t\t</p>\n\t\t\t\t\t<Button variant=\"ghost\" className=\"mt-4\" onClick={() => void refetch()}>\n\t\t\t\t\t\t<ArrowsClockwise className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Loading state */}\n\t\t\t{isLoading && (\n\t\t\t\t<div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n\t\t\t\t\t{Array.from({ length: 6 }).map((_, i) => (\n\t\t\t\t\t\t<div key={i} className=\"animate-pulse rounded-lg border bg-kumo-base p-4\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t<div className=\"h-10 w-10 rounded-lg bg-kumo-tint\" />\n\t\t\t\t\t\t\t\t<div className=\"flex-1 space-y-2\">\n\t\t\t\t\t\t\t\t\t<div className=\"h-4 w-24 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t\t\t<div className=\"h-3 w-16 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"mt-3 space-y-2\">\n\t\t\t\t\t\t\t\t<div className=\"h-3 w-full rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t\t<div className=\"h-3 w-2/3 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Results grid */}\n\t\t\t{plugins && !isLoading && (\n\t\t\t\t<>\n\t\t\t\t\t{plugins.length === 0 ? (\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-8 text-center\">\n\t\t\t\t\t\t\t<PuzzlePiece className=\"mx-auto h-12 w-12 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<h3 className=\"mt-4 text-lg font-medium\">No plugins found</h3>\n\t\t\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t{debouncedQuery\n\t\t\t\t\t\t\t\t\t? `No results for \"${debouncedQuery}\". Try a different search term.`\n\t\t\t\t\t\t\t\t\t: \"The marketplace is empty. Check back later for new plugins.\"}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n\t\t\t\t\t\t\t\t{plugins.map((plugin) => (\n\t\t\t\t\t\t\t\t\t<PluginCard\n\t\t\t\t\t\t\t\t\t\tkey={plugin.id}\n\t\t\t\t\t\t\t\t\t\tplugin={plugin}\n\t\t\t\t\t\t\t\t\t\tisInstalled={installedPluginIds.has(plugin.id)}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{hasNextPage && (\n\t\t\t\t\t\t\t\t<div className=\"flex justify-center\">\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => void fetchNextPage()}\n\t\t\t\t\t\t\t\t\t\tdisabled={isFetchingNextPage}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isFetchingNextPage ? \"Loading...\" : \"Load more\"}\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// PluginCard\n// ---------------------------------------------------------------------------\n\ninterface PluginCardProps {\n\tplugin: MarketplacePluginSummary;\n\tisInstalled: boolean;\n}\n\nfunction PluginCard({ plugin, isInstalled }: PluginCardProps) {\n\tconst navigate = useNavigate();\n\tconst auditVerdict = plugin.latestVersion?.audit?.verdict;\n\tconst imageVerdict = plugin.latestVersion?.imageAudit?.verdict;\n\tconst isImageFlagged = imageVerdict === \"warn\" || imageVerdict === \"fail\";\n\tconst iconSrc = plugin.iconUrl ? safeIconUrl(plugin.iconUrl, 64) : null;\n\n\treturn (\n\t\t<Link\n\t\t\tto=\"/plugins/marketplace/$pluginId\"\n\t\t\tparams={{ pluginId: plugin.id }}\n\t\t\tclassName=\"group block rounded-lg border bg-kumo-base p-4 transition-colors hover:border-kumo-brand/50 hover:bg-kumo-tint/30\"\n\t\t>\n\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t{/* Icon */}\n\t\t\t\t{iconSrc ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={iconSrc}\n\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\tclassName={`h-10 w-10 rounded-lg object-cover ${isImageFlagged ? \"blur-sm\" : \"\"}`}\n\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\taria-label={isImageFlagged ? \"Icon blurred due to image audit\" : undefined}\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<PluginAvatar name={plugin.name} />\n\t\t\t\t)}\n\n\t\t\t\t{/* Name + meta */}\n\t\t\t\t<div className=\"min-w-0 flex-1\">\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<h3 className=\"truncate font-semibold group-hover:text-kumo-brand\">{plugin.name}</h3>\n\t\t\t\t\t\t{isInstalled && (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\trole=\"link\"\n\t\t\t\t\t\t\t\tclassName=\"cursor-pointer\"\n\t\t\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\t\t\te.stopPropagation();\n\t\t\t\t\t\t\t\t\tvoid navigate({ to: \"/plugins-manager\" });\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Badge variant=\"secondary\">Installed</Badge>\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex items-center gap-2 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t<span>{plugin.author.name}</span>\n\t\t\t\t\t\t{plugin.author.verified && <ShieldCheck className=\"h-3 w-3 text-kumo-brand\" />}\n\t\t\t\t\t\t{plugin.latestVersion?.version && <span>v{plugin.latestVersion.version}</span>}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Description */}\n\t\t\t{plugin.description && (\n\t\t\t\t<p className=\"mt-2 line-clamp-2 text-sm text-kumo-subtle\">{plugin.description}</p>\n\t\t\t)}\n\n\t\t\t{/* Footer: install count + audit + capabilities */}\n\t\t\t<div className=\"mt-3 flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-2 text-xs text-kumo-subtle\">\n\t\t\t\t\t<DownloadSimple className=\"h-3.5 w-3.5\" />\n\t\t\t\t\t<span>{formatInstallCount(plugin.installCount)}</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t{auditVerdict && <AuditBadge verdict={auditVerdict} />}\n\t\t\t\t\t{plugin.capabilities.length > 0 && (\n\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t{plugin.capabilities.length} permission{plugin.capabilities.length !== 1 ? \"s\" : \"\"}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</Link>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Shared small components\n// ---------------------------------------------------------------------------\n\nfunction PluginAvatar({ name }: { name: string }) {\n\tconst initial = name.charAt(0).toUpperCase();\n\treturn (\n\t\t<div className=\"flex h-10 w-10 items-center justify-center rounded-lg bg-kumo-brand/10 text-kumo-brand font-bold text-lg\">\n\t\t\t{initial}\n\t\t</div>\n\t);\n}\n\nexport function AuditBadge({ verdict }: { verdict: \"pass\" | \"warn\" | \"fail\" }) {\n\tif (verdict === \"pass\") {\n\t\treturn (\n\t\t\t<span\n\t\t\t\tclassName=\"inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs bg-green-500/10 text-green-600\"\n\t\t\t\ttitle=\"Security audit passed\"\n\t\t\t>\n\t\t\t\t<ShieldCheck className=\"h-3 w-3\" />\n\t\t\t\tPass\n\t\t\t</span>\n\t\t);\n\t}\n\tif (verdict === \"warn\") {\n\t\treturn (\n\t\t\t<span\n\t\t\t\tclassName=\"inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs bg-warning/10 text-warning\"\n\t\t\t\ttitle=\"Security audit flagged concerns\"\n\t\t\t>\n\t\t\t\t<Warning className=\"h-3 w-3\" />\n\t\t\t\tWarn\n\t\t\t</span>\n\t\t);\n\t}\n\treturn (\n\t\t<span\n\t\t\tclassName=\"inline-flex items-center gap-1 rounded px-1.5 py-0.5 text-xs bg-kumo-danger/10 text-kumo-danger\"\n\t\t\ttitle=\"Security audit failed\"\n\t\t>\n\t\t\t<ShieldWarning className=\"h-3 w-3\" />\n\t\t\tFail\n\t\t</span>\n\t);\n}\n\nfunction formatInstallCount(count: number): string {\n\tif (count >= 1000) {\n\t\treturn `${(count / 1000).toFixed(count >= 10000 ? 0 : 1)}k`;\n\t}\n\treturn String(count);\n}\n\nexport default MarketplaceBrowse;\n","/**\n * Capability Consent Dialog\n *\n * Shown before installing or updating a marketplace plugin.\n * Lists each requested capability with a human-readable explanation.\n * User must explicitly confirm before the action proceeds.\n */\n\nimport { Button } from \"@cloudflare/kumo\";\nimport { ShieldCheck, ShieldWarning, Warning } from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport { describeCapability } from \"../lib/api/marketplace.js\";\nimport { cn } from \"../lib/utils.js\";\nimport { DialogError } from \"./DialogError.js\";\n\nexport interface CapabilityConsentDialogProps {\n\t/** Dialog mode */\n\tmode?: \"install\" | \"update\";\n\t/** Plugin display name */\n\tpluginName: string;\n\t/** Capabilities the plugin requests */\n\tcapabilities: string[];\n\t/** Allowed network hosts (for network:fetch capability) */\n\tallowedHosts?: string[];\n\t/** New capabilities added in an update (highlighted differently) */\n\tnewCapabilities?: string[];\n\t/** Audit verdict badge */\n\tauditVerdict?: \"pass\" | \"warn\" | \"fail\";\n\t/** Whether the action is in progress */\n\tisPending?: boolean;\n\t/** Error message to display inline */\n\terror?: string | null;\n\t/** Called when user confirms */\n\tonConfirm: () => void;\n\t/** Called when user cancels */\n\tonCancel: () => void;\n}\n\nexport function CapabilityConsentDialog({\n\tmode,\n\tpluginName,\n\tcapabilities,\n\tallowedHosts,\n\tnewCapabilities = [],\n\tauditVerdict,\n\tisPending = false,\n\terror,\n\tonConfirm,\n\tonCancel,\n}: CapabilityConsentDialogProps) {\n\tconst newSet = new Set(newCapabilities);\n\tconst isUpdate = mode === \"update\" || newCapabilities.length > 0;\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"fixed inset-0 z-50 flex items-center justify-center\"\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t\taria-label=\"Capability consent\"\n\t\t>\n\t\t\t{/* Backdrop */}\n\t\t\t<div className=\"absolute inset-0 bg-black/50\" onClick={() => !isPending && onCancel()} />\n\n\t\t\t{/* Dialog */}\n\t\t\t<div className=\"relative w-full max-w-md rounded-lg border bg-kumo-base shadow-lg\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"border-b px-6 py-4\">\n\t\t\t\t\t<h2 className=\"text-lg font-semibold\">\n\t\t\t\t\t\t{isUpdate ? \"Review New Permissions\" : \"Plugin Permissions\"}\n\t\t\t\t\t</h2>\n\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{isUpdate\n\t\t\t\t\t\t\t? `${pluginName} is requesting additional permissions:`\n\t\t\t\t\t\t\t: `${pluginName} requires the following permissions:`}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Capabilities list */}\n\t\t\t\t<div className=\"px-6 py-4 space-y-3\">\n\t\t\t\t\t{capabilities.map((cap) => {\n\t\t\t\t\t\tconst isNew = newSet.has(cap);\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={cap}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-start gap-3 rounded-md p-2 text-sm\",\n\t\t\t\t\t\t\t\t\tisNew ? \"bg-warning/10 border border-warning/30\" : \"bg-kumo-tint/50\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<ShieldCheck\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"mt-0.5 h-4 w-4 shrink-0\",\n\t\t\t\t\t\t\t\t\t\tisNew ? \"text-warning\" : \"text-kumo-subtle\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<span className={cn(isNew && \"font-medium\")}>\n\t\t\t\t\t\t\t\t\t\t{describeCapability(cap, allowedHosts)}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{isNew && <span className=\"ml-2 text-xs text-warning font-medium\">NEW</span>}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\n\t\t\t\t\t{/* Audit verdict banner */}\n\t\t\t\t\t{auditVerdict && auditVerdict !== \"pass\" && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex items-center gap-2 rounded-md p-3 text-sm mt-2\",\n\t\t\t\t\t\t\t\tauditVerdict === \"warn\"\n\t\t\t\t\t\t\t\t\t? \"bg-warning/10 text-warning\"\n\t\t\t\t\t\t\t\t\t: \"bg-kumo-danger/10 text-kumo-danger\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{auditVerdict === \"warn\" ? (\n\t\t\t\t\t\t\t\t<Warning className=\"h-4 w-4 shrink-0\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<ShieldWarning className=\"h-4 w-4 shrink-0\" />\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t{auditVerdict === \"warn\"\n\t\t\t\t\t\t\t\t\t? \"Security audit flagged potential concerns with this plugin.\"\n\t\t\t\t\t\t\t\t\t: \"Security audit flagged this plugin as potentially unsafe.\"}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Error */}\n\t\t\t\t<DialogError message={error} className=\"mx-6\" />\n\n\t\t\t\t{/* Actions */}\n\t\t\t\t<div className=\"flex justify-end gap-3 border-t px-6 py-4\">\n\t\t\t\t\t<Button variant=\"ghost\" onClick={onCancel} disabled={isPending}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button onClick={onConfirm} disabled={isPending}>\n\t\t\t\t\t\t{isPending\n\t\t\t\t\t\t\t? isUpdate\n\t\t\t\t\t\t\t\t? \"Updating...\"\n\t\t\t\t\t\t\t\t: \"Installing...\"\n\t\t\t\t\t\t\t: isUpdate\n\t\t\t\t\t\t\t\t? \"Accept & Update\"\n\t\t\t\t\t\t\t\t: \"Accept & Install\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default CapabilityConsentDialog;\n","/**\n * Plugin Manager Component\n *\n * Displays list of configured plugins with enable/disable controls.\n * Extended with marketplace features: source badges, update checking,\n * update/uninstall for marketplace-installed plugins.\n */\n\nimport { Badge, Button, Switch, Toast } from \"@cloudflare/kumo\";\nimport {\n\tPuzzlePiece,\n\tGear,\n\tFileText,\n\tSquaresFour,\n\tWebhooksLogo,\n\tCaretDown,\n\tCaretRight,\n\tArrowsClockwise,\n\tStorefront,\n\tTrash,\n\tShieldCheck,\n} from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tfetchPlugins,\n\tenablePlugin,\n\tdisablePlugin,\n\ttype PluginInfo,\n\ttype AdminManifest,\n\tCAPABILITY_LABELS,\n} from \"../lib/api\";\nimport {\n\tcheckPluginUpdates,\n\tupdateMarketplacePlugin,\n\tuninstallMarketplacePlugin,\n\ttype PluginUpdateInfo,\n} from \"../lib/api/marketplace.js\";\nimport { safeIconUrl } from \"../lib/url.js\";\nimport { cn } from \"../lib/utils\";\nimport { CapabilityConsentDialog } from \"./CapabilityConsentDialog.js\";\nimport { DialogError, getMutationError } from \"./DialogError.js\";\n\nexport interface PluginManagerProps {\n\t/** Admin manifest — used to check if marketplace is configured */\n\tmanifest?: AdminManifest;\n}\n\nexport function PluginManager({ manifest }: PluginManagerProps) {\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\tconst hasMarketplace = !!manifest?.marketplace;\n\n\tconst {\n\t\tdata: plugins,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"plugins\"],\n\t\tqueryFn: fetchPlugins,\n\t});\n\n\tconst {\n\t\tdata: updates,\n\t\trefetch: refetchUpdates,\n\t\tisFetching: isCheckingUpdates,\n\t} = useQuery({\n\t\tqueryKey: [\"plugin-updates\"],\n\t\tqueryFn: checkPluginUpdates,\n\t\tenabled: false, // Only fetch on demand\n\t});\n\n\tconst enableMutation = useMutation({\n\t\tmutationFn: enablePlugin,\n\t\tonSuccess: (plugin) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"plugins\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Plugin enabled\",\n\t\t\t\tdescription: `${plugin.name} is now active`,\n\t\t\t});\n\t\t},\n\t\tonError: (err) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to enable plugin\",\n\t\t\t\tdescription: err instanceof Error ? err.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst disableMutation = useMutation({\n\t\tmutationFn: disablePlugin,\n\t\tonSuccess: (plugin) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"plugins\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Plugin disabled\",\n\t\t\t\tdescription: `${plugin.name} has been deactivated`,\n\t\t\t});\n\t\t},\n\t\tonError: (err) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to disable plugin\",\n\t\t\t\tdescription: err instanceof Error ? err.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst updateMap = React.useMemo(() => {\n\t\tif (!updates) return new Map<string, PluginUpdateInfo>();\n\t\treturn new Map(updates.map((u) => [u.pluginId, u]));\n\t}, [updates]);\n\n\tconst hasMarketplacePlugins = plugins?.some((p) => p.source === \"marketplace\");\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-3xl font-bold\">Plugins</h1>\n\t\t\t\t<div className=\"text-kumo-subtle\">Loading plugins...</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-3xl font-bold\">Plugins</h1>\n\t\t\t\t<div className=\"text-kumo-danger\">Failed to load plugins: {error.message}</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<h1 className=\"text-3xl font-bold\">Plugins</h1>\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t{hasMarketplacePlugins && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tonClick={() => void refetchUpdates()}\n\t\t\t\t\t\t\tdisabled={isCheckingUpdates}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowsClockwise\n\t\t\t\t\t\t\t\tclassName={cn(\"mr-2 h-4 w-4\", isCheckingUpdates && \"animate-spin\")}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\tCheck for updates\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t\t{hasMarketplace && (\n\t\t\t\t\t\t<Link to=\"/plugins/marketplace\">\n\t\t\t\t\t\t\t<Button variant=\"ghost\">\n\t\t\t\t\t\t\t\t<Storefront className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\t\tMarketplace\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t)}\n\t\t\t\t\t<span className=\"text-sm text-kumo-subtle\">{plugins?.length ?? 0} plugins</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<p className=\"text-kumo-subtle\">\n\t\t\t\tManage installed plugins. Enable or disable plugins to control their functionality.\n\t\t\t</p>\n\n\t\t\t<div className=\"grid gap-4\">\n\t\t\t\t{plugins?.map((plugin) => (\n\t\t\t\t\t<PluginCard\n\t\t\t\t\t\tkey={plugin.id}\n\t\t\t\t\t\tplugin={plugin}\n\t\t\t\t\t\tupdateInfo={updateMap.get(plugin.id)}\n\t\t\t\t\t\tonEnable={() => enableMutation.mutate(plugin.id)}\n\t\t\t\t\t\tonDisable={() => disableMutation.mutate(plugin.id)}\n\t\t\t\t\t\tisToggling={enableMutation.isPending || disableMutation.isPending}\n\t\t\t\t\t\thasMarketplace={hasMarketplace}\n\t\t\t\t\t/>\n\t\t\t\t))}\n\t\t\t</div>\n\n\t\t\t{plugins?.length === 0 && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-8 text-center\">\n\t\t\t\t\t<PuzzlePiece className=\"mx-auto h-12 w-12 text-kumo-subtle\" />\n\t\t\t\t\t<h3 className=\"mt-4 text-lg font-medium\">No plugins configured</h3>\n\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{hasMarketplace ? (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\tBrowse the{\" \"}\n\t\t\t\t\t\t\t\t<Link to=\"/plugins/marketplace\" className=\"text-kumo-brand hover:underline\">\n\t\t\t\t\t\t\t\t\tmarketplace\n\t\t\t\t\t\t\t\t</Link>{\" \"}\n\t\t\t\t\t\t\t\tto install plugins, or add them to your astro.config.mjs.\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\"Add plugins to your astro.config.mjs to extend EmDash functionality.\"\n\t\t\t\t\t\t)}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\ninterface PluginCardProps {\n\tplugin: PluginInfo;\n\tupdateInfo?: PluginUpdateInfo;\n\tonEnable: () => void;\n\tonDisable: () => void;\n\tisToggling: boolean;\n\t/** Whether the marketplace is configured (controls \"View in Marketplace\" link) */\n\thasMarketplace: boolean;\n}\n\nfunction PluginCard({\n\tplugin,\n\tupdateInfo,\n\tonEnable,\n\tonDisable,\n\tisToggling,\n\thasMarketplace,\n}: PluginCardProps) {\n\tconst [expanded, setExpanded] = React.useState(false);\n\tconst [showUpdateConsent, setShowUpdateConsent] = React.useState(false);\n\tconst [showUninstallConfirm, setShowUninstallConfirm] = React.useState(false);\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\n\tconst isMarketplace = plugin.source === \"marketplace\";\n\tconst hasUpdate = !!updateInfo && updateInfo.installed !== updateInfo.latest;\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: () => updateMarketplacePlugin(plugin.id, { confirmCapabilities: true }),\n\t\tonSuccess: () => {\n\t\t\tsetShowUpdateConsent(false);\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"plugins\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"plugin-updates\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Plugin updated\",\n\t\t\t\tdescription: `${plugin.name} updated to v${updateInfo?.latest}`,\n\t\t\t});\n\t\t},\n\t});\n\n\tconst uninstallMutation = useMutation({\n\t\tmutationFn: (deleteData: boolean) => uninstallMarketplacePlugin(plugin.id, { deleteData }),\n\t\tonSuccess: () => {\n\t\t\tsetShowUninstallConfirm(false);\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"plugins\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Plugin uninstalled\",\n\t\t\t\tdescription: `${plugin.name} has been removed`,\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleToggle = () => {\n\t\tif (plugin.enabled) {\n\t\t\tonDisable();\n\t\t} else {\n\t\t\tonEnable();\n\t\t}\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"rounded-lg border bg-kumo-base transition-colors\",\n\t\t\t\t\t!plugin.enabled && \"opacity-75\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<div className=\"flex items-center gap-4 p-4\">\n\t\t\t\t\t{/* Plugin icon */}\n\t\t\t\t\t{plugin.iconUrl ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={safeIconUrl(plugin.iconUrl, 80) ?? undefined}\n\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\tclassName=\"h-10 w-10 rounded-lg object-cover\"\n\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"flex h-10 w-10 items-center justify-center rounded-lg\",\n\t\t\t\t\t\t\t\tplugin.enabled ? \"bg-kumo-brand/10\" : \"bg-kumo-tint\",\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<PuzzlePiece\n\t\t\t\t\t\t\t\tclassName={cn(\"h-5 w-5\", plugin.enabled ? \"text-kumo-brand\" : \"text-kumo-subtle\")}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Plugin info */}\n\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<h3 className=\"font-semibold truncate\">{plugin.name}</h3>\n\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">v{plugin.version}</span>\n\t\t\t\t\t\t\t{!plugin.enabled && <Badge variant=\"secondary\">Disabled</Badge>}\n\t\t\t\t\t\t\t{isMarketplace && <Badge variant=\"secondary\">Marketplace</Badge>}\n\t\t\t\t\t\t\t{hasUpdate && (\n\t\t\t\t\t\t\t\t<Badge variant=\"outline\" className=\"border-kumo-brand text-kumo-brand\">\n\t\t\t\t\t\t\t\t\tv{updateInfo.latest} available\n\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Description */}\n\t\t\t\t\t\t{plugin.description && (\n\t\t\t\t\t\t\t<p className=\"mt-0.5 text-sm text-kumo-subtle line-clamp-1\">{plugin.description}</p>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Feature indicators + inline capabilities */}\n\t\t\t\t\t\t<div className=\"flex items-center gap-3 mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{plugin.hasAdminPages && (\n\t\t\t\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t\t<FileText className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\tPages\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{plugin.hasDashboardWidgets && (\n\t\t\t\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t\t<SquaresFour className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\tWidgets\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{plugin.hasHooks && (\n\t\t\t\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t\t\t\t<WebhooksLogo className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\tHooks\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{plugin.capabilities.length > 0 && (\n\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-1\"\n\t\t\t\t\t\t\t\t\ttitle={plugin.capabilities.map((c) => CAPABILITY_LABELS[c] ?? c).join(\", \")}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<ShieldCheck className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\t{plugin.capabilities.length} permission\n\t\t\t\t\t\t\t\t\t{plugin.capabilities.length !== 1 ? \"s\" : \"\"}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Actions */}\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t{hasUpdate && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tonClick={() => setShowUpdateConsent(true)}\n\t\t\t\t\t\t\t\tdisabled={updateMutation.isPending}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{updateMutation.isPending ? \"Updating...\" : `Update to v${updateInfo.latest}`}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{isMarketplace && hasMarketplace && (\n\t\t\t\t\t\t\t<Link to=\"/plugins/marketplace/$pluginId\" params={{ pluginId: plugin.id }}>\n\t\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\">\n\t\t\t\t\t\t\t\t\t<Storefront className=\"mr-1.5 h-3.5 w-3.5\" />\n\t\t\t\t\t\t\t\t\tView in Marketplace\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{plugin.hasAdminPages && plugin.enabled && (\n\t\t\t\t\t\t\t<Link to=\"/plugins/$pluginId/$\" params={{ pluginId: plugin.id, _splat: \"settings\" }}>\n\t\t\t\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Settings\">\n\t\t\t\t\t\t\t\t\t<Gear className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Settings</span>\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t<Switch\n\t\t\t\t\t\t\tchecked={plugin.enabled}\n\t\t\t\t\t\t\tonCheckedChange={handleToggle}\n\t\t\t\t\t\t\tdisabled={isToggling}\n\t\t\t\t\t\t\taria-label={plugin.enabled ? \"Disable plugin\" : \"Enable plugin\"}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\taria-label={expanded ? \"Collapse details\" : \"Expand details\"}\n\t\t\t\t\t\t\tonClick={() => setExpanded(!expanded)}\n\t\t\t\t\t\t\taria-expanded={expanded}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{expanded ? <CaretDown className=\"h-4 w-4\" /> : <CaretRight className=\"h-4 w-4\" />}\n\t\t\t\t\t\t\t<span className=\"sr-only\">{expanded ? \"Collapse\" : \"Expand\"} details</span>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Expanded details */}\n\t\t\t\t{expanded && (\n\t\t\t\t\t<div className=\"border-t px-4 py-3 space-y-3\">\n\t\t\t\t\t\t{/* Capabilities */}\n\t\t\t\t\t\t{plugin.capabilities.length > 0 && (\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<h4 className=\"text-xs font-medium text-kumo-subtle uppercase tracking-wider mb-1\">\n\t\t\t\t\t\t\t\t\tCapabilities\n\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t\t\t\t\t{plugin.capabilities.map((cap) => (\n\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\tkey={cap}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"inline-flex items-center rounded-md bg-kumo-tint px-2 py-0.5 text-xs\"\n\t\t\t\t\t\t\t\t\t\t\ttitle={CAPABILITY_LABELS[cap]}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{CAPABILITY_LABELS[cap] ?? cap}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Source */}\n\t\t\t\t\t\t{isMarketplace && (\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<h4 className=\"text-xs font-medium text-kumo-subtle uppercase tracking-wider mb-1\">\n\t\t\t\t\t\t\t\t\tSource\n\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\tInstalled from marketplace (v{plugin.marketplaceVersion || plugin.version})\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Package */}\n\t\t\t\t\t\t{plugin.package && (\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<h4 className=\"text-xs font-medium text-kumo-subtle uppercase tracking-wider mb-1\">\n\t\t\t\t\t\t\t\t\tPackage\n\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t<code className=\"text-xs bg-kumo-tint px-2 py-0.5 rounded\">{plugin.package}</code>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Timestamps */}\n\t\t\t\t\t\t<div className=\"grid grid-cols-2 gap-4 text-xs\">\n\t\t\t\t\t\t\t{plugin.installedAt && (\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Installed:</span>{\" \"}\n\t\t\t\t\t\t\t\t\t{new Date(plugin.installedAt).toLocaleDateString()}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{plugin.activatedAt && (\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Last enabled:</span>{\" \"}\n\t\t\t\t\t\t\t\t\t{new Date(plugin.activatedAt).toLocaleDateString()}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{plugin.deactivatedAt && !plugin.enabled && (\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Disabled:</span>{\" \"}\n\t\t\t\t\t\t\t\t\t{new Date(plugin.deactivatedAt).toLocaleDateString()}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Uninstall button for marketplace plugins */}\n\t\t\t\t\t\t{isMarketplace && (\n\t\t\t\t\t\t\t<div className=\"pt-2 border-t\">\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-kumo-danger hover:text-kumo-danger\"\n\t\t\t\t\t\t\t\t\tonClick={() => setShowUninstallConfirm(true)}\n\t\t\t\t\t\t\t\t\tdisabled={uninstallMutation.isPending}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Trash className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\t\t\tUninstall\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Update consent dialog */}\n\t\t\t{showUpdateConsent && updateInfo && (\n\t\t\t\t<CapabilityConsentDialog\n\t\t\t\t\tmode=\"update\"\n\t\t\t\t\tpluginName={plugin.name}\n\t\t\t\t\tcapabilities={plugin.capabilities}\n\t\t\t\t\tnewCapabilities={[]} // WS3 will populate this from the diff\n\t\t\t\t\tisPending={updateMutation.isPending}\n\t\t\t\t\terror={getMutationError(updateMutation.error)}\n\t\t\t\t\tonConfirm={() => updateMutation.mutate()}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetShowUpdateConsent(false);\n\t\t\t\t\t\tupdateMutation.reset();\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Uninstall confirmation */}\n\t\t\t{showUninstallConfirm && (\n\t\t\t\t<UninstallConfirmDialog\n\t\t\t\t\tpluginName={plugin.name}\n\t\t\t\t\tisPending={uninstallMutation.isPending}\n\t\t\t\t\terror={getMutationError(uninstallMutation.error)}\n\t\t\t\t\tonConfirm={(deleteData) => uninstallMutation.mutate(deleteData)}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetShowUninstallConfirm(false);\n\t\t\t\t\t\tuninstallMutation.reset();\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Uninstall confirmation dialog\n// ---------------------------------------------------------------------------\n\ninterface UninstallConfirmDialogProps {\n\tpluginName: string;\n\tisPending: boolean;\n\terror?: string | null;\n\tonConfirm: (deleteData: boolean) => void;\n\tonCancel: () => void;\n}\n\nexport function UninstallConfirmDialog({\n\tpluginName,\n\tisPending,\n\terror,\n\tonConfirm,\n\tonCancel,\n}: UninstallConfirmDialogProps) {\n\tconst [deleteData, setDeleteData] = React.useState(false);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"fixed inset-0 z-50 flex items-center justify-center\"\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t\taria-label=\"Uninstall confirmation\"\n\t\t>\n\t\t\t<div className=\"absolute inset-0 bg-black/50\" onClick={() => !isPending && onCancel()} />\n\t\t\t<div className=\"relative w-full max-w-sm rounded-lg border bg-kumo-base shadow-lg\">\n\t\t\t\t<div className=\"p-6 space-y-4\">\n\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Uninstall {pluginName}?</h2>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\tThis will remove the plugin and its bundle from your site.\n\t\t\t\t\t</p>\n\t\t\t\t\t<label className=\"flex items-center gap-2 text-sm\">\n\t\t\t\t\t\t<input\n\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\tchecked={deleteData}\n\t\t\t\t\t\t\tonChange={(e) => setDeleteData(e.target.checked)}\n\t\t\t\t\t\t\tclassName=\"rounded border\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\tAlso delete plugin storage data\n\t\t\t\t\t</label>\n\t\t\t\t\t<DialogError message={error} />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex justify-end gap-3 border-t px-6 py-4\">\n\t\t\t\t\t<Button variant=\"ghost\" onClick={onCancel} disabled={isPending}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button variant=\"destructive\" onClick={() => onConfirm(deleteData)} disabled={isPending}>\n\t\t\t\t\t\t{isPending ? \"Uninstalling...\" : \"Uninstall\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default PluginManager;\n","/**\n * Marketplace Plugin Detail\n *\n * Full detail view for a marketplace plugin:\n * - README rendered as markdown\n * - Screenshot gallery\n * - Capability list\n * - Audit summary\n * - Version history\n * - Install button (with capability consent)\n */\n\nimport { Badge, Button } from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tDownloadSimple,\n\tGithubLogo,\n\tGlobe,\n\tShieldCheck,\n\tWarning,\n\tCaretLeft,\n\tCaretRight,\n\tX,\n} from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport DOMPurify from \"dompurify\";\nimport { Marked, Renderer } from \"marked\";\nimport * as React from \"react\";\n\nimport {\n\tfetchMarketplacePlugin,\n\tinstallMarketplacePlugin,\n\tuninstallMarketplacePlugin,\n\tdescribeCapability,\n} from \"../lib/api/marketplace.js\";\nimport { SAFE_URL_RE, isSafeUrl, safeIconUrl } from \"../lib/url.js\";\nimport { CapabilityConsentDialog } from \"./CapabilityConsentDialog.js\";\nimport { getMutationError } from \"./DialogError.js\";\nimport { AuditBadge } from \"./MarketplaceBrowse.js\";\nimport { UninstallConfirmDialog } from \"./PluginManager.js\";\n\nexport interface MarketplacePluginDetailProps {\n\tpluginId: string;\n\t/** IDs of plugins already installed on this site */\n\tinstalledPluginIds?: Set<string>;\n}\n\nexport function MarketplacePluginDetail({\n\tpluginId,\n\tinstalledPluginIds = new Set(),\n}: MarketplacePluginDetailProps) {\n\tconst queryClient = useQueryClient();\n\tconst [showConsent, setShowConsent] = React.useState(false);\n\tconst [showUninstallConfirm, setShowUninstallConfirm] = React.useState(false);\n\tconst [lightboxIndex, setLightboxIndex] = React.useState<number | null>(null);\n\n\tconst {\n\t\tdata: plugin,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"marketplace\", \"plugin\", pluginId],\n\t\tqueryFn: () => fetchMarketplacePlugin(pluginId),\n\t});\n\n\tconst installMutation = useMutation({\n\t\tmutationFn: () =>\n\t\t\tinstallMarketplacePlugin(pluginId, {\n\t\t\t\tversion: plugin?.latestVersion?.version,\n\t\t\t}),\n\t\tonSuccess: () => {\n\t\t\tsetShowConsent(false);\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"plugins\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"marketplace\"] });\n\t\t},\n\t});\n\n\tconst uninstallMutation = useMutation({\n\t\tmutationFn: (deleteData: boolean) => uninstallMarketplacePlugin(pluginId, { deleteData }),\n\t\tonSuccess: () => {\n\t\t\tsetShowUninstallConfirm(false);\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"plugins\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"marketplace\"] });\n\t\t},\n\t});\n\n\tconst isInstalled = installedPluginIds.has(pluginId);\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<BackLink />\n\t\t\t\t<div className=\"animate-pulse space-y-4\">\n\t\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t\t<div className=\"h-16 w-16 rounded-xl bg-kumo-tint\" />\n\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t<div className=\"h-6 w-48 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t<div className=\"h-4 w-32 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"h-4 w-full rounded bg-kumo-tint\" />\n\t\t\t\t\t<div className=\"h-4 w-3/4 rounded bg-kumo-tint\" />\n\t\t\t\t\t<div className=\"h-64 w-full rounded bg-kumo-tint\" />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error || !plugin) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<BackLink />\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-6 text-center\">\n\t\t\t\t\t<Warning className=\"mx-auto h-8 w-8 text-kumo-danger\" />\n\t\t\t\t\t<h3 className=\"mt-3 font-medium text-kumo-danger\">Failed to load plugin</h3>\n\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{error instanceof Error ? error.message : \"Plugin not found\"}\n\t\t\t\t\t</p>\n\t\t\t\t\t<Link to=\"/plugins/marketplace\" className=\"mt-4 inline-block text-kumo-brand text-sm\">\n\t\t\t\t\t\tBack to marketplace\n\t\t\t\t\t</Link>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst latest = plugin.latestVersion;\n\tconst imageVerdict = latest?.imageAudit?.verdict;\n\tconst isImageFlagged = imageVerdict === \"warn\" || imageVerdict === \"fail\";\n\tconst isAuditFailed = latest?.audit?.verdict === \"fail\";\n\tconst screenshots = (latest?.screenshotUrls ?? []).filter(isSafeUrl);\n\tconst iconSrc = plugin.iconUrl ? safeIconUrl(plugin.iconUrl, 128) : null;\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<BackLink />\n\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t{/* Icon */}\n\t\t\t\t\t{iconSrc ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={iconSrc}\n\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\tclassName={`h-16 w-16 rounded-xl object-cover ${isImageFlagged ? \"blur-md\" : \"\"}`}\n\t\t\t\t\t\t\taria-label={isImageFlagged ? \"Icon blurred due to image audit\" : undefined}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"flex h-16 w-16 items-center justify-center rounded-xl bg-kumo-brand/10 text-kumo-brand text-2xl font-bold\">\n\t\t\t\t\t\t\t{plugin.name.charAt(0).toUpperCase()}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h1 className=\"text-2xl font-bold\">{plugin.name}</h1>\n\t\t\t\t\t\t<div className=\"mt-1 flex items-center gap-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t<span>{plugin.author.name}</span>\n\t\t\t\t\t\t\t{plugin.author.verified && <ShieldCheck className=\"h-4 w-4 text-kumo-brand\" />}\n\t\t\t\t\t\t\t{latest && (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<span aria-hidden=\"true\">·</span>\n\t\t\t\t\t\t\t\t\t<span>v{latest.version}</span>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{plugin.description && (\n\t\t\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle max-w-lg\">{plugin.description}</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Action button */}\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t{isInstalled ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<Badge variant=\"secondary\" className=\"text-sm px-3 py-1\">\n\t\t\t\t\t\t\t\tInstalled\n\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tclassName=\"text-kumo-danger hover:text-kumo-danger\"\n\t\t\t\t\t\t\t\tonClick={() => setShowUninstallConfirm(true)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tUninstall\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : isAuditFailed ? (\n\t\t\t\t\t\t<div className=\"flex flex-col items-end gap-1\">\n\t\t\t\t\t\t\t<Button disabled variant=\"secondary\">\n\t\t\t\t\t\t\t\tInstall blocked\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-danger\">Failed security audit</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Button onClick={() => setShowConsent(true)}>\n\t\t\t\t\t\t\t<DownloadSimple className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\tInstall\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Stats bar */}\n\t\t\t<div className=\"flex flex-wrap items-center gap-4 rounded-lg border bg-kumo-tint/30 p-3 text-sm\">\n\t\t\t\t<div className=\"flex items-center gap-1.5\">\n\t\t\t\t\t<DownloadSimple className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t<span>{plugin.installCount.toLocaleString()} installs</span>\n\t\t\t\t</div>\n\t\t\t\t{latest?.audit && <AuditBadge verdict={latest.audit.verdict} />}\n\t\t\t\t{plugin.license && <span className=\"text-kumo-subtle\">{plugin.license}</span>}\n\t\t\t\t{plugin.repositoryUrl && isSafeUrl(plugin.repositoryUrl) && (\n\t\t\t\t\t<a\n\t\t\t\t\t\thref={plugin.repositoryUrl}\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tclassName=\"flex items-center gap-1 text-kumo-brand hover:underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<GithubLogo className=\"h-4 w-4\" />\n\t\t\t\t\t\tSource\n\t\t\t\t\t</a>\n\t\t\t\t)}\n\t\t\t\t{plugin.homepageUrl && isSafeUrl(plugin.homepageUrl) && (\n\t\t\t\t\t<a\n\t\t\t\t\t\thref={plugin.homepageUrl}\n\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\tclassName=\"flex items-center gap-1 text-kumo-brand hover:underline\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<Globe className=\"h-4 w-4\" />\n\t\t\t\t\t\tWebsite\n\t\t\t\t\t</a>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Screenshots */}\n\t\t\t{screenshots.length > 0 && (\n\t\t\t\t<div>\n\t\t\t\t\t<h2 className=\"mb-3 text-lg font-semibold\">Screenshots</h2>\n\t\t\t\t\t<div className=\"flex gap-3 overflow-x-auto pb-2\">\n\t\t\t\t\t\t{screenshots.map((url, i) => (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tkey={url}\n\t\t\t\t\t\t\t\tonClick={() => setLightboxIndex(i)}\n\t\t\t\t\t\t\t\tclassName=\"shrink-0 overflow-hidden rounded-lg border hover:ring-2 hover:ring-kumo-brand transition-shadow\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\tsrc={url}\n\t\t\t\t\t\t\t\t\talt={`Screenshot ${i + 1}`}\n\t\t\t\t\t\t\t\t\tclassName={`h-40 w-auto object-cover ${isImageFlagged ? \"blur-md\" : \"\"}`}\n\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t\taria-label={isImageFlagged ? \"Screenshot blurred due to image audit\" : undefined}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Two-column layout: README + sidebar */}\n\t\t\t<div className=\"grid gap-6 lg:grid-cols-[1fr_280px]\">\n\t\t\t\t{/* README */}\n\t\t\t\t<div>\n\t\t\t\t\t{latest?.readme ? (\n\t\t\t\t\t\t<div className=\"prose prose-sm max-w-none rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t\t\t<div dangerouslySetInnerHTML={{ __html: renderMarkdown(latest.readme) }} />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\tNo detailed description available.\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Sidebar */}\n\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t{/* Capabilities */}\n\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-4\">\n\t\t\t\t\t\t<h3 className=\"text-sm font-semibold mb-2\">Permissions</h3>\n\t\t\t\t\t\t{plugin.capabilities.length === 0 ? (\n\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\tThis plugin requires no special permissions.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<ul className=\"space-y-1.5\">\n\t\t\t\t\t\t\t\t{plugin.capabilities.map((cap) => (\n\t\t\t\t\t\t\t\t\t<li key={cap} className=\"flex items-start gap-2 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t<ShieldCheck className=\"mt-0.5 h-3 w-3 shrink-0 text-kumo-brand\" />\n\t\t\t\t\t\t\t\t\t\t<span>{describeCapability(cap)}</span>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Keywords */}\n\t\t\t\t\t{plugin.keywords && plugin.keywords.length > 0 && (\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-4\">\n\t\t\t\t\t\t\t<h3 className=\"text-sm font-semibold mb-2\">Keywords</h3>\n\t\t\t\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t\t\t\t{plugin.keywords.map((kw) => (\n\t\t\t\t\t\t\t\t\t<span key={kw} className=\"rounded-md bg-kumo-tint px-2 py-0.5 text-xs\">\n\t\t\t\t\t\t\t\t\t\t{kw}\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Audit summary */}\n\t\t\t\t\t{latest?.audit && (\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-4\">\n\t\t\t\t\t\t\t<h3 className=\"text-sm font-semibold mb-2\">Security Audit</h3>\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<AuditBadge verdict={latest.audit.verdict} />\n\t\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\tRisk score: {latest.audit.riskScore}/100\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Version info */}\n\t\t\t\t\t{latest && (\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-4\">\n\t\t\t\t\t\t\t<h3 className=\"text-sm font-semibold mb-2\">Version</h3>\n\t\t\t\t\t\t\t<div className=\"space-y-1 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t<div>v{latest.version}</div>\n\t\t\t\t\t\t\t\t{latest.minEmDashVersion && (\n\t\t\t\t\t\t\t\t\t<div>Requires EmDash {latest.minEmDashVersion}</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<div>Published {new Date(latest.publishedAt).toLocaleDateString()}</div>\n\t\t\t\t\t\t\t\t{latest.bundleSize > 0 && <div>{formatBytes(latest.bundleSize)}</div>}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Capability consent dialog */}\n\t\t\t{showConsent && (\n\t\t\t\t<CapabilityConsentDialog\n\t\t\t\t\tmode=\"install\"\n\t\t\t\t\tpluginName={plugin.name}\n\t\t\t\t\tcapabilities={plugin.capabilities}\n\t\t\t\t\tauditVerdict={latest?.audit?.verdict}\n\t\t\t\t\tisPending={installMutation.isPending}\n\t\t\t\t\terror={getMutationError(installMutation.error)}\n\t\t\t\t\tonConfirm={() => installMutation.mutate()}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetShowConsent(false);\n\t\t\t\t\t\tinstallMutation.reset();\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Uninstall confirmation */}\n\t\t\t{showUninstallConfirm && (\n\t\t\t\t<UninstallConfirmDialog\n\t\t\t\t\tpluginName={plugin.name}\n\t\t\t\t\tisPending={uninstallMutation.isPending}\n\t\t\t\t\terror={getMutationError(uninstallMutation.error)}\n\t\t\t\t\tonConfirm={(deleteData) => uninstallMutation.mutate(deleteData)}\n\t\t\t\t\tonCancel={() => {\n\t\t\t\t\t\tsetShowUninstallConfirm(false);\n\t\t\t\t\t\tuninstallMutation.reset();\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Screenshot lightbox */}\n\t\t\t{lightboxIndex !== null && lightboxIndex < screenshots.length && (\n\t\t\t\t<ScreenshotLightbox\n\t\t\t\t\tscreenshots={screenshots}\n\t\t\t\t\tindex={lightboxIndex}\n\t\t\t\t\tisBlurred={isImageFlagged}\n\t\t\t\t\tonClose={() => setLightboxIndex(null)}\n\t\t\t\t\tonNavigate={setLightboxIndex}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Sub-components\n// ---------------------------------------------------------------------------\n\nfunction BackLink() {\n\treturn (\n\t\t<Link\n\t\t\tto=\"/plugins/marketplace\"\n\t\t\tclassName=\"inline-flex items-center gap-1 text-sm text-kumo-subtle hover:text-kumo-default\"\n\t\t>\n\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\tBack to marketplace\n\t\t</Link>\n\t);\n}\n\ninterface ScreenshotLightboxProps {\n\tscreenshots: string[];\n\tindex: number;\n\tisBlurred?: boolean;\n\tonClose: () => void;\n\tonNavigate: (index: number) => void;\n}\n\nfunction ScreenshotLightbox({\n\tscreenshots,\n\tindex,\n\tisBlurred = false,\n\tonClose,\n\tonNavigate,\n}: ScreenshotLightboxProps) {\n\tconst handleKeyDown = React.useCallback(\n\t\t(e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\") onClose();\n\t\t\tif (e.key === \"ArrowLeft\" && index > 0) onNavigate(index - 1);\n\t\t\tif (e.key === \"ArrowRight\" && index < screenshots.length - 1) onNavigate(index + 1);\n\t\t},\n\t\t[index, screenshots.length, onClose, onNavigate],\n\t);\n\n\tReact.useEffect(() => {\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => document.removeEventListener(\"keydown\", handleKeyDown);\n\t}, [handleKeyDown]);\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"fixed inset-0 z-50 flex items-center justify-center bg-black/80\"\n\t\t\trole=\"dialog\"\n\t\t\taria-modal=\"true\"\n\t\t\taria-label=\"Screenshot viewer\"\n\t\t>\n\t\t\t<button\n\t\t\t\tonClick={onClose}\n\t\t\t\tclassName=\"absolute right-4 top-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70\"\n\t\t\t\taria-label=\"Close\"\n\t\t\t>\n\t\t\t\t<X className=\"h-5 w-5\" />\n\t\t\t</button>\n\n\t\t\t{index > 0 && (\n\t\t\t\t<button\n\t\t\t\t\tonClick={() => onNavigate(index - 1)}\n\t\t\t\t\tclassName=\"absolute left-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70\"\n\t\t\t\t\taria-label=\"Previous screenshot\"\n\t\t\t\t>\n\t\t\t\t\t<CaretLeft className=\"h-5 w-5\" />\n\t\t\t\t</button>\n\t\t\t)}\n\n\t\t\t<img\n\t\t\t\tsrc={screenshots[index]}\n\t\t\t\talt={`Screenshot ${index + 1} of ${screenshots.length}`}\n\t\t\t\tclassName={`max-h-[85vh] max-w-[90vw] rounded-lg object-contain ${\n\t\t\t\t\tisBlurred ? \"blur-md\" : \"\"\n\t\t\t\t}`}\n\t\t\t/>\n\n\t\t\t{index < screenshots.length - 1 && (\n\t\t\t\t<button\n\t\t\t\t\tonClick={() => onNavigate(index + 1)}\n\t\t\t\t\tclassName=\"absolute right-4 rounded-full bg-black/50 p-2 text-white hover:bg-black/70\"\n\t\t\t\t\taria-label=\"Next screenshot\"\n\t\t\t\t>\n\t\t\t\t\t<CaretRight className=\"h-5 w-5\" />\n\t\t\t\t</button>\n\t\t\t)}\n\n\t\t\t{/* Counter */}\n\t\t\t<div className=\"absolute bottom-4 left-1/2 -translate-x-1/2 rounded-full bg-black/50 px-3 py-1 text-sm text-white\">\n\t\t\t\t{index + 1} / {screenshots.length}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n// ---------------------------------------------------------------------------\n// Markdown rendering (via marked, raw HTML blocked, sanitized with DOMPurify)\n// ---------------------------------------------------------------------------\n\nconst HTML_ESCAPE_MAP: Record<string, string> = {\n\t\"&\": \"&\",\n\t\"<\": \"<\",\n\t\">\": \">\",\n\t'\"': \""\",\n\t\"'\": \"'\",\n};\n\nconst HTML_ESCAPE_RE = /[&<>\"']/g;\n\nfunction escapeHtml(str: string): string {\n\treturn str.replace(HTML_ESCAPE_RE, (ch) => HTML_ESCAPE_MAP[ch]!);\n}\n\nconst renderer = new Renderer();\n\nrenderer.link = ({ href, text }) => {\n\tif (!SAFE_URL_RE.test(href)) return escapeHtml(text);\n\treturn `<a href=\"${escapeHtml(href)}\" target=\"_blank\" rel=\"noopener noreferrer\">${escapeHtml(text)}</a>`;\n};\n\nrenderer.image = ({ text }) => escapeHtml(text);\n\nrenderer.html = () => \"\";\n\nconst md = new Marked({ renderer, async: false });\n\n/** Allowed tags and attributes for DOMPurify — only standard markdown output. */\nconst SANITIZE_CONFIG = {\n\tALLOWED_TAGS: [\n\t\t\"h1\",\n\t\t\"h2\",\n\t\t\"h3\",\n\t\t\"h4\",\n\t\t\"h5\",\n\t\t\"h6\",\n\t\t\"p\",\n\t\t\"a\",\n\t\t\"ul\",\n\t\t\"ol\",\n\t\t\"li\",\n\t\t\"blockquote\",\n\t\t\"pre\",\n\t\t\"code\",\n\t\t\"em\",\n\t\t\"strong\",\n\t\t\"del\",\n\t\t\"br\",\n\t\t\"hr\",\n\t\t\"table\",\n\t\t\"thead\",\n\t\t\"tbody\",\n\t\t\"tr\",\n\t\t\"th\",\n\t\t\"td\",\n\t\t\"details\",\n\t\t\"summary\",\n\t\t\"sup\",\n\t\t\"sub\",\n\t],\n\tALLOWED_ATTR: [\"href\", \"target\", \"rel\"],\n};\n\nfunction renderMarkdown(markdown: string): string {\n\tconst result = md.parse(markdown);\n\tconst html = typeof result === \"string\" ? result : \"\";\n\treturn DOMPurify.sanitize(html, SANITIZE_CONFIG);\n}\n\nfunction formatBytes(bytes: number): string {\n\tif (bytes < 1024) return `${bytes} B`;\n\tif (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n\treturn `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nexport default MarketplacePluginDetail;\n","/**\n * Media Detail Panel\n *\n * A slide-out panel for viewing and editing media item metadata.\n * Opens when clicking an item in the MediaLibrary.\n */\n\nimport { Button, Input, InputArea } from \"@cloudflare/kumo\";\nimport { X, Trash, Calendar, HardDrive, Ruler } from \"@phosphor-icons/react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { updateMedia, deleteMedia, type MediaItem } from \"../lib/api\";\nimport { useStableCallback } from \"../lib/hooks\";\nimport { getFileIcon, formatFileSize } from \"../lib/media-utils\";\nimport { cn } from \"../lib/utils\";\nimport { ConfirmDialog } from \"./ConfirmDialog\";\n\nexport interface MediaDetailPanelProps {\n\titem: MediaItem | null;\n\tonClose: () => void;\n\tonDeleted?: () => void;\n}\n\n/**\n * Slide-out panel for viewing and editing media metadata\n */\nexport function MediaDetailPanel({ item, onClose, onDeleted }: MediaDetailPanelProps) {\n\tconst queryClient = useQueryClient();\n\n\t// Form state - controlled inputs\n\tconst [filename, setFilename] = React.useState(item?.filename ?? \"\");\n\tconst [alt, setAlt] = React.useState(item?.alt ?? \"\");\n\tconst [caption, setCaption] = React.useState(item?.caption ?? \"\");\n\n\t// Reset form when item changes\n\tReact.useEffect(() => {\n\t\tif (item) {\n\t\t\tsetFilename(item.filename);\n\t\t\tsetAlt(item.alt ?? \"\");\n\t\t\tsetCaption(item.caption ?? \"\");\n\t\t}\n\t}, [item]);\n\n\t// Track if form has unsaved changes\n\tconst hasChanges = React.useMemo(() => {\n\t\tif (!item) return false;\n\t\treturn (\n\t\t\tfilename !== item.filename || alt !== (item.alt ?? \"\") || caption !== (item.caption ?? \"\")\n\t\t);\n\t}, [item, filename, alt, caption]);\n\n\t// Update mutation\n\tconst updateMutation = useMutation({\n\t\tmutationFn: (data: { alt?: string; caption?: string }) => {\n\t\t\tif (!item) throw new Error(\"No item selected\");\n\t\t\treturn updateMedia(item.id, data);\n\t\t},\n\t\tonSuccess: () => {\n\t\t\t// Invalidate to refresh the list\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"media\"] });\n\t\t},\n\t});\n\n\t// Delete mutation\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: () => {\n\t\t\tif (!item) throw new Error(\"No item selected\");\n\t\t\treturn deleteMedia(item.id);\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"media\"] });\n\t\t\tonDeleted?.();\n\t\t\tonClose();\n\t\t},\n\t});\n\n\tconst handleSave = () => {\n\t\tif (!item || !hasChanges) return;\n\t\tupdateMutation.mutate({\n\t\t\talt: alt || undefined,\n\t\t\tcaption: caption || undefined,\n\t\t});\n\t};\n\n\tconst [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);\n\n\tconst handleDelete = () => {\n\t\tif (!item) return;\n\t\tsetShowDeleteConfirm(true);\n\t};\n\n\tconst stableOnClose = useStableCallback(onClose);\n\tconst stableHandleSave = useStableCallback(handleSave);\n\n\t// Handle keyboard shortcuts\n\tReact.useEffect(() => {\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\") {\n\t\t\t\tstableOnClose();\n\t\t\t}\n\t\t\tif ((e.metaKey || e.ctrlKey) && e.key === \"s\") {\n\t\t\t\te.preventDefault();\n\t\t\t\tstableHandleSave();\n\t\t\t}\n\t\t};\n\n\t\twindow.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => window.removeEventListener(\"keydown\", handleKeyDown);\n\t}, [stableOnClose, stableHandleSave]);\n\n\tif (!item) return null;\n\n\tconst isImage = item.mimeType.startsWith(\"image/\");\n\tconst isVideo = item.mimeType.startsWith(\"video/\");\n\tconst isAudio = item.mimeType.startsWith(\"audio/\");\n\n\treturn (\n\t\t<>\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"fixed inset-y-0 right-0 w-96 bg-kumo-base border-l shadow-xl z-50\",\n\t\t\t\t\t\"flex flex-col\",\n\t\t\t\t\t\"animate-in slide-in-from-right duration-200\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"flex items-center justify-between p-4 border-b\">\n\t\t\t\t\t<h2 className=\"font-semibold truncate pr-2\">Media Details</h2>\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Close\" onClick={onClose}>\n\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Content */}\n\t\t\t\t<div className=\"flex-1 overflow-y-auto\">\n\t\t\t\t\t{/* Preview */}\n\t\t\t\t\t<div className=\"p-4 border-b\">\n\t\t\t\t\t\t<div className=\"aspect-video bg-kumo-tint rounded-lg overflow-hidden flex items-center justify-center\">\n\t\t\t\t\t\t\t{isImage ? (\n\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\tsrc={item.url}\n\t\t\t\t\t\t\t\t\talt={item.alt || item.filename}\n\t\t\t\t\t\t\t\t\tclassName=\"max-h-full max-w-full object-contain\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) : isVideo ? (\n\t\t\t\t\t\t\t\t<video\n\t\t\t\t\t\t\t\t\tsrc={item.url}\n\t\t\t\t\t\t\t\t\tcontrols\n\t\t\t\t\t\t\t\t\tpreload=\"metadata\"\n\t\t\t\t\t\t\t\t\tclassName=\"max-h-full max-w-full\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t) : isAudio ? (\n\t\t\t\t\t\t\t\t<audio src={item.url} controls preload=\"metadata\" className=\"w-full\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<div className=\"text-center p-4\">\n\t\t\t\t\t\t\t\t\t<span className=\"text-4xl\">{getFileIcon(item.mimeType)}</span>\n\t\t\t\t\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">{item.mimeType}</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* File Info */}\n\t\t\t\t\t<div className=\"p-4 border-b space-y-3\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm\">\n\t\t\t\t\t\t\t<HardDrive className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Size:</span>\n\t\t\t\t\t\t\t<span>{formatFileSize(item.size)}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{item.width && item.height && (\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm\">\n\t\t\t\t\t\t\t\t<Ruler className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Dimensions:</span>\n\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t{item.width} × {item.height}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm\">\n\t\t\t\t\t\t\t<Calendar className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Uploaded:</span>\n\t\t\t\t\t\t\t<span>{formatDate(item.createdAt)}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Editable Fields */}\n\t\t\t\t\t<div className=\"p-4 space-y-4\">\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Filename\"\n\t\t\t\t\t\t\tvalue={filename}\n\t\t\t\t\t\t\tonChange={(e) => setFilename(e.target.value)}\n\t\t\t\t\t\t\tdisabled // Filename editing needs backend support\n\t\t\t\t\t\t\tdescription=\"Filename cannot be changed after upload\"\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t{isImage && (\n\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Alt Text\"\n\t\t\t\t\t\t\t\t\tvalue={alt}\n\t\t\t\t\t\t\t\t\tonChange={(e) => setAlt(e.target.value)}\n\t\t\t\t\t\t\t\t\tplaceholder=\"Describe this image for accessibility\"\n\t\t\t\t\t\t\t\t\tdescription=\"Used by screen readers and when image fails to load\"\n\t\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t\t<InputArea\n\t\t\t\t\t\t\t\t\tlabel=\"Caption\"\n\t\t\t\t\t\t\t\t\tvalue={caption}\n\t\t\t\t\t\t\t\t\tonChange={(e) => setCaption(e.target.value)}\n\t\t\t\t\t\t\t\t\tplaceholder=\"Optional caption for display\"\n\t\t\t\t\t\t\t\t\trows={2}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Footer */}\n\t\t\t\t<div className=\"p-4 border-t flex items-center justify-between gap-2\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\ticon={<Trash />}\n\t\t\t\t\t\tonClick={handleDelete}\n\t\t\t\t\t\tdisabled={deleteMutation.isPending}\n\t\t\t\t\t>\n\t\t\t\t\t\t{deleteMutation.isPending ? \"Deleting...\" : \"Delete\"}\n\t\t\t\t\t</Button>\n\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={onClose}>\n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\tonClick={handleSave}\n\t\t\t\t\t\t\tdisabled={!hasChanges || updateMutation.isPending}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{updateMutation.isPending ? \"Saving...\" : \"Save\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={showDeleteConfirm}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetShowDeleteConfirm(false);\n\t\t\t\t\tdeleteMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Delete Media?\"\n\t\t\t\tdescription={`Delete \"${item.filename}\"? This cannot be undone.`}\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={deleteMutation.isPending}\n\t\t\t\terror={deleteMutation.error}\n\t\t\t\tonConfirm={() => deleteMutation.mutate()}\n\t\t\t/>\n\t\t</>\n\t);\n}\n\nfunction formatDate(isoString: string): string {\n\treturn new Date(isoString).toLocaleDateString(undefined, {\n\t\tyear: \"numeric\",\n\t\tmonth: \"short\",\n\t\tday: \"numeric\",\n\t\thour: \"2-digit\",\n\t\tminute: \"2-digit\",\n\t});\n}\n\nexport default MediaDetailPanel;\n","import { Button, Input, Loader } from \"@cloudflare/kumo\";\nimport { Upload, Image, SquaresFour, List, MagnifyingGlass, Check, X } from \"@phosphor-icons/react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport {\n\ttype MediaItem,\n\ttype MediaProviderInfo,\n\ttype MediaProviderItem,\n\tfetchMediaProviders,\n\tfetchProviderMedia,\n\tuploadToProvider,\n} from \"../lib/api\";\nimport { providerItemToMediaItem, getFileIcon, formatFileSize } from \"../lib/media-utils\";\nimport { cn } from \"../lib/utils\";\nimport { MediaDetailPanel } from \"./MediaDetailPanel\";\n\nexport interface MediaLibraryProps {\n\titems?: MediaItem[];\n\tisLoading?: boolean;\n\tonUpload?: (file: File) => Promise<void> | void;\n\tonSelect?: (item: MediaItem) => void;\n\tonDelete?: (id: string) => void;\n\tonItemUpdated?: () => void;\n}\n\n/**\n * Media library component with upload, provider tabs, and grid view\n */\nexport function MediaLibrary({\n\titems = [],\n\tisLoading,\n\tonUpload,\n\tonDelete,\n\tonItemUpdated,\n}: MediaLibraryProps) {\n\tconst [viewMode, setViewMode] = React.useState<\"grid\" | \"list\">(\"grid\");\n\tconst [selectedItem, setSelectedItem] = React.useState<MediaItem | null>(null);\n\tconst [activeProvider, setActiveProvider] = React.useState<string>(\"local\");\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst [uploadState, setUploadState] = React.useState<{\n\t\tstatus: \"idle\" | \"uploading\" | \"success\" | \"error\";\n\t\tmessage?: string;\n\t\tprogress?: { current: number; total: number };\n\t}>({ status: \"idle\" });\n\tconst fileInputRef = React.useRef<HTMLInputElement>(null);\n\t// Track loaded image dimensions for providers that don't return them (e.g., CF Images)\n\tconst [loadedDimensions, setLoadedDimensions] = React.useState<\n\t\tRecord<string, { width: number; height: number }>\n\t>({});\n\n\t// Fetch available providers\n\tconst { data: providers } = useQuery({\n\t\tqueryKey: [\"media-providers\"],\n\t\tqueryFn: fetchMediaProviders,\n\t\tplaceholderData: [],\n\t});\n\n\t// Fetch provider media when a non-local provider is selected\n\tconst {\n\t\tdata: providerData,\n\t\tisLoading: providerLoading,\n\t\trefetch: refetchProviderMedia,\n\t} = useQuery({\n\t\tqueryKey: [\"provider-media\", activeProvider, searchQuery],\n\t\tqueryFn: () =>\n\t\t\tfetchProviderMedia(activeProvider, {\n\t\t\t\tlimit: 50,\n\t\t\t\tquery: searchQuery || undefined,\n\t\t\t}),\n\t\tenabled: activeProvider !== \"local\",\n\t});\n\n\t// Get active provider info\n\tconst activeProviderInfo = React.useMemo(() => {\n\t\tif (activeProvider === \"local\") {\n\t\t\treturn {\n\t\t\t\tid: \"local\",\n\t\t\t\tname: \"Library\",\n\t\t\t\tcapabilities: { browse: true, search: false, upload: true, delete: true },\n\t\t\t} as MediaProviderInfo;\n\t\t}\n\t\treturn providers?.find((p) => p.id === activeProvider);\n\t}, [activeProvider, providers]);\n\n\t// Update selected item when items change (e.g., after metadata update)\n\tReact.useEffect(() => {\n\t\tif (selectedItem && activeProvider === \"local\") {\n\t\t\tconst updated = items.find((i) => i.id === selectedItem.id);\n\t\t\tif (updated) {\n\t\t\t\tsetSelectedItem(updated);\n\t\t\t} else {\n\t\t\t\t// Item was deleted\n\t\t\t\tsetSelectedItem(null);\n\t\t\t}\n\t\t}\n\t}, [items, selectedItem?.id, activeProvider]);\n\n\t// Clear success/error message after a delay\n\tReact.useEffect(() => {\n\t\tif (uploadState.status === \"success\" || uploadState.status === \"error\") {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tsetUploadState({ status: \"idle\" });\n\t\t\t}, 3000);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [uploadState.status]);\n\n\tconst handleFileSelect = async (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst files = e.target.files;\n\t\tif (files && files.length > 0) {\n\t\t\tconst fileArray = [...files];\n\t\t\tconst total = fileArray.length;\n\n\t\t\tif (activeProvider === \"local\") {\n\t\t\t\tsetUploadState({ status: \"uploading\", progress: { current: 0, total } });\n\t\t\t\tlet uploaded = 0;\n\t\t\t\tlet failed = 0;\n\n\t\t\t\tfor (const file of fileArray) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait onUpload?.(file);\n\t\t\t\t\t\tuploaded++;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(\"Upload failed:\", error);\n\t\t\t\t\t\tfailed++;\n\t\t\t\t\t}\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"uploading\",\n\t\t\t\t\t\tprogress: { current: uploaded + failed, total },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (failed === 0) {\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\t\tmessage: total === 1 ? \"File uploaded\" : `${total} files uploaded`,\n\t\t\t\t\t});\n\t\t\t\t} else if (uploaded === 0) {\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\tmessage: total === 1 ? \"Upload failed\" : `All ${total} uploads failed`,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\tmessage: `${uploaded} uploaded, ${failed} failed`,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else if (activeProviderInfo?.capabilities.upload) {\n\t\t\t\t// Upload to external provider\n\t\t\t\tsetUploadState({ status: \"uploading\", progress: { current: 0, total } });\n\t\t\t\tlet uploaded = 0;\n\t\t\t\tlet failed = 0;\n\n\t\t\t\tfor (const file of fileArray) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait uploadToProvider(activeProvider, file);\n\t\t\t\t\t\tuploaded++;\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tconsole.error(\"Upload failed:\", error);\n\t\t\t\t\t\tfailed++;\n\t\t\t\t\t}\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"uploading\",\n\t\t\t\t\t\tprogress: { current: uploaded + failed, total },\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (failed === 0) {\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"success\",\n\t\t\t\t\t\tmessage: total === 1 ? \"File uploaded\" : `${total} files uploaded`,\n\t\t\t\t\t});\n\t\t\t\t} else if (uploaded === 0) {\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\tmessage: total === 1 ? \"Upload failed\" : `All ${total} uploads failed`,\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tsetUploadState({\n\t\t\t\t\t\tstatus: \"error\",\n\t\t\t\t\t\tmessage: `${uploaded} uploaded, ${failed} failed`,\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tvoid refetchProviderMedia();\n\t\t\t}\n\t\t}\n\t\t// Reset input\n\t\tif (fileInputRef.current) {\n\t\t\tfileInputRef.current.value = \"\";\n\t\t}\n\t};\n\n\t// Build provider tabs\n\tconst providerTabs = React.useMemo(() => {\n\t\tconst tabs: Array<{ id: string; name: string; icon?: string }> = [\n\t\t\t{ id: \"local\", name: \"Library\", icon: undefined },\n\t\t];\n\t\tif (providers) {\n\t\t\tfor (const p of providers) {\n\t\t\t\tif (p.id !== \"local\") {\n\t\t\t\t\ttabs.push({ id: p.id, name: p.name, icon: p.icon });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn tabs;\n\t}, [providers]);\n\n\t// Get current items based on active provider\n\tconst currentItems = activeProvider === \"local\" ? items : [];\n\tconst currentProviderItems = activeProvider !== \"local\" ? providerData?.items || [] : [];\n\tconst currentLoading = activeProvider === \"local\" ? isLoading : providerLoading;\n\n\tconst canUpload = activeProviderInfo?.capabilities.upload ?? false;\n\tconst canSearch = activeProviderInfo?.capabilities.search ?? false;\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Media Library</h1>\n\t\t\t\t<div className=\"flex rounded-md border\" role=\"group\" aria-label=\"View mode\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant={viewMode === \"grid\" ? \"secondary\" : \"ghost\"}\n\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\tonClick={() => setViewMode(\"grid\")}\n\t\t\t\t\t\taria-label=\"Grid view\"\n\t\t\t\t\t\taria-pressed={viewMode === \"grid\"}\n\t\t\t\t\t>\n\t\t\t\t\t\t<SquaresFour className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant={viewMode === \"list\" ? \"secondary\" : \"ghost\"}\n\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\tonClick={() => setViewMode(\"list\")}\n\t\t\t\t\t\taria-label=\"List view\"\n\t\t\t\t\t\taria-pressed={viewMode === \"list\"}\n\t\t\t\t\t>\n\t\t\t\t\t\t<List className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Provider Tabs + Upload */}\n\t\t\t<div className=\"flex items-center justify-between gap-4 border-b pb-3\">\n\t\t\t\t{providerTabs.length > 1 && (\n\t\t\t\t\t<div className=\"flex gap-2 overflow-x-auto\">\n\t\t\t\t\t\t{providerTabs.map((tab) => (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tkey={tab.id}\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\tsetActiveProvider(tab.id);\n\t\t\t\t\t\t\t\t\tsetSelectedItem(null);\n\t\t\t\t\t\t\t\t\tsetSearchQuery(\"\");\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md transition-colors whitespace-nowrap\",\n\t\t\t\t\t\t\t\t\tactiveProvider === tab.id\n\t\t\t\t\t\t\t\t\t\t? \"bg-kumo-brand text-white\"\n\t\t\t\t\t\t\t\t\t\t: \"bg-kumo-tint hover:bg-kumo-tint/80 text-kumo-subtle\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{tab.icon &&\n\t\t\t\t\t\t\t\t\t(tab.icon.startsWith(\"data:\") ? (\n\t\t\t\t\t\t\t\t\t\t<img src={tab.icon} alt=\"\" className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<span aria-hidden=\"true\">{tab.icon}</span>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t{tab.name}\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Upload button + status */}\n\t\t\t\t<div className=\"flex items-center gap-3 flex-shrink-0\">\n\t\t\t\t\t{/* Upload status feedback */}\n\t\t\t\t\t{uploadState.status === \"uploading\" && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t<Loader size=\"sm\" />\n\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\tUploading\n\t\t\t\t\t\t\t\t{uploadState.progress &&\n\t\t\t\t\t\t\t\t\tuploadState.progress.total > 1 &&\n\t\t\t\t\t\t\t\t\t` ${uploadState.progress.current}/${uploadState.progress.total}`}\n\t\t\t\t\t\t\t\t...\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{uploadState.status === \"success\" && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm text-green-600\">\n\t\t\t\t\t\t\t<Check className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t<span>{uploadState.message}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{uploadState.status === \"error\" && (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2 text-sm text-kumo-danger\">\n\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t<span>{uploadState.message}</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{canUpload && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tonClick={() => fileInputRef.current?.click()}\n\t\t\t\t\t\t\t\tdisabled={uploadState.status === \"uploading\"}\n\t\t\t\t\t\t\t\ticon={uploadState.status === \"uploading\" ? <Loader size=\"sm\" /> : <Upload />}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tUpload to {activeProviderInfo?.name || \"Library\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\tref={fileInputRef}\n\t\t\t\t\t\t\t\ttype=\"file\"\n\t\t\t\t\t\t\t\tmultiple\n\t\t\t\t\t\t\t\taccept=\"image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx\"\n\t\t\t\t\t\t\t\tclassName=\"sr-only\"\n\t\t\t\t\t\t\t\tonChange={handleFileSelect}\n\t\t\t\t\t\t\t\taria-label=\"Upload files\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Search (for providers that support it) */}\n\t\t\t{canSearch && (\n\t\t\t\t<div className=\"relative max-w-sm\">\n\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tplaceholder=\"Search...\"\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\tclassName=\"pl-9\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Content */}\n\t\t\t{currentLoading ? (\n\t\t\t\t<div className=\"flex items-center justify-center py-12\">\n\t\t\t\t\t<Loader />\n\t\t\t\t</div>\n\t\t\t) : activeProvider === \"local\" && currentItems.length === 0 ? (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t<Image className=\"mx-auto h-12 w-12 text-kumo-subtle\" aria-hidden=\"true\" />\n\t\t\t\t\t<h2 className=\"mt-4 text-lg font-medium\">No media yet</h2>\n\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\tUpload images, videos, and documents to get started.\n\t\t\t\t\t</p>\n\t\t\t\t\t<Button className=\"mt-4\" onClick={() => fileInputRef.current?.click()} icon={<Upload />}>\n\t\t\t\t\t\tUpload Files\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t) : activeProvider !== \"local\" && currentProviderItems.length === 0 ? (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t<Image className=\"mx-auto h-12 w-12 text-kumo-subtle\" aria-hidden=\"true\" />\n\t\t\t\t\t<h2 className=\"mt-4 text-lg font-medium\">No media found</h2>\n\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{canSearch && searchQuery\n\t\t\t\t\t\t\t? \"Try a different search term\"\n\t\t\t\t\t\t\t: canUpload\n\t\t\t\t\t\t\t\t? \"Upload media to get started\"\n\t\t\t\t\t\t\t\t: \"No media available from this provider\"}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t) : viewMode === \"grid\" ? (\n\t\t\t\t<div className=\"grid gap-4 grid-cols-[repeat(auto-fill,minmax(160px,1fr))]\">\n\t\t\t\t\t{activeProvider === \"local\"\n\t\t\t\t\t\t? currentItems.map((item) => (\n\t\t\t\t\t\t\t\t<MediaGridItem\n\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\tselected={selectedItem?.id === item.id}\n\t\t\t\t\t\t\t\t\tonClick={() => setSelectedItem(item)}\n\t\t\t\t\t\t\t\t\tonDelete={() => onDelete?.(item.id)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t: currentProviderItems.map((item) => (\n\t\t\t\t\t\t\t\t<ProviderGridItem\n\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\tselected={selectedItem?.id === item.id}\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t// Merge loaded dimensions if provider didn't return them\n\t\t\t\t\t\t\t\t\t\tconst dims = loadedDimensions[item.id];\n\t\t\t\t\t\t\t\t\t\tconst itemWithDims = dims\n\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t...item,\n\t\t\t\t\t\t\t\t\t\t\t\t\twidth: item.width ?? dims.width,\n\t\t\t\t\t\t\t\t\t\t\t\t\theight: item.height ?? dims.height,\n\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t: item;\n\t\t\t\t\t\t\t\t\t\tsetSelectedItem(providerItemToMediaItem(activeProvider, itemWithDims));\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\tonDimensionsLoaded={(width, height) => {\n\t\t\t\t\t\t\t\t\t\tsetLoadedDimensions((prev) => ({\n\t\t\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\t\t\t[item.id]: { width, height },\n\t\t\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"rounded-md border overflow-x-auto\">\n\t\t\t\t\t<table className=\"w-full\">\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr className=\"border-b bg-kumo-tint/50\">\n\t\t\t\t\t\t\t\t<th className=\"px-4 py-3 text-left text-sm font-medium\">Preview</th>\n\t\t\t\t\t\t\t\t<th className=\"px-4 py-3 text-left text-sm font-medium\">Filename</th>\n\t\t\t\t\t\t\t\t<th className=\"px-4 py-3 text-left text-sm font-medium\">Type</th>\n\t\t\t\t\t\t\t\t<th className=\"px-4 py-3 text-left text-sm font-medium\">Size</th>\n\t\t\t\t\t\t\t\t<th className=\"px-4 py-3 text-right text-sm font-medium\">Actions</th>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t</thead>\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t{activeProvider === \"local\"\n\t\t\t\t\t\t\t\t? currentItems.map((item) => (\n\t\t\t\t\t\t\t\t\t\t<MediaListItem\n\t\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\t\t\tselected={selectedItem?.id === item.id}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => setSelectedItem(item)}\n\t\t\t\t\t\t\t\t\t\t\tonDelete={() => onDelete?.(item.id)}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t))\n\t\t\t\t\t\t\t\t: currentProviderItems.map((item) => (\n\t\t\t\t\t\t\t\t\t\t<ProviderListItem\n\t\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\t\titem={item}\n\t\t\t\t\t\t\t\t\t\t\tselected={selectedItem?.id === item.id}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tconst dims = loadedDimensions[item.id];\n\t\t\t\t\t\t\t\t\t\t\t\tconst itemWithDims = dims\n\t\t\t\t\t\t\t\t\t\t\t\t\t? {\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t...item,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\twidth: item.width ?? dims.width,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: item.height ?? dims.height,\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\t\t\t: item;\n\t\t\t\t\t\t\t\t\t\t\t\tsetSelectedItem(providerItemToMediaItem(activeProvider, itemWithDims));\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\tonDimensionsLoaded={(width, height) => {\n\t\t\t\t\t\t\t\t\t\t\t\tsetLoadedDimensions((prev) => ({\n\t\t\t\t\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\t\t\t\t\t[item.id]: { width, height },\n\t\t\t\t\t\t\t\t\t\t\t\t}));\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</tbody>\n\t\t\t\t\t</table>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Detail Panel */}\n\t\t\t{selectedItem && (\n\t\t\t\t<MediaDetailPanel\n\t\t\t\t\titem={selectedItem}\n\t\t\t\t\tonClose={() => setSelectedItem(null)}\n\t\t\t\t\tonDeleted={() => {\n\t\t\t\t\t\tif (activeProvider === \"local\") {\n\t\t\t\t\t\t\tonDelete?.(selectedItem.id);\n\t\t\t\t\t\t\tonItemUpdated?.();\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tvoid refetchProviderMedia();\n\t\t\t\t\t\t}\n\t\t\t\t\t}}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\ninterface MediaGridItemProps {\n\titem: MediaItem;\n\tselected?: boolean;\n\tonClick?: () => void;\n\tonDelete: () => void;\n}\n\nfunction MediaGridItem({ item, selected, onClick }: MediaGridItemProps) {\n\tconst isImage = item.mimeType.startsWith(\"image/\");\n\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={onClick}\n\t\t\tclassName={cn(\n\t\t\t\t\"group relative overflow-hidden rounded-lg border bg-kumo-base text-left transition-all max-w-[200px]\",\n\t\t\t\tselected ? \"ring-2 ring-kumo-brand border-kumo-brand\" : \"hover:border-kumo-brand/50\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"aspect-square\">\n\t\t\t\t{isImage ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={item.url}\n\t\t\t\t\t\talt={item.alt || item.filename}\n\t\t\t\t\t\tclassName=\"h-full w-full object-cover\"\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"flex h-full w-full items-center justify-center bg-kumo-tint\">\n\t\t\t\t\t\t<span className=\"text-4xl\">{getFileIcon(item.mimeType)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"absolute inset-0 flex items-end bg-gradient-to-t from-black/60 to-transparent opacity-0 transition-opacity group-hover:opacity-100\">\n\t\t\t\t<div className=\"w-full p-3\">\n\t\t\t\t\t<p className=\"truncate text-sm font-medium text-white\">{item.filename}</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</button>\n\t);\n}\n\ninterface ProviderGridItemProps {\n\titem: MediaProviderItem;\n\tselected?: boolean;\n\tonClick?: () => void;\n\t/** Callback when image dimensions are loaded (for providers that don't return dimensions) */\n\tonDimensionsLoaded?: (width: number, height: number) => void;\n}\n\nfunction ProviderGridItem({ item, selected, onClick, onDimensionsLoaded }: ProviderGridItemProps) {\n\tconst isImage = item.mimeType.startsWith(\"image/\");\n\n\tconst handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n\t\tconst img = e.currentTarget;\n\t\t// Only report if we don't already have dimensions\n\t\tif (onDimensionsLoaded && (!item.width || !item.height)) {\n\t\t\tonDimensionsLoaded(img.naturalWidth, img.naturalHeight);\n\t\t}\n\t};\n\n\treturn (\n\t\t<button\n\t\t\ttype=\"button\"\n\t\t\tonClick={onClick}\n\t\t\tclassName={cn(\n\t\t\t\t\"group relative overflow-hidden rounded-lg border bg-kumo-base text-left transition-all max-w-[200px]\",\n\t\t\t\tselected ? \"ring-2 ring-kumo-brand border-kumo-brand\" : \"hover:border-kumo-brand/50\",\n\t\t\t)}\n\t\t>\n\t\t\t<div className=\"aspect-square\">\n\t\t\t\t{isImage && item.previewUrl ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={item.previewUrl}\n\t\t\t\t\t\talt={item.alt || item.filename}\n\t\t\t\t\t\tclassName=\"h-full w-full object-cover\"\n\t\t\t\t\t\tonLoad={handleImageLoad}\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"flex h-full w-full items-center justify-center bg-kumo-tint\">\n\t\t\t\t\t\t<span className=\"text-4xl\">{getFileIcon(item.mimeType)}</span>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t<div className=\"absolute inset-0 flex items-end bg-gradient-to-t from-black/60 to-transparent opacity-0 transition-opacity group-hover:opacity-100\">\n\t\t\t\t<div className=\"w-full p-3\">\n\t\t\t\t\t<p className=\"truncate text-sm font-medium text-white\">{item.filename}</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</button>\n\t);\n}\n\ninterface MediaListItemProps {\n\titem: MediaItem;\n\tselected?: boolean;\n\tonClick?: () => void;\n\tonDelete: () => void;\n}\n\nfunction MediaListItem({ item, selected, onClick }: MediaListItemProps) {\n\tconst isImage = item.mimeType.startsWith(\"image/\");\n\n\treturn (\n\t\t<tr\n\t\t\tclassName={cn(\n\t\t\t\t\"border-b cursor-pointer transition-colors\",\n\t\t\t\tselected ? \"bg-kumo-brand/10\" : \"hover:bg-kumo-tint/25\",\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<div className=\"h-10 w-10 overflow-hidden rounded\">\n\t\t\t\t\t{isImage ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={item.url}\n\t\t\t\t\t\t\talt={item.alt || item.filename}\n\t\t\t\t\t\t\tclassName=\"h-full w-full object-cover\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"flex h-full w-full items-center justify-center bg-kumo-tint text-xl\">\n\t\t\t\t\t\t\t{getFileIcon(item.mimeType)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 font-medium\">{item.filename}</td>\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle\">{item.mimeType}</td>\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle\">{formatFileSize(item.size)}</td>\n\t\t\t<td className=\"px-4 py-3 text-right\">\n\t\t\t\t<span className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t{item.alt ? \"Alt text set\" : \"No alt text\"}\n\t\t\t\t</span>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\ninterface ProviderListItemProps {\n\titem: MediaProviderItem;\n\tselected?: boolean;\n\tonClick?: () => void;\n\t/** Callback when image dimensions are loaded (for providers that don't return dimensions) */\n\tonDimensionsLoaded?: (width: number, height: number) => void;\n}\n\nfunction ProviderListItem({ item, selected, onClick, onDimensionsLoaded }: ProviderListItemProps) {\n\tconst isImage = item.mimeType.startsWith(\"image/\");\n\n\tconst handleImageLoad = (e: React.SyntheticEvent<HTMLImageElement>) => {\n\t\tconst img = e.currentTarget;\n\t\tif (onDimensionsLoaded && (!item.width || !item.height)) {\n\t\t\tonDimensionsLoaded(img.naturalWidth, img.naturalHeight);\n\t\t}\n\t};\n\n\treturn (\n\t\t<tr\n\t\t\tclassName={cn(\n\t\t\t\t\"border-b cursor-pointer transition-colors\",\n\t\t\t\tselected ? \"bg-kumo-brand/10\" : \"hover:bg-kumo-tint/25\",\n\t\t\t)}\n\t\t\tonClick={onClick}\n\t\t>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<div className=\"h-10 w-10 overflow-hidden rounded\">\n\t\t\t\t\t{isImage && item.previewUrl ? (\n\t\t\t\t\t\t<img\n\t\t\t\t\t\t\tsrc={item.previewUrl}\n\t\t\t\t\t\t\talt={item.alt || item.filename}\n\t\t\t\t\t\t\tclassName=\"h-full w-full object-cover\"\n\t\t\t\t\t\t\tonLoad={handleImageLoad}\n\t\t\t\t\t\t/>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"flex h-full w-full items-center justify-center bg-kumo-tint text-xl\">\n\t\t\t\t\t\t\t{getFileIcon(item.mimeType)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 font-medium\">{item.filename}</td>\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle\">{item.mimeType}</td>\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle\">\n\t\t\t\t{item.size ? formatFileSize(item.size) : \"—\"}\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 text-right\">\n\t\t\t\t<span className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t{item.alt ? \"Alt text set\" : \"No alt text\"}\n\t\t\t\t</span>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\nexport default MediaLibrary;\n","/**\n * Content Picker Modal\n *\n * A modal for browsing and selecting content items to add to menus.\n * Uses cursor pagination to allow browsing beyond the initial page.\n */\n\nimport { Button, Dialog, Input, Loader } from \"@cloudflare/kumo\";\nimport { MagnifyingGlass, FolderOpen, X } from \"@phosphor-icons/react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { fetchCollections, fetchContentList, getDraftStatus } from \"../lib/api\";\nimport type { ContentItem } from \"../lib/api\";\nimport { useDebouncedValue } from \"../lib/hooks\";\nimport { cn } from \"../lib/utils\";\n\ninterface ContentPickerModalProps {\n\topen: boolean;\n\tonOpenChange: (open: boolean) => void;\n\tonSelect: (item: { collection: string; id: string; title: string }) => void;\n}\n\nfunction getItemTitle(item: { data: Record<string, unknown>; slug: string | null; id: string }) {\n\tconst rawTitle = item.data.title;\n\tconst rawName = item.data.name;\n\treturn (\n\t\t(typeof rawTitle === \"string\" ? rawTitle : \"\") ||\n\t\t(typeof rawName === \"string\" ? rawName : \"\") ||\n\t\titem.slug ||\n\t\titem.id\n\t);\n}\n\nexport function ContentPickerModal({ open, onOpenChange, onSelect }: ContentPickerModalProps) {\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst debouncedSearch = useDebouncedValue(searchQuery, 300);\n\tconst [selectedCollection, setSelectedCollection] = React.useState<string>(\"\");\n\tconst [allItems, setAllItems] = React.useState<ContentItem[]>([]);\n\tconst [nextCursor, setNextCursor] = React.useState<string | undefined>();\n\tconst [isLoadingMore, setIsLoadingMore] = React.useState(false);\n\n\tconst { data: collections = [] } = useQuery({\n\t\tqueryKey: [\"collections\"],\n\t\tqueryFn: fetchCollections,\n\t\tenabled: open,\n\t});\n\n\t// Default to first collection when collections load\n\tReact.useEffect(() => {\n\t\tif (collections.length > 0 && !selectedCollection) {\n\t\t\tsetSelectedCollection(collections[0]!.slug);\n\t\t}\n\t}, [collections, selectedCollection]);\n\n\tconst { data: contentResult, isLoading: contentLoading } = useQuery({\n\t\tqueryKey: [\"content-picker\", selectedCollection, { limit: 50 }],\n\t\tqueryFn: () => fetchContentList(selectedCollection, { limit: 50 }),\n\t\tenabled: open && !!selectedCollection,\n\t});\n\n\t// Sync initial page into accumulated items\n\tReact.useEffect(() => {\n\t\tif (contentResult) {\n\t\t\tsetAllItems(contentResult.items);\n\t\t\tsetNextCursor(contentResult.nextCursor);\n\t\t}\n\t}, [contentResult]);\n\n\tconst handleLoadMore = async () => {\n\t\tif (!nextCursor || isLoadingMore) return;\n\t\tsetIsLoadingMore(true);\n\t\ttry {\n\t\t\tconst result = await fetchContentList(selectedCollection, {\n\t\t\t\tlimit: 50,\n\t\t\t\tcursor: nextCursor,\n\t\t\t});\n\t\t\tsetAllItems((prev) => [...prev, ...result.items]);\n\t\t\tsetNextCursor(result.nextCursor);\n\t\t} finally {\n\t\t\tsetIsLoadingMore(false);\n\t\t}\n\t};\n\n\tconst filteredItems = React.useMemo(() => {\n\t\tif (!debouncedSearch) return allItems;\n\t\tconst query = debouncedSearch.toLowerCase();\n\t\treturn allItems.filter((item) => getItemTitle(item).toLowerCase().includes(query));\n\t}, [allItems, debouncedSearch]);\n\n\t// Reset state when modal opens or collection changes\n\tReact.useEffect(() => {\n\t\tif (open) {\n\t\t\tsetSearchQuery(\"\");\n\t\t\tsetSelectedCollection(\"\");\n\t\t\tsetAllItems([]);\n\t\t\tsetNextCursor(undefined);\n\t\t}\n\t}, [open]);\n\n\tconst handleSelect = (item: ContentItem) => {\n\t\tonSelect({\n\t\t\tcollection: selectedCollection,\n\t\t\tid: item.id,\n\t\t\ttitle: getItemTitle(item),\n\t\t});\n\t\tonOpenChange(false);\n\t};\n\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={onOpenChange}>\n\t\t\t<Dialog className=\"p-6 w-2xl h-[80vh] flex flex-col\" size=\"lg\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\tSelect Content\n\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Search and collection filter */}\n\t\t\t\t<div className=\"flex items-center gap-4 py-4 border-b\">\n\t\t\t\t\t<div className=\"relative flex-1\">\n\t\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tplaceholder=\"Search content...\"\n\t\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\t\tclassName=\"pl-10\"\n\t\t\t\t\t\t\tautoFocus\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t\t<select\n\t\t\t\t\t\tvalue={selectedCollection}\n\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\tsetSelectedCollection(e.target.value);\n\t\t\t\t\t\t\tsetAllItems([]);\n\t\t\t\t\t\t\tsetNextCursor(undefined);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tclassName=\"h-10 rounded-md border border-kumo-line bg-kumo-base px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-kumo-ring focus:ring-offset-2\"\n\t\t\t\t\t>\n\t\t\t\t\t\t{collections.map((col) => (\n\t\t\t\t\t\t\t<option key={col.slug} value={col.slug}>\n\t\t\t\t\t\t\t\t{col.label}\n\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</select>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Content list */}\n\t\t\t\t<div className=\"flex-1 overflow-y-auto py-4\">\n\t\t\t\t\t{contentLoading ? (\n\t\t\t\t\t\t<div className=\"flex items-center justify-center h-32\">\n\t\t\t\t\t\t\t<div className=\"text-kumo-subtle\">Loading content...</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : filteredItems.length === 0 ? (\n\t\t\t\t\t\t<div className=\"flex flex-col items-center justify-center h-32 text-center\">\n\t\t\t\t\t\t\t{searchQuery ? (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<MagnifyingGlass className=\"h-8 w-8 text-kumo-subtle mb-2\" />\n\t\t\t\t\t\t\t\t\t<p className=\"text-kumo-subtle\">No content found</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">Try adjusting your search</p>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t<FolderOpen className=\"h-8 w-8 text-kumo-subtle mb-2\" />\n\t\t\t\t\t\t\t\t\t<p className=\"text-kumo-subtle\">No content in this collection</p>\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t\t\t{filteredItems.map((item) => {\n\t\t\t\t\t\t\t\tconst status = getDraftStatus(item);\n\t\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => handleSelect(item)}\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"w-full text-left rounded-md px-3 py-2 transition-colors\",\n\t\t\t\t\t\t\t\t\t\t\t\"hover:bg-kumo-tint/50\",\n\t\t\t\t\t\t\t\t\t\t\t\"focus:outline-none focus:ring-2 focus:ring-kumo-ring focus:ring-offset-2\",\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<div className=\"font-medium\">{getItemTitle(item)}</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"text-sm text-kumo-subtle flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\"inline-block h-2 w-2 rounded-full\",\n\t\t\t\t\t\t\t\t\t\t\t\t\tstatus === \"published\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-green-500\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t: status === \"published_with_changes\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-yellow-500\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t: \"bg-gray-400\",\n\t\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t{status === \"published\"\n\t\t\t\t\t\t\t\t\t\t\t\t? \"Published\"\n\t\t\t\t\t\t\t\t\t\t\t\t: status === \"published_with_changes\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? \"Modified\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t: \"Draft\"}\n\t\t\t\t\t\t\t\t\t\t\t{item.slug && (\n\t\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle/50\">/</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span>{item.slug}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t})}\n\t\t\t\t\t\t\t{nextCursor && !searchQuery && (\n\t\t\t\t\t\t\t\t<div className=\"pt-2 text-center\">\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\tonClick={handleLoadMore}\n\t\t\t\t\t\t\t\t\t\tdisabled={isLoadingMore}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isLoadingMore ? (\n\t\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\t\t<Loader size=\"sm\" /> Loading...\n\t\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t\t\"Load more\"\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Footer */}\n\t\t\t\t<div className=\"flex justify-end gap-2 pt-4 border-t\">\n\t\t\t\t\t<Button variant=\"outline\" onClick={() => onOpenChange(false)}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n","/**\n * Menu Editor component\n *\n * Edit menu items with basic reordering (simplified version without drag-and-drop)\n */\n\nimport { Button, Dialog, Input, Select, Toast } from \"@cloudflare/kumo\";\nimport {\n\tPlus,\n\tTrash,\n\tCaretUp,\n\tCaretDown,\n\tLink as LinkIcon,\n\tArrowLeft,\n\tX,\n\tFile as FileIcon,\n} from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useParams, useNavigate } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tfetchMenu,\n\tcreateMenuItem,\n\tdeleteMenuItem,\n\tupdateMenuItem,\n\treorderMenuItems,\n\ttype MenuItem,\n} from \"../lib/api\";\nimport { ContentPickerModal } from \"./ContentPickerModal\";\nimport { DialogError, getMutationError } from \"./DialogError.js\";\n\nexport function MenuEditor() {\n\tconst { name } = useParams({ from: \"/_admin/menus/$name\" });\n\tconst navigate = useNavigate();\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\tconst [isAddOpen, setIsAddOpen] = React.useState(false);\n\tconst [isContentPickerOpen, setIsContentPickerOpen] = React.useState(false);\n\tconst [editingItem, setEditingItem] = React.useState<MenuItem | null>(null);\n\tconst [localItems, setLocalItems] = React.useState<MenuItem[]>([]);\n\tconst [addError, setAddError] = React.useState<string | null>(null);\n\tconst [editError, setEditError] = React.useState<string | null>(null);\n\n\tconst { data: menu, isLoading } = useQuery({\n\t\tqueryKey: [\"menu\", name],\n\t\tqueryFn: () => fetchMenu(name),\n\t\tstaleTime: Infinity,\n\t});\n\n\t// Sync local items with fetched data\n\tReact.useEffect(() => {\n\t\tif (menu?.items) {\n\t\t\tsetLocalItems(menu.items);\n\t\t}\n\t}, [menu]);\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: (input: Parameters<typeof createMenuItem>[1]) => createMenuItem(name, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"menu\", name] });\n\t\t\tsetIsAddOpen(false);\n\t\t\ttoastManager.add({ title: \"Item added\", description: \"Menu item has been added.\" });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\tsetAddError(error.message);\n\t\t},\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (itemId: string) => deleteMenuItem(name, itemId),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"menu\", name] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Item deleted\",\n\t\t\t\tdescription: \"Menu item has been deleted.\",\n\t\t\t});\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Error\",\n\t\t\t\tdescription: error.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: ({\n\t\t\titemId,\n\t\t\tinput,\n\t\t}: {\n\t\t\titemId: string;\n\t\t\tinput: Parameters<typeof updateMenuItem>[2];\n\t\t}) => updateMenuItem(name, itemId, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"menu\", name] });\n\t\t\tsetEditingItem(null);\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Item updated\",\n\t\t\t\tdescription: \"Menu item has been updated.\",\n\t\t\t});\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\tsetEditError(error.message);\n\t\t},\n\t});\n\n\tconst reorderMutation = useMutation({\n\t\tmutationFn: (input: Parameters<typeof reorderMenuItems>[1]) => reorderMenuItems(name, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"menu\", name] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Order saved\",\n\t\t\t\tdescription: \"Menu order has been updated.\",\n\t\t\t});\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Error\",\n\t\t\t\tdescription: error.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleAddCustomLink = (e: React.FormEvent<HTMLFormElement>) => {\n\t\te.preventDefault();\n\t\tsetAddError(null);\n\t\tconst formData = new FormData(e.currentTarget);\n\t\tconst labelVal = formData.get(\"label\");\n\t\tconst urlVal = formData.get(\"url\");\n\t\tconst targetVal = formData.get(\"target\");\n\t\tcreateMutation.mutate({\n\t\t\ttype: \"custom\",\n\t\t\tlabel: typeof labelVal === \"string\" ? labelVal : \"\",\n\t\t\tcustomUrl: typeof urlVal === \"string\" ? urlVal : \"\",\n\t\t\ttarget: (typeof targetVal === \"string\" ? targetVal : \"\") || undefined,\n\t\t});\n\t};\n\n\tconst handleAddContent = (item: { collection: string; id: string; title: string }) => {\n\t\tcreateMutation.mutate({\n\t\t\ttype: item.collection,\n\t\t\tlabel: item.title,\n\t\t\treferenceCollection: item.collection,\n\t\t\treferenceId: item.id,\n\t\t});\n\t};\n\n\tconst handleUpdateItem = (e: React.FormEvent<HTMLFormElement>) => {\n\t\te.preventDefault();\n\t\tsetEditError(null);\n\t\tif (!editingItem) return;\n\t\tconst formData = new FormData(e.currentTarget);\n\t\tconst uLabelVal = formData.get(\"label\");\n\t\tconst uUrlVal = formData.get(\"url\");\n\t\tconst uTargetVal = formData.get(\"target\");\n\t\tupdateMutation.mutate({\n\t\t\titemId: editingItem.id,\n\t\t\tinput: {\n\t\t\t\tlabel: typeof uLabelVal === \"string\" ? uLabelVal : \"\",\n\t\t\t\tcustomUrl:\n\t\t\t\t\teditingItem.type === \"custom\" ? (typeof uUrlVal === \"string\" ? uUrlVal : \"\") : undefined,\n\t\t\t\ttarget: (typeof uTargetVal === \"string\" ? uTargetVal : \"\") || undefined,\n\t\t\t},\n\t\t});\n\t};\n\n\tconst moveItem = (index: number, direction: \"up\" | \"down\") => {\n\t\tconst newItems = [...localItems];\n\t\tconst targetIndex = direction === \"up\" ? index - 1 : index + 1;\n\t\tif (targetIndex < 0 || targetIndex >= newItems.length) return;\n\n\t\tconst currentItem = newItems[index];\n\t\tconst targetItem = newItems[targetIndex];\n\t\tif (!currentItem || !targetItem) return;\n\n\t\tnewItems[index] = targetItem;\n\t\tnewItems[targetIndex] = currentItem;\n\n\t\t// Update sort orders\n\t\tconst reorderedItems = newItems.map((item, i) => ({\n\t\t\tid: item.id,\n\t\t\tparentId: item.parent_id,\n\t\t\tsortOrder: i,\n\t\t}));\n\n\t\tsetLocalItems(newItems);\n\t\treorderMutation.mutate({ items: reorderedItems });\n\t};\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center h-64\">\n\t\t\t\t<div className=\"text-kumo-subtle\">Loading menu...</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (!menu) {\n\t\treturn (\n\t\t\t<div className=\"text-center py-12\">\n\t\t\t\t<p className=\"text-kumo-subtle\">Menu not found</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\taria-label=\"Back\"\n\t\t\t\t\t\tonClick={() => navigate({ to: \"/menus\" })}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h1 className=\"text-3xl font-bold\">{menu.label}</h1>\n\t\t\t\t\t\t<p className=\"text-kumo-subtle\">Edit menu items</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\ticon={<FileIcon />}\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tonClick={() => setIsContentPickerOpen(true)}\n\t\t\t\t\t>\n\t\t\t\t\t\tAdd Content\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Dialog.Root\n\t\t\t\t\t\topen={isAddOpen}\n\t\t\t\t\t\tonOpenChange={(open) => {\n\t\t\t\t\t\t\tsetIsAddOpen(open);\n\t\t\t\t\t\t\tif (!open) setAddError(null);\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t<Button {...props} icon={<Plus />}>\n\t\t\t\t\t\t\t\t\tAdd Custom Link\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t\t\tAdd Custom Link\n\t\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<form onSubmit={handleAddCustomLink} className=\"space-y-4\">\n\t\t\t\t\t\t\t\t<Input label=\"Label\" name=\"label\" required placeholder=\"Home\" />\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"URL\"\n\t\t\t\t\t\t\t\t\tname=\"url\"\n\t\t\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\t\tplaceholder=\"https://example.com\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\tlabel=\"Target\"\n\t\t\t\t\t\t\t\t\tname=\"target\"\n\t\t\t\t\t\t\t\t\tdefaultValue=\"\"\n\t\t\t\t\t\t\t\t\titems={{ \"\": \"Same window\", _blank: \"New window\" }}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Select.Option value=\"\">Same window</Select.Option>\n\t\t\t\t\t\t\t\t\t<Select.Option value=\"_blank\">New window</Select.Option>\n\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t<DialogError message={addError || getMutationError(createMutation.error)} />\n\t\t\t\t\t\t\t\t<div className=\"flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={() => setIsAddOpen(false)}>\n\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t<Button type=\"submit\" disabled={createMutation.isPending}>\n\t\t\t\t\t\t\t\t\t\t{createMutation.isPending ? \"Adding...\" : \"Add\"}\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t</Dialog>\n\t\t\t\t\t</Dialog.Root>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<ContentPickerModal\n\t\t\t\topen={isContentPickerOpen}\n\t\t\t\tonOpenChange={setIsContentPickerOpen}\n\t\t\t\tonSelect={handleAddContent}\n\t\t\t/>\n\n\t\t\t{localItems.length === 0 ? (\n\t\t\t\t<div className=\"border rounded-lg p-12 text-center\">\n\t\t\t\t\t<LinkIcon className=\"mx-auto h-12 w-12 text-kumo-subtle mb-4\" />\n\t\t\t\t\t<h3 className=\"text-lg font-semibold mb-2\">No menu items yet</h3>\n\t\t\t\t\t<p className=\"text-kumo-subtle mb-4\">Add links to build your navigation menu</p>\n\t\t\t\t\t<div className=\"flex justify-center gap-2\">\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ticon={<FileIcon />}\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tonClick={() => setIsContentPickerOpen(true)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tAdd Content\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button icon={<Plus />} onClick={() => setIsAddOpen(true)}>\n\t\t\t\t\t\t\tAdd Custom Link\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t{localItems.map((item, index) => (\n\t\t\t\t\t\t<div key={item.id} className=\"border rounded-lg p-4 flex items-center justify-between\">\n\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t<div className=\"font-medium\">{item.label}</div>\n\t\t\t\t\t\t\t\t<div className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t{item.type === \"custom\" ? (\n\t\t\t\t\t\t\t\t\t\titem.custom_url\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<span className=\"inline-flex items-center rounded-full bg-kumo-brand/10 px-2 py-0.5 text-xs font-medium text-kumo-brand\">\n\t\t\t\t\t\t\t\t\t\t\t{item.reference_collection ?? item.type}\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{item.target === \"_blank\" && \" (opens in new window)\"}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\taria-label=\"Move up\"\n\t\t\t\t\t\t\t\t\tonClick={() => moveItem(index, \"up\")}\n\t\t\t\t\t\t\t\t\tdisabled={index === 0}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<CaretUp className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\taria-label=\"Move down\"\n\t\t\t\t\t\t\t\t\tonClick={() => moveItem(index, \"down\")}\n\t\t\t\t\t\t\t\t\tdisabled={index === localItems.length - 1}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<CaretDown className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={() => setEditingItem(item)}>\n\t\t\t\t\t\t\t\t\tEdit\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\taria-label=\"Delete\"\n\t\t\t\t\t\t\t\t\tonClick={() => deleteMutation.mutate(item.id)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<Dialog.Root\n\t\t\t\topen={editingItem !== null}\n\t\t\t\tonOpenChange={(open: boolean) => {\n\t\t\t\t\tif (!open) {\n\t\t\t\t\t\tsetEditingItem(null);\n\t\t\t\t\t\tsetEditError(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t>\n\t\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\tEdit Menu Item\n\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t\t{editingItem && (\n\t\t\t\t\t\t<form onSubmit={handleUpdateItem} className=\"space-y-4\">\n\t\t\t\t\t\t\t<Input label=\"Label\" name=\"label\" required defaultValue={editingItem.label} />\n\t\t\t\t\t\t\t{editingItem.type === \"custom\" && (\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"URL\"\n\t\t\t\t\t\t\t\t\tname=\"url\"\n\t\t\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\t\tdefaultValue={editingItem.custom_url || \"\"}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\tlabel=\"Target\"\n\t\t\t\t\t\t\t\tname=\"target\"\n\t\t\t\t\t\t\t\tdefaultValue={editingItem.target || \"\"}\n\t\t\t\t\t\t\t\titems={{ \"\": \"Same window\", _blank: \"New window\" }}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Select.Option value=\"\">Same window</Select.Option>\n\t\t\t\t\t\t\t\t<Select.Option value=\"_blank\">New window</Select.Option>\n\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t<DialogError message={editError || getMutationError(updateMutation.error)} />\n\t\t\t\t\t\t\t<div className=\"flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={() => setEditingItem(null)}>\n\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button type=\"submit\" disabled={updateMutation.isPending}>\n\t\t\t\t\t\t\t\t\t{updateMutation.isPending ? \"Saving...\" : \"Save\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</form>\n\t\t\t\t\t)}\n\t\t\t\t</Dialog>\n\t\t\t</Dialog.Root>\n\t\t</div>\n\t);\n}\n","/**\n * Menu List component\n *\n * Displays all menus with ability to create, edit, and delete.\n */\n\nimport { Button, Dialog, Input, Toast, buttonVariants } from \"@cloudflare/kumo\";\nimport { Plus, Pencil, Trash, List as ListIcon } from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { Link, useNavigate } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchMenus, createMenu, deleteMenu } from \"../lib/api\";\nimport { ConfirmDialog } from \"./ConfirmDialog.js\";\nimport { DialogError, getMutationError } from \"./DialogError.js\";\n\nexport function MenuList() {\n\tconst queryClient = useQueryClient();\n\tconst navigate = useNavigate();\n\tconst toastManager = Toast.useToastManager();\n\tconst [isCreateOpen, setIsCreateOpen] = React.useState(false);\n\tconst [deleteMenuName, setDeleteMenuName] = React.useState<string | null>(null);\n\tconst [createError, setCreateError] = React.useState<string | null>(null);\n\n\tconst { data: menus, isLoading } = useQuery({\n\t\tqueryKey: [\"menus\"],\n\t\tqueryFn: fetchMenus,\n\t});\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: createMenu,\n\t\tonSuccess: (menu) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"menus\"] });\n\t\t\tsetIsCreateOpen(false);\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Menu created\",\n\t\t\t\tdescription: `Menu \"${menu.label}\" has been created.`,\n\t\t\t});\n\t\t\tvoid navigate({ to: \"/menus/$name\", params: { name: menu.name } });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\tsetCreateError(error.message);\n\t\t},\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: deleteMenu,\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"menus\"] });\n\t\t\tsetDeleteMenuName(null);\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Menu deleted\",\n\t\t\t\tdescription: \"The menu has been deleted.\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleCreate = (e: React.FormEvent<HTMLFormElement>) => {\n\t\te.preventDefault();\n\t\tsetCreateError(null);\n\t\tconst formData = new FormData(e.currentTarget);\n\t\tconst nameVal = formData.get(\"name\");\n\t\tconst name = typeof nameVal === \"string\" ? nameVal : \"\";\n\t\tconst labelVal = formData.get(\"label\");\n\t\tconst label = typeof labelVal === \"string\" ? labelVal : \"\";\n\t\tcreateMutation.mutate({ name, label });\n\t};\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center h-64\">\n\t\t\t\t<div className=\"text-kumo-subtle\">Loading menus...</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"text-3xl font-bold\">Menus</h1>\n\t\t\t\t\t<p className=\"text-kumo-subtle\">Manage navigation menus for your site</p>\n\t\t\t\t</div>\n\t\t\t\t<Dialog.Root\n\t\t\t\t\topen={isCreateOpen}\n\t\t\t\t\tonOpenChange={(open) => {\n\t\t\t\t\t\tsetIsCreateOpen(open);\n\t\t\t\t\t\tif (!open) setCreateError(null);\n\t\t\t\t\t}}\n\t\t\t\t>\n\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button {...props} icon={<Plus />}>\n\t\t\t\t\t\t\t\tCreate Menu\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t\tCreate New Menu\n\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<form onSubmit={handleCreate} className=\"space-y-4\">\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Name\"\n\t\t\t\t\t\t\t\t\tname=\"name\"\n\t\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\t\tplaceholder=\"primary\"\n\t\t\t\t\t\t\t\t\tpattern=\"[a-z0-9-]+\"\n\t\t\t\t\t\t\t\t\ttitle=\"Only lowercase letters, numbers, and hyphens\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\t\tURL-friendly identifier (e.g., \"primary\", \"footer\")\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<Input label=\"Label\" name=\"label\" required placeholder=\"Primary Navigation\" />\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">Display name for admin interface</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<DialogError message={createError || getMutationError(createMutation.error)} />\n\t\t\t\t\t\t\t<div className=\"flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={() => setIsCreateOpen(false)}>\n\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button type=\"submit\" disabled={createMutation.isPending}>\n\t\t\t\t\t\t\t\t\t{createMutation.isPending ? \"Creating...\" : \"Create\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</form>\n\t\t\t\t\t</Dialog>\n\t\t\t\t</Dialog.Root>\n\t\t\t</div>\n\n\t\t\t{!menus || menus.length === 0 ? (\n\t\t\t\t<div className=\"border rounded-lg p-12 text-center\">\n\t\t\t\t\t<ListIcon className=\"mx-auto h-12 w-12 text-kumo-subtle mb-4\" />\n\t\t\t\t\t<h3 className=\"text-lg font-semibold mb-2\">No menus yet</h3>\n\t\t\t\t\t<p className=\"text-kumo-subtle mb-4\">Create your first navigation menu to get started</p>\n\t\t\t\t\t<Button icon={<Plus />} onClick={() => setIsCreateOpen(true)}>\n\t\t\t\t\t\tCreate Menu\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"grid gap-4\">\n\t\t\t\t\t{menus.map((menu) => (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tkey={menu.id}\n\t\t\t\t\t\t\tclassName=\"border rounded-lg p-6 flex items-center justify-between hover:bg-kumo-tint transition-colors\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Link to=\"/menus/$name\" params={{ name: menu.name }} className=\"flex-1\">\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<h3 className=\"font-semibold text-lg\">{menu.label}</h3>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t{menu.name} • {menu.itemCount || 0} items\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\t\tto=\"/menus/$name\"\n\t\t\t\t\t\t\t\t\tparams={{ name: menu.name }}\n\t\t\t\t\t\t\t\t\tclassName={buttonVariants({ variant: \"outline\", size: \"sm\" })}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Pencil className=\"h-4 w-4 mr-2\" />\n\t\t\t\t\t\t\t\t\tEdit\n\t\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tonClick={() => setDeleteMenuName(menu.name)}\n\t\t\t\t\t\t\t\t\taria-label={`Delete ${menu.name} menu`}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={deleteMenuName !== null}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetDeleteMenuName(null);\n\t\t\t\t\tdeleteMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Delete Menu\"\n\t\t\t\tdescription=\"Are you sure you want to delete this menu? This will also delete all menu items. This action cannot be undone.\"\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={deleteMutation.isPending}\n\t\t\t\terror={deleteMutation.error}\n\t\t\t\tonConfirm={() => deleteMenuName && deleteMutation.mutate(deleteMenuName)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","import { Badge, Button, Dialog, Input, Label, Switch } from \"@cloudflare/kumo\";\nimport {\n\tArrowRight,\n\tMagnifyingGlass,\n\tPlus,\n\tArrowsLeftRight,\n\tTrash,\n\tPencilSimple,\n\tX,\n} from \"@phosphor-icons/react\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { useEffect, useState } from \"react\";\n\nimport {\n\tcreateRedirect,\n\tdeleteRedirect,\n\tfetch404Summary,\n\tfetchRedirects,\n\tupdateRedirect,\n} from \"../lib/api/redirects.js\";\nimport type {\n\tCreateRedirectInput,\n\tNotFoundSummary,\n\tRedirect,\n\tUpdateRedirectInput,\n} from \"../lib/api/redirects.js\";\nimport { cn } from \"../lib/utils.js\";\nimport { ConfirmDialog } from \"./ConfirmDialog.js\";\nimport { DialogError, getMutationError } from \"./DialogError.js\";\n\n// ---------------------------------------------------------------------------\n// Redirect form dialog (create + edit)\n// ---------------------------------------------------------------------------\n\nfunction RedirectFormDialog({\n\topen,\n\tonClose,\n\tredirect,\n\tdefaultSource,\n}: {\n\topen: boolean;\n\tonClose: () => void;\n\t/** Pass for edit mode */\n\tredirect?: Redirect;\n\t/** Pre-fill source for create mode (e.g. from 404 list) */\n\tdefaultSource?: string;\n}) {\n\tconst queryClient = useQueryClient();\n\tconst isEdit = !!redirect;\n\n\tconst [source, setSource] = useState(redirect?.source ?? defaultSource ?? \"\");\n\tconst [destination, setDestination] = useState(redirect?.destination ?? \"\");\n\tconst [type, setType] = useState(String(redirect?.type ?? 301));\n\tconst [enabled, setEnabled] = useState(redirect?.enabled ?? true);\n\tconst [groupName, setGroupName] = useState(redirect?.groupName ?? \"\");\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: (input: CreateRedirectInput) => createRedirect(input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"redirects\"] });\n\t\t\tonClose();\n\t\t},\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: (input: UpdateRedirectInput) => updateRedirect(redirect!.id, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"redirects\"] });\n\t\t\tonClose();\n\t\t},\n\t});\n\n\tconst mutation = isEdit ? updateMutation : createMutation;\n\n\tfunction handleSubmit(e: React.FormEvent) {\n\t\te.preventDefault();\n\t\tconst input = {\n\t\t\tsource: source.trim(),\n\t\t\tdestination: destination.trim(),\n\t\t\ttype: Number(type),\n\t\t\tenabled,\n\t\t\tgroupName: groupName.trim() || null,\n\t\t};\n\n\t\tif (isEdit) {\n\t\t\tupdateMutation.mutate(input);\n\t\t} else {\n\t\t\tcreateMutation.mutate(input);\n\t\t}\n\t}\n\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={(o) => !o && onClose()}>\n\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t{isEdit ? \"Edit Redirect\" : \"New Redirect\"}\n\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t{isEdit\n\t\t\t\t\t\t\t\t? \"Update this redirect rule.\"\n\t\t\t\t\t\t\t\t: \"Use [param] or [...rest] in paths for pattern matching.\"}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t<form onSubmit={handleSubmit} className=\"space-y-4\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Source path\"\n\t\t\t\t\t\tplaceholder=\"/old-page or /blog/[slug]\"\n\t\t\t\t\t\tvalue={source}\n\t\t\t\t\t\tonChange={(e: React.ChangeEvent<HTMLInputElement>) => setSource(e.target.value)}\n\t\t\t\t\t\trequired\n\t\t\t\t\t/>\n\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Destination path\"\n\t\t\t\t\t\tplaceholder=\"/new-page or /articles/[slug]\"\n\t\t\t\t\t\tvalue={destination}\n\t\t\t\t\t\tonChange={(e: React.ChangeEvent<HTMLInputElement>) => setDestination(e.target.value)}\n\t\t\t\t\t\trequired\n\t\t\t\t\t/>\n\n\t\t\t\t\t<div className=\"grid grid-cols-2 gap-4\">\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Label htmlFor=\"redirect-type\">Status code</Label>\n\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\tid=\"redirect-type\"\n\t\t\t\t\t\t\t\tvalue={type}\n\t\t\t\t\t\t\t\tonChange={(e) => setType(e.target.value)}\n\t\t\t\t\t\t\t\tclassName=\"flex h-10 w-full rounded-md border border-kumo-line bg-kumo-base px-3 py-2 text-sm\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<option value=\"301\">301 Permanent</option>\n\t\t\t\t\t\t\t\t<option value=\"302\">302 Temporary</option>\n\t\t\t\t\t\t\t\t<option value=\"307\">307 Temporary (Strict)</option>\n\t\t\t\t\t\t\t\t<option value=\"308\">308 Permanent (Strict)</option>\n\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Group (optional)\"\n\t\t\t\t\t\t\tplaceholder=\"e.g. import, blog\"\n\t\t\t\t\t\t\tvalue={groupName}\n\t\t\t\t\t\t\tonChange={(e: React.ChangeEvent<HTMLInputElement>) => setGroupName(e.target.value)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<Switch checked={enabled} onCheckedChange={setEnabled} id=\"redirect-enabled\" />\n\t\t\t\t\t\t<Label htmlFor=\"redirect-enabled\">Enabled</Label>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<DialogError message={getMutationError(mutation.error)} />\n\n\t\t\t\t\t<div className=\"flex justify-end gap-2\">\n\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={onClose}>\n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button type=\"submit\" disabled={mutation.isPending}>\n\t\t\t\t\t\t\t{mutation.isPending\n\t\t\t\t\t\t\t\t? isEdit\n\t\t\t\t\t\t\t\t\t? \"Saving...\"\n\t\t\t\t\t\t\t\t\t: \"Creating...\"\n\t\t\t\t\t\t\t\t: isEdit\n\t\t\t\t\t\t\t\t\t? \"Save\"\n\t\t\t\t\t\t\t\t\t: \"Create\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// 404 Summary panel\n// ---------------------------------------------------------------------------\n\nfunction NotFoundPanel({\n\titems,\n\tonCreateRedirect,\n}: {\n\titems: NotFoundSummary[];\n\tonCreateRedirect: (path: string) => void;\n}) {\n\tif (items.length === 0) {\n\t\treturn <p className=\"text-sm text-kumo-subtle py-4 text-center\">No 404 errors recorded yet.</p>;\n\t}\n\n\treturn (\n\t\t<div className=\"border rounded-lg\">\n\t\t\t<div className=\"flex items-center gap-4 py-2 px-4 border-b bg-kumo-tint/50 text-sm font-medium text-kumo-subtle\">\n\t\t\t\t<div className=\"flex-1\">Path</div>\n\t\t\t\t<div className=\"w-16 text-right\">Hits</div>\n\t\t\t\t<div className=\"w-32\">Last seen</div>\n\t\t\t\t<div className=\"w-8\" />\n\t\t\t</div>\n\t\t\t{items.map((item) => (\n\t\t\t\t<div\n\t\t\t\t\tkey={item.path}\n\t\t\t\t\tclassName=\"flex items-center gap-4 py-2 px-4 border-b last:border-0 text-sm\"\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex-1 font-mono text-xs truncate\">{item.path}</div>\n\t\t\t\t\t<div className=\"w-16 text-right tabular-nums\">{item.count}</div>\n\t\t\t\t\t<div className=\"w-32 text-kumo-subtle text-xs\">\n\t\t\t\t\t\t{(() => {\n\t\t\t\t\t\t\tconst d = new Date(item.lastSeen);\n\t\t\t\t\t\t\treturn Number.isNaN(d.getTime()) ? item.lastSeen : d.toLocaleDateString();\n\t\t\t\t\t\t})()}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"w-8\">\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tonClick={() => onCreateRedirect(item.path)}\n\t\t\t\t\t\t\tclassName=\"text-kumo-subtle hover:text-kumo-default\"\n\t\t\t\t\t\t\ttitle=\"Create redirect for this path\"\n\t\t\t\t\t\t\taria-label={`Create redirect for ${item.path}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowsLeftRight size={14} />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Main Redirects page\n// ---------------------------------------------------------------------------\n\ntype TabKey = \"redirects\" | \"404s\";\n\nexport function Redirects() {\n\tconst queryClient = useQueryClient();\n\tconst [tab, setTab] = useState<TabKey>(\"redirects\");\n\tconst [search, setSearch] = useState(\"\");\n\tconst [debouncedSearch, setDebouncedSearch] = useState(\"\");\n\tconst [filterEnabled, setFilterEnabled] = useState<string>(\"all\");\n\tconst [filterAuto, setFilterAuto] = useState<string>(\"all\");\n\n\t// Debounce search input\n\tuseEffect(() => {\n\t\tconst timer = setTimeout(setDebouncedSearch, 300, search);\n\t\treturn () => clearTimeout(timer);\n\t}, [search]);\n\n\t// Dialog state\n\tconst [showCreate, setShowCreate] = useState(false);\n\tconst [editRedirect, setEditRedirect] = useState<Redirect | null>(null);\n\tconst [deleteId, setDeleteId] = useState<string | null>(null);\n\tconst [prefillSource, setPrefillSource] = useState(\"\");\n\n\t// Queries\n\tconst enabledFilter = filterEnabled === \"all\" ? undefined : filterEnabled === \"true\";\n\tconst autoFilter = filterAuto === \"all\" ? undefined : filterAuto === \"true\";\n\n\tconst redirectsQuery = useQuery({\n\t\tqueryKey: [\"redirects\", debouncedSearch, enabledFilter, autoFilter],\n\t\tqueryFn: () =>\n\t\t\tfetchRedirects({\n\t\t\t\tsearch: debouncedSearch || undefined,\n\t\t\t\tenabled: enabledFilter,\n\t\t\t\tauto: autoFilter,\n\t\t\t\tlimit: 100,\n\t\t\t}),\n\t});\n\n\tconst notFoundQuery = useQuery({\n\t\tqueryKey: [\"redirects\", \"404-summary\"],\n\t\tqueryFn: () => fetch404Summary(50),\n\t\tenabled: tab === \"404s\",\n\t});\n\n\t// Delete mutation\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (id: string) => deleteRedirect(id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"redirects\"] });\n\t\t\tsetDeleteId(null);\n\t\t},\n\t});\n\n\t// Toggle enabled mutation\n\tconst toggleMutation = useMutation({\n\t\tmutationFn: ({ id, enabled }: { id: string; enabled: boolean }) =>\n\t\t\tupdateRedirect(id, { enabled }),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"redirects\"] });\n\t\t},\n\t\tonError: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"redirects\"] });\n\t\t},\n\t});\n\n\tfunction handleCreateFrom404(path: string) {\n\t\tsetPrefillSource(path);\n\t\tsetShowCreate(true);\n\t\tsetTab(\"redirects\");\n\t}\n\n\tconst redirects = redirectsQuery.data?.items ?? [];\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"text-3xl font-bold\">Redirects</h1>\n\t\t\t\t\t<p className=\"text-kumo-subtle\">Manage URL redirects and view 404 errors.</p>\n\t\t\t\t</div>\n\t\t\t\t<Button icon={<Plus />} onClick={() => setShowCreate(true)}>\n\t\t\t\t\tNew Redirect\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t{/* Tabs */}\n\t\t\t<div className=\"flex gap-1 border-b\">\n\t\t\t\t<button\n\t\t\t\t\tonClick={() => setTab(\"redirects\")}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors\",\n\t\t\t\t\t\ttab === \"redirects\"\n\t\t\t\t\t\t\t? \"border-kumo-brand text-kumo-brand\"\n\t\t\t\t\t\t\t: \"border-transparent text-kumo-subtle hover:text-kumo-default\",\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\tRedirects\n\t\t\t\t\t{redirectsQuery.data && (\n\t\t\t\t\t\t<Badge variant=\"secondary\" className=\"ml-2\">\n\t\t\t\t\t\t\t{redirectsQuery.data.items.length}\n\t\t\t\t\t\t\t{redirectsQuery.data.nextCursor ? \"+\" : \"\"}\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t)}\n\t\t\t\t</button>\n\t\t\t\t<button\n\t\t\t\t\tonClick={() => setTab(\"404s\")}\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors\",\n\t\t\t\t\t\ttab === \"404s\"\n\t\t\t\t\t\t\t? \"border-kumo-brand text-kumo-brand\"\n\t\t\t\t\t\t\t: \"border-transparent text-kumo-subtle hover:text-kumo-default\",\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t404 Errors\n\t\t\t\t</button>\n\t\t\t</div>\n\n\t\t\t{/* Tab content */}\n\t\t\t{tab === \"redirects\" && (\n\t\t\t\t<>\n\t\t\t\t\t{/* Filters */}\n\t\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t\t<div className=\"relative flex-1 max-w-md\">\n\t\t\t\t\t\t\t<MagnifyingGlass\n\t\t\t\t\t\t\t\tclassName=\"absolute left-3 top-1/2 -translate-y-1/2 text-kumo-subtle\"\n\t\t\t\t\t\t\t\tsize={16}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tplaceholder=\"Search source or destination...\"\n\t\t\t\t\t\t\t\tclassName=\"pl-10\"\n\t\t\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\t\t\tonChange={(e: React.ChangeEvent<HTMLInputElement>) => setSearch(e.target.value)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={filterEnabled}\n\t\t\t\t\t\t\tonChange={(e) => setFilterEnabled(e.target.value)}\n\t\t\t\t\t\t\tclassName=\"h-10 rounded-md border border-kumo-line bg-kumo-base px-3 text-sm\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"all\">All statuses</option>\n\t\t\t\t\t\t\t<option value=\"true\">Enabled</option>\n\t\t\t\t\t\t\t<option value=\"false\">Disabled</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={filterAuto}\n\t\t\t\t\t\t\tonChange={(e) => setFilterAuto(e.target.value)}\n\t\t\t\t\t\t\tclassName=\"h-10 rounded-md border border-kumo-line bg-kumo-base px-3 text-sm\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"all\">All types</option>\n\t\t\t\t\t\t\t<option value=\"false\">Manual</option>\n\t\t\t\t\t\t\t<option value=\"true\">Auto (slug change)</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Redirect list */}\n\t\t\t\t\t{redirectsQuery.isLoading ? (\n\t\t\t\t\t\t<div className=\"py-12 text-center text-kumo-subtle\">Loading redirects...</div>\n\t\t\t\t\t) : redirects.length === 0 ? (\n\t\t\t\t\t\t<div className=\"py-12 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t<ArrowsLeftRight size={48} className=\"mx-auto mb-4 opacity-30\" />\n\t\t\t\t\t\t\t<p className=\"text-lg font-medium\">No redirects yet</p>\n\t\t\t\t\t\t\t<p className=\"text-sm mt-1\">Create redirect rules to manage URL changes.</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"border rounded-lg\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-4 py-2 px-4 border-b bg-kumo-tint/50 text-sm font-medium text-kumo-subtle\">\n\t\t\t\t\t\t\t\t<div className=\"flex-1\">Source</div>\n\t\t\t\t\t\t\t\t<div className=\"w-8 text-center\" />\n\t\t\t\t\t\t\t\t<div className=\"flex-1\">Destination</div>\n\t\t\t\t\t\t\t\t<div className=\"w-14 text-center\">Code</div>\n\t\t\t\t\t\t\t\t<div className=\"w-16 text-right\">Hits</div>\n\t\t\t\t\t\t\t\t<div className=\"w-20 text-center\">Status</div>\n\t\t\t\t\t\t\t\t<div className=\"w-20\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{redirects.map((r) => (\n\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\tkey={r.id}\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"flex items-center gap-4 py-2 px-4 border-b last:border-0 text-sm\",\n\t\t\t\t\t\t\t\t\t\t!r.enabled && \"opacity-50\",\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<div className=\"flex-1 font-mono text-xs truncate\" title={r.source}>\n\t\t\t\t\t\t\t\t\t\t{r.source}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"w-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t<ArrowRight size={14} />\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex-1 font-mono text-xs truncate\" title={r.destination}>\n\t\t\t\t\t\t\t\t\t\t{r.destination}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"w-14 text-center\">\n\t\t\t\t\t\t\t\t\t\t<Badge variant=\"secondary\">{r.type}</Badge>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"w-16 text-right tabular-nums text-kumo-subtle\">{r.hits}</div>\n\t\t\t\t\t\t\t\t\t<div className=\"w-20 text-center\">\n\t\t\t\t\t\t\t\t\t\t<Switch\n\t\t\t\t\t\t\t\t\t\t\tchecked={r.enabled}\n\t\t\t\t\t\t\t\t\t\t\tonCheckedChange={(checked) =>\n\t\t\t\t\t\t\t\t\t\t\t\ttoggleMutation.mutate({\n\t\t\t\t\t\t\t\t\t\t\t\t\tid: r.id,\n\t\t\t\t\t\t\t\t\t\t\t\t\tenabled: checked,\n\t\t\t\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\taria-label={r.enabled ? \"Disable redirect\" : \"Enable redirect\"}\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"w-20 flex items-center justify-end gap-1\">\n\t\t\t\t\t\t\t\t\t\t{r.auto && (\n\t\t\t\t\t\t\t\t\t\t\t<Badge variant=\"outline\" className=\"mr-1 text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t\tauto\n\t\t\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => setEditRedirect(r)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"p-1 text-kumo-subtle hover:text-kumo-default\"\n\t\t\t\t\t\t\t\t\t\t\ttitle=\"Edit redirect\"\n\t\t\t\t\t\t\t\t\t\t\taria-label={`Edit redirect ${r.source}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<PencilSimple size={14} />\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => setDeleteId(r.id)}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"p-1 text-kumo-subtle hover:text-kumo-danger\"\n\t\t\t\t\t\t\t\t\t\t\ttitle=\"Delete redirect\"\n\t\t\t\t\t\t\t\t\t\t\taria-label={`Delete redirect ${r.source}`}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<Trash size={14} />\n\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t{tab === \"404s\" && (\n\t\t\t\t<NotFoundPanel items={notFoundQuery.data ?? []} onCreateRedirect={handleCreateFrom404} />\n\t\t\t)}\n\n\t\t\t{/* Create dialog */}\n\t\t\t{showCreate && (\n\t\t\t\t<RedirectFormDialog\n\t\t\t\t\topen\n\t\t\t\t\tonClose={() => {\n\t\t\t\t\t\tsetShowCreate(false);\n\t\t\t\t\t\tsetPrefillSource(\"\");\n\t\t\t\t\t}}\n\t\t\t\t\tdefaultSource={prefillSource || undefined}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Edit dialog */}\n\t\t\t{editRedirect && (\n\t\t\t\t<RedirectFormDialog open onClose={() => setEditRedirect(null)} redirect={editRedirect} />\n\t\t\t)}\n\n\t\t\t{/* Delete confirmation */}\n\t\t\t<ConfirmDialog\n\t\t\t\topen={!!deleteId}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetDeleteId(null);\n\t\t\t\t\tdeleteMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Delete Redirect?\"\n\t\t\t\tdescription=\"This redirect rule will be permanently removed.\"\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={deleteMutation.isPending}\n\t\t\t\terror={deleteMutation.error}\n\t\t\t\tonConfirm={() => deleteId && deleteMutation.mutate(deleteId)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","/**\n * SandboxedPluginPage\n *\n * Renders a plugin's admin page using Block Kit. Sends page_load/block_action/form_submit\n * interactions to the plugin's admin route and renders the returned blocks.\n */\n\nimport { CircleNotch, WarningCircle } from \"@phosphor-icons/react\";\nimport { BlockRenderer } from \"@emdash-cms/blocks\";\nimport type { Block, BlockInteraction, BlockResponse } from \"@emdash-cms/blocks\";\nimport { useCallback, useEffect, useState } from \"react\";\n\nimport { apiFetch, API_BASE } from \"../lib/api/client.js\";\n\ninterface SandboxedPluginPageProps {\n\tpluginId: string;\n\tpage: string;\n}\n\nexport function SandboxedPluginPage({ pluginId, page }: SandboxedPluginPageProps) {\n\tconst [blocks, setBlocks] = useState<Block[]>([]);\n\tconst [loading, setLoading] = useState(true);\n\tconst [error, setError] = useState<string | null>(null);\n\tconst [toast, setToast] = useState<BlockResponse[\"toast\"] | null>(null);\n\n\t// Send an interaction to the plugin admin route\n\tconst sendInteraction = useCallback(\n\t\tasync (interaction: BlockInteraction) => {\n\t\t\ttry {\n\t\t\t\tconst response = await apiFetch(`${API_BASE}/plugins/${pluginId}/admin`, {\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\t\tbody: JSON.stringify(interaction),\n\t\t\t\t});\n\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tconst text = await response.text();\n\t\t\t\t\tsetError(`Plugin responded with ${response.status}: ${text}`);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst body = (await response.json()) as { data: BlockResponse };\n\t\t\t\tconst data = body.data;\n\t\t\t\tsetBlocks(data.blocks);\n\t\t\t\tsetError(null);\n\n\t\t\t\tif (data.toast) {\n\t\t\t\t\tsetToast(data.toast);\n\t\t\t\t\tsetTimeout(setToast, 4000, null);\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tsetError(err instanceof Error ? err.message : \"Failed to communicate with plugin\");\n\t\t\t}\n\t\t},\n\t\t[pluginId],\n\t);\n\n\t// Initial page load\n\tuseEffect(() => {\n\t\tsetLoading(true);\n\t\tsetError(null);\n\t\tvoid sendInteraction({ type: \"page_load\", page }).finally(() => setLoading(false));\n\t}, [sendInteraction, page]);\n\n\t// Handle block actions\n\tconst handleAction = useCallback(\n\t\t(interaction: BlockInteraction) => {\n\t\t\tvoid sendInteraction(interaction);\n\t\t},\n\t\t[sendInteraction],\n\t);\n\n\tif (loading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center py-16\">\n\t\t\t\t<CircleNotch className=\"h-6 w-6 animate-spin text-kumo-subtle\" />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/5 p-6\">\n\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t<WarningCircle className=\"h-5 w-5 shrink-0 text-kumo-danger\" />\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h3 className=\"font-semibold text-kumo-danger\">Plugin Error</h3>\n\t\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">{error}</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"relative\">\n\t\t\t{/* Toast notification */}\n\t\t\t{toast && (\n\t\t\t\t<div\n\t\t\t\t\tclassName={`fixed right-4 top-4 z-50 rounded-lg border px-4 py-3 text-sm shadow-lg ${\n\t\t\t\t\t\ttoast.type === \"success\"\n\t\t\t\t\t\t\t? \"border-green-200 bg-green-50 text-green-800\"\n\t\t\t\t\t\t\t: toast.type === \"error\"\n\t\t\t\t\t\t\t\t? \"border-red-200 bg-red-50 text-red-800\"\n\t\t\t\t\t\t\t\t: \"border-blue-200 bg-blue-50 text-blue-800\"\n\t\t\t\t\t}`}\n\t\t\t\t>\n\t\t\t\t\t{toast.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<BlockRenderer blocks={blocks} onAction={handleAction} />\n\t\t</div>\n\t);\n}\n","/**\n * Section editor page component\n *\n * Edit a section's content and metadata.\n */\n\nimport { Button, Input, InputArea, Label, Loader, Toast } from \"@cloudflare/kumo\";\nimport { ArrowLeft } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { Link, useParams, useNavigate } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchSection, updateSection, type Section, type UpdateSectionInput } from \"../lib/api\";\nimport { slugify } from \"../lib/utils\";\nimport { PortableTextEditor } from \"./PortableTextEditor\";\nimport { SaveButton } from \"./SaveButton\";\n\nexport function SectionEditor() {\n\tconst { slug } = useParams({ from: \"/_admin/sections/$slug\" });\n\tconst navigate = useNavigate();\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\n\tconst {\n\t\tdata: section,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"sections\", slug],\n\t\tqueryFn: () => fetchSection(slug),\n\t\tstaleTime: Infinity,\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: (input: UpdateSectionInput) => updateSection(slug, input),\n\t\tonSuccess: (updated) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"sections\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"sections\", slug] });\n\t\t\ttoastManager.add({ title: \"Section saved\" });\n\t\t\t// If slug changed, navigate to new URL\n\t\t\tif (updated.slug !== slug) {\n\t\t\t\tvoid navigate({ to: \"/sections/$slug\", params: { slug: updated.slug } });\n\t\t\t}\n\t\t},\n\t\tonError: (mutationError: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Error saving section\",\n\t\t\t\tdescription: mutationError.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center h-64\">\n\t\t\t\t<Loader />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error || !section) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t<Link to=\"/sections\">\n\t\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to sections\">\n\t\t\t\t\t\t\t<ArrowLeft className=\"h-5 w-5\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</Link>\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">Section Not Found</h1>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<p className=\"text-kumo-subtle\">\n\t\t\t\t\t\t{error ? error.message : `Section \"${slug}\" could not be found.`}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<SectionEditorForm\n\t\t\tkey={section.updatedAt}\n\t\t\tsection={section}\n\t\t\tisSaving={updateMutation.isPending}\n\t\t\tonSave={(input) => updateMutation.mutate(input)}\n\t\t/>\n\t);\n}\n\ninterface SectionEditorFormProps {\n\tsection: Section;\n\tisSaving: boolean;\n\tonSave: (input: UpdateSectionInput) => void;\n}\n\nfunction SectionEditorForm({ section, isSaving, onSave }: SectionEditorFormProps) {\n\tconst [title, setTitle] = React.useState(section.title);\n\tconst [sectionSlug, setSectionSlug] = React.useState(section.slug);\n\tconst [slugTouched, setSlugTouched] = React.useState(true); // Existing sections have touched slugs\n\tconst [description, setDescription] = React.useState(section.description || \"\");\n\tconst [keywords, setKeywords] = React.useState(section.keywords.join(\", \"));\n\tconst [content, setContent] = React.useState<unknown[]>(section.content);\n\n\t// Track initial state for dirty checking\n\tconst [lastSavedData] = React.useState(() =>\n\t\tJSON.stringify({\n\t\t\ttitle: section.title,\n\t\t\tslug: section.slug,\n\t\t\tdescription: section.description || \"\",\n\t\t\tkeywords: section.keywords.join(\", \"),\n\t\t\tcontent: section.content,\n\t\t}),\n\t);\n\n\t// Auto-generate slug from title if editing title and slug hasn't been manually changed\n\tReact.useEffect(() => {\n\t\tif (!slugTouched && title && title !== section.title) {\n\t\t\tsetSectionSlug(slugify(title));\n\t\t}\n\t}, [title, slugTouched, section.title]);\n\n\tconst currentData = React.useMemo(\n\t\t() => JSON.stringify({ title, slug: sectionSlug, description, keywords, content }),\n\t\t[title, sectionSlug, description, keywords, content],\n\t);\n\tconst isDirty = currentData !== lastSavedData;\n\n\tconst handleSave = () => {\n\t\tconst keywordsArray = keywords\n\t\t\t.split(\",\")\n\t\t\t.map((k) => k.trim())\n\t\t\t.filter(Boolean);\n\n\t\tonSave({\n\t\t\ttitle,\n\t\t\tslug: sectionSlug,\n\t\t\tdescription: description || undefined,\n\t\t\tkeywords: keywordsArray,\n\t\t\tcontent,\n\t\t});\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t<Link to=\"/sections\">\n\t\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to sections\">\n\t\t\t\t\t\t\t<ArrowLeft className=\"h-5 w-5\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</Link>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h1 className=\"text-2xl font-bold\">{section.title}</h1>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{section.source === \"theme\" ? \"Theme Section\" : \"Custom Section\"} ·{\" \"}\n\t\t\t\t\t\t\t{section.slug}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<SaveButton isSaving={isSaving} isDirty={isDirty} onClick={handleSave} />\n\t\t\t</div>\n\n\t\t\t<div className=\"grid grid-cols-12 gap-6\">\n\t\t\t\t{/* Main content */}\n\t\t\t\t<div className=\"col-span-8 space-y-6\">\n\t\t\t\t\t{/* Content editor */}\n\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t\t<Label className=\"text-lg font-semibold mb-4 block\">Content</Label>\n\t\t\t\t\t\t<PortableTextEditor\n\t\t\t\t\t\t\tvalue={content as Parameters<typeof PortableTextEditor>[0][\"value\"]}\n\t\t\t\t\t\t\tonChange={(value) => setContent(value as unknown[])}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Sidebar */}\n\t\t\t\t<div className=\"col-span-4 space-y-6\">\n\t\t\t\t\t{/* Metadata */}\n\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6 space-y-4\">\n\t\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Section Details</h2>\n\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Title\"\n\t\t\t\t\t\t\tvalue={title}\n\t\t\t\t\t\t\tonChange={(e) => setTitle(e.target.value)}\n\t\t\t\t\t\t\tplaceholder=\"Section title\"\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\t\t\tvalue={sectionSlug}\n\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\tsetSectionSlug(e.target.value);\n\t\t\t\t\t\t\t\t\tsetSlugTouched(true);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tplaceholder=\"section-slug\"\n\t\t\t\t\t\t\t\tpattern=\"[a-z0-9-]+\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\tUsed to identify this section. Lowercase letters, numbers, and hyphens only.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<InputArea\n\t\t\t\t\t\t\tlabel=\"Description\"\n\t\t\t\t\t\t\tvalue={description}\n\t\t\t\t\t\t\tonChange={(e) => setDescription(e.target.value)}\n\t\t\t\t\t\t\tplaceholder=\"Describe what this section is for...\"\n\t\t\t\t\t\t\trows={3}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Keywords\"\n\t\t\t\t\t\t\t\tvalue={keywords}\n\t\t\t\t\t\t\t\tonChange={(e) => setKeywords(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"hero, banner, cta\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-1\">Comma-separated keywords for search.</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Source info */}\n\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t\t<h2 className=\"text-lg font-semibold mb-2\">Source</h2>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{section.source === \"theme\" && (\n\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\tThis section is provided by the theme. Editing will create a custom copy that\n\t\t\t\t\t\t\t\t\toverrides the theme version.\n\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{section.source === \"user\" && <>This is a custom section.</>}\n\t\t\t\t\t\t\t{section.source === \"import\" && <>This section was imported from another system.</>}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{section.themeId && (\n\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-2\">Theme ID: {section.themeId}</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * Sections library page component\n *\n * Browse, create, and manage reusable content sections (block patterns).\n */\n\nimport { Button, Dialog, Input, InputArea, Toast } from \"@cloudflare/kumo\";\nimport {\n\tPlus,\n\tMagnifyingGlass,\n\tTrash,\n\tPencilSimple,\n\tCopy,\n\tFolderOpen,\n\tGlobe,\n\tUser,\n\tFileArrowDown,\n} from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { useNavigate } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tfetchSections,\n\tcreateSection,\n\tdeleteSection,\n\ttype Section,\n\ttype SectionSource,\n} from \"../lib/api\";\nimport { slugify } from \"../lib/utils\";\nimport { ConfirmDialog } from \"./ConfirmDialog.js\";\nimport { DialogError, getMutationError } from \"./DialogError.js\";\n\nconst sourceIcons: Record<SectionSource, React.ElementType> = {\n\ttheme: Globe,\n\tuser: User,\n\timport: FileArrowDown,\n};\n\nconst sourceLabels: Record<SectionSource, string> = {\n\ttheme: \"Theme\",\n\tuser: \"Custom\",\n\timport: \"Imported\",\n};\n\nexport function Sections() {\n\tconst navigate = useNavigate();\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\tconst [isCreateOpen, setIsCreateOpen] = React.useState(false);\n\tconst [deleteSlug, setDeleteSlug] = React.useState<string | null>(null);\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst [selectedSource, setSelectedSource] = React.useState<SectionSource | null>(null);\n\n\t// Create form state\n\tconst [createTitle, setCreateTitle] = React.useState(\"\");\n\tconst [createSlug, setCreateSlug] = React.useState(\"\");\n\tconst [createDescription, setCreateDescription] = React.useState(\"\");\n\tconst [slugTouched, setSlugTouched] = React.useState(false);\n\tconst [createError, setCreateError] = React.useState<string | null>(null);\n\n\t// Reset form when dialog closes\n\tReact.useEffect(() => {\n\t\tif (!isCreateOpen) {\n\t\t\tsetCreateTitle(\"\");\n\t\t\tsetCreateSlug(\"\");\n\t\t\tsetCreateDescription(\"\");\n\t\t\tsetSlugTouched(false);\n\t\t\tsetCreateError(null);\n\t\t}\n\t}, [isCreateOpen]);\n\n\tconst { data: sectionsData, isLoading: sectionsLoading } = useQuery({\n\t\tqueryKey: [\"sections\", { source: selectedSource, search: searchQuery }],\n\t\tqueryFn: () =>\n\t\t\tfetchSections({\n\t\t\t\tsource: selectedSource || undefined,\n\t\t\t\tsearch: searchQuery || undefined,\n\t\t\t}),\n\t});\n\tconst sections = sectionsData?.items ?? [];\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: createSection,\n\t\tonSuccess: (section) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"sections\"] });\n\t\t\tsetIsCreateOpen(false);\n\t\t\ttoastManager.add({ title: \"Section created\" });\n\t\t\t// Navigate to edit the new section\n\t\t\tvoid navigate({ to: \"/sections/$slug\", params: { slug: section.slug } });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\tsetCreateError(error.message);\n\t\t},\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: deleteSection,\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"sections\"] });\n\t\t\tsetDeleteSlug(null);\n\t\t\ttoastManager.add({ title: \"Section deleted\" });\n\t\t},\n\t});\n\n\tconst handleCreate = (e: React.FormEvent<HTMLFormElement>) => {\n\t\te.preventDefault();\n\t\tsetCreateError(null);\n\t\tcreateMutation.mutate({\n\t\t\tslug: createSlug,\n\t\t\ttitle: createTitle,\n\t\t\tdescription: createDescription || undefined,\n\t\t\tcontent: [], // Start with empty content\n\t\t});\n\t};\n\n\tconst handleCopySlug = (slug: string) => {\n\t\tvoid navigator.clipboard.writeText(slug);\n\t\ttoastManager.add({ title: \"Slug copied to clipboard\" });\n\t};\n\n\tconst sectionToDelete = sections.find((s) => s.slug === deleteSlug);\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"text-3xl font-bold\">Sections</h1>\n\t\t\t\t\t<p className=\"text-kumo-subtle\">\n\t\t\t\t\t\tReusable content blocks you can insert into any content\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<Dialog.Root open={isCreateOpen} onOpenChange={setIsCreateOpen}>\n\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button {...props} icon={<Plus />}>\n\t\t\t\t\t\t\t\tNew Section\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t\tCreate Section\n\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<form onSubmit={handleCreate} className=\"space-y-4\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Title\"\n\t\t\t\t\t\t\t\tvalue={createTitle}\n\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\tconst title = e.target.value;\n\t\t\t\t\t\t\t\t\tsetCreateTitle(title);\n\t\t\t\t\t\t\t\t\tif (!slugTouched && title) {\n\t\t\t\t\t\t\t\t\t\tsetCreateSlug(slugify(title));\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\tplaceholder=\"Hero Banner\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\t\t\t\tvalue={createSlug}\n\t\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\t\tsetCreateSlug(e.target.value);\n\t\t\t\t\t\t\t\t\t\tsetSlugTouched(true);\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\t\tplaceholder=\"hero-banner\"\n\t\t\t\t\t\t\t\t\tpattern=\"[a-z0-9-]+\"\n\t\t\t\t\t\t\t\t\ttitle=\"Lowercase letters, numbers, and hyphens only\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\t\tUsed to identify this section. Lowercase letters, numbers, and hyphens only.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<InputArea\n\t\t\t\t\t\t\t\tlabel=\"Description\"\n\t\t\t\t\t\t\t\tvalue={createDescription}\n\t\t\t\t\t\t\t\tonChange={(e) => setCreateDescription(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"A full-width hero banner with heading, text, and CTA button\"\n\t\t\t\t\t\t\t\trows={3}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<DialogError message={createError || getMutationError(createMutation.error)} />\n\t\t\t\t\t\t\t<div className=\"flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={() => setIsCreateOpen(false)}>\n\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button type=\"submit\" disabled={createMutation.isPending}>\n\t\t\t\t\t\t\t\t\t{createMutation.isPending ? \"Creating...\" : \"Create\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</form>\n\t\t\t\t\t</Dialog>\n\t\t\t\t</Dialog.Root>\n\t\t\t</div>\n\n\t\t\t{/* Filters */}\n\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t{/* Search */}\n\t\t\t\t<div className=\"relative flex-1 max-w-md\">\n\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t<Input\n\t\t\t\t\t\tplaceholder=\"Search sections...\"\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\tclassName=\"pl-10\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Source filter */}\n\t\t\t\t<select\n\t\t\t\t\tvalue={selectedSource || \"\"}\n\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\tconst val = e.target.value;\n\t\t\t\t\t\tsetSelectedSource(val === \"theme\" || val === \"user\" || val === \"import\" ? val : null);\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"h-10 rounded-md border border-kumo-line bg-kumo-base px-3 py-2 text-sm ring-offset-background focus:outline-none focus:ring-2 focus:ring-kumo-ring focus:ring-offset-2\"\n\t\t\t\t>\n\t\t\t\t\t<option value=\"\">All Sources</option>\n\t\t\t\t\t<option value=\"theme\">Theme</option>\n\t\t\t\t\t<option value=\"user\">Custom</option>\n\t\t\t\t\t<option value=\"import\">Imported</option>\n\t\t\t\t</select>\n\t\t\t</div>\n\n\t\t\t{/* Section Grid */}\n\t\t\t{sectionsLoading ? (\n\t\t\t\t<div className=\"flex items-center justify-center h-64\">\n\t\t\t\t\t<div className=\"text-kumo-subtle\">Loading sections...</div>\n\t\t\t\t</div>\n\t\t\t) : sections.length === 0 ? (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t{searchQuery || selectedSource ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<MagnifyingGlass className=\"mx-auto h-12 w-12 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<h3 className=\"mt-4 text-lg font-semibold\">No sections found</h3>\n\t\t\t\t\t\t\t<p className=\"mt-2 text-kumo-subtle\">Try adjusting your search or filters.</p>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<FolderOpen className=\"mx-auto h-12 w-12 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<h3 className=\"mt-4 text-lg font-semibold\">No sections yet</h3>\n\t\t\t\t\t\t\t<p className=\"mt-2 text-kumo-subtle\">\n\t\t\t\t\t\t\t\tCreate your first reusable content section to get started.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<Button className=\"mt-4\" icon={<Plus />} onClick={() => setIsCreateOpen(true)}>\n\t\t\t\t\t\t\t\tCreate Section\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t) : (\n\t\t\t\t<div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n\t\t\t\t\t{sections.map((section) => (\n\t\t\t\t\t\t<SectionCard\n\t\t\t\t\t\t\tkey={section.id}\n\t\t\t\t\t\t\tsection={section}\n\t\t\t\t\t\t\tonEdit={() => navigate({ to: \"/sections/$slug\", params: { slug: section.slug } })}\n\t\t\t\t\t\t\tonDelete={() => setDeleteSlug(section.slug)}\n\t\t\t\t\t\t\tonCopySlug={() => handleCopySlug(section.slug)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Delete confirmation */}\n\t\t\t<ConfirmDialog\n\t\t\t\topen={!!deleteSlug}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetDeleteSlug(null);\n\t\t\t\t\tdeleteMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Delete Section?\"\n\t\t\t\tdescription={\n\t\t\t\t\tsectionToDelete?.source === \"theme\" ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\tTheme-provided sections cannot be deleted. Edit the section to create a custom copy,\n\t\t\t\t\t\t\tthen delete that.\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\tThis will permanently delete \"{sectionToDelete?.title}\". This action cannot be undone.\n\t\t\t\t\t\t</>\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={deleteMutation.isPending}\n\t\t\t\terror={deleteMutation.error}\n\t\t\t\tonConfirm={() => deleteSlug && deleteMutation.mutate(deleteSlug)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nfunction SectionCard({\n\tsection,\n\tonEdit,\n\tonDelete,\n\tonCopySlug,\n}: {\n\tsection: Section;\n\tonEdit: () => void;\n\tonDelete: () => void;\n\tonCopySlug: () => void;\n}) {\n\tconst SourceIcon = sourceIcons[section.source];\n\n\treturn (\n\t\t<div className=\"rounded-lg border bg-kumo-base overflow-hidden\">\n\t\t\t{/* Preview area */}\n\t\t\t<div className=\"aspect-video bg-kumo-tint flex items-center justify-center\">\n\t\t\t\t{section.previewUrl ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={section.previewUrl}\n\t\t\t\t\t\talt={section.title}\n\t\t\t\t\t\tclassName=\"w-full h-full object-cover\"\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"text-kumo-subtle text-sm\">No preview</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Content */}\n\t\t\t<div className=\"p-4\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-2\">\n\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t<h3 className=\"font-semibold truncate\">{section.title}</h3>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle truncate\">{section.slug}</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName=\"flex items-center gap-1 text-xs text-kumo-subtle\"\n\t\t\t\t\t\ttitle={sourceLabels[section.source]}\n\t\t\t\t\t>\n\t\t\t\t\t\t<SourceIcon className=\"h-3 w-3\" />\n\t\t\t\t\t\t<span>{sourceLabels[section.source]}</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{section.description && (\n\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle line-clamp-2\">{section.description}</p>\n\t\t\t\t)}\n\n\t\t\t\t{section.keywords.length > 0 && (\n\t\t\t\t\t<div className=\"mt-2 flex flex-wrap gap-1\">\n\t\t\t\t\t\t{section.keywords.slice(0, 3).map((keyword) => (\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tkey={keyword}\n\t\t\t\t\t\t\t\tclassName=\"inline-flex items-center rounded bg-kumo-tint px-1.5 py-0.5 text-xs text-kumo-subtle\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{keyword}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t))}\n\t\t\t\t\t\t{section.keywords.length > 3 && (\n\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">+{section.keywords.length - 3} more</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Actions */}\n\t\t\t\t<div className=\"mt-4 flex items-center gap-2\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\ticon={<PencilSimple />}\n\t\t\t\t\t\tonClick={onEdit}\n\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t>\n\t\t\t\t\t\tEdit\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tonClick={onCopySlug}\n\t\t\t\t\t\ttitle=\"Copy slug\"\n\t\t\t\t\t\taria-label={`Copy ${section.slug} to clipboard`}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Copy className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tonClick={onDelete}\n\t\t\t\t\t\ttitle={section.source === \"theme\" ? \"Cannot delete theme sections\" : \"Delete\"}\n\t\t\t\t\t\taria-label={`Delete ${section.title}`}\n\t\t\t\t\t\tdisabled={section.source === \"theme\"}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","import {\n\tGear,\n\tShareNetwork,\n\tMagnifyingGlass,\n\tShield,\n\tGlobe,\n\tKey,\n\tEnvelope,\n\tCaretRight,\n} from \"@phosphor-icons/react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\n\nimport { fetchManifest } from \"../lib/api\";\n\ninterface SettingsLinkProps {\n\tto: string;\n\ticon: React.ReactNode;\n\ttitle: string;\n\tdescription: string;\n}\n\nfunction SettingsLink({ to, icon, title, description }: SettingsLinkProps) {\n\treturn (\n\t\t<Link\n\t\t\tto={to}\n\t\t\tclassName=\"flex items-center justify-between p-4 rounded-lg border bg-kumo-base hover:bg-kumo-tint transition-colors\"\n\t\t>\n\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t<div className=\"text-kumo-subtle\">{icon}</div>\n\t\t\t\t<div>\n\t\t\t\t\t<div className=\"font-medium\">{title}</div>\n\t\t\t\t\t<div className=\"text-sm text-kumo-subtle\">{description}</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t<CaretRight className=\"h-5 w-5 text-kumo-subtle\" />\n\t\t</Link>\n\t);\n}\n\n/**\n * Settings hub page — links to all settings sub-pages.\n */\nexport function Settings() {\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tconst showSecuritySettings = manifest?.authMode === \"passkey\";\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<h1 className=\"text-2xl font-bold\">Settings</h1>\n\n\t\t\t{/* Site settings */}\n\t\t\t<div className=\"space-y-2\">\n\t\t\t\t<SettingsLink\n\t\t\t\t\tto=\"/settings/general\"\n\t\t\t\t\ticon={<Gear className=\"h-5 w-5\" />}\n\t\t\t\t\ttitle=\"General\"\n\t\t\t\t\tdescription=\"Site identity, logo, favicon, and reading preferences\"\n\t\t\t\t/>\n\t\t\t\t<SettingsLink\n\t\t\t\t\tto=\"/settings/social\"\n\t\t\t\t\ticon={<ShareNetwork className=\"h-5 w-5\" />}\n\t\t\t\t\ttitle=\"Social Links\"\n\t\t\t\t\tdescription=\"Social media profile links\"\n\t\t\t\t/>\n\t\t\t\t<SettingsLink\n\t\t\t\t\tto=\"/settings/seo\"\n\t\t\t\t\ticon={<MagnifyingGlass className=\"h-5 w-5\" />}\n\t\t\t\t\ttitle=\"SEO\"\n\t\t\t\t\tdescription=\"Search engine optimization and verification\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{/* Security & access — only for passkey auth */}\n\t\t\t{showSecuritySettings && (\n\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t<SettingsLink\n\t\t\t\t\t\tto=\"/settings/security\"\n\t\t\t\t\t\ticon={<Shield className=\"h-5 w-5\" />}\n\t\t\t\t\t\ttitle=\"Security\"\n\t\t\t\t\t\tdescription=\"Manage your passkeys and authentication\"\n\t\t\t\t\t/>\n\t\t\t\t\t<SettingsLink\n\t\t\t\t\t\tto=\"/settings/allowed-domains\"\n\t\t\t\t\t\ticon={<Globe className=\"h-5 w-5\" />}\n\t\t\t\t\t\ttitle=\"Self-Signup Domains\"\n\t\t\t\t\t\tdescription=\"Allow users from specific domains to sign up\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Always visible for admins */}\n\t\t\t<div className=\"space-y-2\">\n\t\t\t\t<SettingsLink\n\t\t\t\t\tto=\"/settings/api-tokens\"\n\t\t\t\t\ticon={<Key className=\"h-5 w-5\" />}\n\t\t\t\t\ttitle=\"API Tokens\"\n\t\t\t\t\tdescription=\"Create personal access tokens for programmatic API access\"\n\t\t\t\t/>\n\t\t\t\t<SettingsLink\n\t\t\t\t\tto=\"/settings/email\"\n\t\t\t\t\ticon={<Envelope className=\"h-5 w-5\" />}\n\t\t\t\t\ttitle=\"Email\"\n\t\t\t\t\tdescription=\"View email provider status and send test emails\"\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default Settings;\n","/**\n * Allowed Domains Settings - Self-signup domain management\n *\n * Only available when using passkey auth. When external auth (e.g., Cloudflare Access)\n * is configured, this page shows an informational message instead.\n */\n\nimport { Button, Dialog, Input, Select, Switch } from \"@cloudflare/kumo\";\nimport {\n\tGlobe,\n\tPlus,\n\tCheckCircle,\n\tWarningCircle,\n\tTrash,\n\tPencil,\n\tArrowLeft,\n\tInfo,\n} from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tfetchAllowedDomains,\n\tcreateAllowedDomain,\n\tupdateAllowedDomain,\n\tdeleteAllowedDomain,\n\tfetchManifest,\n\ttype AllowedDomain,\n} from \"../../lib/api\";\n\nconst ROLES = [\n\t{ value: 10, label: \"Subscriber\" },\n\t{ value: 20, label: \"Contributor\" },\n\t{ value: 30, label: \"Author\" },\n\t{ value: 40, label: \"Editor\" },\n] as const;\n\nfunction getRoleName(level: number): string {\n\treturn ROLES.find((r) => r.value === level)?.label ?? \"Unknown\";\n}\n\nexport function AllowedDomainsSettings() {\n\tconst queryClient = useQueryClient();\n\tconst [isAddingDomain, setIsAddingDomain] = React.useState(false);\n\tconst [editingDomain, setEditingDomain] = React.useState<AllowedDomain | null>(null);\n\tconst [deletingDomain, setDeletingDomain] = React.useState<string | null>(null);\n\tconst [saveStatus, setSaveStatus] = React.useState<{\n\t\ttype: \"success\" | \"error\";\n\t\tmessage: string;\n\t} | null>(null);\n\n\t// Form state\n\tconst [newDomain, setNewDomain] = React.useState(\"\");\n\tconst [newRole, setNewRole] = React.useState<number>(30); // Default to Author\n\n\t// Fetch manifest for auth mode\n\tconst { data: manifest, isLoading: manifestLoading } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tconst isExternalAuth = manifest?.authMode && manifest.authMode !== \"passkey\";\n\n\t// Fetch domains (only when using passkey auth)\n\tconst {\n\t\tdata: domains,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"allowed-domains\"],\n\t\tqueryFn: fetchAllowedDomains,\n\t\tenabled: !isExternalAuth && !manifestLoading,\n\t});\n\n\t// Clear status message after 3 seconds\n\tReact.useEffect(() => {\n\t\tif (saveStatus) {\n\t\t\tconst timer = setTimeout(setSaveStatus, 3000, null);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [saveStatus]);\n\n\t// Create mutation\n\tconst createMutation = useMutation({\n\t\tmutationFn: createAllowedDomain,\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"allowed-domains\"] });\n\t\t\tsetIsAddingDomain(false);\n\t\t\tsetNewDomain(\"\");\n\t\t\tsetNewRole(30);\n\t\t\tsetSaveStatus({ type: \"success\", message: \"Domain added successfully\" });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: mutationError instanceof Error ? mutationError.message : \"Failed to add domain\",\n\t\t\t});\n\t\t},\n\t});\n\n\t// Update mutation\n\tconst updateMutation = useMutation({\n\t\tmutationFn: ({\n\t\t\tdomain,\n\t\t\tdata,\n\t\t}: {\n\t\t\tdomain: string;\n\t\t\tdata: { enabled?: boolean; defaultRole?: number };\n\t\t}) => updateAllowedDomain(domain, data),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"allowed-domains\"] });\n\t\t\tsetEditingDomain(null);\n\t\t\tsetSaveStatus({ type: \"success\", message: \"Domain updated\" });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: mutationError instanceof Error ? mutationError.message : \"Failed to update domain\",\n\t\t\t});\n\t\t},\n\t});\n\n\t// Delete mutation\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: deleteAllowedDomain,\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"allowed-domains\"] });\n\t\t\tsetDeletingDomain(null);\n\t\t\tsetSaveStatus({ type: \"success\", message: \"Domain removed\" });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: mutationError instanceof Error ? mutationError.message : \"Failed to remove domain\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleAddDomain = () => {\n\t\tif (!newDomain.trim()) return;\n\t\tcreateMutation.mutate({\n\t\t\tdomain: newDomain.trim().toLowerCase(),\n\t\t\tdefaultRole: newRole,\n\t\t});\n\t};\n\n\tconst handleToggleEnabled = (domain: AllowedDomain) => {\n\t\tupdateMutation.mutate({\n\t\t\tdomain: domain.domain,\n\t\t\tdata: { enabled: !domain.enabled },\n\t\t});\n\t};\n\n\tconst handleUpdateRole = (domain: string, role: number) => {\n\t\tupdateMutation.mutate({\n\t\t\tdomain,\n\t\t\tdata: { defaultRole: role },\n\t\t});\n\t\tsetEditingDomain(null);\n\t};\n\n\tconst handleDelete = () => {\n\t\tif (deletingDomain) {\n\t\t\tdeleteMutation.mutate(deletingDomain);\n\t\t}\n\t};\n\n\tif (manifestLoading || isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Self-Signup Domains</h1>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<p className=\"text-kumo-subtle\">Loading...</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// Show message when external auth is configured\n\tif (isExternalAuth) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Self-Signup Domains</h1>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t\t<Info className=\"h-5 w-5 text-kumo-subtle mt-0.5 flex-shrink-0\" />\n\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t<p className=\"text-kumo-subtle\">\n\t\t\t\t\t\t\t\tUser access is managed by an external provider ({manifest?.authMode}). Self-signup\n\t\t\t\t\t\t\t\tdomain settings are not available when using external authentication.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" icon={<ArrowLeft />}>\n\t\t\t\t\t\t\t\t\tBack to Settings\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Self-Signup Domains</h1>\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-6\">\n\t\t\t\t\t<p className=\"text-kumo-danger\">\n\t\t\t\t\t\t{error instanceof Error ? error.message : \"Failed to load allowed domains\"}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<h1 className=\"text-2xl font-bold\">Self-Signup Domains</h1>\n\n\t\t\t{/* Status message */}\n\t\t\t{saveStatus && (\n\t\t\t\t<div\n\t\t\t\t\tclassName={`rounded-lg border p-4 flex items-center gap-2 ${\n\t\t\t\t\t\tsaveStatus.type === \"success\"\n\t\t\t\t\t\t\t? \"bg-green-50 border-green-200 text-green-800 dark:bg-green-900/20 dark:border-green-800 dark:text-green-400\"\n\t\t\t\t\t\t\t: \"bg-red-50 border-red-200 text-red-800 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400\"\n\t\t\t\t\t}`}\n\t\t\t\t>\n\t\t\t\t\t{saveStatus.type === \"success\" ? (\n\t\t\t\t\t\t<CheckCircle className=\"h-5 w-5\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<WarningCircle className=\"h-5 w-5\" />\n\t\t\t\t\t)}\n\t\t\t\t\t<span>{saveStatus.message}</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Domains Section */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-center gap-2 mb-4\">\n\t\t\t\t\t<Globe className=\"h-5 w-5 text-kumo-subtle\" />\n\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Allowed Domains</h2>\n\t\t\t\t</div>\n\n\t\t\t\t<p className=\"text-sm text-kumo-subtle mb-6\">\n\t\t\t\t\tUsers with email addresses from these domains can sign up without an invite. They will be\n\t\t\t\t\tassigned the specified role automatically.\n\t\t\t\t</p>\n\n\t\t\t\t{/* Domain list */}\n\t\t\t\t{domains && domains.length > 0 ? (\n\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t{domains.map((domain) => (\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tkey={domain.domain}\n\t\t\t\t\t\t\t\tclassName={`flex items-center justify-between p-4 rounded-lg border ${\n\t\t\t\t\t\t\t\t\tdomain.enabled ? \"bg-kumo-base\" : \"bg-kumo-tint/50 opacity-60\"\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-4\">\n\t\t\t\t\t\t\t\t\t<Switch\n\t\t\t\t\t\t\t\t\t\tchecked={domain.enabled}\n\t\t\t\t\t\t\t\t\t\tonCheckedChange={() => handleToggleEnabled(domain)}\n\t\t\t\t\t\t\t\t\t\tdisabled={updateMutation.isPending}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"font-medium\">{domain.domain}</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\tDefault role: {getRoleName(domain.defaultRole)}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => setEditingDomain(domain)}\n\t\t\t\t\t\t\t\t\t\tdisabled={updateMutation.isPending}\n\t\t\t\t\t\t\t\t\t\taria-label={`Edit ${domain.domain}`}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Pencil className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => setDeletingDomain(domain.domain)}\n\t\t\t\t\t\t\t\t\t\tdisabled={deleteMutation.isPending}\n\t\t\t\t\t\t\t\t\t\taria-label={`Delete ${domain.domain}`}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-danger\" />\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"rounded-lg border border-dashed p-6 text-center text-kumo-subtle\">\n\t\t\t\t\t\tNo domains configured. Users must be invited individually.\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Add domain section */}\n\t\t\t\t<div className=\"mt-6 pt-6 border-t\">\n\t\t\t\t\t{isAddingDomain ? (\n\t\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t\t<h3 className=\"font-medium\">Add an allowed domain</h3>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\tsetIsAddingDomain(false);\n\t\t\t\t\t\t\t\t\t\tsetNewDomain(\"\");\n\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"grid gap-4 sm:grid-cols-2\">\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Domain\"\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"example.com\"\n\t\t\t\t\t\t\t\t\t\tvalue={newDomain}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setNewDomain(e.target.value)}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\t\tlabel=\"Default Role\"\n\t\t\t\t\t\t\t\t\t\tvalue={String(newRole)}\n\t\t\t\t\t\t\t\t\t\tonValueChange={(v) => v !== null && setNewRole(Number(v))}\n\t\t\t\t\t\t\t\t\t\titems={Object.fromEntries(ROLES.map((r) => [String(r.value), r.label]))}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{ROLES.map((role) => (\n\t\t\t\t\t\t\t\t\t\t\t<Select.Option key={role.value} value={String(role.value)}>\n\t\t\t\t\t\t\t\t\t\t\t\t{role.label}\n\t\t\t\t\t\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tonClick={handleAddDomain}\n\t\t\t\t\t\t\t\tdisabled={!newDomain.trim() || createMutation.isPending}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{createMutation.isPending ? \"Adding...\" : \"Add Domain\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Button onClick={() => setIsAddingDomain(true)} icon={<Plus />}>\n\t\t\t\t\t\t\tAdd Domain\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Edit Domain Dialog */}\n\t\t\t<Dialog.Root\n\t\t\t\topen={!!editingDomain}\n\t\t\t\tonOpenChange={(open: boolean) => !open && setEditingDomain(null)}\n\t\t\t>\n\t\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t<div className=\"flex flex-col space-y-1.5\">\n\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t\tEdit Domain\n\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t<Dialog.Description className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\tUpdate settings for {editingDomain?.domain}\n\t\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"space-y-4 py-4\">\n\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\tlabel=\"Default Role\"\n\t\t\t\t\t\t\t\tvalue={String(editingDomain?.defaultRole ?? 30)}\n\t\t\t\t\t\t\t\tonValueChange={(v) =>\n\t\t\t\t\t\t\t\t\tv !== null && editingDomain && handleUpdateRole(editingDomain.domain, Number(v))\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\titems={Object.fromEntries(ROLES.map((r) => [String(r.value), r.label]))}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{ROLES.map((role) => (\n\t\t\t\t\t\t\t\t\t<Select.Option key={role.value} value={String(role.value)}>\n\t\t\t\t\t\t\t\t\t\t{role.label}\n\t\t\t\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</Dialog>\n\t\t\t</Dialog.Root>\n\n\t\t\t{/* Delete Confirmation */}\n\t\t\t<Dialog.Root\n\t\t\t\topen={!!deletingDomain}\n\t\t\t\tonOpenChange={(open) => !open && setDeletingDomain(null)}\n\t\t\t\tdisablePointerDismissal\n\t\t\t>\n\t\t\t\t<Dialog className=\"p-6\" size=\"sm\">\n\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold\">Remove Domain?</Dialog.Title>\n\t\t\t\t\t<Dialog.Description className=\"text-kumo-subtle\">\n\t\t\t\t\t\tUsers from <strong>{deletingDomain}</strong> will no longer be able to sign up without\n\t\t\t\t\t\tan invite. Existing users are not affected.\n\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t<div className=\"mt-6 flex justify-end gap-2\">\n\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t<Button {...p} variant=\"secondary\">\n\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\trender={(p) => (\n\t\t\t\t\t\t\t\t<Button {...p} variant=\"destructive\" onClick={handleDelete}>\n\t\t\t\t\t\t\t\t\tRemove Domain\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</Dialog>\n\t\t\t</Dialog.Root>\n\t\t</div>\n\t);\n}\n\nexport default AllowedDomainsSettings;\n","/**\n * API Tokens settings page\n *\n * Allows admins to list, create, and revoke Personal Access Tokens.\n */\n\nimport { Button, Checkbox, Input, Loader, Select } from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tCopy,\n\tEye,\n\tEyeSlash,\n\tKey,\n\tPlus,\n\tTrash,\n\tWarningCircle,\n} from \"@phosphor-icons/react\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tfetchApiTokens,\n\tcreateApiToken,\n\trevokeApiToken,\n\tAPI_TOKEN_SCOPES,\n\ttype ApiTokenCreateResult,\n} from \"../../lib/api/api-tokens.js\";\nimport { getMutationError } from \"../DialogError.js\";\n\n// =============================================================================\n// Expiry options\n// =============================================================================\n\nconst EXPIRY_OPTIONS = [\n\t{ value: \"none\", label: \"No expiry\" },\n\t{ value: \"7d\", label: \"7 days\" },\n\t{ value: \"30d\", label: \"30 days\" },\n\t{ value: \"90d\", label: \"90 days\" },\n\t{ value: \"365d\", label: \"1 year\" },\n] as const;\n\nfunction computeExpiryDate(option: string): string | undefined {\n\tif (option === \"none\") return undefined;\n\tconst days = parseInt(option, 10);\n\tif (Number.isNaN(days)) return undefined;\n\tconst date = new Date();\n\tdate.setDate(date.getDate() + days);\n\treturn date.toISOString();\n}\n\n// =============================================================================\n// Main component\n// =============================================================================\n\nexport function ApiTokenSettings() {\n\tconst queryClient = useQueryClient();\n\tconst [showCreateForm, setShowCreateForm] = React.useState(false);\n\tconst [newToken, setNewToken] = React.useState<ApiTokenCreateResult | null>(null);\n\tconst [tokenVisible, setTokenVisible] = React.useState(false);\n\tconst [copied, setCopied] = React.useState(false);\n\tconst [revokeConfirmId, setRevokeConfirmId] = React.useState<string | null>(null);\n\n\t// Queries\n\tconst { data: tokens, isLoading } = useQuery({\n\t\tqueryKey: [\"api-tokens\"],\n\t\tqueryFn: fetchApiTokens,\n\t});\n\n\t// Create mutation\n\tconst createMutation = useMutation({\n\t\tmutationFn: createApiToken,\n\t\tonSuccess: (result) => {\n\t\t\tsetNewToken(result);\n\t\t\tsetShowCreateForm(false);\n\t\t\tsetTokenVisible(false);\n\t\t\tsetCopied(false);\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"api-tokens\"] });\n\t\t},\n\t});\n\n\t// Revoke mutation\n\tconst revokeMutation = useMutation({\n\t\tmutationFn: revokeApiToken,\n\t\tonSuccess: () => {\n\t\t\tsetRevokeConfirmId(null);\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"api-tokens\"] });\n\t\t},\n\t});\n\n\t// Clean up copy feedback timeout on unmount\n\tconst copyTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\tReact.useEffect(() => {\n\t\treturn () => {\n\t\t\tif (copyTimeoutRef.current) clearTimeout(copyTimeoutRef.current);\n\t\t};\n\t}, []);\n\n\tconst handleCopyToken = async () => {\n\t\tif (!newToken) return;\n\t\ttry {\n\t\t\tawait navigator.clipboard.writeText(newToken.token);\n\t\t\tsetCopied(true);\n\t\t\tcopyTimeoutRef.current = setTimeout(setCopied, 2000, false);\n\t\t} catch {\n\t\t\t// Clipboard API can fail in insecure contexts or when denied\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t<Link to=\"/settings\" className=\"text-kumo-subtle hover:text-kumo-default transition-colors\">\n\t\t\t\t\t<ArrowLeft className=\"h-5 w-5\" />\n\t\t\t\t</Link>\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">API Tokens</h1>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\tCreate personal access tokens for programmatic API access\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* New token banner */}\n\t\t\t{newToken && (\n\t\t\t\t<div className=\"rounded-lg border border-green-200 dark:border-green-800 bg-green-50 dark:bg-green-950/30 p-4\">\n\t\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t\t<Key className=\"h-5 w-5 text-green-600 dark:text-green-400 mt-0.5 shrink-0\" />\n\t\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t\t<p className=\"font-medium text-green-800 dark:text-green-200\">\n\t\t\t\t\t\t\t\tToken created: {newToken.info.name}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p className=\"text-sm text-green-700 dark:text-green-300 mt-1\">\n\t\t\t\t\t\t\t\tCopy this token now — it won't be shown again.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<div className=\"mt-3 flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<code className=\"flex-1 rounded bg-white dark:bg-black/30 px-3 py-2 text-sm font-mono border truncate\">\n\t\t\t\t\t\t\t\t\t{tokenVisible ? newToken.token : \"••••••••••••••••••••••••••••\"}\n\t\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\tonClick={() => setTokenVisible(!tokenVisible)}\n\t\t\t\t\t\t\t\t\taria-label={tokenVisible ? \"Hide token\" : \"Show token\"}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{tokenVisible ? <EyeSlash /> : <Eye />}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\tonClick={handleCopyToken}\n\t\t\t\t\t\t\t\t\taria-label=\"Copy token\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<Copy />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{copied && (\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-green-600 dark:text-green-400 mt-1\">\n\t\t\t\t\t\t\t\t\tCopied to clipboard\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\tonClick={() => setNewToken(null)}\n\t\t\t\t\t\t\taria-label=\"Dismiss\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tDismiss\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Create form */}\n\t\t\t{showCreateForm ? (\n\t\t\t\t<CreateTokenForm\n\t\t\t\t\tisCreating={createMutation.isPending}\n\t\t\t\t\terror={createMutation.error?.message ?? null}\n\t\t\t\t\tonSubmit={(input) =>\n\t\t\t\t\t\tcreateMutation.mutate({\n\t\t\t\t\t\t\tname: input.name,\n\t\t\t\t\t\t\tscopes: input.scopes,\n\t\t\t\t\t\t\texpiresAt: input.expiresAt,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tonCancel={() => setShowCreateForm(false)}\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<Button icon={<Plus />} onClick={() => setShowCreateForm(true)}>\n\t\t\t\t\tCreate Token\n\t\t\t\t</Button>\n\t\t\t)}\n\n\t\t\t{/* Token list */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t{isLoading ? (\n\t\t\t\t\t<div className=\"flex items-center justify-center py-8\">\n\t\t\t\t\t\t<Loader />\n\t\t\t\t\t</div>\n\t\t\t\t) : !tokens || tokens.length === 0 ? (\n\t\t\t\t\t<div className=\"py-8 text-center text-sm text-kumo-subtle\">\n\t\t\t\t\t\tNo API tokens yet. Create one to get started.\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"divide-y\">\n\t\t\t\t\t\t{tokens.map((token) => (\n\t\t\t\t\t\t\t<div key={token.id} className=\"flex items-center justify-between p-4\">\n\t\t\t\t\t\t\t\t<div className=\"min-w-0\">\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-medium truncate\">{token.name}</span>\n\t\t\t\t\t\t\t\t\t\t<code className=\"text-xs text-kumo-subtle bg-kumo-tint px-1.5 py-0.5 rounded\">\n\t\t\t\t\t\t\t\t\t\t\t{token.prefix}...\n\t\t\t\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"flex gap-3 mt-1 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t<span>Scopes: {token.scopes.join(\", \")}</span>\n\t\t\t\t\t\t\t\t\t\t{token.expiresAt && (\n\t\t\t\t\t\t\t\t\t\t\t<span>Expires {new Date(token.expiresAt).toLocaleDateString()}</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t{token.lastUsedAt && (\n\t\t\t\t\t\t\t\t\t\t\t<span>Last used {new Date(token.lastUsedAt).toLocaleDateString()}</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle mt-0.5\">\n\t\t\t\t\t\t\t\t\t\tCreated {new Date(token.createdAt).toLocaleDateString()}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{revokeConfirmId === token.id ? (\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 shrink-0\">\n\t\t\t\t\t\t\t\t\t\t{revokeMutation.error && (\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm text-kumo-danger\">\n\t\t\t\t\t\t\t\t\t\t\t\t{getMutationError(revokeMutation.error)}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm text-kumo-danger\">Revoke?</span>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\tdisabled={revokeMutation.isPending}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => revokeMutation.mutate(token.id)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{revokeMutation.isPending ? \"Revoking...\" : \"Confirm\"}\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\tsetRevokeConfirmId(null);\n\t\t\t\t\t\t\t\t\t\t\t\trevokeMutation.reset();\n\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => setRevokeConfirmId(token.id)}\n\t\t\t\t\t\t\t\t\t\taria-label=\"Revoke token\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<Trash className=\"h-4 w-4 text-kumo-subtle hover:text-kumo-danger\" />\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// =============================================================================\n// Create token form\n// =============================================================================\n\ninterface CreateTokenFormProps {\n\tisCreating: boolean;\n\terror: string | null;\n\tonSubmit: (input: { name: string; scopes: string[]; expiresAt?: string }) => void;\n\tonCancel: () => void;\n}\n\nfunction CreateTokenForm({ isCreating, error, onSubmit, onCancel }: CreateTokenFormProps) {\n\tconst [name, setName] = React.useState(\"\");\n\tconst [selectedScopes, setSelectedScopes] = React.useState<Set<string>>(new Set());\n\tconst [expiry, setExpiry] = React.useState(\"30d\");\n\n\tconst toggleScope = (scope: string) => {\n\t\tsetSelectedScopes((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(scope)) {\n\t\t\t\tnext.delete(scope);\n\t\t\t} else {\n\t\t\t\tnext.add(scope);\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t};\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tonSubmit({\n\t\t\tname: name.trim(),\n\t\t\tscopes: [...selectedScopes],\n\t\t\texpiresAt: computeExpiryDate(expiry),\n\t\t});\n\t};\n\n\tconst isValid = name.trim().length > 0 && selectedScopes.size > 0;\n\n\treturn (\n\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t<h2 className=\"text-lg font-semibold mb-4\">Create New Token</h2>\n\n\t\t\t{error && (\n\t\t\t\t<div className=\"mb-4 rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-3 flex items-center gap-2 text-sm text-kumo-danger\">\n\t\t\t\t\t<WarningCircle className=\"h-4 w-4 shrink-0\" />\n\t\t\t\t\t{error}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<form onSubmit={handleSubmit} className=\"space-y-4\">\n\t\t\t\t<Input\n\t\t\t\t\tlabel=\"Token Name\"\n\t\t\t\t\tvalue={name}\n\t\t\t\t\tonChange={(e) => setName(e.target.value)}\n\t\t\t\t\tplaceholder=\"e.g., CI/CD Pipeline\"\n\t\t\t\t\trequired\n\t\t\t\t\tautoFocus\n\t\t\t\t/>\n\n\t\t\t\t<div>\n\t\t\t\t\t<div className=\"text-sm font-medium mb-2\">Scopes</div>\n\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t{API_TOKEN_SCOPES.map((scope) => (\n\t\t\t\t\t\t\t<label key={scope.value} className=\"flex items-start gap-2 cursor-pointer\">\n\t\t\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\t\t\tchecked={selectedScopes.has(scope.value)}\n\t\t\t\t\t\t\t\t\tonCheckedChange={() => toggleScope(scope.value)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<div className=\"text-sm font-medium\">{scope.label}</div>\n\t\t\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle\">{scope.description}</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<Select\n\t\t\t\t\tlabel=\"Expiry\"\n\t\t\t\t\tvalue={expiry}\n\t\t\t\t\tonValueChange={(v) => v !== null && setExpiry(v)}\n\t\t\t\t\titems={Object.fromEntries(EXPIRY_OPTIONS.map((o) => [o.value, o.label]))}\n\t\t\t\t>\n\t\t\t\t\t{EXPIRY_OPTIONS.map((option) => (\n\t\t\t\t\t\t<Select.Option key={option.value} value={option.value}>\n\t\t\t\t\t\t\t{option.label}\n\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t))}\n\t\t\t\t</Select>\n\n\t\t\t\t<div className=\"flex gap-2 pt-2\">\n\t\t\t\t\t<Button type=\"submit\" disabled={!isValid || isCreating}>\n\t\t\t\t\t\t{isCreating ? \"Creating...\" : \"Create Token\"}\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={onCancel}>\n\t\t\t\t\t\tCancel\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n","/**\n * Email settings page\n *\n * Shows current email pipeline status, provider info, and allows\n * sending a test email through the full pipeline.\n */\n\nimport { Button, Input, Loader } from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tCheckCircle,\n\tEnvelope,\n\tPaperPlaneTilt,\n\tPlugsConnected,\n\tWarningCircle,\n} from \"@phosphor-icons/react\";\nimport { useMutation, useQuery } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tfetchEmailSettings,\n\tsendTestEmail,\n\ttype EmailSettings as EmailSettingsData,\n} from \"../../lib/api/email-settings.js\";\nimport { getMutationError } from \"../DialogError.js\";\n\nexport function EmailSettings() {\n\tconst [testEmail, setTestEmail] = React.useState(\"\");\n\tconst [status, setStatus] = React.useState<{\n\t\ttype: \"success\" | \"error\";\n\t\tmessage: string;\n\t} | null>(null);\n\n\t// Clear status after 5 seconds\n\tReact.useEffect(() => {\n\t\tif (!status) return;\n\t\tconst timer = setTimeout(setStatus, 5000, null);\n\t\treturn () => clearTimeout(timer);\n\t}, [status]);\n\n\tconst { data: settings, isLoading } = useQuery({\n\t\tqueryKey: [\"email-settings\"],\n\t\tqueryFn: fetchEmailSettings,\n\t});\n\n\tconst testMutation = useMutation({\n\t\tmutationFn: (to: string) => sendTestEmail(to),\n\t\tonSuccess: (result) => {\n\t\t\tsetStatus({ type: \"success\", message: result.message });\n\t\t\tsetTestEmail(\"\");\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: getMutationError(error) || \"Failed to send test email\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleTestSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (!testEmail) return;\n\t\ttestMutation.mutate(testEmail);\n\t};\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center py-12\">\n\t\t\t\t<Loader size=\"lg\" />\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to settings\">\n\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</Link>\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Email Settings</h1>\n\t\t\t</div>\n\n\t\t\t{/* Status banner */}\n\t\t\t{status && (\n\t\t\t\t<div\n\t\t\t\t\tclassName={`flex items-center gap-2 rounded-lg border p-3 text-sm ${\n\t\t\t\t\t\tstatus.type === \"success\"\n\t\t\t\t\t\t\t? \"border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950/30 dark:text-green-200\"\n\t\t\t\t\t\t\t: \"border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950/30 dark:text-red-200\"\n\t\t\t\t\t}`}\n\t\t\t\t>\n\t\t\t\t\t{status.type === \"success\" ? (\n\t\t\t\t\t\t<CheckCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<WarningCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t)}\n\t\t\t\t\t{status.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Pipeline status */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-center gap-2 mb-4\">\n\t\t\t\t\t<Envelope className=\"h-5 w-5 text-kumo-subtle\" />\n\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Email Pipeline</h2>\n\t\t\t\t</div>\n\n\t\t\t\t<PipelineStatus settings={settings} />\n\t\t\t</div>\n\n\t\t\t{/* Test email */}\n\t\t\t{settings?.available && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<div className=\"flex items-center gap-2 mb-4\">\n\t\t\t\t\t\t<PaperPlaneTilt className=\"h-5 w-5 text-kumo-subtle\" />\n\t\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Send Test Email</h2>\n\t\t\t\t\t</div>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mb-4\">\n\t\t\t\t\t\tSend a test email through the full pipeline to verify your email configuration.\n\t\t\t\t\t</p>\n\t\t\t\t\t<form onSubmit={handleTestSubmit} className=\"flex items-end gap-3\">\n\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Recipient email\"\n\t\t\t\t\t\t\t\ttype=\"email\"\n\t\t\t\t\t\t\t\tvalue={testEmail}\n\t\t\t\t\t\t\t\tonChange={(e) => setTestEmail(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"test@example.com\"\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Button type=\"submit\" disabled={testMutation.isPending || !testEmail}>\n\t\t\t\t\t\t\t{testMutation.isPending ? \"Sending...\" : \"Send Test\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</form>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// =============================================================================\n// Pipeline status display\n// =============================================================================\n\nfunction PipelineStatus({ settings }: { settings: EmailSettingsData | undefined }) {\n\tif (!settings) return null;\n\n\tif (!settings.available) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-950/30 p-4\">\n\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t<WarningCircle className=\"h-5 w-5 text-amber-600 dark:text-amber-400 mt-0.5 flex-shrink-0\" />\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<p className=\"text-sm font-medium text-amber-800 dark:text-amber-200\">\n\t\t\t\t\t\t\tNo email provider configured\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p className=\"text-sm text-amber-700 dark:text-amber-300 mt-1\">\n\t\t\t\t\t\t\tInstall and activate an email provider plugin to enable email features like\n\t\t\t\t\t\t\tinvitations, magic links, and password recovery.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<p className=\"text-sm text-amber-700 dark:text-amber-300 mt-2\">\n\t\t\t\t\t\t\tWithout an email provider, invite links must be shared manually.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Provider */}\n\t\t\t<div className=\"flex items-center gap-3 p-3 rounded-md bg-green-50 dark:bg-green-950/20 border border-green-200 dark:border-green-800\">\n\t\t\t\t<CheckCircle className=\"h-5 w-5 text-green-600 dark:text-green-400 flex-shrink-0\" />\n\t\t\t\t<div>\n\t\t\t\t\t<p className=\"text-sm font-medium text-green-800 dark:text-green-200\">\n\t\t\t\t\t\tEmail provider active\n\t\t\t\t\t</p>\n\t\t\t\t\t<p className=\"text-sm text-green-700 dark:text-green-300\">\n\t\t\t\t\t\tProvider:{\" \"}\n\t\t\t\t\t\t<code className=\"rounded bg-green-100 dark:bg-green-900/40 px-1.5 py-0.5 text-xs\">\n\t\t\t\t\t\t\t{settings.selectedProviderId || \"default\"}\n\t\t\t\t\t\t</code>\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Middleware */}\n\t\t\t{(settings.middleware.beforeSend.length > 0 || settings.middleware.afterSend.length > 0) && (\n\t\t\t\t<div className=\"p-3 rounded-md bg-kumo-tint/50 border\">\n\t\t\t\t\t<div className=\"flex items-center gap-2 mb-2\">\n\t\t\t\t\t\t<PlugsConnected className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t<p className=\"text-sm font-medium\">Email Middleware</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t{settings.middleware.beforeSend.length > 0 && (\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\tBefore send: {settings.middleware.beforeSend.join(\", \")}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t\t{settings.middleware.afterSend.length > 0 && (\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\tAfter send: {settings.middleware.afterSend.join(\", \")}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Available providers (if multiple) */}\n\t\t\t{settings.providers.length > 1 && (\n\t\t\t\t<div className=\"p-3 rounded-md bg-kumo-tint/50 border\">\n\t\t\t\t\t<p className=\"text-sm font-medium mb-1\">Available Providers</p>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{settings.providers.map((p) => p.pluginId).join(\", \")}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","/**\n * General Settings sub-page\n *\n * Site Identity (title, tagline, URL, logo, favicon) and Reading settings\n * (posts per page, date format, timezone).\n */\n\nimport { Button, Input, Label } from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tFloppyDisk,\n\tCheckCircle,\n\tWarningCircle,\n\tUpload,\n\tX,\n} from \"@phosphor-icons/react\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchSettings, updateSettings, type SiteSettings, type MediaItem } from \"../../lib/api\";\nimport { MediaPickerModal } from \"../MediaPickerModal\";\n\nexport function GeneralSettings() {\n\tconst queryClient = useQueryClient();\n\n\tconst { data: settings, isLoading } = useQuery({\n\t\tqueryKey: [\"settings\"],\n\t\tqueryFn: fetchSettings,\n\t\tstaleTime: Infinity,\n\t});\n\n\tconst [formData, setFormData] = React.useState<Partial<SiteSettings>>({});\n\tconst [saveStatus, setSaveStatus] = React.useState<{\n\t\ttype: \"success\" | \"error\";\n\t\tmessage: string;\n\t} | null>(null);\n\n\tconst [logoPickerOpen, setLogoPickerOpen] = React.useState(false);\n\tconst [faviconPickerOpen, setFaviconPickerOpen] = React.useState(false);\n\n\tReact.useEffect(() => {\n\t\tif (settings) setFormData(settings);\n\t}, [settings]);\n\n\tReact.useEffect(() => {\n\t\tif (saveStatus) {\n\t\t\tconst timer = setTimeout(setSaveStatus, 3000, null);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [saveStatus]);\n\n\tconst saveMutation = useMutation({\n\t\tmutationFn: (data: Partial<SiteSettings>) => updateSettings(data),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"settings\"] });\n\t\t\tsetSaveStatus({ type: \"success\", message: \"Settings saved successfully\" });\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: error instanceof Error ? error.message : \"Failed to save settings\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsaveMutation.mutate(formData);\n\t};\n\n\tconst handleChange = (key: keyof SiteSettings, value: unknown) => {\n\t\tsetFormData((prev) => ({ ...prev, [key]: value }));\n\t};\n\n\tconst handleLogoSelect = (media: MediaItem) => {\n\t\tsetFormData((prev) => ({\n\t\t\t...prev,\n\t\t\tlogo: { mediaId: media.id, alt: media.alt || \"\", url: media.url },\n\t\t}));\n\t\tsetLogoPickerOpen(false);\n\t};\n\n\tconst handleFaviconSelect = (media: MediaItem) => {\n\t\tsetFormData((prev) => ({\n\t\t\t...prev,\n\t\t\tfavicon: { mediaId: media.id, url: media.url },\n\t\t}));\n\t\tsetFaviconPickerOpen(false);\n\t};\n\n\tconst handleLogoRemove = () => {\n\t\tsetFormData((prev) => ({ ...prev, logo: undefined }));\n\t};\n\n\tconst handleFaviconRemove = () => {\n\t\tsetFormData((prev) => ({ ...prev, favicon: undefined }));\n\t};\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to settings\">\n\t\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</Link>\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">General Settings</h1>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<p className=\"text-kumo-subtle\">Loading settings...</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to settings\">\n\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</Link>\n\t\t\t\t<h1 className=\"text-2xl font-bold\">General Settings</h1>\n\t\t\t</div>\n\n\t\t\t{/* Status banner */}\n\t\t\t{saveStatus && (\n\t\t\t\t<div\n\t\t\t\t\tclassName={`flex items-center gap-2 rounded-lg border p-3 text-sm ${\n\t\t\t\t\t\tsaveStatus.type === \"success\"\n\t\t\t\t\t\t\t? \"border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950/30 dark:text-green-200\"\n\t\t\t\t\t\t\t: \"border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950/30 dark:text-red-200\"\n\t\t\t\t\t}`}\n\t\t\t\t>\n\t\t\t\t\t{saveStatus.type === \"success\" ? (\n\t\t\t\t\t\t<CheckCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<WarningCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t)}\n\t\t\t\t\t{saveStatus.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<form onSubmit={handleSubmit} className=\"space-y-6\">\n\t\t\t\t{/* Site Identity */}\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<h2 className=\"mb-4 text-lg font-semibold\">Site Identity</h2>\n\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Site Title\"\n\t\t\t\t\t\t\tvalue={formData.title || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleChange(\"title\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"The name of your site, used in the header and metadata\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Tagline\"\n\t\t\t\t\t\t\tvalue={formData.tagline || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleChange(\"tagline\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"A short description of your site\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Site URL\"\n\t\t\t\t\t\t\ttype=\"url\"\n\t\t\t\t\t\t\tvalue={formData.url || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleChange(\"url\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"The public URL of your site (used for canonical links and sitemaps)\"\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t{/* Logo Picker */}\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Label>Logo</Label>\n\t\t\t\t\t\t\t{formData.logo?.url ? (\n\t\t\t\t\t\t\t\t<div className=\"mt-2 space-y-2\">\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tsrc={formData.logo.url}\n\t\t\t\t\t\t\t\t\t\talt={formData.logo.alt || \"Logo\"}\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-16 rounded border bg-kumo-tint object-contain p-2\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\ticon={<Upload />}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => setLogoPickerOpen(true)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tChange Logo\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\ticon={<X />}\n\t\t\t\t\t\t\t\t\t\t\tonClick={handleLogoRemove}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tRemove\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\ticon={<Upload />}\n\t\t\t\t\t\t\t\t\tonClick={() => setLogoPickerOpen(true)}\n\t\t\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tSelect Logo\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Favicon Picker */}\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Label>Favicon</Label>\n\t\t\t\t\t\t\t{formData.favicon?.url ? (\n\t\t\t\t\t\t\t\t<div className=\"mt-2 space-y-2\">\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tsrc={formData.favicon.url}\n\t\t\t\t\t\t\t\t\t\talt=\"Favicon\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-8 w-8 rounded border bg-kumo-tint object-contain p-1\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\ticon={<Upload />}\n\t\t\t\t\t\t\t\t\t\t\tonClick={() => setFaviconPickerOpen(true)}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tChange Favicon\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\t\t\t\ticon={<X />}\n\t\t\t\t\t\t\t\t\t\t\tonClick={handleFaviconRemove}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\tRemove\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\ticon={<Upload />}\n\t\t\t\t\t\t\t\t\tonClick={() => setFaviconPickerOpen(true)}\n\t\t\t\t\t\t\t\t\tclassName=\"mt-2\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tSelect Favicon\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Reading Settings */}\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<h2 className=\"mb-4 text-lg font-semibold\">Reading</h2>\n\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Posts Per Page\"\n\t\t\t\t\t\t\ttype=\"number\"\n\t\t\t\t\t\t\tvalue={formData.postsPerPage || 10}\n\t\t\t\t\t\t\tonChange={(e) => handleChange(\"postsPerPage\", parseInt(e.target.value, 10))}\n\t\t\t\t\t\t\tmin={1}\n\t\t\t\t\t\t\tmax={100}\n\t\t\t\t\t\t\tdescription=\"Number of posts to show per page on list views\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Date Format\"\n\t\t\t\t\t\t\tvalue={formData.dateFormat || \"MMMM d, yyyy\"}\n\t\t\t\t\t\t\tonChange={(e) => handleChange(\"dateFormat\", e.target.value)}\n\t\t\t\t\t\t\tdescription={`Example: ${formData.dateFormat || \"MMMM d, yyyy\"} → January 23, 2026`}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Timezone\"\n\t\t\t\t\t\t\tvalue={formData.timezone || \"UTC\"}\n\t\t\t\t\t\t\tonChange={(e) => handleChange(\"timezone\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Timezone for displaying dates (e.g., America/New_York)\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Save Button */}\n\t\t\t\t<div className=\"flex justify-end\">\n\t\t\t\t\t<Button type=\"submit\" disabled={saveMutation.isPending} icon={<FloppyDisk />}>\n\t\t\t\t\t\t{saveMutation.isPending ? \"Saving...\" : \"Save Settings\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</form>\n\n\t\t\t{/* Media Picker Modals */}\n\t\t\t<MediaPickerModal\n\t\t\t\topen={logoPickerOpen}\n\t\t\t\tonOpenChange={setLogoPickerOpen}\n\t\t\t\tonSelect={handleLogoSelect}\n\t\t\t\tmimeTypeFilter=\"image/\"\n\t\t\t\ttitle=\"Select Logo\"\n\t\t\t/>\n\t\t\t<MediaPickerModal\n\t\t\t\topen={faviconPickerOpen}\n\t\t\t\tonOpenChange={setFaviconPickerOpen}\n\t\t\t\tonSelect={handleFaviconSelect}\n\t\t\t\tmimeTypeFilter=\"image/\"\n\t\t\t\ttitle=\"Select Favicon\"\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nexport default GeneralSettings;\n","/**\n * PasskeyRegistration - WebAuthn credential registration component\n *\n * Handles the passkey registration flow:\n * 1. Fetches registration options from server\n * 2. Triggers browser's WebAuthn credential creation\n * 3. Sends attestation back to server for verification\n *\n * Used in:\n * - Setup wizard (first admin creation)\n * - User settings (adding additional passkeys)\n */\n\nimport { Button, Input } from \"@cloudflare/kumo\";\nimport * as React from \"react\";\n\nimport { apiFetch, parseApiResponse } from \"../../lib/api/client\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst BASE64URL_DASH_REGEX = /-/g;\nconst BASE64URL_UNDERSCORE_REGEX = /_/g;\nconst BASE64_PLUS_REGEX = /\\+/g;\nconst BASE64_SLASH_REGEX = /\\//g;\n\n// ============================================================================\n// WebAuthn types\n// ============================================================================\ninterface PublicKeyCredentialCreationOptionsJSON {\n\tchallenge: string;\n\trp: {\n\t\tname: string;\n\t\tid: string;\n\t};\n\tuser: {\n\t\tid: string;\n\t\tname: string;\n\t\tdisplayName: string;\n\t};\n\tpubKeyCredParams: Array<{\n\t\ttype: \"public-key\";\n\t\talg: number;\n\t}>;\n\ttimeout?: number;\n\tattestation?: \"none\" | \"indirect\" | \"direct\";\n\tauthenticatorSelection?: {\n\t\tauthenticatorAttachment?: \"platform\" | \"cross-platform\";\n\t\tresidentKey?: \"discouraged\" | \"preferred\" | \"required\";\n\t\trequireResidentKey?: boolean;\n\t\tuserVerification?: \"discouraged\" | \"preferred\" | \"required\";\n\t};\n\texcludeCredentials?: Array<{\n\t\ttype: \"public-key\";\n\t\tid: string;\n\t\ttransports?: AuthenticatorTransport[];\n\t}>;\n}\n\ninterface RegistrationResponse {\n\tid: string;\n\trawId: string;\n\ttype: \"public-key\";\n\tresponse: {\n\t\tclientDataJSON: string;\n\t\tattestationObject: string;\n\t\ttransports?: AuthenticatorTransport[];\n\t};\n\tauthenticatorAttachment?: \"platform\" | \"cross-platform\";\n}\n\nexport interface PasskeyRegistrationProps {\n\t/** Endpoint to get registration options */\n\toptionsEndpoint: string;\n\t/** Endpoint to verify registration */\n\tverifyEndpoint: string;\n\t/** Called on successful registration */\n\tonSuccess: (response: unknown) => void;\n\t/** Called on error */\n\tonError?: (error: Error) => void;\n\t/** Button text */\n\tbuttonText?: string;\n\t/** Show passkey name input */\n\tshowNameInput?: boolean;\n\t/** Additional data to send with requests */\n\tadditionalData?: Record<string, unknown>;\n}\n\nconst EMPTY_DATA: Record<string, unknown> = {};\n\ntype RegistrationState =\n\t| { status: \"idle\" }\n\t| { status: \"loading\"; message: string }\n\t| { status: \"error\"; message: string }\n\t| { status: \"success\" };\n\n/**\n * Check if WebAuthn is supported in the current browser\n */\nfunction isWebAuthnSupported(): boolean {\n\treturn (\n\t\ttypeof window !== \"undefined\" &&\n\t\twindow.PublicKeyCredential !== undefined &&\n\t\ttypeof window.PublicKeyCredential === \"function\"\n\t);\n}\n\n/**\n * Convert base64url to ArrayBuffer\n */\nfunction base64urlToBuffer(base64url: string): ArrayBuffer {\n\tconst base64 = base64url\n\t\t.replace(BASE64URL_DASH_REGEX, \"+\")\n\t\t.replace(BASE64URL_UNDERSCORE_REGEX, \"/\");\n\tconst padding = \"=\".repeat((4 - (base64.length % 4)) % 4);\n\tconst binary = atob(base64 + padding);\n\tconst bytes = new Uint8Array(binary.length);\n\tfor (let i = 0; i < binary.length; i++) {\n\t\tbytes[i] = binary.charCodeAt(i);\n\t}\n\treturn bytes.buffer;\n}\n\n/**\n * Convert ArrayBuffer to base64url (with padding for @oslojs/encoding compatibility)\n */\nfunction bufferToBase64url(buffer: ArrayBuffer): string {\n\tconst bytes = new Uint8Array(buffer);\n\tlet binary = \"\";\n\tfor (let i = 0; i < bytes.length; i++) {\n\t\tbinary += String.fromCharCode(bytes[i]!);\n\t}\n\tconst base64 = btoa(binary);\n\t// Convert to base64url but keep padding (required by @oslojs/encoding)\n\treturn base64.replace(BASE64_PLUS_REGEX, \"-\").replace(BASE64_SLASH_REGEX, \"_\");\n}\n\n/**\n * PasskeyRegistration Component\n */\nexport function PasskeyRegistration({\n\toptionsEndpoint,\n\tverifyEndpoint,\n\tonSuccess,\n\tonError,\n\tbuttonText = \"Register Passkey\",\n\tshowNameInput = false,\n\tadditionalData = EMPTY_DATA,\n}: PasskeyRegistrationProps) {\n\tconst [state, setState] = React.useState<RegistrationState>({\n\t\tstatus: \"idle\",\n\t});\n\tconst [passkeyName, setPasskeyName] = React.useState(\"\");\n\n\t// Check WebAuthn support on mount\n\tconst isSupported = React.useMemo(() => isWebAuthnSupported(), []);\n\n\tconst handleRegister = React.useCallback(async () => {\n\t\tif (!isSupported) {\n\t\t\tsetState({\n\t\t\t\tstatus: \"error\",\n\t\t\t\tmessage: \"WebAuthn is not supported in this browser\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\t// Step 1: Get registration options from server\n\t\t\tsetState({ status: \"loading\", message: \"Preparing registration...\" });\n\n\t\t\tconst optionsResponse = await apiFetch(optionsEndpoint, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify(additionalData),\n\t\t\t});\n\n\t\t\tconst optionsData = await parseApiResponse<{\n\t\t\t\toptions: PublicKeyCredentialCreationOptionsJSON;\n\t\t\t}>(optionsResponse, \"Failed to get registration options\");\n\t\t\tconst { options } = optionsData;\n\n\t\t\t// Step 2: Create credential with browser\n\t\t\tsetState({ status: \"loading\", message: \"Waiting for passkey...\" });\n\n\t\t\t// Convert options to the format expected by the browser\n\t\t\tconst publicKeyOptions: PublicKeyCredentialCreationOptions = {\n\t\t\t\tchallenge: base64urlToBuffer(options.challenge),\n\t\t\t\trp: options.rp,\n\t\t\t\tuser: {\n\t\t\t\t\tid: base64urlToBuffer(options.user.id),\n\t\t\t\t\tname: options.user.name,\n\t\t\t\t\tdisplayName: options.user.displayName,\n\t\t\t\t},\n\t\t\t\tpubKeyCredParams: options.pubKeyCredParams,\n\t\t\t\ttimeout: options.timeout,\n\t\t\t\tattestation: options.attestation,\n\t\t\t\tauthenticatorSelection: options.authenticatorSelection,\n\t\t\t\texcludeCredentials: options.excludeCredentials?.map((cred) => ({\n\t\t\t\t\ttype: cred.type,\n\t\t\t\t\tid: base64urlToBuffer(cred.id),\n\t\t\t\t\ttransports: cred.transports,\n\t\t\t\t})),\n\t\t\t};\n\n\t\t\tconst rawCredential = await navigator.credentials.create({\n\t\t\t\tpublicKey: publicKeyOptions,\n\t\t\t});\n\n\t\t\tif (!rawCredential) {\n\t\t\t\tthrow new Error(\"No credential returned from authenticator\");\n\t\t\t}\n\n\t\t\t// Step 3: Send credential to server for verification\n\t\t\tsetState({ status: \"loading\", message: \"Verifying...\" });\n\n\t\t\t// navigator.credentials.create() with publicKey returns PublicKeyCredential\n\t\t\tconst credential = rawCredential as PublicKeyCredential;\n\t\t\tconst attestationResponse = credential.response as AuthenticatorAttestationResponse;\n\n\t\t\t// authenticatorAttachment exists at runtime on PublicKeyCredential but isn't in the base type definition\n\t\t\tconst rawAttachment =\n\t\t\t\t\"authenticatorAttachment\" in credential ? credential.authenticatorAttachment : undefined;\n\t\t\tconst authenticatorAttachment =\n\t\t\t\trawAttachment === \"platform\" || rawAttachment === \"cross-platform\"\n\t\t\t\t\t? rawAttachment\n\t\t\t\t\t: undefined;\n\n\t\t\tconst registrationResponse: RegistrationResponse = {\n\t\t\t\tid: credential.id,\n\t\t\t\trawId: bufferToBase64url(credential.rawId),\n\t\t\t\ttype: \"public-key\",\n\t\t\t\tresponse: {\n\t\t\t\t\tclientDataJSON: bufferToBase64url(attestationResponse.clientDataJSON),\n\t\t\t\t\tattestationObject: bufferToBase64url(attestationResponse.attestationObject),\n\t\t\t\t\ttransports: attestationResponse.getTransports?.() as AuthenticatorTransport[] | undefined,\n\t\t\t\t},\n\t\t\t\tauthenticatorAttachment,\n\t\t\t};\n\n\t\t\tconst verifyResponse = await apiFetch(verifyEndpoint, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tcredential: registrationResponse,\n\t\t\t\t\tname: passkeyName || undefined,\n\t\t\t\t\t...additionalData,\n\t\t\t\t}),\n\t\t\t});\n\n\t\t\tconst result = await parseApiResponse<unknown>(\n\t\t\t\tverifyResponse,\n\t\t\t\t\"Failed to verify registration\",\n\t\t\t);\n\n\t\t\tsetState({ status: \"success\" });\n\t\t\tonSuccess(result);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : \"Registration failed\";\n\n\t\t\t// Handle specific WebAuthn errors\n\t\t\tlet userMessage = message;\n\t\t\tif (error instanceof DOMException) {\n\t\t\t\tswitch (error.name) {\n\t\t\t\t\tcase \"NotAllowedError\":\n\t\t\t\t\t\tuserMessage = \"Registration was cancelled or timed out. Please try again.\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"InvalidStateError\":\n\t\t\t\t\t\tuserMessage = \"This passkey is already registered on this device.\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"NotSupportedError\":\n\t\t\t\t\t\tuserMessage = \"Your device doesn't support the required security features.\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase \"SecurityError\":\n\t\t\t\t\t\tuserMessage = \"Security error. Make sure you're on a secure connection.\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tuserMessage = `Authentication error: ${error.message}`;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tsetState({ status: \"error\", message: userMessage });\n\t\t\tonError?.(new Error(userMessage));\n\t\t}\n\t}, [\n\t\tisSupported,\n\t\toptionsEndpoint,\n\t\tverifyEndpoint,\n\t\tadditionalData,\n\t\tpasskeyName,\n\t\tonSuccess,\n\t\tonError,\n\t]);\n\n\t// Not supported message\n\tif (!isSupported) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-4\">\n\t\t\t\t<h3 className=\"font-medium text-kumo-danger\">Passkeys Not Supported</h3>\n\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\tYour browser doesn't support passkeys. Please use a modern browser like Chrome, Safari,\n\t\t\t\t\tFirefox, or Edge.\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Passkey name input (optional) */}\n\t\t\t{showNameInput && (\n\t\t\t\t<div>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Passkey Name (optional)\"\n\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\tvalue={passkeyName}\n\t\t\t\t\t\tonChange={(e) => setPasskeyName(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"e.g., MacBook Pro, iPhone\"\n\t\t\t\t\t\tdisabled={state.status === \"loading\"}\n\t\t\t\t\t/>\n\t\t\t\t\t<p className=\"mt-1 text-xs text-kumo-subtle\">\n\t\t\t\t\t\tGive this passkey a name to help you identify it later.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Error message */}\n\t\t\t{state.status === \"error\" && (\n\t\t\t\t<div className=\"rounded-lg bg-kumo-danger/10 p-4 text-sm text-kumo-danger\">\n\t\t\t\t\t{state.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Success message */}\n\t\t\t{state.status === \"success\" && (\n\t\t\t\t<div className=\"rounded-lg bg-green-500/10 p-4 text-sm text-green-700 dark:text-green-400\">\n\t\t\t\t\tPasskey registered successfully!\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Register button */}\n\t\t\t<Button\n\t\t\t\ttype=\"button\"\n\t\t\t\tonClick={handleRegister}\n\t\t\t\tloading={state.status === \"loading\"}\n\t\t\t\tclassName=\"w-full justify-center\"\n\t\t\t\tvariant=\"primary\"\n\t\t\t>\n\t\t\t\t{state.status === \"loading\" ? <>{state.message}</> : buttonText}\n\t\t\t</Button>\n\n\t\t\t{/* Help text */}\n\t\t\t<p className=\"text-xs text-kumo-subtle text-center\">\n\t\t\t\tYou'll be prompted to use your device's biometric authentication, security key, or PIN.\n\t\t\t</p>\n\t\t</div>\n\t);\n}\n","/**\n * PasskeyItem - Individual passkey display with rename and delete actions\n */\n\nimport { Button, Input } from \"@cloudflare/kumo\";\nimport { Pencil, Trash, Check, X, DeviceMobile, Cloud } from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport type { PasskeyInfo } from \"../../lib/api\";\nimport { ConfirmDialog } from \"../ConfirmDialog.js\";\n\nexport interface PasskeyItemProps {\n\tpasskey: PasskeyInfo;\n\tcanDelete: boolean;\n\tonRename: (id: string, name: string) => Promise<void>;\n\tonDelete: (id: string) => Promise<void>;\n\tisDeleting?: boolean;\n\tisRenaming?: boolean;\n}\n\nfunction formatDeviceType(type: \"singleDevice\" | \"multiDevice\"): string {\n\treturn type === \"multiDevice\" ? \"Synced passkey\" : \"Device-bound passkey\";\n}\n\nfunction formatRelativeTime(dateString: string): string {\n\tconst date = new Date(dateString);\n\tconst now = new Date();\n\tconst diffMs = now.getTime() - date.getTime();\n\tconst diffSecs = Math.floor(diffMs / 1000);\n\tconst diffMins = Math.floor(diffSecs / 60);\n\tconst diffHours = Math.floor(diffMins / 60);\n\tconst diffDays = Math.floor(diffHours / 24);\n\n\tif (diffSecs < 60) {\n\t\treturn \"just now\";\n\t} else if (diffMins < 60) {\n\t\treturn `${diffMins} minute${diffMins === 1 ? \"\" : \"s\"} ago`;\n\t} else if (diffHours < 24) {\n\t\treturn `${diffHours} hour${diffHours === 1 ? \"\" : \"s\"} ago`;\n\t} else if (diffDays < 7) {\n\t\treturn `${diffDays} day${diffDays === 1 ? \"\" : \"s\"} ago`;\n\t} else {\n\t\treturn date.toLocaleDateString(undefined, {\n\t\t\tmonth: \"short\",\n\t\t\tday: \"numeric\",\n\t\t\tyear: date.getFullYear() !== now.getFullYear() ? \"numeric\" : undefined,\n\t\t});\n\t}\n}\n\nexport function PasskeyItem({\n\tpasskey,\n\tcanDelete,\n\tonRename,\n\tonDelete,\n\tisDeleting,\n\tisRenaming,\n}: PasskeyItemProps) {\n\tconst [isEditing, setIsEditing] = React.useState(false);\n\tconst [editName, setEditName] = React.useState(passkey.name || \"\");\n\tconst [showDeleteDialog, setShowDeleteDialog] = React.useState(false);\n\tconst [deleteError, setDeleteError] = React.useState<string | null>(null);\n\tconst inputRef = React.useRef<HTMLInputElement>(null);\n\n\t// Focus input when editing starts\n\tReact.useEffect(() => {\n\t\tif (isEditing && inputRef.current) {\n\t\t\tinputRef.current.focus();\n\t\t\tinputRef.current.select();\n\t\t}\n\t}, [isEditing]);\n\n\tconst handleSave = async () => {\n\t\ttry {\n\t\t\tawait onRename(passkey.id, editName.trim());\n\t\t\tsetIsEditing(false);\n\t\t} catch {\n\t\t\t// Error handled by parent\n\t\t}\n\t};\n\n\tconst handleCancel = () => {\n\t\tsetEditName(passkey.name || \"\");\n\t\tsetIsEditing(false);\n\t};\n\n\tconst handleKeyDown = (e: React.KeyboardEvent) => {\n\t\tif (e.key === \"Enter\") {\n\t\t\tvoid handleSave();\n\t\t} else if (e.key === \"Escape\") {\n\t\t\thandleCancel();\n\t\t}\n\t};\n\n\tconst handleDelete = async () => {\n\t\ttry {\n\t\t\tsetDeleteError(null);\n\t\t\tawait onDelete(passkey.id);\n\t\t\tsetShowDeleteDialog(false);\n\t\t} catch (err) {\n\t\t\tsetDeleteError(err instanceof Error ? err.message : \"Failed to remove passkey\");\n\t\t}\n\t};\n\n\treturn (\n\t\t<li className=\"flex items-center justify-between p-4 border rounded-lg bg-kumo-base\">\n\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t{/* Icon */}\n\t\t\t\t<div className=\"mt-0.5 p-2 rounded-md bg-kumo-tint\">\n\t\t\t\t\t{passkey.deviceType === \"multiDevice\" ? (\n\t\t\t\t\t\t<Cloud className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<DeviceMobile className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Info */}\n\t\t\t\t<div>\n\t\t\t\t\t{isEditing ? (\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tref={inputRef}\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tvalue={editName}\n\t\t\t\t\t\t\t\tonChange={(e) => setEditName(e.target.value)}\n\t\t\t\t\t\t\t\tonKeyDown={handleKeyDown}\n\t\t\t\t\t\t\t\tclassName=\"h-8 w-48\"\n\t\t\t\t\t\t\t\tplaceholder=\"Passkey name\"\n\t\t\t\t\t\t\t\tdisabled={isRenaming}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tonClick={handleSave}\n\t\t\t\t\t\t\t\tdisabled={isRenaming}\n\t\t\t\t\t\t\t\taria-label=\"Save name\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Check className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tonClick={handleCancel}\n\t\t\t\t\t\t\t\tdisabled={isRenaming}\n\t\t\t\t\t\t\t\taria-label=\"Cancel rename\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"font-medium\">{passkey.name || \"Unnamed passkey\"}</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{formatDeviceType(passkey.deviceType)}\n\t\t\t\t\t\t{passkey.backedUp && (\n\t\t\t\t\t\t\t<span className=\"text-green-600 dark:text-green-400\"> (synced)</span>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"text-xs text-kumo-subtle mt-1\">\n\t\t\t\t\t\tLast used {formatRelativeTime(passkey.lastUsedAt)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Actions */}\n\t\t\t{!isEditing && (\n\t\t\t\t<div className=\"flex items-center gap-1\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\tsetEditName(passkey.name || \"\");\n\t\t\t\t\t\t\tsetIsEditing(true);\n\t\t\t\t\t\t}}\n\t\t\t\t\t\ttitle=\"Rename\"\n\t\t\t\t\t\taria-label={`Rename ${passkey.name || \"passkey\"}`}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Pencil className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t{canDelete && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\tonClick={() => setShowDeleteDialog(true)}\n\t\t\t\t\t\t\tclassName=\"text-kumo-danger hover:text-kumo-danger\"\n\t\t\t\t\t\t\ttitle=\"Remove\"\n\t\t\t\t\t\t\taria-label={`Remove ${passkey.name || \"passkey\"}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Delete confirmation dialog */}\n\t\t\t<ConfirmDialog\n\t\t\t\topen={showDeleteDialog}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetShowDeleteDialog(false);\n\t\t\t\t\tsetDeleteError(null);\n\t\t\t\t}}\n\t\t\t\ttitle=\"Remove passkey?\"\n\t\t\t\tdescription={`You won't be able to use \"${passkey.name || \"this passkey\"}\" to sign in anymore. This action cannot be undone.`}\n\t\t\t\tconfirmLabel=\"Remove\"\n\t\t\t\tpendingLabel=\"Removing...\"\n\t\t\t\tisPending={!!isDeleting}\n\t\t\t\terror={deleteError}\n\t\t\t\tonConfirm={handleDelete}\n\t\t\t/>\n\t\t</li>\n\t);\n}\n","/**\n * PasskeyList - Displays a list of passkeys with actions\n */\n\nimport * as React from \"react\";\n\nimport type { PasskeyInfo } from \"../../lib/api\";\nimport { PasskeyItem } from \"./PasskeyItem\";\n\nexport interface PasskeyListProps {\n\tpasskeys: PasskeyInfo[];\n\tonRename: (id: string, name: string) => Promise<void>;\n\tonDelete: (id: string) => Promise<void>;\n\tisDeleting?: boolean;\n\tisRenaming?: boolean;\n}\n\nexport function PasskeyList({\n\tpasskeys,\n\tonRename,\n\tonDelete,\n\tisDeleting,\n\tisRenaming,\n}: PasskeyListProps) {\n\treturn (\n\t\t<ul className=\"space-y-3\">\n\t\t\t{passkeys.map((passkey) => (\n\t\t\t\t<PasskeyItem\n\t\t\t\t\tkey={passkey.id}\n\t\t\t\t\tpasskey={passkey}\n\t\t\t\t\tcanDelete={passkeys.length > 1}\n\t\t\t\t\tonRename={onRename}\n\t\t\t\t\tonDelete={onDelete}\n\t\t\t\t\tisDeleting={isDeleting}\n\t\t\t\t\tisRenaming={isRenaming}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</ul>\n\t);\n}\n","/**\n * Security Settings page - Passkey management\n *\n * Only available when using passkey auth. When external auth (e.g., Cloudflare Access)\n * is configured, this page shows an informational message instead.\n */\n\nimport { Button } from \"@cloudflare/kumo\";\nimport { Shield, Plus, CheckCircle, WarningCircle, ArrowLeft, Info } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchPasskeys, renamePasskey, deletePasskey, fetchManifest } from \"../../lib/api\";\nimport { PasskeyRegistration } from \"../auth/PasskeyRegistration\";\nimport { PasskeyList } from \"./PasskeyList\";\n\nexport function SecuritySettings() {\n\tconst queryClient = useQueryClient();\n\tconst [isAdding, setIsAdding] = React.useState(false);\n\tconst [saveStatus, setSaveStatus] = React.useState<{\n\t\ttype: \"success\" | \"error\";\n\t\tmessage: string;\n\t} | null>(null);\n\n\t// Fetch manifest for auth mode\n\tconst { data: manifest, isLoading: manifestLoading } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tconst isExternalAuth = manifest?.authMode && manifest.authMode !== \"passkey\";\n\n\t// Fetch passkeys (only when using passkey auth)\n\tconst {\n\t\tdata: passkeys,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"passkeys\"],\n\t\tqueryFn: fetchPasskeys,\n\t\tenabled: !isExternalAuth && !manifestLoading,\n\t});\n\n\t// Clear status message after 3 seconds\n\tReact.useEffect(() => {\n\t\tif (saveStatus) {\n\t\t\tconst timer = setTimeout(setSaveStatus, 3000, null);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [saveStatus]);\n\n\t// Rename mutation\n\tconst renameMutation = useMutation({\n\t\tmutationFn: ({ id, name }: { id: string; name: string }) => renamePasskey(id, name),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"passkeys\"] });\n\t\t\tsetSaveStatus({ type: \"success\", message: \"Passkey renamed\" });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage:\n\t\t\t\t\tmutationError instanceof Error ? mutationError.message : \"Failed to rename passkey\",\n\t\t\t});\n\t\t},\n\t});\n\n\t// Delete mutation\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (id: string) => deletePasskey(id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"passkeys\"] });\n\t\t\tsetSaveStatus({ type: \"success\", message: \"Passkey removed\" });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage:\n\t\t\t\t\tmutationError instanceof Error ? mutationError.message : \"Failed to remove passkey\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleRename = async (id: string, name: string) => {\n\t\tawait renameMutation.mutateAsync({ id, name });\n\t};\n\n\tconst handleDelete = async (id: string) => {\n\t\tawait deleteMutation.mutateAsync(id);\n\t};\n\n\tconst handleAddSuccess = () => {\n\t\tvoid queryClient.invalidateQueries({ queryKey: [\"passkeys\"] });\n\t\tsetIsAdding(false);\n\t\tsetSaveStatus({ type: \"success\", message: \"Passkey added successfully\" });\n\t};\n\n\tif (manifestLoading || isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Security Settings</h1>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<p className=\"text-kumo-subtle\">Loading...</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// Show message when external auth is configured\n\tif (isExternalAuth) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Security Settings</h1>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<div className=\"flex items-start gap-3\">\n\t\t\t\t\t\t<Info className=\"h-5 w-5 text-kumo-subtle mt-0.5 flex-shrink-0\" />\n\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t<p className=\"text-kumo-subtle\">\n\t\t\t\t\t\t\t\tAuthentication is managed by an external provider ({manifest?.authMode}). Passkey\n\t\t\t\t\t\t\t\tsettings are not available when using external authentication.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" icon={<ArrowLeft />}>\n\t\t\t\t\t\t\t\t\tBack to Settings\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Security Settings</h1>\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-6\">\n\t\t\t\t\t<p className=\"text-kumo-danger\">\n\t\t\t\t\t\t{error instanceof Error ? error.message : \"Failed to load passkeys\"}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<h1 className=\"text-2xl font-bold\">Security Settings</h1>\n\n\t\t\t{/* Status message */}\n\t\t\t{saveStatus && (\n\t\t\t\t<div\n\t\t\t\t\tclassName={`rounded-lg border p-4 flex items-center gap-2 ${\n\t\t\t\t\t\tsaveStatus.type === \"success\"\n\t\t\t\t\t\t\t? \"bg-green-50 border-green-200 text-green-800 dark:bg-green-900/20 dark:border-green-800 dark:text-green-400\"\n\t\t\t\t\t\t\t: \"bg-red-50 border-red-200 text-red-800 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400\"\n\t\t\t\t\t}`}\n\t\t\t\t>\n\t\t\t\t\t{saveStatus.type === \"success\" ? (\n\t\t\t\t\t\t<CheckCircle className=\"h-5 w-5\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<WarningCircle className=\"h-5 w-5\" />\n\t\t\t\t\t)}\n\t\t\t\t\t<span>{saveStatus.message}</span>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Passkeys Section */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-center gap-2 mb-4\">\n\t\t\t\t\t<Shield className=\"h-5 w-5 text-kumo-subtle\" />\n\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Passkeys</h2>\n\t\t\t\t</div>\n\n\t\t\t\t<p className=\"text-sm text-kumo-subtle mb-6\">\n\t\t\t\t\tPasskeys are a secure, passwordless way to sign in to your account. You can register\n\t\t\t\t\tmultiple passkeys for different devices.\n\t\t\t\t</p>\n\n\t\t\t\t{/* Passkey list */}\n\t\t\t\t{passkeys && passkeys.length > 0 ? (\n\t\t\t\t\t<PasskeyList\n\t\t\t\t\t\tpasskeys={passkeys}\n\t\t\t\t\t\tonRename={handleRename}\n\t\t\t\t\t\tonDelete={handleDelete}\n\t\t\t\t\t\tisDeleting={deleteMutation.isPending}\n\t\t\t\t\t\tisRenaming={renameMutation.isPending}\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"rounded-lg border border-dashed p-6 text-center text-kumo-subtle\">\n\t\t\t\t\t\tNo passkeys registered yet.\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Add passkey section */}\n\t\t\t\t<div className=\"mt-6 pt-6 border-t\">\n\t\t\t\t\t{isAdding ? (\n\t\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t\t<h3 className=\"font-medium\">Add a new passkey</h3>\n\t\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" onClick={() => setIsAdding(false)}>\n\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<PasskeyRegistration\n\t\t\t\t\t\t\t\toptionsEndpoint=\"/_emdash/api/auth/passkey/register/options\"\n\t\t\t\t\t\t\t\tverifyEndpoint=\"/_emdash/api/auth/passkey/register/verify\"\n\t\t\t\t\t\t\t\tonSuccess={handleAddSuccess}\n\t\t\t\t\t\t\t\tonError={(registrationError) =>\n\t\t\t\t\t\t\t\t\tsetSaveStatus({\n\t\t\t\t\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\t\t\t\t\tmessage: registrationError.message,\n\t\t\t\t\t\t\t\t\t})\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tshowNameInput\n\t\t\t\t\t\t\t\tbuttonText=\"Register Passkey\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Button onClick={() => setIsAdding(true)} icon={<Plus />}>\n\t\t\t\t\t\t\tAdd Passkey\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default SecuritySettings;\n","/**\n * SEO Settings sub-page\n *\n * Title separator, search engine verification codes, and robots.txt.\n */\n\nimport { Button, Input, InputArea } from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tFloppyDisk,\n\tCheckCircle,\n\tWarningCircle,\n\tMagnifyingGlass,\n} from \"@phosphor-icons/react\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchSettings, updateSettings, type SiteSettings } from \"../../lib/api\";\n\nexport function SeoSettings() {\n\tconst queryClient = useQueryClient();\n\n\tconst { data: settings, isLoading } = useQuery({\n\t\tqueryKey: [\"settings\"],\n\t\tqueryFn: fetchSettings,\n\t\tstaleTime: Infinity,\n\t});\n\n\tconst [formData, setFormData] = React.useState<Partial<SiteSettings>>({});\n\tconst [saveStatus, setSaveStatus] = React.useState<{\n\t\ttype: \"success\" | \"error\";\n\t\tmessage: string;\n\t} | null>(null);\n\n\tReact.useEffect(() => {\n\t\tif (settings) setFormData(settings);\n\t}, [settings]);\n\n\tReact.useEffect(() => {\n\t\tif (saveStatus) {\n\t\t\tconst timer = setTimeout(setSaveStatus, 3000, null);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [saveStatus]);\n\n\tconst saveMutation = useMutation({\n\t\tmutationFn: (data: Partial<SiteSettings>) => updateSettings(data),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"settings\"] });\n\t\t\tsetSaveStatus({ type: \"success\", message: \"SEO settings saved\" });\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: error instanceof Error ? error.message : \"Failed to save settings\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsaveMutation.mutate(formData);\n\t};\n\n\tconst handleSeoChange = (key: string, value: unknown) => {\n\t\tsetFormData((prev) => ({\n\t\t\t...prev,\n\t\t\tseo: {\n\t\t\t\t...prev.seo,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t}));\n\t};\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to settings\">\n\t\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</Link>\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">SEO Settings</h1>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<p className=\"text-kumo-subtle\">Loading settings...</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to settings\">\n\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</Link>\n\t\t\t\t<h1 className=\"text-2xl font-bold\">SEO Settings</h1>\n\t\t\t</div>\n\n\t\t\t{/* Status banner */}\n\t\t\t{saveStatus && (\n\t\t\t\t<div\n\t\t\t\t\tclassName={`flex items-center gap-2 rounded-lg border p-3 text-sm ${\n\t\t\t\t\t\tsaveStatus.type === \"success\"\n\t\t\t\t\t\t\t? \"border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950/30 dark:text-green-200\"\n\t\t\t\t\t\t\t: \"border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950/30 dark:text-red-200\"\n\t\t\t\t\t}`}\n\t\t\t\t>\n\t\t\t\t\t{saveStatus.type === \"success\" ? (\n\t\t\t\t\t\t<CheckCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<WarningCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t)}\n\t\t\t\t\t{saveStatus.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<form onSubmit={handleSubmit} className=\"space-y-6\">\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<div className=\"flex items-center gap-2 mb-4\">\n\t\t\t\t\t\t<MagnifyingGlass className=\"h-5 w-5 text-kumo-subtle\" />\n\t\t\t\t\t\t<h2 className=\"text-lg font-semibold\">Search Engine Optimization</h2>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Title Separator\"\n\t\t\t\t\t\t\tvalue={formData.seo?.titleSeparator || \"|\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSeoChange(\"titleSeparator\", e.target.value)}\n\t\t\t\t\t\t\tdescription='Character between page title and site name (e.g., \"My Post | My Site\")'\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Google Verification\"\n\t\t\t\t\t\t\tvalue={formData.seo?.googleVerification || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSeoChange(\"googleVerification\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Meta tag content for Google Search Console verification\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Bing Verification\"\n\t\t\t\t\t\t\tvalue={formData.seo?.bingVerification || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSeoChange(\"bingVerification\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Meta tag content for Bing Webmaster Tools verification\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<InputArea\n\t\t\t\t\t\t\tlabel=\"robots.txt\"\n\t\t\t\t\t\t\tvalue={formData.seo?.robotsTxt || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSeoChange(\"robotsTxt\", e.target.value)}\n\t\t\t\t\t\t\trows={5}\n\t\t\t\t\t\t\tdescription=\"Custom robots.txt content. Leave empty to use the default.\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Save Button */}\n\t\t\t\t<div className=\"flex justify-end\">\n\t\t\t\t\t<Button type=\"submit\" disabled={saveMutation.isPending} icon={<FloppyDisk />}>\n\t\t\t\t\t\t{saveMutation.isPending ? \"Saving...\" : \"Save SEO Settings\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nexport default SeoSettings;\n","/**\n * Social Settings sub-page\n *\n * Social media profile links (Twitter, GitHub, Facebook, Instagram, LinkedIn, YouTube).\n */\n\nimport { Button, Input } from \"@cloudflare/kumo\";\nimport { ArrowLeft, FloppyDisk, CheckCircle, WarningCircle } from \"@phosphor-icons/react\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchSettings, updateSettings, type SiteSettings } from \"../../lib/api\";\n\nexport function SocialSettings() {\n\tconst queryClient = useQueryClient();\n\n\tconst { data: settings, isLoading } = useQuery({\n\t\tqueryKey: [\"settings\"],\n\t\tqueryFn: fetchSettings,\n\t\tstaleTime: Infinity,\n\t});\n\n\tconst [formData, setFormData] = React.useState<Partial<SiteSettings>>({});\n\tconst [saveStatus, setSaveStatus] = React.useState<{\n\t\ttype: \"success\" | \"error\";\n\t\tmessage: string;\n\t} | null>(null);\n\n\tReact.useEffect(() => {\n\t\tif (settings) setFormData(settings);\n\t}, [settings]);\n\n\tReact.useEffect(() => {\n\t\tif (saveStatus) {\n\t\t\tconst timer = setTimeout(setSaveStatus, 3000, null);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [saveStatus]);\n\n\tconst saveMutation = useMutation({\n\t\tmutationFn: (data: Partial<SiteSettings>) => updateSettings(data),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"settings\"] });\n\t\t\tsetSaveStatus({ type: \"success\", message: \"Social links saved\" });\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetSaveStatus({\n\t\t\t\ttype: \"error\",\n\t\t\t\tmessage: error instanceof Error ? error.message : \"Failed to save settings\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsaveMutation.mutate(formData);\n\t};\n\n\tconst handleSocialChange = (key: string, value: string) => {\n\t\tsetFormData((prev) => ({\n\t\t\t...prev,\n\t\t\tsocial: {\n\t\t\t\t...prev.social,\n\t\t\t\t[key]: value,\n\t\t\t},\n\t\t}));\n\t};\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to settings\">\n\t\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</Link>\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">Social Links</h1>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<p className=\"text-kumo-subtle\">Loading settings...</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t<Link to=\"/settings\">\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" aria-label=\"Back to settings\">\n\t\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</Link>\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Social Links</h1>\n\t\t\t</div>\n\n\t\t\t{/* Status banner */}\n\t\t\t{saveStatus && (\n\t\t\t\t<div\n\t\t\t\t\tclassName={`flex items-center gap-2 rounded-lg border p-3 text-sm ${\n\t\t\t\t\t\tsaveStatus.type === \"success\"\n\t\t\t\t\t\t\t? \"border-green-200 bg-green-50 text-green-800 dark:border-green-800 dark:bg-green-950/30 dark:text-green-200\"\n\t\t\t\t\t\t\t: \"border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950/30 dark:text-red-200\"\n\t\t\t\t\t}`}\n\t\t\t\t>\n\t\t\t\t\t{saveStatus.type === \"success\" ? (\n\t\t\t\t\t\t<CheckCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<WarningCircle className=\"h-4 w-4 flex-shrink-0\" />\n\t\t\t\t\t)}\n\t\t\t\t\t{saveStatus.message}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<form onSubmit={handleSubmit} className=\"space-y-6\">\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<h2 className=\"mb-4 text-lg font-semibold\">Social Profiles</h2>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mb-6\">\n\t\t\t\t\t\tAdd your social media profiles. These are available to your site's theme and can be\n\t\t\t\t\t\tdisplayed in headers, footers, or author bios.\n\t\t\t\t\t</p>\n\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Twitter\"\n\t\t\t\t\t\t\tvalue={formData.social?.twitter || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSocialChange(\"twitter\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Your Twitter/X handle (e.g., @username)\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"GitHub\"\n\t\t\t\t\t\t\tvalue={formData.social?.github || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSocialChange(\"github\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Your GitHub username\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Facebook\"\n\t\t\t\t\t\t\tvalue={formData.social?.facebook || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSocialChange(\"facebook\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Your Facebook page or profile username\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Instagram\"\n\t\t\t\t\t\t\tvalue={formData.social?.instagram || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSocialChange(\"instagram\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Your Instagram username\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"LinkedIn\"\n\t\t\t\t\t\t\tvalue={formData.social?.linkedin || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSocialChange(\"linkedin\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Your LinkedIn profile username\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"YouTube\"\n\t\t\t\t\t\t\tvalue={formData.social?.youtube || \"\"}\n\t\t\t\t\t\t\tonChange={(e) => handleSocialChange(\"youtube\", e.target.value)}\n\t\t\t\t\t\t\tdescription=\"Your YouTube channel ID or handle\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Save Button */}\n\t\t\t\t<div className=\"flex justify-end\">\n\t\t\t\t\t<Button type=\"submit\" disabled={saveMutation.isPending} icon={<FloppyDisk />}>\n\t\t\t\t\t\t{saveMutation.isPending ? \"Saving...\" : \"Save Social Links\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</form>\n\t\t</div>\n\t);\n}\n\nexport default SocialSettings;\n","/**\n * Setup Wizard - Multi-step first-run setup page\n *\n * This component is NOT wrapped in the admin Shell.\n * It's a standalone page for initial site configuration.\n *\n * Steps:\n * 1. Site Configuration (title, tagline, sample content)\n * 2. Admin Account (email, name)\n * 3. Passkey Registration\n */\n\nimport { Button, Checkbox, Input, Loader } from \"@cloudflare/kumo\";\nimport { useMutation, useQuery } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { apiFetch, parseApiResponse } from \"../lib/api/client\";\nimport { PasskeyRegistration } from \"./auth/PasskeyRegistration\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface SetupStatusResponse {\n\tneedsSetup: boolean;\n\tstep?: \"start\" | \"site\" | \"admin\" | \"complete\";\n\tseedInfo?: {\n\t\tname: string;\n\t\tdescription: string;\n\t\tcollections: number;\n\t\thasContent: boolean;\n\t};\n\t/** Auth mode - \"cloudflare-access\" or \"passkey\" */\n\tauthMode?: \"cloudflare-access\" | \"passkey\";\n}\n\ninterface SetupSiteRequest {\n\ttitle: string;\n\ttagline?: string;\n\tincludeContent: boolean;\n}\n\ninterface SetupSiteResponse {\n\tsuccess: boolean;\n\terror?: string;\n\t/** In Access mode, setup is complete after site config */\n\tsetupComplete?: boolean;\n\tresult?: {\n\t\tcollections: { created: number; skipped: number };\n\t\tfields: { created: number; skipped: number };\n\t\ttaxonomies: { created: number; terms: number };\n\t\tmenus: { created: number; items: number };\n\t\twidgetAreas: { created: number; widgets: number };\n\t\tsettings: { applied: number };\n\t\tcontent: { created: number; skipped: number };\n\t};\n}\n\ninterface SetupAdminRequest {\n\temail: string;\n\tname?: string;\n}\n\ninterface SetupAdminResponse {\n\tsuccess: boolean;\n\terror?: string;\n\toptions?: unknown; // WebAuthn registration options\n}\n\ntype WizardStep = \"site\" | \"admin\" | \"passkey\";\n\n// ============================================================================\n// API Functions\n// ============================================================================\n\nasync function fetchSetupStatus(): Promise<SetupStatusResponse> {\n\tconst response = await apiFetch(\"/_emdash/api/setup/status\");\n\treturn parseApiResponse<SetupStatusResponse>(response, \"Failed to fetch setup status\");\n}\n\nasync function executeSiteSetup(data: SetupSiteRequest): Promise<SetupSiteResponse> {\n\tconst response = await apiFetch(\"/_emdash/api/setup\", {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(data),\n\t});\n\n\treturn parseApiResponse<SetupSiteResponse>(response, \"Setup failed\");\n}\n\nasync function executeAdminSetup(data: SetupAdminRequest): Promise<SetupAdminResponse> {\n\tconst response = await apiFetch(\"/_emdash/api/setup/admin\", {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify(data),\n\t});\n\n\treturn parseApiResponse<SetupAdminResponse>(response, \"Failed to create admin\");\n}\n\n// ============================================================================\n// Step Components\n// ============================================================================\n\ninterface SiteStepProps {\n\tseedInfo?: SetupStatusResponse[\"seedInfo\"];\n\tonNext: (data: SetupSiteRequest) => void;\n\tisLoading: boolean;\n\terror?: string;\n}\n\nfunction SiteStep({ seedInfo, onNext, isLoading, error }: SiteStepProps) {\n\tconst [title, setTitle] = React.useState(\"\");\n\tconst [tagline, setTagline] = React.useState(\"\");\n\tconst [includeContent, setIncludeContent] = React.useState(true);\n\tconst [errors, setErrors] = React.useState<Record<string, string>>({});\n\n\tconst validate = (): boolean => {\n\t\tconst newErrors: Record<string, string> = {};\n\t\tif (!title.trim()) {\n\t\t\tnewErrors.title = \"Site title is required\";\n\t\t}\n\t\tsetErrors(newErrors);\n\t\treturn Object.keys(newErrors).length === 0;\n\t};\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (!validate()) return;\n\t\tonNext({ title, tagline, includeContent });\n\t};\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} className=\"space-y-6\">\n\t\t\t<div className=\"space-y-4\">\n\t\t\t\t<Input\n\t\t\t\t\tlabel=\"Site Title\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tvalue={title}\n\t\t\t\t\tonChange={(e) => setTitle(e.target.value)}\n\t\t\t\t\tplaceholder=\"My Awesome Blog\"\n\t\t\t\t\tclassName={errors.title ? \"border-kumo-danger\" : \"\"}\n\t\t\t\t\tdisabled={isLoading}\n\t\t\t\t/>\n\t\t\t\t{errors.title && <p className=\"text-sm text-kumo-danger mt-1\">{errors.title}</p>}\n\n\t\t\t\t<Input\n\t\t\t\t\tlabel=\"Tagline\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tvalue={tagline}\n\t\t\t\t\tonChange={(e) => setTagline(e.target.value)}\n\t\t\t\t\tplaceholder=\"Thoughts, tutorials, and more\"\n\t\t\t\t\tdisabled={isLoading}\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{seedInfo?.hasContent && (\n\t\t\t\t<Checkbox\n\t\t\t\t\tlabel=\"Include sample content (recommended for new sites)\"\n\t\t\t\t\tchecked={includeContent}\n\t\t\t\t\tonCheckedChange={(checked) => setIncludeContent(checked)}\n\t\t\t\t\tdisabled={isLoading}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{error && (\n\t\t\t\t<div className=\"rounded-lg bg-kumo-danger/10 p-4 text-sm text-kumo-danger\">{error}</div>\n\t\t\t)}\n\n\t\t\t<Button type=\"submit\" className=\"w-full justify-center\" loading={isLoading} variant=\"primary\">\n\t\t\t\t{isLoading ? <>Setting up...</> : \"Continue →\"}\n\t\t\t</Button>\n\n\t\t\t{seedInfo && (\n\t\t\t\t<p className=\"text-xs text-kumo-subtle text-center\">\n\t\t\t\t\tTemplate: {seedInfo.name} ({seedInfo.collections} collection\n\t\t\t\t\t{seedInfo.collections !== 1 ? \"s\" : \"\"})\n\t\t\t\t</p>\n\t\t\t)}\n\t\t</form>\n\t);\n}\n\ninterface AdminStepProps {\n\tonNext: (data: SetupAdminRequest) => void;\n\tonBack: () => void;\n\tisLoading: boolean;\n\terror?: string;\n}\n\nfunction AdminStep({ onNext, onBack, isLoading, error }: AdminStepProps) {\n\tconst [email, setEmail] = React.useState(\"\");\n\tconst [name, setName] = React.useState(\"\");\n\tconst [errors, setErrors] = React.useState<Record<string, string>>({});\n\n\tconst validate = (): boolean => {\n\t\tconst newErrors: Record<string, string> = {};\n\t\tif (!email.trim()) {\n\t\t\tnewErrors.email = \"Email is required\";\n\t\t} else if (!email.includes(\"@\")) {\n\t\t\tnewErrors.email = \"Please enter a valid email\";\n\t\t}\n\t\tsetErrors(newErrors);\n\t\treturn Object.keys(newErrors).length === 0;\n\t};\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (!validate()) return;\n\t\tonNext({ email, name: name || undefined });\n\t};\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} className=\"space-y-6\">\n\t\t\t<div className=\"space-y-4\">\n\t\t\t\t<Input\n\t\t\t\t\tlabel=\"Your Email\"\n\t\t\t\t\ttype=\"email\"\n\t\t\t\t\tvalue={email}\n\t\t\t\t\tonChange={(e) => setEmail(e.target.value)}\n\t\t\t\t\tplaceholder=\"you@example.com\"\n\t\t\t\t\tclassName={errors.email ? \"border-kumo-danger\" : \"\"}\n\t\t\t\t\tdisabled={isLoading}\n\t\t\t\t\tautoComplete=\"email\"\n\t\t\t\t/>\n\t\t\t\t{errors.email && <p className=\"text-sm text-kumo-danger mt-1\">{errors.email}</p>}\n\n\t\t\t\t<Input\n\t\t\t\t\tlabel=\"Your Name\"\n\t\t\t\t\ttype=\"text\"\n\t\t\t\t\tvalue={name}\n\t\t\t\t\tonChange={(e) => setName(e.target.value)}\n\t\t\t\t\tplaceholder=\"Jane Doe\"\n\t\t\t\t\tdisabled={isLoading}\n\t\t\t\t\tautoComplete=\"name\"\n\t\t\t\t/>\n\t\t\t</div>\n\n\t\t\t{error && (\n\t\t\t\t<div className=\"rounded-lg bg-kumo-danger/10 p-4 text-sm text-kumo-danger\">{error}</div>\n\t\t\t)}\n\n\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={onBack} disabled={isLoading}>\n\t\t\t\t\t← Back\n\t\t\t\t</Button>\n\t\t\t\t<Button\n\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\tclassName=\"flex-1 justify-center\"\n\t\t\t\t\tloading={isLoading}\n\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t>\n\t\t\t\t\t{isLoading ? <>Preparing...</> : \"Continue →\"}\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</form>\n\t);\n}\n\ninterface PasskeyStepProps {\n\tadminData: SetupAdminRequest;\n\tonBack: () => void;\n}\n\nfunction handlePasskeySuccess() {\n\t// Redirect to admin dashboard after successful registration\n\twindow.location.href = \"/_emdash/admin\";\n}\n\nfunction PasskeyStep({ adminData, onBack }: PasskeyStepProps) {\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<div className=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-kumo-brand/10 mb-4\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\tclassName=\"w-8 h-8 text-kumo-brand\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<path\n\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\td=\"M12 11c0 3.517-1.009 6.799-2.753 9.571m-3.44-2.04l.054-.09A13.916 13.916 0 008 11a4 4 0 118 0c0 1.017-.07 2.019-.203 3m-2.118 6.844A21.88 21.88 0 0015.171 17m3.839 1.132c.645-2.266.99-4.659.99-7.132A8 8 0 008 4.07M3 15.364c.64-1.319 1-2.8 1-4.364 0-1.457.39-2.823 1.07-4\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</svg>\n\t\t\t\t</div>\n\t\t\t\t<h3 className=\"text-lg font-medium\">Set up your passkey</h3>\n\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\tPasskeys are more secure than passwords. You'll use your device's biometrics, PIN, or\n\t\t\t\t\tsecurity key to sign in.\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<PasskeyRegistration\n\t\t\t\toptionsEndpoint=\"/_emdash/api/setup/admin\"\n\t\t\t\tverifyEndpoint=\"/_emdash/api/setup/admin/verify\"\n\t\t\t\tonSuccess={handlePasskeySuccess}\n\t\t\t\tbuttonText=\"Create Passkey\"\n\t\t\t\tadditionalData={{ ...adminData }}\n\t\t\t/>\n\n\t\t\t<Button type=\"button\" variant=\"ghost\" onClick={onBack} className=\"w-full\">\n\t\t\t\t← Back\n\t\t\t</Button>\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Progress Indicator\n// ============================================================================\n\ninterface StepIndicatorProps {\n\tcurrentStep: WizardStep;\n\tuseAccessAuth?: boolean;\n}\n\nfunction StepIndicator({ currentStep, useAccessAuth }: StepIndicatorProps) {\n\t// In Access mode, only show the site step\n\tconst steps = useAccessAuth\n\t\t? ([{ key: \"site\", label: \"Site Settings\" }] as const)\n\t\t: ([\n\t\t\t\t{ key: \"site\", label: \"Site\" },\n\t\t\t\t{ key: \"admin\", label: \"Account\" },\n\t\t\t\t{ key: \"passkey\", label: \"Passkey\" },\n\t\t\t] as const);\n\n\tconst currentIndex = steps.findIndex((s) => s.key === currentStep);\n\n\treturn (\n\t\t<div className=\"flex items-center justify-center mb-8\">\n\t\t\t{steps.map((step, index) => (\n\t\t\t\t<React.Fragment key={step.key}>\n\t\t\t\t\t<div className=\"flex items-center\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={`\n\t\t\t\t\t\t\t\tw-8 h-8 rounded-full flex items-center justify-center text-sm font-medium\n\t\t\t\t\t\t\t\t${\n\t\t\t\t\t\t\t\t\tindex < currentIndex\n\t\t\t\t\t\t\t\t\t\t? \"bg-kumo-brand text-white\"\n\t\t\t\t\t\t\t\t\t\t: index === currentIndex\n\t\t\t\t\t\t\t\t\t\t\t? \"bg-kumo-brand text-white\"\n\t\t\t\t\t\t\t\t\t\t\t: \"bg-kumo-tint text-kumo-subtle\"\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{index < currentIndex ? (\n\t\t\t\t\t\t\t\t<svg className=\"w-4 h-4\" fill=\"none\" stroke=\"currentColor\" viewBox=\"0 0 24 24\">\n\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\t\t\t\t\td=\"M5 13l4 4L19 7\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\tindex + 1\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<span\n\t\t\t\t\t\t\tclassName={`ml-2 text-sm ${\n\t\t\t\t\t\t\t\tindex <= currentIndex ? \"text-kumo-default\" : \"text-kumo-subtle\"\n\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{step.label}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t{index < steps.length - 1 && (\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName={`w-12 h-0.5 mx-2 ${index < currentIndex ? \"bg-kumo-brand\" : \"bg-kumo-tint\"}`}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</React.Fragment>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport function SetupWizard() {\n\tconst [currentStep, setCurrentStep] = React.useState<WizardStep>(\"site\");\n\tconst [_siteData, setSiteData] = React.useState<SetupSiteRequest | null>(null);\n\tconst [adminData, setAdminData] = React.useState<SetupAdminRequest | null>(null);\n\tconst [error, setError] = React.useState<string | undefined>();\n\n\t// Check setup status\n\tconst {\n\t\tdata: status,\n\t\tisLoading: statusLoading,\n\t\terror: statusError,\n\t} = useQuery({\n\t\tqueryKey: [\"setup\", \"status\"],\n\t\tqueryFn: fetchSetupStatus,\n\t\tretry: false,\n\t});\n\n\t// Check if using Cloudflare Access auth\n\tconst useAccessAuth = status?.authMode === \"cloudflare-access\";\n\n\t// Site setup mutation\n\tconst siteMutation = useMutation({\n\t\tmutationFn: executeSiteSetup,\n\t\tonSuccess: (data) => {\n\t\t\tsetError(undefined);\n\t\t\t// In Access mode, setup is complete - redirect to admin\n\t\t\tif (data.setupComplete) {\n\t\t\t\twindow.location.href = \"/_emdash/admin\";\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Otherwise continue to admin account creation\n\t\t\tsetCurrentStep(\"admin\");\n\t\t},\n\t\tonError: (err: Error) => {\n\t\t\tsetError(err.message);\n\t\t},\n\t});\n\n\t// Admin setup mutation\n\tconst adminMutation = useMutation({\n\t\tmutationFn: executeAdminSetup,\n\t\tonSuccess: () => {\n\t\t\tsetError(undefined);\n\t\t\tsetCurrentStep(\"passkey\");\n\t\t},\n\t\tonError: (err: Error) => {\n\t\t\tsetError(err.message);\n\t\t},\n\t});\n\n\t// Handle site step completion\n\tconst handleSiteNext = (data: SetupSiteRequest) => {\n\t\tsetSiteData(data);\n\t\tsiteMutation.mutate(data);\n\t};\n\n\t// Handle admin step completion\n\tconst handleAdminNext = (data: SetupAdminRequest) => {\n\t\tsetAdminData(data);\n\t\tadminMutation.mutate(data);\n\t};\n\n\t// Redirect if setup already complete\n\tif (!statusLoading && status && !status.needsSetup) {\n\t\twindow.location.href = \"/_emdash/admin\";\n\t\treturn null;\n\t}\n\n\t// Loading state\n\tif (statusLoading) {\n\t\treturn (\n\t\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base\">\n\t\t\t\t<div className=\"text-center\">\n\t\t\t\t\t<Loader />\n\t\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Loading setup...</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// Error state\n\tif (statusError) {\n\t\treturn (\n\t\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base\">\n\t\t\t\t<div className=\"text-center\">\n\t\t\t\t\t<h1 className=\"text-xl font-bold text-kumo-danger\">Error</h1>\n\t\t\t\t\t<p className=\"mt-2 text-kumo-subtle\">\n\t\t\t\t\t\t{statusError instanceof Error ? statusError.message : \"Failed to load setup\"}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base p-4\">\n\t\t\t<div className=\"w-full max-w-lg\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"text-center mb-6\">\n\t\t\t\t\t<div className=\"text-4xl font-bold mb-2\">💫 EmDash</div>\n\t\t\t\t\t<h1 className=\"text-2xl font-semibold text-kumo-default\">\n\t\t\t\t\t\t{currentStep === \"site\" && \"Set up your site\"}\n\t\t\t\t\t\t{currentStep === \"admin\" && \"Create your account\"}\n\t\t\t\t\t\t{currentStep === \"passkey\" && \"Secure your account\"}\n\t\t\t\t\t</h1>\n\t\t\t\t\t{useAccessAuth && currentStep === \"site\" && (\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-2\">You're signed in via Cloudflare Access</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Progress */}\n\t\t\t\t<StepIndicator currentStep={currentStep} useAccessAuth={useAccessAuth} />\n\n\t\t\t\t{/* Form Card */}\n\t\t\t\t<div className=\"bg-kumo-base border rounded-lg shadow-sm p-6\">\n\t\t\t\t\t{currentStep === \"site\" && (\n\t\t\t\t\t\t<SiteStep\n\t\t\t\t\t\t\tseedInfo={status?.seedInfo}\n\t\t\t\t\t\t\tonNext={handleSiteNext}\n\t\t\t\t\t\t\tisLoading={siteMutation.isPending}\n\t\t\t\t\t\t\terror={error}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{currentStep === \"admin\" && (\n\t\t\t\t\t\t<AdminStep\n\t\t\t\t\t\t\tonNext={handleAdminNext}\n\t\t\t\t\t\t\tonBack={() => {\n\t\t\t\t\t\t\t\tsetError(undefined);\n\t\t\t\t\t\t\t\tsetCurrentStep(\"site\");\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tisLoading={adminMutation.isPending}\n\t\t\t\t\t\t\terror={error}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{currentStep === \"passkey\" && adminData && (\n\t\t\t\t\t\t<PasskeyStep\n\t\t\t\t\t\t\tadminData={adminData}\n\t\t\t\t\t\t\tonBack={() => {\n\t\t\t\t\t\t\t\tsetError(undefined);\n\t\t\t\t\t\t\t\tsetCurrentStep(\"admin\");\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","/**\n * Admin Command Palette\n *\n * Quick navigation and search across the admin interface.\n * Opens with Cmd+K (Mac) or Ctrl+K (Windows/Linux).\n */\n\nimport { CommandPalette } from \"@cloudflare/kumo\";\nimport {\n\tSquaresFour,\n\tFileText,\n\tImage,\n\tGear,\n\tPuzzlePiece,\n\tUpload,\n\tDatabase,\n\tList,\n\tGridFour,\n\tUsers,\n\tStack,\n\tMagnifyingGlass,\n} from \"@phosphor-icons/react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { useNavigate } from \"@tanstack/react-router\";\nimport * as React from \"react\";\nimport { useHotkeys } from \"react-hotkeys-hook\";\n\nimport { apiFetch } from \"../lib/api/client\";\nimport { useCurrentUser } from \"../lib/api/current-user\";\n\n// Role levels (matching @emdash-cms/auth)\nconst ROLE_ADMIN = 50;\nconst ROLE_EDITOR = 40;\n\n// Regex for replacing route params like $collection with actual values\nconst ROUTE_PARAM_REGEX = /\\$(\\w+)/g;\n\n// Debounce delay for content search (ms)\nconst SEARCH_DEBOUNCE_MS = 300;\n\n// Detect macOS for keyboard shortcut display\nconst IS_MAC = typeof navigator !== \"undefined\" && /Mac|iPhone|iPad|iPod/.test(navigator.userAgent);\n\n/**\n * Custom hook for debouncing a value\n */\nfunction useDebouncedValue<T>(value: T, delay: number): T {\n\tconst [debouncedValue, setDebouncedValue] = React.useState(value);\n\n\tReact.useEffect(() => {\n\t\tconst timer = setTimeout(() => {\n\t\t\tsetDebouncedValue(value);\n\t\t}, delay);\n\n\t\treturn () => {\n\t\t\tclearTimeout(timer);\n\t\t};\n\t}, [value, delay]);\n\n\treturn debouncedValue;\n}\n\ninterface SearchResult {\n\tid: string;\n\tcollection: string;\n\ttitle: string;\n\tslug: string;\n\tstatus: string;\n}\n\ninterface SearchResponse {\n\titems: SearchResult[];\n\ttotal: number;\n}\n\ninterface NavItem {\n\tid: string;\n\ttitle: string;\n\tto: string;\n\tparams?: Record<string, string>;\n\ticon: React.ElementType;\n\tminRole?: number;\n\tkeywords?: string[];\n}\n\ninterface ResultGroup {\n\tlabel: string;\n\titems: ResultItem[];\n}\n\ninterface ResultItem {\n\tid: string;\n\ttitle: string;\n\tto: string;\n\tparams?: Record<string, string>;\n\ticon?: React.ReactNode;\n\tdescription?: string;\n\tcollection?: string;\n}\n\ninterface AdminCommandPaletteProps {\n\tmanifest: {\n\t\tcollections: Record<string, { label: string; labelSingular?: string }>;\n\t\tplugins: Record<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\tpackage?: string;\n\t\t\t\tenabled?: boolean;\n\t\t\t\tadminPages?: Array<{\n\t\t\t\t\tpath: string;\n\t\t\t\t\tlabel?: string;\n\t\t\t\t}>;\n\t\t\t}\n\t\t>;\n\t};\n}\n\nasync function searchContent(query: string): Promise<SearchResponse> {\n\tif (!query || query.length < 2) {\n\t\treturn { items: [], total: 0 };\n\t}\n\tconst response = await apiFetch(`/_emdash/api/search?q=${encodeURIComponent(query)}&limit=10`);\n\tif (!response.ok) {\n\t\treturn { items: [], total: 0 };\n\t}\n\tconst body = (await response.json()) as { data: SearchResponse };\n\treturn body.data;\n}\n\nfunction buildNavItems(\n\tmanifest: AdminCommandPaletteProps[\"manifest\"],\n\tuserRole: number,\n): NavItem[] {\n\tconst items: NavItem[] = [\n\t\t{\n\t\t\tid: \"dashboard\",\n\t\t\ttitle: \"Dashboard\",\n\t\t\tto: \"/\",\n\t\t\ticon: SquaresFour,\n\t\t\tkeywords: [\"home\", \"overview\"],\n\t\t},\n\t];\n\n\t// Add collection links\n\tfor (const [name, config] of Object.entries(manifest.collections)) {\n\t\titems.push({\n\t\t\tid: `collection-${name}`,\n\t\t\ttitle: config.label,\n\t\t\tto: \"/content/$collection\",\n\t\t\tparams: { collection: name },\n\t\t\ticon: FileText,\n\t\t\tkeywords: [\"content\", name],\n\t\t});\n\t}\n\n\t// Add core admin links\n\titems.push(\n\t\t{\n\t\t\tid: \"media\",\n\t\t\ttitle: \"Media Library\",\n\t\t\tto: \"/media\",\n\t\t\ticon: Image,\n\t\t\tkeywords: [\"images\", \"files\", \"uploads\"],\n\t\t},\n\t\t{\n\t\t\tid: \"menus\",\n\t\t\ttitle: \"Menus\",\n\t\t\tto: \"/menus\",\n\t\t\ticon: List,\n\t\t\tminRole: ROLE_EDITOR,\n\t\t\tkeywords: [\"navigation\"],\n\t\t},\n\t\t{\n\t\t\tid: \"widgets\",\n\t\t\ttitle: \"Widgets\",\n\t\t\tto: \"/widgets\",\n\t\t\ticon: GridFour,\n\t\t\tminRole: ROLE_EDITOR,\n\t\t\tkeywords: [\"sidebar\", \"footer\"],\n\t\t},\n\t\t{\n\t\t\tid: \"sections\",\n\t\t\ttitle: \"Sections\",\n\t\t\tto: \"/sections\",\n\t\t\ticon: Stack,\n\t\t\tminRole: ROLE_EDITOR,\n\t\t\tkeywords: [\"page builder\", \"blocks\"],\n\t\t},\n\t\t{\n\t\t\tid: \"content-types\",\n\t\t\ttitle: \"Content Types\",\n\t\t\tto: \"/content-types\",\n\t\t\ticon: Database,\n\t\t\tminRole: ROLE_ADMIN,\n\t\t\tkeywords: [\"schema\", \"collections\"],\n\t\t},\n\t\t{\n\t\t\tid: \"categories\",\n\t\t\ttitle: \"Categories\",\n\t\t\tto: \"/taxonomies/$taxonomy\",\n\t\t\tparams: { taxonomy: \"category\" },\n\t\t\ticon: FileText,\n\t\t\tminRole: ROLE_EDITOR,\n\t\t\tkeywords: [\"taxonomy\"],\n\t\t},\n\t\t{\n\t\t\tid: \"tags\",\n\t\t\ttitle: \"Tags\",\n\t\t\tto: \"/taxonomies/$taxonomy\",\n\t\t\tparams: { taxonomy: \"tag\" },\n\t\t\ticon: FileText,\n\t\t\tminRole: ROLE_EDITOR,\n\t\t\tkeywords: [\"taxonomy\"],\n\t\t},\n\t\t{\n\t\t\tid: \"users\",\n\t\t\ttitle: \"Users\",\n\t\t\tto: \"/users\",\n\t\t\ticon: Users,\n\t\t\tminRole: ROLE_ADMIN,\n\t\t\tkeywords: [\"accounts\", \"team\"],\n\t\t},\n\t\t{\n\t\t\tid: \"plugins\",\n\t\t\ttitle: \"Plugins\",\n\t\t\tto: \"/plugins-manager\",\n\t\t\ticon: PuzzlePiece,\n\t\t\tminRole: ROLE_ADMIN,\n\t\t\tkeywords: [\"extensions\", \"add-ons\"],\n\t\t},\n\t\t{\n\t\t\tid: \"import\",\n\t\t\ttitle: \"Import\",\n\t\t\tto: \"/import/wordpress\",\n\t\t\ticon: Upload,\n\t\t\tminRole: ROLE_ADMIN,\n\t\t\tkeywords: [\"wordpress\", \"migrate\"],\n\t\t},\n\t\t{\n\t\t\tid: \"settings\",\n\t\t\ttitle: \"Settings\",\n\t\t\tto: \"/settings\",\n\t\t\ticon: Gear,\n\t\t\tminRole: ROLE_ADMIN,\n\t\t\tkeywords: [\"configuration\", \"preferences\"],\n\t\t},\n\t\t{\n\t\t\tid: \"security\",\n\t\t\ttitle: \"Security Settings\",\n\t\t\tto: \"/settings/security\",\n\t\t\ticon: Gear,\n\t\t\tminRole: ROLE_ADMIN,\n\t\t\tkeywords: [\"passkeys\", \"authentication\"],\n\t\t},\n\t);\n\n\t// Add plugin pages\n\tfor (const [pluginId, config] of Object.entries(manifest.plugins)) {\n\t\tif (config.enabled === false) continue;\n\t\tif (config.adminPages && config.adminPages.length > 0) {\n\t\t\tfor (const page of config.adminPages) {\n\t\t\t\tconst label =\n\t\t\t\t\tpage.label ||\n\t\t\t\t\tpluginId\n\t\t\t\t\t\t.split(\"-\")\n\t\t\t\t\t\t.map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n\t\t\t\t\t\t.join(\" \");\n\n\t\t\t\titems.push({\n\t\t\t\t\tid: `plugin-${pluginId}-${page.path}`,\n\t\t\t\t\ttitle: label,\n\t\t\t\t\tto: `/plugins/${pluginId}${page.path}`,\n\t\t\t\t\ticon: PuzzlePiece,\n\t\t\t\t\tkeywords: [\"plugin\", pluginId],\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t// Filter by role\n\treturn items.filter((item) => !item.minRole || userRole >= item.minRole);\n}\n\nfunction filterNavItems(items: NavItem[], query: string): NavItem[] {\n\tif (!query) return items;\n\tconst lowerQuery = query.toLowerCase();\n\treturn items.filter((item) => {\n\t\tconst titleMatch = item.title.toLowerCase().includes(lowerQuery);\n\t\tconst keywordMatch = item.keywords?.some((k) => k.toLowerCase().includes(lowerQuery));\n\t\treturn titleMatch || keywordMatch;\n\t});\n}\n\nexport function AdminCommandPalette({ manifest }: AdminCommandPaletteProps) {\n\tconst [open, setOpen] = React.useState(false);\n\tconst [query, setQuery] = React.useState(\"\");\n\tconst navigate = useNavigate();\n\n\t// Debounce the search query to avoid flickering on every keystroke\n\tconst debouncedQuery = useDebouncedValue(query, SEARCH_DEBOUNCE_MS);\n\n\tconst { data: user } = useCurrentUser();\n\n\tconst userRole = user?.role ?? 0;\n\n\t// Search content when debounced query is long enough\n\tconst { data: searchResults, isFetching: isSearching } = useQuery({\n\t\tqueryKey: [\"command-palette-search\", debouncedQuery],\n\t\tqueryFn: () => searchContent(debouncedQuery),\n\t\tenabled: debouncedQuery.length >= 2,\n\t\tstaleTime: 30 * 1000,\n\t});\n\n\t// Show loading while waiting for debounce or API response\n\tconst isWaitingForDebounce = query.length >= 2 && query !== debouncedQuery;\n\tconst isPendingSearch = isWaitingForDebounce || isSearching;\n\n\t// Build navigation items\n\tconst allNavItems = React.useMemo(() => buildNavItems(manifest, userRole), [manifest, userRole]);\n\n\t// Filter nav items based on query\n\tconst filteredNavItems = React.useMemo(\n\t\t() => filterNavItems(allNavItems, query),\n\t\t[allNavItems, query],\n\t);\n\n\t// Build result groups\n\tconst resultGroups = React.useMemo((): ResultGroup[] => {\n\t\tconst groups: ResultGroup[] = [];\n\n\t\t// Navigation group\n\t\tif (filteredNavItems.length > 0) {\n\t\t\tgroups.push({\n\t\t\t\tlabel: \"Navigation\",\n\t\t\t\titems: filteredNavItems.map((item) => ({\n\t\t\t\t\tid: item.id,\n\t\t\t\t\ttitle: item.title,\n\t\t\t\t\tto: item.to,\n\t\t\t\t\tparams: item.params,\n\t\t\t\t\ticon: <item.icon className=\"h-4 w-4\" />,\n\t\t\t\t})),\n\t\t\t});\n\t\t}\n\n\t\t// Content search results\n\t\tif (searchResults?.items && searchResults.items.length > 0) {\n\t\t\tconst contentItems = searchResults.items.map((result) => {\n\t\t\t\tconst collectionConfig = manifest.collections[result.collection];\n\t\t\t\treturn {\n\t\t\t\t\tid: `content-${result.id}`,\n\t\t\t\t\ttitle: result.title || result.slug,\n\t\t\t\t\tto: \"/content/$collection/$id\",\n\t\t\t\t\tparams: { collection: result.collection, id: result.id },\n\t\t\t\t\ticon: <FileText className=\"h-4 w-4\" />,\n\t\t\t\t\tdescription: collectionConfig?.label || result.collection,\n\t\t\t\t\tcollection: result.collection,\n\t\t\t\t};\n\t\t\t});\n\n\t\t\tgroups.push({\n\t\t\t\tlabel: \"Content\",\n\t\t\t\titems: contentItems,\n\t\t\t});\n\t\t}\n\n\t\treturn groups;\n\t}, [filteredNavItems, searchResults, manifest.collections]);\n\n\t// Keyboard shortcut to open (Cmd+K / Ctrl+K)\n\tuseHotkeys(\"mod+k\", (e) => {\n\t\te.preventDefault();\n\t\tsetOpen(true);\n\t});\n\n\t// Reset query when closing\n\tReact.useEffect(() => {\n\t\tif (!open) {\n\t\t\tsetQuery(\"\");\n\t\t}\n\t}, [open]);\n\n\tconst handleSelect = React.useCallback(\n\t\t(item: ResultItem, options: { newTab: boolean }) => {\n\t\t\tsetOpen(false);\n\t\t\tif (options.newTab) {\n\t\t\t\t// Build the full URL for new tab\n\t\t\t\tconst path = item.params\n\t\t\t\t\t? item.to.replace(ROUTE_PARAM_REGEX, (_, key) => item.params?.[key] ?? \"\")\n\t\t\t\t\t: item.to;\n\t\t\t\twindow.open(`/_emdash/admin${path}`, \"_blank\");\n\t\t\t} else {\n\t\t\t\t// Navigate within the app\n\t\t\t\tvoid navigate({\n\t\t\t\t\tto: item.to as \"/\",\n\t\t\t\t\tparams: item.params,\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\t[navigate],\n\t);\n\n\tconst handleItemClick = React.useCallback(\n\t\t(item: ResultItem, e: React.MouseEvent) => {\n\t\t\thandleSelect(item, { newTab: e.metaKey || e.ctrlKey });\n\t\t},\n\t\t[handleSelect],\n\t);\n\n\treturn (\n\t\t<CommandPalette.Root\n\t\t\topen={open}\n\t\t\tonOpenChange={setOpen}\n\t\t\titems={resultGroups}\n\t\t\tvalue={query}\n\t\t\tonValueChange={setQuery}\n\t\t\titemToStringValue={(group) => group.label}\n\t\t\tonSelect={handleSelect}\n\t\t\tgetSelectableItems={(groups) => groups.flatMap((g) => g.items)}\n\t\t>\n\t\t\t<CommandPalette.Input\n\t\t\t\tplaceholder=\"Search pages and content...\"\n\t\t\t\tleading={<MagnifyingGlass className=\"h-4 w-4 text-kumo-subtle\" weight=\"bold\" />}\n\t\t\t/>\n\t\t\t<CommandPalette.List>\n\t\t\t\t{isPendingSearch ? (\n\t\t\t\t\t<CommandPalette.Loading />\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<CommandPalette.Results>\n\t\t\t\t\t\t\t{(group: ResultGroup) => (\n\t\t\t\t\t\t\t\t<CommandPalette.Group key={group.label} items={group.items}>\n\t\t\t\t\t\t\t\t\t<CommandPalette.GroupLabel>{group.label}</CommandPalette.GroupLabel>\n\t\t\t\t\t\t\t\t\t<CommandPalette.Items>\n\t\t\t\t\t\t\t\t\t\t{(item: ResultItem) => (\n\t\t\t\t\t\t\t\t\t\t\t<CommandPalette.ResultItem\n\t\t\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\t\t\tvalue={item}\n\t\t\t\t\t\t\t\t\t\t\t\ttitle={item.title}\n\t\t\t\t\t\t\t\t\t\t\t\tdescription={item.description}\n\t\t\t\t\t\t\t\t\t\t\t\ticon={item.icon}\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={(e: React.MouseEvent) => handleItemClick(item, e)}\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t</CommandPalette.Items>\n\t\t\t\t\t\t\t\t</CommandPalette.Group>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</CommandPalette.Results>\n\t\t\t\t\t\t<CommandPalette.Empty>No results found</CommandPalette.Empty>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</CommandPalette.List>\n\t\t\t<CommandPalette.Footer>\n\t\t\t\t<div className=\"flex items-center gap-4 text-kumo-subtle\">\n\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t<kbd className=\"rounded bg-kumo-control px-1.5 py-0.5 text-xs\">Enter</kbd>\n\t\t\t\t\t\t<span>to select</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t<kbd className=\"rounded bg-kumo-control px-1.5 py-0.5 text-xs\">\n\t\t\t\t\t\t\t{IS_MAC ? \"Cmd\" : \"Ctrl\"}+Enter\n\t\t\t\t\t\t</kbd>\n\t\t\t\t\t\t<span>new tab</span>\n\t\t\t\t\t</span>\n\t\t\t\t\t<span className=\"flex items-center gap-1\">\n\t\t\t\t\t\t<kbd className=\"rounded bg-kumo-control px-1.5 py-0.5 text-xs\">Esc</kbd>\n\t\t\t\t\t\t<span>to close</span>\n\t\t\t\t\t</span>\n\t\t\t\t</div>\n\t\t\t</CommandPalette.Footer>\n\t\t</CommandPalette.Root>\n\t);\n}\n","import { Sidebar as KumoSidebar, Tooltip, useSidebar } from \"@cloudflare/kumo\";\nimport {\n\tSquaresFour,\n\tFileText,\n\tImage,\n\tChatCircle,\n\tGear,\n\tPuzzlePiece,\n\tStorefront,\n\tPalette,\n\tUpload,\n\tDatabase,\n\tList,\n\tGridFour,\n\tUsers,\n\tStack,\n\tArrowsLeftRight,\n} from \"@phosphor-icons/react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport { Link, useLocation } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchCommentCounts } from \"../lib/api/comments\";\nimport { useCurrentUser } from \"../lib/api/current-user\";\nimport { usePluginAdmins } from \"../lib/plugin-context\";\nimport { cn } from \"../lib/utils\";\n\n// Re-export for Shell.tsx and Header.tsx\nexport { KumoSidebar as Sidebar, useSidebar };\n\n// Role levels (matching @emdash-cms/auth)\nconst ROLE_ADMIN = 50;\nconst ROLE_EDITOR = 40;\n\nexport interface SidebarNavProps {\n\tmanifest: {\n\t\tcollections: Record<string, { label: string }>;\n\t\tplugins: Record<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\tpackage?: string;\n\t\t\t\tenabled?: boolean;\n\t\t\t\tadminMode?: \"react\" | \"blocks\" | \"none\";\n\t\t\t\tadminPages?: Array<{\n\t\t\t\t\tpath: string;\n\t\t\t\t\tlabel?: string;\n\t\t\t\t\ticon?: string;\n\t\t\t\t}>;\n\t\t\t\tdashboardWidgets?: Array<{ id: string; title?: string }>;\n\t\t\t\tversion?: string;\n\t\t\t}\n\t\t>;\n\t\tversion?: string;\n\t\tmarketplace?: string;\n\t};\n}\n\ninterface NavItem {\n\tto: string;\n\tlabel: string;\n\ticon: React.ElementType;\n\tparams?: Record<string, string>;\n\t/** Minimum role level required to see this item */\n\tminRole?: number;\n\t/** Optional badge count (e.g., pending comments) */\n\tbadge?: number;\n}\n\n/**\n * Navigation item rendered as a TanStack Router <Link> inside kumo's\n * Sidebar.MenuItem. Styled to match kumo MenuButton appearance.\n * This approach guarantees client-side navigation works correctly.\n */\nfunction NavMenuLink({ item, isActive }: { item: NavItem; isActive: boolean }) {\n\tconst { state } = useSidebar();\n\tconst Icon = item.icon;\n\n\tconst link = (\n\t\t<Link\n\t\t\t// eslint-disable-next-line typescript-eslint(no-unsafe-type-assertion) -- TanStack Router requires literal route types\n\t\t\tto={item.to as \"/\"}\n\t\t\tparams={item.params}\n\t\t\taria-current={isActive ? \"page\" : undefined}\n\t\t\tdata-active={isActive || undefined}\n\t\t\tdata-sidebar=\"menu-button\"\n\t\t\tclassName={cn(\n\t\t\t\t\"emdash-nav-link group/menu-button flex w-full min-w-0 items-center gap-2.5 rounded-md no-underline outline-none cursor-pointer\",\n\t\t\t\t\"min-h-[36px] px-3 py-1.5 text-[13px]\",\n\t\t\t\t\"transition-all duration-200 ease-out\",\n\t\t\t\tisActive ? \"bg-kumo-brand text-white\" : \"text-white/70 hover:text-white hover:bg-white/8\",\n\t\t\t\t\"focus-visible:ring-2 focus-visible:ring-kumo-brand/50\",\n\t\t\t)}\n\t\t>\n\t\t\t<Icon\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"emdash-nav-icon size-[18px] shrink-0 transition-colors duration-200\",\n\t\t\t\t\tisActive ? \"text-white\" : \"text-white/60 group-hover/menu-button:text-white/90\",\n\t\t\t\t)}\n\t\t\t\taria-hidden=\"true\"\n\t\t\t/>\n\t\t\t<span className=\"emdash-nav-label flex flex-1 items-center min-w-0 text-left overflow-hidden\">\n\t\t\t\t{item.label}\n\t\t\t\t{item.badge != null && item.badge > 0 && (\n\t\t\t\t\t<KumoSidebar.MenuBadge>{item.badge}</KumoSidebar.MenuBadge>\n\t\t\t\t)}\n\t\t\t</span>\n\t\t</Link>\n\t);\n\n\treturn (\n\t\t<KumoSidebar.MenuItem>\n\t\t\t{state === \"collapsed\" ? (\n\t\t\t\t<Tooltip content={item.label} side=\"right\" asChild>\n\t\t\t\t\t{link}\n\t\t\t\t</Tooltip>\n\t\t\t) : (\n\t\t\t\tlink\n\t\t\t)}\n\t\t</KumoSidebar.MenuItem>\n\t);\n}\n\n/** Resolves a nav item's route path by substituting $param placeholders. */\nfunction resolveItemPath(item: NavItem): string {\n\tlet path = item.to;\n\tif (item.params) {\n\t\tfor (const [key, value] of Object.entries(item.params)) {\n\t\t\tpath = path.replace(`$${key}`, value);\n\t\t}\n\t}\n\treturn path;\n}\n\n/** Checks if a nav item is active based on the current router path. */\nfunction isItemActive(itemPath: string, currentPath: string): boolean {\n\treturn itemPath === \"/\"\n\t\t? currentPath === \"/\"\n\t\t: currentPath === itemPath || currentPath.startsWith(`${itemPath}/`);\n}\n\n/**\n * Admin sidebar navigation using kumo's Sidebar compound component.\n */\nexport function SidebarNav({ manifest }: SidebarNavProps) {\n\tconst location = useLocation();\n\tconst currentPath = location.pathname;\n\tconst pluginAdmins = usePluginAdmins();\n\n\tconst { data: user } = useCurrentUser();\n\tconst userRole = user?.role ?? 0;\n\n\t// Fetch pending comment count for badge\n\tconst { data: commentCounts } = useQuery({\n\t\tqueryKey: [\"commentCounts\"],\n\t\tqueryFn: fetchCommentCounts,\n\t\tstaleTime: 60 * 1000,\n\t\tretry: false,\n\t\tenabled: userRole >= ROLE_EDITOR,\n\t});\n\n\t// --- Build nav item groups ---\n\n\tconst contentItems: NavItem[] = [{ to: \"/\", label: \"Dashboard\", icon: SquaresFour }];\n\tfor (const [name, config] of Object.entries(manifest.collections)) {\n\t\tcontentItems.push({\n\t\t\tto: \"/content/$collection\",\n\t\t\tlabel: config.label,\n\t\t\ticon: FileText,\n\t\t\tparams: { collection: name },\n\t\t});\n\t}\n\tcontentItems.push({ to: \"/media\", label: \"Media\", icon: Image });\n\n\tconst manageItems: NavItem[] = [\n\t\t{\n\t\t\tto: \"/comments\",\n\t\t\tlabel: \"Comments\",\n\t\t\ticon: ChatCircle,\n\t\t\tminRole: ROLE_EDITOR,\n\t\t\tbadge: commentCounts?.pending,\n\t\t},\n\t\t{ to: \"/menus\", label: \"Menus\", icon: List, minRole: ROLE_EDITOR },\n\t\t{ to: \"/redirects\", label: \"Redirects\", icon: ArrowsLeftRight, minRole: ROLE_ADMIN },\n\t\t{ to: \"/widgets\", label: \"Widgets\", icon: GridFour, minRole: ROLE_EDITOR },\n\t\t{ to: \"/sections\", label: \"Sections\", icon: Stack, minRole: ROLE_EDITOR },\n\t\t{\n\t\t\tto: \"/taxonomies/$taxonomy\",\n\t\t\tlabel: \"Categories\",\n\t\t\ticon: FileText,\n\t\t\tparams: { taxonomy: \"category\" },\n\t\t\tminRole: ROLE_EDITOR,\n\t\t},\n\t\t{\n\t\t\tto: \"/taxonomies/$taxonomy\",\n\t\t\tlabel: \"Tags\",\n\t\t\ticon: FileText,\n\t\t\tparams: { taxonomy: \"tag\" },\n\t\t\tminRole: ROLE_EDITOR,\n\t\t},\n\t\t{ to: \"/bylines\", label: \"Bylines\", icon: FileText, minRole: ROLE_EDITOR },\n\t];\n\n\tconst adminItems: NavItem[] = [\n\t\t{ to: \"/content-types\", label: \"Content Types\", icon: Database, minRole: ROLE_ADMIN },\n\t\t{ to: \"/users\", label: \"Users\", icon: Users, minRole: ROLE_ADMIN },\n\t\t{ to: \"/plugins-manager\", label: \"Plugins\", icon: PuzzlePiece, minRole: ROLE_ADMIN },\n\t];\n\n\tif (manifest.marketplace) {\n\t\tadminItems.push(\n\t\t\t{ to: \"/plugins/marketplace\", label: \"Marketplace\", icon: Storefront, minRole: ROLE_ADMIN },\n\t\t\t{ to: \"/themes/marketplace\", label: \"Themes\", icon: Palette, minRole: ROLE_ADMIN },\n\t\t);\n\t}\n\n\tadminItems.push(\n\t\t{ to: \"/import/wordpress\", label: \"Import\", icon: Upload, minRole: ROLE_ADMIN },\n\t\t{ to: \"/settings\", label: \"Settings\", icon: Gear, minRole: ROLE_ADMIN },\n\t);\n\n\tconst pluginItems: NavItem[] = [];\n\tfor (const [pluginId, config] of Object.entries(manifest.plugins)) {\n\t\tif (config.enabled === false) continue;\n\t\tif (config.adminPages && config.adminPages.length > 0) {\n\t\t\tconst pluginPages = pluginAdmins[pluginId]?.pages;\n\t\t\tconst isBlocksMode = config.adminMode === \"blocks\";\n\t\t\tfor (const page of config.adminPages) {\n\t\t\t\tif (!isBlocksMode && !pluginPages?.[page.path]) continue;\n\t\t\t\tconst label =\n\t\t\t\t\tpage.label ||\n\t\t\t\t\tpluginId\n\t\t\t\t\t\t.split(\"-\")\n\t\t\t\t\t\t.map((w) => w.charAt(0).toUpperCase() + w.slice(1))\n\t\t\t\t\t\t.join(\" \");\n\t\t\t\tpluginItems.push({ to: `/plugins/${pluginId}${page.path}`, label, icon: PuzzlePiece });\n\t\t\t}\n\t\t}\n\t}\n\n\tconst filterByRole = (items: NavItem[]) =>\n\t\titems.filter((item) => !item.minRole || userRole >= item.minRole);\n\n\tconst visibleContent = filterByRole(contentItems);\n\tconst visibleManage = filterByRole(manageItems);\n\tconst visibleAdmin = filterByRole(adminItems);\n\tconst visiblePlugins = filterByRole(pluginItems);\n\n\tfunction renderNavItems(items: NavItem[]) {\n\t\treturn items.map((item, index) => {\n\t\t\tconst itemPath = resolveItemPath(item);\n\t\t\tconst active = isItemActive(itemPath, currentPath);\n\t\t\treturn <NavMenuLink key={`${item.to}-${index}`} item={item} isActive={active} />;\n\t\t});\n\t}\n\n\treturn (\n\t\t<>\n\t\t\t{/* Injected styles — Tailwind 4 strips [data-sidebar] attribute selectors from CSS files.\n\t\t\t All sidebar-specific overrides go here to avoid conflicting with kumo's inline styles. */}\n\t\t\t<style\n\t\t\t\tdangerouslySetInnerHTML={{\n\t\t\t\t\t__html: `\n\t\t\t/* Classic dark chrome — override kumo tokens within the sidebar */\n\t\t\t.emdash-sidebar {\n\t\t\t\t--color-kumo-base: #1d2327;\n\t\t\t\t--color-kumo-tint: rgba(255,255,255,0.1);\n\t\t\t\t--color-kumo-line: rgba(255,255,255,0.08);\n\t\t\t\t--color-kumo-brand: #2271b1;\n\t\t\t\t--text-color-kumo-default: #fff;\n\t\t\t\t--text-color-kumo-subtle: rgba(255,255,255,0.7);\n\t\t\t\t--text-color-kumo-strong: #fff;\n\t\t\t\tbackground-color: #1d2327 !important;\n\t\t\t\tcolor: #fff !important;\n\t\t\t\tborder-color: rgba(255,255,255,0.08) !important;\n\t\t\t}\n\t\t\t/* Group labels — uppercase muted style */\n\t\t\t.emdash-sidebar [data-sidebar=\"group-label\"] {\n\t\t\t\tcolor: rgba(255,255,255,0.45) !important;\n\t\t\t\tfont-size: 11px !important;\n\t\t\t\ttext-transform: uppercase;\n\t\t\t\tletter-spacing: 0.06em;\n\t\t\t\tfont-weight: 600;\n\t\t\t\tpadding-left: 0.75rem;\n\t\t\t\tpadding-right: 0.75rem;\n\t\t\t}\n\t\t\t.emdash-sidebar [data-sidebar=\"group-label\"] svg {\n\t\t\t\tcolor: rgba(255,255,255,0.3);\n\t\t\t}\n\t\t\t.emdash-sidebar [data-sidebar=\"group-label\"]:hover svg {\n\t\t\t\tcolor: rgba(255,255,255,0.6);\n\t\t\t}\n\t\t\t/* Separators */\n\t\t\t.emdash-sidebar [data-sidebar=\"separator\"] {\n\t\t\t\tborder-color: rgba(255,255,255,0.06) !important;\n\t\t\t\tmargin: 0.5rem 0.75rem;\n\t\t\t}\n\t\t\t/* Header/footer borders */\n\t\t\t.emdash-sidebar [data-sidebar=\"header\"] {\n\t\t\t\tborder-bottom: 1px solid rgba(255,255,255,0.08);\n\t\t\t}\n\t\t\t.emdash-sidebar [data-sidebar=\"footer\"] {\n\t\t\t\tborder-top: 1px solid rgba(255,255,255,0.08);\n\t\t\t}\n\n\t\t\t/* Keep all nav icons visible when sidebar collapses to icon mode */\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] [data-sidebar=\"group-content\"] {\n\t\t\t\tgrid-template-rows: 1fr !important;\n\t\t\t}\n\t\t\t/* Collapsed separators — thin centered line */\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] [data-sidebar=\"separator\"] {\n\t\t\t\tmargin: 0.375rem 0.625rem;\n\t\t\t}\n\t\t\t/* Collapsed: tighten group spacing */\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] [data-sidebar=\"group\"] {\n\t\t\t\tgap: 0.125rem;\n\t\t\t}\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] [data-sidebar=\"menu\"] {\n\t\t\t\tgap: 0.125rem;\n\t\t\t}\n\n\t\t\t/* Collapsed: nav links — center icon, hide text */\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] .emdash-nav-link {\n\t\t\t\tjustify-content: center;\n\t\t\t\tpadding: 0.5rem 0;\n\t\t\t\tgap: 0;\n\t\t\t\tmin-height: 36px;\n\t\t\t}\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] .emdash-nav-label {\n\t\t\t\tdisplay: none !important;\n\t\t\t}\n\t\t\t/* Collapsed: brand link */\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] .emdash-brand-link {\n\t\t\t\tjustify-content: center;\n\t\t\t\tpadding-left: 0;\n\t\t\t\tpadding-right: 0;\n\t\t\t}\n\t\t\t.emdash-sidebar[data-state=\"collapsed\"] .emdash-brand-text {\n\t\t\t\tdisplay: none !important;\n\t\t\t}\n\t\t`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t\t<KumoSidebar className=\"emdash-sidebar\" aria-label=\"Admin navigation\">\n\t\t\t\t<KumoSidebar.Header>\n\t\t\t\t\t<Link\n\t\t\t\t\t\tto=\"/\"\n\t\t\t\t\t\tclassName=\"emdash-brand-link flex w-full min-w-0 items-center gap-2 px-3 py-1\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<span className=\"text-base shrink-0\" aria-hidden=\"true\">\n\t\t\t\t\t\t\t💫\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span className=\"emdash-brand-text font-semibold truncate\">EmDash</span>\n\t\t\t\t\t</Link>\n\t\t\t\t</KumoSidebar.Header>\n\n\t\t\t\t<KumoSidebar.Content>\n\t\t\t\t\t{/* Dashboard — standalone */}\n\t\t\t\t\t<KumoSidebar.Group>\n\t\t\t\t\t\t<KumoSidebar.Menu>\n\t\t\t\t\t\t\t<NavMenuLink\n\t\t\t\t\t\t\t\titem={{ to: \"/\", label: \"Dashboard\", icon: SquaresFour }}\n\t\t\t\t\t\t\t\tisActive={isItemActive(\"/\", currentPath)}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t</KumoSidebar.Menu>\n\t\t\t\t\t</KumoSidebar.Group>\n\n\t\t\t\t\t<KumoSidebar.Separator />\n\n\t\t\t\t\t{/* Content — collections + media (collapsible) */}\n\t\t\t\t\t{visibleContent.length > 1 && (\n\t\t\t\t\t\t<KumoSidebar.Group collapsible defaultOpen>\n\t\t\t\t\t\t\t<KumoSidebar.GroupLabel>Content</KumoSidebar.GroupLabel>\n\t\t\t\t\t\t\t<KumoSidebar.GroupContent>\n\t\t\t\t\t\t\t\t<KumoSidebar.Menu>\n\t\t\t\t\t\t\t\t\t{renderNavItems(visibleContent.filter((i) => i.to !== \"/\"))}\n\t\t\t\t\t\t\t\t</KumoSidebar.Menu>\n\t\t\t\t\t\t\t</KumoSidebar.GroupContent>\n\t\t\t\t\t\t</KumoSidebar.Group>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<KumoSidebar.Separator />\n\n\t\t\t\t\t{/* Manage — comments, menus, taxonomies, etc. (collapsible) */}\n\t\t\t\t\t{visibleManage.length > 0 && (\n\t\t\t\t\t\t<KumoSidebar.Group collapsible defaultOpen>\n\t\t\t\t\t\t\t<KumoSidebar.GroupLabel>Manage</KumoSidebar.GroupLabel>\n\t\t\t\t\t\t\t<KumoSidebar.GroupContent>\n\t\t\t\t\t\t\t\t<KumoSidebar.Menu>{renderNavItems(visibleManage)}</KumoSidebar.Menu>\n\t\t\t\t\t\t\t</KumoSidebar.GroupContent>\n\t\t\t\t\t\t</KumoSidebar.Group>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<KumoSidebar.Separator />\n\n\t\t\t\t\t{/* Admin — content types, users, plugins, import (collapsible) */}\n\t\t\t\t\t{visibleAdmin.length > 0 && (\n\t\t\t\t\t\t<KumoSidebar.Group collapsible defaultOpen>\n\t\t\t\t\t\t\t<KumoSidebar.GroupLabel>Admin</KumoSidebar.GroupLabel>\n\t\t\t\t\t\t\t<KumoSidebar.GroupContent>\n\t\t\t\t\t\t\t\t<KumoSidebar.Menu>{renderNavItems(visibleAdmin)}</KumoSidebar.Menu>\n\t\t\t\t\t\t\t</KumoSidebar.GroupContent>\n\t\t\t\t\t\t</KumoSidebar.Group>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{/* Plugin pages (collapsible) */}\n\t\t\t\t\t{visiblePlugins.length > 0 && (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<KumoSidebar.Separator />\n\t\t\t\t\t\t\t<KumoSidebar.Group collapsible defaultOpen>\n\t\t\t\t\t\t\t\t<KumoSidebar.GroupLabel>Plugins</KumoSidebar.GroupLabel>\n\t\t\t\t\t\t\t\t<KumoSidebar.GroupContent>\n\t\t\t\t\t\t\t\t\t<KumoSidebar.Menu>{renderNavItems(visiblePlugins)}</KumoSidebar.Menu>\n\t\t\t\t\t\t\t\t</KumoSidebar.GroupContent>\n\t\t\t\t\t\t\t</KumoSidebar.Group>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</KumoSidebar.Content>\n\n\t\t\t\t<KumoSidebar.Footer>\n\t\t\t\t\t<p className=\"emdash-nav-label px-3 py-2 text-[11px] text-white/30\">\n\t\t\t\t\t\tEmDash CMS v{manifest.version || \"0.0.0\"}\n\t\t\t\t\t</p>\n\t\t\t\t</KumoSidebar.Footer>\n\t\t\t</KumoSidebar>\n\t\t</>\n\t);\n}\n","import { Button } from \"@cloudflare/kumo\";\nimport { Sun, Moon, Monitor } from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport { useTheme } from \"./ThemeProvider\";\n\n/**\n * Theme toggle button that cycles through: system -> light -> dark\n */\nexport function ThemeToggle() {\n\tconst { theme, setTheme, resolvedTheme } = useTheme();\n\n\tconst cycleTheme = () => {\n\t\tconst order: [\"system\", \"light\", \"dark\"] = [\"system\", \"light\", \"dark\"];\n\t\tconst currentIndex = order.indexOf(theme);\n\t\tconst nextIndex = (currentIndex + 1) % order.length;\n\t\tsetTheme(order[nextIndex]!);\n\t};\n\n\tconst label =\n\t\ttheme === \"system\" ? `System (${resolvedTheme})` : theme === \"light\" ? \"Light\" : \"Dark\";\n\n\treturn (\n\t\t<Button\n\t\t\tvariant=\"ghost\"\n\t\t\tshape=\"square\"\n\t\t\taria-label={`Toggle theme (current: ${label})`}\n\t\t\tonClick={cycleTheme}\n\t\t\ttitle={`Theme: ${label}`}\n\t\t>\n\t\t\t{theme === \"system\" ? (\n\t\t\t\t<Monitor className=\"h-5 w-5\" />\n\t\t\t) : theme === \"light\" ? (\n\t\t\t\t<Sun className=\"h-5 w-5\" />\n\t\t\t) : (\n\t\t\t\t<Moon className=\"h-5 w-5\" />\n\t\t\t)}\n\t\t\t<span className=\"sr-only\">Toggle theme (current: {label})</span>\n\t\t</Button>\n\t);\n}\n","import { Button, LinkButton, Popover } from \"@cloudflare/kumo\";\nimport { SignOut, Shield, Gear, ArrowSquareOut } from \"@phosphor-icons/react\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { apiFetch } from \"../lib/api/client\";\nimport { useCurrentUser } from \"../lib/api/current-user\";\nimport { Sidebar } from \"./Sidebar\";\nimport { ThemeToggle } from \"./ThemeToggle\";\n\nexport type { CurrentUser } from \"../lib/api/current-user\";\n\nasync function handleLogout() {\n\tconst res = await apiFetch(\"/_emdash/api/auth/logout?redirect=/_emdash/admin/login\", {\n\t\tmethod: \"POST\",\n\t\tcredentials: \"same-origin\",\n\t});\n\tif (res.redirected) {\n\t\twindow.location.href = res.url;\n\t} else {\n\t\twindow.location.href = \"/_emdash/admin/login\";\n\t}\n}\n\n/**\n * Admin header with mobile menu toggle and user actions.\n * Uses useSidebar() hook from kumo Sidebar.Provider context.\n */\nexport function Header() {\n\tconst [userMenuOpen, setUserMenuOpen] = React.useState(false);\n\n\tconst { data: user } = useCurrentUser();\n\n\t// Get display name and initials\n\tconst displayName = user?.name || user?.email || \"User\";\n\tconst initialsSource = user?.name || user?.email || \"U\";\n\tconst initials = (initialsSource[0] ?? \"U\").toUpperCase();\n\n\treturn (\n\t\t<header className=\"sticky top-0 z-10 flex h-16 items-center justify-between border-b bg-kumo-base px-4\">\n\t\t\t{/* Sidebar toggle — collapses to icon mode on desktop, opens drawer on mobile */}\n\t\t\t<Sidebar.Trigger />\n\n\t\t\t{/* Right side actions */}\n\t\t\t<div className=\"flex items-center space-x-2\">\n\t\t\t\t{/* View site link */}\n\t\t\t\t<LinkButton variant=\"ghost\" size=\"sm\" href=\"/\" external>\n\t\t\t\t\t<ArrowSquareOut className=\"h-4 w-4 mr-1\" />\n\t\t\t\t\tView Site\n\t\t\t\t</LinkButton>\n\n\t\t\t\t{/* Theme toggle */}\n\t\t\t\t<ThemeToggle />\n\n\t\t\t\t{/* User menu */}\n\t\t\t\t<Popover open={userMenuOpen} onOpenChange={setUserMenuOpen}>\n\t\t\t\t\t<Popover.Trigger asChild>\n\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" className=\"gap-2\">\n\t\t\t\t\t\t\t{user?.avatarUrl ? (\n\t\t\t\t\t\t\t\t<img src={user.avatarUrl} alt=\"\" className=\"h-6 w-6 rounded-full object-cover\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<div className=\"h-6 w-6 rounded-full bg-kumo-brand/10 flex items-center justify-center text-xs font-medium\">\n\t\t\t\t\t\t\t\t\t{initials}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<span className=\"hidden sm:inline max-w-[120px] truncate\">{displayName}</span>\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</Popover.Trigger>\n\n\t\t\t\t\t<Popover.Content className=\"w-56 p-2\" align=\"end\">\n\t\t\t\t\t\t{/* User info */}\n\t\t\t\t\t\t<div className=\"px-3 py-2 border-b mb-1\">\n\t\t\t\t\t\t\t<div className=\"font-medium truncate\">{user?.name || \"User\"}</div>\n\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle truncate\">{user?.email}</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"grid gap-1\">\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tto=\"/settings/security\"\n\t\t\t\t\t\t\t\tonClick={() => setUserMenuOpen(false)}\n\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-md px-3 py-2 text-sm hover:bg-kumo-tint\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Shield className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\tSecurity Settings\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t<Link\n\t\t\t\t\t\t\t\tto=\"/settings\"\n\t\t\t\t\t\t\t\tonClick={() => setUserMenuOpen(false)}\n\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-md px-3 py-2 text-sm hover:bg-kumo-tint\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Gear className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\tSettings\n\t\t\t\t\t\t\t</Link>\n\t\t\t\t\t\t\t<hr className=\"my-1\" />\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tonClick={handleLogout}\n\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 rounded-md px-3 py-2 text-sm text-kumo-danger hover:bg-kumo-danger/10 w-full text-left\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<SignOut className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\tLog out\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Popover.Content>\n\t\t\t\t</Popover>\n\t\t\t</div>\n\t\t</header>\n\t);\n}\n","/**\n * Welcome Modal\n *\n * Shown to new users on their first login to welcome them to EmDash.\n */\n\nimport { Button, Dialog } from \"@cloudflare/kumo\";\nimport { Sparkle } from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { apiFetch, throwResponseError } from \"../lib/api/client\";\n\ninterface WelcomeModalProps {\n\topen: boolean;\n\tonClose: () => void;\n\tuserName?: string;\n\tuserRole: number;\n}\n\n// Role labels\nfunction getRoleLabel(role: number): string {\n\tif (role >= 50) return \"Administrator\";\n\tif (role >= 40) return \"Editor\";\n\tif (role >= 30) return \"Author\";\n\tif (role >= 20) return \"Contributor\";\n\treturn \"Subscriber\";\n}\n\nasync function dismissWelcome(): Promise<void> {\n\tconst response = await apiFetch(\"/_emdash/api/auth/me\", {\n\t\tmethod: \"POST\",\n\t\theaders: { \"Content-Type\": \"application/json\" },\n\t\tbody: JSON.stringify({ action: \"dismissWelcome\" }),\n\t});\n\tif (!response.ok) await throwResponseError(response, \"Failed to dismiss welcome\");\n}\n\nexport function WelcomeModal({ open, onClose, userName, userRole }: WelcomeModalProps) {\n\tconst queryClient = useQueryClient();\n\n\tconst dismissMutation = useMutation({\n\t\tmutationFn: dismissWelcome,\n\t\tonSuccess: () => {\n\t\t\t// Update the cached user data to reflect that they've seen the welcome\n\t\t\tqueryClient.setQueryData([\"currentUser\"], (old: unknown) => {\n\t\t\t\tif (old && typeof old === \"object\") {\n\t\t\t\t\treturn { ...old, isFirstLogin: false };\n\t\t\t\t}\n\t\t\t\treturn old;\n\t\t\t});\n\t\t\tonClose();\n\t\t},\n\t\tonError: () => {\n\t\t\t// Still close on error - don't block the user\n\t\t\tonClose();\n\t\t},\n\t});\n\n\tconst handleGetStarted = () => {\n\t\tdismissMutation.mutate();\n\t};\n\n\tconst roleLabel = getRoleLabel(userRole);\n\tconst isAdmin = userRole >= 50;\n\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={(isOpen: boolean) => !isOpen && handleGetStarted()}>\n\t\t\t<Dialog className=\"p-6 sm:max-w-md\" size=\"lg\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4\">\n\t\t\t\t\t<div className=\"flex-1\" />\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex flex-col space-y-1.5 text-center sm:text-center\">\n\t\t\t\t\t<div className=\"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-kumo-brand/10\">\n\t\t\t\t\t\t<Sparkle className=\"h-8 w-8 text-kumo-brand\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<Dialog.Title className=\"text-2xl font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\tWelcome to EmDash{userName ? `, ${userName.split(\" \")[0]}` : \"\"}!\n\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t<Dialog.Description className=\"text-base text-kumo-subtle\">\n\t\t\t\t\t\tYour account has been created successfully.\n\t\t\t\t\t</Dialog.Description>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"space-y-4 py-4\">\n\t\t\t\t\t<div className=\"rounded-lg bg-kumo-tint p-4\">\n\t\t\t\t\t\t<div className=\"text-sm font-medium\">Your Role</div>\n\t\t\t\t\t\t<div className=\"text-lg font-semibold text-kumo-brand\">{roleLabel}</div>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t{isAdmin\n\t\t\t\t\t\t\t\t? \"You have full access to manage this site, including users, settings, and all content.\"\n\t\t\t\t\t\t\t\t: userRole >= 40\n\t\t\t\t\t\t\t\t\t? \"You can manage content, media, menus, and taxonomies.\"\n\t\t\t\t\t\t\t\t\t: userRole >= 30\n\t\t\t\t\t\t\t\t\t\t? \"You can create and edit your own content.\"\n\t\t\t\t\t\t\t\t\t\t: \"You can view and contribute to the site.\"}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{isAdmin && (\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\tAs an administrator, you can invite other users from the{\" \"}\n\t\t\t\t\t\t\t<span className=\"font-medium\">Users</span> section.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"flex flex-col-reverse sm:flex-row sm:justify-center sm:space-x-2\">\n\t\t\t\t\t<Button onClick={handleGetStarted} disabled={dismissMutation.isPending} size=\"lg\">\n\t\t\t\t\t\t{dismissMutation.isPending ? \"Loading...\" : \"Get Started\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n","import * as React from \"react\";\n\nimport { useCurrentUser } from \"../lib/api/current-user\";\nimport { AdminCommandPalette } from \"./AdminCommandPalette\";\nimport { Header } from \"./Header\";\nimport { Sidebar, SidebarNav } from \"./Sidebar\";\nimport { WelcomeModal } from \"./WelcomeModal\";\n\nexport interface ShellProps {\n\tchildren: React.ReactNode;\n\tmanifest: {\n\t\tcollections: Record<string, { label: string }>;\n\t\tplugins: Record<\n\t\t\tstring,\n\t\t\t{\n\t\t\t\tpackage?: string;\n\t\t\t\tadminPages?: Array<{ path: string; label?: string; icon?: string }>;\n\t\t\t}\n\t\t>;\n\t\tversion?: string;\n\t};\n}\n\n/**\n * Admin shell layout with kumo Sidebar component.\n *\n * Sidebar.Provider wraps both the sidebar and main content area,\n * handling collapse state, mobile detection, and layout transitions.\n */\nexport function Shell({ children, manifest }: ShellProps) {\n\tconst [welcomeModalOpen, setWelcomeModalOpen] = React.useState(false);\n\n\tconst { data: user } = useCurrentUser();\n\n\t// Show welcome modal on first login\n\tReact.useEffect(() => {\n\t\tif (user?.isFirstLogin) {\n\t\t\tsetWelcomeModalOpen(true);\n\t\t}\n\t}, [user?.isFirstLogin]);\n\n\treturn (\n\t\t<Sidebar.Provider\n\t\t\tdefaultOpen\n\t\t\tstyle={\n\t\t\t\t{\n\t\t\t\t\theight: \"100svh\",\n\t\t\t\t\tminHeight: \"0\",\n\t\t\t\t\toverflow: \"hidden\",\n\t\t\t\t\t\"--sidebar-width-icon\": \"53px\",\n\t\t\t\t} as React.CSSProperties\n\t\t\t}\n\t\t>\n\t\t\t{/* Sidebar navigation */}\n\t\t\t<SidebarNav manifest={manifest} />\n\n\t\t\t{/* Main content area — scrolls independently so sidebar stays full height */}\n\t\t\t<div className=\"flex flex-1 flex-col overflow-hidden\">\n\t\t\t\t<Header />\n\t\t\t\t<main className=\"flex-1 overflow-y-auto p-6\">{children}</main>\n\t\t\t</div>\n\n\t\t\t{/* Welcome modal for first-time users */}\n\t\t\t{user && (\n\t\t\t\t<WelcomeModal\n\t\t\t\t\topen={welcomeModalOpen}\n\t\t\t\t\tonClose={() => setWelcomeModalOpen(false)}\n\t\t\t\t\tuserName={user.name}\n\t\t\t\t\tuserRole={user.role}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Command palette for quick navigation */}\n\t\t\t<AdminCommandPalette manifest={manifest} />\n\t\t</Sidebar.Provider>\n\t);\n}\n","/**\n * Signup Page - Self-signup for allowed domains\n *\n * This component is NOT wrapped in the admin Shell.\n * It's a standalone public page for self-signup.\n *\n * Flow:\n * 1. Email input form\n * 2. \"Check your email\" confirmation\n * 3. After clicking email link: Passkey registration\n */\n\nimport { Button, Input, Loader } from \"@cloudflare/kumo\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { requestSignup, verifySignupToken, type SignupVerifyResult } from \"../lib/api\";\nimport { PasskeyRegistration } from \"./auth/PasskeyRegistration\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype SignupStep = \"email\" | \"check-email\" | \"verify\" | \"complete\" | \"error\";\n\n// ============================================================================\n// Step Components\n// ============================================================================\n\ninterface EmailStepProps {\n\tonSubmit: (email: string) => void;\n\tisLoading: boolean;\n\terror?: string;\n}\n\nfunction EmailStep({ onSubmit, isLoading, error }: EmailStepProps) {\n\tconst [email, setEmail] = React.useState(\"\");\n\tconst [validationError, setValidationError] = React.useState<string | null>(null);\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsetValidationError(null);\n\n\t\tif (!email.trim()) {\n\t\t\tsetValidationError(\"Email is required\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!email.includes(\"@\") || !email.includes(\".\")) {\n\t\t\tsetValidationError(\"Please enter a valid email address\");\n\t\t\treturn;\n\t\t}\n\n\t\tonSubmit(email.trim().toLowerCase());\n\t};\n\n\treturn (\n\t\t<form onSubmit={handleSubmit} className=\"space-y-6\">\n\t\t\t<div className=\"space-y-4\">\n\t\t\t\t<div>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Email address\"\n\t\t\t\t\t\ttype=\"email\"\n\t\t\t\t\t\tvalue={email}\n\t\t\t\t\t\tonChange={(e) => setEmail(e.target.value)}\n\t\t\t\t\t\tplaceholder=\"you@company.com\"\n\t\t\t\t\t\tclassName={validationError ? \"border-kumo-danger\" : \"\"}\n\t\t\t\t\t\tdisabled={isLoading}\n\t\t\t\t\t\tautoComplete=\"email\"\n\t\t\t\t\t\tautoFocus\n\t\t\t\t\t/>\n\t\t\t\t\t{validationError && <p className=\"text-sm text-kumo-danger mt-1\">{validationError}</p>}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{error && (\n\t\t\t\t<div className=\"rounded-lg bg-kumo-danger/10 p-4 text-sm text-kumo-danger\">{error}</div>\n\t\t\t)}\n\n\t\t\t<Button type=\"submit\" className=\"w-full\" disabled={isLoading}>\n\t\t\t\t{isLoading ? (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<Loader size=\"sm\" />\n\t\t\t\t\t\tSending...\n\t\t\t\t\t</>\n\t\t\t\t) : (\n\t\t\t\t\t\"Continue\"\n\t\t\t\t)}\n\t\t\t</Button>\n\n\t\t\t<p className=\"text-xs text-kumo-subtle text-center\">\n\t\t\t\tOnly email addresses from allowed domains can sign up.\n\t\t\t</p>\n\t\t</form>\n\t);\n}\n\ninterface CheckEmailStepProps {\n\temail: string;\n\tonResend: () => void;\n\tisResending: boolean;\n\tresendCooldown: number;\n}\n\nfunction CheckEmailStep({ email, onResend, isResending, resendCooldown }: CheckEmailStepProps) {\n\treturn (\n\t\t<div className=\"space-y-6 text-center\">\n\t\t\t<div className=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-kumo-brand/10 mx-auto\">\n\t\t\t\t<svg\n\t\t\t\t\tclassName=\"w-8 h-8 text-kumo-brand\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\td=\"M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t</div>\n\n\t\t\t<div>\n\t\t\t\t<h2 className=\"text-xl font-semibold\">Check your email</h2>\n\t\t\t\t<p className=\"text-kumo-subtle mt-2\">\n\t\t\t\t\tWe've sent a verification link to{\" \"}\n\t\t\t\t\t<span className=\"font-medium text-kumo-default\">{email}</span>\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t<div className=\"text-sm text-kumo-subtle\">\n\t\t\t\t<p>Click the link in the email to continue setting up your account.</p>\n\t\t\t\t<p className=\"mt-2\">The link will expire in 15 minutes.</p>\n\t\t\t</div>\n\n\t\t\t<div className=\"pt-4 border-t\">\n\t\t\t\t<p className=\"text-sm text-kumo-subtle mb-2\">Didn't receive the email?</p>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tonClick={onResend}\n\t\t\t\t\tdisabled={isResending || resendCooldown > 0}\n\t\t\t\t>\n\t\t\t\t\t{isResending\n\t\t\t\t\t\t? \"Sending...\"\n\t\t\t\t\t\t: resendCooldown > 0\n\t\t\t\t\t\t\t? `Resend in ${resendCooldown}s`\n\t\t\t\t\t\t\t: \"Resend email\"}\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\ninterface VerifyStepProps {\n\tverifyResult: SignupVerifyResult;\n\ttoken: string;\n\tonBack: () => void;\n}\n\nfunction handleSignupSuccess() {\n\t// Redirect to admin dashboard after successful signup\n\twindow.location.href = \"/_emdash/admin\";\n}\n\nfunction VerifyStep({ verifyResult, token, onBack: _onBack }: VerifyStepProps) {\n\tconst [name, setName] = React.useState(\"\");\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<div className=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-green-500/10 mx-auto mb-4\">\n\t\t\t\t\t<svg\n\t\t\t\t\t\tclassName=\"w-8 h-8 text-green-600\"\n\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t>\n\t\t\t\t\t\t<path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M5 13l4 4L19 7\" />\n\t\t\t\t\t</svg>\n\t\t\t\t</div>\n\t\t\t\t<h2 className=\"text-xl font-semibold\">Email verified!</h2>\n\t\t\t\t<p className=\"text-kumo-subtle mt-2\">\n\t\t\t\t\tYou'll be signing up as{\" \"}\n\t\t\t\t\t<span className=\"font-medium text-kumo-default\">{verifyResult.roleName}</span>\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t{/* Email display (read-only) */}\n\t\t\t<Input label=\"Email\" value={verifyResult.email} disabled className=\"bg-kumo-tint\" />\n\n\t\t\t{/* Name input (optional) */}\n\t\t\t<Input\n\t\t\t\tlabel=\"Your name (optional)\"\n\t\t\t\ttype=\"text\"\n\t\t\t\tvalue={name}\n\t\t\t\tonChange={(e) => setName(e.target.value)}\n\t\t\t\tplaceholder=\"Jane Doe\"\n\t\t\t\tautoComplete=\"name\"\n\t\t\t/>\n\n\t\t\t{/* Passkey registration */}\n\t\t\t<div className=\"pt-4 border-t\">\n\t\t\t\t<h3 className=\"text-sm font-medium mb-3\">Create your passkey</h3>\n\t\t\t\t<p className=\"text-sm text-kumo-subtle mb-4\">\n\t\t\t\t\tPasskeys are a secure, passwordless way to sign in using your device's biometrics, PIN, or\n\t\t\t\t\tsecurity key.\n\t\t\t\t</p>\n\n\t\t\t\t<PasskeyRegistration\n\t\t\t\t\toptionsEndpoint=\"/_emdash/api/setup/admin\"\n\t\t\t\t\tverifyEndpoint=\"/_emdash/api/auth/signup/complete\"\n\t\t\t\t\tonSuccess={handleSignupSuccess}\n\t\t\t\t\tbuttonText=\"Create Account\"\n\t\t\t\t\tadditionalData={{ token, name: name || undefined }}\n\t\t\t\t/>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\ninterface ErrorStepProps {\n\tmessage: string;\n\tcode?: string;\n\tonRetry?: () => void;\n}\n\nfunction ErrorStep({ message, code, onRetry }: ErrorStepProps) {\n\treturn (\n\t\t<div className=\"space-y-6 text-center\">\n\t\t\t<div className=\"inline-flex items-center justify-center w-16 h-16 rounded-full bg-kumo-danger/10 mx-auto\">\n\t\t\t\t<svg\n\t\t\t\t\tclassName=\"w-8 h-8 text-kumo-danger\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\tstrokeLinecap=\"round\"\n\t\t\t\t\t\tstrokeLinejoin=\"round\"\n\t\t\t\t\t\tstrokeWidth={2}\n\t\t\t\t\t\td=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"\n\t\t\t\t\t/>\n\t\t\t\t</svg>\n\t\t\t</div>\n\n\t\t\t<div>\n\t\t\t\t<h2 className=\"text-xl font-semibold text-kumo-danger\">\n\t\t\t\t\t{code === \"token_expired\"\n\t\t\t\t\t\t? \"Link expired\"\n\t\t\t\t\t\t: code === \"invalid_token\"\n\t\t\t\t\t\t\t? \"Invalid link\"\n\t\t\t\t\t\t\t: code === \"user_exists\"\n\t\t\t\t\t\t\t\t? \"Account exists\"\n\t\t\t\t\t\t\t\t: \"Something went wrong\"}\n\t\t\t\t</h2>\n\t\t\t\t<p className=\"text-kumo-subtle mt-2\">{message}</p>\n\t\t\t</div>\n\n\t\t\t<div className=\"space-y-2\">\n\t\t\t\t{code === \"user_exists\" ? (\n\t\t\t\t\t<Link to=\"/login\">\n\t\t\t\t\t\t<Button className=\"w-full\">Sign in instead</Button>\n\t\t\t\t\t</Link>\n\t\t\t\t) : (\n\t\t\t\t\tonRetry && (\n\t\t\t\t\t\t<Button onClick={onRetry} className=\"w-full\">\n\t\t\t\t\t\t\tRequest a new link\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)\n\t\t\t\t)}\n\t\t\t\t<Link to=\"/login\">\n\t\t\t\t\t<Button variant=\"ghost\" className=\"w-full\">\n\t\t\t\t\t\tBack to login\n\t\t\t\t\t</Button>\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\nexport function SignupPage() {\n\tconst [step, setStep] = React.useState<SignupStep>(\"email\");\n\tconst [email, setEmail] = React.useState(\"\");\n\tconst [error, setError] = React.useState<string | undefined>();\n\tconst [errorCode, setErrorCode] = React.useState<string | undefined>();\n\tconst [isLoading, setIsLoading] = React.useState(false);\n\tconst [verifyResult, setVerifyResult] = React.useState<SignupVerifyResult | null>(null);\n\tconst [token, setToken] = React.useState<string | null>(null);\n\tconst [resendCooldown, setResendCooldown] = React.useState(0);\n\n\t// Check for token in URL on mount\n\tReact.useEffect(() => {\n\t\tconst params = new URLSearchParams(window.location.search);\n\t\tconst urlToken = params.get(\"token\");\n\n\t\tif (urlToken) {\n\t\t\tsetToken(urlToken);\n\t\t\tvoid verifyToken(urlToken);\n\t\t}\n\t}, []);\n\n\t// Resend cooldown timer\n\tReact.useEffect(() => {\n\t\tif (resendCooldown > 0) {\n\t\t\tconst timer = setTimeout(() => setResendCooldown((c) => c - 1), 1000);\n\t\t\treturn () => clearTimeout(timer);\n\t\t}\n\t}, [resendCooldown]);\n\n\tconst verifyToken = async (tokenToVerify: string) => {\n\t\tsetIsLoading(true);\n\t\tsetError(undefined);\n\t\tsetErrorCode(undefined);\n\n\t\ttry {\n\t\t\tconst result = await verifySignupToken(tokenToVerify);\n\t\t\tsetVerifyResult(result);\n\t\t\tsetStep(\"verify\");\n\t\t} catch (err) {\n\t\t\tconst verifyError = err instanceof Error ? err : new Error(String(err));\n\t\t\tconst errorWithCode = verifyError as Error & { code?: string };\n\t\t\tsetError(verifyError.message);\n\t\t\tsetErrorCode(typeof errorWithCode.code === \"string\" ? errorWithCode.code : undefined);\n\t\t\tsetStep(\"error\");\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t};\n\n\tconst handleEmailSubmit = async (submittedEmail: string) => {\n\t\tsetIsLoading(true);\n\t\tsetError(undefined);\n\t\tsetEmail(submittedEmail);\n\n\t\ttry {\n\t\t\tawait requestSignup(submittedEmail);\n\t\t\tsetStep(\"check-email\");\n\t\t} catch (err) {\n\t\t\tsetError(err instanceof Error ? err.message : \"Failed to send verification email\");\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t};\n\n\tconst handleResend = async () => {\n\t\tif (!email || resendCooldown > 0) return;\n\n\t\tsetIsLoading(true);\n\t\ttry {\n\t\t\tawait requestSignup(email);\n\t\t\tsetResendCooldown(60); // 60 second cooldown\n\t\t} catch {\n\t\t\t// Silently fail - don't reveal if email exists\n\t\t} finally {\n\t\t\tsetIsLoading(false);\n\t\t}\n\t};\n\n\tconst handleRetry = () => {\n\t\tsetStep(\"email\");\n\t\tsetError(undefined);\n\t\tsetErrorCode(undefined);\n\t\tsetToken(null);\n\t\t// Clear token from URL\n\t\twindow.history.replaceState({}, \"\", window.location.pathname);\n\t};\n\n\t// Loading state for token verification\n\tif (isLoading && token) {\n\t\treturn (\n\t\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base\">\n\t\t\t\t<div className=\"text-center\">\n\t\t\t\t\t<Loader />\n\t\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Verifying your link...</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div className=\"min-h-screen flex items-center justify-center bg-kumo-base p-4\">\n\t\t\t<div className=\"w-full max-w-md\">\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"text-center mb-8\">\n\t\t\t\t\t<div className=\"text-4xl font-bold mb-2\">💫 EmDash</div>\n\t\t\t\t\t<h1 className=\"text-2xl font-semibold text-kumo-default\">\n\t\t\t\t\t\t{step === \"email\" && \"Create an account\"}\n\t\t\t\t\t\t{step === \"check-email\" && \"Check your email\"}\n\t\t\t\t\t\t{step === \"verify\" && \"Complete signup\"}\n\t\t\t\t\t\t{step === \"error\" && \"Oops!\"}\n\t\t\t\t\t</h1>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Form Card */}\n\t\t\t\t<div className=\"bg-kumo-base border rounded-lg shadow-sm p-6\">\n\t\t\t\t\t{step === \"email\" && (\n\t\t\t\t\t\t<EmailStep onSubmit={handleEmailSubmit} isLoading={isLoading} error={error} />\n\t\t\t\t\t)}\n\n\t\t\t\t\t{step === \"check-email\" && (\n\t\t\t\t\t\t<CheckEmailStep\n\t\t\t\t\t\t\temail={email}\n\t\t\t\t\t\t\tonResend={handleResend}\n\t\t\t\t\t\t\tisResending={isLoading}\n\t\t\t\t\t\t\tresendCooldown={resendCooldown}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\n\t\t\t\t\t{step === \"verify\" && verifyResult && token && (\n\t\t\t\t\t\t<VerifyStep verifyResult={verifyResult} token={token} onBack={handleRetry} />\n\t\t\t\t\t)}\n\n\t\t\t\t\t{step === \"error\" && (\n\t\t\t\t\t\t<ErrorStep\n\t\t\t\t\t\t\tmessage={error ?? \"An unknown error occurred\"}\n\t\t\t\t\t\t\tcode={errorCode}\n\t\t\t\t\t\t\tonRetry={handleRetry}\n\t\t\t\t\t\t/>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Login link */}\n\t\t\t\t{step === \"email\" && (\n\t\t\t\t\t<p className=\"text-center mt-6 text-sm text-kumo-subtle\">\n\t\t\t\t\t\tAlready have an account?{\" \"}\n\t\t\t\t\t\t<Link to=\"/login\" className=\"text-kumo-brand hover:underline font-medium\">\n\t\t\t\t\t\t\tSign in\n\t\t\t\t\t\t</Link>\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default SignupPage;\n","/**\n * Taxonomy Terms Manager\n *\n * Provides UI for managing taxonomy terms (categories, tags, custom taxonomies).\n * Shows hierarchical structure for categories, flat list for tags.\n */\n\nimport { Button, Checkbox, Dialog, Input, InputArea, Select, Toast } from \"@cloudflare/kumo\";\nimport { Plus, Pencil, Trash, X } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { fetchManifest } from \"../lib/api/client.js\";\nimport type { TaxonomyTerm, TaxonomyDef, CreateTaxonomyInput } from \"../lib/api/taxonomies.js\";\nimport {\n\tfetchTaxonomyDef,\n\tfetchTerms,\n\tcreateTaxonomy,\n\tcreateTerm,\n\tupdateTerm,\n\tdeleteTerm,\n} from \"../lib/api/taxonomies.js\";\nimport { slugify } from \"../lib/utils\";\nimport { ConfirmDialog } from \"./ConfirmDialog.js\";\nimport { DialogError, getMutationError } from \"./DialogError.js\";\n\ninterface TaxonomyManagerProps {\n\ttaxonomyName: string;\n}\n\n// Regex patterns for taxonomy name generation and validation (module-scoped per lint rules)\nconst NON_ALPHANUMERIC_PATTERN = /[^a-z0-9]+/g;\nconst LEADING_TRAILING_UNDERSCORE_PATTERN = /^_|_$/g;\nconst TAXONOMY_NAME_PATTERN = /^[a-z][a-z0-9_]*$/;\n\n/**\n * Flatten tree to get all terms\n */\nfunction flattenTerms(terms: TaxonomyTerm[]): TaxonomyTerm[] {\n\treturn terms.flatMap((t) => [t, ...flattenTerms(t.children)]);\n}\n\n/**\n * Term row component (recursive for hierarchy)\n */\nfunction TermRow({\n\tterm,\n\tlevel = 0,\n\tonEdit,\n\tonDelete,\n}: {\n\tterm: TaxonomyTerm;\n\tlevel?: number;\n\tonEdit: (term: TaxonomyTerm) => void;\n\tonDelete: (term: TaxonomyTerm) => void;\n}) {\n\treturn (\n\t\t<>\n\t\t\t<div className=\"flex items-center gap-4 py-2 px-4 border-b hover:bg-kumo-tint/50\">\n\t\t\t\t<div style={{ marginLeft: `${level * 1.5}rem` }} className=\"flex-1\">\n\t\t\t\t\t<span className=\"font-medium\">{term.label}</span>\n\t\t\t\t\t<span className=\"text-sm text-kumo-subtle ml-2\">({term.slug})</span>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"text-sm text-kumo-subtle\">{term.count || 0}</div>\n\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\taria-label={`Edit ${term.label}`}\n\t\t\t\t\t\tonClick={() => onEdit(term)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Pencil className=\"w-4 h-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\taria-label={`Delete ${term.label}`}\n\t\t\t\t\t\tonClick={() => onDelete(term)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Trash className=\"w-4 h-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t\t{term.children.map((child) => (\n\t\t\t\t<TermRow\n\t\t\t\t\tkey={child.id}\n\t\t\t\t\tterm={child}\n\t\t\t\t\tlevel={level + 1}\n\t\t\t\t\tonEdit={onEdit}\n\t\t\t\t\tonDelete={onDelete}\n\t\t\t\t/>\n\t\t\t))}\n\t\t</>\n\t);\n}\n\n/**\n * Term form dialog\n */\nfunction TermFormDialog({\n\topen,\n\tonClose,\n\ttaxonomyName,\n\ttaxonomyDef,\n\tterm,\n\tallTerms,\n}: {\n\topen: boolean;\n\tonClose: () => void;\n\ttaxonomyName: string;\n\ttaxonomyDef: TaxonomyDef;\n\tterm?: TaxonomyTerm;\n\tallTerms: TaxonomyTerm[];\n}) {\n\tconst queryClient = useQueryClient();\n\tconst [label, setLabel] = React.useState(term?.label || \"\");\n\tconst [slug, setSlug] = React.useState(term?.slug || \"\");\n\tconst [parentId, setParentId] = React.useState(term?.parentId || \"\");\n\tconst [description, setDescription] = React.useState(term?.description || \"\");\n\tconst [autoSlug, setAutoSlug] = React.useState(!term);\n\tconst [error, setError] = React.useState<string | null>(null);\n\n\t// Auto-generate slug from label\n\tReact.useEffect(() => {\n\t\tif (autoSlug && label) {\n\t\t\tsetSlug(slugify(label));\n\t\t}\n\t}, [label, autoSlug]);\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: () =>\n\t\t\tcreateTerm(taxonomyName, {\n\t\t\t\tslug,\n\t\t\t\tlabel,\n\t\t\t\tparentId: parentId || undefined,\n\t\t\t\tdescription: description || undefined,\n\t\t\t}),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"taxonomy-terms\", taxonomyName],\n\t\t\t});\n\t\t\tonClose();\n\t\t},\n\t\tonError: (err: Error) => {\n\t\t\tsetError(err.message);\n\t\t},\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: () => {\n\t\t\tif (!term) throw new Error(\"No term to update\");\n\t\t\treturn updateTerm(taxonomyName, term.slug, {\n\t\t\t\tslug,\n\t\t\t\tlabel,\n\t\t\t\tparentId: parentId || undefined,\n\t\t\t\tdescription: description || undefined,\n\t\t\t});\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"taxonomy-terms\", taxonomyName],\n\t\t\t});\n\t\t\tonClose();\n\t\t},\n\t\tonError: (err: Error) => {\n\t\t\tsetError(err.message);\n\t\t},\n\t});\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\t\tif (term) {\n\t\t\tupdateMutation.mutate();\n\t\t} else {\n\t\t\tcreateMutation.mutate();\n\t\t}\n\t};\n\n\t// Flatten terms for parent selector (exclude current term and its children)\n\tconst flatTerms = flattenTerms(allTerms);\n\tconst availableParents = term\n\t\t? flatTerms.filter((t) => t.id !== term.id && t.parentId !== term.id)\n\t\t: flatTerms;\n\n\treturn (\n\t\t<Dialog.Root\n\t\t\topen={open}\n\t\t\tonOpenChange={(isOpen: boolean) => {\n\t\t\t\tif (!isOpen) {\n\t\t\t\t\tsetError(null);\n\t\t\t\t\tonClose();\n\t\t\t\t}\n\t\t\t}}\n\t\t>\n\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t<div className=\"flex flex-col space-y-1.5\">\n\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t\t{term ? \"Edit\" : \"Add\"} {taxonomyDef.labelSingular || \"Term\"}\n\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t<Dialog.Description className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t{term\n\t\t\t\t\t\t\t\t\t? `Update the ${taxonomyDef.labelSingular?.toLowerCase() || \"term\"} details`\n\t\t\t\t\t\t\t\t\t: `Create a new ${taxonomyDef.labelSingular?.toLowerCase() || \"term\"}`}\n\t\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"space-y-4 py-4\">\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Name\"\n\t\t\t\t\t\t\tvalue={label}\n\t\t\t\t\t\t\tonChange={(e) => setLabel(e.target.value)}\n\t\t\t\t\t\t\tplaceholder=\"News\"\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\t\t\tvalue={slug}\n\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\tsetSlug(e.target.value);\n\t\t\t\t\t\t\t\t\tsetAutoSlug(false);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tplaceholder=\"news\"\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\tAuto-generated from name (you can edit)\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{taxonomyDef.hierarchical && (\n\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\tlabel=\"Parent\"\n\t\t\t\t\t\t\t\tvalue={parentId}\n\t\t\t\t\t\t\t\tonValueChange={(v) => setParentId(v ?? \"\")}\n\t\t\t\t\t\t\t\titems={{\n\t\t\t\t\t\t\t\t\t\"\": \"None (top level)\",\n\t\t\t\t\t\t\t\t\t...Object.fromEntries(availableParents.map((t) => [t.id, t.label])),\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Select.Option value=\"\">None (top level)</Select.Option>\n\t\t\t\t\t\t\t\t{availableParents.map((t) => (\n\t\t\t\t\t\t\t\t\t<Select.Option key={t.id} value={t.id}>\n\t\t\t\t\t\t\t\t\t\t{t.label}\n\t\t\t\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t<InputArea\n\t\t\t\t\t\t\tlabel=\"Description (optional)\"\n\t\t\t\t\t\t\tvalue={description}\n\t\t\t\t\t\t\tonChange={(e) => setDescription(e.target.value)}\n\t\t\t\t\t\t\tplaceholder=\"Optional description\"\n\t\t\t\t\t\t\trows={3}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<DialogError\n\t\t\t\t\t\t\tmessage={\n\t\t\t\t\t\t\t\terror ||\n\t\t\t\t\t\t\t\tgetMutationError(createMutation.error) ||\n\t\t\t\t\t\t\t\tgetMutationError(updateMutation.error)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\">\n\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={onClose}>\n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button type=\"submit\" disabled={createMutation.isPending || updateMutation.isPending}>\n\t\t\t\t\t\t\t{createMutation.isPending || updateMutation.isPending\n\t\t\t\t\t\t\t\t? \"Saving...\"\n\t\t\t\t\t\t\t\t: term\n\t\t\t\t\t\t\t\t\t? \"Update\"\n\t\t\t\t\t\t\t\t\t: \"Create\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n\n/**\n * Create Taxonomy dialog\n */\nfunction CreateTaxonomyDialog({\n\topen,\n\tonClose,\n\tonCreated,\n}: {\n\topen: boolean;\n\tonClose: () => void;\n\tonCreated: () => void;\n}) {\n\tconst queryClient = useQueryClient();\n\tconst [name, setName] = React.useState(\"\");\n\tconst [label, setLabel] = React.useState(\"\");\n\tconst [hierarchical, setHierarchical] = React.useState(false);\n\tconst [selectedCollections, setSelectedCollections] = React.useState<string[]>([]);\n\tconst [autoName, setAutoName] = React.useState(true);\n\tconst [error, setError] = React.useState<string | null>(null);\n\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tconst collectionEntries = manifest\n\t\t? Object.entries(manifest.collections).map(([slug, config]) => ({\n\t\t\t\tslug,\n\t\t\t\tlabel: config.label,\n\t\t\t}))\n\t\t: [];\n\n\t// Auto-generate name from label\n\tReact.useEffect(() => {\n\t\tif (autoName && label) {\n\t\t\tsetName(\n\t\t\t\tlabel\n\t\t\t\t\t.toLowerCase()\n\t\t\t\t\t.replace(NON_ALPHANUMERIC_PATTERN, \"_\")\n\t\t\t\t\t.replace(LEADING_TRAILING_UNDERSCORE_PATTERN, \"\"),\n\t\t\t);\n\t\t}\n\t}, [label, autoName]);\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: (input: CreateTaxonomyInput) => createTaxonomy(input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"taxonomy-defs\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"taxonomy-def\"] });\n\t\t\tonCreated();\n\t\t\tresetForm();\n\t\t},\n\t});\n\n\tconst resetForm = () => {\n\t\tsetName(\"\");\n\t\tsetLabel(\"\");\n\t\tsetHierarchical(false);\n\t\tsetSelectedCollections([]);\n\t\tsetAutoName(true);\n\t\tsetError(null);\n\t\tcreateMutation.reset();\n\t};\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tsetError(null);\n\n\t\tif (!name || !label) {\n\t\t\tsetError(\"Name and label are required\");\n\t\t\treturn;\n\t\t}\n\n\t\tif (!TAXONOMY_NAME_PATTERN.test(name)) {\n\t\t\tsetError(\n\t\t\t\t\"Name must start with a letter and contain only lowercase letters, numbers, and underscores\",\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tcreateMutation.mutate({\n\t\t\tname,\n\t\t\tlabel,\n\t\t\thierarchical,\n\t\t\tcollections: selectedCollections,\n\t\t});\n\t};\n\n\tconst toggleCollection = (slug: string) => {\n\t\tsetSelectedCollections((prev) =>\n\t\t\tprev.includes(slug) ? prev.filter((s) => s !== slug) : [...prev, slug],\n\t\t);\n\t};\n\n\treturn (\n\t\t<Dialog.Root\n\t\t\topen={open}\n\t\t\tonOpenChange={(isOpen: boolean) => {\n\t\t\t\tif (!isOpen) {\n\t\t\t\t\tresetForm();\n\t\t\t\t\tonClose();\n\t\t\t\t}\n\t\t\t}}\n\t\t>\n\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t<div className=\"flex flex-col space-y-1.5\">\n\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t\tCreate Taxonomy\n\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t<Dialog.Description className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\tDefine a new taxonomy for classifying content\n\t\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"space-y-4 py-4\">\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tlabel=\"Label\"\n\t\t\t\t\t\t\tvalue={label}\n\t\t\t\t\t\t\tonChange={(e) => setLabel(e.target.value)}\n\t\t\t\t\t\t\tplaceholder=\"Genres\"\n\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Name\"\n\t\t\t\t\t\t\t\tvalue={name}\n\t\t\t\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\t\t\t\tsetName(e.target.value);\n\t\t\t\t\t\t\t\t\tsetAutoName(false);\n\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\tplaceholder=\"genre\"\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\tpattern=\"[a-z][a-z0-9_]*\"\n\t\t\t\t\t\t\t\ttitle=\"Lowercase letters, numbers, and underscores only, starting with a letter\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\tUsed as the identifier. Lowercase letters, numbers, and underscores only.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<Checkbox\n\t\t\t\t\t\t\tlabel=\"Hierarchical (like categories, with parent/child relationships)\"\n\t\t\t\t\t\t\tchecked={hierarchical}\n\t\t\t\t\t\t\tonCheckedChange={(checked) => setHierarchical(checked)}\n\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t{collectionEntries.length > 0 && (\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<label className=\"text-sm font-medium\">Collections</label>\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mb-2\">\n\t\t\t\t\t\t\t\t\tWhich content types can use this taxonomy\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<div className=\"border rounded-md p-2 space-y-1\">\n\t\t\t\t\t\t\t\t\t{collectionEntries.map(({ slug, label: collLabel }) => (\n\t\t\t\t\t\t\t\t\t\t<label\n\t\t\t\t\t\t\t\t\t\t\tkey={slug}\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex items-center gap-2 py-1 px-2 cursor-pointer hover:bg-kumo-tint/50 rounded\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\t\t\tchecked={selectedCollections.includes(slug)}\n\t\t\t\t\t\t\t\t\t\t\t\tonChange={() => toggleCollection(slug)}\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"rounded\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-sm\">{collLabel}</span>\n\t\t\t\t\t\t\t\t\t\t</label>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t<DialogError message={error || getMutationError(createMutation.error)} />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\">\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tresetForm();\n\t\t\t\t\t\t\t\tonClose();\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button type=\"submit\" disabled={createMutation.isPending}>\n\t\t\t\t\t\t\t{createMutation.isPending ? \"Creating...\" : \"Create Taxonomy\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n\n/**\n * Main TaxonomyManager component\n */\nexport function TaxonomyManager({ taxonomyName }: TaxonomyManagerProps) {\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\tconst [formOpen, setFormOpen] = React.useState(false);\n\tconst [editingTerm, setEditingTerm] = React.useState<TaxonomyTerm | undefined>();\n\tconst [deleteTarget, setDeleteTarget] = React.useState<TaxonomyTerm | null>(null);\n\tconst [createTaxonomyOpen, setCreateTaxonomyOpen] = React.useState(false);\n\n\tconst { data: taxonomyDef, isLoading: defLoading } = useQuery({\n\t\tqueryKey: [\"taxonomy-def\", taxonomyName],\n\t\tqueryFn: () => fetchTaxonomyDef(taxonomyName),\n\t});\n\n\tconst { data: terms = [], isLoading: termsLoading } = useQuery({\n\t\tqueryKey: [\"taxonomy-terms\", taxonomyName],\n\t\tqueryFn: () => fetchTerms(taxonomyName),\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (term: TaxonomyTerm) => deleteTerm(taxonomyName, term.slug),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"taxonomy-terms\", taxonomyName],\n\t\t\t});\n\t\t\tsetDeleteTarget(null);\n\t\t\ttoastManager.add({ title: \"Term deleted\" });\n\t\t},\n\t});\n\n\tconst handleEdit = (term: TaxonomyTerm) => {\n\t\tsetEditingTerm(term);\n\t\tsetFormOpen(true);\n\t};\n\n\tconst handleDelete = (term: TaxonomyTerm) => {\n\t\tsetDeleteTarget(term);\n\t};\n\n\tconst handleCloseForm = () => {\n\t\tsetFormOpen(false);\n\t\tsetEditingTerm(undefined);\n\t};\n\n\tif (defLoading) {\n\t\treturn <div>Loading...</div>;\n\t}\n\n\tif (!taxonomyDef) {\n\t\treturn <div>Taxonomy not found: {taxonomyName}</div>;\n\t}\n\n\tconst flatTerms = flattenTerms(terms);\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div>\n\t\t\t\t\t<h1 className=\"text-3xl font-bold\">{taxonomyDef.label}</h1>\n\t\t\t\t\t<p className=\"text-kumo-subtle mt-1\">\n\t\t\t\t\t\tManage {taxonomyDef.label.toLowerCase()} for {taxonomyDef.collections.join(\", \")}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t<Button variant=\"outline\" icon={<Plus />} onClick={() => setCreateTaxonomyOpen(true)}>\n\t\t\t\t\t\tNew Taxonomy\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button icon={<Plus />} onClick={() => setFormOpen(true)}>\n\t\t\t\t\t\tAdd {taxonomyDef.labelSingular || \"Term\"}\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"border rounded-lg\">\n\t\t\t\t<div className=\"flex items-center gap-4 py-2 px-4 border-b bg-kumo-tint/50 font-medium\">\n\t\t\t\t\t<div className=\"flex-1\">Name</div>\n\t\t\t\t\t<div className=\"w-16 text-center\">Count</div>\n\t\t\t\t\t<div className=\"w-24 text-center\">Actions</div>\n\t\t\t\t</div>\n\n\t\t\t\t{termsLoading ? (\n\t\t\t\t\t<div className=\"p-8 text-center text-kumo-subtle\">Loading terms...</div>\n\t\t\t\t) : terms.length === 0 ? (\n\t\t\t\t\t<div className=\"p-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\tNo {taxonomyDef.label.toLowerCase()} yet. Create one to get started.\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<div>\n\t\t\t\t\t\t{terms.map((term) => (\n\t\t\t\t\t\t\t<TermRow key={term.id} term={term} onEdit={handleEdit} onDelete={handleDelete} />\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<TermFormDialog\n\t\t\t\topen={formOpen}\n\t\t\t\tonClose={handleCloseForm}\n\t\t\t\ttaxonomyName={taxonomyName}\n\t\t\t\ttaxonomyDef={taxonomyDef}\n\t\t\t\tterm={editingTerm}\n\t\t\t\tallTerms={flatTerms}\n\t\t\t/>\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={!!deleteTarget}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetDeleteTarget(null);\n\t\t\t\t\tdeleteMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle={`Delete ${taxonomyDef.labelSingular || \"Term\"}?`}\n\t\t\t\tdescription={\n\t\t\t\t\t<>This will permanently delete \"{deleteTarget?.label}\" and remove it from all content.</>\n\t\t\t\t}\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={deleteMutation.isPending}\n\t\t\t\terror={deleteMutation.error}\n\t\t\t\tonConfirm={() => deleteTarget && deleteMutation.mutate(deleteTarget)}\n\t\t\t/>\n\n\t\t\t<CreateTaxonomyDialog\n\t\t\t\topen={createTaxonomyOpen}\n\t\t\t\tonClose={() => setCreateTaxonomyOpen(false)}\n\t\t\t\tonCreated={() => {\n\t\t\t\t\tsetCreateTaxonomyOpen(false);\n\t\t\t\t\ttoastManager.add({ title: \"Taxonomy created\" });\n\t\t\t\t}}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","/**\n * Theme Marketplace Browse\n *\n * Visual-first grid of theme cards with large thumbnails.\n * Navigates to theme detail on card click.\n */\n\nimport { Button } from \"@cloudflare/kumo\";\nimport {\n\tMagnifyingGlass,\n\tPalette,\n\tWarning,\n\tArrowsClockwise,\n\tArrowSquareOut,\n\tEye,\n\tShieldCheck,\n} from \"@phosphor-icons/react\";\nimport { useInfiniteQuery, useMutation } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport {\n\tsearchThemes,\n\tgeneratePreviewUrl,\n\ttype ThemeSummary,\n\ttype ThemeSearchOpts,\n} from \"../lib/api/theme-marketplace.js\";\n\ntype SortOption = \"updated\" | \"created\" | \"name\";\n\nconst SORT_LABELS: Record<SortOption, string> = {\n\tupdated: \"Recently Updated\",\n\tcreated: \"Newest\",\n\tname: \"Name\",\n};\n\nconst VALID_SORTS = new Set<string>([\"updated\", \"created\", \"name\"]);\n\nfunction isSortOption(value: string): value is SortOption {\n\treturn VALID_SORTS.has(value);\n}\n\nexport function ThemeMarketplaceBrowse() {\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst [sort, setSort] = React.useState<SortOption>(\"updated\");\n\tconst [debouncedQuery, setDebouncedQuery] = React.useState(\"\");\n\n\tReact.useEffect(() => {\n\t\tconst timer = setTimeout(setDebouncedQuery, 300, searchQuery);\n\t\treturn () => clearTimeout(timer);\n\t}, [searchQuery]);\n\n\tconst searchOpts: ThemeSearchOpts = {\n\t\tq: debouncedQuery || undefined,\n\t\tsort,\n\t\tlimit: 12,\n\t};\n\n\tconst { data, isLoading, error, refetch, fetchNextPage, hasNextPage, isFetchingNextPage } =\n\t\tuseInfiniteQuery({\n\t\t\tqueryKey: [\"themes\", \"search\", searchOpts],\n\t\t\tqueryFn: ({ pageParam }) => searchThemes({ ...searchOpts, cursor: pageParam }),\n\t\t\tinitialPageParam: undefined as string | undefined,\n\t\t\tgetNextPageParam: (lastPage) => lastPage.nextCursor,\n\t\t});\n\n\tconst themes = data?.pages.flatMap((p) => p.items);\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Header */}\n\t\t\t<div>\n\t\t\t\t<h1 className=\"text-3xl font-bold\">Themes</h1>\n\t\t\t\t<p className=\"mt-1 text-kumo-subtle\">\n\t\t\t\t\tBrowse themes and preview them with your own content.\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t{/* Search + Sort */}\n\t\t\t<div className=\"flex flex-col gap-3 sm:flex-row sm:items-center\">\n\t\t\t\t<div className=\"relative flex-1\">\n\t\t\t\t\t<MagnifyingGlass className=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-kumo-subtle\" />\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tplaceholder=\"Search themes...\"\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={(e) => setSearchQuery(e.target.value)}\n\t\t\t\t\t\tclassName=\"w-full rounded-md border bg-kumo-base px-3 py-2 pl-9 text-sm placeholder:text-kumo-subtle focus:outline-none focus:ring-2 focus:ring-kumo-ring\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<select\n\t\t\t\t\tvalue={sort}\n\t\t\t\t\tonChange={(e) => {\n\t\t\t\t\t\tconst v = e.target.value;\n\t\t\t\t\t\tif (isSortOption(v)) setSort(v);\n\t\t\t\t\t}}\n\t\t\t\t\tclassName=\"rounded-md border bg-kumo-base px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-kumo-ring\"\n\t\t\t\t\taria-label=\"Sort themes\"\n\t\t\t\t>\n\t\t\t\t\t{Object.entries(SORT_LABELS).map(([value, label]) => (\n\t\t\t\t\t\t<option key={value} value={value}>\n\t\t\t\t\t\t\t{label}\n\t\t\t\t\t\t</option>\n\t\t\t\t\t))}\n\t\t\t\t</select>\n\t\t\t</div>\n\n\t\t\t{/* Error state */}\n\t\t\t{error && (\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-6 text-center\">\n\t\t\t\t\t<Warning className=\"mx-auto h-8 w-8 text-kumo-danger\" />\n\t\t\t\t\t<h3 className=\"mt-3 font-medium text-kumo-danger\">Unable to reach marketplace</h3>\n\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{error instanceof Error ? error.message : \"An error occurred\"}\n\t\t\t\t\t</p>\n\t\t\t\t\t<Button variant=\"ghost\" className=\"mt-4\" onClick={() => void refetch()}>\n\t\t\t\t\t\t<ArrowsClockwise className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\tRetry\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Loading state — skeleton cards with thumbnail aspect ratio */}\n\t\t\t{isLoading && (\n\t\t\t\t<div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n\t\t\t\t\t{Array.from({ length: 6 }).map((_, i) => (\n\t\t\t\t\t\t<div key={i} className=\"animate-pulse rounded-lg border bg-kumo-base overflow-hidden\">\n\t\t\t\t\t\t\t<div className=\"aspect-video bg-kumo-tint\" />\n\t\t\t\t\t\t\t<div className=\"p-4 space-y-2\">\n\t\t\t\t\t\t\t\t<div className=\"h-4 w-32 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t\t<div className=\"h-3 w-48 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t\t<div className=\"h-3 w-20 rounded bg-kumo-tint\" />\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Results grid */}\n\t\t\t{themes && !isLoading && (\n\t\t\t\t<>\n\t\t\t\t\t{themes.length === 0 ? (\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-8 text-center\">\n\t\t\t\t\t\t\t<Palette className=\"mx-auto h-12 w-12 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<h3 className=\"mt-4 text-lg font-medium\">No themes found</h3>\n\t\t\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t{debouncedQuery\n\t\t\t\t\t\t\t\t\t? `No results for \"${debouncedQuery}\". Try a different search term.`\n\t\t\t\t\t\t\t\t\t: \"The theme marketplace is empty. Check back later.\"}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n\t\t\t\t\t\t\t\t{themes.map((theme) => (\n\t\t\t\t\t\t\t\t\t<ThemeCard key={theme.id} theme={theme} />\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t{hasNextPage && (\n\t\t\t\t\t\t\t\t<div className=\"flex justify-center\">\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => void fetchNextPage()}\n\t\t\t\t\t\t\t\t\t\tdisabled={isFetchingNextPage}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{isFetchingNextPage ? \"Loading...\" : \"Load more\"}\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// ThemeCard\n// ---------------------------------------------------------------------------\n\nfunction ThemeCard({ theme }: { theme: ThemeSummary }) {\n\tconst thumbnailUrl = theme.thumbnailUrl\n\t\t? `/_emdash/api/admin/themes/marketplace/${encodeURIComponent(theme.id)}/thumbnail`\n\t\t: null;\n\n\tconst previewMutation = useMutation({\n\t\tmutationFn: () => generatePreviewUrl(theme.previewUrl),\n\t\tonSuccess: (url) => {\n\t\t\twindow.open(url, \"_blank\", \"noopener\");\n\t\t},\n\t});\n\n\treturn (\n\t\t<div className=\"group rounded-lg border bg-kumo-base overflow-hidden transition-colors hover:border-kumo-brand/50\">\n\t\t\t{/* Thumbnail */}\n\t\t\t<Link\n\t\t\t\tto={\"/themes/marketplace/$themeId\" as \"/\"}\n\t\t\t\tparams={{ themeId: theme.id }}\n\t\t\t\tclassName=\"block\"\n\t\t\t>\n\t\t\t\t{thumbnailUrl ? (\n\t\t\t\t\t<img\n\t\t\t\t\t\tsrc={thumbnailUrl}\n\t\t\t\t\t\talt={`${theme.name} preview`}\n\t\t\t\t\t\tclassName=\"aspect-video w-full object-cover bg-kumo-tint\"\n\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t/>\n\t\t\t\t) : (\n\t\t\t\t\t<div className=\"aspect-video w-full bg-kumo-tint flex items-center justify-center\">\n\t\t\t\t\t\t<Palette className=\"h-12 w-12 text-kumo-subtle/40\" />\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</Link>\n\n\t\t\t{/* Info */}\n\t\t\t<div className=\"p-4\">\n\t\t\t\t<Link\n\t\t\t\t\tto={\"/themes/marketplace/$themeId\" as \"/\"}\n\t\t\t\t\tparams={{ themeId: theme.id }}\n\t\t\t\t\tclassName=\"block\"\n\t\t\t\t>\n\t\t\t\t\t<h3 className=\"font-semibold group-hover:text-kumo-brand truncate\">{theme.name}</h3>\n\t\t\t\t</Link>\n\n\t\t\t\t<div className=\"flex items-center gap-2 mt-1 text-xs text-kumo-subtle\">\n\t\t\t\t\t<span>{theme.author.name}</span>\n\t\t\t\t\t{theme.author.verified && <ShieldCheck className=\"h-3 w-3 text-kumo-brand\" />}\n\t\t\t\t</div>\n\n\t\t\t\t{theme.description && (\n\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle line-clamp-2\">{theme.description}</p>\n\t\t\t\t)}\n\n\t\t\t\t{/* Action buttons */}\n\t\t\t\t<div className=\"mt-3 flex items-center gap-2\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tonClick={(e) => {\n\t\t\t\t\t\t\te.preventDefault();\n\t\t\t\t\t\t\tpreviewMutation.mutate();\n\t\t\t\t\t\t}}\n\t\t\t\t\t\tdisabled={previewMutation.isPending}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Eye className=\"mr-1.5 h-3.5 w-3.5\" />\n\t\t\t\t\t\t{previewMutation.isPending ? \"Loading...\" : \"Try with my data\"}\n\t\t\t\t\t</Button>\n\n\t\t\t\t\t{theme.demoUrl && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\tonClick={() => window.open(theme.demoUrl!, \"_blank\", \"noopener\")}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowSquareOut className=\"mr-1.5 h-3.5 w-3.5\" />\n\t\t\t\t\t\t\tDemo\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{previewMutation.error && (\n\t\t\t\t\t<p className=\"mt-2 text-xs text-kumo-danger\">\n\t\t\t\t\t\t{previewMutation.error instanceof Error\n\t\t\t\t\t\t\t? previewMutation.error.message\n\t\t\t\t\t\t\t: \"Failed to generate preview\"}\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default ThemeMarketplaceBrowse;\n","/**\n * Theme Marketplace Detail\n *\n * Full detail view for a marketplace theme:\n * - Screenshot gallery\n * - Description, author, license\n * - \"Try with my data\" button\n * - Demo + repository links\n */\n\nimport { Badge, Button } from \"@cloudflare/kumo\";\nimport {\n\tArrowLeft,\n\tArrowSquareOut,\n\tEye,\n\tGithubLogo,\n\tGlobe,\n\tPalette,\n\tShieldCheck,\n\tCaretLeft,\n\tCaretRight,\n\tX,\n} from \"@phosphor-icons/react\";\nimport { useQuery, useMutation } from \"@tanstack/react-query\";\nimport { Link } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { fetchTheme, generatePreviewUrl } from \"../lib/api/theme-marketplace.js\";\n\n/** Only allow safe URL protocols for external links */\nfunction isSafeUrl(url: string): boolean {\n\ttry {\n\t\tconst parsed = new URL(url);\n\t\treturn parsed.protocol === \"https:\" || parsed.protocol === \"http:\";\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nexport interface ThemeMarketplaceDetailProps {\n\tthemeId: string;\n}\n\nexport function ThemeMarketplaceDetail({ themeId }: ThemeMarketplaceDetailProps) {\n\tconst [lightboxIndex, setLightboxIndex] = React.useState<number | null>(null);\n\n\tconst {\n\t\tdata: theme,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"themes\", \"detail\", themeId],\n\t\tqueryFn: () => fetchTheme(themeId),\n\t});\n\n\tconst previewMutation = useMutation({\n\t\tmutationFn: () => generatePreviewUrl(theme!.previewUrl),\n\t\tonSuccess: (url) => {\n\t\t\twindow.open(url, \"_blank\", \"noopener\");\n\t\t},\n\t});\n\n\t// Loading\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6 animate-pulse\">\n\t\t\t\t<div className=\"h-6 w-48 rounded bg-kumo-tint\" />\n\t\t\t\t<div className=\"aspect-video max-w-2xl rounded-lg bg-kumo-tint\" />\n\t\t\t\t<div className=\"space-y-3\">\n\t\t\t\t\t<div className=\"h-4 w-64 rounded bg-kumo-tint\" />\n\t\t\t\t\t<div className=\"h-4 w-96 rounded bg-kumo-tint\" />\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// Error\n\tif (error || !theme) {\n\t\treturn (\n\t\t\t<div className=\"space-y-4\">\n\t\t\t\t<Link\n\t\t\t\t\tto={\"/themes/marketplace\" as \"/\"}\n\t\t\t\t\tclassName=\"inline-flex items-center gap-1 text-sm text-kumo-subtle hover:text-kumo-default\"\n\t\t\t\t>\n\t\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\t\tBack to Themes\n\t\t\t\t</Link>\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-6 text-center\">\n\t\t\t\t\t<h3 className=\"font-medium text-kumo-danger\">Failed to load theme</h3>\n\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t{error instanceof Error ? error.message : \"Theme not found\"}\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst thumbnailUrl = theme.hasThumbnail\n\t\t? `/_emdash/api/admin/themes/marketplace/${encodeURIComponent(theme.id)}/thumbnail`\n\t\t: null;\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Back link */}\n\t\t\t<Link\n\t\t\t\tto={\"/themes/marketplace\" as \"/\"}\n\t\t\t\tclassName=\"inline-flex items-center gap-1 text-sm text-kumo-subtle hover:text-kumo-default\"\n\t\t\t>\n\t\t\t\t<ArrowLeft className=\"h-4 w-4\" />\n\t\t\t\tBack to Themes\n\t\t\t</Link>\n\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between\">\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t{thumbnailUrl ? (\n\t\t\t\t\t\t<img src={thumbnailUrl} alt=\"\" className=\"h-16 w-16 rounded-lg object-cover\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"flex h-16 w-16 items-center justify-center rounded-lg bg-kumo-brand/10\">\n\t\t\t\t\t\t\t<Palette className=\"h-8 w-8 text-kumo-brand\" />\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h1 className=\"text-2xl font-bold\">{theme.name}</h1>\n\t\t\t\t\t\t<div className=\"mt-1 flex items-center gap-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t<span>{theme.author.name}</span>\n\t\t\t\t\t\t\t{theme.author.verified && <ShieldCheck className=\"h-4 w-4 text-kumo-brand\" />}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{theme.description && (\n\t\t\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle max-w-xl\">{theme.description}</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Actions */}\n\t\t\t\t<div className=\"flex gap-2 shrink-0\">\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\tonClick={() => previewMutation.mutate()}\n\t\t\t\t\t\tdisabled={previewMutation.isPending}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Eye className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t{previewMutation.isPending ? \"Loading...\" : \"Try with my data\"}\n\t\t\t\t\t</Button>\n\t\t\t\t\t{theme.demoUrl && isSafeUrl(theme.demoUrl) && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\tonClick={() => window.open(theme.demoUrl!, \"_blank\", \"noopener\")}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<ArrowSquareOut className=\"mr-2 h-4 w-4\" />\n\t\t\t\t\t\t\tDemo\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{previewMutation.error && (\n\t\t\t\t<div className=\"rounded-md border border-kumo-danger/50 bg-kumo-danger/10 p-3 text-sm text-kumo-danger\">\n\t\t\t\t\t{previewMutation.error instanceof Error\n\t\t\t\t\t\t? previewMutation.error.message\n\t\t\t\t\t\t: \"Failed to generate preview URL\"}\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Screenshot gallery */}\n\t\t\t{theme.screenshotCount > 0 && (\n\t\t\t\t<div>\n\t\t\t\t\t<h2 className=\"text-lg font-semibold mb-3\">Screenshots</h2>\n\t\t\t\t\t<div className=\"grid gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n\t\t\t\t\t\t{theme.screenshotUrls.map((url, i) => (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tkey={i}\n\t\t\t\t\t\t\t\tclassName=\"rounded-lg border overflow-hidden hover:border-kumo-brand/50 transition-colors cursor-pointer\"\n\t\t\t\t\t\t\t\tonClick={() => setLightboxIndex(i)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\tsrc={url}\n\t\t\t\t\t\t\t\t\talt={`Screenshot ${i + 1}`}\n\t\t\t\t\t\t\t\t\tclassName=\"aspect-video w-full object-cover\"\n\t\t\t\t\t\t\t\t\tloading=\"lazy\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Details */}\n\t\t\t<div className=\"grid gap-6 sm:grid-cols-2\">\n\t\t\t\t{/* Keywords */}\n\t\t\t\t{theme.keywords.length > 0 && (\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h3 className=\"text-sm font-medium text-kumo-subtle mb-2\">Keywords</h3>\n\t\t\t\t\t\t<div className=\"flex flex-wrap gap-1\">\n\t\t\t\t\t\t\t{theme.keywords.map((kw) => (\n\t\t\t\t\t\t\t\t<Badge key={kw} variant=\"secondary\">\n\t\t\t\t\t\t\t\t\t{kw}\n\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* License */}\n\t\t\t\t{theme.license && (\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h3 className=\"text-sm font-medium text-kumo-subtle mb-2\">License</h3>\n\t\t\t\t\t\t<p className=\"text-sm\">{theme.license}</p>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{/* Links */}\n\t\t\t\t<div>\n\t\t\t\t\t<h3 className=\"text-sm font-medium text-kumo-subtle mb-2\">Links</h3>\n\t\t\t\t\t<div className=\"flex flex-col gap-1.5\">\n\t\t\t\t\t\t{theme.repositoryUrl && isSafeUrl(theme.repositoryUrl) && (\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\thref={theme.repositoryUrl}\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\tclassName=\"inline-flex items-center gap-1.5 text-sm text-kumo-brand hover:underline\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<GithubLogo className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\tRepository\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{theme.homepageUrl && isSafeUrl(theme.homepageUrl) && (\n\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\thref={theme.homepageUrl}\n\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\tclassName=\"inline-flex items-center gap-1.5 text-sm text-kumo-brand hover:underline\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<Globe className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\tHomepage\n\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Lightbox */}\n\t\t\t{lightboxIndex !== null && (\n\t\t\t\t<Lightbox\n\t\t\t\t\turls={theme.screenshotUrls}\n\t\t\t\t\tindex={lightboxIndex}\n\t\t\t\t\tonClose={() => setLightboxIndex(null)}\n\t\t\t\t\tonPrev={() =>\n\t\t\t\t\t\tsetLightboxIndex((i) => (i !== null && i > 0 ? i - 1 : theme.screenshotUrls.length - 1))\n\t\t\t\t\t}\n\t\t\t\t\tonNext={() =>\n\t\t\t\t\t\tsetLightboxIndex((i) => (i !== null && i < theme.screenshotUrls.length - 1 ? i + 1 : 0))\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// ---------------------------------------------------------------------------\n// Lightbox\n// ---------------------------------------------------------------------------\n\nfunction Lightbox({\n\turls,\n\tindex,\n\tonClose,\n\tonPrev,\n\tonNext,\n}: {\n\turls: string[];\n\tindex: number;\n\tonClose: () => void;\n\tonPrev: () => void;\n\tonNext: () => void;\n}) {\n\tReact.useEffect(() => {\n\t\tfunction onKeyDown(e: KeyboardEvent) {\n\t\t\tif (e.key === \"Escape\") onClose();\n\t\t\tif (e.key === \"ArrowLeft\") onPrev();\n\t\t\tif (e.key === \"ArrowRight\") onNext();\n\t\t}\n\t\tdocument.addEventListener(\"keydown\", onKeyDown);\n\t\treturn () => document.removeEventListener(\"keydown\", onKeyDown);\n\t}, [onClose, onPrev, onNext]);\n\n\tconst url = urls[index];\n\tif (!url) return null;\n\n\treturn (\n\t\t<div\n\t\t\tclassName=\"fixed inset-0 z-50 flex items-center justify-center bg-black/80\"\n\t\t\tonClick={onClose}\n\t\t>\n\t\t\t<div className=\"relative max-h-[90vh] max-w-[90vw]\" onClick={(e) => e.stopPropagation()}>\n\t\t\t\t<img src={url} alt={`Screenshot ${index + 1}`} className=\"max-h-[85vh] rounded-lg\" />\n\n\t\t\t\t<button\n\t\t\t\t\tonClick={onClose}\n\t\t\t\t\tclassName=\"absolute -top-3 -right-3 rounded-full bg-kumo-base p-1.5 shadow-lg hover:bg-kumo-tint\"\n\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t>\n\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t</button>\n\n\t\t\t\t{urls.length > 1 && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tonClick={onPrev}\n\t\t\t\t\t\t\tclassName=\"absolute left-2 top-1/2 -translate-y-1/2 rounded-full bg-kumo-base/80 p-2 shadow hover:bg-kumo-base\"\n\t\t\t\t\t\t\taria-label=\"Previous\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CaretLeft className=\"h-5 w-5\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t\t<button\n\t\t\t\t\t\t\tonClick={onNext}\n\t\t\t\t\t\t\tclassName=\"absolute right-2 top-1/2 -translate-y-1/2 rounded-full bg-kumo-base/80 p-2 shadow hover:bg-kumo-base\"\n\t\t\t\t\t\t\taria-label=\"Next\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<CaretRight className=\"h-5 w-5\" />\n\t\t\t\t\t\t</button>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\n\t\t\t\t<div className=\"absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-kumo-base/80 px-3 py-1 text-xs\">\n\t\t\t\t\t{index + 1} / {urls.length}\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport default ThemeMarketplaceDetail;\n","/**\n * Widgets page component\n *\n * Manage widget areas and widgets with drag-and-drop support.\n * Available widgets can be dragged from the palette into widget areas.\n * Widgets within an area can be reordered via drag-and-drop.\n */\n\nimport { Button, Dialog, Input, Label, Select, Switch, Toast } from \"@cloudflare/kumo\";\nimport {\n\tDndContext,\n\tDragOverlay,\n\ttype CollisionDetection,\n\ttype DragEndEvent,\n\ttype DragStartEvent,\n\tKeyboardSensor,\n\tclosestCenter,\n\trectIntersection,\n\tuseSensor,\n\tuseSensors,\n\tuseDraggable,\n\tuseDroppable,\n\tPointerSensor,\n} from \"@dnd-kit/core\";\nimport {\n\tSortableContext,\n\tsortableKeyboardCoordinates,\n\tverticalListSortingStrategy,\n\tuseSortable,\n} from \"@dnd-kit/sortable\";\nimport { CSS } from \"@dnd-kit/utilities\";\nimport { Plus, DotsSixVertical, Trash, CaretDown, CaretRight } from \"@phosphor-icons/react\";\nimport { X } from \"@phosphor-icons/react\";\nimport { useQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport {\n\tfetchWidgetAreas,\n\tfetchWidgetComponents,\n\tfetchMenus,\n\tcreateWidgetArea,\n\tcreateWidget,\n\tupdateWidget,\n\tdeleteWidget,\n\tdeleteWidgetArea,\n\treorderWidgets,\n\ttype WidgetArea,\n\ttype Widget,\n\ttype WidgetComponent,\n\ttype CreateWidgetInput,\n\ttype UpdateWidgetInput,\n} from \"../lib/api\";\nimport { ConfirmDialog } from \"./ConfirmDialog.js\";\nimport { DialogError, getMutationError } from \"./DialogError.js\";\nimport { PortableTextEditor } from \"./PortableTextEditor\";\n\n/** Palette item types that can be dragged into areas */\ninterface PaletteItemData {\n\tsource: \"palette\";\n\twidgetInput: CreateWidgetInput;\n\tlabel: string;\n}\n\n/** Identifies an existing widget being reordered */\ninterface ExistingWidgetData {\n\tsource: \"area\";\n\tareaName: string;\n}\n\ntype DragItemData = PaletteItemData | ExistingWidgetData;\n\nfunction isPaletteItem(data: DragItemData): data is PaletteItemData {\n\treturn data.source === \"palette\";\n}\n\n/** Built-in widget types available in the palette */\nconst BUILTIN_WIDGETS: Array<{\n\tid: string;\n\tlabel: string;\n\tdescription: string;\n\tinput: CreateWidgetInput;\n}> = [\n\t{\n\t\tid: \"palette-content\",\n\t\tlabel: \"Content Block\",\n\t\tdescription: \"Rich text content\",\n\t\tinput: { type: \"content\", title: \"Content Block\" },\n\t},\n\t{\n\t\tid: \"palette-menu\",\n\t\tlabel: \"Menu\",\n\t\tdescription: \"Display a navigation menu\",\n\t\tinput: { type: \"menu\", title: \"Menu\" },\n\t},\n];\n\nexport function Widgets() {\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\tconst [isCreateAreaOpen, setIsCreateAreaOpen] = React.useState(false);\n\tconst [createAreaError, setCreateAreaError] = React.useState<string | null>(null);\n\tconst [activeId, setActiveId] = React.useState<string | null>(null);\n\tconst [activeDragData, setActiveDragData] = React.useState<DragItemData | null>(null);\n\tconst [expandedWidgets, setExpandedWidgets] = React.useState<Set<string>>(new Set());\n\t// Track palette drag source across the full drag lifecycle (including drop animation)\n\tconst draggingFromPaletteRef = React.useRef(false);\n\n\tconst { data: areas = [], isLoading } = useQuery({\n\t\tqueryKey: [\"widget-areas\"],\n\t\tqueryFn: fetchWidgetAreas,\n\t});\n\n\tconst { data: components = [] } = useQuery({\n\t\tqueryKey: [\"widget-components\"],\n\t\tqueryFn: fetchWidgetComponents,\n\t});\n\n\tconst createAreaMutation = useMutation({\n\t\tmutationFn: createWidgetArea,\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"widget-areas\"] });\n\t\t\tsetIsCreateAreaOpen(false);\n\t\t\ttoastManager.add({ title: \"Widget area created\" });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\tsetCreateAreaError(error.message);\n\t\t},\n\t});\n\n\tconst createWidgetMutation = useMutation({\n\t\tmutationFn: ({ areaName, input }: { areaName: string; input: CreateWidgetInput }) =>\n\t\t\tcreateWidget(areaName, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"widget-areas\"] });\n\t\t\ttoastManager.add({ title: \"Widget added\" });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Error adding widget\",\n\t\t\t\tdescription: error.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleCreateArea = (e: React.FormEvent<HTMLFormElement>) => {\n\t\te.preventDefault();\n\t\tsetCreateAreaError(null);\n\t\tconst formData = new FormData(e.currentTarget);\n\t\tconst nameVal = formData.get(\"name\");\n\t\tconst labelVal = formData.get(\"label\");\n\t\tconst descVal = formData.get(\"description\");\n\t\tcreateAreaMutation.mutate({\n\t\t\tname: typeof nameVal === \"string\" ? nameVal : \"\",\n\t\t\tlabel: typeof labelVal === \"string\" ? labelVal : \"\",\n\t\t\tdescription: typeof descVal === \"string\" ? descVal : \"\",\n\t\t});\n\t};\n\n\tconst sensors = useSensors(\n\t\tuseSensor(PointerSensor, {\n\t\t\tactivationConstraint: { distance: 8 },\n\t\t}),\n\t\tuseSensor(KeyboardSensor, {\n\t\t\tcoordinateGetter: sortableKeyboardCoordinates,\n\t\t}),\n\t);\n\n\t// Custom collision detection: palette items use rectIntersection (anywhere\n\t// over the area counts) and only match area:* droppables. Existing widgets\n\t// use closestCenter for precise reorder positioning.\n\tconst collisionDetection: CollisionDetection = React.useCallback((args) => {\n\t\tconst dragData = args.active.data.current as DragItemData | undefined;\n\t\tif (dragData && isPaletteItem(dragData)) {\n\t\t\t// Only consider area droppables, use generous rect intersection\n\t\t\tconst areaContainers = args.droppableContainers.filter((c) =>\n\t\t\t\tString(c.id).startsWith(\"area:\"),\n\t\t\t);\n\t\t\treturn rectIntersection({ ...args, droppableContainers: areaContainers });\n\t\t}\n\t\treturn closestCenter(args);\n\t}, []);\n\n\tconst handleDragStart = (event: DragStartEvent) => {\n\t\tconst id = String(event.active.id);\n\t\tconst data = (event.active.data.current as DragItemData) ?? null;\n\t\tsetActiveId(id);\n\t\tsetActiveDragData(data);\n\t\tdraggingFromPaletteRef.current = data !== null && isPaletteItem(data);\n\t};\n\n\tconst reorderMutation = useMutation({\n\t\tmutationFn: ({ areaName, widgetIds }: { areaName: string; widgetIds: string[] }) =>\n\t\t\treorderWidgets(areaName, widgetIds),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"widget-areas\"] });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Error reordering widgets\",\n\t\t\t\tdescription: error.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst handleDragEnd = (event: DragEndEvent) => {\n\t\tconst { active, over } = event;\n\t\tconst dragData = active.data.current as DragItemData | undefined;\n\n\t\tsetActiveId(null);\n\t\tsetActiveDragData(null);\n\n\t\tif (!over || !dragData) return;\n\n\t\t// Case 1: Dragging from palette into an area\n\t\tif (isPaletteItem(dragData)) {\n\t\t\tconst overId = String(over.id);\n\t\t\t// The drop target is a widget area (droppable id = \"area:{name}\")\n\t\t\tif (overId.startsWith(\"area:\")) {\n\t\t\t\tconst areaName = overId.slice(5);\n\t\t\t\tcreateWidgetMutation.mutate({\n\t\t\t\t\tareaName,\n\t\t\t\t\tinput: dragData.widgetInput,\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Case 2: Reordering within an area\n\t\tif (active.id === over.id) return;\n\n\t\tconst sourceArea = areas.find((area) => area.widgets?.some((w) => w.id === active.id));\n\t\tif (!sourceArea?.widgets) return;\n\n\t\tconst oldIndex = sourceArea.widgets.findIndex((w) => w.id === active.id);\n\t\tconst newIndex = sourceArea.widgets.findIndex((w) => w.id === over.id);\n\t\tif (oldIndex === -1 || newIndex === -1) return;\n\n\t\tconst newWidgets = [...sourceArea.widgets];\n\t\tconst [movedWidget] = newWidgets.splice(oldIndex, 1);\n\t\tif (!movedWidget) return;\n\t\tnewWidgets.splice(newIndex, 0, movedWidget);\n\n\t\treorderMutation.mutate({\n\t\t\tareaName: sourceArea.name,\n\t\t\twidgetIds: newWidgets.map((w) => w.id),\n\t\t});\n\t};\n\n\tconst toggleWidget = (widgetId: string) => {\n\t\tsetExpandedWidgets((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(widgetId)) {\n\t\t\t\tnext.delete(widgetId);\n\t\t\t} else {\n\t\t\t\tnext.add(widgetId);\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t};\n\n\t// Build the palette label for the drag overlay\n\tconst activePaletteLabel =\n\t\tactiveDragData && isPaletteItem(activeDragData) ? activeDragData.label : null;\n\t// Find the existing widget being dragged for overlay\n\tconst activeWidget =\n\t\tactiveId && activeDragData && !isPaletteItem(activeDragData)\n\t\t\t? areas.flatMap((a) => a.widgets ?? []).find((w) => w.id === activeId)\n\t\t\t: null;\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center h-64\">\n\t\t\t\t<div className=\"text-kumo-subtle\">Loading widgets...</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<DndContext\n\t\t\tsensors={sensors}\n\t\t\tcollisionDetection={collisionDetection}\n\t\t\tonDragStart={handleDragStart}\n\t\t\tonDragEnd={handleDragEnd}\n\t\t>\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h1 className=\"text-3xl font-bold\">Widgets</h1>\n\t\t\t\t\t\t<p className=\"text-kumo-subtle\">Manage content widgets in your widget areas</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Dialog.Root\n\t\t\t\t\t\topen={isCreateAreaOpen}\n\t\t\t\t\t\tonOpenChange={(open) => {\n\t\t\t\t\t\t\tsetIsCreateAreaOpen(open);\n\t\t\t\t\t\t\tif (!open) setCreateAreaError(null);\n\t\t\t\t\t\t}}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Dialog.Trigger\n\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t<Button {...props} icon={<Plus />}>\n\t\t\t\t\t\t\t\t\tAdd Widget Area\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<Dialog className=\"p-6\" size=\"lg\">\n\t\t\t\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t\t\tCreate Widget Area\n\t\t\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<form onSubmit={handleCreateArea} className=\"space-y-4\">\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Name\"\n\t\t\t\t\t\t\t\t\tname=\"name\"\n\t\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\t\tplaceholder=\"sidebar\"\n\t\t\t\t\t\t\t\t\tpattern=\"[a-z0-9-]+\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<Input label=\"Label\" name=\"label\" required placeholder=\"Main Sidebar\" />\n\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\tlabel=\"Description\"\n\t\t\t\t\t\t\t\t\tname=\"description\"\n\t\t\t\t\t\t\t\t\tplaceholder=\"Appears on posts and pages\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<DialogError\n\t\t\t\t\t\t\t\t\tmessage={createAreaError || getMutationError(createAreaMutation.error)}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div className=\"flex justify-end gap-2\">\n\t\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\t\tonClick={() => setIsCreateAreaOpen(false)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t\t<Button type=\"submit\" disabled={createAreaMutation.isPending}>\n\t\t\t\t\t\t\t\t\t\tCreate\n\t\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</form>\n\t\t\t\t\t\t</Dialog>\n\t\t\t\t\t</Dialog.Root>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"grid grid-cols-12 gap-6\">\n\t\t\t\t\t{/* Available Widgets (draggable palette) */}\n\t\t\t\t\t<div className=\"col-span-4\">\n\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6 space-y-4\">\n\t\t\t\t\t\t\t<h2 className=\"text-xl font-semibold\">Available Widgets</h2>\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">Drag widgets into an area to add them</p>\n\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t{BUILTIN_WIDGETS.map((item) => (\n\t\t\t\t\t\t\t\t\t<DraggablePaletteItem\n\t\t\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\t\t\tid={item.id}\n\t\t\t\t\t\t\t\t\t\tlabel={item.label}\n\t\t\t\t\t\t\t\t\t\tdescription={item.description}\n\t\t\t\t\t\t\t\t\t\twidgetInput={item.input}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t{components.map((comp) => (\n\t\t\t\t\t\t\t\t\t<DraggablePaletteItem\n\t\t\t\t\t\t\t\t\t\tkey={`palette-comp-${comp.id}`}\n\t\t\t\t\t\t\t\t\t\tid={`palette-comp-${comp.id}`}\n\t\t\t\t\t\t\t\t\t\tlabel={comp.label}\n\t\t\t\t\t\t\t\t\t\tdescription={comp.description}\n\t\t\t\t\t\t\t\t\t\twidgetInput={{\n\t\t\t\t\t\t\t\t\t\t\ttype: \"component\",\n\t\t\t\t\t\t\t\t\t\t\ttitle: comp.label,\n\t\t\t\t\t\t\t\t\t\t\tcomponentId: comp.id,\n\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Widget Areas (droppable + sortable) */}\n\t\t\t\t\t<div className=\"col-span-8 space-y-4\">\n\t\t\t\t\t\t{areas.length === 0 ? (\n\t\t\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t\t\t\t<p className=\"text-kumo-subtle\">No widget areas yet. Create one to get started.</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\tareas.map((area) => (\n\t\t\t\t\t\t\t\t<WidgetAreaPanel\n\t\t\t\t\t\t\t\t\tkey={area.id}\n\t\t\t\t\t\t\t\t\tarea={area}\n\t\t\t\t\t\t\t\t\texpandedWidgets={expandedWidgets}\n\t\t\t\t\t\t\t\t\tonToggleWidget={toggleWidget}\n\t\t\t\t\t\t\t\t\tisDraggingPalette={activeDragData !== null && isPaletteItem(activeDragData)}\n\t\t\t\t\t\t\t\t\tcomponents={components}\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Drag overlay — no drop animation for palette items (source stays in place).\n\t\t\t Use ref because state is cleared in handleDragEnd before animation runs. */}\n\t\t\t<DragOverlay dropAnimation={draggingFromPaletteRef.current ? null : undefined}>\n\t\t\t\t{activePaletteLabel ? (\n\t\t\t\t\t<div className=\"rounded border bg-kumo-base p-3 shadow-lg opacity-90\">\n\t\t\t\t\t\t<div className=\"font-medium\">{activePaletteLabel}</div>\n\t\t\t\t\t</div>\n\t\t\t\t) : activeWidget ? (\n\t\t\t\t\t<div className=\"rounded border bg-kumo-base p-3 shadow-lg opacity-90\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<DotsSixVertical className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<span className=\"font-medium\">{activeWidget.title || \"Untitled Widget\"}</span>\n\t\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">({activeWidget.type})</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t) : null}\n\t\t\t</DragOverlay>\n\t\t</DndContext>\n\t);\n}\n\n/** A draggable item in the available widgets palette */\nfunction DraggablePaletteItem({\n\tid,\n\tlabel,\n\tdescription,\n\twidgetInput,\n}: {\n\tid: string;\n\tlabel: string;\n\tdescription?: string;\n\twidgetInput: CreateWidgetInput;\n}) {\n\tconst { attributes, listeners, setNodeRef, isDragging } = useDraggable({\n\t\tid,\n\t\tdata: {\n\t\t\tsource: \"palette\",\n\t\t\twidgetInput,\n\t\t\tlabel,\n\t\t} satisfies PaletteItemData,\n\t});\n\n\treturn (\n\t\t<div\n\t\t\tref={setNodeRef}\n\t\t\t{...attributes}\n\t\t\t{...listeners}\n\t\t\tclassName={`p-3 rounded border cursor-grab active:cursor-grabbing select-none ${\n\t\t\t\tisDragging ? \"opacity-50\" : \"hover:bg-kumo-tint\"\n\t\t\t}`}\n\t\t>\n\t\t\t<div className=\"font-medium\">{label}</div>\n\t\t\t{description && <div className=\"text-sm text-kumo-subtle\">{description}</div>}\n\t\t</div>\n\t);\n}\n\nfunction WidgetAreaPanel({\n\tarea,\n\texpandedWidgets,\n\tonToggleWidget,\n\tisDraggingPalette,\n\tcomponents,\n}: {\n\tarea: WidgetArea;\n\texpandedWidgets: Set<string>;\n\tonToggleWidget: (id: string) => void;\n\tisDraggingPalette: boolean;\n\tcomponents: WidgetComponent[];\n}) {\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\tconst [deleteAreaName, setDeleteAreaName] = React.useState<string | null>(null);\n\n\t// Make the area a droppable target for palette items\n\tconst { setNodeRef: setDropRef, isOver } = useDroppable({\n\t\tid: `area:${area.name}`,\n\t});\n\n\tconst deleteAreaMutation = useMutation({\n\t\tmutationFn: deleteWidgetArea,\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"widget-areas\"] });\n\t\t\tsetDeleteAreaName(null);\n\t\t\ttoastManager.add({ title: \"Widget area deleted\" });\n\t\t},\n\t});\n\n\tconst hasWidgets = area.widgets && area.widgets.length > 0;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={`rounded-lg border bg-kumo-base transition-colors ${isOver ? \"ring-2 ring-kumo-brand\" : \"\"}`}\n\t\t>\n\t\t\t<div className=\"p-4 border-b flex items-center justify-between\">\n\t\t\t\t<div>\n\t\t\t\t\t<h3 className=\"text-lg font-semibold\">{area.label}</h3>\n\t\t\t\t\t{area.description && <p className=\"text-sm text-kumo-subtle\">{area.description}</p>}\n\t\t\t\t</div>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tonClick={() => setDeleteAreaName(area.name)}\n\t\t\t\t\taria-label={`Delete ${area.label} widget area`}\n\t\t\t\t>\n\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t<div ref={setDropRef} className=\"p-4 space-y-2 min-h-[80px]\">\n\t\t\t\t{hasWidgets ? (\n\t\t\t\t\t<SortableContext\n\t\t\t\t\t\titems={area.widgets!.map((w) => w.id)}\n\t\t\t\t\t\tstrategy={verticalListSortingStrategy}\n\t\t\t\t\t>\n\t\t\t\t\t\t{area.widgets!.map((widget) => (\n\t\t\t\t\t\t\t<WidgetItem\n\t\t\t\t\t\t\t\tkey={widget.id}\n\t\t\t\t\t\t\t\twidget={widget}\n\t\t\t\t\t\t\t\tareaName={area.name}\n\t\t\t\t\t\t\t\tisExpanded={expandedWidgets.has(widget.id)}\n\t\t\t\t\t\t\t\tonToggle={() => onToggleWidget(widget.id)}\n\t\t\t\t\t\t\t\tcomponents={components}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</SortableContext>\n\t\t\t\t) : null}\n\t\t\t\t{/* Drop zone hint — shown when dragging a palette item */}\n\t\t\t\t{isDraggingPalette && (\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={`text-center py-4 rounded border-2 border-dashed transition-colors ${\n\t\t\t\t\t\t\tisOver\n\t\t\t\t\t\t\t\t? \"border-kumo-brand bg-kumo-brand/5 text-kumo-brand\"\n\t\t\t\t\t\t\t\t: \"border-kumo-subtle/30 text-kumo-subtle\"\n\t\t\t\t\t\t}`}\n\t\t\t\t\t>\n\t\t\t\t\t\t{isOver ? \"Drop to add widget\" : \"Drag here to add\"}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t\t{!hasWidgets && !isDraggingPalette && (\n\t\t\t\t\t<div className=\"text-center py-8 text-kumo-subtle\">Drag widgets here to add them</div>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={deleteAreaName === area.name}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetDeleteAreaName(null);\n\t\t\t\t\tdeleteAreaMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Delete Widget Area?\"\n\t\t\t\tdescription=\"This will delete the widget area and all its widgets. This action cannot be undone.\"\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={deleteAreaMutation.isPending}\n\t\t\t\terror={deleteAreaMutation.error}\n\t\t\t\tonConfirm={() => deleteAreaMutation.mutate(area.name)}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n\nfunction WidgetItem({\n\twidget,\n\tareaName,\n\tisExpanded,\n\tonToggle,\n\tcomponents,\n}: {\n\twidget: Widget;\n\tareaName: string;\n\tisExpanded: boolean;\n\tonToggle: () => void;\n\tcomponents: WidgetComponent[];\n}) {\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\tconst { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({\n\t\tid: widget.id,\n\t\tdata: {\n\t\t\tsource: \"area\",\n\t\t\tareaName,\n\t\t} satisfies ExistingWidgetData,\n\t});\n\n\tconst style = {\n\t\ttransform: CSS.Transform.toString(transform),\n\t\ttransition,\n\t};\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: () => deleteWidget(areaName, widget.id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"widget-areas\"] });\n\t\t\ttoastManager.add({ title: \"Widget deleted\" });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Error\",\n\t\t\t\tdescription: error.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: (input: UpdateWidgetInput) => updateWidget(areaName, widget.id, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"widget-areas\"] });\n\t\t\ttoastManager.add({ title: \"Widget updated\" });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Error updating widget\",\n\t\t\t\tdescription: error.message,\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\treturn (\n\t\t<div\n\t\t\tref={setNodeRef}\n\t\t\tstyle={style}\n\t\t\tclassName={`rounded border bg-kumo-base p-3 ${isDragging ? \"opacity-50\" : \"\"}`}\n\t\t>\n\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t<button\n\t\t\t\t\t{...attributes}\n\t\t\t\t\t{...listeners}\n\t\t\t\t\tclassName=\"cursor-grab active:cursor-grabbing\"\n\t\t\t\t\taria-label={`Drag to reorder ${widget.title || \"widget\"}`}\n\t\t\t\t>\n\t\t\t\t\t<DotsSixVertical className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t</button>\n\t\t\t\t<button onClick={onToggle} className=\"flex-1 text-left\" aria-expanded={isExpanded}>\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t{isExpanded ? <CaretDown className=\"h-4 w-4\" /> : <CaretRight className=\"h-4 w-4\" />}\n\t\t\t\t\t\t<span className=\"font-medium\">{widget.title || \"Untitled Widget\"}</span>\n\t\t\t\t\t\t<span className=\"text-xs text-kumo-subtle\">({widget.type})</span>\n\t\t\t\t\t</div>\n\t\t\t\t</button>\n\t\t\t\t<Button\n\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\tonClick={() => deleteMutation.mutate()}\n\t\t\t\t\taria-label={`Delete ${widget.title || \"widget\"}`}\n\t\t\t\t>\n\t\t\t\t\t<Trash className=\"h-4 w-4\" />\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t{isExpanded && (\n\t\t\t\t<WidgetEditor\n\t\t\t\t\twidget={widget}\n\t\t\t\t\tcomponents={components}\n\t\t\t\t\tonSave={(input) => updateMutation.mutate(input)}\n\t\t\t\t\tisSaving={updateMutation.isPending}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n/** Inline editor form for a widget, rendered when the widget is expanded */\nfunction WidgetEditor({\n\twidget,\n\tcomponents,\n\tonSave,\n\tisSaving,\n}: {\n\twidget: Widget;\n\tcomponents: WidgetComponent[];\n\tonSave: (input: UpdateWidgetInput) => void;\n\tisSaving: boolean;\n}) {\n\tconst [title, setTitle] = React.useState(widget.title ?? \"\");\n\tconst [content, setContent] = React.useState<unknown[]>(\n\t\tArray.isArray(widget.content) ? widget.content : [],\n\t);\n\tconst [menuName, setMenuName] = React.useState(widget.menuName ?? \"\");\n\tconst [componentId, setComponentId] = React.useState(widget.componentId ?? \"\");\n\tconst [componentProps, setComponentProps] = React.useState<Record<string, unknown>>(\n\t\twidget.componentProps ?? {},\n\t);\n\n\tconst { data: menus = [] } = useQuery({\n\t\tqueryKey: [\"menus\"],\n\t\tqueryFn: fetchMenus,\n\t\tenabled: widget.type === \"menu\",\n\t});\n\n\tconst selectedComponent = components.find((c) => c.id === componentId);\n\n\tconst handleSave = () => {\n\t\tconst input: UpdateWidgetInput = { title };\n\t\tif (widget.type === \"content\") {\n\t\t\tinput.content = content;\n\t\t} else if (widget.type === \"menu\") {\n\t\t\tinput.menuName = menuName;\n\t\t} else if (widget.type === \"component\") {\n\t\t\tinput.componentId = componentId;\n\t\t\tinput.componentProps = componentProps;\n\t\t}\n\t\tonSave(input);\n\t};\n\n\treturn (\n\t\t<div className=\"mt-3 p-3 bg-kumo-tint rounded space-y-4\">\n\t\t\t<Input\n\t\t\t\tlabel=\"Title\"\n\t\t\t\tvalue={title}\n\t\t\t\tonChange={(e) => setTitle(e.target.value)}\n\t\t\t\tplaceholder=\"Widget title\"\n\t\t\t/>\n\n\t\t\t{widget.type === \"content\" && (\n\t\t\t\t<div>\n\t\t\t\t\t<Label className=\"text-sm font-medium mb-2 block\">Content</Label>\n\t\t\t\t\t<PortableTextEditor\n\t\t\t\t\t\tvalue={content as Parameters<typeof PortableTextEditor>[0][\"value\"]}\n\t\t\t\t\t\tonChange={(value) => setContent(value as unknown[])}\n\t\t\t\t\t\tminimal\n\t\t\t\t\t\tplaceholder=\"Write widget content...\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{widget.type === \"menu\" && (\n\t\t\t\t<Select\n\t\t\t\t\tlabel=\"Menu\"\n\t\t\t\t\tvalue={menuName}\n\t\t\t\t\tonValueChange={(v) => setMenuName(v ?? \"\")}\n\t\t\t\t\titems={Object.fromEntries(menus.map((m) => [m.name, m.label || m.name]))}\n\t\t\t\t>\n\t\t\t\t\t<Select.Option value=\"\">Select a menu...</Select.Option>\n\t\t\t\t\t{menus.map((m) => (\n\t\t\t\t\t\t<Select.Option key={m.name} value={m.name}>\n\t\t\t\t\t\t\t{m.label || m.name}\n\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t))}\n\t\t\t\t</Select>\n\t\t\t)}\n\n\t\t\t{widget.type === \"component\" && (\n\t\t\t\t<>\n\t\t\t\t\t<Select\n\t\t\t\t\t\tlabel=\"Component\"\n\t\t\t\t\t\tvalue={componentId}\n\t\t\t\t\t\tonValueChange={(v) => {\n\t\t\t\t\t\t\tsetComponentId(v ?? \"\");\n\t\t\t\t\t\t\t// Reset props when component changes\n\t\t\t\t\t\t\tif (v !== componentId) {\n\t\t\t\t\t\t\t\tconst comp = components.find((c) => c.id === v);\n\t\t\t\t\t\t\t\tif (comp) {\n\t\t\t\t\t\t\t\t\tconst defaults: Record<string, unknown> = {};\n\t\t\t\t\t\t\t\t\tfor (const [key, def] of Object.entries(comp.props)) {\n\t\t\t\t\t\t\t\t\t\tdefaults[key] = def.default ?? \"\";\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tsetComponentProps(defaults);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tsetComponentProps({});\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}}\n\t\t\t\t\t\titems={Object.fromEntries(components.map((c) => [c.id, c.label]))}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Select.Option value=\"\">Select a component...</Select.Option>\n\t\t\t\t\t\t{components.map((c) => (\n\t\t\t\t\t\t\t<Select.Option key={c.id} value={c.id}>\n\t\t\t\t\t\t\t\t{c.label}\n\t\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</Select>\n\n\t\t\t\t\t{selectedComponent &&\n\t\t\t\t\t\tObject.entries(selectedComponent.props).map(([key, def]) => (\n\t\t\t\t\t\t\t<ComponentPropField\n\t\t\t\t\t\t\t\tkey={key}\n\t\t\t\t\t\t\t\tpropKey={key}\n\t\t\t\t\t\t\t\tdef={def}\n\t\t\t\t\t\t\t\tvalue={componentProps[key] ?? def.default ?? \"\"}\n\t\t\t\t\t\t\t\tonChange={(v) => setComponentProps((prev) => ({ ...prev, [key]: v }))}\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t</>\n\t\t\t)}\n\n\t\t\t<div className=\"flex justify-end\">\n\t\t\t\t<Button size=\"sm\" onClick={handleSave} disabled={isSaving}>\n\t\t\t\t\t{isSaving ? \"Saving...\" : \"Save\"}\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n/** Renders a single prop field for a component widget based on PropDef type */\nfunction ComponentPropField({\n\tdef,\n\tvalue,\n\tonChange,\n}: {\n\tpropKey: string;\n\tdef: WidgetComponent[\"props\"][string];\n\tvalue: unknown;\n\tonChange: (value: unknown) => void;\n}) {\n\tswitch (def.type) {\n\t\tcase \"string\":\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={def.label}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => onChange(e.target.value)}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"number\":\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={def.label}\n\t\t\t\t\ttype=\"number\"\n\t\t\t\t\tvalue={typeof value === \"number\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => onChange(Number(e.target.value))}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"boolean\":\n\t\t\treturn (\n\t\t\t\t<Switch\n\t\t\t\t\tlabel={def.label}\n\t\t\t\t\tchecked={typeof value === \"boolean\" ? value : false}\n\t\t\t\t\tonCheckedChange={onChange}\n\t\t\t\t/>\n\t\t\t);\n\t\tcase \"select\": {\n\t\t\tconst items: Record<string, string> = {};\n\t\t\tfor (const opt of def.options ?? []) {\n\t\t\t\titems[opt.value] = opt.label;\n\t\t\t}\n\t\t\treturn (\n\t\t\t\t<Select\n\t\t\t\t\tlabel={def.label}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonValueChange={(v) => onChange(v ?? \"\")}\n\t\t\t\t\titems={items}\n\t\t\t\t>\n\t\t\t\t\t{def.options?.map((opt) => (\n\t\t\t\t\t\t<Select.Option key={opt.value} value={opt.value}>\n\t\t\t\t\t\t\t{opt.label}\n\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t))}\n\t\t\t\t</Select>\n\t\t\t);\n\t\t}\n\t\tdefault:\n\t\t\treturn (\n\t\t\t\t<Input\n\t\t\t\t\tlabel={def.label}\n\t\t\t\t\tvalue={typeof value === \"string\" ? value : \"\"}\n\t\t\t\t\tonChange={(e) => onChange(e.target.value)}\n\t\t\t\t/>\n\t\t\t);\n\t}\n}\n","import { Badge, Button, Input, LinkButton, Loader, buttonVariants } from \"@cloudflare/kumo\";\nimport {\n\tUpload,\n\tCheck,\n\tX,\n\tWarning,\n\tWarningCircle,\n\tPlus,\n\tDatabase,\n\tFileText,\n\tCaretDown,\n\tCaretRight,\n\tImage,\n\tDownloadSimple,\n\tGlobe,\n\tArrowSquareOut,\n\tList,\n\tGear,\n\tSparkle,\n\tUser,\n} from \"@phosphor-icons/react\";\nimport { useMutation } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport {\n\tanalyzeWxr,\n\tprepareWxrImport,\n\texecuteWxrImport,\n\timportWxrMedia,\n\trewriteContentUrls,\n\tprobeImportUrl,\n\tanalyzeWpPluginSite,\n\texecuteWpPluginImport,\n\tfetchUsers,\n\ttype WxrAnalysis,\n\ttype WpPluginAnalysis,\n\ttype PostTypeAnalysis,\n\ttype ImportConfig,\n\ttype ImportResult,\n\ttype PrepareResult,\n\ttype MediaImportResult,\n\ttype MediaImportProgress,\n\ttype RewriteUrlsResult,\n\ttype AttachmentInfo,\n\ttype ProbeResult,\n\ttype AuthorMapping,\n\ttype UserListItem,\n} from \"../lib/api\";\nimport { cn } from \"../lib/utils\";\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst TRAILING_SLASH_REGEX = /\\/$/;\nconst WHITESPACE_REGEX = /\\s/g;\n\n// ============================================================================\n// Types\n// ============================================================================\n\ntype ImportStep =\n\t| \"choose\" // New: choose how to import (URL or file)\n\t| \"probing\" // New: probing URL\n\t| \"probe-result\" // New: showing probe results\n\t| \"plugin-auth\" // Authenticating with WordPress plugin\n\t| \"analyzing-plugin\" // Analyzing WordPress plugin site\n\t| \"upload\"\n\t| \"review\"\n\t| \"authors\" // Author mapping step\n\t| \"preparing\"\n\t| \"importing\"\n\t| \"media\"\n\t| \"importing-media\"\n\t| \"rewriting\"\n\t| \"complete\";\n\n/** Import source - either WXR file or Plugin API */\ntype ImportSource =\n\t| { type: \"wxr\"; file: File }\n\t| { type: \"wordpress-plugin\"; url: string; token: string };\n\ninterface PostTypeSelection {\n\tenabled: boolean;\n\tcollection: string;\n}\n\n/** Union type for analysis results */\ntype ImportAnalysis = WxrAnalysis | WpPluginAnalysis;\n\nexport function WordPressImport() {\n\tconst [step, setStep] = React.useState<ImportStep>(\"choose\");\n\tconst [urlInput, setUrlInput] = React.useState(\"\");\n\tconst [probeResult, setProbeResult] = React.useState<ProbeResult | null>(null);\n\tconst [importSource, setImportSource] = React.useState<ImportSource | null>(null);\n\tconst [_file, setFile] = React.useState<File | null>(null);\n\tconst [analysis, setAnalysis] = React.useState<ImportAnalysis | null>(null);\n\t// Plugin auth state\n\tconst [pluginUsername, setPluginUsername] = React.useState(\"\");\n\tconst [pluginPassword, setPluginPassword] = React.useState(\"\");\n\tconst [selections, setSelections] = React.useState<Record<string, PostTypeSelection>>({});\n\tconst [prepareResult, setPrepareResult] = React.useState<PrepareResult | null>(null);\n\tconst [result, setResult] = React.useState<ImportResult | null>(null);\n\tconst [expandedTypes, setExpandedTypes] = React.useState<Set<string>>(new Set());\n\tconst [prepareError, setPrepareError] = React.useState<string | null>(null);\n\tconst [importError, setImportError] = React.useState<string | null>(null);\n\tconst [mediaResult, setMediaResult] = React.useState<MediaImportResult | null>(null);\n\tconst [rewriteResult, setRewriteResult] = React.useState<RewriteUrlsResult | null>(null);\n\tconst [mediaError, setMediaError] = React.useState<string | null>(null);\n\tconst [skipMedia, setSkipMedia] = React.useState(false);\n\tconst [mediaProgress, setMediaProgress] = React.useState<MediaImportProgress | null>(null);\n\n\t// New state for import options\n\tconst [importMenus, setImportMenus] = React.useState(true);\n\tconst [importSiteTitle, setImportSiteTitle] = React.useState(true);\n\tconst [importLogo, setImportLogo] = React.useState(true);\n\tconst [importSeo, setImportSeo] = React.useState(false);\n\n\t// Author mapping state\n\tconst [authorMappings, setAuthorMappings] = React.useState<AuthorMapping[]>([]);\n\tconst [emdashUsers, setEmDashUsers] = React.useState<UserListItem[]>([]);\n\n\t// Initialize author mappings from analysis, auto-matching by email\n\tconst initializeAuthorMappings = React.useCallback(\n\t\t(importAnalysis: ImportAnalysis, users: UserListItem[]) => {\n\t\t\tconst mappings: AuthorMapping[] = importAnalysis.authors.map((author) => {\n\t\t\t\t// Try to match by email (case-insensitive)\n\t\t\t\tconst matchedUser = author.email\n\t\t\t\t\t? users.find((u) => u.email.toLowerCase() === author.email?.toLowerCase())\n\t\t\t\t\t: undefined;\n\n\t\t\t\treturn {\n\t\t\t\t\twpLogin: author.login || author.displayName || \"unknown\",\n\t\t\t\t\twpDisplayName: author.displayName || author.login || \"Unknown\",\n\t\t\t\t\twpEmail: author.email,\n\t\t\t\t\temdashUserId: matchedUser?.id ?? null,\n\t\t\t\t\tpostCount: author.postCount,\n\t\t\t\t};\n\t\t\t});\n\t\t\tsetAuthorMappings(mappings);\n\t\t},\n\t\t[],\n\t);\n\n\t// Check for OAuth callback on mount\n\tReact.useEffect(() => {\n\t\tconst params = new URLSearchParams(window.location.search);\n\t\tconst authStatus = params.get(\"auth\");\n\t\tconst error = params.get(\"error\");\n\n\t\tif (error === \"auth_rejected\") {\n\t\t\tsetImportError(\"WordPress authorization was rejected\");\n\t\t\tsetStep(\"probe-result\");\n\t\t\t// Clean up URL\n\t\t\twindow.history.replaceState({}, \"\", window.location.pathname);\n\t\t\treturn;\n\t\t}\n\n\t\tif (authStatus === \"success\") {\n\t\t\t// Get credentials from cookie\n\t\t\tconst cookie = document.cookie.split(\"; \").find((row) => row.startsWith(\"emdash_wp_auth=\"));\n\n\t\t\tif (cookie) {\n\t\t\t\ttry {\n\t\t\t\t\tconst encoded = cookie.split(\"=\")[1] ?? \"\";\n\t\t\t\t\t// URL decode first (cookie values may be URL-encoded), then base64 decode\n\t\t\t\t\tconst urlDecoded = decodeURIComponent(encoded);\n\t\t\t\t\tconst authData = JSON.parse(atob(urlDecoded));\n\n\t\t\t\t\t// Check timestamp (5 minute expiry)\n\t\t\t\t\tif (Date.now() - authData.timestamp < 5 * 60 * 1000) {\n\t\t\t\t\t\t// Set up import source and start analyzing\n\t\t\t\t\t\tsetImportSource({\n\t\t\t\t\t\t\ttype: \"wordpress-plugin\",\n\t\t\t\t\t\t\turl: authData.siteUrl,\n\t\t\t\t\t\t\ttoken: authData.token,\n\t\t\t\t\t\t});\n\t\t\t\t\t\tsetUrlInput(authData.siteUrl);\n\n\t\t\t\t\t\t// Clear the cookie\n\t\t\t\t\t\tdocument.cookie = \"emdash_wp_auth=; path=/_emdash/; max-age=0\";\n\n\t\t\t\t\t\t// Start analyzing\n\t\t\t\t\t\tsetStep(\"analyzing-plugin\");\n\t\t\t\t\t\twpPluginAnalyzeMutation.mutate({\n\t\t\t\t\t\t\turl: authData.siteUrl,\n\t\t\t\t\t\t\ttoken: authData.token,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (e) {\n\t\t\t\t\tconsole.error(\"Failed to parse auth cookie:\", e);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Clean up URL\n\t\t\twindow.history.replaceState({}, \"\", window.location.pathname);\n\t\t}\n\t}, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n\t// Probe mutation\n\tconst probeMutation = useMutation({\n\t\tmutationFn: probeImportUrl,\n\t\tonSuccess: (data) => {\n\t\t\tsetProbeResult(data);\n\t\t\tsetStep(\"probe-result\");\n\t\t},\n\t\tonError: () => {\n\t\t\t// On error, show probe result step with no matches\n\t\t\tsetProbeResult({\n\t\t\t\turl: urlInput,\n\t\t\t\tisWordPress: false,\n\t\t\t\tbestMatch: null,\n\t\t\t\tallMatches: [],\n\t\t\t});\n\t\t\tsetStep(\"probe-result\");\n\t\t},\n\t});\n\n\t// Analyze mutation\n\tconst analyzeMutation = useMutation({\n\t\tmutationFn: analyzeWxr,\n\t\tonSuccess: async (data) => {\n\t\t\tsetAnalysis(data);\n\t\t\t// Initialize selections from analysis\n\t\t\tconst initialSelections: Record<string, PostTypeSelection> = {};\n\t\t\tfor (const pt of data.postTypes) {\n\t\t\t\tinitialSelections[pt.name] = {\n\t\t\t\t\tenabled: pt.schemaStatus.canImport,\n\t\t\t\t\tcollection: pt.suggestedCollection,\n\t\t\t\t};\n\t\t\t}\n\t\t\tsetSelections(initialSelections);\n\t\t\t// Initialize menu import state based on analysis\n\t\t\tif (\"navMenus\" in data && data.navMenus && data.navMenus.length > 0) {\n\t\t\t\tsetImportMenus(true);\n\t\t\t}\n\t\t\t// Fetch EmDash users for author mapping\n\t\t\ttry {\n\t\t\t\tconst usersResult = await fetchUsers({ limit: 100 });\n\t\t\t\tsetEmDashUsers(usersResult.items);\n\t\t\t\tinitializeAuthorMappings(data, usersResult.items);\n\t\t\t} catch {\n\t\t\t\t// If user fetch fails, continue without auto-matching\n\t\t\t\tinitializeAuthorMappings(data, []);\n\t\t\t}\n\t\t\tsetStep(\"review\");\n\t\t},\n\t});\n\n\t// Prepare mutation (create collections/fields)\n\tconst prepareMutation = useMutation({\n\t\tmutationFn: prepareWxrImport,\n\t\tonSuccess: (data) => {\n\t\t\tsetPrepareError(null);\n\t\t\tsetPrepareResult(data);\n\t\t\tif (data.success) {\n\t\t\t\texecuteImport();\n\t\t\t} else {\n\t\t\t\tsetStep(\"review\");\n\t\t\t}\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetPrepareError(error instanceof Error ? error.message : \"Failed to prepare import\");\n\t\t\tsetStep(\"review\");\n\t\t},\n\t});\n\n\t// Import mutation\n\tconst importMutation = useMutation({\n\t\tmutationFn: ({ file, config }: { file: File; config: ImportConfig }) =>\n\t\t\texecuteWxrImport(file, config),\n\t\tonSuccess: (data) => {\n\t\t\tsetImportError(null);\n\t\t\tsetResult(data);\n\t\t\tif (analysis && analysis.attachments.count > 0) {\n\t\t\t\tsetStep(\"media\");\n\t\t\t} else {\n\t\t\t\tsetStep(\"complete\");\n\t\t\t}\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetImportError(error instanceof Error ? error.message : \"Failed to execute import\");\n\t\t\tsetStep(\"review\");\n\t\t},\n\t});\n\n\t// Media import mutation\n\tconst mediaMutation = useMutation({\n\t\tmutationFn: (attachments: AttachmentInfo[]) =>\n\t\t\timportWxrMedia(attachments, (progress) => {\n\t\t\t\tsetMediaProgress(progress);\n\t\t\t}),\n\t\tonSuccess: (data) => {\n\t\t\tsetMediaError(null);\n\t\t\tsetMediaProgress(null);\n\t\t\tsetMediaResult(data);\n\t\t\tif (Object.keys(data.urlMap).length > 0) {\n\t\t\t\tsetStep(\"rewriting\");\n\t\t\t\trewriteMutation.mutate(data.urlMap);\n\t\t\t} else {\n\t\t\t\tsetStep(\"complete\");\n\t\t\t}\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetMediaProgress(null);\n\t\t\tsetMediaError(error instanceof Error ? error.message : \"Failed to import media\");\n\t\t\tsetStep(\"media\");\n\t\t},\n\t});\n\n\t// URL rewrite mutation\n\tconst rewriteMutation = useMutation({\n\t\tmutationFn: (urlMap: Record<string, string>) => rewriteContentUrls(urlMap),\n\t\tonSuccess: (data) => {\n\t\t\tsetRewriteResult(data);\n\t\t\tsetStep(\"complete\");\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetMediaError(error instanceof Error ? error.message : \"Failed to rewrite URLs\");\n\t\t\tsetStep(\"complete\");\n\t\t},\n\t});\n\n\t// WordPress Plugin analyze mutation\n\tconst wpPluginAnalyzeMutation = useMutation({\n\t\tmutationFn: ({ url, token }: { url: string; token: string }) => analyzeWpPluginSite(url, token),\n\t\tonSuccess: async (data) => {\n\t\t\tsetAnalysis(data);\n\t\t\t// Initialize selections from analysis\n\t\t\tconst initialSelections: Record<string, PostTypeSelection> = {};\n\t\t\tfor (const pt of data.postTypes) {\n\t\t\t\tinitialSelections[pt.name] = {\n\t\t\t\t\tenabled: pt.schemaStatus.canImport,\n\t\t\t\t\tcollection: pt.suggestedCollection,\n\t\t\t\t};\n\t\t\t}\n\t\t\tsetSelections(initialSelections);\n\t\t\t// Initialize menu import state based on analysis\n\t\t\tif (\"navMenus\" in data && data.navMenus && data.navMenus.length > 0) {\n\t\t\t\tsetImportMenus(true);\n\t\t\t}\n\t\t\t// Fetch EmDash users for author mapping\n\t\t\ttry {\n\t\t\t\tconst usersResult = await fetchUsers({ limit: 100 });\n\t\t\t\tsetEmDashUsers(usersResult.items);\n\t\t\t\tinitializeAuthorMappings(data, usersResult.items);\n\t\t\t} catch {\n\t\t\t\t// If user fetch fails, continue without auto-matching\n\t\t\t\tinitializeAuthorMappings(data, []);\n\t\t\t}\n\t\t\tsetStep(\"review\");\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetImportError(error instanceof Error ? error.message : \"Failed to analyze WordPress site\");\n\t\t\tsetStep(\"plugin-auth\");\n\t\t},\n\t});\n\n\t// WordPress Plugin import mutation\n\tconst wpPluginImportMutation = useMutation({\n\t\tmutationFn: ({ url, token, config }: { url: string; token: string; config: ImportConfig }) =>\n\t\t\texecuteWpPluginImport(url, token, config),\n\t\tonSuccess: (data) => {\n\t\t\tsetImportError(null);\n\t\t\tsetResult(data);\n\t\t\tif (analysis && analysis.attachments.count > 0) {\n\t\t\t\tsetStep(\"media\");\n\t\t\t} else {\n\t\t\t\tsetStep(\"complete\");\n\t\t\t}\n\t\t},\n\t\tonError: (error) => {\n\t\t\tsetImportError(error instanceof Error ? error.message : \"Failed to import from WordPress\");\n\t\t\tsetStep(\"review\");\n\t\t},\n\t});\n\n\tconst handleProbeUrl = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (!urlInput.trim()) return;\n\t\tsetStep(\"probing\");\n\t\tprobeMutation.mutate(urlInput.trim());\n\t};\n\n\tconst handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n\t\tconst selectedFile = e.target.files?.[0];\n\t\tif (selectedFile) {\n\t\t\tsetFile(selectedFile);\n\t\t\tsetImportSource({ type: \"wxr\", file: selectedFile });\n\t\t\tsetStep(\"upload\");\n\t\t\tanalyzeMutation.mutate(selectedFile);\n\t\t}\n\t};\n\n\tconst handleDrop = (e: React.DragEvent) => {\n\t\te.preventDefault();\n\t\tconst droppedFile = e.dataTransfer.files[0];\n\t\tif (droppedFile && droppedFile.name.endsWith(\".xml\")) {\n\t\t\tsetFile(droppedFile);\n\t\t\tsetImportSource({ type: \"wxr\", file: droppedFile });\n\t\t\tsetStep(\"upload\");\n\t\t\tanalyzeMutation.mutate(droppedFile);\n\t\t}\n\t};\n\n\tconst handlePluginConnect = () => {\n\t\tif (!probeResult?.url) return;\n\n\t\t// Check if we're on localhost - OAuth won't work, fall back to manual\n\t\tif (window.location.hostname === \"localhost\" || window.location.hostname === \"127.0.0.1\") {\n\t\t\tsetImportError(\"OAuth authorization requires HTTPS. Please use manual credentials.\");\n\t\t\tsetStep(\"plugin-auth\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Build the WordPress Application Password authorization URL\n\t\tconst wpUrl = probeResult.url.replace(TRAILING_SLASH_REGEX, \"\");\n\t\tconst callbackUrl = `${window.location.origin}/_emdash/api/import/wordpress-plugin/callback`;\n\n\t\t// WordPress requires a valid UUID for app_id\n\t\tconst appId = \"a1b2c3d4-e5f6-7890-abcd-ef1234567890\";\n\n\t\tconst authUrl = new URL(`${wpUrl}/wp-admin/authorize-application.php`);\n\t\tauthUrl.searchParams.set(\"app_name\", \"EmDash CMS\");\n\t\tauthUrl.searchParams.set(\"app_id\", appId);\n\t\tauthUrl.searchParams.set(\"success_url\", callbackUrl);\n\n\t\t// Redirect to WordPress for authorization\n\t\twindow.location.href = authUrl.toString();\n\t};\n\n\tconst handlePluginManualAuth = () => {\n\t\t// Fallback to manual password entry\n\t\tsetStep(\"plugin-auth\");\n\t};\n\n\tconst handlePluginAuth = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (!pluginUsername.trim() || !pluginPassword.trim()) return;\n\n\t\t// Create Basic Auth token\n\t\tconst cleanPassword = pluginPassword.replace(WHITESPACE_REGEX, \"\");\n\t\tconst token = btoa(`${pluginUsername}:${cleanPassword}`);\n\n\t\tconst probeUrl = probeResult?.url;\n\t\tif (!probeUrl) return;\n\t\tsetImportSource({ type: \"wordpress-plugin\", url: probeUrl, token });\n\t\tsetStep(\"analyzing-plugin\");\n\t\twpPluginAnalyzeMutation.mutate({ url: probeUrl, token });\n\t};\n\n\tconst executeImport = () => {\n\t\tif (!analysis || !importSource) return;\n\t\tsetStep(\"importing\");\n\n\t\t// Build author mappings record (wpLogin -> emdashUserId)\n\t\tconst authorMappingsRecord: Record<string, string | null> = {};\n\t\tfor (const mapping of authorMappings) {\n\t\t\tauthorMappingsRecord[mapping.wpLogin] = mapping.emdashUserId;\n\t\t}\n\n\t\t// Build extended config with new options\n\t\tconst config: ImportConfig = {\n\t\t\tpostTypeMappings: selections,\n\t\t\tskipExisting: true,\n\t\t\tauthorMappings: authorMappingsRecord,\n\t\t};\n\n\t\tif (importSource.type === \"wxr\") {\n\t\t\timportMutation.mutate({\n\t\t\t\tfile: importSource.file,\n\t\t\t\tconfig,\n\t\t\t});\n\t\t} else if (importSource.type === \"wordpress-plugin\") {\n\t\t\twpPluginImportMutation.mutate({\n\t\t\t\turl: importSource.url,\n\t\t\t\ttoken: importSource.token,\n\t\t\t\tconfig,\n\t\t\t});\n\t\t}\n\t};\n\n\tconst handleStartImport = () => {\n\t\tif (!analysis || !importSource) return;\n\n\t\tsetPrepareError(null);\n\t\tsetImportError(null);\n\t\tsetPrepareResult(null);\n\n\t\t// If there are authors to map, show the author mapping step\n\t\tif (analysis.authors.length > 0) {\n\t\t\tsetStep(\"authors\");\n\t\t\treturn;\n\t\t}\n\n\t\t// Otherwise, proceed directly to import\n\t\tproceedToImport();\n\t};\n\n\tconst proceedToImport = () => {\n\t\tif (!analysis || !importSource) return;\n\n\t\tconst needsSchemaChanges = analysis.postTypes.filter((pt) => {\n\t\t\tconst selection = selections[pt.name];\n\t\t\tif (!selection?.enabled) return false;\n\t\t\treturn (\n\t\t\t\t!pt.schemaStatus.exists ||\n\t\t\t\tObject.values(pt.schemaStatus.fieldStatus).some((f) => f.status === \"missing\")\n\t\t\t);\n\t\t});\n\n\t\tif (needsSchemaChanges.length > 0) {\n\t\t\tsetStep(\"preparing\");\n\t\t\tprepareMutation.mutate({\n\t\t\t\tpostTypes: needsSchemaChanges.map((pt) => ({\n\t\t\t\t\tname: pt.name,\n\t\t\t\t\tcollection: selections[pt.name]?.collection ?? pt.suggestedCollection,\n\t\t\t\t\tfields: pt.requiredFields,\n\t\t\t\t})),\n\t\t\t});\n\t\t} else {\n\t\t\texecuteImport();\n\t\t}\n\t};\n\n\tconst handleReset = () => {\n\t\tsetStep(\"choose\");\n\t\tsetUrlInput(\"\");\n\t\tsetProbeResult(null);\n\t\tsetImportSource(null);\n\t\tsetFile(null);\n\t\tsetAnalysis(null);\n\t\tsetSelections({});\n\t\tsetPrepareResult(null);\n\t\tsetPrepareError(null);\n\t\tsetImportError(null);\n\t\tsetResult(null);\n\t\tsetExpandedTypes(new Set());\n\t\tsetMediaResult(null);\n\t\tsetRewriteResult(null);\n\t\tsetMediaError(null);\n\t\tsetSkipMedia(false);\n\t\tsetMediaProgress(null);\n\t\tsetPluginUsername(\"\");\n\t\tsetPluginPassword(\"\");\n\t\t// Reset new state\n\t\tsetImportMenus(true);\n\t\tsetImportSiteTitle(true);\n\t\tsetImportLogo(true);\n\t\tsetImportSeo(false);\n\t\t// Reset author mappings\n\t\tsetAuthorMappings([]);\n\t\tsetEmDashUsers([]);\n\t};\n\n\tconst handleStartMediaImport = () => {\n\t\tif (!analysis) return;\n\t\tsetMediaError(null);\n\t\tsetMediaProgress(null);\n\t\tsetStep(\"importing-media\");\n\t\tmediaMutation.mutate(analysis.attachments.items);\n\t};\n\n\tconst handleSkipMedia = () => {\n\t\tsetSkipMedia(true);\n\t\tsetStep(\"complete\");\n\t};\n\n\tconst handleProceedWithUpload = () => {\n\t\tsetStep(\"upload\");\n\t};\n\n\tconst toggleExpanded = (name: string) => {\n\t\tsetExpandedTypes((prev) => {\n\t\t\tconst next = new Set(prev);\n\t\t\tif (next.has(name)) {\n\t\t\t\tnext.delete(name);\n\t\t\t} else {\n\t\t\t\tnext.add(name);\n\t\t\t}\n\t\t\treturn next;\n\t\t});\n\t};\n\n\t// Calculate summary stats\n\tconst selectedCount = Object.values(selections).filter((s) => s.enabled).length;\n\tconst hasIncompatible = analysis?.postTypes.some((pt) => !pt.schemaStatus.canImport) ?? false;\n\tconst needsNewCollections =\n\t\tanalysis?.postTypes.filter((pt) => selections[pt.name]?.enabled && !pt.schemaStatus.exists)\n\t\t\t.length ?? 0;\n\tconst needsNewFields =\n\t\tanalysis?.postTypes.filter((pt) => {\n\t\t\tif (!selections[pt.name]?.enabled) return false;\n\t\t\tif (!pt.schemaStatus.exists) return false;\n\t\t\treturn Object.values(pt.schemaStatus.fieldStatus).some((f) => f.status === \"missing\");\n\t\t}).length ?? 0;\n\n\t// Check if we're using the plugin source\n\tconst isPluginSource = importSource?.type === \"wordpress-plugin\";\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div>\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Import from WordPress</h1>\n\t\t\t\t<p className=\"text-kumo-subtle mt-1\">\n\t\t\t\t\tImport posts, pages, and custom post types from WordPress.\n\t\t\t\t</p>\n\t\t\t</div>\n\n\t\t\t{/* Step indicator */}\n\t\t\t<div className=\"flex items-center gap-2 text-sm flex-wrap\">\n\t\t\t\t<StepIndicator\n\t\t\t\t\tnumber={1}\n\t\t\t\t\tlabel=\"Connect\"\n\t\t\t\t\tactive={\n\t\t\t\t\t\tstep === \"choose\" ||\n\t\t\t\t\t\tstep === \"probing\" ||\n\t\t\t\t\t\tstep === \"probe-result\" ||\n\t\t\t\t\t\tstep === \"plugin-auth\" ||\n\t\t\t\t\t\tstep === \"analyzing-plugin\" ||\n\t\t\t\t\t\tstep === \"upload\"\n\t\t\t\t\t}\n\t\t\t\t\tcomplete={\n\t\t\t\t\t\tstep === \"review\" ||\n\t\t\t\t\t\tstep === \"authors\" ||\n\t\t\t\t\t\tstep === \"preparing\" ||\n\t\t\t\t\t\tstep === \"importing\" ||\n\t\t\t\t\t\tstep === \"media\" ||\n\t\t\t\t\t\tstep === \"importing-media\" ||\n\t\t\t\t\t\tstep === \"rewriting\" ||\n\t\t\t\t\t\tstep === \"complete\"\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t\t<div className=\"h-px w-8 bg-kumo-line\" />\n\t\t\t\t<StepIndicator\n\t\t\t\t\tnumber={2}\n\t\t\t\t\tlabel=\"Review\"\n\t\t\t\t\tactive={step === \"review\" || step === \"authors\"}\n\t\t\t\t\tcomplete={\n\t\t\t\t\t\tstep === \"preparing\" ||\n\t\t\t\t\t\tstep === \"importing\" ||\n\t\t\t\t\t\tstep === \"media\" ||\n\t\t\t\t\t\tstep === \"importing-media\" ||\n\t\t\t\t\t\tstep === \"rewriting\" ||\n\t\t\t\t\t\tstep === \"complete\"\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t\t<div className=\"h-px w-8 bg-kumo-line\" />\n\t\t\t\t<StepIndicator\n\t\t\t\t\tnumber={3}\n\t\t\t\t\tlabel=\"Import\"\n\t\t\t\t\tactive={step === \"preparing\" || step === \"importing\"}\n\t\t\t\t\tcomplete={\n\t\t\t\t\t\tstep === \"media\" ||\n\t\t\t\t\t\tstep === \"importing-media\" ||\n\t\t\t\t\t\tstep === \"rewriting\" ||\n\t\t\t\t\t\tstep === \"complete\"\n\t\t\t\t\t}\n\t\t\t\t/>\n\t\t\t\t{analysis && analysis.attachments.count > 0 && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<div className=\"h-px w-8 bg-kumo-line\" />\n\t\t\t\t\t\t<StepIndicator\n\t\t\t\t\t\t\tnumber={4}\n\t\t\t\t\t\t\tlabel=\"Media\"\n\t\t\t\t\t\t\tactive={step === \"media\" || step === \"importing-media\" || step === \"rewriting\"}\n\t\t\t\t\t\t\tcomplete={step === \"complete\"}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\n\t\t\t{/* Choose step - URL input or file upload */}\n\t\t\t{step === \"choose\" && (\n\t\t\t\t<ChooseStep\n\t\t\t\t\turlInput={urlInput}\n\t\t\t\t\tonUrlChange={setUrlInput}\n\t\t\t\t\tonProbeUrl={handleProbeUrl}\n\t\t\t\t\tonFileSelect={handleFileSelect}\n\t\t\t\t\tonDrop={handleDrop}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Probing step */}\n\t\t\t{step === \"probing\" && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t<Loader />\n\t\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Checking {urlInput}...</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Probe result step */}\n\t\t\t{step === \"probe-result\" && probeResult && (\n\t\t\t\t<ProbeResultStep\n\t\t\t\t\tresult={probeResult}\n\t\t\t\t\tonUploadFile={handleProceedWithUpload}\n\t\t\t\t\tonPluginConnect={handlePluginConnect}\n\t\t\t\t\tonPluginManualAuth={handlePluginManualAuth}\n\t\t\t\t\tonReset={handleReset}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Plugin auth step */}\n\t\t\t{step === \"plugin-auth\" && probeResult && (\n\t\t\t\t<PluginAuthStep\n\t\t\t\t\tsiteTitle={probeResult.bestMatch?.detected.siteTitle}\n\t\t\t\t\tsiteUrl={probeResult.url}\n\t\t\t\t\tusername={pluginUsername}\n\t\t\t\t\tpassword={pluginPassword}\n\t\t\t\t\tonUsernameChange={setPluginUsername}\n\t\t\t\t\tonPasswordChange={setPluginPassword}\n\t\t\t\t\tonSubmit={handlePluginAuth}\n\t\t\t\t\tonBack={() => setStep(\"probe-result\")}\n\t\t\t\t\terror={importError}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Analyzing WordPress Plugin step */}\n\t\t\t{step === \"analyzing-plugin\" && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t<Loader />\n\t\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Analyzing WordPress site...</p>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\tFetching content from the EmDash Exporter API.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Upload step (analyzing file) */}\n\t\t\t{step === \"upload\" && (\n\t\t\t\t<UploadStep\n\t\t\t\t\tisLoading={analyzeMutation.isPending}\n\t\t\t\t\terror={analyzeMutation.error}\n\t\t\t\t\tonFileSelect={handleFileSelect}\n\t\t\t\t\tonDrop={handleDrop}\n\t\t\t\t\tonRetry={() => analyzeMutation.reset()}\n\t\t\t\t\tonBack={() => setStep(\"choose\")}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Review step */}\n\t\t\t{step === \"review\" && analysis && (\n\t\t\t\t<ReviewStep\n\t\t\t\t\tanalysis={analysis}\n\t\t\t\t\tselections={selections}\n\t\t\t\t\texpandedTypes={expandedTypes}\n\t\t\t\t\tprepareError={prepareError}\n\t\t\t\t\timportError={importError}\n\t\t\t\t\tprepareResult={prepareResult}\n\t\t\t\t\tselectedCount={selectedCount}\n\t\t\t\t\thasIncompatible={hasIncompatible}\n\t\t\t\t\tneedsNewCollections={needsNewCollections}\n\t\t\t\t\tneedsNewFields={needsNewFields}\n\t\t\t\t\tonToggleExpand={toggleExpanded}\n\t\t\t\t\tonToggleEnabled={(name, enabled) =>\n\t\t\t\t\t\tsetSelections((prev) => {\n\t\t\t\t\t\t\tconst existing = prev[name];\n\t\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t[name]: {\n\t\t\t\t\t\t\t\t\tenabled,\n\t\t\t\t\t\t\t\t\tcollection: existing?.collection ?? name,\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t\tonStartImport={handleStartImport}\n\t\t\t\t\tonReset={handleReset}\n\t\t\t\t\t// New props for menus and settings\n\t\t\t\t\timportMenus={importMenus}\n\t\t\t\t\tonImportMenusChange={setImportMenus}\n\t\t\t\t\tisPluginSource={isPluginSource}\n\t\t\t\t\timportSiteTitle={importSiteTitle}\n\t\t\t\t\timportLogo={importLogo}\n\t\t\t\t\timportSeo={importSeo}\n\t\t\t\t\tonImportSiteTitleChange={setImportSiteTitle}\n\t\t\t\t\tonImportLogoChange={setImportLogo}\n\t\t\t\t\tonImportSeoChange={setImportSeo}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Author mapping step */}\n\t\t\t{step === \"authors\" && analysis && (\n\t\t\t\t<AuthorMappingStep\n\t\t\t\t\tauthorMappings={authorMappings}\n\t\t\t\t\temdashUsers={emdashUsers}\n\t\t\t\t\tonMappingChange={(wpLogin, emdashUserId) => {\n\t\t\t\t\t\tsetAuthorMappings((prev) =>\n\t\t\t\t\t\t\tprev.map((m) => (m.wpLogin === wpLogin ? { ...m, emdashUserId } : m)),\n\t\t\t\t\t\t);\n\t\t\t\t\t}}\n\t\t\t\t\tonContinue={proceedToImport}\n\t\t\t\t\tonBack={() => setStep(\"review\")}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Preparing step */}\n\t\t\t{step === \"preparing\" && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t<Loader />\n\t\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Creating collections and fields...</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Importing step */}\n\t\t\t{step === \"importing\" && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t<Loader />\n\t\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Importing content...</p>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">This may take a while for large exports.</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Media step */}\n\t\t\t{step === \"media\" && analysis && (\n\t\t\t\t<MediaStep\n\t\t\t\t\tattachments={analysis.attachments}\n\t\t\t\t\terror={mediaError}\n\t\t\t\t\tonImport={handleStartMediaImport}\n\t\t\t\t\tonSkip={handleSkipMedia}\n\t\t\t\t/>\n\t\t\t)}\n\n\t\t\t{/* Importing media step */}\n\t\t\t{step === \"importing-media\" && (\n\t\t\t\t<MediaProgressStep progress={mediaProgress} total={analysis?.attachments.count ?? 0} />\n\t\t\t)}\n\n\t\t\t{/* Rewriting URLs step */}\n\t\t\t{step === \"rewriting\" && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-12 text-center\">\n\t\t\t\t\t<Loader />\n\t\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Updating content URLs...</p>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Complete step */}\n\t\t\t{step === \"complete\" && result && (\n\t\t\t\t<CompleteStep\n\t\t\t\t\tresult={result}\n\t\t\t\t\tprepareResult={prepareResult}\n\t\t\t\t\tmediaResult={mediaResult}\n\t\t\t\t\trewriteResult={rewriteResult}\n\t\t\t\t\tskippedMedia={skipMedia}\n\t\t\t\t\tonReset={handleReset}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// =============================================================================\n// Sub-components\n// =============================================================================\n\nfunction StepIndicator({\n\tnumber,\n\tlabel,\n\tactive,\n\tcomplete,\n}: {\n\tnumber: number;\n\tlabel: string;\n\tactive: boolean;\n\tcomplete: boolean;\n}) {\n\treturn (\n\t\t<div className=\"flex items-center gap-2\">\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"w-6 h-6 rounded-full flex items-center justify-center text-xs font-medium\",\n\t\t\t\t\tcomplete\n\t\t\t\t\t\t? \"bg-kumo-brand text-white\"\n\t\t\t\t\t\t: active\n\t\t\t\t\t\t\t? \"bg-kumo-brand text-white\"\n\t\t\t\t\t\t\t: \"bg-kumo-tint text-kumo-subtle\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{complete ? <Check className=\"h-3 w-3\" /> : number}\n\t\t\t</div>\n\t\t\t<span\n\t\t\t\tclassName={cn(\"text-sm\", active || complete ? \"text-kumo-default\" : \"text-kumo-subtle\")}\n\t\t\t>\n\t\t\t\t{label}\n\t\t\t</span>\n\t\t</div>\n\t);\n}\n\nfunction ChooseStep({\n\turlInput,\n\tonUrlChange,\n\tonProbeUrl,\n\tonFileSelect,\n\tonDrop,\n}: {\n\turlInput: string;\n\tonUrlChange: (url: string) => void;\n\tonProbeUrl: (e: React.FormEvent) => void;\n\tonFileSelect: (e: React.ChangeEvent<HTMLInputElement>) => void;\n\tonDrop: (e: React.DragEvent) => void;\n}) {\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* URL input - primary path */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t<div className=\"p-3 rounded-full bg-blue-100 dark:bg-blue-900/30\">\n\t\t\t\t\t\t<Globe className=\"h-6 w-6 text-blue-600 dark:text-blue-400\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t<h3 className=\"text-lg font-medium\">Enter your WordPress site URL</h3>\n\t\t\t\t\t\t<p className=\"text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\tWe'll check what import options are available for your site.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<form onSubmit={onProbeUrl} className=\"mt-4 flex gap-2\">\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\t\tplaceholder=\"https://yoursite.com\"\n\t\t\t\t\t\t\t\tvalue={urlInput}\n\t\t\t\t\t\t\t\tonChange={(e) => onUrlChange(e.target.value)}\n\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t<Button type=\"submit\" disabled={!urlInput.trim()}>\n\t\t\t\t\t\t\t\tCheck Site\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</form>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"relative\">\n\t\t\t\t<div className=\"absolute inset-0 flex items-center\">\n\t\t\t\t\t<div className=\"w-full border-t\" />\n\t\t\t\t</div>\n\t\t\t\t<div className=\"relative flex justify-center text-xs uppercase\">\n\t\t\t\t\t<span className=\"bg-kumo-base px-2 text-kumo-subtle\">or upload directly</span>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* File upload - fallback */}\n\t\t\t<div\n\t\t\t\tclassName=\"border-2 border-dashed rounded-lg p-8 text-center transition-colors hover:border-kumo-brand/50 cursor-pointer\"\n\t\t\t\tonDragOver={(e) => e.preventDefault()}\n\t\t\t\tonDrop={onDrop}\n\t\t\t>\n\t\t\t\t<Upload className=\"mx-auto h-10 w-10 text-kumo-subtle\" />\n\t\t\t\t<h3 className=\"mt-3 text-sm font-medium\">Upload WordPress export file</h3>\n\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">Drag and drop or click to browse (.xml)</p>\n\t\t\t\t<label className=\"mt-3 inline-block\">\n\t\t\t\t\t<input type=\"file\" accept=\".xml\" className=\"sr-only\" onChange={onFileSelect} />\n\t\t\t\t\t<span className={buttonVariants({ variant: \"outline\", size: \"sm\" })}>Browse Files</span>\n\t\t\t\t</label>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// =============================================================================\n// Feature Comparison Component\n// =============================================================================\n\ninterface FeatureComparisonItem {\n\tfeature: string;\n\twxr: \"full\" | \"partial\" | \"none\";\n\twxrNote?: string;\n\tplugin: \"full\" | \"partial\" | \"none\";\n\tpluginNote?: string;\n}\n\nconst FEATURE_COMPARISON: FeatureComparisonItem[] = [\n\t{ feature: \"Posts & Pages\", wxr: \"full\", plugin: \"full\" },\n\t{ feature: \"Media\", wxr: \"full\", plugin: \"full\" },\n\t{ feature: \"Categories & Tags\", wxr: \"full\", plugin: \"full\" },\n\t{ feature: \"Custom Taxonomies\", wxr: \"full\", plugin: \"full\" },\n\t{ feature: \"Featured Images\", wxr: \"full\", plugin: \"full\" },\n\t{ feature: \"Menus\", wxr: \"full\", plugin: \"full\" },\n\t{\n\t\tfeature: \"Site Settings\",\n\t\twxr: \"partial\",\n\t\twxrNote: \"Partial\",\n\t\tplugin: \"full\",\n\t\tpluginNote: \"Full\",\n\t},\n\t{ feature: \"Widgets\", wxr: \"none\", plugin: \"full\" },\n\t{ feature: \"ACF Fields\", wxr: \"none\", plugin: \"full\" },\n\t{\n\t\tfeature: \"Yoast/RankMath\",\n\t\twxr: \"partial\",\n\t\twxrNote: \"Raw meta\",\n\t\tplugin: \"full\",\n\t\tpluginNote: \"Structured\",\n\t},\n\t{ feature: \"Drafts & Private\", wxr: \"full\", plugin: \"full\" },\n];\n\nfunction FeatureComparison() {\n\treturn (\n\t\t<div className=\"rounded-lg border bg-kumo-base overflow-hidden\">\n\t\t\t<div className=\"border-b p-4 bg-kumo-tint/30\">\n\t\t\t\t<h3 className=\"font-medium text-sm\">Import Capabilities</h3>\n\t\t\t</div>\n\t\t\t<div className=\"overflow-x-auto\">\n\t\t\t\t<table className=\"w-full text-sm\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr className=\"border-b bg-kumo-tint/20\">\n\t\t\t\t\t\t\t<th className=\"text-left p-3 font-medium\">Feature</th>\n\t\t\t\t\t\t\t<th className=\"text-center p-3 font-medium whitespace-nowrap\">WXR File</th>\n\t\t\t\t\t\t\t<th className=\"text-center p-3 font-medium whitespace-nowrap\">Plugin</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{FEATURE_COMPARISON.map((item) => (\n\t\t\t\t\t\t\t<tr key={item.feature} className=\"border-b last:border-0\">\n\t\t\t\t\t\t\t\t<td className=\"p-3 text-kumo-subtle\">{item.feature}</td>\n\t\t\t\t\t\t\t\t<td className=\"p-3 text-center\">\n\t\t\t\t\t\t\t\t\t<FeatureStatus status={item.wxr} note={item.wxrNote} />\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td className=\"p-3 text-center\">\n\t\t\t\t\t\t\t\t\t<FeatureStatus status={item.plugin} note={item.pluginNote} />\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\t\t\t<div className=\"border-t p-3 bg-blue-50 dark:bg-blue-900/20\">\n\t\t\t\t<div className=\"flex items-start gap-2 text-sm\">\n\t\t\t\t\t<Sparkle className=\"h-4 w-4 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5\" />\n\t\t\t\t\t<p className=\"text-blue-800 dark:text-blue-200\">\n\t\t\t\t\t\tFor the best import experience, install the{\" \"}\n\t\t\t\t\t\t<span className=\"font-medium\">EmDash Exporter</span> plugin on your WordPress site.\n\t\t\t\t\t</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction FeatureStatus({ status, note }: { status: \"full\" | \"partial\" | \"none\"; note?: string }) {\n\tif (status === \"full\") {\n\t\treturn (\n\t\t\t<span className=\"inline-flex items-center gap-1 text-green-600 dark:text-green-400\">\n\t\t\t\t<Check className=\"h-4 w-4\" />\n\t\t\t</span>\n\t\t);\n\t}\n\tif (status === \"partial\") {\n\t\treturn (\n\t\t\t<span className=\"inline-flex items-center gap-1 text-yellow-600 dark:text-yellow-400\">\n\t\t\t\t<Warning className=\"h-3.5 w-3.5\" />\n\t\t\t\t{note && <span className=\"text-xs\">{note}</span>}\n\t\t\t</span>\n\t\t);\n\t}\n\treturn (\n\t\t<span className=\"inline-flex items-center gap-1 text-kumo-subtle\">\n\t\t\t<X className=\"h-4 w-4\" />\n\t\t</span>\n\t);\n}\n\nfunction ProbeResultStep({\n\tresult,\n\tonUploadFile,\n\tonPluginConnect,\n\tonPluginManualAuth,\n\tonReset,\n}: {\n\tresult: ProbeResult;\n\tonUploadFile: () => void;\n\tonPluginConnect: () => void;\n\tonPluginManualAuth: () => void;\n\tonReset: () => void;\n}) {\n\tconst bestMatch = result.bestMatch;\n\tconst hasPlugin = bestMatch?.sourceId === \"wordpress-plugin\";\n\n\tif (!result.isWordPress) {\n\t\treturn (\n\t\t\t<div className=\"space-y-6\">\n\t\t\t\t<div className=\"rounded-lg border-l-4 border-l-orange-500 border border-kumo-line bg-kumo-base p-6\">\n\t\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t\t<Warning className=\"h-6 w-6 text-orange-500 flex-shrink-0\" />\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<h3 className=\"font-medium\">Couldn't detect WordPress</h3>\n\t\t\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\tWe couldn't connect to a WordPress site at {result.url}. This could mean the site\n\t\t\t\t\t\t\t\tisn't WordPress, the REST API is disabled, or the site isn't accessible.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t\t<h3 className=\"font-medium\">Export from WordPress manually</h3>\n\t\t\t\t\t<ol className=\"mt-3 space-y-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t<li>1. Log into your WordPress admin dashboard</li>\n\t\t\t\t\t\t<li>\n\t\t\t\t\t\t\t2. Go to <strong>Tools → Export</strong>\n\t\t\t\t\t\t</li>\n\t\t\t\t\t\t<li>3. Select \"All content\"</li>\n\t\t\t\t\t\t<li>4. Click \"Download Export File\"</li>\n\t\t\t\t\t\t<li>5. Upload the file here</li>\n\t\t\t\t\t</ol>\n\t\t\t\t\t<div className=\"mt-4 flex gap-2\">\n\t\t\t\t\t\t<Button onClick={onUploadFile}>Upload Export File</Button>\n\t\t\t\t\t\t<Button variant=\"outline\" onClick={onReset}>\n\t\t\t\t\t\t\tTry Another URL\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\t// WordPress detected\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Detection success */}\n\t\t\t<div className=\"rounded-lg border-l-4 border-l-green-500 border border-kumo-line bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t<Check className=\"h-6 w-6 text-green-500 flex-shrink-0\" />\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h3 className=\"font-medium\">\n\t\t\t\t\t\t\t{bestMatch?.detected.siteTitle || \"WordPress site\"} detected\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{hasPlugin\n\t\t\t\t\t\t\t\t? \"EmDash Exporter plugin detected! You can import directly.\"\n\t\t\t\t\t\t\t\t: \"This is a WordPress site.\"}\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Preview counts if available */}\n\t\t\t{bestMatch?.preview && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base p-4\">\n\t\t\t\t\t<h4 className=\"text-sm font-medium mb-3\">Content found:</h4>\n\t\t\t\t\t<div className=\"grid grid-cols-3 gap-4 text-center\">\n\t\t\t\t\t\t{bestMatch.preview.posts !== undefined && (\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<p className=\"text-2xl font-bold\">{bestMatch.preview.posts}</p>\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">Posts</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{bestMatch.preview.pages !== undefined && (\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<p className=\"text-2xl font-bold\">{bestMatch.preview.pages}</p>\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">Pages</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{bestMatch.preview.media !== undefined && (\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<p className=\"text-2xl font-bold\">{bestMatch.preview.media}</p>\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">Media</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Feature comparison - only show when plugin is NOT detected (to explain the benefits) */}\n\t\t\t{!hasPlugin && <FeatureComparison />}\n\n\t\t\t{/* EmDash Exporter plugin detected - primary option */}\n\t\t\t{hasPlugin && (\n\t\t\t\t<div className=\"rounded-lg border-l-4 border-l-green-500 border border-kumo-line bg-kumo-base p-6\">\n\t\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t\t<div className=\"p-2 rounded-full bg-green-100 dark:bg-green-900/50\">\n\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\tclassName=\"h-5 w-5 text-green-600 dark:text-green-400\"\n\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\t\tfocusable=\"false\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<path d=\"M12 2L2 7l10 5 10-5-10-5z\" />\n\t\t\t\t\t\t\t\t<path d=\"M2 17l10 5 10-5\" />\n\t\t\t\t\t\t\t\t<path d=\"M2 12l10 5 10-5\" />\n\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t<h3 className=\"font-medium\">Import via EmDash Exporter</h3>\n\t\t\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\tImport all content directly including drafts, custom post types, ACF fields, and SEO\n\t\t\t\t\t\t\t\tdata. No file download needed.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p className=\"mt-2 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\tYou'll be redirected to WordPress to authorize the connection.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<div className=\"mt-3 flex items-center gap-3\">\n\t\t\t\t\t\t\t\t<Button icon={<ArrowSquareOut />} onClick={onPluginConnect}>\n\t\t\t\t\t\t\t\t\tConnect with WordPress\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\t\tclassName=\"text-xs text-kumo-subtle hover:text-kumo-default underline\"\n\t\t\t\t\t\t\t\t\tonClick={onPluginManualAuth}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tEnter credentials manually\n\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* File upload fallback */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<h3 className=\"font-medium\">\n\t\t\t\t\t{hasPlugin ? \"Or upload an export file\" : \"Upload an export file\"}\n\t\t\t\t</h3>\n\t\t\t\t<p className=\"mt-1 text-sm text-kumo-subtle\">\n\t\t\t\t\t{hasPlugin\n\t\t\t\t\t\t? \"Alternatively, you can export from WordPress (Tools → Export) and upload the file.\"\n\t\t\t\t\t\t: bestMatch?.capabilities.privateContent\n\t\t\t\t\t\t\t? \"Export your content from WordPress to import everything including drafts.\"\n\t\t\t\t\t\t\t: \"For a complete import including drafts and all content, export from WordPress.\"}\n\t\t\t\t</p>\n\t\t\t\t{bestMatch?.suggestedAction.type === \"upload\" && (\n\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">{bestMatch.suggestedAction.instructions}</p>\n\t\t\t\t)}\n\t\t\t\t<div className=\"mt-4 flex gap-2\">\n\t\t\t\t\t<Button variant={hasPlugin ? \"outline\" : \"primary\"} onClick={onUploadFile}>\n\t\t\t\t\t\tUpload Export File\n\t\t\t\t\t</Button>\n\t\t\t\t\t<Button variant=\"outline\" onClick={onReset}>\n\t\t\t\t\t\tTry Another URL\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction PluginAuthStep({\n\tsiteTitle,\n\tsiteUrl,\n\tusername,\n\tpassword,\n\tonUsernameChange,\n\tonPasswordChange,\n\tonSubmit,\n\tonBack,\n\terror,\n}: {\n\tsiteTitle?: string;\n\tsiteUrl: string;\n\tusername: string;\n\tpassword: string;\n\tonUsernameChange: (value: string) => void;\n\tonPasswordChange: (value: string) => void;\n\tonSubmit: (e: React.FormEvent) => void;\n\tonBack: () => void;\n\terror: string | null;\n}) {\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t<div className=\"p-3 rounded-full bg-green-100 dark:bg-green-900/30\">\n\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\tclassName=\"h-6 w-6 text-green-600 dark:text-green-400\"\n\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\tstrokeWidth=\"2\"\n\t\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t\t\tfocusable=\"false\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<rect x=\"3\" y=\"11\" width=\"18\" height=\"11\" rx=\"2\" ry=\"2\" />\n\t\t\t\t\t\t\t<path d=\"M7 11V7a5 5 0 0 1 10 0v4\" />\n\t\t\t\t\t\t</svg>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t<h3 className=\"text-lg font-medium\">Connect to {siteTitle || \"WordPress\"}</h3>\n\t\t\t\t\t\t<p className=\"text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\tEnter your WordPress credentials to import content directly.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{error && (\n\t\t\t\t\t<div className=\"mt-4 p-3 rounded-lg border border-kumo-danger/50 bg-kumo-danger/10\">\n\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t<WarningCircle className=\"h-4 w-4 text-kumo-danger flex-shrink-0 mt-0.5\" />\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-danger\">{error}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t<form onSubmit={onSubmit} className=\"mt-6 space-y-4\">\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<label htmlFor=\"wp-username\" className=\"block text-sm font-medium mb-1\">\n\t\t\t\t\t\t\tWordPress Username\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tid=\"wp-username\"\n\t\t\t\t\t\t\ttype=\"text\"\n\t\t\t\t\t\t\tvalue={username}\n\t\t\t\t\t\t\tonChange={(e) => onUsernameChange(e.target.value)}\n\t\t\t\t\t\t\tplaceholder=\"admin\"\n\t\t\t\t\t\t\tautoComplete=\"username\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<label htmlFor=\"wp-password\" className=\"block text-sm font-medium mb-1\">\n\t\t\t\t\t\t\tApplication Password\n\t\t\t\t\t\t</label>\n\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\tid=\"wp-password\"\n\t\t\t\t\t\t\ttype=\"password\"\n\t\t\t\t\t\t\tvalue={password}\n\t\t\t\t\t\t\tonChange={(e) => onPasswordChange(e.target.value)}\n\t\t\t\t\t\t\tplaceholder=\"xxxx xxxx xxxx xxxx xxxx xxxx\"\n\t\t\t\t\t\t\tautoComplete=\"current-password\"\n\t\t\t\t\t\t/>\n\t\t\t\t\t\t<p className=\"mt-1 text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\tCreate one in WordPress: Users → Profile → Application Passwords\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div className=\"flex gap-2 pt-2\">\n\t\t\t\t\t\t<Button type=\"submit\" disabled={!username.trim() || !password.trim()}>\n\t\t\t\t\t\t\tConnect & Analyze\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t<Button type=\"button\" variant=\"outline\" onClick={onBack}>\n\t\t\t\t\t\t\tBack\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</form>\n\t\t\t</div>\n\n\t\t\t<div className=\"rounded-lg border-l-4 border-l-blue-500 border border-kumo-line bg-kumo-base p-4\">\n\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t<ArrowSquareOut className=\"h-5 w-5 text-blue-500 flex-shrink-0\" />\n\t\t\t\t\t<div className=\"text-sm\">\n\t\t\t\t\t\t<p className=\"font-medium\">How to create an Application Password</p>\n\t\t\t\t\t\t<ol className=\"mt-2 space-y-1 text-kumo-subtle\">\n\t\t\t\t\t\t\t<li>1. Log into your WordPress admin</li>\n\t\t\t\t\t\t\t<li>2. Go to Users → Profile</li>\n\t\t\t\t\t\t\t<li>3. Scroll to \"Application Passwords\"</li>\n\t\t\t\t\t\t\t<li>4. Enter \"EmDash\" and click \"Add New\"</li>\n\t\t\t\t\t\t\t<li>5. Copy the generated password</li>\n\t\t\t\t\t\t</ol>\n\t\t\t\t\t\t<a\n\t\t\t\t\t\t\thref={`${siteUrl}/wp-admin/profile.php#application-passwords-section`}\n\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\tclassName=\"mt-2 inline-flex items-center gap-1 text-blue-600 hover:underline\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tOpen WordPress Profile\n\t\t\t\t\t\t\t<ArrowSquareOut className=\"h-3 w-3\" />\n\t\t\t\t\t\t</a>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction UploadStep({\n\tisLoading,\n\terror,\n\tonFileSelect,\n\tonDrop,\n\tonRetry,\n\tonBack,\n}: {\n\tisLoading: boolean;\n\terror: Error | null;\n\tonFileSelect: (e: React.ChangeEvent<HTMLInputElement>) => void;\n\tonDrop: (e: React.DragEvent) => void;\n\tonRetry: () => void;\n\tonBack?: () => void;\n}) {\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"border-2 border-dashed rounded-lg p-12 text-center transition-colors\",\n\t\t\t\t\tisLoading\n\t\t\t\t\t\t? \"border-kumo-brand bg-kumo-brand/5\"\n\t\t\t\t\t\t: \"border-kumo-line hover:border-kumo-brand/50\",\n\t\t\t\t)}\n\t\t\t\tonDragOver={(e) => e.preventDefault()}\n\t\t\t\tonDrop={onDrop}\n\t\t\t>\n\t\t\t\t{isLoading ? (\n\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t<Loader />\n\t\t\t\t\t\t<p className=\"text-kumo-subtle\">Analyzing export file...</p>\n\t\t\t\t\t</div>\n\t\t\t\t) : error ? (\n\t\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t\t<Warning className=\"mx-auto h-12 w-12 text-kumo-danger\" />\n\t\t\t\t\t\t<p className=\"text-kumo-danger\">{error.message}</p>\n\t\t\t\t\t\t<Button variant=\"outline\" onClick={onRetry}>\n\t\t\t\t\t\t\tTry Again\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<Upload className=\"mx-auto h-12 w-12 text-kumo-subtle\" />\n\t\t\t\t\t\t<h3 className=\"mt-4 text-lg font-medium\">Drop your WordPress export file here</h3>\n\t\t\t\t\t\t<p className=\"mt-2 text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\tOr click to browse. Accepts .xml files exported from WordPress.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t<label className=\"mt-4 inline-block\">\n\t\t\t\t\t\t\t<input type=\"file\" accept=\".xml\" className=\"sr-only\" onChange={onFileSelect} />\n\t\t\t\t\t\t\t<span className={buttonVariants({ variant: \"outline\" })}>Browse Files</span>\n\t\t\t\t\t\t</label>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t\t{onBack && !isLoading && !error && (\n\t\t\t\t<Button variant=\"ghost\" onClick={onBack}>\n\t\t\t\t\t← Back\n\t\t\t\t</Button>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\n// =============================================================================\n// Menu Info (Type-safe helper for navMenus)\n// =============================================================================\n\ninterface NavMenuItem {\n\tname: string;\n\tslug: string;\n\tcount: number;\n}\n\nfunction getNavMenus(analysis: ImportAnalysis): NavMenuItem[] | undefined {\n\tif (\"navMenus\" in analysis && Array.isArray(analysis.navMenus)) {\n\t\treturn analysis.navMenus as NavMenuItem[];\n\t}\n\treturn undefined;\n}\n\n// =============================================================================\n// Review Step with Menus and Settings\n// =============================================================================\n\nfunction ReviewStep({\n\tanalysis,\n\tselections,\n\texpandedTypes,\n\tprepareError,\n\timportError,\n\tprepareResult,\n\tselectedCount,\n\thasIncompatible,\n\tneedsNewCollections,\n\tneedsNewFields,\n\tonToggleExpand,\n\tonToggleEnabled,\n\tonStartImport,\n\tonReset,\n\t// New props\n\timportMenus,\n\tonImportMenusChange,\n\tisPluginSource,\n\timportSiteTitle,\n\timportLogo,\n\timportSeo,\n\tonImportSiteTitleChange,\n\tonImportLogoChange,\n\tonImportSeoChange,\n}: {\n\tanalysis: ImportAnalysis;\n\tselections: Record<string, PostTypeSelection>;\n\texpandedTypes: Set<string>;\n\tprepareError: string | null;\n\timportError: string | null;\n\tprepareResult: PrepareResult | null;\n\tselectedCount: number;\n\thasIncompatible: boolean;\n\tneedsNewCollections: number;\n\tneedsNewFields: number;\n\tonToggleExpand: (name: string) => void;\n\tonToggleEnabled: (name: string, enabled: boolean) => void;\n\tonStartImport: () => void;\n\tonReset: () => void;\n\t// New props\n\timportMenus: boolean;\n\tonImportMenusChange: (value: boolean) => void;\n\tisPluginSource: boolean;\n\timportSiteTitle: boolean;\n\timportLogo: boolean;\n\timportSeo: boolean;\n\tonImportSiteTitleChange: (value: boolean) => void;\n\tonImportLogoChange: (value: boolean) => void;\n\tonImportSeoChange: (value: boolean) => void;\n}) {\n\tconst navMenus = getNavMenus(analysis);\n\tconst hasMenus = navMenus && navMenus.length > 0;\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t{/* Site info */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-4\">\n\t\t\t\t<h3 className=\"font-medium\">{analysis.site.title}</h3>\n\t\t\t\t<p className=\"text-sm text-kumo-subtle\">{analysis.site.url}</p>\n\t\t\t</div>\n\n\t\t\t{/* Errors */}\n\t\t\t{(prepareError || importError) && (\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-4\">\n\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t<WarningCircle className=\"h-5 w-5 text-kumo-danger flex-shrink-0\" />\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<p className=\"font-medium text-kumo-danger\">\n\t\t\t\t\t\t\t\t{prepareError ? \"Schema preparation failed\" : \"Import failed\"}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p className=\"mt-1 text-sm text-kumo-danger/90 font-mono\">\n\t\t\t\t\t\t\t\t{prepareError || importError}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{prepareResult && !prepareResult.success && (\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-4\">\n\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t<WarningCircle className=\"h-5 w-5 text-kumo-danger flex-shrink-0\" />\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<p className=\"font-medium text-kumo-danger\">Failed to create some collections</p>\n\t\t\t\t\t\t\t<ul className=\"mt-2 text-sm space-y-1\">\n\t\t\t\t\t\t\t\t{prepareResult.errors.map((err, i) => (\n\t\t\t\t\t\t\t\t\t<li key={i}>\n\t\t\t\t\t\t\t\t\t\t<strong>{err.collection}:</strong>{\" \"}\n\t\t\t\t\t\t\t\t\t\t<span className=\"font-mono\">{err.error}</span>\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Post type list */}\n\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t<div className=\"border-b p-4\">\n\t\t\t\t\t<h3 className=\"font-medium\">Content to Import</h3>\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">Select which content types to import.</p>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"divide-y\">\n\t\t\t\t\t{analysis.postTypes.map((pt) => (\n\t\t\t\t\t\t<PostTypeRow\n\t\t\t\t\t\t\tkey={pt.name}\n\t\t\t\t\t\t\tpostType={pt}\n\t\t\t\t\t\t\tselection={selections[pt.name]}\n\t\t\t\t\t\t\texpanded={expandedTypes.has(pt.name)}\n\t\t\t\t\t\t\tonToggleExpand={() => onToggleExpand(pt.name)}\n\t\t\t\t\t\t\tonToggleEnabled={(enabled) => onToggleEnabled(pt.name, enabled)}\n\t\t\t\t\t\t/>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Structure section - Menus and Taxonomies */}\n\t\t\t{hasMenus && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t\t<div className=\"border-b p-4\">\n\t\t\t\t\t\t<h3 className=\"font-medium\">Structure</h3>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">Additional data to import.</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"divide-y\">\n\t\t\t\t\t\t{/* Menus */}\n\t\t\t\t\t\t<div className=\"p-4\">\n\t\t\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\tchecked={importMenus}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => onImportMenusChange(e.target.checked)}\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 rounded border-gray-300\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Import navigation menus\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<List className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">Menus ({navMenus.length})</p>\n\t\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t{navMenus.map((m) => m.name).join(\", \")}\n\t\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Categories count */}\n\t\t\t\t\t\t{analysis.categories > 0 && (\n\t\t\t\t\t\t\t<div className=\"p-4\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\tchecked={true}\n\t\t\t\t\t\t\t\t\t\tdisabled\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 rounded border-gray-300\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Categories will be imported\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">Categories ({analysis.categories})</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t{/* Tags count */}\n\t\t\t\t\t\t{analysis.tags > 0 && (\n\t\t\t\t\t\t\t<div className=\"p-4\">\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\t\tchecked={true}\n\t\t\t\t\t\t\t\t\t\tdisabled\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 rounded border-gray-300\"\n\t\t\t\t\t\t\t\t\t\taria-label=\"Tags will be imported\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">Tags ({analysis.tags})</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{/* Site Settings section - Plugin only */}\n\t\t\t{isPluginSource && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t\t<div className=\"border-b p-4\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<Gear className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t\t<h3 className=\"font-medium\">Settings</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\tImport site configuration from WordPress.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"divide-y\">\n\t\t\t\t\t\t{/* Site title & tagline */}\n\t\t\t\t\t\t<div className=\"p-4\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\tchecked={importSiteTitle}\n\t\t\t\t\t\t\t\t\tonChange={(e) => onImportSiteTitleChange(e.target.checked)}\n\t\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 rounded border-gray-300\"\n\t\t\t\t\t\t\t\t\taria-label=\"Import site title and tagline\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">Site title & tagline</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* Logo & favicon */}\n\t\t\t\t\t\t<div className=\"p-4\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\tchecked={importLogo}\n\t\t\t\t\t\t\t\t\tonChange={(e) => onImportLogoChange(e.target.checked)}\n\t\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 rounded border-gray-300\"\n\t\t\t\t\t\t\t\t\taria-label=\"Import logo and favicon\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">Logo & favicon</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{/* SEO settings */}\n\t\t\t\t\t\t<div className=\"p-4\">\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t\t<input\n\t\t\t\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\t\t\t\tchecked={importSeo}\n\t\t\t\t\t\t\t\t\tonChange={(e) => onImportSeoChange(e.target.checked)}\n\t\t\t\t\t\t\t\t\tclassName=\"h-4 w-4 rounded border-gray-300\"\n\t\t\t\t\t\t\t\t\taria-label=\"Import SEO settings\"\n\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t<p className=\"font-medium\">SEO settings (Yoast)</p>\n\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\tMeta titles, descriptions, and social images\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{hasIncompatible && (\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-4\">\n\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t<WarningCircle className=\"h-5 w-5 text-kumo-danger flex-shrink-0\" />\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<p className=\"font-medium text-kumo-danger\">Some content types cannot be imported</p>\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\tThe existing collection has fields with incompatible types.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{selectedCount > 0 && (\n\t\t\t\t<div className=\"rounded-lg border-l-4 border-l-blue-500 border border-kumo-line bg-kumo-base p-4\">\n\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t<Database className=\"h-5 w-5 text-blue-500 flex-shrink-0\" />\n\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t<p className=\"font-medium\">What will happen when you import</p>\n\t\t\t\t\t\t\t<ul className=\"text-sm text-kumo-subtle space-y-1\">\n\t\t\t\t\t\t\t\t{needsNewCollections > 0 && (\n\t\t\t\t\t\t\t\t\t<li className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<Plus className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t{needsNewCollections} new collection\n\t\t\t\t\t\t\t\t\t\t{needsNewCollections > 1 ? \"s\" : \"\"} will be created\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{needsNewFields > 0 && (\n\t\t\t\t\t\t\t\t\t<li className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<Plus className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\tFields will be added to {needsNewFields} existing collection\n\t\t\t\t\t\t\t\t\t\t{needsNewFields > 1 ? \"s\" : \"\"}\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<li className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t<FileText className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t{analysis.postTypes\n\t\t\t\t\t\t\t\t\t\t.filter((pt) => selections[pt.name]?.enabled)\n\t\t\t\t\t\t\t\t\t\t.reduce((sum, pt) => sum + pt.count, 0)}{\" \"}\n\t\t\t\t\t\t\t\t\titems will be imported\n\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t{hasMenus && importMenus && (\n\t\t\t\t\t\t\t\t\t<li className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<List className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t\t\t{navMenus.length} menu{navMenus.length > 1 ? \"s\" : \"\"} will be imported\n\t\t\t\t\t\t\t\t\t</li>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t<Button variant=\"outline\" onClick={onReset}>\n\t\t\t\t\tCancel\n\t\t\t\t</Button>\n\t\t\t\t<Button onClick={onStartImport} disabled={selectedCount === 0}>\n\t\t\t\t\t{needsNewCollections > 0 || needsNewFields > 0\n\t\t\t\t\t\t? \"Create Schema & Import\"\n\t\t\t\t\t\t: \"Start Import\"}\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction PostTypeRow({\n\tpostType,\n\tselection,\n\texpanded,\n\tonToggleExpand,\n\tonToggleEnabled,\n}: {\n\tpostType: PostTypeAnalysis;\n\tselection: PostTypeSelection | undefined;\n\texpanded: boolean;\n\tonToggleExpand: () => void;\n\tonToggleEnabled: (enabled: boolean) => void;\n}) {\n\tconst { schemaStatus } = postType;\n\tconst canImport = schemaStatus.canImport;\n\tconst isNew = !schemaStatus.exists;\n\tconst hasMissingFields =\n\t\tschemaStatus.exists &&\n\t\tObject.values(schemaStatus.fieldStatus).some((f) => f.status === \"missing\");\n\n\treturn (\n\t\t<div className=\"p-4\">\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t<input\n\t\t\t\t\t\ttype=\"checkbox\"\n\t\t\t\t\t\tchecked={selection?.enabled ?? false}\n\t\t\t\t\t\tdisabled={!canImport}\n\t\t\t\t\t\tonChange={(e) => onToggleEnabled(e.target.checked)}\n\t\t\t\t\t\tclassName=\"h-4 w-4 rounded border-gray-300\"\n\t\t\t\t\t\taria-label={`Import ${postType.name}`}\n\t\t\t\t\t/>\n\t\t\t\t\t<button\n\t\t\t\t\t\tonClick={onToggleExpand}\n\t\t\t\t\t\tclassName=\"flex items-center gap-1 text-left\"\n\t\t\t\t\t\taria-expanded={expanded}\n\t\t\t\t\t>\n\t\t\t\t\t\t{expanded ? (\n\t\t\t\t\t\t\t<CaretDown className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t<CaretRight className=\"h-4 w-4 text-kumo-subtle\" />\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<p className=\"font-medium\">{postType.name}</p>\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t{postType.count} items → {postType.suggestedCollection}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</button>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t{!canImport ? (\n\t\t\t\t\t\t<Badge variant=\"destructive\">Incompatible</Badge>\n\t\t\t\t\t) : isNew ? (\n\t\t\t\t\t\t<Badge variant=\"secondary\">New collection</Badge>\n\t\t\t\t\t) : hasMissingFields ? (\n\t\t\t\t\t\t<Badge variant=\"secondary\">Add fields</Badge>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<Badge>Ready</Badge>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{expanded && (\n\t\t\t\t<div className=\"mt-4 ml-8 p-3 rounded-lg bg-kumo-tint/50 text-sm\">\n\t\t\t\t\t{!canImport && schemaStatus.reason && (\n\t\t\t\t\t\t<div className=\"mb-3 p-2 rounded bg-red-100 dark:bg-red-900/30 text-red-800 dark:text-red-300\">\n\t\t\t\t\t\t\t<WarningCircle className=\"inline h-4 w-4 mr-1\" />\n\t\t\t\t\t\t\t{schemaStatus.reason}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<p className=\"font-medium mb-2\">Required fields:</p>\n\t\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t\t{postType.requiredFields.map((field) => {\n\t\t\t\t\t\t\tconst status = schemaStatus.fieldStatus[field.slug];\n\t\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t\t<div key={field.slug} className=\"flex items-center justify-between\">\n\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t{field.label} <span className=\"text-kumo-subtle\">({field.type})</span>\n\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t{status?.status === \"compatible\" ? (\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-green-600 dark:text-green-400\">\n\t\t\t\t\t\t\t\t\t\t\t<Check className=\"inline h-3 w-3\" /> Exists\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t) : status?.status === \"missing\" ? (\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-blue-600 dark:text-blue-400\">\n\t\t\t\t\t\t\t\t\t\t\t<Plus className=\"inline h-3 w-3\" /> Will create\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t) : status?.status === \"type_mismatch\" ? (\n\t\t\t\t\t\t\t\t\t\t<span className=\"text-red-600 dark:text-red-400\">\n\t\t\t\t\t\t\t\t\t\t\t<X className=\"inline h-3 w-3\" /> Type mismatch ({status.existingType})\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t})}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\nfunction MediaStep({\n\tattachments,\n\terror,\n\tonImport,\n\tonSkip,\n}: {\n\tattachments: { count: number; items: AttachmentInfo[] };\n\terror: string | null;\n\tonImport: () => void;\n\tonSkip: () => void;\n}) {\n\tconst byType = attachments.items.reduce(\n\t\t(acc, att) => {\n\t\t\tconst type = att.mimeType?.split(\"/\")[0] || \"other\";\n\t\t\tacc[type] = (acc[type] || 0) + 1;\n\t\t\treturn acc;\n\t\t},\n\t\t{} as Record<string, number>,\n\t);\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t<div className=\"p-3 rounded-full bg-blue-100 dark:bg-blue-900/30\">\n\t\t\t\t\t\t<Image className=\"h-6 w-6 text-blue-600 dark:text-blue-400\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t<h3 className=\"text-lg font-medium\">Import Media Files</h3>\n\t\t\t\t\t\t<p className=\"text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\tYour WordPress export contains {attachments.count} media files.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"mt-4 p-4 rounded-lg bg-kumo-tint/50\">\n\t\t\t\t\t<div className=\"grid grid-cols-2 sm:grid-cols-4 gap-4 text-sm\">\n\t\t\t\t\t\t{Object.entries(byType).map(([type, count]) => (\n\t\t\t\t\t\t\t<div key={type}>\n\t\t\t\t\t\t\t\t<p className=\"text-kumo-subtle capitalize\">{type}</p>\n\t\t\t\t\t\t\t\t<p className=\"font-medium\">{count} files</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{error && (\n\t\t\t\t\t<div className=\"mt-4 p-3 rounded-lg border border-kumo-danger/50 bg-kumo-danger/10\">\n\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t<WarningCircle className=\"h-4 w-4 text-kumo-danger flex-shrink-0 mt-0.5\" />\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-danger\">{error}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t<div className=\"mt-4 p-4 rounded-lg border-l-4 border-l-blue-500 border border-kumo-line bg-kumo-base\">\n\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t<DownloadSimple className=\"h-5 w-5 text-blue-500 flex-shrink-0\" />\n\t\t\t\t\t\t<div className=\"text-sm\">\n\t\t\t\t\t\t\t<p className=\"font-medium\">What happens when you import:</p>\n\t\t\t\t\t\t\t<ul className=\"mt-1 space-y-1 text-kumo-subtle\">\n\t\t\t\t\t\t\t\t<li>• Files are downloaded from your WordPress site</li>\n\t\t\t\t\t\t\t\t<li>• Uploaded to your EmDash media storage</li>\n\t\t\t\t\t\t\t\t<li>• URLs in your content are updated automatically</li>\n\t\t\t\t\t\t\t</ul>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t<Button variant=\"outline\" onClick={onSkip}>\n\t\t\t\t\tSkip Media Import\n\t\t\t\t</Button>\n\t\t\t\t<Button icon={<DownloadSimple />} onClick={onImport}>\n\t\t\t\t\tImport Media\n\t\t\t\t</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction MediaProgressStep({\n\tprogress,\n\ttotal,\n}: {\n\tprogress: MediaImportProgress | null;\n\ttotal: number;\n}) {\n\tconst current = progress?.current ?? 0;\n\tconst percentage = total > 0 ? Math.round((current / total) * 100) : 0;\n\n\tconst statusLabels: Record<MediaImportProgress[\"status\"], string> = {\n\t\tdownloading: \"Downloading\",\n\t\tuploading: \"Uploading\",\n\t\tdone: \"Done\",\n\t\tskipped: \"Skipped\",\n\t\tfailed: \"Failed\",\n\t};\n\n\treturn (\n\t\t<div className=\"rounded-lg border bg-kumo-base p-8\">\n\t\t\t<div className=\"flex flex-col items-center text-center\">\n\t\t\t\t<Loader size=\"lg\" />\n\t\t\t\t<p className=\"text-sm font-medium mt-2\">{percentage}%</p>\n\n\t\t\t\t<h3 className=\"mt-6 text-lg font-medium\">Importing Media</h3>\n\n\t\t\t\t<div className=\"w-full max-w-md mt-4\">\n\t\t\t\t\t<div className=\"flex justify-between text-sm text-kumo-subtle mb-1\">\n\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t{current} of {total}\n\t\t\t\t\t\t</span>\n\t\t\t\t\t\t<span>{percentage}%</span>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"h-2 bg-kumo-tint rounded-full overflow-hidden\">\n\t\t\t\t\t\t<div\n\t\t\t\t\t\t\tclassName=\"h-full bg-kumo-brand transition-all duration-300\"\n\t\t\t\t\t\t\tstyle={{ width: `${percentage}%` }}\n\t\t\t\t\t\t/>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t{progress && (\n\t\t\t\t\t<div className=\"mt-4 p-3 rounded-lg bg-kumo-tint/50 w-full max-w-md\">\n\t\t\t\t\t\t<div className=\"flex items-center justify-between text-sm\">\n\t\t\t\t\t\t\t<span className=\"font-medium truncate max-w-[70%]\">\n\t\t\t\t\t\t\t\t{progress.filename || `File ${progress.current}`}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t<span\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"px-2 py-0.5 rounded text-xs\",\n\t\t\t\t\t\t\t\t\tprogress.status === \"downloading\" &&\n\t\t\t\t\t\t\t\t\t\t\"bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400\",\n\t\t\t\t\t\t\t\t\tprogress.status === \"uploading\" &&\n\t\t\t\t\t\t\t\t\t\t\"bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400\",\n\t\t\t\t\t\t\t\t\tprogress.status === \"done\" &&\n\t\t\t\t\t\t\t\t\t\t\"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400\",\n\t\t\t\t\t\t\t\t\tprogress.status === \"skipped\" &&\n\t\t\t\t\t\t\t\t\t\t\"bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400\",\n\t\t\t\t\t\t\t\t\tprogress.status === \"failed\" &&\n\t\t\t\t\t\t\t\t\t\t\"bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400\",\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{statusLabels[progress.status]}\n\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{progress.error && <p className=\"mt-1 text-xs text-kumo-danger\">{progress.error}</p>}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t\t{!progress && (\n\t\t\t\t\t<p className=\"mt-4 text-sm text-kumo-subtle\">\n\t\t\t\t\t\tPreparing to download files from WordPress...\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction CompleteStep({\n\tresult,\n\tprepareResult,\n\tmediaResult,\n\trewriteResult,\n\tskippedMedia,\n\tonReset,\n}: {\n\tresult: ImportResult;\n\tprepareResult: PrepareResult | null;\n\tmediaResult: MediaImportResult | null;\n\trewriteResult: RewriteUrlsResult | null;\n\tskippedMedia: boolean;\n\tonReset: () => void;\n}) {\n\tconst hasMediaErrors = mediaResult && mediaResult.failed.length > 0;\n\tconst hasContentErrors = result.errors.length > 0;\n\tconst overallSuccess = !hasContentErrors && (!mediaResult || mediaResult.failed.length === 0);\n\n\tconst wasMediaOnlyImport =\n\t\tresult.imported === 0 && result.skipped > 0 && mediaResult && mediaResult.imported.length > 0;\n\n\tconst getSummaryMessage = () => {\n\t\tconst parts: string[] = [];\n\t\tif (result.imported > 0) {\n\t\t\tparts.push(`${result.imported} content items imported`);\n\t\t}\n\t\tif (result.skipped > 0 && result.imported > 0) {\n\t\t\tparts.push(`${result.skipped} skipped (already exist)`);\n\t\t}\n\t\tif (mediaResult && mediaResult.imported.length > 0) {\n\t\t\tparts.push(`${mediaResult.imported.length} media files imported`);\n\t\t}\n\t\tif (hasContentErrors) {\n\t\t\tparts.push(`${result.errors.length} content errors`);\n\t\t}\n\t\tif (hasMediaErrors) {\n\t\t\tparts.push(`${mediaResult.failed.length} media errors`);\n\t\t}\n\t\treturn parts.join(\" · \");\n\t};\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"rounded-lg border p-6 text-center\",\n\t\t\t\t\toverallSuccess\n\t\t\t\t\t\t? \"border-green-200 bg-green-50 dark:border-green-900/50 dark:bg-green-900/20\"\n\t\t\t\t\t\t: \"border-yellow-200 bg-yellow-50 dark:border-yellow-900/50 dark:bg-yellow-900/20\",\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t{overallSuccess ? (\n\t\t\t\t\t<Check className=\"mx-auto h-12 w-12 text-green-600 dark:text-green-400\" />\n\t\t\t\t) : (\n\t\t\t\t\t<Warning className=\"mx-auto h-12 w-12 text-yellow-600 dark:text-yellow-400\" />\n\t\t\t\t)}\n\t\t\t\t<h3 className=\"mt-4 text-lg font-medium\">\n\t\t\t\t\t{overallSuccess\n\t\t\t\t\t\t? wasMediaOnlyImport\n\t\t\t\t\t\t\t? \"Media Import Complete\"\n\t\t\t\t\t\t\t: \"Import Complete\"\n\t\t\t\t\t\t: \"Import Completed with Errors\"}\n\t\t\t\t</h3>\n\t\t\t\t<p className=\"mt-2 text-kumo-subtle\">{getSummaryMessage()}</p>\n\t\t\t\t{wasMediaOnlyImport && (\n\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\tContent was skipped because it already exists\n\t\t\t\t\t</p>\n\t\t\t\t)}\n\t\t\t\t{skippedMedia && <p className=\"text-sm text-kumo-subtle mt-1\">Media import was skipped</p>}\n\t\t\t</div>\n\n\t\t\t{prepareResult &&\n\t\t\t\t(prepareResult.collectionsCreated.length > 0 || prepareResult.fieldsCreated.length > 0) && (\n\t\t\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t\t\t<div className=\"border-b p-4\">\n\t\t\t\t\t\t\t<h3 className=\"font-medium\">Schema Changes</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<div className=\"p-4 space-y-2 text-sm\">\n\t\t\t\t\t\t\t{prepareResult.collectionsCreated.length > 0 && (\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\t<strong>Collections created:</strong>{\" \"}\n\t\t\t\t\t\t\t\t\t{prepareResult.collectionsCreated.join(\", \")}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{prepareResult.fieldsCreated.length > 0 && (\n\t\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t\t<strong>Fields created:</strong>{\" \"}\n\t\t\t\t\t\t\t\t\t{prepareResult.fieldsCreated.map((f) => `${f.collection}.${f.field}`).join(\", \")}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\n\t\t\t{Object.keys(result.byCollection).length > 0 && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t\t<div className=\"border-b p-4\">\n\t\t\t\t\t\t<h3 className=\"font-medium\">Imported by Collection</h3>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"divide-y\">\n\t\t\t\t\t\t{Object.entries(result.byCollection).map(([collection, count]) => (\n\t\t\t\t\t\t\t<div key={collection} className=\"flex items-center justify-between p-4\">\n\t\t\t\t\t\t\t\t<span className=\"font-medium\">{collection}</span>\n\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">{count} items</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{mediaResult && mediaResult.imported.length > 0 && (\n\t\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t\t<div className=\"border-b p-4\">\n\t\t\t\t\t\t<h3 className=\"font-medium\">Media Import</h3>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"p-4 space-y-2 text-sm\">\n\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t<strong>{mediaResult.imported.length}</strong> files imported\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{rewriteResult && rewriteResult.updated > 0 && (\n\t\t\t\t\t\t\t<p>\n\t\t\t\t\t\t\t\t<strong>{rewriteResult.urlsRewritten}</strong> image URLs updated in{\" \"}\n\t\t\t\t\t\t\t\t<strong>{rewriteResult.updated}</strong> content items\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{result.errors.length > 0 && (\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10\">\n\t\t\t\t\t<div className=\"border-b border-kumo-danger/50 p-4\">\n\t\t\t\t\t\t<h3 className=\"font-medium text-kumo-danger\">\n\t\t\t\t\t\t\tContent Errors ({result.errors.length})\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"divide-y divide-destructive/20 max-h-64 overflow-y-auto\">\n\t\t\t\t\t\t{result.errors.map((error, i) => (\n\t\t\t\t\t\t\t<div key={i} className=\"p-4\">\n\t\t\t\t\t\t\t\t<p className=\"font-medium\">{error.title}</p>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">{error.error}</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t{hasMediaErrors && (\n\t\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10\">\n\t\t\t\t\t<div className=\"border-b border-kumo-danger/50 p-4\">\n\t\t\t\t\t\t<h3 className=\"font-medium text-kumo-danger\">\n\t\t\t\t\t\t\tMedia Errors ({mediaResult.failed.length})\n\t\t\t\t\t\t</h3>\n\t\t\t\t\t</div>\n\t\t\t\t\t<div className=\"divide-y divide-destructive/20 max-h-64 overflow-y-auto\">\n\t\t\t\t\t\t{mediaResult.failed.map((error, i) => (\n\t\t\t\t\t\t\t<div key={i} className=\"p-4\">\n\t\t\t\t\t\t\t\t<p className=\"font-medium text-sm font-mono truncate\">{error.originalUrl}</p>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">{error.error}</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t<Button variant=\"outline\" onClick={onReset}>\n\t\t\t\t\tImport Another File\n\t\t\t\t</Button>\n\t\t\t\t<LinkButton href=\"/_emdash/admin\">Go to Dashboard</LinkButton>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\n// =============================================================================\n// Author Mapping Step\n// =============================================================================\n\nfunction AuthorMappingStep({\n\tauthorMappings,\n\temdashUsers,\n\tonMappingChange,\n\tonContinue,\n\tonBack,\n}: {\n\tauthorMappings: AuthorMapping[];\n\temdashUsers: UserListItem[];\n\tonMappingChange: (wpLogin: string, emdashUserId: string | null) => void;\n\tonContinue: () => void;\n\tonBack: () => void;\n}) {\n\t// Count matched vs unmatched\n\tconst matchedCount = authorMappings.filter((m) => m.emdashUserId !== null).length;\n\tconst totalCount = authorMappings.length;\n\n\treturn (\n\t\t<div className=\"space-y-6\">\n\t\t\t<div className=\"rounded-lg border bg-kumo-base p-6\">\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t<div className=\"p-3 rounded-full bg-blue-100 dark:bg-blue-900/30\">\n\t\t\t\t\t\t<User className=\"h-6 w-6 text-blue-600 dark:text-blue-400\" />\n\t\t\t\t\t</div>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<h3 className=\"text-lg font-medium\">Map Authors</h3>\n\t\t\t\t\t\t<p className=\"text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\tAssign WordPress authors to EmDash users. Posts will be attributed to the selected\n\t\t\t\t\t\t\tuser.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t\t{matchedCount > 0 && (\n\t\t\t\t\t\t\t<p className=\"text-sm text-green-600 dark:text-green-400 mt-2\">\n\t\t\t\t\t\t\t\t<Check className=\"inline h-4 w-4 mr-1\" />\n\t\t\t\t\t\t\t\t{matchedCount} of {totalCount} authors matched by email\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"rounded-lg border bg-kumo-base\">\n\t\t\t\t<div className=\"border-b p-4\">\n\t\t\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t\t\t<h3 className=\"font-medium\">Author Mapping</h3>\n\t\t\t\t\t\t<span className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{matchedCount} of {totalCount} assigned\n\t\t\t\t\t\t</span>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t\t<div className=\"divide-y\">\n\t\t\t\t\t{authorMappings.map((mapping) => (\n\t\t\t\t\t\t<div key={mapping.wpLogin} className=\"p-4 flex items-center justify-between gap-4\">\n\t\t\t\t\t\t\t<div className=\"flex-1 min-w-0\">\n\t\t\t\t\t\t\t\t<p className=\"font-medium truncate\">{mapping.wpDisplayName}</p>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t{mapping.wpEmail || mapping.wpLogin}\n\t\t\t\t\t\t\t\t\t{mapping.postCount > 0 && (\n\t\t\t\t\t\t\t\t\t\t<span className=\"ml-2\">• {mapping.postCount} posts</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">→</span>\n\t\t\t\t\t\t\t\t<select\n\t\t\t\t\t\t\t\t\tvalue={mapping.emdashUserId || \"\"}\n\t\t\t\t\t\t\t\t\tonChange={(e) => onMappingChange(mapping.wpLogin, e.target.value || null)}\n\t\t\t\t\t\t\t\t\tclassName=\"w-48 px-3 py-2 rounded-md border bg-kumo-base text-sm\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<option value=\"\">Leave unassigned</option>\n\t\t\t\t\t\t\t\t\t{emdashUsers.map((user) => (\n\t\t\t\t\t\t\t\t\t\t<option key={user.id} value={user.id}>\n\t\t\t\t\t\t\t\t\t\t\t{user.name || user.email}\n\t\t\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</select>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{emdashUsers.length === 0 && (\n\t\t\t\t<div className=\"rounded-lg border-l-4 border-l-yellow-500 border border-kumo-line bg-kumo-base p-4\">\n\t\t\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t\t\t<Warning className=\"h-5 w-5 text-yellow-500 flex-shrink-0\" />\n\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t<p className=\"font-medium\">No EmDash users found</p>\n\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle mt-1\">\n\t\t\t\t\t\t\t\tAll imported content will be unassigned. You can reassign authors later from the\n\t\t\t\t\t\t\t\tcontent editor.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t)}\n\n\t\t\t<div className=\"flex gap-3\">\n\t\t\t\t<Button variant=\"outline\" onClick={onBack}>\n\t\t\t\t\tBack\n\t\t\t\t</Button>\n\t\t\t\t<Button onClick={onContinue}>Continue Import</Button>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","import { Button, Input, InputArea, Loader, Switch } from \"@cloudflare/kumo\";\nimport { useMutation, useQuery, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { ConfirmDialog } from \"../components/ConfirmDialog.js\";\nimport { DialogError, getMutationError } from \"../components/DialogError.js\";\nimport {\n\tcreateByline,\n\tdeleteByline,\n\tfetchBylines,\n\tfetchUsers,\n\tupdateByline,\n\ttype BylineSummary,\n\ttype UserListItem,\n} from \"../lib/api\";\n\ninterface BylineFormState {\n\tslug: string;\n\tdisplayName: string;\n\tbio: string;\n\twebsiteUrl: string;\n\tuserId: string | null;\n\tisGuest: boolean;\n}\n\nfunction toFormState(byline?: BylineSummary | null): BylineFormState {\n\tif (!byline) {\n\t\treturn {\n\t\t\tslug: \"\",\n\t\t\tdisplayName: \"\",\n\t\t\tbio: \"\",\n\t\t\twebsiteUrl: \"\",\n\t\t\tuserId: null,\n\t\t\tisGuest: false,\n\t\t};\n\t}\n\n\treturn {\n\t\tslug: byline.slug,\n\t\tdisplayName: byline.displayName,\n\t\tbio: byline.bio ?? \"\",\n\t\twebsiteUrl: byline.websiteUrl ?? \"\",\n\t\tuserId: byline.userId,\n\t\tisGuest: byline.isGuest,\n\t};\n}\n\nfunction getUserLabel(user: UserListItem): string {\n\tif (user.name) return `${user.name} (${user.email})`;\n\treturn user.email;\n}\n\nexport function BylinesPage() {\n\tconst queryClient = useQueryClient();\n\tconst [search, setSearch] = React.useState(\"\");\n\tconst [guestFilter, setGuestFilter] = React.useState<\"all\" | \"guest\" | \"linked\">(\"all\");\n\tconst [selectedId, setSelectedId] = React.useState<string | null>(null);\n\tconst [showDeleteConfirm, setShowDeleteConfirm] = React.useState(false);\n\tconst [allItems, setAllItems] = React.useState<BylineSummary[]>([]);\n\tconst [nextCursor, setNextCursor] = React.useState<string | undefined>(undefined);\n\n\tconst { data, isLoading, error } = useQuery({\n\t\tqueryKey: [\"bylines\", search, guestFilter],\n\t\tqueryFn: () =>\n\t\t\tfetchBylines({\n\t\t\t\tsearch: search || undefined,\n\t\t\t\tisGuest: guestFilter === \"all\" ? undefined : guestFilter === \"guest\",\n\t\t\t\tlimit: 50,\n\t\t\t}),\n\t});\n\n\t// Reset accumulated items when filters change\n\tReact.useEffect(() => {\n\t\tif (data) {\n\t\t\tsetAllItems(data.items);\n\t\t\tsetNextCursor(data.nextCursor);\n\t\t}\n\t}, [data]);\n\n\tconst { data: usersData } = useQuery({\n\t\tqueryKey: [\"users\", \"byline-linking\"],\n\t\tqueryFn: () => fetchUsers({ limit: 100 }),\n\t});\n\n\tconst users = usersData?.items ?? [];\n\n\tconst loadMoreMutation = useMutation({\n\t\tmutationFn: async () => {\n\t\t\tif (!nextCursor) return null;\n\t\t\treturn fetchBylines({\n\t\t\t\tsearch: search || undefined,\n\t\t\t\tisGuest: guestFilter === \"all\" ? undefined : guestFilter === \"guest\",\n\t\t\t\tlimit: 50,\n\t\t\t\tcursor: nextCursor,\n\t\t\t});\n\t\t},\n\t\tonSuccess: (result) => {\n\t\t\tif (result) {\n\t\t\t\tsetAllItems((prev) => [...prev, ...result.items]);\n\t\t\t\tsetNextCursor(result.nextCursor);\n\t\t\t}\n\t\t},\n\t});\n\n\tconst items = allItems;\n\tconst selected = items.find((item) => item.id === selectedId) ?? null;\n\n\tconst [form, setForm] = React.useState<BylineFormState>(() => toFormState(null));\n\n\tReact.useEffect(() => {\n\t\tsetForm(toFormState(selected));\n\t}, [selectedId, selected]);\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: () =>\n\t\t\tcreateByline({\n\t\t\t\tslug: form.slug,\n\t\t\t\tdisplayName: form.displayName,\n\t\t\t\tbio: form.bio || null,\n\t\t\t\twebsiteUrl: form.websiteUrl || null,\n\t\t\t\tuserId: form.userId,\n\t\t\t\tisGuest: form.isGuest,\n\t\t\t}),\n\t\tonSuccess: (created) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"bylines\"] });\n\t\t\tsetSelectedId(created.id);\n\t\t},\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: () => {\n\t\t\tif (!selectedId) throw new Error(\"No byline selected\");\n\t\t\treturn updateByline(selectedId, {\n\t\t\t\tslug: form.slug,\n\t\t\t\tdisplayName: form.displayName,\n\t\t\t\tbio: form.bio || null,\n\t\t\t\twebsiteUrl: form.websiteUrl || null,\n\t\t\t\tuserId: form.userId,\n\t\t\t\tisGuest: form.isGuest,\n\t\t\t});\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"bylines\"] });\n\t\t},\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: () => {\n\t\t\tif (!selectedId) throw new Error(\"No byline selected\");\n\t\t\treturn deleteByline(selectedId);\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"bylines\"] });\n\t\t\tsetSelectedId(null);\n\t\t\tsetShowDeleteConfirm(false);\n\t\t},\n\t});\n\n\tif (isLoading) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center min-h-[30vh]\">\n\t\t\t\t<Loader />\n\t\t\t</div>\n\t\t);\n\t}\n\n\tif (error) {\n\t\treturn <div className=\"text-kumo-danger\">Failed to load bylines: {error.message}</div>;\n\t}\n\n\tconst isSaving = createMutation.isPending || updateMutation.isPending;\n\tconst mutationError = createMutation.error || updateMutation.error || deleteMutation.error;\n\n\treturn (\n\t\t<div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[320px_1fr]\">\n\t\t\t<div className=\"rounded-lg border p-4\">\n\t\t\t\t<div className=\"mb-4 space-y-2\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\tplaceholder=\"Search bylines\"\n\t\t\t\t\t\tvalue={search}\n\t\t\t\t\t\tonChange={(e) => setSearch(e.target.value)}\n\t\t\t\t\t/>\n\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\taria-label=\"Filter byline type\"\n\t\t\t\t\t\t\tvalue={guestFilter}\n\t\t\t\t\t\t\tonChange={(e) => setGuestFilter(e.target.value as \"all\" | \"guest\" | \"linked\")}\n\t\t\t\t\t\t\tclassName=\"w-full rounded border bg-kumo-base px-3 py-2 text-sm\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"all\">All bylines</option>\n\t\t\t\t\t\t\t<option value=\"guest\">Guest only</option>\n\t\t\t\t\t\t\t<option value=\"linked\">Linked only</option>\n\t\t\t\t\t\t</select>\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tsetSelectedId(null);\n\t\t\t\t\t\t\t\tsetForm(toFormState(null));\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tNew\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<div className=\"space-y-2 max-h-[70vh] overflow-auto\">\n\t\t\t\t\t{items.map((item) => {\n\t\t\t\t\t\tconst active = item.id === selectedId;\n\t\t\t\t\t\treturn (\n\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\tkey={item.id}\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tonClick={() => setSelectedId(item.id)}\n\t\t\t\t\t\t\t\tclassName={`w-full rounded border p-3 text-left ${\n\t\t\t\t\t\t\t\t\tactive ? \"border-kumo-brand bg-kumo-brand/10\" : \"border-kumo-line\"\n\t\t\t\t\t\t\t\t}`}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<p className=\"font-medium\">{item.displayName}</p>\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t{item.slug}\n\t\t\t\t\t\t\t\t\t{item.isGuest ? \" - Guest\" : item.userId ? \" - Linked\" : \"\"}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t);\n\t\t\t\t\t})}\n\t\t\t\t\t{items.length === 0 && <p className=\"text-sm text-kumo-subtle\">No bylines found</p>}\n\t\t\t\t\t{nextCursor && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tclassName=\"w-full mt-2\"\n\t\t\t\t\t\t\tonClick={() => loadMoreMutation.mutate()}\n\t\t\t\t\t\t\tdisabled={loadMoreMutation.isPending}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{loadMoreMutation.isPending ? \"Loading...\" : \"Load more\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<div className=\"rounded-lg border p-6\">\n\t\t\t\t<h2 className=\"text-lg font-semibold mb-4\">\n\t\t\t\t\t{selected ? `Edit ${selected.displayName}` : \"Create byline\"}\n\t\t\t\t</h2>\n\n\t\t\t\t<div className=\"space-y-4\">\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Display name\"\n\t\t\t\t\t\tvalue={form.displayName}\n\t\t\t\t\t\tonChange={(e) => setForm((prev) => ({ ...prev, displayName: e.target.value }))}\n\t\t\t\t\t/>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Slug\"\n\t\t\t\t\t\tvalue={form.slug}\n\t\t\t\t\t\tonChange={(e) => setForm((prev) => ({ ...prev, slug: e.target.value }))}\n\t\t\t\t\t/>\n\t\t\t\t\t<Input\n\t\t\t\t\t\tlabel=\"Website URL\"\n\t\t\t\t\t\tvalue={form.websiteUrl}\n\t\t\t\t\t\tonChange={(e) => setForm((prev) => ({ ...prev, websiteUrl: e.target.value }))}\n\t\t\t\t\t/>\n\t\t\t\t\t<InputArea\n\t\t\t\t\t\tlabel=\"Bio\"\n\t\t\t\t\t\tvalue={form.bio}\n\t\t\t\t\t\tonChange={(e) => setForm((prev) => ({ ...prev, bio: e.target.value }))}\n\t\t\t\t\t\trows={5}\n\t\t\t\t\t/>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<label className=\"text-sm font-medium\">Linked user</label>\n\t\t\t\t\t\t<select\n\t\t\t\t\t\t\tvalue={form.userId ?? \"\"}\n\t\t\t\t\t\t\tonChange={(e) =>\n\t\t\t\t\t\t\t\tsetForm((prev) => ({\n\t\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\t\tuserId: e.target.value || null,\n\t\t\t\t\t\t\t\t\tisGuest: e.target.value ? false : prev.isGuest,\n\t\t\t\t\t\t\t\t}))\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tclassName=\"mt-1 w-full rounded border bg-kumo-base px-3 py-2 text-sm\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<option value=\"\">No linked user</option>\n\t\t\t\t\t\t\t{users.map((user) => (\n\t\t\t\t\t\t\t\t<option key={user.id} value={user.id}>\n\t\t\t\t\t\t\t\t\t{getUserLabel(user)}\n\t\t\t\t\t\t\t\t</option>\n\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t</select>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Switch\n\t\t\t\t\t\tlabel=\"Guest byline\"\n\t\t\t\t\t\tchecked={form.isGuest}\n\t\t\t\t\t\tonCheckedChange={(checked) =>\n\t\t\t\t\t\t\tsetForm((prev) => ({\n\t\t\t\t\t\t\t\t...prev,\n\t\t\t\t\t\t\t\tisGuest: checked,\n\t\t\t\t\t\t\t\tuserId: checked ? null : prev.userId,\n\t\t\t\t\t\t\t}))\n\t\t\t\t\t\t}\n\t\t\t\t\t/>\n\n\t\t\t\t\t<DialogError message={getMutationError(mutationError)} />\n\n\t\t\t\t\t<div className=\"flex gap-2 pt-2\">\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\tif (selected) {\n\t\t\t\t\t\t\t\t\tupdateMutation.mutate();\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tcreateMutation.mutate();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\tdisabled={!form.displayName || !form.slug || isSaving}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{isSaving ? \"Saving...\" : selected ? \"Save\" : \"Create\"}\n\t\t\t\t\t\t</Button>\n\n\t\t\t\t\t\t{selected && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"destructive\"\n\t\t\t\t\t\t\t\tonClick={() => setShowDeleteConfirm(true)}\n\t\t\t\t\t\t\t\tdisabled={deleteMutation.isPending}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tDelete\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t<ConfirmDialog\n\t\t\t\topen={showDeleteConfirm}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetShowDeleteConfirm(false);\n\t\t\t\t\tdeleteMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Delete Byline?\"\n\t\t\t\tdescription=\"This removes the byline profile. Content byline links are removed and lead pointers are cleared.\"\n\t\t\t\tconfirmLabel=\"Delete\"\n\t\t\t\tpendingLabel=\"Deleting...\"\n\t\t\t\tisPending={deleteMutation.isPending}\n\t\t\t\terror={deleteMutation.error}\n\t\t\t\tonConfirm={() => deleteMutation.mutate()}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","import { cn } from \"../../lib/utils\";\n\n/** Role level to name mapping */\nconst ROLE_CONFIG: Record<number, { label: string; color: string; description: string }> = {\n\t10: {\n\t\tlabel: \"Subscriber\",\n\t\tcolor: \"gray\",\n\t\tdescription: \"Can view content\",\n\t},\n\t20: {\n\t\tlabel: \"Contributor\",\n\t\tcolor: \"blue\",\n\t\tdescription: \"Can create content\",\n\t},\n\t30: {\n\t\tlabel: \"Author\",\n\t\tcolor: \"green\",\n\t\tdescription: \"Can publish own content\",\n\t},\n\t40: {\n\t\tlabel: \"Editor\",\n\t\tcolor: \"purple\",\n\t\tdescription: \"Can manage all content\",\n\t},\n\t50: {\n\t\tlabel: \"Admin\",\n\t\tcolor: \"red\",\n\t\tdescription: \"Full access\",\n\t},\n};\n\n/** Get role config, with fallback for unknown roles */\nexport function getRoleConfig(role: number) {\n\treturn (\n\t\tROLE_CONFIG[role] ?? {\n\t\t\tlabel: `Role ${role}`,\n\t\t\tcolor: \"gray\",\n\t\t\tdescription: \"Unknown role\",\n\t\t}\n\t);\n}\n\n/** Get role label from role level */\nexport function getRoleLabel(role: number): string {\n\treturn getRoleConfig(role).label;\n}\n\nexport interface RoleBadgeProps {\n\trole: number;\n\tsize?: \"sm\" | \"md\";\n\tshowDescription?: boolean;\n\tclassName?: string;\n}\n\n/**\n * Role badge component with semantic colors\n */\nexport function RoleBadge({\n\trole,\n\tsize = \"sm\",\n\tshowDescription = false,\n\tclassName,\n}: RoleBadgeProps) {\n\tconst config = getRoleConfig(role);\n\n\tconst colorClasses: Record<string, string> = {\n\t\tgray: \"bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200\",\n\t\tblue: \"bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200\",\n\t\tgreen: \"bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200\",\n\t\tpurple: \"bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200\",\n\t\tred: \"bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200\",\n\t};\n\n\tconst sizeClasses = {\n\t\tsm: \"px-2 py-0.5 text-xs\",\n\t\tmd: \"px-2.5 py-1 text-sm\",\n\t};\n\n\treturn (\n\t\t<span\n\t\t\tclassName={cn(\n\t\t\t\t\"inline-flex items-center rounded-full font-medium\",\n\t\t\t\tsizeClasses[size],\n\t\t\t\tcolorClasses[config.color],\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t\ttitle={showDescription ? undefined : config.description}\n\t\t>\n\t\t\t{config.label}\n\t\t\t{showDescription && <span className=\"ml-1 opacity-75\">- {config.description}</span>}\n\t\t</span>\n\t);\n}\n\n/** List of all roles for dropdowns */\nexport const ROLES = [\n\t{ value: 10, label: \"Subscriber\", description: \"Can view content\" },\n\t{ value: 20, label: \"Contributor\", description: \"Can create content\" },\n\t{ value: 30, label: \"Author\", description: \"Can publish own content\" },\n\t{ value: 40, label: \"Editor\", description: \"Can manage all content\" },\n\t{ value: 50, label: \"Admin\", description: \"Full access\" },\n];\n","import { Button, Input, Loader, Select } from \"@cloudflare/kumo\";\nimport { MagnifyingGlass, UserPlus, Prohibit, CheckCircle } from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport type { UserListItem } from \"../../lib/api\";\nimport { cn } from \"../../lib/utils\";\nimport { RoleBadge, ROLES } from \"./RoleBadge\";\n\nexport interface UserListProps {\n\tusers: UserListItem[];\n\tisLoading?: boolean;\n\thasMore?: boolean;\n\tsearchQuery: string;\n\troleFilter: number | undefined;\n\tonSearchChange: (query: string) => void;\n\tonRoleFilterChange: (role: number | undefined) => void;\n\tonSelectUser: (id: string) => void;\n\tonInviteUser: () => void;\n\tonLoadMore?: () => void;\n}\n\n/**\n * User list component with search, filter, and table display\n */\nexport function UserList({\n\tusers,\n\tisLoading,\n\thasMore,\n\tsearchQuery,\n\troleFilter,\n\tonSearchChange,\n\tonRoleFilterChange,\n\tonSelectUser,\n\tonInviteUser,\n\tonLoadMore,\n}: UserListProps) {\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Header */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Users</h1>\n\t\t\t\t<Button onClick={onInviteUser} icon={<UserPlus />}>\n\t\t\t\t\tInvite User\n\t\t\t\t</Button>\n\t\t\t</div>\n\n\t\t\t{/* Filters */}\n\t\t\t<div className=\"flex gap-4\">\n\t\t\t\t<div className=\"relative flex-1 max-w-sm\">\n\t\t\t\t\t<MagnifyingGlass\n\t\t\t\t\t\tclassName=\"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-kumo-subtle\"\n\t\t\t\t\t\taria-hidden=\"true\"\n\t\t\t\t\t/>\n\t\t\t\t\t<Input\n\t\t\t\t\t\ttype=\"search\"\n\t\t\t\t\t\tplaceholder=\"Search by name or email...\"\n\t\t\t\t\t\tclassName=\"pl-10\"\n\t\t\t\t\t\tvalue={searchQuery}\n\t\t\t\t\t\tonChange={(e) => onSearchChange(e.target.value)}\n\t\t\t\t\t\taria-label=\"Search users\"\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\t\t\t\t<Select\n\t\t\t\t\tvalue={roleFilter?.toString() ?? \"all\"}\n\t\t\t\t\tonValueChange={(value) =>\n\t\t\t\t\t\tonRoleFilterChange(value === \"all\" || value === null ? undefined : parseInt(value, 10))\n\t\t\t\t\t}\n\t\t\t\t\titems={{\n\t\t\t\t\t\tall: \"All roles\",\n\t\t\t\t\t\t...Object.fromEntries(ROLES.map((r) => [r.value.toString(), r.label])),\n\t\t\t\t\t}}\n\t\t\t\t\taria-label=\"Filter by role\"\n\t\t\t\t>\n\t\t\t\t\t<Select.Option value=\"all\">All roles</Select.Option>\n\t\t\t\t\t{ROLES.map((role) => (\n\t\t\t\t\t\t<Select.Option key={role.value} value={role.value.toString()}>\n\t\t\t\t\t\t\t{role.label}\n\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t))}\n\t\t\t\t</Select>\n\t\t\t</div>\n\n\t\t\t{/* Table */}\n\t\t\t<div className=\"rounded-md border overflow-x-auto\">\n\t\t\t\t<table className=\"w-full\">\n\t\t\t\t\t<thead>\n\t\t\t\t\t\t<tr className=\"border-b bg-kumo-tint/50\">\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tUser\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tRole\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tStatus\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tLast Login\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t\t<th scope=\"col\" className=\"px-4 py-3 text-left text-sm font-medium\">\n\t\t\t\t\t\t\t\tPasskeys\n\t\t\t\t\t\t\t</th>\n\t\t\t\t\t\t</tr>\n\t\t\t\t\t</thead>\n\t\t\t\t\t<tbody>\n\t\t\t\t\t\t{users.length === 0 && !isLoading ? (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={5} className=\"px-4 py-8 text-center text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t{searchQuery || roleFilter !== undefined ? (\n\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\tNo users found matching your filters.{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t<button\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"text-kumo-brand underline\"\n\t\t\t\t\t\t\t\t\t\t\t\tonClick={() => {\n\t\t\t\t\t\t\t\t\t\t\t\t\tonSearchChange(\"\");\n\t\t\t\t\t\t\t\t\t\t\t\t\tonRoleFilterChange(undefined);\n\t\t\t\t\t\t\t\t\t\t\t\t}}\n\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\tClear filters\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t\t\t\t\tNo users yet.{\" \"}\n\t\t\t\t\t\t\t\t\t\t\t<button className=\"text-kumo-brand underline\" onClick={onInviteUser}>\n\t\t\t\t\t\t\t\t\t\t\t\tInvite your first team member\n\t\t\t\t\t\t\t\t\t\t\t</button>\n\t\t\t\t\t\t\t\t\t\t</>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\tusers.map((user) => (\n\t\t\t\t\t\t\t\t<UserListRow key={user.id} user={user} onSelect={() => onSelectUser(user.id)} />\n\t\t\t\t\t\t\t))\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{isLoading && (\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<td colSpan={5} className=\"px-4 py-4\">\n\t\t\t\t\t\t\t\t\t<div className=\"flex items-center justify-center gap-2 text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t<Loader size=\"sm\" />\n\t\t\t\t\t\t\t\t\t\tLoading...\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t</tr>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</tbody>\n\t\t\t\t</table>\n\t\t\t</div>\n\n\t\t\t{/* Load more */}\n\t\t\t{hasMore && !isLoading && (\n\t\t\t\t<div className=\"flex justify-center\">\n\t\t\t\t\t<Button variant=\"outline\" onClick={onLoadMore}>\n\t\t\t\t\t\tLoad More\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n\ninterface UserListRowProps {\n\tuser: UserListItem;\n\tonSelect: () => void;\n}\n\nfunction UserListRow({ user, onSelect }: UserListRowProps) {\n\tconst displayName = user.name || user.email;\n\tconst lastLogin = user.lastLogin ? new Date(user.lastLogin).toLocaleDateString() : \"Never\";\n\n\treturn (\n\t\t<tr className=\"border-b hover:bg-kumo-tint/25 cursor-pointer\" onClick={onSelect}>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<div className=\"flex items-center gap-3\">\n\t\t\t\t\t{/* Avatar */}\n\t\t\t\t\t{user.avatarUrl ? (\n\t\t\t\t\t\t<img src={user.avatarUrl} alt=\"\" className=\"h-8 w-8 rounded-full object-cover\" />\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"h-8 w-8 rounded-full bg-kumo-tint flex items-center justify-center text-sm font-medium\">\n\t\t\t\t\t\t\t{(user.name || user.email)?.[0]?.toUpperCase() ?? \"?\"}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<div className=\"font-medium\">{displayName}</div>\n\t\t\t\t\t\t{user.name && <div className=\"text-sm text-kumo-subtle\">{user.email}</div>}\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<RoleBadge role={user.role} />\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t{user.disabled ? (\n\t\t\t\t\t<span className=\"inline-flex items-center gap-1 text-sm text-kumo-danger\">\n\t\t\t\t\t\t<Prohibit className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n\t\t\t\t\t\tDisabled\n\t\t\t\t\t</span>\n\t\t\t\t) : (\n\t\t\t\t\t<span className=\"inline-flex items-center gap-1 text-sm text-green-600 dark:text-green-400\">\n\t\t\t\t\t\t<CheckCircle className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n\t\t\t\t\t\tActive\n\t\t\t\t\t</span>\n\t\t\t\t)}\n\t\t\t</td>\n\t\t\t<td className=\"px-4 py-3 text-sm text-kumo-subtle\">{lastLogin}</td>\n\t\t\t<td className=\"px-4 py-3\">\n\t\t\t\t<span className={cn(\"text-sm\", user.credentialCount === 0 && \"text-kumo-subtle\")}>\n\t\t\t\t\t{user.credentialCount}\n\t\t\t\t</span>\n\t\t\t</td>\n\t\t</tr>\n\t);\n}\n\n/** Loading skeleton for user list */\nexport function UserListSkeleton() {\n\treturn (\n\t\t<div className=\"space-y-4\">\n\t\t\t{/* Header skeleton */}\n\t\t\t<div className=\"flex items-center justify-between\">\n\t\t\t\t<div className=\"h-8 w-24 bg-kumo-tint animate-pulse rounded\" />\n\t\t\t\t<div className=\"h-10 w-32 bg-kumo-tint animate-pulse rounded\" />\n\t\t\t</div>\n\n\t\t\t{/* Filters skeleton */}\n\t\t\t<div className=\"flex gap-4\">\n\t\t\t\t<div className=\"h-10 w-64 bg-kumo-tint animate-pulse rounded\" />\n\t\t\t\t<div className=\"h-10 w-44 bg-kumo-tint animate-pulse rounded\" />\n\t\t\t</div>\n\n\t\t\t{/* Table skeleton */}\n\t\t\t<div className=\"rounded-md border\">\n\t\t\t\t<div className=\"border-b bg-kumo-tint/50 px-4 py-3\">\n\t\t\t\t\t<div className=\"h-4 w-full bg-kumo-tint animate-pulse rounded\" />\n\t\t\t\t</div>\n\t\t\t\t{Array.from({ length: 5 }, (_, i) => (\n\t\t\t\t\t<div key={i} className=\"border-b px-4 py-4\">\n\t\t\t\t\t\t<div className=\"h-8 w-full bg-kumo-tint animate-pulse rounded\" />\n\t\t\t\t\t</div>\n\t\t\t\t))}\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n","import { Button, Input, Select } from \"@cloudflare/kumo\";\nimport {\n\tX,\n\tKey,\n\tProhibit,\n\tCheckCircle,\n\tArrowSquareOut,\n\tFloppyDisk,\n\tEnvelope,\n} from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport type { UserDetail as UserDetailType, UpdateUserInput } from \"../../lib/api\";\nimport { useStableCallback } from \"../../lib/hooks\";\nimport { cn } from \"../../lib/utils\";\nimport { ROLES, getRoleLabel } from \"./RoleBadge\";\n\nexport interface UserDetailProps {\n\tuser: UserDetailType | null;\n\tisLoading?: boolean;\n\tisOpen: boolean;\n\tisSaving?: boolean;\n\tisSendingRecovery?: boolean;\n\trecoverySent?: boolean;\n\trecoveryError?: string | null;\n\tcurrentUserId?: string;\n\tonClose: () => void;\n\tonSave: (data: UpdateUserInput) => void;\n\tonDisable: () => void;\n\tonEnable: () => void;\n\tonSendRecovery?: () => void;\n}\n\n/**\n * User detail slide-over panel with inline editing\n */\nexport function UserDetail({\n\tuser,\n\tisLoading,\n\tisOpen,\n\tisSaving,\n\tisSendingRecovery,\n\trecoverySent,\n\trecoveryError,\n\tcurrentUserId,\n\tonClose,\n\tonSave,\n\tonDisable,\n\tonEnable,\n\tonSendRecovery,\n}: UserDetailProps) {\n\tconst [name, setName] = React.useState(user?.name ?? \"\");\n\tconst [email, setEmail] = React.useState(user?.email ?? \"\");\n\tconst [role, setRole] = React.useState(user?.role ?? 30);\n\n\t// Reset form when viewing a different user\n\tconst userIdRef = React.useRef(user?.id);\n\tif (user?.id !== userIdRef.current) {\n\t\tuserIdRef.current = user?.id;\n\t\tif (user) {\n\t\t\tsetName(user.name ?? \"\");\n\t\t\tsetEmail(user.email ?? \"\");\n\t\t\tsetRole(user.role);\n\t\t}\n\t}\n\n\tconst stableOnClose = useStableCallback(onClose);\n\n\t// Close on Escape key\n\tReact.useEffect(() => {\n\t\tconst handleKeyDown = (e: KeyboardEvent) => {\n\t\t\tif (e.key === \"Escape\") {\n\t\t\t\tstableOnClose();\n\t\t\t}\n\t\t};\n\t\tdocument.addEventListener(\"keydown\", handleKeyDown);\n\t\treturn () => document.removeEventListener(\"keydown\", handleKeyDown);\n\t}, [stableOnClose]);\n\n\tif (!isOpen) return null;\n\n\tconst isSelf = user && currentUserId && user.id === currentUserId;\n\n\tconst isDirty =\n\t\tuser && (name !== (user.name ?? \"\") || email !== user.email || role !== user.role);\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tif (!user) return;\n\n\t\tconst data: UpdateUserInput = {};\n\n\t\tif (name !== (user.name ?? \"\")) {\n\t\t\tdata.name = name || undefined;\n\t\t}\n\t\tif (email !== user.email) {\n\t\t\tdata.email = email;\n\t\t}\n\t\tif (role !== user.role && !isSelf) {\n\t\t\tdata.role = role;\n\t\t}\n\n\t\tonSave(data);\n\t};\n\n\treturn (\n\t\t<>\n\t\t\t{/* Backdrop */}\n\t\t\t<div className=\"fixed inset-0 z-40 bg-black/50\" onClick={onClose} aria-hidden=\"true\" />\n\n\t\t\t{/* Panel */}\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"fixed right-0 top-0 z-50 flex h-full w-full max-w-md flex-col bg-kumo-base shadow-xl\",\n\t\t\t\t\t\"transform transition-transform duration-200\",\n\t\t\t\t\tisOpen ? \"translate-x-0\" : \"translate-x-full\",\n\t\t\t\t)}\n\t\t\t\trole=\"dialog\"\n\t\t\t\taria-modal=\"true\"\n\t\t\t\taria-labelledby=\"user-detail-title\"\n\t\t\t>\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"flex items-center justify-between border-b px-6 py-4\">\n\t\t\t\t\t<h2 id=\"user-detail-title\" className=\"text-lg font-semibold\">\n\t\t\t\t\t\tUser Details\n\t\t\t\t\t</h2>\n\t\t\t\t\t<Button variant=\"ghost\" shape=\"square\" onClick={onClose} aria-label=\"Close panel\">\n\t\t\t\t\t\t<X className=\"h-5 w-5\" aria-hidden=\"true\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Content */}\n\t\t\t\t<div className=\"flex-1 overflow-y-auto p-6\">\n\t\t\t\t\t{isLoading ? (\n\t\t\t\t\t\t<UserDetailSkeleton />\n\t\t\t\t\t) : user ? (\n\t\t\t\t\t\t<form id=\"user-edit-form\" onSubmit={handleSubmit} className=\"space-y-6\">\n\t\t\t\t\t\t\t{/* Avatar + editable fields */}\n\t\t\t\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t\t\t\t{user.avatarUrl ? (\n\t\t\t\t\t\t\t\t\t<img\n\t\t\t\t\t\t\t\t\t\tsrc={user.avatarUrl}\n\t\t\t\t\t\t\t\t\t\talt=\"\"\n\t\t\t\t\t\t\t\t\t\tclassName=\"h-16 w-16 shrink-0 rounded-full object-cover\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<div className=\"h-16 w-16 shrink-0 rounded-full bg-kumo-tint flex items-center justify-center text-2xl font-medium\">\n\t\t\t\t\t\t\t\t\t\t{(name || email)?.[0]?.toUpperCase() ?? \"?\"}\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<div className=\"flex-1 min-w-0 space-y-3\">\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Name\"\n\t\t\t\t\t\t\t\t\t\tvalue={name}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setName(e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"Enter name\"\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\tlabel=\"Email\"\n\t\t\t\t\t\t\t\t\t\ttype=\"email\"\n\t\t\t\t\t\t\t\t\t\tvalue={email}\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setEmail(e.target.value)}\n\t\t\t\t\t\t\t\t\t\tplaceholder=\"Enter email\"\n\t\t\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Role + status */}\n\t\t\t\t\t\t\t<div className=\"flex items-end gap-3\">\n\t\t\t\t\t\t\t\t{isSelf ? (\n\t\t\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\t\t\t\tlabel=\"Role\"\n\t\t\t\t\t\t\t\t\t\t\tvalue={getRoleLabel(role)}\n\t\t\t\t\t\t\t\t\t\t\tdisabled\n\t\t\t\t\t\t\t\t\t\t\tclassName=\"cursor-not-allowed\"\n\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle mt-1\">You cannot change your own role</p>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\t\t\tlabel=\"Role\"\n\t\t\t\t\t\t\t\t\t\t\tvalue={role.toString()}\n\t\t\t\t\t\t\t\t\t\t\tonValueChange={(v) => v !== null && setRole(parseInt(v, 10))}\n\t\t\t\t\t\t\t\t\t\t\titems={Object.fromEntries(ROLES.map((r) => [r.value.toString(), r.label]))}\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t{ROLES.map((r) => (\n\t\t\t\t\t\t\t\t\t\t\t\t<Select.Option key={r.value} value={r.value.toString()}>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div>{r.label}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle\">{r.description}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t<div className=\"pb-1\">\n\t\t\t\t\t\t\t\t\t{user.disabled ? (\n\t\t\t\t\t\t\t\t\t\t<span className=\"inline-flex items-center gap-1 text-sm text-kumo-danger\">\n\t\t\t\t\t\t\t\t\t\t\t<Prohibit className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t\t\tDisabled\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<span className=\"inline-flex items-center gap-1 text-sm text-green-600 dark:text-green-400\">\n\t\t\t\t\t\t\t\t\t\t\t<CheckCircle className=\"h-3.5 w-3.5\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t\t\tActive\n\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Info cards */}\n\t\t\t\t\t\t\t<div className=\"grid gap-4\">\n\t\t\t\t\t\t\t\t{/* Timestamps */}\n\t\t\t\t\t\t\t\t<div className=\"rounded-lg border p-4\">\n\t\t\t\t\t\t\t\t\t<h4 className=\"text-sm font-medium text-kumo-subtle mb-3\">Account Info</h4>\n\t\t\t\t\t\t\t\t\t<div className=\"space-y-2 text-sm\">\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Created</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>{new Date(user.createdAt).toLocaleDateString()}</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Last updated</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>{new Date(user.updatedAt).toLocaleDateString()}</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Last login</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>\n\t\t\t\t\t\t\t\t\t\t\t\t{user.lastLogin ? new Date(user.lastLogin).toLocaleDateString() : \"Never\"}\n\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">Email verified</span>\n\t\t\t\t\t\t\t\t\t\t\t<span>{user.emailVerified ? \"Yes\" : \"No\"}</span>\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{/* Passkeys */}\n\t\t\t\t\t\t\t\t<div className=\"rounded-lg border p-4\">\n\t\t\t\t\t\t\t\t\t<h4 className=\"text-sm font-medium text-kumo-subtle mb-3 flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t<Key className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t\tPasskeys ({user.credentials.length})\n\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t{user.credentials.length === 0 ? (\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm text-kumo-subtle\">No passkeys registered</p>\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t\t\t{user.credentials.map((cred) => (\n\t\t\t\t\t\t\t\t\t\t\t\t<div key={cred.id} className=\"flex justify-between text-sm\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div>{cred.name || \"Unnamed passkey\"}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{cred.deviceType === \"multiDevice\" ? \"Synced\" : \"Device-bound\"}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-right text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div>Created {new Date(cred.createdAt).toLocaleDateString()}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-xs\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tLast used {new Date(cred.lastUsedAt).toLocaleDateString()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{/* OAuth accounts */}\n\t\t\t\t\t\t\t\t{user.oauthAccounts.length > 0 && (\n\t\t\t\t\t\t\t\t\t<div className=\"rounded-lg border p-4\">\n\t\t\t\t\t\t\t\t\t\t<h4 className=\"text-sm font-medium text-kumo-subtle mb-3 flex items-center gap-2\">\n\t\t\t\t\t\t\t\t\t\t\t<ArrowSquareOut className=\"h-4 w-4\" aria-hidden=\"true\" />\n\t\t\t\t\t\t\t\t\t\t\tLinked Accounts ({user.oauthAccounts.length})\n\t\t\t\t\t\t\t\t\t\t</h4>\n\t\t\t\t\t\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t\t\t\t\t\t{user.oauthAccounts.map((account, i) => (\n\t\t\t\t\t\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={`${account.provider}-${i}`}\n\t\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"flex justify-between text-sm\"\n\t\t\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"capitalize\">{account.provider}</span>\n\t\t\t\t\t\t\t\t\t\t\t\t\t<span className=\"text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tConnected {new Date(account.createdAt).toLocaleDateString()}\n\t\t\t\t\t\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</form>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<div className=\"text-center text-kumo-subtle py-8\">User not found</div>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Footer actions */}\n\t\t\t\t{user && (\n\t\t\t\t\t<div className=\"border-t px-6 py-4 space-y-2\">\n\t\t\t\t\t\t<div className=\"flex gap-2\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"submit\"\n\t\t\t\t\t\t\t\tform=\"user-edit-form\"\n\t\t\t\t\t\t\t\tclassName=\"flex-1\"\n\t\t\t\t\t\t\t\tdisabled={!isDirty || isSaving}\n\t\t\t\t\t\t\t\ticon={<FloppyDisk />}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{isSaving ? \"Saving...\" : \"Save Changes\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t{!isSelf && (\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant={user.disabled ? \"outline\" : \"destructive\"}\n\t\t\t\t\t\t\t\t\tonClick={user.disabled ? onEnable : onDisable}\n\t\t\t\t\t\t\t\t\ticon={user.disabled ? <CheckCircle /> : <Prohibit />}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{user.disabled ? \"Enable\" : \"Disable\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{!isSelf && onSendRecovery && (\n\t\t\t\t\t\t\t<div className=\"space-y-1\">\n\t\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\t\tclassName=\"w-full\"\n\t\t\t\t\t\t\t\t\tonClick={onSendRecovery}\n\t\t\t\t\t\t\t\t\tdisabled={isSendingRecovery}\n\t\t\t\t\t\t\t\t\ticon={<Envelope />}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{isSendingRecovery ? \"Sending...\" : \"Send Recovery Link\"}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t{recoverySent && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-green-600 dark:text-green-400 text-center\">\n\t\t\t\t\t\t\t\t\t\tRecovery link sent to {user.email}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{recoveryError && (\n\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-danger text-center\">{recoveryError}</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</>\n\t);\n}\n\n/** Loading skeleton for user detail */\nfunction UserDetailSkeleton() {\n\treturn (\n\t\t<div className=\"space-y-6 animate-pulse\">\n\t\t\t{/* Profile skeleton */}\n\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t<div className=\"h-16 w-16 rounded-full bg-kumo-tint\" />\n\t\t\t\t<div className=\"flex-1 space-y-2\">\n\t\t\t\t\t<div className=\"h-6 w-48 bg-kumo-tint rounded\" />\n\t\t\t\t\t<div className=\"h-4 w-36 bg-kumo-tint rounded\" />\n\t\t\t\t\t<div className=\"h-5 w-24 bg-kumo-tint rounded\" />\n\t\t\t\t</div>\n\t\t\t</div>\n\n\t\t\t{/* Cards skeleton */}\n\t\t\t{Array.from({ length: 2 }, (_, i) => (\n\t\t\t\t<div key={i} className=\"rounded-lg border p-4 space-y-3\">\n\t\t\t\t\t<div className=\"h-4 w-24 bg-kumo-tint rounded\" />\n\t\t\t\t\t<div className=\"space-y-2\">\n\t\t\t\t\t\t<div className=\"h-4 w-full bg-kumo-tint rounded\" />\n\t\t\t\t\t\t<div className=\"h-4 w-full bg-kumo-tint rounded\" />\n\t\t\t\t\t\t<div className=\"h-4 w-3/4 bg-kumo-tint rounded\" />\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\t\t\t))}\n\t\t</div>\n\t);\n}\n","import { Button, Dialog, Input, Select } from \"@cloudflare/kumo\";\nimport { Check, Copy, X } from \"@phosphor-icons/react\";\nimport * as React from \"react\";\n\nimport { ROLES } from \"./RoleBadge\";\n\nexport interface InviteUserModalProps {\n\topen: boolean;\n\tisSending?: boolean;\n\terror?: string | null;\n\t/** When set, shows a copy-link view instead of the form (no email provider) */\n\tinviteUrl?: string | null;\n\tonOpenChange: (open: boolean) => void;\n\tonInvite: (email: string, role: number) => void;\n}\n\n/**\n * Invite user modal — sends invite email or shows copy-link fallback\n */\nexport function InviteUserModal({\n\topen,\n\tisSending,\n\terror,\n\tinviteUrl,\n\tonOpenChange,\n\tonInvite,\n}: InviteUserModalProps) {\n\tconst [email, setEmail] = React.useState(\"\");\n\tconst [role, setRole] = React.useState(30); // Default to Author\n\tconst [copied, setCopied] = React.useState(false);\n\tconst [copyError, setCopyError] = React.useState(false);\n\n\tconst copyTimeoutRef = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n\t// Reset form when modal opens\n\tReact.useEffect(() => {\n\t\tif (open) {\n\t\t\tsetEmail(\"\");\n\t\t\tsetRole(30);\n\t\t\tsetCopied(false);\n\t\t\tsetCopyError(false);\n\t\t}\n\t}, [open]);\n\n\t// Clean up timeout on unmount\n\tReact.useEffect(() => {\n\t\treturn () => {\n\t\t\tif (copyTimeoutRef.current) clearTimeout(copyTimeoutRef.current);\n\t\t};\n\t}, []);\n\n\tconst handleSubmit = (e: React.FormEvent) => {\n\t\te.preventDefault();\n\t\tonInvite(email, role);\n\t};\n\n\tconst handleCopyUrl = async () => {\n\t\tif (!inviteUrl) return;\n\t\ttry {\n\t\t\tawait navigator.clipboard.writeText(inviteUrl);\n\t\t\tsetCopied(true);\n\t\t\tsetCopyError(false);\n\t\t\tcopyTimeoutRef.current = setTimeout(setCopied, 2000, false);\n\t\t} catch {\n\t\t\t// Clipboard API can fail in insecure contexts\n\t\t\tsetCopyError(true);\n\t\t}\n\t};\n\n\treturn (\n\t\t<Dialog.Root open={open} onOpenChange={onOpenChange}>\n\t\t\t<Dialog className=\"p-6 max-w-md\" size=\"lg\">\n\t\t\t\t<div className=\"flex items-start justify-between gap-4 mb-4\">\n\t\t\t\t\t<div className=\"flex flex-col space-y-1.5\">\n\t\t\t\t\t\t<Dialog.Title className=\"text-lg font-semibold leading-none tracking-tight\">\n\t\t\t\t\t\t\t{inviteUrl ? \"Invite Link Created\" : \"Invite User\"}\n\t\t\t\t\t\t</Dialog.Title>\n\t\t\t\t\t\t<Dialog.Description className=\"text-sm text-kumo-subtle\">\n\t\t\t\t\t\t\t{inviteUrl\n\t\t\t\t\t\t\t\t? \"No email provider configured. Share this link manually.\"\n\t\t\t\t\t\t\t\t: \"Send an invitation email to a new team member.\"}\n\t\t\t\t\t\t</Dialog.Description>\n\t\t\t\t\t</div>\n\t\t\t\t\t<Dialog.Close\n\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\trender={(props) => (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\t{...props}\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\taria-label=\"Close\"\n\t\t\t\t\t\t\t\tclassName=\"absolute right-4 top-4\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<X className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t<span className=\"sr-only\">Close</span>\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t/>\n\t\t\t\t</div>\n\n\t\t\t\t{inviteUrl ? (\n\t\t\t\t\t/* Copy-link view — shown when no email provider is configured */\n\t\t\t\t\t<div className=\"py-4 space-y-4\">\n\t\t\t\t\t\t<div className=\"rounded-lg border border-amber-200 dark:border-amber-800 bg-amber-50 dark:bg-amber-950/30 p-4\">\n\t\t\t\t\t\t\t<p className=\"text-sm text-amber-800 dark:text-amber-200 font-medium\">\n\t\t\t\t\t\t\t\tShare this link with the invited user\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p className=\"text-xs text-amber-700 dark:text-amber-300 mt-1\">\n\t\t\t\t\t\t\t\tThis link expires in 7 days and can only be used once.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<code className=\"flex-1 rounded bg-kumo-tint px-3 py-2 text-sm font-mono border truncate\">\n\t\t\t\t\t\t\t\t{inviteUrl}\n\t\t\t\t\t\t\t</code>\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tshape=\"square\"\n\t\t\t\t\t\t\t\tonClick={handleCopyUrl}\n\t\t\t\t\t\t\t\taria-label=\"Copy invite link\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t{copied ? (\n\t\t\t\t\t\t\t\t\t<Check className=\"h-4 w-4 text-green-600\" />\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t<Copy className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t{copied && (\n\t\t\t\t\t\t\t<p className=\"text-xs text-green-600 dark:text-green-400\">Copied to clipboard</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t\t{copyError && (\n\t\t\t\t\t\t\t<p className=\"text-xs text-amber-600 dark:text-amber-400\">\n\t\t\t\t\t\t\t\tCould not copy automatically. Please select the URL above and copy manually.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\n\t\t\t\t\t\t<div className=\"flex justify-end\">\n\t\t\t\t\t\t\t<Button type=\"button\" onClick={() => onOpenChange(false)}>\n\t\t\t\t\t\t\t\tDone\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t) : (\n\t\t\t\t\t/* Standard invite form */\n\t\t\t\t\t<form onSubmit={handleSubmit}>\n\t\t\t\t\t\t<div className=\"grid gap-4 py-4\">\n\t\t\t\t\t\t\t{/* Email */}\n\t\t\t\t\t\t\t<Input\n\t\t\t\t\t\t\t\tlabel=\"Email address\"\n\t\t\t\t\t\t\t\ttype=\"email\"\n\t\t\t\t\t\t\t\tvalue={email}\n\t\t\t\t\t\t\t\tonChange={(e) => setEmail(e.target.value)}\n\t\t\t\t\t\t\t\tplaceholder=\"colleague@example.com\"\n\t\t\t\t\t\t\t\trequired\n\t\t\t\t\t\t\t\tautoComplete=\"off\"\n\t\t\t\t\t\t\t/>\n\n\t\t\t\t\t\t\t{/* Role */}\n\t\t\t\t\t\t\t<div className=\"grid gap-2\">\n\t\t\t\t\t\t\t\t<Select\n\t\t\t\t\t\t\t\t\tlabel=\"Role\"\n\t\t\t\t\t\t\t\t\tvalue={role.toString()}\n\t\t\t\t\t\t\t\t\tonValueChange={(v) => v !== null && setRole(parseInt(v, 10))}\n\t\t\t\t\t\t\t\t\titems={Object.fromEntries(ROLES.map((r) => [r.value.toString(), r.label]))}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t{ROLES.map((r) => (\n\t\t\t\t\t\t\t\t\t\t<Select.Option key={r.value} value={r.value.toString()}>\n\t\t\t\t\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t\t\t\t\t<div>{r.label}</div>\n\t\t\t\t\t\t\t\t\t\t\t\t<div className=\"text-xs text-kumo-subtle\">{r.description}</div>\n\t\t\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t\t</Select.Option>\n\t\t\t\t\t\t\t\t\t))}\n\t\t\t\t\t\t\t\t</Select>\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-kumo-subtle\">\n\t\t\t\t\t\t\t\t\tThe invited user will have this role once they complete registration.\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{/* Error message */}\n\t\t\t\t\t\t\t{error && (\n\t\t\t\t\t\t\t\t<div className=\"rounded-md bg-kumo-danger/10 p-3 text-sm text-kumo-danger\">\n\t\t\t\t\t\t\t\t\t{error}\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2\">\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\ttype=\"button\"\n\t\t\t\t\t\t\t\tvariant=\"outline\"\n\t\t\t\t\t\t\t\tonClick={() => onOpenChange(false)}\n\t\t\t\t\t\t\t\tdisabled={isSending}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\tCancel\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t<Button type=\"submit\" disabled={isSending || !email}>\n\t\t\t\t\t\t\t\t{isSending ? \"Sending...\" : \"Send Invite\"}\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</form>\n\t\t\t\t)}\n\t\t\t</Dialog>\n\t\t</Dialog.Root>\n\t);\n}\n","/**\n * Users management page\n *\n * Admin-only route for managing users, roles, and invites.\n */\n\nimport { useQuery, useInfiniteQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport * as React from \"react\";\n\nimport { ConfirmDialog } from \"../components/ConfirmDialog.js\";\nimport {\n\tUserList,\n\tUserListSkeleton,\n\tUserDetail,\n\tInviteUserModal,\n\tgetRoleLabel,\n} from \"../components/users\";\nimport {\n\tfetchUsers,\n\tfetchUser,\n\tupdateUser,\n\tsendRecoveryLink,\n\tdisableUser,\n\tenableUser,\n\tinviteUser,\n\ttype UpdateUserInput,\n} from \"../lib/api\";\n\n/**\n * Debounce hook for search input\n */\nfunction useDebounce<T>(value: T, delay: number): T {\n\tconst [debouncedValue, setDebouncedValue] = React.useState(value);\n\n\tReact.useEffect(() => {\n\t\tconst timer = setTimeout(setDebouncedValue, delay, value);\n\t\treturn () => clearTimeout(timer);\n\t}, [value, delay]);\n\n\treturn debouncedValue;\n}\n\nexport function UsersPage() {\n\tconst queryClient = useQueryClient();\n\n\t// State\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst [roleFilter, setRoleFilter] = React.useState<number | undefined>();\n\tconst [selectedUserId, setSelectedUserId] = React.useState<string | null>(null);\n\tconst [isDetailOpen, setIsDetailOpen] = React.useState(false);\n\tconst [isInviteOpen, setIsInviteOpen] = React.useState(false);\n\tconst [showDisableConfirm, setShowDisableConfirm] = React.useState(false);\n\tconst [showDemoteConfirm, setShowDemoteConfirm] = React.useState(false);\n\tconst [pendingSaveData, setPendingSaveData] = React.useState<UpdateUserInput | null>(null);\n\tconst [inviteError, setInviteError] = React.useState<string | null>(null);\n\tconst [inviteUrl, setInviteUrl] = React.useState<string | null>(null);\n\n\t// Debounced search\n\tconst debouncedSearch = useDebounce(searchQuery, 300);\n\n\t// Queries\n\tconst usersQuery = useInfiniteQuery({\n\t\tqueryKey: [\"users\", debouncedSearch, roleFilter],\n\t\tqueryFn: ({ pageParam }) =>\n\t\t\tfetchUsers({\n\t\t\t\tsearch: debouncedSearch || undefined,\n\t\t\t\trole: roleFilter,\n\t\t\t\tcursor: pageParam,\n\t\t\t}),\n\t\tinitialPageParam: undefined as string | undefined,\n\t\tgetNextPageParam: (lastPage) => lastPage.nextCursor,\n\t});\n\n\tconst userDetailQuery = useQuery({\n\t\tqueryKey: [\"users\", selectedUserId],\n\t\tqueryFn: () => fetchUser(selectedUserId!),\n\t\tenabled: !!selectedUserId,\n\t});\n\n\t// Mutations\n\tconst updateUserMutation = useMutation({\n\t\tmutationFn: ({ id, data }: { id: string; data: UpdateUserInput }) => updateUser(id, data),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"users\"] });\n\t\t\tsetShowDemoteConfirm(false);\n\t\t\tsetPendingSaveData(null);\n\t\t},\n\t});\n\n\tconst disableMutation = useMutation({\n\t\tmutationFn: (id: string) => disableUser(id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"users\"] });\n\t\t\tsetShowDisableConfirm(false);\n\t\t},\n\t});\n\n\tconst enableMutation = useMutation({\n\t\tmutationFn: (id: string) => enableUser(id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"users\"] });\n\t\t},\n\t});\n\n\tconst recoveryMutation = useMutation({\n\t\tmutationFn: (id: string) => sendRecoveryLink(id),\n\t\tonSuccess: () => {\n\t\t\t// Auto-clear success status after a few seconds\n\t\t\tsetTimeout(() => recoveryMutation.reset(), 4000);\n\t\t},\n\t});\n\n\tconst inviteMutation = useMutation({\n\t\tmutationFn: ({ email, role }: { email: string; role: number }) => inviteUser(email, role),\n\t\tonSuccess: (result) => {\n\t\t\tsetInviteError(null);\n\t\t\tif (result.inviteUrl) {\n\t\t\t\t// No email provider — show copy-link view in the modal\n\t\t\t\tsetInviteUrl(result.inviteUrl);\n\t\t\t} else {\n\t\t\t\t// Email sent — close modal\n\t\t\t\tsetIsInviteOpen(false);\n\t\t\t}\n\t\t\t// Refresh user list (invite token was created either way)\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"users\"] });\n\t\t},\n\t\tonError: (error: Error) => {\n\t\t\tsetInviteError(error.message);\n\t\t},\n\t});\n\n\t// Handlers\n\tconst handleSelectUser = (id: string) => {\n\t\tsetSelectedUserId(id);\n\t\tsetIsDetailOpen(true);\n\t};\n\n\tconst handleCloseDetail = () => {\n\t\tsetIsDetailOpen(false);\n\t\t// Keep selectedUserId for a moment to prevent flicker\n\t\tsetTimeout(setSelectedUserId, 200, null);\n\t};\n\n\tconst handleSave = (data: UpdateUserInput) => {\n\t\tif (!selectedUserId) return;\n\n\t\t// Check for role demotion — require confirmation.\n\t\t// Guard: only check when user data is loaded (currentRole defined).\n\t\tconst currentRole = userDetailQuery.data?.role;\n\t\tif (data.role !== undefined && currentRole !== undefined && data.role < currentRole) {\n\t\t\tsetPendingSaveData(data);\n\t\t\tsetShowDemoteConfirm(true);\n\t\t\treturn;\n\t\t}\n\n\t\tupdateUserMutation.mutate({ id: selectedUserId, data });\n\t};\n\n\tconst handleConfirmDemote = () => {\n\t\tif (selectedUserId && pendingSaveData) {\n\t\t\tupdateUserMutation.mutate({ id: selectedUserId, data: pendingSaveData });\n\t\t}\n\t};\n\n\tconst handleDisable = () => {\n\t\tsetShowDisableConfirm(true);\n\t};\n\n\tconst handleConfirmDisable = () => {\n\t\tif (selectedUserId) {\n\t\t\tdisableMutation.mutate(selectedUserId);\n\t\t}\n\t};\n\n\tconst handleEnable = () => {\n\t\tif (selectedUserId) {\n\t\t\tenableMutation.mutate(selectedUserId);\n\t\t}\n\t};\n\n\tconst handleSendRecovery = () => {\n\t\tif (selectedUserId) {\n\t\t\trecoveryMutation.mutate(selectedUserId);\n\t\t}\n\t};\n\n\tconst handleInvite = (email: string, role: number) => {\n\t\tsetInviteError(null);\n\t\tinviteMutation.mutate({ email, role });\n\t};\n\n\t// Loading state\n\tif (usersQuery.isLoading && !usersQuery.data) {\n\t\treturn <UserListSkeleton />;\n\t}\n\n\t// Error state\n\tif (usersQuery.error) {\n\t\treturn (\n\t\t\t<div className=\"rounded-lg border border-kumo-danger/50 bg-kumo-danger/10 p-6 text-center\">\n\t\t\t\t<p className=\"text-kumo-danger\">Failed to load users: {usersQuery.error.message}</p>\n\t\t\t\t<button\n\t\t\t\t\tonClick={() => usersQuery.refetch()}\n\t\t\t\t\tclassName=\"mt-4 text-sm text-kumo-brand underline\"\n\t\t\t\t>\n\t\t\t\t\tTry again\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst users = usersQuery.data?.pages.flatMap((p) => p.items) ?? [];\n\tconst selectedUser = userDetailQuery.data ?? null;\n\n\treturn (\n\t\t<>\n\t\t\t<UserList\n\t\t\t\tusers={users}\n\t\t\t\tisLoading={usersQuery.isFetching}\n\t\t\t\thasMore={!!usersQuery.hasNextPage}\n\t\t\t\tsearchQuery={searchQuery}\n\t\t\t\troleFilter={roleFilter}\n\t\t\t\tonSearchChange={setSearchQuery}\n\t\t\t\tonRoleFilterChange={setRoleFilter}\n\t\t\t\tonSelectUser={handleSelectUser}\n\t\t\t\tonInviteUser={() => setIsInviteOpen(true)}\n\t\t\t\tonLoadMore={() => void usersQuery.fetchNextPage()}\n\t\t\t/>\n\n\t\t\t<UserDetail\n\t\t\t\tuser={selectedUser}\n\t\t\t\tisLoading={userDetailQuery.isLoading}\n\t\t\t\tisOpen={isDetailOpen}\n\t\t\t\tisSaving={updateUserMutation.isPending}\n\t\t\t\tisSendingRecovery={recoveryMutation.isPending}\n\t\t\t\trecoverySent={recoveryMutation.isSuccess}\n\t\t\t\trecoveryError={recoveryMutation.error?.message ?? null}\n\t\t\t\tcurrentUserId={undefined} // Would come from session\n\t\t\t\tonClose={handleCloseDetail}\n\t\t\t\tonSave={handleSave}\n\t\t\t\tonDisable={handleDisable}\n\t\t\t\tonEnable={handleEnable}\n\t\t\t\tonSendRecovery={handleSendRecovery}\n\t\t\t/>\n\n\t\t\t<InviteUserModal\n\t\t\t\topen={isInviteOpen}\n\t\t\t\tisSending={inviteMutation.isPending}\n\t\t\t\terror={inviteError}\n\t\t\t\tinviteUrl={inviteUrl}\n\t\t\t\tonOpenChange={(open) => {\n\t\t\t\t\tsetIsInviteOpen(open);\n\t\t\t\t\tif (!open) {\n\t\t\t\t\t\tsetInviteError(null);\n\t\t\t\t\t\tsetInviteUrl(null);\n\t\t\t\t\t}\n\t\t\t\t}}\n\t\t\t\tonInvite={handleInvite}\n\t\t\t/>\n\n\t\t\t{/* Disable confirmation */}\n\t\t\t<ConfirmDialog\n\t\t\t\topen={showDisableConfirm}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetShowDisableConfirm(false);\n\t\t\t\t\tdisableMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Disable User?\"\n\t\t\t\tdescription={\n\t\t\t\t\t<>\n\t\t\t\t\t\tDisabling <strong>{selectedUser?.name || selectedUser?.email}</strong> will prevent them\n\t\t\t\t\t\tfrom logging in until re-enabled. Their content will be preserved.\n\t\t\t\t\t</>\n\t\t\t\t}\n\t\t\t\tconfirmLabel=\"Disable User\"\n\t\t\t\tpendingLabel=\"Disabling...\"\n\t\t\t\tisPending={disableMutation.isPending}\n\t\t\t\terror={disableMutation.error}\n\t\t\t\tonConfirm={handleConfirmDisable}\n\t\t\t/>\n\n\t\t\t{/* Role demotion confirmation */}\n\t\t\t<ConfirmDialog\n\t\t\t\topen={showDemoteConfirm}\n\t\t\t\tonClose={() => {\n\t\t\t\t\tsetShowDemoteConfirm(false);\n\t\t\t\t\tsetPendingSaveData(null);\n\t\t\t\t\tupdateUserMutation.reset();\n\t\t\t\t}}\n\t\t\t\ttitle=\"Demote User?\"\n\t\t\t\tdescription={\n\t\t\t\t\t<>\n\t\t\t\t\t\tChange <strong>{selectedUser?.name || selectedUser?.email}</strong> from{\" \"}\n\t\t\t\t\t\t<strong>{getRoleLabel(selectedUser?.role ?? 0)}</strong> to{\" \"}\n\t\t\t\t\t\t<strong>{getRoleLabel(pendingSaveData?.role ?? 0)}</strong>? They will lose access to\n\t\t\t\t\t\thigher-level features.\n\t\t\t\t\t</>\n\t\t\t\t}\n\t\t\t\tconfirmLabel=\"Demote User\"\n\t\t\t\tpendingLabel=\"Demoting...\"\n\t\t\t\tisPending={updateUserMutation.isPending}\n\t\t\t\terror={updateUserMutation.error}\n\t\t\t\tonConfirm={handleConfirmDemote}\n\t\t\t/>\n\t\t</>\n\t);\n}\n","/**\n * TanStack Router configuration for EmDash Admin\n *\n * Defines all admin routes and their components.\n */\n\nimport { Loader, Toast } from \"@cloudflare/kumo\";\nimport type { QueryClient } from \"@tanstack/react-query\";\nimport { useQuery, useInfiniteQuery, useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport {\n\tcreateRouter,\n\tcreateRootRouteWithContext,\n\tcreateRoute,\n\tOutlet,\n\tLink,\n\tuseParams,\n\tuseNavigate,\n\tuseSearch,\n} from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { CommentInbox } from \"./components/comments/CommentInbox\";\nimport { ContentEditor } from \"./components/ContentEditor\";\nimport { ContentList } from \"./components/ContentList\";\nimport { ContentTypeEditor } from \"./components/ContentTypeEditor\";\nimport { ContentTypeList } from \"./components/ContentTypeList\";\nimport { Dashboard } from \"./components/Dashboard\";\nimport { DeviceAuthorizePage } from \"./components/DeviceAuthorizePage\";\nimport { LoginPage } from \"./components/LoginPage\";\nimport { MarketplaceBrowse } from \"./components/MarketplaceBrowse\";\nimport { MarketplacePluginDetail } from \"./components/MarketplacePluginDetail\";\nimport { MediaLibrary } from \"./components/MediaLibrary\";\nimport { MenuEditor } from \"./components/MenuEditor\";\nimport { MenuList } from \"./components/MenuList\";\nimport { PluginManager } from \"./components/PluginManager\";\nimport type { PluginBlockDef } from \"./components/PortableTextEditor\";\nimport { Redirects } from \"./components/Redirects\";\nimport { SandboxedPluginPage } from \"./components/SandboxedPluginPage\";\nimport { SectionEditor } from \"./components/SectionEditor\";\nimport { Sections } from \"./components/Sections\";\nimport { Settings } from \"./components/Settings\";\nimport { AllowedDomainsSettings } from \"./components/settings/AllowedDomainsSettings\";\nimport { ApiTokenSettings } from \"./components/settings/ApiTokenSettings\";\nimport { EmailSettings } from \"./components/settings/EmailSettings\";\nimport { GeneralSettings } from \"./components/settings/GeneralSettings\";\nimport { SecuritySettings } from \"./components/settings/SecuritySettings\";\nimport { SeoSettings } from \"./components/settings/SeoSettings\";\nimport { SocialSettings } from \"./components/settings/SocialSettings\";\nimport { SetupWizard } from \"./components/SetupWizard\";\nimport { Shell } from \"./components/Shell\";\nimport { SignupPage } from \"./components/SignupPage\";\nimport { TaxonomyManager } from \"./components/TaxonomyManager\";\nimport { ThemeMarketplaceBrowse } from \"./components/ThemeMarketplaceBrowse\";\nimport { ThemeMarketplaceDetail } from \"./components/ThemeMarketplaceDetail\";\nimport { Widgets } from \"./components/Widgets\";\nimport { WordPressImport } from \"./components/WordPressImport\";\nimport {\n\tapiFetch,\n\tparseApiResponse,\n\tfetchManifest,\n\tfetchContentList,\n\tfetchContent,\n\tcreateContent,\n\tupdateContent,\n\tdeleteContent,\n\tfetchTranslations,\n\tfetchMediaList,\n\tuploadMedia,\n\tdeleteMedia,\n\tfetchCollections,\n\tfetchCollection,\n\tcreateCollection,\n\tupdateCollection,\n\tdeleteCollection,\n\tcreateField,\n\tupdateField,\n\tdeleteField,\n\tfetchOrphanedTables,\n\tregisterOrphanedTable,\n\tfetchUsers,\n\tfetchBylines,\n\tcreateByline,\n\tupdateByline,\n\tsetSearchEnabled,\n\tfetchTrashedContent,\n\trestoreContent,\n\tpermanentDeleteContent,\n\tduplicateContent,\n\tscheduleContent,\n\tunscheduleContent,\n\tpublishContent,\n\tunpublishContent,\n\tdiscardDraft,\n\tfetchRevision,\n\ttype AdminManifest,\n\ttype CreateCollectionInput,\n\ttype UpdateCollectionInput,\n\ttype CreateFieldInput,\n\ttype BylineCreditInput,\n\ttype ContentSeoInput,\n} from \"./lib/api\";\nimport {\n\tfetchComments,\n\tfetchCommentCounts,\n\tupdateCommentStatus,\n\tdeleteComment,\n\tbulkCommentAction,\n\ttype CommentStatus,\n} from \"./lib/api/comments\";\nimport { usePluginPage } from \"./lib/plugin-context\";\nimport { sanitizeRedirectUrl } from \"./lib/url\";\nimport { BylinesPage } from \"./routes/bylines\";\nimport { UsersPage } from \"./routes/users\";\n\n// Router context type\ninterface RouterContext {\n\tqueryClient: QueryClient;\n}\n\n// Create a base root route without Shell for setup\nconst baseRootRoute = createRootRouteWithContext<RouterContext>()({\n\tcomponent: () => <Outlet />,\n});\n\n// Setup route (standalone, no Shell)\nconst setupRoute = createRoute({\n\tgetParentRoute: () => baseRootRoute,\n\tpath: \"/setup\",\n\tcomponent: SetupWizard,\n});\n\n// Login route (standalone, no Shell)\nconst loginRoute = createRoute({\n\tgetParentRoute: () => baseRootRoute,\n\tpath: \"/login\",\n\tcomponent: LoginPageWrapper,\n});\n\nfunction LoginPageWrapper() {\n\t// Extract redirect URL from query params, sanitized to prevent open redirect / XSS\n\tconst searchParams = new URLSearchParams(window.location.search);\n\tconst redirect = sanitizeRedirectUrl(searchParams.get(\"redirect\") || \"/_emdash/admin\");\n\treturn <LoginPage redirectUrl={redirect} />;\n}\n\n// Signup route (standalone, no Shell)\nconst signupRoute = createRoute({\n\tgetParentRoute: () => baseRootRoute,\n\tpath: \"/signup\",\n\tcomponent: SignupPage,\n});\n\n// Device authorization route (standalone, no Shell)\nconst deviceRoute = createRoute({\n\tgetParentRoute: () => baseRootRoute,\n\tpath: \"/device\",\n\tcomponent: DeviceAuthorizePage,\n});\n\n// Layout route with Shell wrapper for admin pages (pathless - matches all admin routes)\nconst adminLayoutRoute = createRoute({\n\tgetParentRoute: () => baseRootRoute,\n\tid: \"_admin\",\n\tcomponent: RootComponent,\n});\n\nfunction RootComponent() {\n\tconst {\n\t\tdata: manifest,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tif (isLoading) {\n\t\treturn <LoadingScreen />;\n\t}\n\n\tif (error || !manifest) {\n\t\treturn <ErrorScreen error={error?.message || \"Failed to load admin\"} />;\n\t}\n\n\t// Plugin admin components are passed via props and available through PluginAdminContext\n\treturn (\n\t\t<Shell manifest={manifest}>\n\t\t\t<Outlet />\n\t\t</Shell>\n\t);\n}\n\n// Dashboard route - matches the index path \"/\"\nconst dashboardRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/\",\n\tcomponent: DashboardPage,\n});\n\nfunction DashboardPage() {\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tif (!manifest) return null;\n\n\treturn <Dashboard manifest={manifest} />;\n}\n\n// Content list route\nconst contentListRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/content/$collection\",\n\tcomponent: ContentListPage,\n\tvalidateSearch: (search: Record<string, unknown>) => ({\n\t\tlocale: typeof search.locale === \"string\" ? search.locale : undefined,\n\t}),\n});\n\nfunction ContentListPage() {\n\tconst { collection } = useParams({ from: \"/_admin/content/$collection\" });\n\tconst { locale: localeParam } = useSearch({ from: \"/_admin/content/$collection\" });\n\tconst queryClient = useQueryClient();\n\tconst navigate = useNavigate();\n\tconst toastManager = Toast.useToastManager();\n\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tconst i18n = manifest?.i18n;\n\n\t// Default to defaultLocale when i18n is enabled and no locale specified\n\tconst activeLocale = i18n ? (localeParam ?? i18n.defaultLocale) : undefined;\n\n\tconst { data, isLoading, error } = useQuery({\n\t\tqueryKey: [\"content\", collection, { locale: activeLocale }],\n\t\tqueryFn: () => fetchContentList(collection, { locale: activeLocale }),\n\t});\n\n\t// Fetch trashed items\n\tconst { data: trashedData, isLoading: isTrashedLoading } = useQuery({\n\t\tqueryKey: [\"content\", collection, \"trash\"],\n\t\tqueryFn: () => fetchTrashedContent(collection),\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (id: string) => deleteContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, \"trash\"] });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to delete\",\n\t\t\t\tdescription: mutationError instanceof Error ? mutationError.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst restoreMutation = useMutation({\n\t\tmutationFn: (id: string) => restoreContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, \"trash\"] });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to restore\",\n\t\t\t\tdescription: mutationError instanceof Error ? mutationError.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst permanentDeleteMutation = useMutation({\n\t\tmutationFn: (id: string) => permanentDeleteContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, \"trash\"] });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to delete\",\n\t\t\t\tdescription: mutationError instanceof Error ? mutationError.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst duplicateMutation = useMutation({\n\t\tmutationFn: (id: string) => duplicateContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection] });\n\t\t},\n\t\tonError: (mutationError) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to duplicate\",\n\t\t\t\tdescription: mutationError instanceof Error ? mutationError.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tif (!manifest) {\n\t\treturn <LoadingScreen />;\n\t}\n\n\tconst collectionConfig = manifest.collections[collection];\n\n\tif (!collectionConfig) {\n\t\treturn <NotFoundPage message={`Collection \"${collection}\" not found`} />;\n\t}\n\n\tif (error) {\n\t\treturn <ErrorScreen error={error.message} />;\n\t}\n\n\tconst handleLocaleChange = (locale: string) => {\n\t\t// Update URL search params without full navigation\n\t\tvoid navigate({\n\t\t\tto: \"/content/$collection\",\n\t\t\tparams: { collection },\n\t\t\tsearch: { locale: locale || undefined },\n\t\t});\n\t};\n\n\treturn (\n\t\t<ContentList\n\t\t\tcollection={collection}\n\t\t\tcollectionLabel={collectionConfig.label}\n\t\t\titems={data?.items || []}\n\t\t\ttrashedItems={trashedData?.items || []}\n\t\t\tisLoading={isLoading}\n\t\t\tisTrashedLoading={isTrashedLoading}\n\t\t\ttrashedCount={trashedData?.items?.length || 0}\n\t\t\tonDelete={(id) => deleteMutation.mutate(id)}\n\t\t\tonRestore={(id) => restoreMutation.mutate(id)}\n\t\t\tonPermanentDelete={(id) => permanentDeleteMutation.mutate(id)}\n\t\t\tonDuplicate={(id) => duplicateMutation.mutate(id)}\n\t\t\ti18n={i18n}\n\t\t\tactiveLocale={activeLocale}\n\t\t\tonLocaleChange={handleLocaleChange}\n\t\t/>\n\t);\n}\n\n/** Extract plugin block definitions from the manifest for Portable Text editor */\nfunction getPluginBlocks(manifest: AdminManifest): PluginBlockDef[] {\n\tconst blocks: PluginBlockDef[] = [];\n\tfor (const [pluginId, plugin] of Object.entries(manifest.plugins)) {\n\t\tif (plugin.portableTextBlocks) {\n\t\t\tfor (const block of plugin.portableTextBlocks) {\n\t\t\t\tblocks.push({ ...block, pluginId });\n\t\t\t}\n\t\t}\n\t}\n\treturn blocks;\n}\n\n// Content new route\nconst contentNewRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/content/$collection/new\",\n\tcomponent: ContentNewPage,\n});\n\nfunction ContentNewPage() {\n\tconst { collection } = useParams({ from: \"/_admin/content/$collection/new\" });\n\tconst navigate = useNavigate();\n\tconst queryClient = useQueryClient();\n\tconst [selectedBylines, setSelectedBylines] = React.useState<BylineCreditInput[]>([]);\n\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: (data: {\n\t\t\tdata: Record<string, unknown>;\n\t\t\tslug?: string;\n\t\t\tbylines?: BylineCreditInput[];\n\t\t}) => createContent(collection, data),\n\t\tonSuccess: (result) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection] });\n\t\t\tvoid navigate({\n\t\t\t\tto: \"/content/$collection/$id\",\n\t\t\t\tparams: { collection, id: result.id },\n\t\t\t});\n\t\t},\n\t});\n\n\tconst pluginBlocks = React.useMemo(() => (manifest ? getPluginBlocks(manifest) : []), [manifest]);\n\n\tconst { data: bylinesData } = useQuery({\n\t\tqueryKey: [\"bylines\"],\n\t\tqueryFn: () => fetchBylines({ limit: 100 }),\n\t});\n\n\tconst createBylineMutation = useMutation({\n\t\tmutationFn: (input: { slug: string; displayName: string }) =>\n\t\t\tcreateByline({ ...input, isGuest: true }),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"bylines\"] });\n\t\t},\n\t});\n\n\tconst updateBylineMutation = useMutation({\n\t\tmutationFn: (input: { id: string; slug: string; displayName: string }) =>\n\t\t\tupdateByline(input.id, {\n\t\t\t\tslug: input.slug,\n\t\t\t\tdisplayName: input.displayName,\n\t\t\t}),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"bylines\"] });\n\t\t},\n\t});\n\n\tif (!manifest) {\n\t\treturn <LoadingScreen />;\n\t}\n\n\tconst collectionConfig = manifest.collections[collection];\n\n\tif (!collectionConfig) {\n\t\treturn <NotFoundPage message={`Collection \"${collection}\" not found`} />;\n\t}\n\n\tconst handleSave = (payload: {\n\t\tdata: Record<string, unknown>;\n\t\tslug?: string;\n\t\tbylines?: BylineCreditInput[];\n\t}) => {\n\t\tcreateMutation.mutate(payload);\n\t};\n\n\treturn (\n\t\t<ContentEditor\n\t\t\tcollection={collection}\n\t\t\tcollectionLabel={collectionConfig.labelSingular || collectionConfig.label}\n\t\t\tfields={collectionConfig.fields}\n\t\t\tisNew\n\t\t\tisSaving={createMutation.isPending}\n\t\t\tonSave={handleSave}\n\t\t\tpluginBlocks={pluginBlocks}\n\t\t\tavailableBylines={bylinesData?.items}\n\t\t\tselectedBylines={selectedBylines}\n\t\t\tonBylinesChange={setSelectedBylines}\n\t\t\tonQuickCreateByline={async (input) => {\n\t\t\t\tconst created = await createBylineMutation.mutateAsync(input);\n\t\t\t\treturn created;\n\t\t\t}}\n\t\t\tonQuickEditByline={async (bylineId, input) => {\n\t\t\t\tconst updated = await updateBylineMutation.mutateAsync({ id: bylineId, ...input });\n\t\t\t\treturn updated;\n\t\t\t}}\n\t\t\tmanifest={manifest ?? null}\n\t\t/>\n\t);\n}\n\n// Content edit route\nconst contentEditRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/content/$collection/$id\",\n\tcomponent: ContentEditPage,\n});\n\n// Editor role level from @emdash-cms/auth\nconst ROLE_EDITOR = 40;\n\nfunction ContentEditPage() {\n\tconst { collection, id } = useParams({\n\t\tfrom: \"/_admin/content/$collection/$id\",\n\t});\n\tconst queryClient = useQueryClient();\n\tconst navigate = useNavigate();\n\tconst toastManager = Toast.useToastManager();\n\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\tconst i18n = manifest?.i18n;\n\n\tconst { data: rawItem, isLoading } = useQuery({\n\t\tqueryKey: [\"content\", collection, id],\n\t\tqueryFn: () => fetchContent(collection, id),\n\t});\n\n\t// Fetch translations when i18n is enabled\n\tconst { data: translationsData } = useQuery({\n\t\tqueryKey: [\"translations\", collection, id],\n\t\tqueryFn: () => fetchTranslations(collection, id),\n\t\tenabled: !!i18n && !!rawItem,\n\t});\n\n\t// When a draft revision exists, fetch its data for the editor form.\n\t// The content table holds published data; the draft revision holds\n\t// the editor's working copy.\n\tconst { data: draftRevision } = useQuery({\n\t\tqueryKey: [\"revision\", rawItem?.draftRevisionId],\n\t\tqueryFn: () => fetchRevision(rawItem!.draftRevisionId!),\n\t\tenabled: !!rawItem?.draftRevisionId,\n\t});\n\n\t// Merge draft revision data into the item for the editor.\n\t// The item's metadata (id, status, slug, etc.) comes from the content table;\n\t// the data fields come from the draft revision if available.\n\tconst item = React.useMemo(() => {\n\t\tif (!rawItem) return undefined;\n\t\tif (!draftRevision?.data) return rawItem;\n\t\t// Strip revision metadata keys (prefixed with _)\n\t\tconst draftData: Record<string, unknown> = {};\n\t\tfor (const [key, value] of Object.entries(draftRevision.data)) {\n\t\t\tif (!key.startsWith(\"_\")) {\n\t\t\t\tdraftData[key] = value;\n\t\t\t}\n\t\t}\n\t\t// Draft slug override\n\t\tconst draftSlug =\n\t\t\ttypeof draftRevision.data._slug === \"string\" ? draftRevision.data._slug : rawItem.slug;\n\t\treturn {\n\t\t\t...rawItem,\n\t\t\tslug: draftSlug,\n\t\t\tdata: { ...rawItem.data, ...draftData },\n\t\t};\n\t}, [rawItem, draftRevision]);\n\n\t// Fetch current user for permission checks\n\tconst { data: currentUser } = useQuery({\n\t\tqueryKey: [\"currentUser\"],\n\t\tqueryFn: async (): Promise<{ id: string; role: number }> => {\n\t\t\tconst response = await apiFetch(\"/_emdash/api/auth/me\");\n\t\t\treturn parseApiResponse<{ id: string; role: number }>(response, \"Failed to fetch user\");\n\t\t},\n\t\tstaleTime: 5 * 60 * 1000,\n\t});\n\n\t// Fetch users list for author selector (only if user is editor+)\n\tconst { data: usersData } = useQuery({\n\t\tqueryKey: [\"users\"],\n\t\tqueryFn: () => fetchUsers({ limit: 100 }),\n\t\tenabled: !!currentUser && currentUser.role >= ROLE_EDITOR,\n\t\tstaleTime: 5 * 60 * 1000,\n\t});\n\n\tconst { data: bylinesData } = useQuery({\n\t\tqueryKey: [\"bylines\"],\n\t\tqueryFn: () => fetchBylines({ limit: 100 }),\n\t});\n\n\tconst createBylineMutation = useMutation({\n\t\tmutationFn: (input: { slug: string; displayName: string }) =>\n\t\t\tcreateByline({ ...input, isGuest: true }),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"bylines\"] });\n\t\t},\n\t});\n\n\tconst updateBylineMutation = useMutation({\n\t\tmutationFn: (input: { id: string; slug: string; displayName: string }) =>\n\t\t\tupdateByline(input.id, {\n\t\t\t\tslug: input.slug,\n\t\t\t\tdisplayName: input.displayName,\n\t\t\t}),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"bylines\"] });\n\t\t},\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: (data: {\n\t\t\tdata?: Record<string, unknown>;\n\t\t\tslug?: string;\n\t\t\tauthorId?: string | null;\n\t\t\tbylines?: BylineCreditInput[];\n\t\t\tskipRevision?: boolean;\n\t\t\tseo?: ContentSeoInput;\n\t\t}) => updateContent(collection, id, data),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, id] });\n\t\t\t// Also invalidate revisions since a new one was created\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"revisions\", collection, id] });\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to save\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\t// Autosave mutation - skips revision creation\n\tconst [lastAutosaveAt, setLastAutosaveAt] = React.useState<Date | null>(null);\n\tconst autosaveMutation = useMutation({\n\t\tmutationFn: (data: {\n\t\t\tdata?: Record<string, unknown>;\n\t\t\tslug?: string;\n\t\t\tbylines?: BylineCreditInput[];\n\t\t}) => updateContent(collection, id, { ...data, skipRevision: true }),\n\t\tonSuccess: () => {\n\t\t\tsetLastAutosaveAt(new Date());\n\t\t\t// Silently update the cache without full invalidation\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, id] });\n\t\t},\n\t\tonError: (err) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Autosave failed\",\n\t\t\t\tdescription: err instanceof Error ? err.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst publishMutation = useMutation({\n\t\tmutationFn: () => publishContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, id] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"revisions\", collection, id] });\n\t\t\ttoastManager.add({ title: \"Published\", description: \"Content is now live\" });\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to publish\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst unpublishMutation = useMutation({\n\t\tmutationFn: () => unpublishContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, id] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"revisions\", collection, id] });\n\t\t\ttoastManager.add({ title: \"Unpublished\", description: \"Content removed from public view\" });\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to unpublish\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst discardDraftMutation = useMutation({\n\t\tmutationFn: () => discardDraft(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, id] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"revisions\", collection, id] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Changes discarded\",\n\t\t\t\tdescription: \"Reverted to published version\",\n\t\t\t});\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to discard changes\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst scheduleMutation = useMutation({\n\t\tmutationFn: (scheduledAt: string) => scheduleContent(collection, id, scheduledAt),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, id] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Scheduled\",\n\t\t\t\tdescription: \"Content has been scheduled for publishing\",\n\t\t\t});\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to schedule\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst unscheduleMutation = useMutation({\n\t\tmutationFn: () => unscheduleContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, id] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Unscheduled\",\n\t\t\t\tdescription: \"Content reverted to draft\",\n\t\t\t});\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to unschedule\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\t// Create translation mutation\n\tconst translateMutation = useMutation({\n\t\tmutationFn: (locale: string) =>\n\t\t\tcreateContent(collection, {\n\t\t\t\tdata: rawItem?.data ?? {},\n\t\t\t\tslug: rawItem?.slug ?? undefined,\n\t\t\t\tlocale,\n\t\t\t\ttranslationOf: id,\n\t\t\t}),\n\t\tonSuccess: (result) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"translations\", collection, id] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection] });\n\t\t\tvoid navigate({\n\t\t\t\tto: \"/content/$collection/$id\",\n\t\t\t\tparams: { collection, id: result.id },\n\t\t\t});\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Translation created\",\n\t\t\t\tdescription: `Created ${result.locale?.toUpperCase() ?? \"new\"} translation`,\n\t\t\t});\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to create translation\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: () => deleteContent(collection, id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"content\", collection, \"trash\"] });\n\t\t\tvoid navigate({\n\t\t\t\tto: \"/content/$collection\",\n\t\t\t\tparams: { collection },\n\t\t\t\tsearch: { locale: undefined },\n\t\t\t});\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to delete\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst pluginBlocks = React.useMemo(() => (manifest ? getPluginBlocks(manifest) : []), [manifest]);\n\n\tif (!manifest) {\n\t\treturn <LoadingScreen />;\n\t}\n\n\tconst collectionConfig = manifest.collections[collection];\n\n\tif (!collectionConfig) {\n\t\treturn <NotFoundPage message={`Collection \"${collection}\" not found`} />;\n\t}\n\n\tif (isLoading) {\n\t\treturn <LoadingScreen />;\n\t}\n\n\tconst handleSave = (payload: {\n\t\tdata: Record<string, unknown>;\n\t\tslug?: string;\n\t\tbylines?: BylineCreditInput[];\n\t}) => {\n\t\tupdateMutation.mutate(payload);\n\t};\n\n\tconst handleAutosave = (payload: {\n\t\tdata: Record<string, unknown>;\n\t\tslug?: string;\n\t\tbylines?: BylineCreditInput[];\n\t}) => {\n\t\tautosaveMutation.mutate(payload);\n\t};\n\n\tconst handleAuthorChange = (authorId: string | null) => {\n\t\tupdateMutation.mutate({ authorId });\n\t};\n\n\tconst handleSeoChange = (seo: ContentSeoInput) => {\n\t\tupdateMutation.mutate({ seo });\n\t};\n\n\treturn (\n\t\t<ContentEditor\n\t\t\tcollection={collection}\n\t\t\tcollectionLabel={collectionConfig.labelSingular || collectionConfig.label}\n\t\t\titem={item}\n\t\t\tfields={collectionConfig.fields}\n\t\t\tisSaving={updateMutation.isPending}\n\t\t\tonSave={handleSave}\n\t\t\tonAutosave={handleAutosave}\n\t\t\tisAutosaving={autosaveMutation.isPending}\n\t\t\tlastAutosaveAt={lastAutosaveAt}\n\t\t\tonPublish={() => publishMutation.mutate()}\n\t\t\tonUnpublish={() => unpublishMutation.mutate()}\n\t\t\tonDiscardDraft={() => discardDraftMutation.mutate()}\n\t\t\tonSchedule={(scheduledAt) => scheduleMutation.mutate(scheduledAt)}\n\t\t\tonUnschedule={() => unscheduleMutation.mutate()}\n\t\t\tisScheduling={scheduleMutation.isPending}\n\t\t\tonDelete={() => deleteMutation.mutate()}\n\t\t\tisDeleting={deleteMutation.isPending}\n\t\t\tsupportsDrafts={collectionConfig.supports.includes(\"drafts\")}\n\t\t\tsupportsRevisions={collectionConfig.supports.includes(\"revisions\")}\n\t\t\tcurrentUser={currentUser}\n\t\t\tusers={usersData?.items}\n\t\t\tonAuthorChange={handleAuthorChange}\n\t\t\ti18n={i18n}\n\t\t\ttranslations={translationsData?.translations}\n\t\t\tonTranslate={(locale) => translateMutation.mutate(locale)}\n\t\t\tpluginBlocks={pluginBlocks}\n\t\t\thasSeo={collectionConfig.hasSeo}\n\t\t\tonSeoChange={handleSeoChange}\n\t\t\tavailableBylines={bylinesData?.items}\n\t\t\tonQuickCreateByline={async (input) => {\n\t\t\t\tconst created = await createBylineMutation.mutateAsync(input);\n\t\t\t\treturn created;\n\t\t\t}}\n\t\t\tonQuickEditByline={async (bylineId, input) => {\n\t\t\t\tconst updated = await updateBylineMutation.mutateAsync({ id: bylineId, ...input });\n\t\t\t\treturn updated;\n\t\t\t}}\n\t\t\tmanifest={manifest ?? null}\n\t\t/>\n\t);\n}\n\n// Media library route\nconst mediaRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/media\",\n\tcomponent: MediaPage,\n});\n\nfunction MediaPage() {\n\tconst queryClient = useQueryClient();\n\n\tconst { data, isLoading, error } = useQuery({\n\t\tqueryKey: [\"media\"],\n\t\tqueryFn: () => fetchMediaList(),\n\t});\n\n\tconst uploadMutation = useMutation({\n\t\tmutationFn: (file: File) => uploadMedia(file),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"media\"] });\n\t\t},\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (id: string) => deleteMedia(id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"media\"] });\n\t\t},\n\t});\n\n\tif (error) {\n\t\treturn <ErrorScreen error={error.message} />;\n\t}\n\n\treturn (\n\t\t<MediaLibrary\n\t\t\titems={data?.items || []}\n\t\t\tisLoading={isLoading}\n\t\t\tonUpload={(file) => uploadMutation.mutate(file)}\n\t\t\tonDelete={(id) => deleteMutation.mutate(id)}\n\t\t/>\n\t);\n}\n\n// Comments moderation inbox route\nconst commentsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/comments\",\n\tcomponent: CommentsPage,\n});\n\n// Admin role level from @emdash-cms/auth\nconst ROLE_ADMIN = 50;\n\nfunction CommentsPage() {\n\tconst queryClient = useQueryClient();\n\tconst toastManager = Toast.useToastManager();\n\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\n\t// Current user for ADMIN check (hard delete)\n\tconst { data: currentUser } = useQuery({\n\t\tqueryKey: [\"currentUser\"],\n\t\tqueryFn: async (): Promise<{ id: string; role: number }> => {\n\t\t\tconst response = await apiFetch(\"/_emdash/api/auth/me\");\n\t\t\treturn parseApiResponse<{ id: string; role: number }>(response, \"Failed to fetch user\");\n\t\t},\n\t\tstaleTime: 5 * 60 * 1000,\n\t});\n\n\t// Filter state\n\tconst [activeStatus, setActiveStatus] = React.useState<CommentStatus>(\"pending\");\n\tconst [collectionFilter, setCollectionFilter] = React.useState(\"\");\n\tconst [searchQuery, setSearchQuery] = React.useState(\"\");\n\tconst [debouncedSearch, setDebouncedSearch] = React.useState(\"\");\n\n\t// Debounce search\n\tReact.useEffect(() => {\n\t\tconst timer = setTimeout(setDebouncedSearch, 300, searchQuery);\n\t\treturn () => clearTimeout(timer);\n\t}, [searchQuery]);\n\n\t// Fetch comments\n\tconst {\n\t\tdata: commentsData,\n\t\tisLoading,\n\t\tfetchNextPage,\n\t\thasNextPage,\n\t} = useInfiniteQuery({\n\t\tqueryKey: [\"comments\", activeStatus, collectionFilter, debouncedSearch],\n\t\tqueryFn: ({ pageParam }) =>\n\t\t\tfetchComments({\n\t\t\t\tstatus: activeStatus,\n\t\t\t\tcollection: collectionFilter || undefined,\n\t\t\t\tsearch: debouncedSearch || undefined,\n\t\t\t\tcursor: pageParam,\n\t\t\t\tlimit: 50,\n\t\t\t}),\n\t\tinitialPageParam: undefined as string | undefined,\n\t\tgetNextPageParam: (lastPage) => lastPage.nextCursor,\n\t});\n\n\t// Fetch counts\n\tconst { data: counts } = useQuery({\n\t\tqueryKey: [\"commentCounts\"],\n\t\tqueryFn: fetchCommentCounts,\n\t});\n\n\t// Status change mutation\n\tconst statusMutation = useMutation({\n\t\tmutationFn: ({ id, status }: { id: string; status: CommentStatus }) =>\n\t\t\tupdateCommentStatus(id, status),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"comments\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"commentCounts\"] });\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to update status\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\t// Delete mutation\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (id: string) => deleteComment(id),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"comments\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"commentCounts\"] });\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to delete comment\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\t// Bulk action mutation\n\tconst bulkMutation = useMutation({\n\t\tmutationFn: ({\n\t\t\tids,\n\t\t\taction,\n\t\t}: {\n\t\t\tids: string[];\n\t\t\taction: \"approve\" | \"spam\" | \"trash\" | \"delete\";\n\t\t}) => bulkCommentAction(ids, action),\n\t\tonSuccess: (result) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"comments\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"commentCounts\"] });\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: `${result.affected} comment${result.affected !== 1 ? \"s\" : \"\"} updated`,\n\t\t\t});\n\t\t},\n\t\tonError: (error) => {\n\t\t\ttoastManager.add({\n\t\t\t\ttitle: \"Failed to perform bulk action\",\n\t\t\t\tdescription: error instanceof Error ? error.message : \"An error occurred\",\n\t\t\t\ttype: \"error\",\n\t\t\t});\n\t\t},\n\t});\n\n\tconst allComments = commentsData?.pages.flatMap((p) => p.items) ?? [];\n\tconst lastPage = commentsData?.pages[commentsData.pages.length - 1];\n\n\t// Require EDITOR role for comment moderation\n\tif (currentUser && currentUser.role < ROLE_EDITOR) {\n\t\treturn (\n\t\t\t<div className=\"flex items-center justify-center min-h-[50vh]\">\n\t\t\t\t<div className=\"text-center\">\n\t\t\t\t\t<h1 className=\"text-2xl font-bold\">Access Denied</h1>\n\t\t\t\t\t<p className=\"mt-2 text-kumo-subtle\">You need Editor permissions to moderate comments.</p>\n\t\t\t\t</div>\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<CommentInbox\n\t\t\tcomments={allComments}\n\t\t\tcounts={counts ?? { pending: 0, approved: 0, spam: 0, trash: 0 }}\n\t\t\tisLoading={isLoading}\n\t\t\tnextCursor={lastPage?.nextCursor}\n\t\t\tcollections={manifest?.collections ?? {}}\n\t\t\tactiveStatus={activeStatus}\n\t\t\tonStatusChange={setActiveStatus}\n\t\t\tcollectionFilter={collectionFilter}\n\t\t\tonCollectionFilterChange={setCollectionFilter}\n\t\t\tsearchQuery={searchQuery}\n\t\t\tonSearchChange={setSearchQuery}\n\t\t\tonCommentStatusChange={(id, status) =>\n\t\t\t\tstatusMutation.mutateAsync({ id, status }).catch(() => {})\n\t\t\t}\n\t\t\tonCommentDelete={(id) => deleteMutation.mutateAsync(id).catch(() => {})}\n\t\t\tonBulkAction={(ids, action) => bulkMutation.mutateAsync({ ids, action }).catch(() => {})}\n\t\t\tonLoadMore={() => {\n\t\t\t\tif (hasNextPage) void fetchNextPage();\n\t\t\t}}\n\t\t\tisAdmin={(currentUser?.role ?? 0) >= ROLE_ADMIN}\n\t\t\tisStatusPending={\n\t\t\t\tstatusMutation.isPending || deleteMutation.isPending || bulkMutation.isPending\n\t\t\t}\n\t\t\tdeleteError={deleteMutation.error}\n\t\t\tonDeleteErrorReset={() => deleteMutation.reset()}\n\t\t/>\n\t);\n}\n\n// Settings route\nconst settingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings\",\n\tcomponent: Settings,\n});\n\n// Security settings route\nconst securitySettingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings/security\",\n\tcomponent: SecuritySettings,\n});\n\n// Allowed domains settings route\nconst allowedDomainsSettingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings/allowed-domains\",\n\tcomponent: AllowedDomainsSettings,\n});\n\n// API tokens settings route\nconst apiTokenSettingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings/api-tokens\",\n\tcomponent: ApiTokenSettings,\n});\n\n// Email settings route\nconst emailSettingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings/email\",\n\tcomponent: EmailSettings,\n});\n\n// General settings route\nconst generalSettingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings/general\",\n\tcomponent: GeneralSettings,\n});\n\n// Social settings route\nconst socialSettingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings/social\",\n\tcomponent: SocialSettings,\n});\n\n// SEO settings route\nconst seoSettingsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/settings/seo\",\n\tcomponent: SeoSettings,\n});\n\n// Plugin manager route\nconst pluginManagerRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/plugins-manager\",\n\tcomponent: PluginManagerPage,\n});\n\nfunction PluginManagerPage() {\n\tconst { data: manifest } = useQuery({\n\t\tqueryKey: [\"manifest\"],\n\t\tqueryFn: fetchManifest,\n\t});\n\treturn <PluginManager manifest={manifest} />;\n}\n\n// Marketplace browse route\nconst marketplaceBrowseRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/plugins/marketplace\",\n\tcomponent: MarketplaceBrowsePage,\n});\n\nfunction MarketplaceBrowsePage() {\n\tconst { data: plugins } = useQuery({\n\t\tqueryKey: [\"plugins\"],\n\t\tqueryFn: async () => {\n\t\t\tconst { fetchPlugins } = await import(\"./lib/api/plugins.js\");\n\t\t\treturn fetchPlugins();\n\t\t},\n\t});\n\n\tconst installedIds = React.useMemo(() => {\n\t\tif (!plugins) return new Set<string>();\n\t\treturn new Set(plugins.map((p) => p.id));\n\t}, [plugins]);\n\n\treturn <MarketplaceBrowse installedPluginIds={installedIds} />;\n}\n\n// Marketplace plugin detail route\nconst marketplaceDetailRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/plugins/marketplace/$pluginId\",\n\tcomponent: MarketplaceDetailPage,\n});\n\nfunction MarketplaceDetailPage() {\n\tconst { pluginId } = useParams({ from: \"/_admin/plugins/marketplace/$pluginId\" });\n\n\tconst { data: plugins } = useQuery({\n\t\tqueryKey: [\"plugins\"],\n\t\tqueryFn: async () => {\n\t\t\tconst { fetchPlugins } = await import(\"./lib/api/plugins.js\");\n\t\t\treturn fetchPlugins();\n\t\t},\n\t});\n\n\tconst installedIds = React.useMemo(() => {\n\t\tif (!plugins) return new Set<string>();\n\t\treturn new Set(plugins.map((p) => p.id));\n\t}, [plugins]);\n\n\treturn <MarketplacePluginDetail pluginId={pluginId} installedPluginIds={installedIds} />;\n}\n\n// Theme marketplace browse route\nconst themeMarketplaceBrowseRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/themes/marketplace\",\n\tcomponent: ThemeMarketplaceBrowse,\n});\n\n// Theme marketplace detail route\nconst themeMarketplaceDetailRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/themes/marketplace/$themeId\",\n\tcomponent: ThemeDetailPage,\n});\n\nfunction ThemeDetailPage() {\n\tconst { themeId } = useParams({ from: \"/_admin/themes/marketplace/$themeId\" });\n\treturn <ThemeMarketplaceDetail themeId={themeId} />;\n}\n\n// WordPress import route\nconst wordpressImportRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/import/wordpress\",\n\tcomponent: WordPressImport,\n});\n\n// Menu routes\nconst menuListRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/menus\",\n\tcomponent: MenuList,\n});\n\nconst menuEditorRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/menus/$name\",\n\tcomponent: MenuEditor,\n});\n\n// Taxonomy manager route\nconst taxonomyRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/taxonomies/$taxonomy\",\n\tcomponent: TaxonomyPage,\n});\n\nfunction TaxonomyPage() {\n\tconst { taxonomy } = useParams({ from: \"/_admin/taxonomies/$taxonomy\" });\n\treturn <TaxonomyManager taxonomyName={taxonomy} />;\n}\n\n// Widgets route\nconst widgetsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/widgets\",\n\tcomponent: Widgets,\n});\n\n// Sections routes\nconst redirectsRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/redirects\",\n\tcomponent: Redirects,\n});\n\nconst sectionsListRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/sections\",\n\tcomponent: Sections,\n});\n\nconst sectionEditRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/sections/$slug\",\n\tcomponent: SectionEditor,\n});\n\n// Users route\nconst usersRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/users\",\n\tcomponent: UsersPage,\n});\n\n// Bylines route\nconst bylinesRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/bylines\",\n\tcomponent: BylinesPage,\n});\n\n// Content Types routes\nconst contentTypesListRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/content-types\",\n\tcomponent: ContentTypesListPage,\n});\n\nfunction ContentTypesListPage() {\n\tconst queryClient = useQueryClient();\n\n\tconst {\n\t\tdata: collections,\n\t\tisLoading: collectionsLoading,\n\t\terror: collectionsError,\n\t} = useQuery({\n\t\tqueryKey: [\"schema\", \"collections\"],\n\t\tqueryFn: fetchCollections,\n\t});\n\n\tconst {\n\t\tdata: orphanedTables,\n\t\tisLoading: orphansLoading,\n\t\terror: orphansError,\n\t} = useQuery({\n\t\tqueryKey: [\"schema\", \"orphans\"],\n\t\tqueryFn: fetchOrphanedTables,\n\t});\n\n\tconst deleteMutation = useMutation({\n\t\tmutationFn: (slug: string) => deleteCollection(slug, true),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"schema\", \"collections\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t},\n\t});\n\n\tconst registerOrphanMutation = useMutation({\n\t\tmutationFn: (slug: string) => registerOrphanedTable(slug),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"schema\", \"collections\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"schema\", \"orphans\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t},\n\t});\n\n\tconst error = collectionsError || orphansError;\n\tif (error) {\n\t\treturn <ErrorScreen error={error.message} />;\n\t}\n\n\treturn (\n\t\t<ContentTypeList\n\t\t\tcollections={collections ?? []}\n\t\t\torphanedTables={orphanedTables}\n\t\t\tisLoading={collectionsLoading || orphansLoading}\n\t\t\tonDelete={(slug) => deleteMutation.mutate(slug)}\n\t\t\tonRegisterOrphan={(slug) => registerOrphanMutation.mutate(slug)}\n\t\t/>\n\t);\n}\n\nconst contentTypesNewRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/content-types/new\",\n\tcomponent: ContentTypesNewPage,\n});\n\nfunction ContentTypesNewPage() {\n\tconst navigate = useNavigate();\n\tconst queryClient = useQueryClient();\n\n\tconst createMutation = useMutation({\n\t\tmutationFn: (input: CreateCollectionInput) => createCollection(input),\n\t\tonSuccess: (result) => {\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"schema\", \"collections\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t\tvoid navigate({\n\t\t\t\tto: \"/content-types/$slug\",\n\t\t\t\tparams: { slug: result.slug },\n\t\t\t});\n\t\t},\n\t});\n\n\treturn (\n\t\t<ContentTypeEditor\n\t\t\tisNew\n\t\t\tisSaving={createMutation.isPending}\n\t\t\tonSave={(input) => {\n\t\t\t\tcreateMutation.mutate(input as CreateCollectionInput);\n\t\t\t}}\n\t\t/>\n\t);\n}\n\nconst contentTypesEditRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/content-types/$slug\",\n\tcomponent: ContentTypesEditPage,\n});\n\nfunction ContentTypesEditPage() {\n\tconst { slug } = useParams({ from: \"/_admin/content-types/$slug\" });\n\tconst queryClient = useQueryClient();\n\n\tconst {\n\t\tdata: collection,\n\t\tisLoading,\n\t\terror,\n\t} = useQuery({\n\t\tqueryKey: [\"schema\", \"collections\", slug],\n\t\tqueryFn: () => fetchCollection(slug),\n\t});\n\n\tconst updateMutation = useMutation({\n\t\tmutationFn: async (input: UpdateCollectionInput) => {\n\t\t\t// Check if search support is being toggled\n\t\t\tconst oldSupports = collection?.supports ?? [];\n\t\t\tconst newSupports = input.supports ?? oldSupports;\n\t\t\tconst hadSearch = oldSupports.includes(\"search\");\n\t\t\tconst hasSearch = newSupports.includes(\"search\");\n\n\t\t\t// Update the collection first\n\t\t\tconst result = await updateCollection(slug, input);\n\n\t\t\t// If search support changed, enable/disable search\n\t\t\tif (hadSearch !== hasSearch) {\n\t\t\t\ttry {\n\t\t\t\t\tawait setSearchEnabled(slug, hasSearch);\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// Log but don't fail the mutation - search can be enabled manually\n\t\t\t\t\tconsole.error(\"Failed to toggle search:\", err);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn result;\n\t\t},\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"schema\", \"collections\", slug],\n\t\t\t});\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"schema\", \"collections\"] });\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t},\n\t});\n\n\tconst addFieldMutation = useMutation({\n\t\tmutationFn: (input: CreateFieldInput) => createField(slug, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"schema\", \"collections\", slug],\n\t\t\t});\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t},\n\t});\n\n\tconst updateFieldMutation = useMutation({\n\t\tmutationFn: ({ fieldSlug, input }: { fieldSlug: string; input: CreateFieldInput }) =>\n\t\t\tupdateField(slug, fieldSlug, input),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"schema\", \"collections\", slug],\n\t\t\t});\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t},\n\t});\n\n\tconst deleteFieldMutation = useMutation({\n\t\tmutationFn: (fieldSlug: string) => deleteField(slug, fieldSlug),\n\t\tonSuccess: () => {\n\t\t\tvoid queryClient.invalidateQueries({\n\t\t\t\tqueryKey: [\"schema\", \"collections\", slug],\n\t\t\t});\n\t\t\tvoid queryClient.invalidateQueries({ queryKey: [\"manifest\"] });\n\t\t},\n\t});\n\n\tif (error) {\n\t\treturn <ErrorScreen error={error.message} />;\n\t}\n\n\tif (isLoading) {\n\t\treturn <LoadingScreen />;\n\t}\n\n\treturn (\n\t\t<ContentTypeEditor\n\t\t\tcollection={collection}\n\t\t\tisSaving={updateMutation.isPending}\n\t\t\tonSave={(input) => updateMutation.mutate(input as UpdateCollectionInput)}\n\t\t\tonAddField={(input) => addFieldMutation.mutateAsync(input)}\n\t\t\tonUpdateField={(fieldSlug, input) => updateFieldMutation.mutateAsync({ fieldSlug, input })}\n\t\t\tonDeleteField={(fieldSlug) => deleteFieldMutation.mutate(fieldSlug)}\n\t\t/>\n\t);\n}\n\n// Plugin page route\nconst pluginRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"/plugins/$pluginId/$\",\n\tcomponent: PluginPage,\n});\n\nfunction PluginPage() {\n\tconst { pluginId } = useParams({ from: \"/_admin/plugins/$pluginId/$\" });\n\tconst { _splat } = useParams({ from: \"/_admin/plugins/$pluginId/$\" });\n\tconst pagePath = \"/\" + (_splat || \"\");\n\n\t// Get plugin page component from context (trusted plugins with React)\n\tconst PluginComponent = usePluginPage(pluginId, pagePath);\n\n\tif (PluginComponent) {\n\t\treturn <PluginComponent />;\n\t}\n\n\t// No React component — fall back to Block Kit rendering\n\treturn <SandboxedPluginPage pluginId={pluginId} page={pagePath} />;\n}\n\n// Catch-all 404 route\nconst notFoundRoute = createRoute({\n\tgetParentRoute: () => adminLayoutRoute,\n\tpath: \"*\",\n\tcomponent: () => <NotFoundPage />,\n});\n\n// Create route tree with admin routes under layout and setup route separate\nconst adminRoutes = adminLayoutRoute.addChildren([\n\tdashboardRoute,\n\tcontentListRoute,\n\tcontentNewRoute,\n\tcontentEditRoute,\n\tcontentTypesListRoute,\n\tcontentTypesNewRoute,\n\tcontentTypesEditRoute,\n\tmediaRoute,\n\tcommentsRoute,\n\tmenuListRoute,\n\tmenuEditorRoute,\n\tpluginManagerRoute,\n\tmarketplaceDetailRoute,\n\tmarketplaceBrowseRoute,\n\tthemeMarketplaceBrowseRoute,\n\tthemeMarketplaceDetailRoute,\n\tpluginRoute,\n\tredirectsRoute,\n\tsectionsListRoute,\n\tsectionEditRoute,\n\ttaxonomyRoute,\n\tusersRoute,\n\tbylinesRoute,\n\twidgetsRoute,\n\tsettingsRoute,\n\tgeneralSettingsRoute,\n\tsocialSettingsRoute,\n\tseoSettingsRoute,\n\tsecuritySettingsRoute,\n\tallowedDomainsSettingsRoute,\n\tapiTokenSettingsRoute,\n\temailSettingsRoute,\n\twordpressImportRoute,\n\tnotFoundRoute,\n]);\n\nconst routeTree = baseRootRoute.addChildren([\n\tsetupRoute,\n\tloginRoute,\n\tsignupRoute,\n\tdeviceRoute,\n\tadminRoutes,\n]);\n\n// Create router\nexport function createAdminRouter(queryClient: QueryClient) {\n\treturn createRouter({\n\t\trouteTree,\n\t\tcontext: { queryClient },\n\t\tbasepath: \"/_emdash/admin\",\n\t\tdefaultPreload: \"intent\",\n\t});\n}\n\n// Declare router type\ndeclare module \"@tanstack/react-router\" {\n\tinterface Register {\n\t\trouter: ReturnType<typeof createAdminRouter>;\n\t}\n}\n\n// Shared components\n\nfunction LoadingScreen() {\n\treturn (\n\t\t<div className=\"flex items-center justify-center min-h-screen\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<Loader />\n\t\t\t\t<p className=\"mt-4 text-kumo-subtle\">Loading configuration...</p>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction ErrorScreen({ error }: { error: string }) {\n\treturn (\n\t\t<div className=\"flex items-center justify-center min-h-screen\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<h1 className=\"text-xl font-bold text-kumo-danger\">Error</h1>\n\t\t\t\t<p className=\"mt-2 text-kumo-subtle\">{error}</p>\n\t\t\t\t<button\n\t\t\t\t\tonClick={() => window.location.reload()}\n\t\t\t\t\tclassName=\"mt-4 px-4 py-2 bg-kumo-brand text-white rounded-md\"\n\t\t\t\t>\n\t\t\t\t\tRetry\n\t\t\t\t</button>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nfunction NotFoundPage({ message }: { message?: string }) {\n\treturn (\n\t\t<div className=\"flex items-center justify-center min-h-[50vh]\">\n\t\t\t<div className=\"text-center\">\n\t\t\t\t<h1 className=\"text-2xl font-bold\">Page Not Found</h1>\n\t\t\t\t<p className=\"mt-2 text-kumo-subtle\">\n\t\t\t\t\t{message || \"The page you're looking for doesn't exist.\"}\n\t\t\t\t</p>\n\t\t\t\t<Link to=\"/\" className=\"mt-4 inline-block text-kumo-brand\">\n\t\t\t\t\tGo to Dashboard\n\t\t\t\t</Link>\n\t\t\t</div>\n\t\t</div>\n\t);\n}\n\nexport { Link, useNavigate, useParams };\n","/**\n * EmDash Admin React Application\n *\n * This is the main entry point for the admin SPA.\n * Uses TanStack Router for client-side routing and TanStack Query for data fetching.\n *\n * Plugin admin components are passed via the pluginAdmins prop and made\n * available throughout the app via PluginAdminContext.\n */\n\nimport { Toasty } from \"@cloudflare/kumo\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport { RouterProvider } from \"@tanstack/react-router\";\nimport * as React from \"react\";\n\nimport { ThemeProvider } from \"./components/ThemeProvider\";\nimport { PluginAdminProvider, type PluginAdmins } from \"./lib/plugin-context\";\nimport { createAdminRouter } from \"./router\";\n\n// Create a query client\nconst queryClient = new QueryClient({\n\tdefaultOptions: {\n\t\tqueries: {\n\t\t\tstaleTime: 1000 * 60, // 1 minute\n\t\t\tretry: 1,\n\t\t},\n\t},\n});\n\n// Create the router with query client context\nconst router = createAdminRouter(queryClient);\n\nexport interface AdminAppProps {\n\t/** Plugin admin modules keyed by plugin ID */\n\tpluginAdmins?: PluginAdmins;\n}\n\n/**\n * Main Admin Application\n */\nconst EMPTY_PLUGINS: PluginAdmins = {};\n\nexport function AdminApp({ pluginAdmins = EMPTY_PLUGINS }: AdminAppProps) {\n\tReact.useEffect(() => {\n\t\tdocument.getElementById(\"emdash-boot-loader\")?.remove();\n\t}, []);\n\n\treturn (\n\t\t<ThemeProvider>\n\t\t\t<Toasty>\n\t\t\t\t<PluginAdminProvider pluginAdmins={pluginAdmins}>\n\t\t\t\t\t<QueryClientProvider client={queryClient}>\n\t\t\t\t\t\t<RouterProvider router={router} />\n\t\t\t\t\t</QueryClientProvider>\n\t\t\t\t</PluginAdminProvider>\n\t\t\t</Toasty>\n\t\t</ThemeProvider>\n\t);\n}\n\nexport default AdminApp;\n"],"x_google_ignoreList":[34],"mappings":"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"}
|