autoblogger 0.1.14 → 0.1.15
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.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -1
- package/dist/index.mjs.map +1 -1
- package/dist/ui.js +33 -23
- package/dist/ui.js.map +1 -1
- package/dist/ui.mjs +33 -23
- package/dist/ui.mjs.map +1 -1
- package/package.json +1 -1
package/dist/ui.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ui/dashboard.tsx","../src/ui/context.tsx","../src/ui/pages/WriterDashboard.tsx","../src/ui/components/ControlButton.tsx","../src/ui/components/ModelSelector.tsx","../src/lib/models.ts","../src/lib/format.ts","../src/ui/pages/EditorPage.tsx","../src/ui/components/EditorToolbar.tsx","../src/lib/cn.ts","../src/ui/components/Skeleton.tsx","../src/ui/components/toolbar/ToolbarButton.tsx","../src/ui/components/toolbar/FormatButtons.tsx","../src/lib/markdown-helpers.ts","../src/ui/components/toolbar/BlockButtons.tsx","../src/ui/components/toolbar/MediaButtons.tsx","../src/ui/components/toolbar/HistoryButtons.tsx","../src/ui/components/RevisionHistoryDropdown.tsx","../src/ui/components/Dropdown.tsx","../src/ui/components/TiptapEditor.tsx","../src/lib/markdown.ts","../node_modules/@tiptap/core/src/helpers/createChainableState.ts","../node_modules/@tiptap/core/src/CommandManager.ts","../node_modules/@tiptap/core/src/commands/index.ts","../node_modules/@tiptap/core/src/commands/blur.ts","../node_modules/@tiptap/core/src/commands/clearContent.ts","../node_modules/@tiptap/core/src/commands/clearNodes.ts","../node_modules/@tiptap/core/src/commands/command.ts","../node_modules/@tiptap/core/src/commands/createParagraphNear.ts","../node_modules/@tiptap/core/src/commands/cut.ts","../node_modules/@tiptap/core/src/commands/deleteCurrentNode.ts","../node_modules/@tiptap/core/src/helpers/getNodeType.ts","../node_modules/@tiptap/core/src/commands/deleteNode.ts","../node_modules/@tiptap/core/src/commands/deleteRange.ts","../node_modules/@tiptap/core/src/commands/deleteSelection.ts","../node_modules/@tiptap/core/src/commands/enter.ts","../node_modules/@tiptap/core/src/commands/exitCode.ts","../node_modules/@tiptap/core/src/commands/extendMarkRange.ts","../node_modules/@tiptap/core/src/utilities/isRegExp.ts","../node_modules/@tiptap/core/src/utilities/objectIncludes.ts","../node_modules/@tiptap/core/src/helpers/getMarkRange.ts","../node_modules/@tiptap/core/src/helpers/getMarkType.ts","../node_modules/@tiptap/core/src/commands/first.ts","../node_modules/@tiptap/core/src/helpers/isTextSelection.ts","../node_modules/@tiptap/core/src/helpers/resolveFocusPosition.ts","../node_modules/@tiptap/core/src/utilities/minMax.ts","../node_modules/@tiptap/core/src/utilities/isAndroid.ts","../node_modules/@tiptap/core/src/utilities/isiOS.ts","../node_modules/@tiptap/core/src/utilities/isSafari.ts","../node_modules/@tiptap/core/src/commands/focus.ts","../node_modules/@tiptap/core/src/commands/forEach.ts","../node_modules/@tiptap/core/src/commands/insertContent.ts","../node_modules/@tiptap/core/src/commands/insertContentAt.ts","../node_modules/@tiptap/core/src/helpers/createNodeFromContent.ts","../node_modules/@tiptap/core/src/utilities/elementFromString.ts","../node_modules/@tiptap/core/src/helpers/selectionToInsertionEnd.ts","../node_modules/@tiptap/core/src/commands/join.ts","../node_modules/@tiptap/core/src/commands/joinItemBackward.ts","../node_modules/@tiptap/core/src/commands/joinItemForward.ts","../node_modules/@tiptap/core/src/commands/joinTextblockBackward.ts","../node_modules/@tiptap/core/src/commands/joinTextblockForward.ts","../node_modules/@tiptap/core/src/utilities/isMacOS.ts","../node_modules/@tiptap/core/src/commands/keyboardShortcut.ts","../node_modules/@tiptap/core/src/commands/lift.ts","../node_modules/@tiptap/core/src/helpers/isNodeActive.ts","../node_modules/@tiptap/core/src/commands/liftEmptyBlock.ts","../node_modules/@tiptap/core/src/commands/liftListItem.ts","../node_modules/@tiptap/core/src/commands/newlineInCode.ts","../node_modules/@tiptap/core/src/helpers/getSchemaTypeNameByName.ts","../node_modules/@tiptap/core/src/utilities/deleteProps.ts","../node_modules/@tiptap/core/src/commands/resetAttributes.ts","../node_modules/@tiptap/core/src/commands/scrollIntoView.ts","../node_modules/@tiptap/core/src/commands/selectAll.ts","../node_modules/@tiptap/core/src/commands/selectNodeBackward.ts","../node_modules/@tiptap/core/src/commands/selectNodeForward.ts","../node_modules/@tiptap/core/src/commands/selectParentNode.ts","../node_modules/@tiptap/core/src/commands/selectTextblockEnd.ts","../node_modules/@tiptap/core/src/commands/selectTextblockStart.ts","../node_modules/@tiptap/core/src/helpers/createDocument.ts","../node_modules/@tiptap/core/src/commands/setContent.ts","../node_modules/@tiptap/core/src/helpers/getMarkAttributes.ts","../node_modules/@tiptap/core/src/helpers/combineTransactionSteps.ts","../node_modules/@tiptap/core/src/helpers/defaultBlockAt.ts","../node_modules/@tiptap/core/src/helpers/findChildren.ts","../node_modules/@tiptap/core/src/helpers/findChildrenInRange.ts","../node_modules/@tiptap/core/src/helpers/findParentNodeClosestToPos.ts","../node_modules/@tiptap/core/src/helpers/findParentNode.ts","../node_modules/@tiptap/core/src/helpers/getExtensionField.ts","../node_modules/@tiptap/core/src/helpers/flattenExtensions.ts","../node_modules/@tiptap/core/src/helpers/generateHTML.ts","../node_modules/@tiptap/core/src/helpers/getHTMLFromFragment.ts","../node_modules/@tiptap/core/src/helpers/getSchemaByResolvedExtensions.ts","../node_modules/@tiptap/core/src/utilities/isFunction.ts","../node_modules/@tiptap/core/src/utilities/callOrReturn.ts","../node_modules/@tiptap/core/src/utilities/isEmptyObject.ts","../node_modules/@tiptap/core/src/helpers/splitExtensions.ts","../node_modules/@tiptap/core/src/helpers/getAttributesFromExtensions.ts","../node_modules/@tiptap/core/src/utilities/mergeAttributes.ts","../node_modules/@tiptap/core/src/helpers/getRenderedAttributes.ts","../node_modules/@tiptap/core/src/utilities/fromString.ts","../node_modules/@tiptap/core/src/helpers/injectExtensionAttributesToParseRule.ts","../node_modules/@tiptap/core/src/utilities/findDuplicates.ts","../node_modules/@tiptap/core/src/helpers/sortExtensions.ts","../node_modules/@tiptap/core/src/helpers/resolveExtensions.ts","../node_modules/@tiptap/core/src/helpers/getSchema.ts","../node_modules/@tiptap/core/src/helpers/generateJSON.ts","../node_modules/@tiptap/core/src/helpers/generateText.ts","../node_modules/@tiptap/core/src/helpers/getTextBetween.ts","../node_modules/@tiptap/core/src/helpers/getText.ts","../node_modules/@tiptap/core/src/helpers/getTextSerializersFromSchema.ts","../node_modules/@tiptap/core/src/helpers/getNodeAttributes.ts","../node_modules/@tiptap/core/src/helpers/getAttributes.ts","../node_modules/@tiptap/core/src/utilities/removeDuplicates.ts","../node_modules/@tiptap/core/src/helpers/getChangedRanges.ts","../node_modules/@tiptap/core/src/helpers/getDebugJSON.ts","../node_modules/@tiptap/core/src/helpers/getMarksBetween.ts","../node_modules/@tiptap/core/src/helpers/getNodeAtPosition.ts","../node_modules/@tiptap/core/src/helpers/getSchemaTypeByName.ts","../node_modules/@tiptap/core/src/helpers/getSplittedAttributes.ts","../node_modules/@tiptap/core/src/helpers/getTextContentFromNodes.ts","../node_modules/@tiptap/core/src/helpers/isMarkActive.ts","../node_modules/@tiptap/core/src/helpers/isActive.ts","../node_modules/@tiptap/core/src/helpers/isAtEndOfNode.ts","../node_modules/@tiptap/core/src/helpers/isAtStartOfNode.ts","../node_modules/@tiptap/core/src/helpers/isExtensionRulesEnabled.ts","../node_modules/@tiptap/core/src/helpers/isList.ts","../node_modules/@tiptap/core/src/helpers/isNodeEmpty.ts","../node_modules/@tiptap/core/src/helpers/isNodeSelection.ts","../node_modules/@tiptap/core/src/helpers/MappablePosition.ts","../node_modules/@tiptap/core/src/helpers/posToDOMRect.ts","../node_modules/@tiptap/core/src/helpers/rewriteUnknownContent.ts","../node_modules/@tiptap/core/src/commands/setMark.ts","../node_modules/@tiptap/core/src/commands/setMeta.ts","../node_modules/@tiptap/core/src/commands/setNode.ts","../node_modules/@tiptap/core/src/commands/setNodeSelection.ts","../node_modules/@tiptap/core/src/commands/setTextDirection.ts","../node_modules/@tiptap/core/src/commands/setTextSelection.ts","../node_modules/@tiptap/core/src/commands/sinkListItem.ts","../node_modules/@tiptap/core/src/commands/splitBlock.ts","../node_modules/@tiptap/core/src/commands/splitListItem.ts","../node_modules/@tiptap/core/src/commands/toggleList.ts","../node_modules/@tiptap/core/src/commands/toggleMark.ts","../node_modules/@tiptap/core/src/commands/toggleNode.ts","../node_modules/@tiptap/core/src/commands/toggleWrap.ts","../node_modules/@tiptap/core/src/commands/undoInputRule.ts","../node_modules/@tiptap/core/src/commands/unsetAllMarks.ts","../node_modules/@tiptap/core/src/commands/unsetMark.ts","../node_modules/@tiptap/core/src/commands/unsetTextDirection.ts","../node_modules/@tiptap/core/src/commands/updateAttributes.ts","../node_modules/@tiptap/core/src/commands/wrapIn.ts","../node_modules/@tiptap/core/src/commands/wrapInList.ts","../node_modules/@tiptap/core/src/Editor.ts","../node_modules/@tiptap/core/src/EventEmitter.ts","../node_modules/@tiptap/core/src/ExtensionManager.ts","../node_modules/@tiptap/core/src/InputRule.ts","../node_modules/@tiptap/core/src/utilities/isPlainObject.ts","../node_modules/@tiptap/core/src/utilities/mergeDeep.ts","../node_modules/@tiptap/core/src/Extendable.ts","../node_modules/@tiptap/core/src/Mark.ts","../node_modules/@tiptap/core/src/PasteRule.ts","../node_modules/@tiptap/core/src/utilities/isNumber.ts","../node_modules/@tiptap/core/src/extensions/index.ts","../node_modules/@tiptap/core/src/extensions/clipboardTextSerializer.ts","../node_modules/@tiptap/core/src/Extension.ts","../node_modules/@tiptap/core/src/extensions/commands.ts","../node_modules/@tiptap/core/src/extensions/delete.ts","../node_modules/@tiptap/core/src/extensions/drop.ts","../node_modules/@tiptap/core/src/extensions/editable.ts","../node_modules/@tiptap/core/src/extensions/focusEvents.ts","../node_modules/@tiptap/core/src/extensions/keymap.ts","../node_modules/@tiptap/core/src/extensions/paste.ts","../node_modules/@tiptap/core/src/extensions/tabindex.ts","../node_modules/@tiptap/core/src/extensions/textDirection.ts","../node_modules/@tiptap/core/src/NodePos.ts","../node_modules/@tiptap/core/src/style.ts","../node_modules/@tiptap/core/src/utilities/createStyleTag.ts","../node_modules/@tiptap/core/src/inputRules/markInputRule.ts","../node_modules/@tiptap/core/src/inputRules/nodeInputRule.ts","../node_modules/@tiptap/core/src/inputRules/textblockTypeInputRule.ts","../node_modules/@tiptap/core/src/inputRules/textInputRule.ts","../node_modules/@tiptap/core/src/inputRules/wrappingInputRule.ts","../node_modules/@tiptap/core/src/jsx-runtime.ts","../node_modules/@tiptap/core/src/lib/ResizableNodeView.ts","../node_modules/@tiptap/core/src/utilities/canInsertNode.ts","../node_modules/@tiptap/core/src/utilities/escapeForRegEx.ts","../node_modules/@tiptap/core/src/utilities/isString.ts","../node_modules/@tiptap/core/src/utilities/markdown/index.ts","../node_modules/@tiptap/core/src/utilities/markdown/attributeUtils.ts","../node_modules/@tiptap/core/src/utilities/markdown/createAtomBlockMarkdownSpec.ts","../node_modules/@tiptap/core/src/utilities/markdown/createBlockMarkdownSpec.ts","../node_modules/@tiptap/core/src/utilities/markdown/createInlineMarkdownSpec.ts","../node_modules/@tiptap/core/src/utilities/markdown/parseIndentedBlocks.ts","../node_modules/@tiptap/core/src/utilities/markdown/renderNestedMarkdownContent.ts","../node_modules/@tiptap/core/src/MarkView.ts","../node_modules/@tiptap/core/src/Node.ts","../node_modules/@tiptap/core/src/NodeView.ts","../node_modules/@tiptap/core/src/pasteRules/markPasteRule.ts","../node_modules/@tiptap/core/src/pasteRules/nodePasteRule.ts","../node_modules/@tiptap/core/src/pasteRules/textPasteRule.ts","../node_modules/@tiptap/core/src/Tracker.ts","../src/lib/comment-mark.ts","../src/ui/components/CommentsPanel.tsx","../src/ui/components/CommentThread.tsx","../src/lib/comments.ts","../src/ui/components/TagsSection.tsx","../src/ui/hooks/useComments.ts","../src/ui/pages/SettingsPage.tsx","../src/ui/components/Navbar.tsx","../src/ui/hooks/useKeyboard.ts","../src/ui/hooks/useChat.tsx","../src/ui/components/ChatPanel.tsx","../src/ui/hooks/useAIModels.ts"],"sourcesContent":["'use client'\n\nimport { useState, type ReactNode } from 'react'\nimport { Save, Loader2 } from 'lucide-react'\nimport type { CustomFieldConfig, StylesConfig } from './types'\nimport { DashboardProvider, useDashboardContext, type Session, type EditorState, type EditHandler } from './context'\nimport { WriterDashboard } from './pages/WriterDashboard'\nimport { EditorPage } from './pages/EditorPage'\nimport { SettingsPage } from './pages/SettingsPage'\nimport { Navbar } from './components/Navbar'\nimport { useDashboardKeyboard } from './hooks/useKeyboard'\n\ninterface AutobloggerDashboardProps {\n basePath?: string\n apiBasePath?: string\n styles?: StylesConfig\n fields?: CustomFieldConfig[]\n session?: Session | null\n onEditorStateChange?: (state: EditorState | null) => void\n onRegisterEditHandler?: (handler: EditHandler | null) => void\n onToggleView?: (currentPath: string, slug?: string) => void\n // Navbar props\n onSignOut?: () => void\n onThemeToggle?: () => void\n theme?: 'light' | 'dark'\n navbarRightSlot?: ReactNode\n}\n\nexport function AutobloggerDashboard({\n basePath = '/writer',\n apiBasePath = '/api/cms',\n styles,\n fields = [],\n session = null,\n onEditorStateChange,\n onRegisterEditHandler,\n onToggleView,\n onSignOut,\n onThemeToggle,\n theme,\n navbarRightSlot,\n}: AutobloggerDashboardProps) {\n return (\n <DashboardProvider basePath={basePath} apiBasePath={apiBasePath} styles={styles} fields={fields} session={session} onEditorStateChange={onEditorStateChange} onRegisterEditHandler={onRegisterEditHandler}>\n <DashboardLayout \n onToggleView={onToggleView}\n onSignOut={onSignOut}\n onThemeToggle={onThemeToggle}\n theme={theme}\n navbarRightSlot={navbarRightSlot}\n />\n </DashboardProvider>\n )\n}\n\ninterface DashboardLayoutProps {\n onToggleView?: (currentPath: string, slug?: string) => void\n onSignOut?: () => void\n onThemeToggle?: () => void\n theme?: 'light' | 'dark'\n navbarRightSlot?: ReactNode\n}\n\nfunction DashboardLayout({ \n onToggleView,\n onSignOut,\n onThemeToggle,\n theme,\n navbarRightSlot,\n}: DashboardLayoutProps) {\n const { basePath, currentPath, navigate, onEditorStateChange } = useDashboardContext()\n const [editorState, setEditorState] = useState<EditorState | null>(null)\n\n // Extract slug from editor path\n const editorSlug = currentPath.startsWith('/editor/') \n ? currentPath.replace('/editor/', '') \n : currentPath === '/editor' ? undefined : undefined\n \n const isEditorPage = currentPath.startsWith('/editor')\n\n // Handle editor state changes - track internally AND forward to host app\n const handleEditorStateChange = (state: EditorState | null) => {\n setEditorState(state)\n onEditorStateChange?.(state)\n }\n\n useDashboardKeyboard({\n basePath,\n onToggleView: onToggleView ? () => onToggleView(currentPath, editorSlug) : undefined,\n onToggleSettings: () => {\n if (currentPath.startsWith('/settings')) navigate('/')\n else navigate('/settings')\n },\n onNewPost: () => {\n if (currentPath === '/' || currentPath === '') navigate('/editor')\n },\n onEscape: () => {\n if (currentPath !== '/' && currentPath !== '') navigate('/')\n },\n })\n\n // Build the right slot with save button + any custom slot from host app\n const rightSlotWithSave = (\n <>\n {/* Save button - only show on editor page */}\n {isEditorPage && editorState && (\n <button\n type=\"button\"\n onClick={() => editorState.onSave('draft')}\n disabled={!editorState.hasUnsavedChanges || !!editorState.savingAs}\n className=\"w-9 h-9 rounded-md border border-border hover:bg-accent text-muted-foreground flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed\"\n aria-label=\"Save\"\n title={editorState.hasUnsavedChanges ? 'Save changes (⌘S)' : 'No unsaved changes'}\n >\n {editorState.savingAs ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n </button>\n )}\n {/* Extra slot from host app */}\n {navbarRightSlot}\n </>\n )\n\n return (\n <div className=\"min-h-screen bg-background flex flex-col\">\n <Navbar\n onSignOut={onSignOut}\n onThemeToggle={onThemeToggle}\n theme={theme}\n rightSlot={rightSlotWithSave}\n />\n <main className=\"flex-1\">\n <DashboardRouter path={currentPath} onEditorStateChange={handleEditorStateChange} />\n </main>\n </div>\n )\n}\n\nfunction DashboardRouter({ path, onEditorStateChange }: { path: string; onEditorStateChange?: (state: EditorState | null) => void }) {\n // Strip query params from path for routing\n const pathWithoutQuery = path.split('?')[0]\n \n if (pathWithoutQuery === '/' || pathWithoutQuery === '') return <WriterDashboard />\n if (pathWithoutQuery.startsWith('/editor')) {\n const slug = pathWithoutQuery.replace('/editor/', '').replace('/editor', '')\n // Use path as key to force remount when navigating to same editor with different query params\n return <EditorPage key={path} slug={slug || undefined} onEditorStateChange={onEditorStateChange} />\n }\n if (pathWithoutQuery.startsWith('/settings')) return <SettingsPage subPath={pathWithoutQuery.replace('/settings', '')} />\n return <div className=\"max-w-4xl mx-auto px-6 py-8\"><p className=\"text-muted-foreground\">Page not found: {path}</p></div>\n}\n\nexport type { AutobloggerDashboardProps }\n","'use client'\n\nimport { createContext, useContext, useState, useEffect, useCallback, useMemo, type ReactNode } from 'react'\nimport type { CustomFieldConfig, StylesConfig } from './types'\n\nexport interface DashboardConfig {\n fields: CustomFieldConfig[]\n styles: Required<StylesConfig>\n}\n\n// Session info (provided by the host app)\nexport interface SessionUser {\n id?: string\n name?: string | null\n email?: string\n role?: string\n}\n\nexport interface Session {\n user?: SessionUser\n}\n\n// Shared data fetched once by the provider\nexport interface SharedData {\n counts: Record<string, number>\n settings: { autoDraftEnabled: boolean }\n posts: unknown[]\n suggestedPosts: unknown[]\n aiSettings: { defaultModel: string; availableModels: unknown[] }\n}\n\n// Editor content for parent app integration (e.g., chat context)\nexport interface EditorContent {\n title: string\n subtitle: string\n markdown: string\n}\n\n// Edit command from parent app (e.g., AI agent mode)\nexport interface EditCommand {\n type: 'replace_all' | 'replace_section' | 'insert' | 'delete'\n title?: string\n subtitle?: string\n markdown?: string\n find?: string\n replace?: string\n position?: 'before' | 'after' | 'start' | 'end'\n}\n\nexport type EditHandler = (edit: EditCommand) => boolean\n\n// Editor state exposed to parent app\nexport interface EditorState {\n hasUnsavedChanges: boolean\n status: 'draft' | 'published'\n savingAs: 'draft' | 'published' | null\n onSave: (status: 'draft' | 'published') => void\n confirmLeave: () => boolean\n // Content for chat integration\n content: EditorContent\n}\n\nexport interface DashboardContextValue {\n basePath: string\n apiBasePath: string\n styles: Required<StylesConfig>\n fields: CustomFieldConfig[]\n currentPath: string\n navigate: (path: string) => void\n goBack: () => void\n canGoBack: boolean\n config: DashboardConfig\n // Session\n session: Session | null\n // Shared data\n sharedData: SharedData | null\n sharedDataLoading: boolean\n refetchSharedData: () => Promise<void>\n // Editor state callback (for parent app integration)\n onEditorStateChange?: (state: EditorState | null) => void\n // Edit handler registration (for AI agent mode)\n onRegisterEditHandler?: (handler: EditHandler | null) => void\n}\n\nconst DashboardContext = createContext<DashboardContextValue | null>(null)\n\nexport function useDashboardContext() {\n const ctx = useContext(DashboardContext)\n if (!ctx) throw new Error('useDashboardContext must be used within DashboardProvider')\n return ctx\n}\n\nconst DEFAULT_STYLES: Required<StylesConfig> = {\n container: 'max-w-[680px] mx-auto px-6',\n title: 'text-2xl font-bold',\n subtitle: 'text-lg text-muted-foreground',\n byline: 'text-sm text-muted-foreground',\n prose: 'prose dark:prose-invert max-w-none',\n}\n\nfunction extractPath(pathname: string, basePath: string): string {\n const normalized = pathname.replace(/\\/$/, '')\n const base = basePath.replace(/\\/$/, '')\n if (normalized === base) return '/'\n if (normalized.startsWith(base + '/')) return normalized.slice(base.length) || '/'\n return '/'\n}\n\ninterface DashboardProviderProps {\n basePath?: string\n apiBasePath?: string\n styles?: StylesConfig\n fields?: CustomFieldConfig[]\n session?: Session | null\n onEditorStateChange?: (state: EditorState | null) => void\n onRegisterEditHandler?: (handler: EditHandler | null) => void\n children: ReactNode\n}\n\nexport function DashboardProvider({\n basePath = '/writer',\n apiBasePath = '/api/cms',\n styles,\n fields = [],\n session = null,\n onEditorStateChange,\n onRegisterEditHandler,\n children,\n}: DashboardProviderProps) {\n const [currentPath, setCurrentPath] = useState('/')\n const [sharedData, setSharedData] = useState<SharedData | null>(null)\n const [sharedDataLoading, setSharedDataLoading] = useState(true)\n // Track navigation history within the dashboard\n const [historyDepth, setHistoryDepth] = useState(0)\n \n useEffect(() => {\n if (typeof window !== 'undefined') {\n setCurrentPath(extractPath(window.location.pathname, basePath))\n const handlePopState = () => {\n setCurrentPath(extractPath(window.location.pathname, basePath))\n // Decrement history depth on back navigation\n setHistoryDepth(d => Math.max(0, d - 1))\n }\n window.addEventListener('popstate', handlePopState)\n return () => window.removeEventListener('popstate', handlePopState)\n }\n }, [basePath])\n\n const navigate = useCallback((path: string) => {\n const fullPath = path.startsWith('/') ? basePath + path : basePath + '/' + path\n window.history.pushState({}, '', fullPath)\n setCurrentPath(path.startsWith('/') ? path : '/' + path)\n // Increment history depth on navigation\n setHistoryDepth(d => d + 1)\n }, [basePath])\n\n const goBack = useCallback(() => {\n if (historyDepth > 0) {\n // We have navigation history, use browser back\n window.history.back()\n } else {\n // No history (user landed directly on subpage), navigate to root\n navigate('/')\n }\n }, [historyDepth, navigate])\n\n const canGoBack = historyDepth > 0\n\n // Fetch all shared data once on mount\n const fetchSharedData = useCallback(async () => {\n setSharedDataLoading(true)\n try {\n const [countsRes, settingsRes, postsRes, aiSettingsRes] = await Promise.all([\n fetch(`${apiBasePath}/admin/counts`).then(r => r.ok ? r.json() : null),\n fetch(`${apiBasePath}/settings`).then(r => r.ok ? r.json() : null),\n fetch(`${apiBasePath}/posts`).then(r => r.ok ? r.json() : null),\n fetch(`${apiBasePath}/ai/settings`).then(r => r.ok ? r.json() : null),\n ])\n\n const autoDraftEnabled = settingsRes?.data?.autoDraftEnabled ?? false\n \n // Fetch suggested posts only if autoDraft is enabled\n let suggestedPosts: unknown[] = []\n if (autoDraftEnabled) {\n const suggestedRes = await fetch(`${apiBasePath}/posts?status=suggested`).then(r => r.ok ? r.json() : null)\n suggestedPosts = suggestedRes?.data || []\n }\n\n setSharedData({\n counts: countsRes?.data || {},\n settings: { autoDraftEnabled },\n posts: postsRes?.data || [],\n suggestedPosts,\n aiSettings: {\n defaultModel: aiSettingsRes?.data?.defaultModel || 'claude-sonnet',\n availableModels: aiSettingsRes?.data?.availableModels || [],\n },\n })\n } catch (err) {\n console.error('Failed to fetch shared data:', err)\n setSharedData({\n counts: {},\n settings: { autoDraftEnabled: false },\n posts: [],\n suggestedPosts: [],\n aiSettings: { defaultModel: 'claude-sonnet', availableModels: [] },\n })\n } finally {\n setSharedDataLoading(false)\n }\n }, [apiBasePath])\n\n useEffect(() => {\n fetchSharedData()\n }, [fetchSharedData])\n\n const mergedStyles = useMemo<Required<StylesConfig>>(\n () => ({ ...DEFAULT_STYLES, ...styles }),\n [styles]\n )\n \n const config = useMemo<DashboardConfig>(\n () => ({ fields, styles: mergedStyles }),\n [fields, mergedStyles]\n )\n\n const contextValue = useMemo<DashboardContextValue>(() => ({\n basePath, \n apiBasePath, \n styles: mergedStyles, \n fields, \n currentPath, \n navigate,\n goBack,\n canGoBack,\n config,\n session,\n sharedData,\n sharedDataLoading,\n refetchSharedData: fetchSharedData,\n onEditorStateChange,\n onRegisterEditHandler,\n }), [\n basePath,\n apiBasePath,\n mergedStyles,\n fields,\n currentPath,\n navigate,\n goBack,\n canGoBack,\n config,\n session,\n sharedData,\n sharedDataLoading,\n fetchSharedData,\n onEditorStateChange,\n onRegisterEditHandler,\n ])\n\n return (\n <DashboardContext.Provider value={contextValue}>\n {children}\n </DashboardContext.Provider>\n )\n}\n\nexport { DashboardContext }\n","'use client'\n\nimport { useState, useEffect, useMemo, useRef } from 'react'\nimport { Globe, Brain, ArrowUp, ChevronDown, Check, X, Plus, Search, MoreVertical, ExternalLink } from 'lucide-react'\nimport { useDashboardContext } from '../context'\nimport { ControlButton } from '../components/ControlButton'\nimport { ModelSelector } from '../components/ModelSelector'\nimport { LENGTH_OPTIONS, DEFAULT_MODELS, type AIModelOption } from '../../lib/models'\nimport { formatRelativeTime } from '../../lib/format'\n\ninterface Post {\n id: string\n title: string\n slug: string\n status: 'draft' | 'published' | 'suggested' | 'deleted'\n wordCount?: number\n updatedAt: string\n publishedAt: string | null\n}\n\ninterface SuggestedPost {\n id: string\n title: string\n subtitle: string | null\n slug: string\n sourceUrl: string | null\n createdAt: string\n topic: { id: string; name: string } | null\n}\n\ntype TabType = 'all' | 'drafts' | 'published'\n\nexport function WriterDashboard() {\n const { apiBasePath, navigate, sharedData, sharedDataLoading } = useDashboardContext()\n const [posts, setPosts] = useState<Post[]>([])\n const [loading, setLoading] = useState(true)\n const [searchQuery, setSearchQuery] = useState('')\n const [searchOpen, setSearchOpen] = useState(false)\n const [activeTab, setActiveTab] = useState<TabType>('all')\n const [suggestedPosts, setSuggestedPosts] = useState<SuggestedPost[]>([])\n const [autoDraftEnabled, setAutoDraftEnabled] = useState(false)\n const [suggestedOpen, setSuggestedOpen] = useState(false)\n const [actionLoading, setActionLoading] = useState<string | null>(null)\n \n const [models, setModels] = useState<AIModelOption[]>(DEFAULT_MODELS)\n const [modelId, setModelId] = useState('claude-sonnet')\n const [length, setLength] = useState<number>(500)\n const [webEnabled, setWebEnabled] = useState(false)\n const [thinkingEnabled, setThinkingEnabled] = useState(false)\n const [lengthOpen, setLengthOpen] = useState(false)\n\n // Sync with shared data from context\n useEffect(() => {\n if (sharedData) {\n setPosts(sharedData.posts as Post[])\n setSuggestedPosts(sharedData.suggestedPosts as SuggestedPost[])\n setAutoDraftEnabled(sharedData.settings.autoDraftEnabled)\n if (sharedData.aiSettings.availableModels.length > 0) {\n setModels(sharedData.aiSettings.availableModels as AIModelOption[])\n }\n if (sharedData.aiSettings.defaultModel) {\n setModelId(sharedData.aiSettings.defaultModel)\n }\n setLoading(false)\n }\n }, [sharedData])\n\n // Also stop loading when sharedDataLoading becomes false (even if data is empty)\n useEffect(() => {\n if (!sharedDataLoading) {\n setLoading(false)\n }\n }, [sharedDataLoading])\n\n const currentModel = models.find(m => m.id === modelId)\n const draftCount = useMemo(() => posts.filter(p => p.status === 'draft').length, [posts])\n const publishedCount = useMemo(() => posts.filter(p => p.status === 'published').length, [posts])\n\n const filteredPosts = useMemo(() => {\n let result = posts.filter(p => p.status !== 'suggested' && p.status !== 'deleted')\n if (searchQuery) {\n result = result.filter(p => p.title.toLowerCase().includes(searchQuery.toLowerCase()))\n }\n if (activeTab === 'drafts') result = result.filter(p => p.status === 'draft')\n else if (activeTab === 'published') result = result.filter(p => p.status === 'published')\n return result.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n }, [posts, searchQuery, activeTab])\n\n async function handleDelete(id: string) {\n if (!confirm('Delete this post?')) return\n await fetch(`${apiBasePath}/posts/${id}`, { method: 'DELETE' })\n setPosts(posts.filter(p => p.id !== id))\n }\n\n async function handlePublish(id: string) {\n await fetch(`${apiBasePath}/posts/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'published' }),\n })\n setPosts(posts.map(p => p.id === id ? { ...p, status: 'published' as const } : p))\n }\n\n async function handleUnpublish(id: string) {\n await fetch(`${apiBasePath}/posts/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'draft' }),\n })\n setPosts(posts.map(p => p.id === id ? { ...p, status: 'draft' as const } : p))\n }\n\n async function handleAcceptSuggested(post: SuggestedPost) {\n setActionLoading(post.id)\n try {\n const res = await fetch(`${apiBasePath}/posts/${post.id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'draft' }),\n })\n if (res.ok) navigate(`/editor/${post.slug}`)\n } finally {\n setActionLoading(null)\n }\n }\n\n async function handleRejectSuggested(post: SuggestedPost) {\n if (!confirm(`Reject \"${post.title}\"?`)) return\n setActionLoading(post.id)\n try {\n const res = await fetch(`${apiBasePath}/posts/${post.id}`, { method: 'DELETE' })\n if (res.ok) setSuggestedPosts(suggestedPosts.filter(p => p.id !== post.id))\n } finally {\n setActionLoading(null)\n }\n }\n\n function handleIdeaSubmit(e: React.FormEvent<HTMLFormElement>) {\n e.preventDefault()\n const formData = new FormData(e.currentTarget)\n const idea = (formData.get('idea') as string)?.trim()\n if (idea) {\n const params = new URLSearchParams({\n idea,\n model: modelId,\n length: String(length),\n ...(webEnabled && { web: '1' }),\n ...(thinkingEnabled && { thinking: '1' })\n })\n navigate(`/editor?${params}`)\n }\n }\n\n if (loading) {\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n <div className=\"mt-4 mb-8\">\n <div className=\"h-6 w-48 bg-muted rounded animate-pulse mb-4\" />\n <div className=\"h-24 w-full bg-muted rounded animate-pulse\" />\n </div>\n <div className=\"flex border-b border-border mb-6 gap-4\">\n <div className=\"h-10 w-16 bg-muted rounded animate-pulse\" />\n <div className=\"h-10 w-20 bg-muted rounded animate-pulse\" />\n <div className=\"h-10 w-24 bg-muted rounded animate-pulse\" />\n </div>\n {[1,2,3,4,5].map(i => (\n <div key={i} className=\"h-16 bg-muted rounded animate-pulse mb-2\" />\n ))}\n </div>\n )\n }\n\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8 pb-16\">\n <button\n onClick={() => navigate('/editor')}\n className=\"sm:hidden fixed bottom-6 right-6 w-14 h-14 rounded-full shadow-lg z-50 bg-primary text-primary-foreground flex items-center justify-center\"\n >\n <Plus className=\"w-6 h-6\" />\n </button>\n\n <div className=\"mt-4 mb-8\">\n <h2 className=\"text-lg font-semibold mb-4\">What's on your mind?</h2>\n <form onSubmit={handleIdeaSubmit}>\n <div className=\"relative\">\n <textarea\n name=\"idea\"\n placeholder=\"Describe your idea...\"\n rows={3}\n className=\"w-full min-h-[100px] px-3 py-2 pr-14 border border-input rounded-md bg-transparent resize-none text-base focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring placeholder:text-muted-foreground\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.currentTarget.form?.requestSubmit()\n }\n }}\n />\n <button\n type=\"submit\"\n className=\"absolute bottom-3 right-3 rounded-full w-10 h-10 bg-secondary text-secondary-foreground flex items-center justify-center hover:bg-secondary/80\"\n >\n <ArrowUp className=\"w-5 h-5\" />\n </button>\n </div>\n \n <div className=\"flex items-center gap-3\">\n <div className=\"relative\">\n <ControlButton onClick={() => setLengthOpen(!lengthOpen)}>\n {length} words\n <ChevronDown className=\"w-3.5 h-3.5\" />\n </ControlButton>\n {lengthOpen && (\n <div className=\"absolute top-full left-0 mt-1 bg-popover border border-border rounded-md shadow-md z-50\">\n {LENGTH_OPTIONS.map(len => (\n <button\n key={len}\n onClick={() => { setLength(len); setLengthOpen(false) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between min-w-[120px]\"\n >\n {len} words\n {length === len && <Check className=\"w-4 h-4\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n \n <ControlButton onClick={() => setWebEnabled(!webEnabled)} active={webEnabled} title=\"Search the web\">\n <Globe className=\"w-4 h-4\" />\n </ControlButton>\n \n <ControlButton onClick={() => setThinkingEnabled(!thinkingEnabled)} active={thinkingEnabled} title=\"Enable thinking mode\">\n <Brain className=\"w-4 h-4\" />\n </ControlButton>\n \n <ModelSelector models={models} selectedModel={modelId} onModelChange={setModelId} currentModel={currentModel} />\n </div>\n </form>\n </div>\n\n {autoDraftEnabled && (\n <section className=\"mb-8\">\n <button onClick={() => setSuggestedOpen(!suggestedOpen)} className=\"flex items-center gap-2 text-lg font-semibold mb-4\">\n <ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform ${suggestedOpen ? '' : '-rotate-90'}`} />\n Suggested\n {suggestedPosts.length > 0 && (\n <span className=\"bg-primary text-primary-foreground text-xs px-1.5 py-0.5 rounded-full min-w-[1.25rem] text-center\">\n {suggestedPosts.length}\n </span>\n )}\n </button>\n {suggestedOpen && (\n suggestedPosts.length > 0 ? (\n <div className=\"space-y-2\">\n {suggestedPosts.map(post => (\n <div key={post.id} className=\"flex items-center justify-between gap-4 py-3 border-b border-border\">\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{post.title}</p>\n <div className=\"flex items-center gap-3 text-sm text-muted-foreground\">\n {post.topic && <span className=\"bg-muted px-2 py-0.5 rounded text-xs\">{post.topic.name}</span>}\n <span>{new Date(post.createdAt).toLocaleDateString()}</span>\n {post.sourceUrl && (\n <a\n href={post.sourceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 hover:text-foreground\"\n onClick={(e) => e.stopPropagation()}\n >\n <ExternalLink className=\"h-3 w-3\" />\n Source\n </a>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n <button\n onClick={() => handleRejectSuggested(post)}\n disabled={actionLoading === post.id}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-accent rounded-md\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleAcceptSuggested(post)}\n disabled={actionLoading === post.id}\n className=\"px-3 py-1.5 bg-primary text-primary-foreground rounded text-sm flex items-center gap-1\"\n >\n <Check className=\"h-4 w-4\" />\n Accept\n </button>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-muted-foreground text-sm\">\n No suggested essays — configure topics to generate drafts\n </p>\n )\n )}\n </section>\n )}\n\n <div className=\"relative flex items-center justify-between border-b border-border mb-6\">\n <div className={`flex ${searchOpen ? 'invisible sm:visible' : ''}`}>\n {(['all', 'drafts', 'published'] as TabType[]).map(tab => (\n <button\n key={tab}\n onClick={() => setActiveTab(tab)}\n className={`px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors ${\n activeTab === tab ? 'border-foreground text-foreground' : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.charAt(0).toUpperCase() + tab.slice(1)}\n <span className=\"text-muted-foreground ml-1\">\n ({tab === 'all' ? posts.filter(p => p.status !== 'suggested' && p.status !== 'deleted').length : tab === 'drafts' ? draftCount : publishedCount})\n </span>\n </button>\n ))}\n </div>\n \n <div className={`flex items-center gap-2 pb-2 ${searchOpen ? 'absolute inset-x-0 sm:relative' : ''}`}>\n <button onClick={() => navigate('/editor')} className=\"hidden sm:flex p-2 text-muted-foreground hover:text-foreground rounded-md hover:bg-accent\">\n <Plus className=\"w-4 h-4\" />\n </button>\n {searchOpen ? (\n <div className=\"flex items-center gap-2 flex-1 sm:flex-initial\">\n <input\n type=\"search\"\n placeholder=\"Search...\"\n value={searchQuery}\n onChange={e => setSearchQuery(e.target.value)}\n autoFocus\n className=\"flex-1 sm:w-48 px-3 py-1.5 text-base bg-background border border-border rounded-md outline-none focus-visible:border-ring\"\n />\n <button onClick={() => { setSearchOpen(false); setSearchQuery('') }} className=\"p-2 text-muted-foreground hover:text-foreground sm:hidden\">\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n ) : (\n <button onClick={() => setSearchOpen(true)} className=\"p-2 text-muted-foreground hover:text-foreground rounded-md hover:bg-accent\">\n <Search className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n </div>\n\n <section>\n {filteredPosts.length > 0 ? (\n <div className=\"space-y-0\">\n {filteredPosts.map(post => (\n <PostItem\n key={post.id}\n post={post}\n onNavigate={() => navigate(`/editor/${post.slug}`)}\n onDelete={() => handleDelete(post.id)}\n onPublish={() => handlePublish(post.id)}\n onUnpublish={() => handleUnpublish(post.id)}\n showStatus={activeTab === 'all'}\n />\n ))}\n </div>\n ) : (\n <p className=\"text-muted-foreground py-8 text-center\">\n {searchQuery ? 'No matching articles' : 'No articles yet'}\n </p>\n )}\n </section>\n </div>\n )\n}\n\n// PostItem component matching the original design\ninterface PostItemProps {\n post: Post\n onNavigate: () => void\n onDelete: () => void\n onPublish: () => void\n onUnpublish: () => void\n showStatus?: boolean\n}\n\nfunction PostItem({ post, onNavigate, onDelete, onPublish, onUnpublish, showStatus }: PostItemProps) {\n const [menuOpen, setMenuOpen] = useState(false)\n const menuRef = useRef<HTMLDivElement>(null)\n\n // Close menu when clicking outside\n useEffect(() => {\n if (!menuOpen) return\n function handleClick(e: MouseEvent) {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n setMenuOpen(false)\n }\n }\n document.addEventListener('click', handleClick)\n return () => document.removeEventListener('click', handleClick)\n }, [menuOpen])\n\n return (\n <div className=\"flex items-center justify-between py-4 border-b border-border group\">\n <div className=\"flex-1 min-w-0\">\n <button onClick={onNavigate} className=\"block text-left w-full\">\n <h3 className=\"font-medium truncate group-hover:text-muted-foreground\">\n {post.title || 'Untitled'}\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1 flex items-center gap-2\">\n {showStatus && (\n <span className={`text-xs px-1.5 py-0.5 rounded uppercase font-medium ${\n post.status === 'draft' \n ? 'bg-amber-500/20 text-amber-600 dark:text-amber-400'\n : 'bg-green-500/20 text-green-600 dark:text-green-400'\n }`}>\n {post.status}\n </span>\n )}\n <span>{formatRelativeTime(post.updatedAt)}{post.wordCount ? ` · ${post.wordCount} words` : ''}</span>\n </p>\n </button>\n </div>\n \n <div className=\"relative\" ref={menuRef}>\n <button\n onClick={() => setMenuOpen(!menuOpen)}\n className=\"p-2 text-muted-foreground hover:text-foreground rounded-md hover:bg-accent\"\n >\n <MoreVertical className=\"w-4 h-4\" />\n </button>\n {menuOpen && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-lg z-50 min-w-[12rem] md:min-w-[8rem] p-1\">\n <button\n onClick={() => { onNavigate(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n Edit\n </button>\n {post.status === 'draft' && (\n <button\n onClick={() => { onPublish(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n Publish\n </button>\n )}\n {post.status === 'published' && (\n <button\n onClick={() => { onUnpublish(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n Unpublish\n </button>\n )}\n <div className=\"h-px bg-border my-1\" />\n <button\n onClick={() => { onDelete(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm text-destructive hover:bg-accent cursor-default\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { forwardRef } from 'react'\n\ninterface ControlButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n active?: boolean\n}\n\nexport const ControlButton = forwardRef<HTMLButtonElement, ControlButtonProps>(\n ({ className = '', active, disabled, children, type = 'button', ...props }, ref) => {\n const baseClasses = \"inline-flex items-center gap-1 text-sm transition-colors focus:outline-none\"\n const stateClasses = disabled\n ? \"text-muted-foreground/30 cursor-not-allowed\"\n : active\n ? \"text-blue-500 dark:text-blue-400\"\n : \"text-muted-foreground hover:text-foreground\"\n \n return (\n <button\n ref={ref}\n type={type}\n disabled={disabled}\n className={`${baseClasses} ${stateClasses} ${className}`}\n {...props}\n >\n {children}\n </button>\n )\n }\n)\n\nControlButton.displayName = 'ControlButton'\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\nimport { ChevronDown, Check } from 'lucide-react'\nimport type { AIModelOption } from '../../lib/models'\nimport { ControlButton } from './ControlButton'\n\ninterface ModelSelectorProps {\n models: AIModelOption[]\n selectedModel: string\n onModelChange: (id: string) => void\n currentModel?: AIModelOption\n}\n\nexport function ModelSelector({\n models,\n selectedModel,\n onModelChange,\n currentModel,\n}: ModelSelectorProps) {\n const [open, setOpen] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const displayModel = currentModel ?? models.find(m => m.id === selectedModel)\n\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [])\n\n return (\n <div ref={ref} className=\"relative\">\n <ControlButton onClick={() => setOpen(!open)}>\n {displayModel?.name || 'Select model'}\n <ChevronDown className=\"w-3.5 h-3.5\" />\n </ControlButton>\n {open && (\n <div className=\"absolute top-full left-0 mt-1 min-w-[180px] bg-popover border border-border rounded-md shadow-md z-50\">\n {models.map(model => (\n <button\n key={model.id}\n onClick={() => { onModelChange(model.id); setOpen(false) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between\"\n >\n <span>{model.name}</span>\n {selectedModel === model.id && <Check className=\"w-4 h-4\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/** Word count options for essay generation */\nexport const LENGTH_OPTIONS = [300, 500, 800, 1000] as const\nexport type LengthOption = (typeof LENGTH_OPTIONS)[number]\n\n/** AI model option for UI dropdowns */\nexport interface AIModelOption {\n id: string\n name: string\n description: string\n hasNativeSearch: boolean\n}\n\n/** Default models - can be overridden by API */\nexport const DEFAULT_MODELS: AIModelOption[] = [\n { id: 'claude-sonnet', name: 'Sonnet 4.5', description: 'Fast, capable', hasNativeSearch: false },\n { id: 'claude-opus', name: 'Opus 4.5', description: 'Highest quality', hasNativeSearch: false },\n { id: 'gpt-5.2', name: 'GPT-5.2', description: 'Latest OpenAI', hasNativeSearch: true },\n { id: 'gpt-5-mini', name: 'GPT-5 Mini', description: 'Fast and efficient', hasNativeSearch: true },\n]\n","const MINUTE = 60_000, HOUR = 3_600_000, DAY = 86_400_000\n\n/**\n * Format a date as relative time (e.g., \"5m ago\", \"2h ago\", \"Yesterday\")\n */\nexport function formatRelativeTime(isoDate: string): string {\n const date = new Date(isoDate)\n const diffMs = Date.now() - date.getTime()\n const mins = Math.floor(diffMs / MINUTE)\n const hours = Math.floor(diffMs / HOUR)\n const days = Math.floor(diffMs / DAY)\n \n if (mins < 1) return 'Just now'\n if (mins < 60) return `${mins}m ago`\n if (hours < 24) return `${hours}h ago`\n if (days === 1) return 'Yesterday'\n if (days < 7) return `${days}d ago`\n return date.toLocaleDateString()\n}\n\n/**\n * Format a date for display\n */\nexport function formatDate(date: Date | string, options?: Intl.DateTimeFormatOptions): string {\n const d = typeof date === 'string' ? new Date(date) : date\n \n const defaultOptions: Intl.DateTimeFormatOptions = {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n }\n \n return d.toLocaleDateString('en-US', options || defaultOptions)\n}\n\n/**\n * Format saved time for display (e.g., \"just now\", \"5m ago\", \"2:30 PM\")\n */\nexport function formatSavedTime(date: Date): string {\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffSecs = Math.floor(diffMs / 1000)\n const diffMins = Math.floor(diffMs / MINUTE)\n \n // Show relative time for recent saves\n if (diffSecs < 10) return 'just now'\n if (diffSecs < 60) return `${diffSecs}s ago`\n if (diffMins < 60) return `${diffMins}m ago`\n \n // Show clock time for today\n const isToday = date.toDateString() === now.toDateString()\n if (isToday) {\n return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })\n }\n \n return date.toLocaleDateString('en-US', { \n month: 'short', \n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\n/**\n * Count words in text\n */\nexport function countWords(text?: string | null): number {\n if (!text) return 0\n return text.split(/\\s+/).filter(Boolean).length\n}\n\n/**\n * Truncate text to a maximum length\n */\nexport function truncate(text: string, maxLength: number): string {\n // Strip markdown formatting for description\n const stripped = text\n .replace(/#+\\s/g, '')\n .replace(/\\*\\*/g, '')\n .replace(/\\*/g, '')\n .replace(/`/g, '')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/\\n+/g, ' ')\n .trim()\n \n if (stripped.length <= maxLength) return stripped\n \n return stripped.slice(0, maxLength - 3).trim() + '...'\n}\n","'use client'\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport { useDashboardContext, type EditCommand } from '../context'\nimport { EditorToolbar } from '../components/EditorToolbar'\nimport { TiptapEditor, type SelectionState } from '../components/TiptapEditor'\nimport { CommentsPanel } from '../components/CommentsPanel'\nimport { TagsSection } from '../components/TagsSection'\nimport { useComments } from '../hooks/useComments'\nimport { formatSavedTime, countWords } from '../../lib/format'\n\ninterface Tag {\n id: string\n name: string\n}\n\ninterface Post {\n id?: string\n title: string\n subtitle: string\n slug: string\n markdown: string\n status: 'draft' | 'published'\n tags?: Tag[]\n [key: string]: unknown\n}\n\ninterface Revision {\n id: string\n createdAt: string\n title: string | null\n subtitle: string | null\n markdown: string\n polyhedraShape?: string | null\n}\n\n// Skeleton component\nfunction Skeleton({ className }: { className?: string }) {\n return <div className={`bg-gray-200 dark:bg-gray-800 animate-pulse rounded ${className || ''}`} />\n}\n\n// Content skeleton matching the editor layout\nfunction ContentSkeleton({ styles }: { styles: { container: string } }) {\n return (\n <div className={`${styles.container} pt-12 pb-24 mx-auto`}>\n <div className=\"space-y-2 mb-8\">\n <Skeleton className=\"h-8 w-4/5\" />\n <Skeleton className=\"h-5 w-3/5\" />\n <div className=\"!mt-4\">\n <Skeleton className=\"h-3 w-24\" />\n </div>\n </div>\n <div className=\"space-y-3\">\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-5/6\" />\n </div>\n </div>\n )\n}\n\n// Auto-resizing textarea for title/subtitle\nfunction AutoResizeTextarea({\n value,\n onChange,\n placeholder,\n disabled,\n className,\n}: {\n value: string\n onChange: (value: string) => void\n placeholder?: string\n disabled?: boolean\n className?: string\n}) {\n const ref = useRef<HTMLTextAreaElement>(null)\n\n useEffect(() => {\n if (ref.current) {\n ref.current.style.height = 'auto'\n ref.current.style.height = `${ref.current.scrollHeight}px`\n }\n }, [value])\n\n return (\n <textarea\n ref={ref}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n onKeyDown={(e) => { if (e.key === 'Enter') e.preventDefault() }}\n className={`resize-none overflow-hidden ${className || ''}`}\n />\n )\n}\n\ninterface EditorPageProps {\n slug?: string\n onEditorStateChange?: (state: import('../context').EditorState | null) => void\n}\n\nexport function EditorPage({ slug, onEditorStateChange: onEditorStateChangeProp }: EditorPageProps) {\n const { apiBasePath, styles, fields, navigate, basePath, onRegisterEditHandler } = useDashboardContext()\n // Use prop callback (passed from DashboardLayout for internal save button)\n const onEditorStateChange = onEditorStateChangeProp\n const [post, setPost] = useState<Post>({\n title: '',\n subtitle: '',\n slug: '',\n markdown: '',\n status: 'draft',\n tags: [],\n })\n const [loading, setLoading] = useState(!!slug)\n const [saving, setSaving] = useState(false)\n const [savingAs, setSavingAs] = useState<'draft' | 'published' | null>(null)\n const [lastSaved, setLastSaved] = useState<Date | null>(null)\n const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)\n const [generating, setGenerating] = useState(false)\n const savedContent = useRef<string>('')\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const hasTriggeredGeneration = useRef(false)\n \n // Rich text vs markdown mode\n const [showMarkdown, setShowMarkdown] = useState(false)\n const [editor, setEditor] = useState<Editor | null>(null)\n\n // Revision state\n const [revisions, setRevisions] = useState<Revision[]>([])\n const [revisionsLoading, setRevisionsLoading] = useState(false)\n const [previewingRevision, setPreviewingRevision] = useState<Revision | null>(null)\n const [originalPost, setOriginalPost] = useState<Post | null>(null)\n\n // Comments state\n const [commentsOpen, setCommentsOpen] = useState(false)\n \n // Get session info from context (if provided)\n const { session } = useDashboardContext()\n const currentUserEmail = session?.user?.email || ''\n const isAdmin = session?.user?.role === 'admin'\n\n // Define savePost and handlePublish early (before hooks that depend on them)\n const savePost = useCallback(async (silent = false) => {\n if (!silent) {\n setSaving(true)\n setSavingAs('draft')\n }\n try {\n const method = post.id ? 'PATCH' : 'POST'\n const url = post.id ? `${apiBasePath}/posts/${post.id}` : `${apiBasePath}/posts`\n \n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n title: post.title || 'Untitled Draft',\n subtitle: post.subtitle || null,\n slug: post.slug || undefined,\n markdown: post.markdown,\n status: post.status,\n tagIds: post.tags?.map(t => t.id),\n ...Object.fromEntries(fields.map(f => [f.name, post[f.name]]))\n }),\n })\n \n const data = await res.json()\n if (data.data) {\n setPost(prev => ({ ...prev, ...data.data }))\n savedContent.current = JSON.stringify({ title: data.data.title, subtitle: data.data.subtitle, markdown: data.data.markdown })\n setLastSaved(new Date())\n setHasUnsavedChanges(false)\n if (!post.id && data.data.slug) {\n navigate(`/editor/${data.data.slug}`)\n }\n }\n } catch (err) {\n console.error('Save failed:', err)\n throw err\n } finally {\n if (!silent) {\n setSaving(false)\n setSavingAs(null)\n }\n }\n }, [post.id, post.title, post.subtitle, post.slug, post.markdown, post.status, post.tags, apiBasePath, fields, navigate])\n\n const handlePublish = useCallback(async () => {\n if (!confirm('Publish this essay?')) return\n setSaving(true)\n setSavingAs('published')\n try {\n const method = post.id ? 'PATCH' : 'POST'\n const url = post.id ? `${apiBasePath}/posts/${post.id}` : `${apiBasePath}/posts`\n\n // Use setPost's callback form to get current post state without adding post to deps\n let currentPost: typeof post\n setPost(p => { currentPost = p; return p })\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...currentPost!,\n title: currentPost!.title || 'Untitled',\n status: 'published',\n ...Object.fromEntries(fields.map(f => [f.name, currentPost![f.name]]))\n }),\n })\n\n if (res.ok) {\n navigate('/')\n }\n } finally {\n setSaving(false)\n setSavingAs(null)\n }\n }, [post.id, apiBasePath, fields, navigate])\n\n // Comments hook - handles all comment logic\n const comments = useComments({\n postId: post.id || null,\n editor,\n apiBasePath,\n onSave: async () => {\n // Auto-save the post and return the new post ID\n await savePost(true)\n return post.id || null\n },\n })\n\n // URL params for AI generation\n const [urlParams, setUrlParams] = useState<{\n idea?: string\n model?: string\n length?: string\n web?: string\n thinking?: string\n comment?: string\n }>({})\n\n useEffect(() => {\n if (typeof window !== 'undefined') {\n const params = new URLSearchParams(window.location.search)\n setUrlParams({\n idea: params.get('idea') || undefined,\n model: params.get('model') || undefined,\n length: params.get('length') || undefined,\n web: params.get('web') || undefined,\n thinking: params.get('thinking') || undefined,\n comment: params.get('comment') || undefined,\n })\n }\n }, [])\n\n // Handle comment URL param - open panel and scroll to comment\n useEffect(() => {\n if (urlParams.comment && !comments.loading && comments.list.length > 0) {\n // Open the comments panel\n setCommentsOpen(true)\n // Set active comment and scroll to it\n comments.setActiveId(urlParams.comment)\n // Small delay to ensure panel is open before scrolling\n setTimeout(() => {\n comments.scrollTo(urlParams.comment!)\n }, 100)\n }\n }, [urlParams.comment, comments.loading, comments.list.length])\n\n // Load post if editing existing\n useEffect(() => {\n if (slug) {\n fetch(`${apiBasePath}/posts`)\n .then(r => r.json())\n .then(d => {\n const found = d.data?.find((p: Post) => p.slug === slug)\n if (found) {\n setPost(found)\n savedContent.current = JSON.stringify({ title: found.title, subtitle: found.subtitle, markdown: found.markdown })\n }\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }\n }, [slug, apiBasePath])\n\n // Track unsaved changes\n useEffect(() => {\n const current = JSON.stringify({ title: post.title, subtitle: post.subtitle, markdown: post.markdown })\n // For new posts (savedContent is empty), any content is unsaved\n // For existing posts, compare against saved content\n if (savedContent.current === '') {\n setHasUnsavedChanges(!!(post.title || post.subtitle || post.markdown))\n } else {\n setHasUnsavedChanges(current !== savedContent.current)\n }\n }, [post.title, post.subtitle, post.markdown])\n\n // Report editor state to parent app (for navbar save button etc.)\n // Use refs for callbacks to avoid triggering effect re-runs\n const savePostRef = useRef(savePost)\n const handlePublishRef = useRef(handlePublish)\n const onEditorStateChangeRef = useRef(onEditorStateChange)\n useEffect(() => { savePostRef.current = savePost }, [savePost])\n useEffect(() => { handlePublishRef.current = handlePublish }, [handlePublish])\n useEffect(() => { onEditorStateChangeRef.current = onEditorStateChange }, [onEditorStateChange])\n \n useEffect(() => {\n if (!onEditorStateChangeRef.current) return\n\n const confirmLeave = () => {\n if (hasUnsavedChanges) {\n return confirm('You have unsaved changes. Leave anyway?')\n }\n return true\n }\n\n onEditorStateChangeRef.current({\n hasUnsavedChanges,\n status: post.status,\n savingAs,\n onSave: (status) => {\n if (status === 'draft') {\n savePostRef.current()\n } else {\n handlePublishRef.current()\n }\n },\n confirmLeave,\n // Include content for chat integration\n content: {\n title: post.title,\n subtitle: post.subtitle,\n markdown: post.markdown,\n },\n })\n\n return () => {\n onEditorStateChangeRef.current?.(null)\n }\n }, [hasUnsavedChanges, post.status, savingAs, post.title, post.subtitle, post.markdown])\n\n // Register edit handler for AI agent mode\n useEffect(() => {\n if (!onRegisterEditHandler) return\n\n const handleEdit = (edit: EditCommand): boolean => {\n if (edit.type === 'replace_all') {\n setPost(prev => ({\n ...prev,\n title: edit.title ?? prev.title,\n subtitle: edit.subtitle ?? prev.subtitle,\n markdown: edit.markdown ?? prev.markdown,\n }))\n return true\n }\n\n if (edit.type === 'replace_section' && edit.find && edit.replace !== undefined) {\n if (post.markdown.includes(edit.find)) {\n setPost(prev => ({\n ...prev,\n markdown: prev.markdown.replace(edit.find!, edit.replace!),\n }))\n return true\n }\n return false\n }\n\n if (edit.type === 'insert' && edit.replace !== undefined) {\n if (edit.position === 'start') {\n setPost(prev => ({ ...prev, markdown: edit.replace + prev.markdown }))\n return true\n }\n if (edit.position === 'end') {\n setPost(prev => ({ ...prev, markdown: prev.markdown + edit.replace }))\n return true\n }\n if (edit.find && post.markdown.includes(edit.find)) {\n const idx = post.markdown.indexOf(edit.find)\n const insertPoint = edit.position === 'before' ? idx : idx + edit.find.length\n setPost(prev => ({\n ...prev,\n markdown: prev.markdown.slice(0, insertPoint) + edit.replace + prev.markdown.slice(insertPoint),\n }))\n return true\n }\n return false\n }\n\n if (edit.type === 'delete' && edit.find) {\n if (post.markdown.includes(edit.find)) {\n setPost(prev => ({\n ...prev,\n markdown: prev.markdown.replace(edit.find!, ''),\n }))\n return true\n }\n return false\n }\n\n return false\n }\n\n onRegisterEditHandler(handleEdit)\n\n return () => {\n onRegisterEditHandler(null)\n }\n }, [post.markdown, onRegisterEditHandler])\n\n // Auto-save drafts (3s debounce)\n // Deps include content fields to reset timer on each keystroke (debounce behavior)\n useEffect(() => {\n if (!post.id || post.status === 'published' || !hasUnsavedChanges || previewingRevision) return\n const timeout = setTimeout(() => savePostRef.current(true), 3000)\n return () => clearTimeout(timeout)\n }, [post.id, post.status, post.title, post.subtitle, post.markdown, hasUnsavedChanges, previewingRevision])\n\n // Warn on navigation with unsaved changes\n useEffect(() => {\n const handler = (e: BeforeUnloadEvent) => {\n if (hasUnsavedChanges) {\n e.preventDefault()\n e.returnValue = ''\n }\n }\n window.addEventListener('beforeunload', handler)\n return () => window.removeEventListener('beforeunload', handler)\n }, [hasUnsavedChanges])\n\n // Keyboard shortcuts\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (previewingRevision) {\n cancelRevisionPreview()\n return\n }\n if (generating) return\n if (hasUnsavedChanges && !confirm('You have unsaved changes. Leave anyway?')) return\n navigate('/')\n }\n }\n window.addEventListener('keydown', handler)\n return () => window.removeEventListener('keydown', handler)\n }, [hasUnsavedChanges, generating, navigate, previewingRevision])\n\n // Auto-generate from idea param\n useEffect(() => {\n if (urlParams.idea && !slug && !loading && !hasTriggeredGeneration.current) {\n hasTriggeredGeneration.current = true\n setGenerating(true)\n\n const wordCount = urlParams.length ? parseInt(urlParams.length) : 500\n\n // Clear the URL params immediately\n if (typeof window !== 'undefined') {\n window.history.replaceState({}, '', `${basePath}/editor`)\n }\n\n const runGenerate = async () => {\n try {\n const res = await fetch(`${apiBasePath}/ai/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n prompt: urlParams.idea,\n wordCount,\n model: urlParams.model,\n }),\n })\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ error: 'Generation failed' }))\n console.error('Generation failed:', error)\n return\n }\n\n // Consume SSE stream\n const reader = res.body?.getReader()\n if (!reader) return\n\n const decoder = new TextDecoder()\n let fullContent = ''\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n const lines = chunk.split('\\n')\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6)\n if (data === '[DONE]') continue\n try {\n const parsed = JSON.parse(data)\n if (parsed.text) {\n fullContent += parsed.text\n // Update markdown progressively for live streaming effect\n setPost(prev => ({ ...prev, markdown: fullContent }))\n }\n } catch {\n // Ignore parse errors for incomplete chunks\n }\n }\n }\n }\n\n // Parse title and subtitle from generated content\n // Look for # Title on first line, and *subtitle* or _subtitle_ on second line\n const lines = fullContent.trim().split('\\n')\n let title = ''\n let subtitle = ''\n let markdownStart = 0\n\n // Check for H1 title\n if (lines[0]?.startsWith('# ')) {\n title = lines[0].slice(2).trim()\n markdownStart = 1\n\n // Check for italic subtitle on next line\n const nextLine = lines[1]?.trim()\n if (nextLine) {\n const italicMatch = nextLine.match(/^\\*(.+)\\*$/) || nextLine.match(/^_(.+)_$/)\n if (italicMatch) {\n subtitle = italicMatch[1]\n markdownStart = 2\n }\n }\n }\n\n // Update with parsed title/subtitle and remaining markdown\n const remainingMarkdown = lines.slice(markdownStart).join('\\n').trim()\n setPost(prev => ({\n ...prev,\n title: title || prev.title,\n subtitle: subtitle || prev.subtitle,\n markdown: remainingMarkdown || fullContent,\n }))\n } catch (err) {\n console.error('Generation error:', err)\n } finally {\n setGenerating(false)\n }\n }\n\n runGenerate()\n }\n }, [urlParams, slug, loading, apiBasePath, basePath])\n\n // Fetch revisions\n const fetchRevisions = useCallback(async () => {\n if (!post.id) return\n setRevisionsLoading(true)\n try {\n const res = await fetch(`${apiBasePath}/revisions?postId=${post.id}`)\n const data = await res.json()\n setRevisions(data.data || [])\n } catch (err) {\n console.error('Failed to fetch revisions:', err)\n } finally {\n setRevisionsLoading(false)\n }\n }, [post.id, apiBasePath])\n\n // Preview a revision\n const previewRevision = useCallback(async (revisionId: string) => {\n const revision = revisions.find(r => r.id === revisionId)\n if (!revision) return\n \n // Store original post state\n if (!originalPost) {\n setOriginalPost({ ...post })\n }\n \n setPreviewingRevision(revision)\n setPost(prev => ({\n ...prev,\n title: revision.title || prev.title,\n subtitle: revision.subtitle || prev.subtitle,\n markdown: revision.markdown,\n }))\n }, [revisions, post, originalPost])\n\n // Cancel revision preview\n const cancelRevisionPreview = useCallback(() => {\n if (originalPost) {\n setPost(originalPost)\n setOriginalPost(null)\n }\n setPreviewingRevision(null)\n }, [originalPost])\n\n // Restore revision\n const restoreRevision = useCallback(async () => {\n if (!previewingRevision) return\n setOriginalPost(null)\n setPreviewingRevision(null)\n // The post is already set to the revision content, just save it\n await savePost()\n }, [previewingRevision, savePost])\n\n const handleUnpublish = async () => {\n if (!confirm('Unpublish this essay?')) return\n await fetch(`${apiBasePath}/posts/${post.id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'draft' }),\n })\n setPost(prev => ({ ...prev, status: 'draft' }))\n }\n\n const words = countWords(post.markdown)\n const isPublished = post.status === 'published'\n\n // Loading skeleton\n if (loading) {\n return (\n <div className=\"flex flex-col h-full\">\n <EditorToolbar\n textareaRef={textareaRef}\n markdown=\"\"\n onMarkdownChange={() => {}}\n loading={true}\n />\n <main className=\"flex-1 overflow-auto pb-20\">\n <ContentSkeleton styles={styles} />\n </main>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Revision Preview Banner */}\n {previewingRevision && (\n <div className=\"bg-amber-100 dark:bg-amber-900/30 border-b border-amber-200 dark:border-amber-800 px-4 py-2 flex items-center justify-between\">\n <span className=\"text-sm text-amber-800 dark:text-amber-200\">\n Previewing revision from {new Date(previewingRevision.createdAt).toLocaleString()}\n </span>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={cancelRevisionPreview}\n className=\"px-3 py-1 text-sm border border-amber-300 dark:border-amber-700 rounded hover:bg-amber-200 dark:hover:bg-amber-800\"\n >\n Cancel\n </button>\n <button\n onClick={restoreRevision}\n className=\"px-3 py-1 text-sm bg-amber-600 text-white rounded hover:bg-amber-700\"\n >\n Restore\n </button>\n </div>\n </div>\n )}\n\n {/* Toolbar */}\n {!previewingRevision && (\n <EditorToolbar\n editor={showMarkdown ? null : editor}\n textareaRef={showMarkdown ? textareaRef : undefined}\n markdown={post.markdown}\n onMarkdownChange={(md) => setPost(prev => ({ ...prev, markdown: md }))}\n showMarkdown={showMarkdown}\n setShowMarkdown={setShowMarkdown}\n aiGenerating={generating}\n postSlug={slug}\n revisions={post.id ? {\n list: revisions,\n loading: revisionsLoading,\n previewLoading: false,\n previewing: previewingRevision,\n fetch: fetchRevisions,\n preview: previewRevision,\n cancel: cancelRevisionPreview,\n restore: restoreRevision,\n } : undefined}\n apiBasePath={apiBasePath}\n hasSelection={!!comments.selectedText && !comments.selectedText.hasExistingComment}\n selectionHasComment={comments.selectedText?.hasExistingComment}\n onAddComment={() => setCommentsOpen(true)}\n commentsCount={comments.list.filter(c => !c.resolved).length}\n onViewComments={() => setCommentsOpen(true)}\n />\n )}\n\n {/* Editor Content */}\n <main className=\"flex-1 overflow-auto pb-20 overscroll-contain\">\n <article className={`${styles.container} pt-12 pb-24 mx-auto`}>\n {/* Header - Title & Subtitle */}\n <header className=\"space-y-2 mb-8\">\n {generating && !post.title ? (\n <Skeleton className=\"h-8 w-4/5\" />\n ) : (\n <AutoResizeTextarea\n value={post.title}\n onChange={(val) => setPost(prev => ({ ...prev, title: val }))}\n placeholder=\"Title\"\n disabled={generating || !!previewingRevision}\n className={`${styles.title} w-full bg-transparent border-none outline-none placeholder-gray-300 dark:placeholder-gray-700 ${(generating || previewingRevision) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n )}\n {generating && !post.subtitle ? (\n <Skeleton className=\"h-5 w-3/5\" />\n ) : (\n <AutoResizeTextarea\n value={post.subtitle}\n onChange={(val) => setPost(prev => ({ ...prev, subtitle: val }))}\n placeholder=\"Subtitle\"\n disabled={generating || !!previewingRevision}\n className={`${styles.subtitle} w-full bg-transparent border-none outline-none placeholder-gray-300 dark:placeholder-gray-700 ${(generating || previewingRevision) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n )}\n <div className=\"!mt-4\">\n <span className={`${styles.byline} underline ${generating ? 'opacity-60' : ''}`}>\n {session?.user?.name || session?.user?.email || 'Author'}\n </span>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"mt-8\">\n {generating && !post.markdown ? (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-5/6\" />\n </div>\n ) : showMarkdown ? (\n <textarea\n ref={textareaRef}\n value={post.markdown}\n onChange={e => setPost(prev => ({ ...prev, markdown: e.target.value }))}\n placeholder=\"Start writing...\"\n disabled={generating || !!previewingRevision}\n className={`${styles.prose} w-full min-h-[400px] bg-transparent border-none outline-none resize-none placeholder-gray-400 leading-relaxed font-mono text-sm ${(generating || previewingRevision) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n ) : (\n <TiptapEditor\n content={post.markdown}\n onChange={(md) => setPost(prev => ({ ...prev, markdown: md }))}\n onEditorReady={setEditor}\n autoFocus={!slug}\n proseClasses={styles.prose}\n onSelectionChange={(sel: SelectionState | null) => {\n if (sel?.hasSelection) {\n comments.setSelectedText({\n text: sel.text,\n from: sel.from,\n to: sel.to,\n hasExistingComment: sel.hasExistingComment,\n })\n } else {\n comments.setSelectedText(null)\n }\n }}\n onCommentClick={(commentId: string) => {\n comments.setActiveId(commentId)\n setCommentsOpen(true)\n }}\n />\n )}\n </div>\n\n {/* Footer Metadata */}\n {!previewingRevision && (\n <div className=\"mt-12 pt-8 border-t border-gray-200 dark:border-gray-800 space-y-4\">\n {/* URL */}\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-gray-500 w-14\">URL</span>\n <span className=\"text-gray-400\">/e/</span>\n {isPublished ? (\n <span className=\"flex items-center gap-1.5 text-gray-600 dark:text-gray-400\">\n {post.slug}\n <svg className=\"w-3 h-3 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\" />\n </svg>\n </span>\n ) : (\n <input\n type=\"text\"\n value={post.slug}\n onChange={(e) => setPost(prev => ({ ...prev, slug: e.target.value }))}\n placeholder=\"post-slug\"\n className=\"flex-1 bg-transparent border-none outline-none placeholder-gray-400 text-gray-600 dark:text-gray-400\"\n />\n )}\n </div>\n </div>\n\n {/* Custom Fields (footer position) */}\n {fields.filter(f => f.position === 'footer').map(field => {\n const handleFieldChange = (name: string, value: unknown) => {\n setPost(prev => ({ ...prev, [name]: value }))\n }\n \n // If no label, render component full-width (for section-style fields like SEO)\n if (!field.label) {\n return (\n <field.component\n key={field.name}\n value={post[field.name] as any}\n onChange={(val: unknown) => setPost(prev => ({ ...prev, [field.name]: val }))}\n onFieldChange={handleFieldChange}\n post={post as any}\n disabled={saving || generating}\n />\n )\n }\n \n // With label, render inline row style\n return (\n <div key={field.name} className=\"flex items-center justify-between text-sm gap-2\">\n <div className=\"flex items-center gap-2 flex-1\">\n <span className=\"text-gray-500 w-14 flex-shrink-0\">{field.label}</span>\n <div className=\"flex-1\">\n <field.component\n value={post[field.name] as any}\n onChange={(val: unknown) => setPost(prev => ({ ...prev, [field.name]: val }))}\n onFieldChange={handleFieldChange}\n post={post as any}\n disabled={saving || generating}\n />\n </div>\n </div>\n </div>\n )\n })}\n\n {/* Tags */}\n <TagsSection\n tags={post.tags || []}\n onTagsChange={(tags) => setPost(prev => ({ ...prev, tags }))}\n apiBasePath={apiBasePath}\n disabled={saving || generating}\n />\n\n {/* Status */}\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-gray-500 w-14\">Status</span>\n <span className={isPublished ? \"text-green-600 dark:text-green-400\" : \"text-gray-500\"}>\n {isPublished ? 'Published' : 'Draft'}\n </span>\n </div>\n \n {/* Publish/Unpublish/Update button */}\n {isPublished ? (\n hasUnsavedChanges ? (\n <button\n onClick={handlePublish}\n disabled={saving || generating}\n className=\"px-3 py-1.5 text-sm bg-primary text-primary-foreground rounded-md hover:opacity-90 disabled:opacity-50 flex items-center gap-1\"\n >\n {savingAs === 'published' && (\n <svg className=\"w-3 h-3 animate-spin\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" fill=\"none\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\" />\n </svg>\n )}\n Update\n </button>\n ) : (\n <button\n onClick={handleUnpublish}\n className=\"px-2.5 py-1 text-xs rounded bg-gray-100 dark:bg-gray-800 text-gray-500 hover:text-red-500 transition-colors\"\n >\n Unpublish\n </button>\n )\n ) : (\n <button\n onClick={handlePublish}\n disabled={saving || generating}\n className=\"px-3 py-1.5 text-sm bg-primary text-primary-foreground rounded-md hover:opacity-90 disabled:opacity-50 flex items-center gap-1\"\n >\n {savingAs === 'published' && (\n <svg className=\"w-3 h-3 animate-spin\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" fill=\"none\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\" />\n </svg>\n )}\n Publish\n </button>\n )}\n </div>\n\n {/* Word count */}\n <div className=\"text-sm text-gray-500 pt-2 border-t border-gray-200 dark:border-gray-800\">\n {words.toLocaleString()} words · ~{Math.ceil(words / 200)} min read\n </div>\n </div>\n )}\n </article>\n </main>\n\n {/* Footer */}\n <footer className=\"fixed bottom-0 left-0 right-0 border-t border-gray-200 dark:border-gray-800 px-4 py-3 bg-white dark:bg-black touch-none\">\n <div className=\"flex items-center justify-end text-sm text-gray-500\">\n {generating ? (\n <button className=\"hover:text-gray-900 dark:hover:text-white transition-colors\">\n Press Esc to stop generating\n </button>\n ) : previewingRevision ? (\n <button \n onClick={cancelRevisionPreview}\n className=\"hover:text-gray-900 dark:hover:text-white transition-colors\"\n >\n Press Esc to cancel\n </button>\n ) : lastSaved ? (\n <span>Saved {formatSavedTime(lastSaved)}</span>\n ) : (\n <span>Not saved yet</span>\n )}\n </div>\n </footer>\n\n {/* Comments Panel */}\n {currentUserEmail && (\n <CommentsPanel\n comments={comments.list}\n currentUserEmail={currentUserEmail}\n isAdmin={isAdmin}\n selectedText={comments.selectedText?.text ?? null}\n onCreateComment={comments.create}\n onReply={comments.reply}\n onEdit={comments.edit}\n onDelete={comments.remove}\n onResolve={comments.resolve}\n onCommentClick={comments.scrollTo}\n activeCommentId={comments.activeId}\n isOpen={commentsOpen}\n onClose={() => setCommentsOpen(false)}\n onClearSelection={() => comments.setSelectedText(null)}\n />\n )}\n </div>\n )\n}\n","'use client'\n\nimport type { RefObject } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport { MessageSquarePlus, MessageSquare } from 'lucide-react'\nimport { ToolbarButton, Divider, SkeletonButton } from './toolbar/ToolbarButton'\nimport { FormatButtons } from './toolbar/FormatButtons'\nimport { BlockButtons } from './toolbar/BlockButtons'\nimport { MediaButtons } from './toolbar/MediaButtons'\nimport { HistoryButtons } from './toolbar/HistoryButtons'\nimport type { RevisionState } from '../../lib/editor-types'\n\ninterface EditorToolbarProps {\n editor?: Editor | null\n // For markdown mode\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n // Mode toggle\n showMarkdown?: boolean\n setShowMarkdown?: (show: boolean) => void\n // Revision history\n postSlug?: string\n revisions?: RevisionState\n // AI generation state (disables all toolbar buttons during generation)\n aiGenerating?: boolean\n // Comments\n hasSelection?: boolean\n selectionHasComment?: boolean\n onAddComment?: () => void\n commentsCount?: number\n onViewComments?: () => void\n // Loading state\n loading?: boolean\n // API base path\n apiBasePath?: string\n}\n\nexport function EditorToolbar({\n editor,\n textareaRef,\n markdown,\n onMarkdownChange,\n showMarkdown,\n setShowMarkdown,\n postSlug,\n revisions,\n aiGenerating,\n hasSelection,\n selectionHasComment,\n onAddComment,\n commentsCount,\n onViewComments,\n loading = false,\n apiBasePath = '/api/cms',\n}: EditorToolbarProps) {\n // Navbar height: py-4 (32px) + h-9 content (36px) + border (1px) = 69px\n // Skeleton state - each button group renders its own skeletons\n if (loading) {\n return (\n <div className=\"sticky top-[69px] z-40 flex items-center justify-start lg:justify-center gap-0.5 px-4 py-2 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-black overflow-x-auto\">\n <FormatButtons loading={true} />\n <Divider />\n <BlockButtons loading={true} />\n <Divider />\n <MediaButtons loading={true} />\n <Divider />\n <HistoryButtons loading={true} />\n {/* Comment buttons skeleton */}\n <Divider />\n <SkeletonButton />\n <SkeletonButton />\n </div>\n )\n }\n\n return (\n <div className=\"sticky top-[69px] z-40 flex items-center justify-start lg:justify-center gap-0.5 px-4 py-2 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-black overflow-x-auto\">\n <FormatButtons\n editor={editor}\n textareaRef={textareaRef}\n markdown={markdown}\n onMarkdownChange={onMarkdownChange}\n aiGenerating={aiGenerating}\n apiBasePath={apiBasePath}\n />\n\n <Divider />\n\n <BlockButtons\n editor={editor}\n textareaRef={textareaRef}\n markdown={markdown}\n onMarkdownChange={onMarkdownChange}\n aiGenerating={aiGenerating}\n />\n\n <Divider />\n\n <MediaButtons\n editor={editor}\n textareaRef={textareaRef}\n markdown={markdown}\n onMarkdownChange={onMarkdownChange}\n aiGenerating={aiGenerating}\n apiBasePath={apiBasePath}\n />\n\n <Divider />\n\n <HistoryButtons\n editor={editor}\n textareaRef={textareaRef}\n showMarkdown={showMarkdown}\n setShowMarkdown={setShowMarkdown}\n postSlug={postSlug}\n revisions={revisions}\n aiGenerating={aiGenerating}\n />\n\n {/* Comments - always visible */}\n <Divider />\n <ToolbarButton\n onClick={onAddComment ?? (() => {})}\n disabled={aiGenerating || !hasSelection || !onAddComment}\n title={\n hasSelection \n ? 'New comment (⌘⌥M)' \n : selectionHasComment \n ? 'Text already has a comment' \n : 'Select text to comment'\n }\n >\n <MessageSquarePlus className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={onViewComments ?? (() => {})}\n disabled={aiGenerating || !onViewComments}\n title=\"View all comments\"\n >\n <span className=\"flex items-center gap-1\">\n <MessageSquare className=\"w-4 h-4\" />\n {commentsCount !== undefined && commentsCount > 0 && (\n <span className=\"text-xs tabular-nums\">{commentsCount}</span>\n )}\n </span>\n </ToolbarButton>\n </div>\n )\n}\n","import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","'use client'\n\nimport { cn } from '../../lib/cn'\n\ninterface SkeletonProps {\n className?: string\n}\n\n/**\n * Base skeleton component with pulse animation.\n */\nexport function Skeleton({ className }: SkeletonProps) {\n return <div className={cn('animate-pulse bg-muted rounded', className)} />\n}\n\n/**\n * Skeleton for text content with multiple lines.\n */\nexport function SkeletonText({ \n lines = 1, \n className \n}: { \n lines?: number\n className?: string \n}) {\n return (\n <div className={cn('space-y-2', className)}>\n {Array.from({ length: lines }).map((_, i) => (\n <Skeleton \n key={i} \n className={cn(\n 'h-4',\n // Make last line shorter for more natural look\n i === lines - 1 && lines > 1 ? 'w-3/4' : 'w-full'\n )} \n />\n ))}\n </div>\n )\n}\n\n/**\n * Skeleton for toolbar buttons.\n */\nexport function SkeletonButton({ className }: SkeletonProps) {\n return <Skeleton className={cn('h-8 w-8', className)} />\n}\n\n/**\n * Skeleton for cards/panels.\n */\nexport function SkeletonCard({ className }: SkeletonProps) {\n return <Skeleton className={cn('h-24 w-full rounded-lg', className)} />\n}\n\n/**\n * Skeleton for table rows.\n */\nexport function SkeletonTableRow({ columns = 4 }: { columns?: number }) {\n return (\n <tr>\n {Array.from({ length: columns }).map((_, i) => (\n <td key={i} className=\"px-4 py-3\">\n <Skeleton className=\"h-4 w-full\" />\n </td>\n ))}\n </tr>\n )\n}\n","'use client'\n\nimport { cn } from '../../../lib/cn'\nimport { Skeleton } from '../Skeleton'\n\ninterface ToolbarButtonProps {\n onClick: () => void\n active?: boolean\n disabled?: boolean\n children: React.ReactNode\n title?: string\n}\n\nexport function ToolbarButton({ onClick, active, disabled, children, title }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={title}\n className={cn(\n 'px-2.5 py-1.5 text-sm font-medium rounded transition-colors',\n 'flex items-center justify-center',\n 'hover:bg-gray-100 dark:hover:bg-gray-800',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n active && 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white',\n !active && 'text-gray-600 dark:text-gray-400'\n )}\n >\n {children}\n </button>\n )\n}\n\nexport function Divider() {\n return <div className=\"w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1\" />\n}\n\n/** Skeleton placeholder for a toolbar button - matches ToolbarButton dimensions */\nexport function SkeletonButton() {\n return <Skeleton className=\"h-7 w-7 shrink-0 bg-gray-200 dark:bg-gray-800\" />\n}\n","'use client'\n\nimport { useState, useEffect, useCallback } from 'react'\nimport type { Editor } from '@tiptap/react'\n\n// Cast editor to any for StarterKit command support\ntype AnyEditor = Editor & { chain: () => any; isActive: (name: string, attrs?: any) => boolean }\nimport type { RefObject } from 'react'\nimport { Loader2, Wand2 } from 'lucide-react'\nimport { ToolbarButton, Divider, SkeletonButton } from './ToolbarButton'\nimport { setHeadingAtCursor, insertAtCursor } from '../../../lib/markdown-helpers'\n\ninterface FormatButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n aiGenerating?: boolean\n loading?: boolean\n apiBasePath?: string\n}\n\nexport function FormatButtons({ editor: editorProp, textareaRef, markdown, onMarkdownChange, aiGenerating, loading, apiBasePath = '/api/cms' }: FormatButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n const isMarkdownMode = !editor && textareaRef && markdown !== undefined && onMarkdownChange\n const [hasSelection, setHasSelection] = useState(false)\n const [isRewriting, setIsRewriting] = useState(false)\n\n // Track text selection in the editor\n useEffect(() => {\n if (!editor) {\n setHasSelection(false)\n return\n }\n\n const updateSelection = () => {\n const { from, to } = editor.state.selection\n setHasSelection(from !== to)\n }\n\n editor.on('selectionUpdate', updateSelection)\n editor.on('transaction', updateSelection)\n \n return () => {\n editor.off('selectionUpdate', updateSelection)\n editor.off('transaction', updateSelection)\n }\n }, [editor])\n\n // Handle rewrite selected text\n const handleRewrite = useCallback(async () => {\n if (!editor || isRewriting) return\n \n const { from, to } = editor.state.selection\n if (from === to) return\n\n const selectedText = editor.state.doc.textBetween(from, to, ' ')\n if (!selectedText.trim()) return\n\n setIsRewriting(true)\n try {\n const res = await fetch(`${apiBasePath}/ai/rewrite`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text: selectedText }),\n })\n\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Rewrite failed')\n }\n\n const result = await res.json()\n \n // Replace the selected text with the rewritten version\n editor.chain().focus().deleteSelection().insertContent(result.text).run()\n } catch (err) {\n console.error('Rewrite error:', err)\n alert(err instanceof Error ? err.message : 'Failed to rewrite text')\n } finally {\n setIsRewriting(false)\n }\n }, [editor, isRewriting, apiBasePath])\n\n const setHeading = (level: number) => {\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n setHeadingAtCursor(textareaRef.current, level, markdown, onMarkdownChange)\n }\n }\n\n const wrapSelection = (before: string, after: string) => {\n if (editor) return\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n insertAtCursor(textareaRef.current, before, after, markdown, onMarkdownChange)\n }\n }\n\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* H1, H2, H3 */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n <Divider />\n {/* Bold, Italic, Strikethrough */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n {/* AI Rewrite (shown in rich text mode) */}\n <Divider />\n <SkeletonButton />\n </>\n )\n }\n\n return (\n <>\n {/* Headings */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleHeading({ level: 1 }).run() : setHeading(1)}\n active={editor?.isActive('heading', { level: 1 })}\n disabled={aiGenerating}\n title=\"Heading 1\"\n >\n H1\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleHeading({ level: 2 }).run() : setHeading(2)}\n active={editor?.isActive('heading', { level: 2 })}\n disabled={aiGenerating}\n title=\"Heading 2\"\n >\n H2\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleHeading({ level: 3 }).run() : setHeading(3)}\n active={editor?.isActive('heading', { level: 3 })}\n disabled={aiGenerating}\n title=\"Heading 3\"\n >\n H3\n </ToolbarButton>\n\n <Divider />\n\n {/* Text formatting */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleBold().run() : wrapSelection('**', '**')}\n active={editor?.isActive('bold')}\n disabled={aiGenerating}\n title=\"Bold (⌘B)\"\n >\n <span className=\"font-bold\">B</span>\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleItalic().run() : wrapSelection('*', '*')}\n active={editor?.isActive('italic')}\n disabled={aiGenerating}\n title=\"Italic (⌘I)\"\n >\n <span className=\"italic\">I</span>\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleStrike().run() : wrapSelection('~~', '~~')}\n active={editor?.isActive('strike')}\n disabled={aiGenerating}\n title=\"Strikethrough\"\n >\n <span className=\"line-through\">S</span>\n </ToolbarButton>\n\n {/* AI Rewrite (only in rich text mode, but always visible when not in markdown mode) */}\n {!isMarkdownMode && (\n <>\n <Divider />\n <ToolbarButton\n onClick={handleRewrite}\n disabled={!editor || !hasSelection || isRewriting || aiGenerating}\n title={\n !editor \n ? 'Editor loading...' \n : hasSelection \n ? 'Rewrite selection with AI' \n : 'Select text to rewrite'\n }\n >\n {isRewriting ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Wand2 className=\"w-4 h-4\" />\n )}\n </ToolbarButton>\n </>\n )}\n </>\n )\n}\n","/**\n * Helper functions for markdown mode in the editor toolbar.\n * Handles cursor-based text manipulation in textareas.\n */\n\n/**\n * Insert text before and after the current selection in a textarea.\n * Used for inline formatting like bold (**), italic (*), code (`), etc.\n */\nexport function insertAtCursor(\n textarea: HTMLTextAreaElement,\n before: string,\n after: string,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n const selected = markdown.substring(start, end)\n const newText = markdown.substring(0, start) + before + selected + after + markdown.substring(end)\n onMarkdownChange(newText)\n \n // Restore focus and selection after React re-render\n requestAnimationFrame(() => {\n textarea.focus()\n const newCursorPos = start + before.length + selected.length\n textarea.setSelectionRange(newCursorPos, newCursorPos)\n })\n}\n\n/**\n * Insert or toggle a block prefix at the start of the current line.\n * Used for lists (- ), blockquotes (> ), etc.\n */\nexport function insertBlockAtCursor(\n textarea: HTMLTextAreaElement,\n prefix: string,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n \n // Find the start of the current line\n let lineStart = start\n while (lineStart > 0 && markdown[lineStart - 1] !== '\\n') {\n lineStart--\n }\n \n // Get current line content\n let lineEnd = end\n while (lineEnd < markdown.length && markdown[lineEnd] !== '\\n') {\n lineEnd++\n }\n const lineContent = markdown.substring(lineStart, lineEnd)\n \n // Check if already has prefix, toggle off\n if (lineContent.startsWith(prefix)) {\n const newText = markdown.substring(0, lineStart) + lineContent.substring(prefix.length) + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n } else {\n const newText = markdown.substring(0, lineStart) + prefix + lineContent + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n }\n \n requestAnimationFrame(() => {\n textarea.focus()\n })\n}\n\n/**\n * Set or toggle a heading level on the current line.\n * Replaces any existing heading prefix with the new level.\n */\nexport function setHeadingAtCursor(\n textarea: HTMLTextAreaElement,\n level: number,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n \n // Find the start of the current line\n let lineStart = start\n while (lineStart > 0 && markdown[lineStart - 1] !== '\\n') {\n lineStart--\n }\n \n // Get current line content\n let lineEnd = end\n while (lineEnd < markdown.length && markdown[lineEnd] !== '\\n') {\n lineEnd++\n }\n const lineContent = markdown.substring(lineStart, lineEnd)\n \n // Remove any existing heading prefix\n const withoutHeading = lineContent.replace(/^#{1,6}\\s*/, '')\n const newPrefix = '#'.repeat(level) + ' '\n \n // If the line already has this exact heading level, toggle it off\n const currentHeadingMatch = lineContent.match(/^(#{1,6})\\s/)\n if (currentHeadingMatch && currentHeadingMatch[1].length === level) {\n // Toggle off - remove heading\n const newText = markdown.substring(0, lineStart) + withoutHeading + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n } else {\n // Set new heading level\n const newText = markdown.substring(0, lineStart) + newPrefix + withoutHeading + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n }\n \n requestAnimationFrame(() => {\n textarea.focus()\n })\n}\n\n/**\n * Strip common markdown syntax from selected text.\n * Removes bold, italic, strikethrough, code, and links.\n */\nexport function clearMarkdownFormatting(\n textarea: HTMLTextAreaElement,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n \n if (start === end) return // No selection\n \n const selected = markdown.substring(start, end)\n const cleaned = selected\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1') // bold\n .replace(/\\*(.+?)\\*/g, '$1') // italic\n .replace(/~~(.+?)~~/g, '$1') // strikethrough\n .replace(/`(.+?)`/g, '$1') // inline code\n .replace(/\\[(.+?)\\]\\(.+?\\)/g, '$1') // links\n \n const newText = markdown.substring(0, start) + cleaned + markdown.substring(end)\n onMarkdownChange(newText)\n \n requestAnimationFrame(() => {\n textarea.focus()\n textarea.setSelectionRange(start, start + cleaned.length)\n })\n}\n","'use client'\n\nimport type { Editor } from '@tiptap/react'\nimport type { RefObject } from 'react'\nimport { List, ListOrdered, Quote, Code2, Minus } from 'lucide-react'\nimport { ToolbarButton, SkeletonButton } from './ToolbarButton'\nimport { insertBlockAtCursor, insertAtCursor } from '../../../lib/markdown-helpers'\n\ninterface BlockButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n aiGenerating?: boolean\n loading?: boolean\n}\n\nexport function BlockButtons({ editor: editorProp, textareaRef, markdown, onMarkdownChange, aiGenerating, loading }: BlockButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* Bullet list, Numbered list */}\n <SkeletonButton />\n <SkeletonButton />\n {/* Blockquote, Code block, Horizontal rule */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n </>\n )\n }\n const isMarkdownMode = !editor && textareaRef && markdown !== undefined && onMarkdownChange\n\n const insertBlock = (prefix: string) => {\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n insertBlockAtCursor(textareaRef.current, prefix, markdown, onMarkdownChange)\n }\n }\n\n const wrapSelection = (before: string, after: string) => {\n if (editor) return\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n insertAtCursor(textareaRef.current, before, after, markdown, onMarkdownChange)\n }\n }\n\n const handleHorizontalRule = () => {\n if (editor) {\n editor.chain().focus().setHorizontalRule().run()\n } else if (isMarkdownMode && textareaRef?.current) {\n const textarea = textareaRef.current\n const start = textarea.selectionStart\n const newText = markdown!.substring(0, start) + '\\n---\\n' + markdown!.substring(start)\n onMarkdownChange!(newText)\n requestAnimationFrame(() => textarea.focus())\n }\n }\n\n return (\n <>\n {/* Lists */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleBulletList().run() : insertBlock('- ')}\n active={editor?.isActive('bulletList')}\n disabled={aiGenerating}\n title=\"Bullet list\"\n >\n <List className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleOrderedList().run() : insertBlock('1. ')}\n active={editor?.isActive('orderedList')}\n disabled={aiGenerating}\n title=\"Numbered list\"\n >\n <ListOrdered className=\"w-4 h-4\" />\n </ToolbarButton>\n\n {/* Block elements */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleBlockquote().run() : insertBlock('> ')}\n active={editor?.isActive('blockquote')}\n disabled={aiGenerating}\n title=\"Blockquote\"\n >\n <Quote className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleCodeBlock().run() : wrapSelection('```\\n', '\\n```')}\n active={editor?.isActive('codeBlock')}\n disabled={aiGenerating}\n title=\"Code block\"\n >\n <Code2 className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton onClick={handleHorizontalRule} disabled={aiGenerating} title=\"Horizontal rule\">\n <Minus className=\"w-4 h-4\" />\n </ToolbarButton>\n </>\n )\n}\n","'use client'\n\nimport { useRef, useCallback } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport type { RefObject } from 'react'\nimport { Link2, Image, RemoveFormatting } from 'lucide-react'\nimport { ToolbarButton, SkeletonButton } from './ToolbarButton'\nimport { clearMarkdownFormatting } from '../../../lib/markdown-helpers'\n\ninterface MediaButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n aiGenerating?: boolean\n loading?: boolean\n apiBasePath?: string\n}\n\nexport function MediaButtons({ editor: editorProp, textareaRef, markdown, onMarkdownChange, aiGenerating, loading, apiBasePath = '/api/cms' }: MediaButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n const fileInputRef = useRef<HTMLInputElement>(null)\n const isMarkdownMode = !editor && textareaRef && markdown !== undefined && onMarkdownChange\n\n const handleLinkClick = useCallback(() => {\n if (editor) {\n if (editor.isActive('link')) {\n editor.chain().focus().unsetLink().run()\n return\n }\n const url = window.prompt('Enter URL:')\n if (url) {\n editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()\n }\n } else if (isMarkdownMode) {\n const url = window.prompt('Enter URL:')\n if (url && textareaRef?.current) {\n const textarea = textareaRef.current\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n const selected = markdown!.substring(start, end) || 'link text'\n const newText = markdown!.substring(0, start) + `[${selected}](${url})` + markdown!.substring(end)\n onMarkdownChange!(newText)\n requestAnimationFrame(() => textarea.focus())\n }\n }\n }, [editor, isMarkdownMode, textareaRef, markdown, onMarkdownChange])\n\n const handleImageUpload = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (!file) return\n\n // Client-side validation\n const maxSize = 4 * 1024 * 1024 // 4MB\n if (file.size > maxSize) {\n alert(`Image too large (${(file.size / 1024 / 1024).toFixed(1)}MB). Maximum size is 4MB.`)\n e.target.value = ''\n return\n }\n\n const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']\n if (!validTypes.includes(file.type)) {\n alert(`Invalid file type: ${file.type}. Supported types: JPEG, PNG, GIF, WebP.`)\n e.target.value = ''\n return\n }\n\n const formData = new FormData()\n formData.append('image', file)\n\n try {\n const res = await fetch(`${apiBasePath}/upload`, {\n method: 'POST',\n body: formData,\n })\n \n const data = await res.json()\n \n if (!res.ok) {\n alert(data.error || 'Failed to upload image')\n e.target.value = ''\n return\n }\n\n // Handle both { url: ... } and { data: { url: ... } } response formats\n const url = data.url || data.data?.url\n if (url) {\n if (editor) {\n editor.chain().focus().setImage({ src: url }).run()\n } else if (isMarkdownMode && textareaRef?.current) {\n const textarea = textareaRef.current\n const start = textarea.selectionStart\n const newText = markdown!.substring(0, start) + `` + markdown!.substring(start)\n onMarkdownChange!(newText)\n requestAnimationFrame(() => textarea.focus())\n }\n } else {\n alert('Upload succeeded but no URL returned')\n }\n } catch (err) {\n alert('Failed to upload image. Please try again.')\n console.error('Image upload error:', err)\n }\n\n e.target.value = ''\n }, [editor, isMarkdownMode, textareaRef, markdown, onMarkdownChange, apiBasePath])\n\n const handleClearFormatting = useCallback(() => {\n if (editor) {\n editor.chain().focus().unsetAllMarks().clearNodes().run()\n } else if (isMarkdownMode && textareaRef?.current && markdown && onMarkdownChange) {\n clearMarkdownFormatting(textareaRef.current, markdown, onMarkdownChange)\n }\n }, [editor, isMarkdownMode, textareaRef, markdown, onMarkdownChange])\n\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* Link, Image, Clear formatting */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n </>\n )\n }\n\n return (\n <>\n <ToolbarButton\n onClick={handleLinkClick}\n active={editor?.isActive('link')}\n disabled={aiGenerating}\n title=\"Insert link\"\n >\n <Link2 className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => fileInputRef.current?.click()} disabled={aiGenerating} title=\"Insert image\">\n <Image className=\"w-4 h-4\" />\n </ToolbarButton>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleImageUpload}\n className=\"hidden\"\n />\n <ToolbarButton onClick={handleClearFormatting} disabled={aiGenerating} title=\"Clear formatting\">\n <RemoveFormatting className=\"w-4 h-4\" />\n </ToolbarButton>\n </>\n )\n}\n","'use client'\n\nimport { useCallback } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport type { RefObject } from 'react'\nimport { Undo2, Redo2 } from 'lucide-react'\nimport { ToolbarButton, Divider, SkeletonButton } from './ToolbarButton'\nimport { RevisionHistoryDropdown } from '../RevisionHistoryDropdown'\nimport type { RevisionState } from '../../../lib/editor-types'\n\ninterface HistoryButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n showMarkdown?: boolean\n setShowMarkdown?: (show: boolean) => void\n postSlug?: string\n revisions?: RevisionState\n aiGenerating?: boolean\n loading?: boolean\n}\n\nexport function HistoryButtons({\n editor: editorProp,\n textareaRef,\n showMarkdown,\n setShowMarkdown,\n postSlug,\n revisions,\n aiGenerating,\n loading,\n}: HistoryButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n \n const handleUndo = useCallback(() => {\n if (editor) {\n editor.chain().focus().undo().run()\n } else if (textareaRef?.current) {\n textareaRef.current.focus()\n document.execCommand('undo')\n }\n }, [editor, textareaRef])\n\n const handleRedo = useCallback(() => {\n if (editor) {\n editor.chain().focus().redo().run()\n } else if (textareaRef?.current) {\n textareaRef.current.focus()\n document.execCommand('redo')\n }\n }, [editor, textareaRef])\n\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* Undo, Redo */}\n <SkeletonButton />\n <SkeletonButton />\n {/* MD toggle */}\n <Divider />\n <SkeletonButton />\n {/* Revision dropdown */}\n <Divider />\n <SkeletonButton />\n </>\n )\n }\n\n return (\n <>\n {/* Undo/Redo */}\n <ToolbarButton\n onClick={handleUndo}\n disabled={aiGenerating || (editor ? !editor.can().undo() : false)}\n title=\"Undo (⌘Z)\"\n >\n <Undo2 className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={handleRedo}\n disabled={aiGenerating || (editor ? !editor.can().redo() : false)}\n title=\"Redo (⌘⇧Z)\"\n >\n <Redo2 className=\"w-4 h-4\" />\n </ToolbarButton>\n\n {/* Markdown/Rich Text mode toggle */}\n {setShowMarkdown && (\n <>\n <Divider />\n <ToolbarButton\n onClick={() => setShowMarkdown(!showMarkdown)}\n active={showMarkdown}\n disabled={aiGenerating}\n title={showMarkdown ? 'Switch to rich text editor' : 'Switch to markdown mode'}\n >\n <span className=\"font-mono text-xs\">MD</span>\n </ToolbarButton>\n </>\n )}\n\n {/* Revision History */}\n {revisions && (\n <>\n <Divider />\n <RevisionHistoryDropdown\n revisions={revisions.list}\n loading={revisions.loading}\n previewLoading={revisions.previewLoading}\n disabled={aiGenerating || !postSlug}\n isPreviewMode={!!revisions.previewing}\n onOpen={revisions.fetch}\n onSelect={revisions.preview}\n />\n </>\n )}\n </>\n )\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { Loader2, History } from 'lucide-react'\nimport { formatRelativeTime } from '../../lib/format'\nimport type { RevisionSummary } from '../../lib/editor-types'\nimport { Dropdown, DropdownItem, DropdownLabel } from './Dropdown'\n\ninterface Props {\n revisions: RevisionSummary[]\n loading: boolean\n previewLoading: boolean\n disabled: boolean\n isPreviewMode: boolean\n onOpen: () => void\n onSelect: (id: string) => void\n}\n\nexport function RevisionHistoryDropdown({\n revisions,\n loading,\n previewLoading,\n disabled,\n isPreviewMode,\n onOpen,\n onSelect,\n}: Props) {\n const [open, setOpen] = useState(false)\n\n const handleOpenChange = (isOpen: boolean) => {\n if (isOpen && !open) {\n onOpen()\n }\n setOpen(isOpen)\n }\n\n const trigger = (\n <button\n type=\"button\"\n disabled={disabled || isPreviewMode || previewLoading}\n title={disabled ? 'Save post to enable history' : 'Revision history'}\n className=\"px-2.5 py-1.5 text-sm font-medium rounded transition-colors flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed text-gray-600 dark:text-gray-400\"\n >\n {previewLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <History className=\"w-4 h-4\" />\n )}\n </button>\n )\n\n return (\n <Dropdown\n trigger={trigger}\n open={open}\n onOpenChange={handleOpenChange}\n disabled={disabled || isPreviewMode || previewLoading}\n align=\"right\"\n className=\"w-64 max-h-80 overflow-y-auto p-0\"\n >\n <DropdownLabel>Revision History</DropdownLabel>\n <div className=\"border-t border-border\" />\n \n {loading ? (\n <div className=\"flex items-center justify-center py-4\">\n <Loader2 className=\"h-4 w-4 animate-spin text-gray-500\" />\n </div>\n ) : revisions.length === 0 ? (\n <div className=\"py-4 text-center text-sm text-gray-500\">\n No revisions yet\n </div>\n ) : (\n revisions.map((rev) => (\n <DropdownItem key={rev.id} onClick={() => onSelect(rev.id)}>\n <div className=\"flex flex-col\">\n <span className=\"text-sm truncate\">{rev.title || 'Untitled'}</span>\n <span className=\"text-xs text-gray-500\">\n {formatRelativeTime(rev.createdAt)}\n </span>\n </div>\n </DropdownItem>\n ))\n )}\n </Dropdown>\n )\n}\n","'use client'\n\nimport { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from '../../lib/cn'\n\n// ============================================\n// DROPDOWN CONTEXT\n// ============================================\n\nconst DropdownContext = createContext<{ close: () => void } | null>(null)\n\n// ============================================\n// DROPDOWN CONTAINER\n// ============================================\n\ninterface DropdownProps {\n /** The trigger element (button, avatar, etc.) */\n trigger: React.ReactNode\n /** Dropdown content */\n children: React.ReactNode\n /** Alignment relative to trigger */\n align?: 'left' | 'right'\n /** Custom width class */\n className?: string\n /** Controlled open state */\n open?: boolean\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void\n /** Disable the dropdown */\n disabled?: boolean\n}\n\nexport function Dropdown({\n trigger,\n children,\n align = 'right',\n className,\n open: controlledOpen,\n onOpenChange,\n disabled,\n}: DropdownProps) {\n const [internalOpen, setInternalOpen] = useState(false)\n const [position, setPosition] = useState({ top: 0, left: 0, right: 0 })\n const [mounted, setMounted] = useState(false)\n \n const triggerRef = useRef<HTMLDivElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n \n // Controlled vs uncontrolled\n const isControlled = controlledOpen !== undefined\n const isOpen = isControlled ? controlledOpen : internalOpen\n \n const setOpen = useCallback((value: boolean) => {\n if (!isControlled) {\n setInternalOpen(value)\n }\n onOpenChange?.(value)\n }, [isControlled, onOpenChange])\n\n // Mount check for portal\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Calculate position when opening\n const updatePosition = useCallback(() => {\n if (triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect()\n setPosition({\n top: rect.bottom + 4, // 4px gap\n left: rect.left,\n right: window.innerWidth - rect.right,\n })\n }\n }, [])\n\n // Update position on open\n useEffect(() => {\n if (isOpen) {\n updatePosition()\n }\n }, [isOpen, updatePosition])\n\n // Close on click outside\n useEffect(() => {\n if (!isOpen) return\n \n const handleClick = (e: MouseEvent) => {\n const target = e.target as Node\n if (\n menuRef.current && !menuRef.current.contains(target) &&\n triggerRef.current && !triggerRef.current.contains(target)\n ) {\n setOpen(false)\n }\n }\n \n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [isOpen, setOpen])\n\n // Close on escape\n useEffect(() => {\n if (!isOpen) return\n \n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setOpen(false)\n }\n }\n \n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [isOpen, setOpen])\n\n // Update position on scroll/resize\n useEffect(() => {\n if (!isOpen) return\n \n const handleUpdate = () => updatePosition()\n window.addEventListener('scroll', handleUpdate, true)\n window.addEventListener('resize', handleUpdate)\n return () => {\n window.removeEventListener('scroll', handleUpdate, true)\n window.removeEventListener('resize', handleUpdate)\n }\n }, [isOpen, updatePosition])\n\n const handleTriggerClick = () => {\n if (disabled) return\n setOpen(!isOpen)\n }\n\n const close = useCallback(() => setOpen(false), [setOpen])\n\n const menu = isOpen && mounted ? createPortal(\n <DropdownContext.Provider value={{ close }}>\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: position.top,\n ...(align === 'right' \n ? { right: position.right } \n : { left: position.left }\n ),\n }}\n className={cn(\n 'z-50 min-w-[160px] bg-popover border border-border rounded-md shadow-lg p-1 overscroll-contain',\n className\n )}\n onWheel={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </DropdownContext.Provider>,\n document.body\n ) : null\n\n return (\n <>\n <div ref={triggerRef} onClick={handleTriggerClick}>\n {trigger}\n </div>\n {menu}\n </>\n )\n}\n\n// ============================================\n// DROPDOWN ITEM\n// ============================================\n\ninterface DropdownItemProps {\n onClick?: () => void\n destructive?: boolean\n disabled?: boolean\n children: React.ReactNode\n className?: string\n}\n\nexport function DropdownItem({\n onClick,\n destructive,\n disabled,\n children,\n className,\n}: DropdownItemProps) {\n const context = useContext(DropdownContext)\n \n const handleClick = () => {\n if (!disabled) {\n onClick?.()\n context?.close()\n }\n }\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={disabled}\n className={cn(\n 'w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0',\n 'text-left text-sm rounded-sm cursor-default',\n 'hover:bg-accent focus:bg-accent focus:outline-none',\n destructive && 'text-destructive',\n disabled && 'opacity-50 cursor-not-allowed',\n className\n )}\n >\n {children}\n </button>\n )\n}\n\n// ============================================\n// DROPDOWN DIVIDER\n// ============================================\n\nexport function DropdownDivider() {\n return <div className=\"h-px bg-border my-1\" />\n}\n\n// ============================================\n// DROPDOWN LABEL (non-interactive header)\n// ============================================\n\nexport function DropdownLabel({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"px-3 py-1.5 md:px-2 md:py-1 text-xs font-medium text-muted-foreground\">\n {children}\n </div>\n )\n}\n","'use client'\n\nimport { useEffect, useMemo, useCallback } from 'react'\nimport { useEditor, EditorContent, Editor } from '@tiptap/react'\nimport StarterKit from '@tiptap/starter-kit'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport Link from '@tiptap/extension-link'\nimport Image from '@tiptap/extension-image'\nimport { renderMarkdown, htmlToMarkdown } from '../../lib/markdown'\nimport { CommentMark } from '../../lib/comment-mark'\n\nexport interface SelectionState {\n hasSelection: boolean\n text: string\n from: number\n to: number\n hasExistingComment: boolean\n}\n\ninterface TiptapEditorProps {\n content: string // markdown\n onChange: (markdown: string) => void\n placeholder?: string\n autoFocus?: boolean\n onEditorReady?: (editor: Editor) => void\n onSelectionChange?: (selection: SelectionState | null) => void\n onCommentClick?: (commentId: string) => void\n proseClasses?: string\n}\n\nconst DEFAULT_PROSE_CLASSES = 'prose prose-gray dark:prose-invert max-w-none'\n\nexport function TiptapEditor({ \n content, \n onChange, \n placeholder = 'Start writing...',\n autoFocus = false,\n onEditorReady,\n onSelectionChange,\n onCommentClick,\n proseClasses = DEFAULT_PROSE_CLASSES,\n}: TiptapEditorProps) {\n // Convert initial content to HTML once\n const initialHtml = useMemo(() => content ? renderMarkdown(content) : '', [content])\n\n // Memoize extensions\n const extensions = useMemo(() => [\n StarterKit.configure({\n heading: {\n levels: [1, 2, 3],\n },\n }),\n Placeholder.configure({\n placeholder,\n }),\n Link.configure({\n openOnClick: false,\n HTMLAttributes: {\n class: 'text-blue-600 dark:text-blue-400 underline',\n },\n }),\n Image.configure({\n HTMLAttributes: {\n class: 'rounded-lg max-w-full',\n },\n }),\n CommentMark.configure({\n onCommentClick,\n }),\n ], [placeholder, onCommentClick])\n\n const editor = useEditor({\n immediatelyRender: false, // Prevent SSR hydration mismatch\n extensions,\n content: initialHtml,\n onUpdate: ({ editor }) => {\n const html = editor.getHTML()\n const markdown = htmlToMarkdown(html)\n onChange(markdown)\n },\n onSelectionUpdate: ({ editor }) => {\n if (!onSelectionChange) return\n \n const { from, to, empty } = editor.state.selection\n if (empty) {\n onSelectionChange(null)\n } else {\n const text = editor.state.doc.textBetween(from, to, ' ')\n \n // Check if selection contains any comment marks\n let hasExistingComment = false\n editor.state.doc.nodesBetween(from, to, (node) => {\n if (node.marks.some(mark => mark.type.name === 'comment')) {\n hasExistingComment = true\n return false // stop iteration\n }\n })\n \n onSelectionChange({\n hasSelection: true,\n text,\n from,\n to,\n hasExistingComment,\n })\n }\n },\n editorProps: {\n attributes: {\n class: `${proseClasses} min-h-[500px] outline-none`,\n },\n },\n })\n\n // Notify parent when editor is ready and auto-focus if requested\n useEffect(() => {\n if (editor) {\n if (onEditorReady) {\n onEditorReady(editor)\n }\n if (autoFocus) {\n // Small delay to ensure DOM is ready\n setTimeout(() => editor.commands.focus(), 0)\n }\n }\n }, [editor, onEditorReady, autoFocus])\n\n // Sync external content changes (e.g., loading saved post)\n useEffect(() => {\n if (editor && content !== undefined) {\n const currentMarkdown = htmlToMarkdown(editor.getHTML())\n // Only update if content is actually different to avoid cursor jumping\n if (currentMarkdown !== content) {\n const html = content ? renderMarkdown(content) : ''\n editor.commands.setContent(html, { emitUpdate: false })\n }\n }\n }, [editor, content])\n\n return <EditorContent editor={editor} />\n}\n","import { marked } from 'marked'\nimport TurndownService from 'turndown'\n\n// Configure marked for consistent rendering\nmarked.setOptions({\n gfm: true,\n breaks: false,\n})\n\n/**\n * Render markdown to HTML\n */\nexport function renderMarkdown(markdown: string): string {\n return marked.parse(markdown) as string\n}\n\n/**\n * Convert markdown to HTML with GFM and line breaks enabled.\n * Safe for client-side use in components like ChatPanel.\n */\nexport function markdownToHtml(markdown: string): string {\n return marked.parse(markdown, { gfm: true, breaks: true }) as string\n}\n\n/**\n * Parse markdown to tokens (AST)\n */\nexport function parseMarkdown(markdown: string) {\n return marked.lexer(markdown)\n}\n\n// Configure Turndown for HTML to markdown conversion\nconst turndownService = new TurndownService({\n headingStyle: 'atx',\n codeBlockStyle: 'fenced',\n bulletListMarker: '-',\n})\n\n/**\n * Convert HTML to markdown\n */\nexport function htmlToMarkdown(html: string): string {\n return turndownService.turndown(html)\n}\n","import type { EditorState, Transaction } from '@tiptap/pm/state'\n\n/**\n * Takes a Transaction & Editor State and turns it into a chainable state object\n * @param config The transaction and state to create the chainable state from\n * @returns A chainable Editor state object\n */\nexport function createChainableState(config: { transaction: Transaction; state: EditorState }): EditorState {\n const { state, transaction } = config\n let { selection } = transaction\n let { doc } = transaction\n let { storedMarks } = transaction\n\n return {\n ...state,\n apply: state.apply.bind(state),\n applyTransaction: state.applyTransaction.bind(state),\n plugins: state.plugins,\n schema: state.schema,\n reconfigure: state.reconfigure.bind(state),\n toJSON: state.toJSON.bind(state),\n get storedMarks() {\n return storedMarks\n },\n get selection() {\n return selection\n },\n get doc() {\n return doc\n },\n get tr() {\n selection = transaction.selection\n doc = transaction.doc\n storedMarks = transaction.storedMarks\n\n return transaction\n },\n }\n}\n","import type { EditorState, Transaction } from '@tiptap/pm/state'\n\nimport type { Editor } from './Editor.js'\nimport { createChainableState } from './helpers/createChainableState.js'\nimport type { AnyCommands, CanCommands, ChainedCommands, CommandProps, SingleCommands } from './types.js'\n\nexport class CommandManager {\n editor: Editor\n\n rawCommands: AnyCommands\n\n customState?: EditorState\n\n constructor(props: { editor: Editor; state?: EditorState }) {\n this.editor = props.editor\n this.rawCommands = this.editor.extensionManager.commands\n this.customState = props.state\n }\n\n get hasCustomState(): boolean {\n return !!this.customState\n }\n\n get state(): EditorState {\n return this.customState || this.editor.state\n }\n\n get commands(): SingleCommands {\n const { rawCommands, editor, state } = this\n const { view } = editor\n const { tr } = state\n const props = this.buildProps(tr)\n\n return Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n const method = (...args: any[]) => {\n const callback = command(...args)(props)\n\n if (!tr.getMeta('preventDispatch') && !this.hasCustomState) {\n view.dispatch(tr)\n }\n\n return callback\n }\n\n return [name, method]\n }),\n ) as unknown as SingleCommands\n }\n\n get chain(): () => ChainedCommands {\n return () => this.createChain()\n }\n\n get can(): () => CanCommands {\n return () => this.createCan()\n }\n\n public createChain(startTr?: Transaction, shouldDispatch = true): ChainedCommands {\n const { rawCommands, editor, state } = this\n const { view } = editor\n const callbacks: boolean[] = []\n const hasStartTransaction = !!startTr\n const tr = startTr || state.tr\n\n const run = () => {\n if (!hasStartTransaction && shouldDispatch && !tr.getMeta('preventDispatch') && !this.hasCustomState) {\n view.dispatch(tr)\n }\n\n return callbacks.every(callback => callback === true)\n }\n\n const chain = {\n ...Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n const chainedCommand = (...args: never[]) => {\n const props = this.buildProps(tr, shouldDispatch)\n const callback = command(...args)(props)\n\n callbacks.push(callback)\n\n return chain\n }\n\n return [name, chainedCommand]\n }),\n ),\n run,\n } as unknown as ChainedCommands\n\n return chain\n }\n\n public createCan(startTr?: Transaction): CanCommands {\n const { rawCommands, state } = this\n const dispatch = false\n const tr = startTr || state.tr\n const props = this.buildProps(tr, dispatch)\n const formattedCommands = Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n return [name, (...args: never[]) => command(...args)({ ...props, dispatch: undefined })]\n }),\n ) as unknown as SingleCommands\n\n return {\n ...formattedCommands,\n chain: () => this.createChain(tr, dispatch),\n } as CanCommands\n }\n\n public buildProps(tr: Transaction, shouldDispatch = true): CommandProps {\n const { rawCommands, editor, state } = this\n const { view } = editor\n\n const props: CommandProps = {\n tr,\n editor,\n view,\n state: createChainableState({\n state,\n transaction: tr,\n }),\n dispatch: shouldDispatch ? () => undefined : undefined,\n chain: () => this.createChain(tr, shouldDispatch),\n can: () => this.createCan(tr),\n get commands() {\n return Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n return [name, (...args: never[]) => command(...args)(props)]\n }),\n ) as unknown as SingleCommands\n },\n }\n\n return props\n }\n}\n","export * from './blur.js'\nexport * from './clearContent.js'\nexport * from './clearNodes.js'\nexport * from './command.js'\nexport * from './createParagraphNear.js'\nexport * from './cut.js'\nexport * from './deleteCurrentNode.js'\nexport * from './deleteNode.js'\nexport * from './deleteRange.js'\nexport * from './deleteSelection.js'\nexport * from './enter.js'\nexport * from './exitCode.js'\nexport * from './extendMarkRange.js'\nexport * from './first.js'\nexport * from './focus.js'\nexport * from './forEach.js'\nexport * from './insertContent.js'\nexport * from './insertContentAt.js'\nexport * from './join.js'\nexport * from './joinItemBackward.js'\nexport * from './joinItemForward.js'\nexport * from './joinTextblockBackward.js'\nexport * from './joinTextblockForward.js'\nexport * from './keyboardShortcut.js'\nexport * from './lift.js'\nexport * from './liftEmptyBlock.js'\nexport * from './liftListItem.js'\nexport * from './newlineInCode.js'\nexport * from './resetAttributes.js'\nexport * from './scrollIntoView.js'\nexport * from './selectAll.js'\nexport * from './selectNodeBackward.js'\nexport * from './selectNodeForward.js'\nexport * from './selectParentNode.js'\nexport * from './selectTextblockEnd.js'\nexport * from './selectTextblockStart.js'\nexport * from './setContent.js'\nexport * from './setMark.js'\nexport * from './setMeta.js'\nexport * from './setNode.js'\nexport * from './setNodeSelection.js'\nexport * from './setTextDirection.js'\nexport * from './setTextSelection.js'\nexport * from './sinkListItem.js'\nexport * from './splitBlock.js'\nexport * from './splitListItem.js'\nexport * from './toggleList.js'\nexport * from './toggleMark.js'\nexport * from './toggleNode.js'\nexport * from './toggleWrap.js'\nexport * from './undoInputRule.js'\nexport * from './unsetAllMarks.js'\nexport * from './unsetMark.js'\nexport * from './unsetTextDirection.js'\nexport * from './updateAttributes.js'\nexport * from './wrapIn.js'\nexport * from './wrapInList.js'\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n blur: {\n /**\n * Removes focus from the editor.\n * @example editor.commands.blur()\n */\n blur: () => ReturnType\n }\n }\n}\n\nexport const blur: RawCommands['blur'] =\n () =>\n ({ editor, view }) => {\n requestAnimationFrame(() => {\n if (!editor.isDestroyed) {\n ;(view.dom as HTMLElement).blur()\n\n // Browsers should remove the caret on blur but safari does not.\n // See: https://github.com/ueberdosis/tiptap/issues/2405\n window?.getSelection()?.removeAllRanges()\n }\n })\n\n return true\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n clearContent: {\n /**\n * Clear the whole document.\n * @example editor.commands.clearContent()\n */\n clearContent: (\n /**\n * Whether to emit an update event.\n * @default true\n */\n emitUpdate?: boolean,\n ) => ReturnType\n }\n }\n}\n\nexport const clearContent: RawCommands['clearContent'] =\n (emitUpdate = true) =>\n ({ commands }) => {\n return commands.setContent('', { emitUpdate })\n }\n","import { liftTarget } from '@tiptap/pm/transform'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n clearNodes: {\n /**\n * Normalize nodes to a simple paragraph.\n * @example editor.commands.clearNodes()\n */\n clearNodes: () => ReturnType\n }\n }\n}\n\nexport const clearNodes: RawCommands['clearNodes'] =\n () =>\n ({ state, tr, dispatch }) => {\n const { selection } = tr\n const { ranges } = selection\n\n if (!dispatch) {\n return true\n }\n\n ranges.forEach(({ $from, $to }) => {\n state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {\n if (node.type.isText) {\n return\n }\n\n const { doc, mapping } = tr\n const $mappedFrom = doc.resolve(mapping.map(pos))\n const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize))\n const nodeRange = $mappedFrom.blockRange($mappedTo)\n\n if (!nodeRange) {\n return\n }\n\n const targetLiftDepth = liftTarget(nodeRange)\n\n if (node.type.isTextblock) {\n const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index())\n\n tr.setNodeMarkup(nodeRange.start, defaultType)\n }\n\n if (targetLiftDepth || targetLiftDepth === 0) {\n tr.lift(nodeRange, targetLiftDepth)\n }\n })\n })\n\n return true\n }\n","import type { Command, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n command: {\n /**\n * Define a command inline.\n * @param fn The command function.\n * @example\n * editor.commands.command(({ tr, state }) => {\n * ...\n * return true\n * })\n */\n command: (fn: (props: Parameters<Command>[0]) => boolean) => ReturnType\n }\n }\n}\n\nexport const command: RawCommands['command'] = fn => props => {\n return fn(props)\n}\n","import { createParagraphNear as originalCreateParagraphNear } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n createParagraphNear: {\n /**\n * Create a paragraph nearby.\n * @example editor.commands.createParagraphNear()\n */\n createParagraphNear: () => ReturnType\n }\n }\n}\n\nexport const createParagraphNear: RawCommands['createParagraphNear'] =\n () =>\n ({ state, dispatch }) => {\n return originalCreateParagraphNear(state, dispatch)\n }\n","import { TextSelection } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n cut: {\n /**\n * Cuts content from a range and inserts it at a given position.\n * @param range The range to cut.\n * @param range.from The start position of the range.\n * @param range.to The end position of the range.\n * @param targetPos The position to insert the content at.\n * @example editor.commands.cut({ from: 1, to: 3 }, 5)\n */\n cut: ({ from, to }: { from: number; to: number }, targetPos: number) => ReturnType\n }\n }\n}\n\nexport const cut: RawCommands['cut'] =\n (originRange, targetPos) =>\n ({ editor, tr }) => {\n const { state } = editor\n\n const contentSlice = state.doc.slice(originRange.from, originRange.to)\n\n tr.deleteRange(originRange.from, originRange.to)\n const newPos = tr.mapping.map(targetPos)\n\n tr.insert(newPos, contentSlice.content)\n\n tr.setSelection(new TextSelection(tr.doc.resolve(Math.max(newPos - 1, 0))))\n\n return true\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteCurrentNode: {\n /**\n * Delete the node that currently has the selection anchor.\n * @example editor.commands.deleteCurrentNode()\n */\n deleteCurrentNode: () => ReturnType\n }\n }\n}\n\nexport const deleteCurrentNode: RawCommands['deleteCurrentNode'] =\n () =>\n ({ tr, dispatch }) => {\n const { selection } = tr\n const currentNode = selection.$anchor.node()\n\n // if there is content inside the current node, break out of this command\n if (currentNode.content.size > 0) {\n return false\n }\n\n const $pos = tr.selection.$anchor\n\n for (let depth = $pos.depth; depth > 0; depth -= 1) {\n const node = $pos.node(depth)\n\n if (node.type === currentNode.type) {\n if (dispatch) {\n const from = $pos.before(depth)\n const to = $pos.after(depth)\n\n tr.delete(from, to).scrollIntoView()\n }\n\n return true\n }\n }\n\n return false\n }\n","import type { NodeType, Schema } from '@tiptap/pm/model'\n\nexport function getNodeType(nameOrType: string | NodeType, schema: Schema): NodeType {\n if (typeof nameOrType === 'string') {\n if (!schema.nodes[nameOrType]) {\n throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`)\n }\n\n return schema.nodes[nameOrType]\n }\n\n return nameOrType\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteNode: {\n /**\n * Delete a node with a given type or name.\n * @param typeOrName The type or name of the node.\n * @example editor.commands.deleteNode('paragraph')\n */\n deleteNode: (typeOrName: string | NodeType) => ReturnType\n }\n }\n}\n\nexport const deleteNode: RawCommands['deleteNode'] =\n typeOrName =>\n ({ tr, state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n const $pos = tr.selection.$anchor\n\n for (let depth = $pos.depth; depth > 0; depth -= 1) {\n const node = $pos.node(depth)\n\n if (node.type === type) {\n if (dispatch) {\n const from = $pos.before(depth)\n const to = $pos.after(depth)\n\n tr.delete(from, to).scrollIntoView()\n }\n\n return true\n }\n }\n\n return false\n }\n","import type { Range, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteRange: {\n /**\n * Delete a given range.\n * @param range The range to delete.\n * @example editor.commands.deleteRange({ from: 1, to: 3 })\n */\n deleteRange: (range: Range) => ReturnType\n }\n }\n}\n\nexport const deleteRange: RawCommands['deleteRange'] =\n range =>\n ({ tr, dispatch }) => {\n const { from, to } = range\n\n if (dispatch) {\n tr.delete(from, to)\n }\n\n return true\n }\n","import { deleteSelection as originalDeleteSelection } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteSelection: {\n /**\n * Delete the selection, if there is one.\n * @example editor.commands.deleteSelection()\n */\n deleteSelection: () => ReturnType\n }\n }\n}\n\nexport const deleteSelection: RawCommands['deleteSelection'] =\n () =>\n ({ state, dispatch }) => {\n return originalDeleteSelection(state, dispatch)\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n enter: {\n /**\n * Trigger enter.\n * @example editor.commands.enter()\n */\n enter: () => ReturnType\n }\n }\n}\n\nexport const enter: RawCommands['enter'] =\n () =>\n ({ commands }) => {\n return commands.keyboardShortcut('Enter')\n }\n","import { exitCode as originalExitCode } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n exitCode: {\n /**\n * Exit from a code block.\n * @example editor.commands.exitCode()\n */\n exitCode: () => ReturnType\n }\n }\n}\n\nexport const exitCode: RawCommands['exitCode'] =\n () =>\n ({ state, dispatch }) => {\n return originalExitCode(state, dispatch)\n }\n","import type { MarkType } from '@tiptap/pm/model'\nimport { TextSelection } from '@tiptap/pm/state'\n\nimport { getMarkRange } from '../helpers/getMarkRange.js'\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n extendMarkRange: {\n /**\n * Extends the text selection to the current mark by type or name.\n * @param typeOrName The type or name of the mark.\n * @param attributes The attributes of the mark.\n * @example editor.commands.extendMarkRange('bold')\n * @example editor.commands.extendMarkRange('mention', { userId: \"1\" })\n */\n extendMarkRange: (\n /**\n * The type or name of the mark.\n */\n typeOrName: string | MarkType,\n\n /**\n * The attributes of the mark.\n */\n attributes?: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const extendMarkRange: RawCommands['extendMarkRange'] =\n (typeOrName, attributes = {}) =>\n ({ tr, state, dispatch }) => {\n const type = getMarkType(typeOrName, state.schema)\n const { doc, selection } = tr\n const { $from, from, to } = selection\n\n if (dispatch) {\n const range = getMarkRange($from, type, attributes)\n\n if (range && range.from <= from && range.to >= to) {\n const newSelection = TextSelection.create(doc, range.from, range.to)\n\n tr.setSelection(newSelection)\n }\n }\n\n return true\n }\n","export function isRegExp(value: any): value is RegExp {\n return Object.prototype.toString.call(value) === '[object RegExp]'\n}\n","import { isRegExp } from './isRegExp.js'\n\n/**\n * Check if object1 includes object2\n * @param object1 Object\n * @param object2 Object\n */\nexport function objectIncludes(\n object1: Record<string, any>,\n object2: Record<string, any>,\n options: { strict: boolean } = { strict: true },\n): boolean {\n const keys = Object.keys(object2)\n\n if (!keys.length) {\n return true\n }\n\n return keys.every(key => {\n if (options.strict) {\n return object2[key] === object1[key]\n }\n\n if (isRegExp(object2[key])) {\n return object2[key].test(object1[key])\n }\n\n return object2[key] === object1[key]\n })\n}\n","import type { Mark as ProseMirrorMark, MarkType, ResolvedPos } from '@tiptap/pm/model'\n\nimport type { Range } from '../types.js'\nimport { objectIncludes } from '../utilities/objectIncludes.js'\n\nfunction findMarkInSet(\n marks: ProseMirrorMark[],\n type: MarkType,\n attributes: Record<string, any> = {},\n): ProseMirrorMark | undefined {\n return marks.find(item => {\n return (\n item.type === type &&\n objectIncludes(\n // Only check equality for the attributes that are provided\n Object.fromEntries(Object.keys(attributes).map(k => [k, item.attrs[k]])),\n attributes,\n )\n )\n })\n}\n\nfunction isMarkInSet(marks: ProseMirrorMark[], type: MarkType, attributes: Record<string, any> = {}): boolean {\n return !!findMarkInSet(marks, type, attributes)\n}\n\n/**\n * Get the range of a mark at a resolved position.\n */\nexport function getMarkRange(\n /**\n * The position to get the mark range for.\n */\n $pos: ResolvedPos,\n /**\n * The mark type to get the range for.\n */\n type: MarkType,\n /**\n * The attributes to match against.\n * If not provided, only the first mark at the position will be matched.\n */\n attributes?: Record<string, any>,\n): Range | void {\n if (!$pos || !type) {\n return\n }\n let start = $pos.parent.childAfter($pos.parentOffset)\n\n // If the cursor is at the start of a text node that does not have the mark, look backward\n if (!start.node || !start.node.marks.some(mark => mark.type === type)) {\n start = $pos.parent.childBefore($pos.parentOffset)\n }\n\n // If there is no text node with the mark even backward, return undefined\n if (!start.node || !start.node.marks.some(mark => mark.type === type)) {\n return\n }\n\n // Default to only matching against the first mark's attributes\n attributes = attributes || start.node.marks[0]?.attrs\n\n // We now know that the cursor is either at the start, middle or end of a text node with the specified mark\n // so we can look it up on the targeted mark\n const mark = findMarkInSet([...start.node.marks], type, attributes)\n\n if (!mark) {\n return\n }\n\n let startIndex = start.index\n let startPos = $pos.start() + start.offset\n let endIndex = startIndex + 1\n let endPos = startPos + start.node.nodeSize\n\n while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {\n startIndex -= 1\n startPos -= $pos.parent.child(startIndex).nodeSize\n }\n\n while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {\n endPos += $pos.parent.child(endIndex).nodeSize\n endIndex += 1\n }\n\n return {\n from: startPos,\n to: endPos,\n }\n}\n","import type { MarkType, Schema } from '@tiptap/pm/model'\n\nexport function getMarkType(nameOrType: string | MarkType, schema: Schema): MarkType {\n if (typeof nameOrType === 'string') {\n if (!schema.marks[nameOrType]) {\n throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`)\n }\n\n return schema.marks[nameOrType]\n }\n\n return nameOrType\n}\n","import type { Command, CommandProps, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n first: {\n /**\n * Runs one command after the other and stops at the first which returns true.\n * @param commands The commands to run.\n * @example editor.commands.first([command1, command2])\n */\n first: (commands: Command[] | ((props: CommandProps) => Command[])) => ReturnType\n }\n }\n}\n\nexport const first: RawCommands['first'] = commands => props => {\n const items = typeof commands === 'function' ? commands(props) : commands\n\n for (let i = 0; i < items.length; i += 1) {\n if (items[i](props)) {\n return true\n }\n }\n\n return false\n}\n","import { TextSelection } from '@tiptap/pm/state'\n\nexport function isTextSelection(value: unknown): value is TextSelection {\n return value instanceof TextSelection\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Selection, TextSelection } from '@tiptap/pm/state'\n\nimport type { FocusPosition } from '../types.js'\nimport { minMax } from '../utilities/minMax.js'\n\nexport function resolveFocusPosition(doc: ProseMirrorNode, position: FocusPosition = null): Selection | null {\n if (!position) {\n return null\n }\n\n const selectionAtStart = Selection.atStart(doc)\n const selectionAtEnd = Selection.atEnd(doc)\n\n if (position === 'start' || position === true) {\n return selectionAtStart\n }\n\n if (position === 'end') {\n return selectionAtEnd\n }\n\n const minPos = selectionAtStart.from\n const maxPos = selectionAtEnd.to\n\n if (position === 'all') {\n return TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos))\n }\n\n return TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos))\n}\n","export function minMax(value = 0, min = 0, max = 0): number {\n return Math.min(Math.max(value, min), max)\n}\n","export function isAndroid(): boolean {\n return navigator.platform === 'Android' || /android/i.test(navigator.userAgent)\n}\n","export function isiOS(): boolean {\n return (\n ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) ||\n // iPad on iOS 13 detection\n (navigator.userAgent.includes('Mac') && 'ontouchend' in document)\n )\n}\n","/**\n * Detects if the current browser is Safari (but not iOS Safari or Chrome).\n * @returns `true` if the browser is Safari, `false` otherwise.\n * @example\n * if (isSafari()) {\n * // Safari-specific handling\n * }\n */\nexport function isSafari(): boolean {\n return typeof navigator !== 'undefined' ? /^((?!chrome|android).)*safari/i.test(navigator.userAgent) : false\n}\n","import { isTextSelection } from '../helpers/isTextSelection.js'\nimport { resolveFocusPosition } from '../helpers/resolveFocusPosition.js'\nimport type { FocusPosition, RawCommands } from '../types.js'\nimport { isAndroid } from '../utilities/isAndroid.js'\nimport { isiOS } from '../utilities/isiOS.js'\nimport { isSafari } from '../utilities/isSafari.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n focus: {\n /**\n * Focus the editor at the given position.\n * @param position The position to focus at.\n * @param options.scrollIntoView Scroll the focused position into view after focusing\n * @example editor.commands.focus()\n * @example editor.commands.focus(32, { scrollIntoView: false })\n */\n focus: (\n /**\n * The position to focus at.\n */\n position?: FocusPosition,\n\n /**\n * Optional options\n * @default { scrollIntoView: true }\n */\n options?: {\n scrollIntoView?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\nexport const focus: RawCommands['focus'] =\n (position = null, options = {}) =>\n ({ editor, view, tr, dispatch }) => {\n options = {\n scrollIntoView: true,\n ...options,\n }\n\n const delayedFocus = () => {\n // focus within `requestAnimationFrame` breaks focus on iOS and Android\n // so we have to call this\n if (isiOS() || isAndroid()) {\n ;(view.dom as HTMLElement).focus()\n }\n\n // Safari requires preventScroll to avoid the browser scrolling to the\n // top of the editor when focus is called before the selection is set.\n // We exclude iOS and Android since they are already handled above.\n // see: https://github.com/ueberdosis/tiptap/issues/7318\n if (isSafari() && !isiOS() && !isAndroid()) {\n ;(view.dom as HTMLElement).focus({ preventScroll: true })\n }\n\n // For React we have to focus asynchronously. Otherwise wild things happen.\n // see: https://github.com/ueberdosis/tiptap/issues/1520\n requestAnimationFrame(() => {\n if (!editor.isDestroyed) {\n view.focus()\n\n if (options?.scrollIntoView) {\n editor.commands.scrollIntoView()\n }\n }\n })\n }\n\n if ((view.hasFocus() && position === null) || position === false) {\n return true\n }\n\n // we don’t try to resolve a NodeSelection or CellSelection\n if (dispatch && position === null && !isTextSelection(editor.state.selection)) {\n delayedFocus()\n return true\n }\n\n // pass through tr.doc instead of editor.state.doc\n // since transactions could change the editors state before this command has been run\n const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection\n const isSameSelection = editor.state.selection.eq(selection)\n\n if (dispatch) {\n if (!isSameSelection) {\n tr.setSelection(selection)\n }\n\n // `tr.setSelection` resets the stored marks\n // so we’ll restore them if the selection is the same as before\n if (isSameSelection && tr.storedMarks) {\n tr.setStoredMarks(tr.storedMarks)\n }\n\n delayedFocus()\n }\n\n return true\n }\n","import type { CommandProps, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n forEach: {\n /**\n * Loop through an array of items.\n */\n forEach: <T>(\n items: T[],\n fn: (\n item: T,\n props: CommandProps & {\n index: number\n },\n ) => boolean,\n ) => ReturnType\n }\n }\n}\n\nexport const forEach: RawCommands['forEach'] = (items, fn) => props => {\n return items.every((item, index) => fn(item, { ...props, index }))\n}\n","import type { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'\n\nimport type { Content, RawCommands } from '../types.js'\n\nexport interface InsertContentOptions {\n /**\n * Options for parsing the content.\n */\n parseOptions?: ParseOptions\n\n /**\n * Whether to update the selection after inserting the content.\n */\n updateSelection?: boolean\n applyInputRules?: boolean\n applyPasteRules?: boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n insertContent: {\n /**\n * Insert a node or string of HTML at the current position.\n * @example editor.commands.insertContent('<h1>Example</h1>')\n * @example editor.commands.insertContent('<h1>Example</h1>', { updateSelection: false })\n */\n insertContent: (\n /**\n * The ProseMirror content to insert.\n */\n value: Content | ProseMirrorNode | Fragment,\n\n /**\n * Optional options\n */\n options?: InsertContentOptions,\n ) => ReturnType\n }\n }\n}\n\nexport const insertContent: RawCommands['insertContent'] =\n (value, options) =>\n ({ tr, commands }) => {\n return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options)\n }\n","import type { Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'\nimport { Fragment } from '@tiptap/pm/model'\n\nimport { createNodeFromContent } from '../helpers/createNodeFromContent.js'\nimport { selectionToInsertionEnd } from '../helpers/selectionToInsertionEnd.js'\nimport type { Content, Range, RawCommands } from '../types.js'\n\nexport interface InsertContentAtOptions {\n /**\n * Options for parsing the content.\n */\n parseOptions?: ParseOptions\n\n /**\n * Whether to update the selection after inserting the content.\n */\n updateSelection?: boolean\n\n /**\n * Whether to apply input rules after inserting the content.\n */\n applyInputRules?: boolean\n\n /**\n * Whether to apply paste rules after inserting the content.\n */\n applyPasteRules?: boolean\n\n /**\n * Whether to throw an error if the content is invalid.\n */\n errorOnInvalidContent?: boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n insertContentAt: {\n /**\n * Insert a node or string of HTML at a specific position.\n * @example editor.commands.insertContentAt(0, '<h1>Example</h1>')\n */\n insertContentAt: (\n /**\n * The position to insert the content at.\n */\n position: number | Range,\n\n /**\n * The ProseMirror content to insert.\n */\n value: Content | ProseMirrorNode | Fragment,\n\n /**\n * Optional options\n */\n options?: InsertContentAtOptions,\n ) => ReturnType\n }\n }\n}\n\nconst isFragment = (nodeOrFragment: ProseMirrorNode | Fragment): nodeOrFragment is Fragment => {\n return !('type' in nodeOrFragment)\n}\n\nexport const insertContentAt: RawCommands['insertContentAt'] =\n (position, value, options) =>\n ({ tr, dispatch, editor }) => {\n if (dispatch) {\n options = {\n parseOptions: editor.options.parseOptions,\n updateSelection: true,\n applyInputRules: false,\n applyPasteRules: false,\n ...options,\n }\n\n let content: Fragment | ProseMirrorNode\n\n const emitContentError = (error: Error) => {\n editor.emit('contentError', {\n editor,\n error,\n disableCollaboration: () => {\n if (\n 'collaboration' in editor.storage &&\n typeof editor.storage.collaboration === 'object' &&\n editor.storage.collaboration\n ) {\n ;(editor.storage.collaboration as any).isDisabled = true\n }\n },\n })\n }\n\n const parseOptions: ParseOptions = {\n preserveWhitespace: 'full',\n ...options.parseOptions,\n }\n\n // If `emitContentError` is enabled, we want to check the content for errors\n // but ignore them (do not remove the invalid content from the document)\n if (!options.errorOnInvalidContent && !editor.options.enableContentCheck && editor.options.emitContentError) {\n try {\n createNodeFromContent(value, editor.schema, {\n parseOptions,\n errorOnInvalidContent: true,\n })\n } catch (e) {\n emitContentError(e as Error)\n }\n }\n\n try {\n content = createNodeFromContent(value, editor.schema, {\n parseOptions,\n errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,\n })\n } catch (e) {\n emitContentError(e as Error)\n return false\n }\n\n let { from, to } =\n typeof position === 'number' ? { from: position, to: position } : { from: position.from, to: position.to }\n\n let isOnlyTextContent = true\n let isOnlyBlockContent = true\n const nodes = isFragment(content) ? content : [content]\n\n nodes.forEach(node => {\n // check if added node is valid\n node.check()\n\n isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false\n\n isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false\n })\n\n // check if we can replace the wrapping node by\n // the newly inserted content\n // example:\n // replace an empty paragraph by an inserted image\n // instead of inserting the image below the paragraph\n if (from === to && isOnlyBlockContent) {\n const { parent } = tr.doc.resolve(from)\n const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount\n\n if (isEmptyTextBlock) {\n from -= 1\n to += 1\n }\n }\n\n let newContent\n\n // if there is only plain text we have to use `insertText`\n // because this will keep the current marks\n if (isOnlyTextContent) {\n // if value is string, we can use it directly\n // otherwise if it is an array, we have to join it\n if (Array.isArray(value)) {\n newContent = value.map(v => v.text || '').join('')\n } else if (value instanceof Fragment) {\n let text = ''\n\n value.forEach(node => {\n if (node.text) {\n text += node.text\n }\n })\n\n newContent = text\n } else if (typeof value === 'object' && !!value && !!value.text) {\n newContent = value.text\n } else {\n newContent = value as string\n }\n\n tr.insertText(newContent, from, to)\n } else {\n newContent = content\n\n const $from = tr.doc.resolve(from)\n const $fromNode = $from.node()\n const fromSelectionAtStart = $from.parentOffset === 0\n const isTextSelection = $fromNode.isText || $fromNode.isTextblock\n const hasContent = $fromNode.content.size > 0\n\n if (fromSelectionAtStart && isTextSelection && hasContent) {\n from = Math.max(0, from - 1)\n }\n\n tr.replaceWith(from, to, newContent)\n }\n\n // set cursor at end of inserted content\n if (options.updateSelection) {\n selectionToInsertionEnd(tr, tr.steps.length - 1, -1)\n }\n\n if (options.applyInputRules) {\n tr.setMeta('applyInputRules', { from, text: newContent })\n }\n\n if (options.applyPasteRules) {\n tr.setMeta('applyPasteRules', { from, text: newContent })\n }\n }\n\n return true\n }\n","import type { ParseOptions } from '@tiptap/pm/model'\nimport { DOMParser, Fragment, Node as ProseMirrorNode, Schema } from '@tiptap/pm/model'\n\nimport type { Content } from '../types.js'\nimport { elementFromString } from '../utilities/elementFromString.js'\n\nexport type CreateNodeFromContentOptions = {\n slice?: boolean\n parseOptions?: ParseOptions\n errorOnInvalidContent?: boolean\n}\n\n/**\n * Takes a JSON or HTML content and creates a Prosemirror node or fragment from it.\n * @param content The JSON or HTML content to create the node from\n * @param schema The Prosemirror schema to use for the node\n * @param options Options for the parser\n * @returns The created Prosemirror node or fragment\n */\nexport function createNodeFromContent(\n content: Content | ProseMirrorNode | Fragment,\n schema: Schema,\n options?: CreateNodeFromContentOptions,\n): ProseMirrorNode | Fragment {\n if (content instanceof ProseMirrorNode || content instanceof Fragment) {\n return content\n }\n options = {\n slice: true,\n parseOptions: {},\n ...options,\n }\n\n const isJSONContent = typeof content === 'object' && content !== null\n const isTextContent = typeof content === 'string'\n\n if (isJSONContent) {\n try {\n const isArrayContent = Array.isArray(content) && content.length > 0\n\n // if the JSON Content is an array of nodes, create a fragment for each node\n if (isArrayContent) {\n return Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)))\n }\n\n const node = schema.nodeFromJSON(content)\n\n if (options.errorOnInvalidContent) {\n node.check()\n }\n\n return node\n } catch (error) {\n if (options.errorOnInvalidContent) {\n throw new Error('[tiptap error]: Invalid JSON content', { cause: error as Error })\n }\n\n console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)\n\n return createNodeFromContent('', schema, options)\n }\n }\n\n if (isTextContent) {\n // Check for invalid content\n if (options.errorOnInvalidContent) {\n let hasInvalidContent = false\n let invalidContent = ''\n\n // A copy of the current schema with a catch-all node at the end\n const contentCheckSchema = new Schema({\n topNode: schema.spec.topNode,\n marks: schema.spec.marks,\n // Prosemirror's schemas are executed such that: the last to execute, matches last\n // This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle\n nodes: schema.spec.nodes.append({\n __tiptap__private__unknown__catch__all__node: {\n content: 'inline*',\n group: 'block',\n parseDOM: [\n {\n tag: '*',\n getAttrs: e => {\n // If this is ever called, we know that the content has something that we don't know how to handle in the schema\n hasInvalidContent = true\n // Try to stringify the element for a more helpful error message\n invalidContent = typeof e === 'string' ? e : e.outerHTML\n return null\n },\n },\n ],\n },\n }),\n })\n\n if (options.slice) {\n DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions)\n } else {\n DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions)\n }\n\n if (options.errorOnInvalidContent && hasInvalidContent) {\n throw new Error('[tiptap error]: Invalid HTML content', {\n cause: new Error(`Invalid element found: ${invalidContent}`),\n })\n }\n }\n\n const parser = DOMParser.fromSchema(schema)\n\n if (options.slice) {\n return parser.parseSlice(elementFromString(content), options.parseOptions).content\n }\n\n return parser.parse(elementFromString(content), options.parseOptions)\n }\n\n return createNodeFromContent('', schema, options)\n}\n","const removeWhitespaces = (node: HTMLElement) => {\n const children = node.childNodes\n\n for (let i = children.length - 1; i >= 0; i -= 1) {\n const child = children[i]\n\n if (child.nodeType === 3 && child.nodeValue && /^(\\n\\s\\s|\\n)$/.test(child.nodeValue)) {\n node.removeChild(child)\n } else if (child.nodeType === 1) {\n removeWhitespaces(child as HTMLElement)\n }\n }\n\n return node\n}\n\nexport function elementFromString(value: string): HTMLElement {\n if (typeof window === 'undefined') {\n throw new Error('[tiptap error]: there is no window object available, so this function cannot be used')\n }\n // add a wrapper to preserve leading and trailing whitespace\n const wrappedValue = `<body>${value}</body>`\n\n const html = new window.DOMParser().parseFromString(wrappedValue, 'text/html').body\n\n return removeWhitespaces(html)\n}\n","import type { Transaction } from '@tiptap/pm/state'\nimport { Selection } from '@tiptap/pm/state'\nimport { ReplaceAroundStep, ReplaceStep } from '@tiptap/pm/transform'\n\n// source: https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466\nexport function selectionToInsertionEnd(tr: Transaction, startLen: number, bias: number) {\n const last = tr.steps.length - 1\n\n if (last < startLen) {\n return\n }\n\n const step = tr.steps[last]\n\n if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) {\n return\n }\n\n const map = tr.mapping.maps[last]\n let end = 0\n\n map.forEach((_from, _to, _newFrom, newTo) => {\n if (end === 0) {\n end = newTo\n }\n })\n\n tr.setSelection(Selection.near(tr.doc.resolve(end), bias))\n}\n","import {\n joinBackward as originalJoinBackward,\n joinDown as originalJoinDown,\n joinForward as originalJoinForward,\n joinUp as originalJoinUp,\n} from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinUp: {\n /**\n * Join the selected block or, if there is a text selection, the closest ancestor block of the selection that can be joined, with the sibling above it.\n * @example editor.commands.joinUp()\n */\n joinUp: () => ReturnType\n }\n joinDown: {\n /**\n * Join the selected block, or the closest ancestor of the selection that can be joined, with the sibling after it.\n * @example editor.commands.joinDown()\n */\n joinDown: () => ReturnType\n }\n joinBackward: {\n /**\n * If the selection is empty and at the start of a textblock, try to reduce the distance between that block and the one before it—if there's a block directly before it that can be joined, join them.\n * If not, try to move the selected block closer to the next one in the document structure by lifting it out of its\n * parent or moving it into a parent of the previous block. Will use the view for accurate (bidi-aware) start-of-textblock detection if given.\n * @example editor.commands.joinBackward()\n */\n joinBackward: () => ReturnType\n }\n joinForward: {\n /**\n * If the selection is empty and the cursor is at the end of a textblock, try to reduce or remove the boundary between that block and the one after it,\n * either by joining them or by moving the other block closer to this one in the tree structure.\n * Will use the view for accurate start-of-textblock detection if given.\n * @example editor.commands.joinForward()\n */\n joinForward: () => ReturnType\n }\n }\n}\n\nexport const joinUp: RawCommands['joinUp'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinUp(state, dispatch)\n }\n\nexport const joinDown: RawCommands['joinDown'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinDown(state, dispatch)\n }\n\nexport const joinBackward: RawCommands['joinBackward'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinBackward(state, dispatch)\n }\n\nexport const joinForward: RawCommands['joinForward'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinForward(state, dispatch)\n }\n","import { joinPoint } from '@tiptap/pm/transform'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinItemBackward: {\n /**\n * Join two items backward.\n * @example editor.commands.joinItemBackward()\n */\n joinItemBackward: () => ReturnType\n }\n }\n}\n\nexport const joinItemBackward: RawCommands['joinItemBackward'] =\n () =>\n ({ state, dispatch, tr }) => {\n try {\n const point = joinPoint(state.doc, state.selection.$from.pos, -1)\n\n if (point === null || point === undefined) {\n return false\n }\n\n tr.join(point, 2)\n\n if (dispatch) {\n dispatch(tr)\n }\n\n return true\n } catch {\n return false\n }\n }\n","import { joinPoint } from '@tiptap/pm/transform'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinItemForward: {\n /**\n * Join two items Forwards.\n * @example editor.commands.joinItemForward()\n */\n joinItemForward: () => ReturnType\n }\n }\n}\n\nexport const joinItemForward: RawCommands['joinItemForward'] =\n () =>\n ({ state, dispatch, tr }) => {\n try {\n const point = joinPoint(state.doc, state.selection.$from.pos, +1)\n\n if (point === null || point === undefined) {\n return false\n }\n\n tr.join(point, 2)\n\n if (dispatch) {\n dispatch(tr)\n }\n\n return true\n } catch {\n return false\n }\n }\n","import { joinTextblockBackward as originalCommand } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinTextblockBackward: {\n /**\n * A more limited form of joinBackward that only tries to join the current textblock to the one before it, if the cursor is at the start of a textblock.\n */\n joinTextblockBackward: () => ReturnType\n }\n }\n}\n\nexport const joinTextblockBackward: RawCommands['joinTextblockBackward'] =\n () =>\n ({ state, dispatch }) => {\n return originalCommand(state, dispatch)\n }\n","import { joinTextblockForward as originalCommand } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinTextblockForward: {\n /**\n * A more limited form of joinForward that only tries to join the current textblock to the one after it, if the cursor is at the end of a textblock.\n */\n joinTextblockForward: () => ReturnType\n }\n }\n}\n\nexport const joinTextblockForward: RawCommands['joinTextblockForward'] =\n () =>\n ({ state, dispatch }) => {\n return originalCommand(state, dispatch)\n }\n","export function isMacOS(): boolean {\n return typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false\n}\n","import type { RawCommands } from '../types.js'\nimport { isiOS } from '../utilities/isiOS.js'\nimport { isMacOS } from '../utilities/isMacOS.js'\n\nfunction normalizeKeyName(name: string) {\n const parts = name.split(/-(?!$)/)\n let result = parts[parts.length - 1]\n\n if (result === 'Space') {\n result = ' '\n }\n\n let alt\n let ctrl\n let shift\n let meta\n\n for (let i = 0; i < parts.length - 1; i += 1) {\n const mod = parts[i]\n\n if (/^(cmd|meta|m)$/i.test(mod)) {\n meta = true\n } else if (/^a(lt)?$/i.test(mod)) {\n alt = true\n } else if (/^(c|ctrl|control)$/i.test(mod)) {\n ctrl = true\n } else if (/^s(hift)?$/i.test(mod)) {\n shift = true\n } else if (/^mod$/i.test(mod)) {\n if (isiOS() || isMacOS()) {\n meta = true\n } else {\n ctrl = true\n }\n } else {\n throw new Error(`Unrecognized modifier name: ${mod}`)\n }\n }\n\n if (alt) {\n result = `Alt-${result}`\n }\n\n if (ctrl) {\n result = `Ctrl-${result}`\n }\n\n if (meta) {\n result = `Meta-${result}`\n }\n\n if (shift) {\n result = `Shift-${result}`\n }\n\n return result\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n keyboardShortcut: {\n /**\n * Trigger a keyboard shortcut.\n * @param name The name of the keyboard shortcut.\n * @example editor.commands.keyboardShortcut('Mod-b')\n */\n keyboardShortcut: (name: string) => ReturnType\n }\n }\n}\n\nexport const keyboardShortcut: RawCommands['keyboardShortcut'] =\n name =>\n ({ editor, view, tr, dispatch }) => {\n const keys = normalizeKeyName(name).split(/-(?!$)/)\n const key = keys.find(item => !['Alt', 'Ctrl', 'Meta', 'Shift'].includes(item))\n const event = new KeyboardEvent('keydown', {\n key: key === 'Space' ? ' ' : key,\n altKey: keys.includes('Alt'),\n ctrlKey: keys.includes('Ctrl'),\n metaKey: keys.includes('Meta'),\n shiftKey: keys.includes('Shift'),\n bubbles: true,\n cancelable: true,\n })\n\n const capturedTransaction = editor.captureTransaction(() => {\n view.someProp('handleKeyDown', f => f(view, event))\n })\n\n capturedTransaction?.steps.forEach(step => {\n const newStep = step.map(tr.mapping)\n\n if (newStep && dispatch) {\n tr.maybeStep(newStep)\n }\n })\n\n return true\n }\n","import { lift as originalLift } from '@tiptap/pm/commands'\nimport type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isNodeActive } from '../helpers/isNodeActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n lift: {\n /**\n * Removes an existing wrap if possible lifting the node out of it\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.lift('paragraph')\n * @example editor.commands.lift('heading', { level: 1 })\n */\n lift: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const lift: RawCommands['lift'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n const isActive = isNodeActive(state, type, attributes)\n\n if (!isActive) {\n return false\n }\n\n return originalLift(state, dispatch)\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport type { NodeRange } from '../types.js'\nimport { objectIncludes } from '../utilities/objectIncludes.js'\nimport { getNodeType } from './getNodeType.js'\n\nexport function isNodeActive(\n state: EditorState,\n typeOrName: NodeType | string | null,\n attributes: Record<string, any> = {},\n): boolean {\n const { from, to, empty } = state.selection\n const type = typeOrName ? getNodeType(typeOrName, state.schema) : null\n\n const nodeRanges: NodeRange[] = []\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n return\n }\n\n const relativeFrom = Math.max(from, pos)\n const relativeTo = Math.min(to, pos + node.nodeSize)\n\n nodeRanges.push({\n node,\n from: relativeFrom,\n to: relativeTo,\n })\n })\n\n const selectionRange = to - from\n const matchedNodeRanges = nodeRanges\n .filter(nodeRange => {\n if (!type) {\n return true\n }\n\n return type.name === nodeRange.node.type.name\n })\n .filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }))\n\n if (empty) {\n return !!matchedNodeRanges.length\n }\n\n const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0)\n\n return range >= selectionRange\n}\n","import { liftEmptyBlock as originalLiftEmptyBlock } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n liftEmptyBlock: {\n /**\n * If the cursor is in an empty textblock that can be lifted, lift the block.\n * @example editor.commands.liftEmptyBlock()\n */\n liftEmptyBlock: () => ReturnType\n }\n }\n}\n\nexport const liftEmptyBlock: RawCommands['liftEmptyBlock'] =\n () =>\n ({ state, dispatch }) => {\n return originalLiftEmptyBlock(state, dispatch)\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport { liftListItem as originalLiftListItem } from '@tiptap/pm/schema-list'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n liftListItem: {\n /**\n * Create a command to lift the list item around the selection up into a wrapping list.\n * @param typeOrName The type or name of the node.\n * @example editor.commands.liftListItem('listItem')\n */\n liftListItem: (typeOrName: string | NodeType) => ReturnType\n }\n }\n}\n\nexport const liftListItem: RawCommands['liftListItem'] =\n typeOrName =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalLiftListItem(type)(state, dispatch)\n }\n","import { newlineInCode as originalNewlineInCode } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n newlineInCode: {\n /**\n * Add a newline character in code.\n * @example editor.commands.newlineInCode()\n */\n newlineInCode: () => ReturnType\n }\n }\n}\n\nexport const newlineInCode: RawCommands['newlineInCode'] =\n () =>\n ({ state, dispatch }) => {\n return originalNewlineInCode(state, dispatch)\n }\n","import type { Schema } from '@tiptap/pm/model'\n\n/**\n * Get the type of a schema item by its name.\n * @param name The name of the schema item\n * @param schema The Prosemiror schema to search in\n * @returns The type of the schema item (`node` or `mark`), or null if it doesn't exist\n */\nexport function getSchemaTypeNameByName(name: string, schema: Schema): 'node' | 'mark' | null {\n if (schema.nodes[name]) {\n return 'node'\n }\n\n if (schema.marks[name]) {\n return 'mark'\n }\n\n return null\n}\n","/**\n * Remove a property or an array of properties from an object\n * @param obj Object\n * @param key Key to remove\n */\nexport function deleteProps(obj: Record<string, any>, propOrProps: string | string[]): Record<string, any> {\n const props = typeof propOrProps === 'string' ? [propOrProps] : propOrProps\n\n return Object.keys(obj).reduce((newObj: Record<string, any>, prop) => {\n if (!props.includes(prop)) {\n newObj[prop] = obj[prop]\n }\n\n return newObj\n }, {})\n}\n","import type { MarkType, NodeType } from '@tiptap/pm/model'\n\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { getSchemaTypeNameByName } from '../helpers/getSchemaTypeNameByName.js'\nimport type { RawCommands } from '../types.js'\nimport { deleteProps } from '../utilities/deleteProps.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n resetAttributes: {\n /**\n * Resets some node attributes to the default value.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node to reset.\n * @example editor.commands.resetAttributes('heading', 'level')\n */\n resetAttributes: (typeOrName: string | NodeType | MarkType, attributes: string | string[]) => ReturnType\n }\n }\n}\n\nexport const resetAttributes: RawCommands['resetAttributes'] =\n (typeOrName, attributes) =>\n ({ tr, state, dispatch }) => {\n let nodeType: NodeType | null = null\n let markType: MarkType | null = null\n\n const schemaType = getSchemaTypeNameByName(\n typeof typeOrName === 'string' ? typeOrName : typeOrName.name,\n state.schema,\n )\n\n if (!schemaType) {\n return false\n }\n\n if (schemaType === 'node') {\n nodeType = getNodeType(typeOrName as NodeType, state.schema)\n }\n\n if (schemaType === 'mark') {\n markType = getMarkType(typeOrName as MarkType, state.schema)\n }\n\n let canReset = false\n\n tr.selection.ranges.forEach(range => {\n state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {\n if (nodeType && nodeType === node.type) {\n canReset = true\n\n if (dispatch) {\n tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes))\n }\n }\n\n if (markType && node.marks.length) {\n node.marks.forEach(mark => {\n if (markType === mark.type) {\n canReset = true\n\n if (dispatch) {\n tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)))\n }\n }\n })\n }\n })\n })\n\n return canReset\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n scrollIntoView: {\n /**\n * Scroll the selection into view.\n * @example editor.commands.scrollIntoView()\n */\n scrollIntoView: () => ReturnType\n }\n }\n}\n\nexport const scrollIntoView: RawCommands['scrollIntoView'] =\n () =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n tr.scrollIntoView()\n }\n\n return true\n }\n","import { AllSelection } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectAll: {\n /**\n * Select the whole document.\n * @example editor.commands.selectAll()\n */\n selectAll: () => ReturnType\n }\n }\n}\n\nexport const selectAll: RawCommands['selectAll'] =\n () =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n const selection = new AllSelection(tr.doc)\n\n tr.setSelection(selection)\n }\n\n return true\n }\n","import { selectNodeBackward as originalSelectNodeBackward } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectNodeBackward: {\n /**\n * Select a node backward.\n * @example editor.commands.selectNodeBackward()\n */\n selectNodeBackward: () => ReturnType\n }\n }\n}\n\nexport const selectNodeBackward: RawCommands['selectNodeBackward'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectNodeBackward(state, dispatch)\n }\n","import { selectNodeForward as originalSelectNodeForward } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectNodeForward: {\n /**\n * Select a node forward.\n * @example editor.commands.selectNodeForward()\n */\n selectNodeForward: () => ReturnType\n }\n }\n}\n\nexport const selectNodeForward: RawCommands['selectNodeForward'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectNodeForward(state, dispatch)\n }\n","import { selectParentNode as originalSelectParentNode } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectParentNode: {\n /**\n * Select the parent node.\n * @example editor.commands.selectParentNode()\n */\n selectParentNode: () => ReturnType\n }\n }\n}\n\nexport const selectParentNode: RawCommands['selectParentNode'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectParentNode(state, dispatch)\n }\n","// @ts-ignore\n// TODO: add types to @types/prosemirror-commands\nimport { selectTextblockEnd as originalSelectTextblockEnd } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectTextblockEnd: {\n /**\n * Moves the cursor to the end of current text block.\n * @example editor.commands.selectTextblockEnd()\n */\n selectTextblockEnd: () => ReturnType\n }\n }\n}\n\nexport const selectTextblockEnd: RawCommands['selectTextblockEnd'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectTextblockEnd(state, dispatch)\n }\n","// @ts-ignore\n// TODO: add types to @types/prosemirror-commands\nimport { selectTextblockStart as originalSelectTextblockStart } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectTextblockStart: {\n /**\n * Moves the cursor to the start of current text block.\n * @example editor.commands.selectTextblockStart()\n */\n selectTextblockStart: () => ReturnType\n }\n }\n}\n\nexport const selectTextblockStart: RawCommands['selectTextblockStart'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectTextblockStart(state, dispatch)\n }\n","import type { Fragment, Node as ProseMirrorNode, ParseOptions, Schema } from '@tiptap/pm/model'\n\nimport type { Content } from '../types.js'\nimport { createNodeFromContent } from './createNodeFromContent.js'\n\n/**\n * Create a new Prosemirror document node from content.\n * @param content The JSON or HTML content to create the document from\n * @param schema The Prosemirror schema to use for the document\n * @param parseOptions Options for the parser\n * @returns The created Prosemirror document node\n */\nexport function createDocument(\n content: Content | ProseMirrorNode | Fragment,\n schema: Schema,\n parseOptions: ParseOptions = {},\n options: { errorOnInvalidContent?: boolean } = {},\n): ProseMirrorNode {\n return createNodeFromContent(content, schema, {\n slice: false,\n parseOptions,\n errorOnInvalidContent: options.errorOnInvalidContent,\n }) as ProseMirrorNode\n}\n","import type { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'\n\nimport { createDocument } from '../helpers/createDocument.js'\nimport type { Content, RawCommands } from '../types.js'\n\nexport interface SetContentOptions {\n /**\n * Options for parsing the content.\n * @default {}\n */\n parseOptions?: ParseOptions\n\n /**\n * Whether to throw an error if the content is invalid.\n */\n errorOnInvalidContent?: boolean\n\n /**\n * Whether to emit an update event.\n * @default true\n */\n emitUpdate?: boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setContent: {\n /**\n * Replace the whole document with new content.\n * @param content The new content.\n * @param emitUpdate Whether to emit an update event.\n * @param parseOptions Options for parsing the content.\n * @example editor.commands.setContent('<p>Example text</p>')\n */\n setContent: (\n /**\n * The new content.\n */\n content: Content | Fragment | ProseMirrorNode,\n\n /**\n * Options for `setContent`.\n */\n options?: SetContentOptions,\n ) => ReturnType\n }\n }\n}\n\nexport const setContent: RawCommands['setContent'] =\n (content, { errorOnInvalidContent, emitUpdate = true, parseOptions = {} } = {}) =>\n ({ editor, tr, dispatch, commands }) => {\n const { doc } = tr\n\n // This is to keep backward compatibility with the previous behavior\n // TODO remove this in the next major version\n if (parseOptions.preserveWhitespace !== 'full') {\n const document = createDocument(content, editor.schema, parseOptions, {\n errorOnInvalidContent: errorOnInvalidContent ?? editor.options.enableContentCheck,\n })\n\n if (dispatch) {\n tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', !emitUpdate)\n }\n return true\n }\n\n if (dispatch) {\n tr.setMeta('preventUpdate', !emitUpdate)\n }\n\n return commands.insertContentAt({ from: 0, to: doc.content.size }, content, {\n parseOptions,\n errorOnInvalidContent: errorOnInvalidContent ?? editor.options.enableContentCheck,\n })\n }\n","import type { Mark, MarkType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { getMarkType } from './getMarkType.js'\n\nexport function getMarkAttributes(state: EditorState, typeOrName: string | MarkType): Record<string, any> {\n const type = getMarkType(typeOrName, state.schema)\n const { from, to, empty } = state.selection\n const marks: Mark[] = []\n\n if (empty) {\n if (state.storedMarks) {\n marks.push(...state.storedMarks)\n }\n\n marks.push(...state.selection.$head.marks())\n } else {\n state.doc.nodesBetween(from, to, node => {\n marks.push(...node.marks)\n })\n }\n\n const mark = marks.find(markItem => markItem.type.name === type.name)\n\n if (!mark) {\n return {}\n }\n\n return { ...mark.attrs }\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport type { Transaction } from '@tiptap/pm/state'\nimport { Transform } from '@tiptap/pm/transform'\n\n/**\n * Returns a new `Transform` based on all steps of the passed transactions.\n * @param oldDoc The Prosemirror node to start from\n * @param transactions The transactions to combine\n * @returns A new `Transform` with all steps of the passed transactions\n */\nexport function combineTransactionSteps(oldDoc: ProseMirrorNode, transactions: Transaction[]): Transform {\n const transform = new Transform(oldDoc)\n\n transactions.forEach(transaction => {\n transaction.steps.forEach(step => {\n transform.step(step)\n })\n })\n\n return transform\n}\n","import type { ContentMatch, NodeType } from '@tiptap/pm/model'\n\n/**\n * Gets the default block type at a given match\n * @param match The content match to get the default block type from\n * @returns The default block type or null\n */\nexport function defaultBlockAt(match: ContentMatch): NodeType | null {\n for (let i = 0; i < match.edgeCount; i += 1) {\n const { type } = match.edge(i)\n\n if (type.isTextblock && !type.hasRequiredAttrs()) {\n return type\n }\n }\n\n return null\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { NodeWithPos, Predicate } from '../types.js'\n\n/**\n * Find children inside a Prosemirror node that match a predicate.\n * @param node The Prosemirror node to search in\n * @param predicate The predicate to match\n * @returns An array of nodes with their positions\n */\nexport function findChildren(node: ProseMirrorNode, predicate: Predicate): NodeWithPos[] {\n const nodesWithPos: NodeWithPos[] = []\n\n node.descendants((child, pos) => {\n if (predicate(child)) {\n nodesWithPos.push({\n node: child,\n pos,\n })\n }\n })\n\n return nodesWithPos\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { NodeWithPos, Predicate, Range } from '../types.js'\n\n/**\n * Same as `findChildren` but searches only within a `range`.\n * @param node The Prosemirror node to search in\n * @param range The range to search in\n * @param predicate The predicate to match\n * @returns An array of nodes with their positions\n */\nexport function findChildrenInRange(node: ProseMirrorNode, range: Range, predicate: Predicate): NodeWithPos[] {\n const nodesWithPos: NodeWithPos[] = []\n\n // if (range.from === range.to) {\n // const nodeAt = node.nodeAt(range.from)\n\n // if (nodeAt) {\n // nodesWithPos.push({\n // node: nodeAt,\n // pos: range.from,\n // })\n // }\n // }\n\n node.nodesBetween(range.from, range.to, (child, pos) => {\n if (predicate(child)) {\n nodesWithPos.push({\n node: child,\n pos,\n })\n }\n })\n\n return nodesWithPos\n}\n","import type { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'\n\nimport type { Predicate } from '../types.js'\n\n/**\n * Finds the closest parent node to a resolved position that matches a predicate.\n * @param $pos The resolved position to search from\n * @param predicate The predicate to match\n * @returns The closest parent node to the resolved position that matches the predicate\n * @example ```js\n * findParentNodeClosestToPos($from, node => node.type.name === 'paragraph')\n * ```\n */\nexport function findParentNodeClosestToPos(\n $pos: ResolvedPos,\n predicate: Predicate,\n):\n | {\n pos: number\n start: number\n depth: number\n node: ProseMirrorNode\n }\n | undefined {\n for (let i = $pos.depth; i > 0; i -= 1) {\n const node = $pos.node(i)\n\n if (predicate(node)) {\n return {\n pos: i > 0 ? $pos.before(i) : 0,\n start: $pos.start(i),\n depth: i,\n node,\n }\n }\n }\n}\n","import type { Selection } from '@tiptap/pm/state'\n\nimport type { Predicate } from '../types.js'\nimport { findParentNodeClosestToPos } from './findParentNodeClosestToPos.js'\n\n/**\n * Finds the closest parent node to the current selection that matches a predicate.\n * @param predicate The predicate to match\n * @returns A command that finds the closest parent node to the current selection that matches the predicate\n * @example ```js\n * findParentNode(node => node.type.name === 'paragraph')\n * ```\n */\nexport function findParentNode(\n predicate: Predicate,\n): (selection: Selection) => ReturnType<typeof findParentNodeClosestToPos> {\n return (selection: Selection) => findParentNodeClosestToPos(selection.$from, predicate)\n}\n","import type { ExtensionConfig } from '../Extension.js'\nimport type { MarkConfig } from '../Mark.js'\nimport type { NodeConfig } from '../Node.js'\nimport type { AnyExtension, MaybeThisParameterType, RemoveThis } from '../types.js'\n\n/**\n * Returns a field from an extension\n * @param extension The Tiptap extension\n * @param field The field, for example `renderHTML` or `priority`\n * @param context The context object that should be passed as `this` into the function\n * @returns The field value\n */\nexport function getExtensionField<T = any, E extends AnyExtension = any>(\n extension: E,\n field: keyof ExtensionConfig | keyof MarkConfig | keyof NodeConfig,\n context?: Omit<MaybeThisParameterType<T>, 'parent'>,\n): RemoveThis<T> {\n if (extension.config[field as keyof typeof extension.config] === undefined && extension.parent) {\n return getExtensionField(extension.parent, field, context)\n }\n\n if (typeof extension.config[field as keyof typeof extension.config] === 'function') {\n const value = (extension.config[field as keyof typeof extension.config] as any).bind({\n ...context,\n parent: extension.parent ? getExtensionField(extension.parent, field, context) : null,\n })\n\n return value\n }\n\n return extension.config[field as keyof typeof extension.config] as RemoveThis<T>\n}\n","import type { AnyConfig, Extensions } from '../types.js'\nimport { getExtensionField } from './getExtensionField.js'\n\n/**\n * Create a flattened array of extensions by traversing the `addExtensions` field.\n * @param extensions An array of Tiptap extensions\n * @returns A flattened array of Tiptap extensions\n */\nexport function flattenExtensions(extensions: Extensions): Extensions {\n return (\n extensions\n .map(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n const addExtensions = getExtensionField<AnyConfig['addExtensions']>(extension, 'addExtensions', context)\n\n if (addExtensions) {\n return [extension, ...flattenExtensions(addExtensions())]\n }\n\n return extension\n })\n // `Infinity` will break TypeScript so we set a number that is probably high enough\n .flat(10)\n )\n}\n","import { Node } from '@tiptap/pm/model'\n\nimport type { Extensions, JSONContent } from '../types.js'\nimport { getHTMLFromFragment } from './getHTMLFromFragment.js'\nimport { getSchema } from './getSchema.js'\n\n/**\n * Generate HTML from a JSONContent\n * @param doc The JSONContent to generate HTML from\n * @param extensions The extensions to use for the schema\n * @returns The generated HTML\n */\nexport function generateHTML(doc: JSONContent, extensions: Extensions): string {\n const schema = getSchema(extensions)\n const contentNode = Node.fromJSON(schema, doc)\n\n return getHTMLFromFragment(contentNode.content, schema)\n}\n","import type { Fragment, Schema } from '@tiptap/pm/model'\nimport { DOMSerializer } from '@tiptap/pm/model'\n\nexport function getHTMLFromFragment(fragment: Fragment, schema: Schema): string {\n const documentFragment = DOMSerializer.fromSchema(schema).serializeFragment(fragment)\n\n const temporaryDocument = document.implementation.createHTMLDocument()\n const container = temporaryDocument.createElement('div')\n\n container.appendChild(documentFragment)\n\n return container.innerHTML\n}\n","import type { MarkSpec, NodeSpec, TagParseRule } from '@tiptap/pm/model'\nimport { Schema } from '@tiptap/pm/model'\n\nimport type { Editor, MarkConfig, NodeConfig } from '../index.js'\nimport type { AnyConfig, Extensions } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\nimport { isEmptyObject } from '../utilities/isEmptyObject.js'\nimport { getAttributesFromExtensions } from './getAttributesFromExtensions.js'\nimport { getExtensionField } from './getExtensionField.js'\nimport { getRenderedAttributes } from './getRenderedAttributes.js'\nimport { injectExtensionAttributesToParseRule } from './injectExtensionAttributesToParseRule.js'\nimport { splitExtensions } from './splitExtensions.js'\n\nfunction cleanUpSchemaItem<T>(data: T) {\n return Object.fromEntries(\n // @ts-ignore\n Object.entries(data).filter(([key, value]) => {\n if (key === 'attrs' && isEmptyObject(value as object | undefined)) {\n return false\n }\n\n return value !== null && value !== undefined\n }),\n ) as T\n}\n\n/**\n * Builds an attribute spec tuple for ProseMirror schema from an extension attribute.\n * @param extensionAttribute The extension attribute to build the spec for\n * @returns A tuple of [attributeName, spec]\n */\nfunction buildAttributeSpec(\n extensionAttribute: ReturnType<typeof getAttributesFromExtensions>[number],\n): [string, Record<string, any>] {\n const spec: Record<string, any> = {}\n\n // Only include 'default' if the attribute is not required and default is set on the attribute\n if (!extensionAttribute?.attribute?.isRequired && 'default' in (extensionAttribute?.attribute || {})) {\n spec.default = extensionAttribute.attribute.default\n }\n\n // Only include 'validate' if it's defined\n if (extensionAttribute?.attribute?.validate !== undefined) {\n spec.validate = extensionAttribute.attribute.validate\n }\n\n return [extensionAttribute.name, spec]\n}\n\n/**\n * Creates a new Prosemirror schema based on the given extensions.\n * @param extensions An array of Tiptap extensions\n * @param editor The editor instance\n * @returns A Prosemirror schema\n */\nexport function getSchemaByResolvedExtensions(extensions: Extensions, editor?: Editor): Schema {\n const allAttributes = getAttributesFromExtensions(extensions)\n const { nodeExtensions, markExtensions } = splitExtensions(extensions)\n const topNode = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))?.name\n\n const nodes = Object.fromEntries(\n nodeExtensions.map(extension => {\n const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n editor,\n }\n\n const extraNodeFields = extensions.reduce((fields, e) => {\n const extendNodeSchema = getExtensionField<AnyConfig['extendNodeSchema']>(e, 'extendNodeSchema', context)\n\n return {\n ...fields,\n ...(extendNodeSchema ? extendNodeSchema(extension) : {}),\n }\n }, {})\n\n const schema: NodeSpec = cleanUpSchemaItem({\n ...extraNodeFields,\n content: callOrReturn(getExtensionField<NodeConfig['content']>(extension, 'content', context)),\n marks: callOrReturn(getExtensionField<NodeConfig['marks']>(extension, 'marks', context)),\n group: callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context)),\n inline: callOrReturn(getExtensionField<NodeConfig['inline']>(extension, 'inline', context)),\n atom: callOrReturn(getExtensionField<NodeConfig['atom']>(extension, 'atom', context)),\n selectable: callOrReturn(getExtensionField<NodeConfig['selectable']>(extension, 'selectable', context)),\n draggable: callOrReturn(getExtensionField<NodeConfig['draggable']>(extension, 'draggable', context)),\n code: callOrReturn(getExtensionField<NodeConfig['code']>(extension, 'code', context)),\n whitespace: callOrReturn(getExtensionField<NodeConfig['whitespace']>(extension, 'whitespace', context)),\n linebreakReplacement: callOrReturn(\n getExtensionField<NodeConfig['linebreakReplacement']>(extension, 'linebreakReplacement', context),\n ),\n defining: callOrReturn(getExtensionField<NodeConfig['defining']>(extension, 'defining', context)),\n isolating: callOrReturn(getExtensionField<NodeConfig['isolating']>(extension, 'isolating', context)),\n attrs: Object.fromEntries(extensionAttributes.map(buildAttributeSpec)),\n })\n\n const parseHTML = callOrReturn(getExtensionField<NodeConfig['parseHTML']>(extension, 'parseHTML', context))\n\n if (parseHTML) {\n schema.parseDOM = parseHTML.map(parseRule =>\n injectExtensionAttributesToParseRule(parseRule, extensionAttributes),\n ) as TagParseRule[]\n }\n\n const renderHTML = getExtensionField<NodeConfig['renderHTML']>(extension, 'renderHTML', context)\n\n if (renderHTML) {\n schema.toDOM = node =>\n renderHTML({\n node,\n HTMLAttributes: getRenderedAttributes(node, extensionAttributes),\n })\n }\n\n const renderText = getExtensionField<NodeConfig['renderText']>(extension, 'renderText', context)\n\n if (renderText) {\n schema.toText = renderText\n }\n\n return [extension.name, schema]\n }),\n )\n\n const marks = Object.fromEntries(\n markExtensions.map(extension => {\n const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n editor,\n }\n\n const extraMarkFields = extensions.reduce((fields, e) => {\n const extendMarkSchema = getExtensionField<AnyConfig['extendMarkSchema']>(e, 'extendMarkSchema', context)\n\n return {\n ...fields,\n ...(extendMarkSchema ? extendMarkSchema(extension as any) : {}),\n }\n }, {})\n\n const schema: MarkSpec = cleanUpSchemaItem({\n ...extraMarkFields,\n inclusive: callOrReturn(getExtensionField<MarkConfig['inclusive']>(extension, 'inclusive', context)),\n excludes: callOrReturn(getExtensionField<MarkConfig['excludes']>(extension, 'excludes', context)),\n group: callOrReturn(getExtensionField<MarkConfig['group']>(extension, 'group', context)),\n spanning: callOrReturn(getExtensionField<MarkConfig['spanning']>(extension, 'spanning', context)),\n code: callOrReturn(getExtensionField<MarkConfig['code']>(extension, 'code', context)),\n attrs: Object.fromEntries(extensionAttributes.map(buildAttributeSpec)),\n })\n\n const parseHTML = callOrReturn(getExtensionField<MarkConfig['parseHTML']>(extension, 'parseHTML', context))\n\n if (parseHTML) {\n schema.parseDOM = parseHTML.map(parseRule =>\n injectExtensionAttributesToParseRule(parseRule, extensionAttributes),\n )\n }\n\n const renderHTML = getExtensionField<MarkConfig['renderHTML']>(extension, 'renderHTML', context)\n\n if (renderHTML) {\n schema.toDOM = mark =>\n renderHTML({\n mark,\n HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),\n })\n }\n\n return [extension.name, schema]\n }),\n )\n\n return new Schema({\n topNode,\n nodes,\n marks,\n })\n}\n","// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function isFunction(value: any): value is Function {\n return typeof value === 'function'\n}\n","import type { MaybeReturnType } from '../types.js'\nimport { isFunction } from './isFunction.js'\n\n/**\n * Optionally calls `value` as a function.\n * Otherwise it is returned directly.\n * @param value Function or any value.\n * @param context Optional context to bind to function.\n * @param props Optional props to pass to function.\n */\nexport function callOrReturn<T>(value: T, context: any = undefined, ...props: any[]): MaybeReturnType<T> {\n if (isFunction(value)) {\n if (context) {\n return value.bind(context)(...props)\n }\n\n return value(...props)\n }\n\n return value as MaybeReturnType<T>\n}\n","export function isEmptyObject(value = {}): boolean {\n return Object.keys(value).length === 0 && value.constructor === Object\n}\n","import type { Extension } from '../Extension.js'\nimport type { Mark } from '../Mark.js'\nimport type { Node } from '../Node.js'\nimport type { Extensions } from '../types.js'\n\nexport function splitExtensions(extensions: Extensions) {\n const baseExtensions = extensions.filter(extension => extension.type === 'extension') as Extension[]\n const nodeExtensions = extensions.filter(extension => extension.type === 'node') as Node[]\n const markExtensions = extensions.filter(extension => extension.type === 'mark') as Mark[]\n\n return {\n baseExtensions,\n nodeExtensions,\n markExtensions,\n }\n}\n","import type { MarkConfig, NodeConfig } from '../index.js'\nimport type { AnyConfig, Attribute, Attributes, ExtensionAttribute, Extensions } from '../types.js'\nimport { getExtensionField } from './getExtensionField.js'\nimport { splitExtensions } from './splitExtensions.js'\n\n/**\n * Get a list of all extension attributes defined in `addAttribute` and `addGlobalAttribute`.\n * @param extensions List of extensions\n */\nexport function getAttributesFromExtensions(extensions: Extensions): ExtensionAttribute[] {\n const extensionAttributes: ExtensionAttribute[] = []\n const { nodeExtensions, markExtensions } = splitExtensions(extensions)\n const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions]\n const defaultAttribute: Required<Omit<Attribute, 'validate'>> & Pick<Attribute, 'validate'> = {\n default: null,\n validate: undefined,\n rendered: true,\n renderHTML: null,\n parseHTML: null,\n keepOnSplit: true,\n isRequired: false,\n }\n\n extensions.forEach(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n extensions: nodeAndMarkExtensions,\n }\n\n const addGlobalAttributes = getExtensionField<AnyConfig['addGlobalAttributes']>(\n extension,\n 'addGlobalAttributes',\n context,\n )\n\n if (!addGlobalAttributes) {\n return\n }\n\n const globalAttributes = addGlobalAttributes()\n\n globalAttributes.forEach(globalAttribute => {\n globalAttribute.types.forEach(type => {\n Object.entries(globalAttribute.attributes).forEach(([name, attribute]) => {\n extensionAttributes.push({\n type,\n name,\n attribute: {\n ...defaultAttribute,\n ...attribute,\n },\n })\n })\n })\n })\n })\n\n nodeAndMarkExtensions.forEach(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n const addAttributes = getExtensionField<NodeConfig['addAttributes'] | MarkConfig['addAttributes']>(\n extension,\n 'addAttributes',\n context,\n )\n\n if (!addAttributes) {\n return\n }\n\n // TODO: remove `as Attributes`\n const attributes = addAttributes() as Attributes\n\n Object.entries(attributes).forEach(([name, attribute]) => {\n const mergedAttr = {\n ...defaultAttribute,\n ...attribute,\n }\n\n if (typeof mergedAttr?.default === 'function') {\n mergedAttr.default = mergedAttr.default()\n }\n\n if (mergedAttr?.isRequired && mergedAttr?.default === undefined) {\n delete mergedAttr.default\n }\n\n extensionAttributes.push({\n type: extension.name,\n name,\n attribute: mergedAttr,\n })\n })\n })\n\n return extensionAttributes\n}\n","export function mergeAttributes(...objects: Record<string, any>[]): Record<string, any> {\n return objects\n .filter(item => !!item)\n .reduce((items, item) => {\n const mergedAttributes = { ...items }\n\n Object.entries(item).forEach(([key, value]) => {\n const exists = mergedAttributes[key]\n\n if (!exists) {\n mergedAttributes[key] = value\n\n return\n }\n\n if (key === 'class') {\n const valueClasses: string[] = value ? String(value).split(' ') : []\n const existingClasses: string[] = mergedAttributes[key] ? mergedAttributes[key].split(' ') : []\n\n const insertClasses = valueClasses.filter(valueClass => !existingClasses.includes(valueClass))\n\n mergedAttributes[key] = [...existingClasses, ...insertClasses].join(' ')\n } else if (key === 'style') {\n const newStyles: string[] = value\n ? value\n .split(';')\n .map((style: string) => style.trim())\n .filter(Boolean)\n : []\n const existingStyles: string[] = mergedAttributes[key]\n ? mergedAttributes[key]\n .split(';')\n .map((style: string) => style.trim())\n .filter(Boolean)\n : []\n\n const styleMap = new Map<string, string>()\n\n existingStyles.forEach(style => {\n const [property, val] = style.split(':').map(part => part.trim())\n\n styleMap.set(property, val)\n })\n\n newStyles.forEach(style => {\n const [property, val] = style.split(':').map(part => part.trim())\n\n styleMap.set(property, val)\n })\n\n mergedAttributes[key] = Array.from(styleMap.entries())\n .map(([property, val]) => `${property}: ${val}`)\n .join('; ')\n } else {\n mergedAttributes[key] = value\n }\n })\n\n return mergedAttributes\n }, {})\n}\n","import type { Mark, Node } from '@tiptap/pm/model'\n\nimport type { ExtensionAttribute } from '../types.js'\nimport { mergeAttributes } from '../utilities/mergeAttributes.js'\n\nexport function getRenderedAttributes(\n nodeOrMark: Node | Mark,\n extensionAttributes: ExtensionAttribute[],\n): Record<string, any> {\n return extensionAttributes\n .filter(attribute => attribute.type === nodeOrMark.type.name)\n .filter(item => item.attribute.rendered)\n .map(item => {\n if (!item.attribute.renderHTML) {\n return {\n [item.name]: nodeOrMark.attrs[item.name],\n }\n }\n\n return item.attribute.renderHTML(nodeOrMark.attrs) || {}\n })\n .reduce((attributes, attribute) => mergeAttributes(attributes, attribute), {})\n}\n","export function fromString(value: any): any {\n if (typeof value !== 'string') {\n return value\n }\n\n if (value.match(/^[+-]?(?:\\d*\\.)?\\d+$/)) {\n return Number(value)\n }\n\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n return value\n}\n","import type { ParseRule } from '@tiptap/pm/model'\n\nimport type { ExtensionAttribute } from '../types.js'\nimport { fromString } from '../utilities/fromString.js'\n\n/**\n * This function merges extension attributes into parserule attributes (`attrs` or `getAttrs`).\n * Cancels when `getAttrs` returned `false`.\n * @param parseRule ProseMirror ParseRule\n * @param extensionAttributes List of attributes to inject\n */\nexport function injectExtensionAttributesToParseRule(\n parseRule: ParseRule,\n extensionAttributes: ExtensionAttribute[],\n): ParseRule {\n if ('style' in parseRule) {\n return parseRule\n }\n\n return {\n ...parseRule,\n getAttrs: (node: HTMLElement) => {\n const oldAttributes = parseRule.getAttrs ? parseRule.getAttrs(node) : parseRule.attrs\n\n if (oldAttributes === false) {\n return false\n }\n\n const newAttributes = extensionAttributes.reduce((items, item) => {\n const value = item.attribute.parseHTML\n ? item.attribute.parseHTML(node)\n : fromString(node.getAttribute(item.name))\n\n if (value === null || value === undefined) {\n return items\n }\n\n return {\n ...items,\n [item.name]: value,\n }\n }, {})\n\n return { ...oldAttributes, ...newAttributes }\n },\n }\n}\n","/**\n * Find duplicates in an array.\n */\nexport function findDuplicates<T>(items: T[]): T[] {\n const filtered = items.filter((el, index) => items.indexOf(el) !== index)\n\n return Array.from(new Set(filtered))\n}\n","import type { AnyConfig, Extensions } from '../types.js'\nimport { getExtensionField } from './getExtensionField.js'\n\n/**\n * Sort extensions by priority.\n * @param extensions An array of Tiptap extensions\n * @returns A sorted array of Tiptap extensions by priority\n */\nexport function sortExtensions(extensions: Extensions): Extensions {\n const defaultPriority = 100\n\n return extensions.sort((a, b) => {\n const priorityA = getExtensionField<AnyConfig['priority']>(a, 'priority') || defaultPriority\n const priorityB = getExtensionField<AnyConfig['priority']>(b, 'priority') || defaultPriority\n\n if (priorityA > priorityB) {\n return -1\n }\n\n if (priorityA < priorityB) {\n return 1\n }\n\n return 0\n })\n}\n","import type { Extensions } from '../types.js'\nimport { findDuplicates } from '../utilities/findDuplicates.js'\nimport { flattenExtensions } from './flattenExtensions.js'\nimport { sortExtensions } from './sortExtensions.js'\n\n/**\n * Returns a flattened and sorted extension list while\n * also checking for duplicated extensions and warns the user.\n * @param extensions An array of Tiptap extensions\n * @returns An flattened and sorted array of Tiptap extensions\n */\nexport function resolveExtensions(extensions: Extensions): Extensions {\n const resolvedExtensions = sortExtensions(flattenExtensions(extensions))\n const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))\n\n if (duplicatedNames.length) {\n console.warn(\n `[tiptap warn]: Duplicate extension names found: [${duplicatedNames\n .map(item => `'${item}'`)\n .join(', ')}]. This can lead to issues.`,\n )\n }\n\n return resolvedExtensions\n}\n","import type { Schema } from '@tiptap/pm/model'\n\nimport type { Editor } from '../Editor.js'\nimport type { Extensions } from '../types.js'\nimport { getSchemaByResolvedExtensions } from './getSchemaByResolvedExtensions.js'\nimport { resolveExtensions } from './resolveExtensions.js'\n\nexport function getSchema(extensions: Extensions, editor?: Editor): Schema {\n const resolvedExtensions = resolveExtensions(extensions)\n\n return getSchemaByResolvedExtensions(resolvedExtensions, editor)\n}\n","import { DOMParser } from '@tiptap/pm/model'\n\nimport type { Extensions } from '../types.js'\nimport { elementFromString } from '../utilities/elementFromString.js'\nimport { getSchema } from './getSchema.js'\n\n/**\n * Generate JSONContent from HTML\n * @param html The HTML to generate JSONContent from\n * @param extensions The extensions to use for the schema\n * @returns The generated JSONContent\n */\nexport function generateJSON(html: string, extensions: Extensions): Record<string, any> {\n const schema = getSchema(extensions)\n const dom = elementFromString(html)\n\n return DOMParser.fromSchema(schema).parse(dom).toJSON()\n}\n","import { Node } from '@tiptap/pm/model'\n\nimport type { Extensions, JSONContent, TextSerializer } from '../types.js'\nimport { getSchema } from './getSchema.js'\nimport { getText } from './getText.js'\nimport { getTextSerializersFromSchema } from './getTextSerializersFromSchema.js'\n\n/**\n * Generate raw text from a JSONContent\n * @param doc The JSONContent to generate text from\n * @param extensions The extensions to use for the schema\n * @param options Options for the text generation f.e. blockSeparator or textSerializers\n * @returns The generated text\n */\nexport function generateText(\n doc: JSONContent,\n extensions: Extensions,\n options?: {\n blockSeparator?: string\n textSerializers?: Record<string, TextSerializer>\n },\n): string {\n const { blockSeparator = '\\n\\n', textSerializers = {} } = options || {}\n const schema = getSchema(extensions)\n const contentNode = Node.fromJSON(schema, doc)\n\n return getText(contentNode, {\n blockSeparator,\n textSerializers: {\n ...getTextSerializersFromSchema(schema),\n ...textSerializers,\n },\n })\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { Range, TextSerializer } from '../types.js'\n\n/**\n * Gets the text between two positions in a Prosemirror node\n * and serializes it using the given text serializers and block separator (see getText)\n * @param startNode The Prosemirror node to start from\n * @param range The range of the text to get\n * @param options Options for the text serializer & block separator\n * @returns The text between the two positions\n */\nexport function getTextBetween(\n startNode: ProseMirrorNode,\n range: Range,\n options?: {\n blockSeparator?: string\n textSerializers?: Record<string, TextSerializer>\n },\n): string {\n const { from, to } = range\n const { blockSeparator = '\\n\\n', textSerializers = {} } = options || {}\n let text = ''\n\n startNode.nodesBetween(from, to, (node, pos, parent, index) => {\n if (node.isBlock && pos > from) {\n text += blockSeparator\n }\n\n const textSerializer = textSerializers?.[node.type.name]\n\n if (textSerializer) {\n if (parent) {\n text += textSerializer({\n node,\n pos,\n parent,\n index,\n range,\n })\n }\n // do not descend into child nodes when there exists a serializer\n return false\n }\n\n if (node.isText) {\n text += node?.text?.slice(Math.max(from, pos) - pos, to - pos) // eslint-disable-line\n }\n })\n\n return text\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { TextSerializer } from '../types.js'\nimport { getTextBetween } from './getTextBetween.js'\n\n/**\n * Gets the text of a Prosemirror node\n * @param node The Prosemirror node\n * @param options Options for the text serializer & block separator\n * @returns The text of the node\n * @example ```js\n * const text = getText(node, { blockSeparator: '\\n' })\n * ```\n */\nexport function getText(\n node: ProseMirrorNode,\n options?: {\n blockSeparator?: string\n textSerializers?: Record<string, TextSerializer>\n },\n) {\n const range = {\n from: 0,\n to: node.content.size,\n }\n\n return getTextBetween(node, range, options)\n}\n","import type { Schema } from '@tiptap/pm/model'\n\nimport type { TextSerializer } from '../types.js'\n\n/**\n * Find text serializers `toText` in a Prosemirror schema\n * @param schema The Prosemirror schema to search in\n * @returns A record of text serializers by node name\n */\nexport function getTextSerializersFromSchema(schema: Schema): Record<string, TextSerializer> {\n return Object.fromEntries(\n Object.entries(schema.nodes)\n .filter(([, node]) => node.spec.toText)\n .map(([name, node]) => [name, node.spec.toText]),\n )\n}\n","import type { Node, NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { getNodeType } from './getNodeType.js'\n\nexport function getNodeAttributes(state: EditorState, typeOrName: string | NodeType): Record<string, any> {\n const type = getNodeType(typeOrName, state.schema)\n const { from, to } = state.selection\n const nodes: Node[] = []\n\n state.doc.nodesBetween(from, to, node => {\n nodes.push(node)\n })\n\n const node = nodes.reverse().find(nodeItem => nodeItem.type.name === type.name)\n\n if (!node) {\n return {}\n }\n\n return { ...node.attrs }\n}\n","import type { MarkType, NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { getMarkAttributes } from './getMarkAttributes.js'\nimport { getNodeAttributes } from './getNodeAttributes.js'\nimport { getSchemaTypeNameByName } from './getSchemaTypeNameByName.js'\n\n/**\n * Get node or mark attributes by type or name on the current editor state\n * @param state The current editor state\n * @param typeOrName The node or mark type or name\n * @returns The attributes of the node or mark or an empty object\n */\nexport function getAttributes(state: EditorState, typeOrName: string | NodeType | MarkType): Record<string, any> {\n const schemaType = getSchemaTypeNameByName(\n typeof typeOrName === 'string' ? typeOrName : typeOrName.name,\n state.schema,\n )\n\n if (schemaType === 'node') {\n return getNodeAttributes(state, typeOrName as NodeType)\n }\n\n if (schemaType === 'mark') {\n return getMarkAttributes(state, typeOrName as MarkType)\n }\n\n return {}\n}\n","/**\n * Removes duplicated values within an array.\n * Supports numbers, strings and objects.\n */\nexport function removeDuplicates<T>(array: T[], by = JSON.stringify): T[] {\n const seen: Record<any, any> = {}\n\n return array.filter(item => {\n const key = by(item)\n\n return Object.prototype.hasOwnProperty.call(seen, key) ? false : (seen[key] = true)\n })\n}\n","import type { Step, Transform } from '@tiptap/pm/transform'\n\nimport type { Range } from '../types.js'\nimport { removeDuplicates } from '../utilities/removeDuplicates.js'\n\nexport type ChangedRange = {\n oldRange: Range\n newRange: Range\n}\n\n/**\n * Removes duplicated ranges and ranges that are\n * fully captured by other ranges.\n */\nfunction simplifyChangedRanges(changes: ChangedRange[]): ChangedRange[] {\n const uniqueChanges = removeDuplicates(changes)\n\n return uniqueChanges.length === 1\n ? uniqueChanges\n : uniqueChanges.filter((change, index) => {\n const rest = uniqueChanges.filter((_, i) => i !== index)\n\n return !rest.some(otherChange => {\n return (\n change.oldRange.from >= otherChange.oldRange.from &&\n change.oldRange.to <= otherChange.oldRange.to &&\n change.newRange.from >= otherChange.newRange.from &&\n change.newRange.to <= otherChange.newRange.to\n )\n })\n })\n}\n\n/**\n * Returns a list of changed ranges\n * based on the first and last state of all steps.\n */\nexport function getChangedRanges(transform: Transform): ChangedRange[] {\n const { mapping, steps } = transform\n const changes: ChangedRange[] = []\n\n mapping.maps.forEach((stepMap, index) => {\n const ranges: Range[] = []\n\n // This accounts for step changes where no range was actually altered\n // e.g. when setting a mark, node attribute, etc.\n // @ts-ignore\n if (!stepMap.ranges.length) {\n const { from, to } = steps[index] as Step & {\n from?: number\n to?: number\n }\n\n if (from === undefined || to === undefined) {\n return\n }\n\n ranges.push({ from, to })\n } else {\n stepMap.forEach((from, to) => {\n ranges.push({ from, to })\n })\n }\n\n ranges.forEach(({ from, to }) => {\n const newStart = mapping.slice(index).map(from, -1)\n const newEnd = mapping.slice(index).map(to)\n const oldStart = mapping.invert().map(newStart, -1)\n const oldEnd = mapping.invert().map(newEnd)\n\n changes.push({\n oldRange: {\n from: oldStart,\n to: oldEnd,\n },\n newRange: {\n from: newStart,\n to: newEnd,\n },\n })\n })\n })\n\n return simplifyChangedRanges(changes)\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { JSONContent } from '../types.js'\n\ninterface DebugJSONContent extends JSONContent {\n from: number\n to: number\n}\n\nexport function getDebugJSON(node: ProseMirrorNode, startOffset = 0): DebugJSONContent {\n const isTopNode = node.type === node.type.schema.topNodeType\n const increment = isTopNode ? 0 : 1\n const from = startOffset\n const to = from + node.nodeSize\n const marks = node.marks.map(mark => {\n const output: { type: string; attrs?: Record<string, any> } = {\n type: mark.type.name,\n }\n\n if (Object.keys(mark.attrs).length) {\n output.attrs = { ...mark.attrs }\n }\n\n return output\n })\n const attrs = { ...node.attrs }\n const output: DebugJSONContent = {\n type: node.type.name,\n from,\n to,\n }\n\n if (Object.keys(attrs).length) {\n output.attrs = attrs\n }\n\n if (marks.length) {\n output.marks = marks\n }\n\n if (node.content.childCount) {\n output.content = []\n\n node.forEach((child, offset) => {\n output.content?.push(getDebugJSON(child, startOffset + offset + increment))\n })\n }\n\n if (node.text) {\n output.text = node.text\n }\n\n return output\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { MarkRange } from '../types.js'\nimport { getMarkRange } from './getMarkRange.js'\n\nexport function getMarksBetween(from: number, to: number, doc: ProseMirrorNode): MarkRange[] {\n const marks: MarkRange[] = []\n\n // get all inclusive marks on empty selection\n if (from === to) {\n doc\n .resolve(from)\n .marks()\n .forEach(mark => {\n const $pos = doc.resolve(from)\n const range = getMarkRange($pos, mark.type)\n\n if (!range) {\n return\n }\n\n marks.push({\n mark,\n ...range,\n })\n })\n } else {\n doc.nodesBetween(from, to, (node, pos) => {\n if (!node || node?.nodeSize === undefined) {\n return\n }\n\n marks.push(\n ...node.marks.map(mark => ({\n from: pos,\n to: pos + node.nodeSize,\n mark,\n })),\n )\n })\n }\n\n return marks\n}\n","import type { Node, NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\n/**\n * Finds the first node of a given type or name in the current selection.\n * @param state The editor state.\n * @param typeOrName The node type or name.\n * @param pos The position to start searching from.\n * @param maxDepth The maximum depth to search.\n * @returns The node and the depth as an array.\n */\nexport const getNodeAtPosition = (state: EditorState, typeOrName: string | NodeType, pos: number, maxDepth = 20) => {\n const $pos = state.doc.resolve(pos)\n\n let currentDepth = maxDepth\n let node: Node | null = null\n\n while (currentDepth > 0 && node === null) {\n const currentNode = $pos.node(currentDepth)\n\n if (currentNode?.type.name === typeOrName) {\n node = currentNode\n } else {\n currentDepth -= 1\n }\n }\n\n return [node, currentDepth] as [Node | null, number]\n}\n","import type { MarkType, NodeType, Schema } from '@tiptap/pm/model'\n\n/**\n * Tries to get a node or mark type by its name.\n * @param name The name of the node or mark type\n * @param schema The Prosemiror schema to search in\n * @returns The node or mark type, or null if it doesn't exist\n */\nexport function getSchemaTypeByName(name: string, schema: Schema): NodeType | MarkType | null {\n return schema.nodes[name] || schema.marks[name] || null\n}\n","import type { ExtensionAttribute } from '../types.js'\n\n/**\n * Return attributes of an extension that should be splitted by keepOnSplit flag\n * @param extensionAttributes Array of extension attributes\n * @param typeName The type of the extension\n * @param attributes The attributes of the extension\n * @returns The splitted attributes\n */\nexport function getSplittedAttributes(\n extensionAttributes: ExtensionAttribute[],\n typeName: string,\n attributes: Record<string, any>,\n): Record<string, any> {\n return Object.fromEntries(\n Object.entries(attributes).filter(([name]) => {\n const extensionAttribute = extensionAttributes.find(item => {\n return item.type === typeName && item.name === name\n })\n\n if (!extensionAttribute) {\n return false\n }\n\n return extensionAttribute.attribute.keepOnSplit\n }),\n )\n}\n","import type { ResolvedPos } from '@tiptap/pm/model'\n\n/**\n * Returns the text content of a resolved prosemirror position\n * @param $from The resolved position to get the text content from\n * @param maxMatch The maximum number of characters to match\n * @returns The text content\n */\nexport const getTextContentFromNodes = ($from: ResolvedPos, maxMatch = 500) => {\n let textBefore = ''\n\n const sliceEndPos = $from.parentOffset\n\n $from.parent.nodesBetween(Math.max(0, sliceEndPos - maxMatch), sliceEndPos, (node, pos, parent, index) => {\n const chunk =\n node.type.spec.toText?.({\n node,\n pos,\n parent,\n index,\n }) ||\n node.textContent ||\n '%leaf%'\n\n textBefore += node.isAtom && !node.isText ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos))\n })\n\n return textBefore\n}\n","import type { MarkType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport type { MarkRange } from '../types.js'\nimport { objectIncludes } from '../utilities/objectIncludes.js'\nimport { getMarkType } from './getMarkType.js'\n\nexport function isMarkActive(\n state: EditorState,\n typeOrName: MarkType | string | null,\n attributes: Record<string, any> = {},\n): boolean {\n const { empty, ranges } = state.selection\n const type = typeOrName ? getMarkType(typeOrName, state.schema) : null\n\n if (empty) {\n return !!(state.storedMarks || state.selection.$from.marks())\n .filter(mark => {\n if (!type) {\n return true\n }\n\n return type.name === mark.type.name\n })\n .find(mark => objectIncludes(mark.attrs, attributes, { strict: false }))\n }\n\n let selectionRange = 0\n const markRanges: MarkRange[] = []\n\n ranges.forEach(({ $from, $to }) => {\n const from = $from.pos\n const to = $to.pos\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n if (!node.isText && !node.marks.length) {\n return\n }\n\n const relativeFrom = Math.max(from, pos)\n const relativeTo = Math.min(to, pos + node.nodeSize)\n const range = relativeTo - relativeFrom\n\n selectionRange += range\n\n markRanges.push(\n ...node.marks.map(mark => ({\n mark,\n from: relativeFrom,\n to: relativeTo,\n })),\n )\n })\n })\n\n if (selectionRange === 0) {\n return false\n }\n\n // calculate range of matched mark\n const matchedRange = markRanges\n .filter(markRange => {\n if (!type) {\n return true\n }\n\n return type.name === markRange.mark.type.name\n })\n .filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))\n .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0)\n\n // calculate range of marks that excludes the searched mark\n // for example `code` doesn’t allow any other marks\n const excludedRange = markRanges\n .filter(markRange => {\n if (!type) {\n return true\n }\n\n return markRange.mark.type !== type && markRange.mark.type.excludes(type)\n })\n .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0)\n\n // we only include the result of `excludedRange`\n // if there is a match at all\n const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange\n\n return range >= selectionRange\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { getSchemaTypeNameByName } from './getSchemaTypeNameByName.js'\nimport { isMarkActive } from './isMarkActive.js'\nimport { isNodeActive } from './isNodeActive.js'\n\nexport function isActive(state: EditorState, name: string | null, attributes: Record<string, any> = {}): boolean {\n if (!name) {\n return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes)\n }\n\n const schemaType = getSchemaTypeNameByName(name, state.schema)\n\n if (schemaType === 'node') {\n return isNodeActive(state, name, attributes)\n }\n\n if (schemaType === 'mark') {\n return isMarkActive(state, name, attributes)\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findParentNode } from './findParentNode.js'\n\nexport const isAtEndOfNode = (state: EditorState, nodeType?: string) => {\n const { $from, $to, $anchor } = state.selection\n\n if (nodeType) {\n const parentNode = findParentNode(node => node.type.name === nodeType)(state.selection)\n\n if (!parentNode) {\n return false\n }\n\n const $parentPos = state.doc.resolve(parentNode.pos + 1)\n\n if ($anchor.pos + 1 === $parentPos.end()) {\n return true\n }\n\n return false\n }\n\n if ($to.parentOffset < $to.parent.nodeSize - 2 || $from.pos !== $to.pos) {\n return false\n }\n\n return true\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const isAtStartOfNode = (state: EditorState) => {\n const { $from, $to } = state.selection\n\n if ($from.parentOffset > 0 || $from.pos !== $to.pos) {\n return false\n }\n\n return true\n}\n","import type { AnyExtension, EnableRules } from '../types.js'\n\nexport function isExtensionRulesEnabled(extension: AnyExtension, enabled: EnableRules): boolean {\n if (Array.isArray(enabled)) {\n return enabled.some(enabledExtension => {\n const name = typeof enabledExtension === 'string' ? enabledExtension : enabledExtension.name\n\n return name === extension.name\n })\n }\n\n return enabled\n}\n","import { getExtensionField } from '../helpers/getExtensionField.js'\nimport type { NodeConfig } from '../index.js'\nimport type { Extensions } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\nimport { splitExtensions } from './splitExtensions.js'\n\nexport function isList(name: string, extensions: Extensions): boolean {\n const { nodeExtensions } = splitExtensions(extensions)\n const extension = nodeExtensions.find(item => item.name === name)\n\n if (!extension) {\n return false\n }\n\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n const group = callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context))\n\n if (typeof group !== 'string') {\n return false\n }\n\n return group.split(' ').includes('list')\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\n/**\n * Returns true if the given prosemirror node is empty.\n */\nexport function isNodeEmpty(\n node: ProseMirrorNode,\n {\n checkChildren = true,\n ignoreWhitespace = false,\n }: {\n /**\n * When true (default), it will also check if all children are empty.\n */\n checkChildren?: boolean\n /**\n * When true, it will ignore whitespace when checking for emptiness.\n */\n ignoreWhitespace?: boolean\n } = {},\n): boolean {\n if (ignoreWhitespace) {\n if (node.type.name === 'hardBreak') {\n // Hard breaks are considered empty\n return true\n }\n if (node.isText) {\n return /^\\s*$/m.test(node.text ?? '')\n }\n }\n\n if (node.isText) {\n return !node.text\n }\n\n if (node.isAtom || node.isLeaf) {\n return false\n }\n\n if (node.content.childCount === 0) {\n return true\n }\n\n if (checkChildren) {\n let isContentEmpty = true\n\n node.content.forEach(childNode => {\n if (isContentEmpty === false) {\n // Exit early for perf\n return\n }\n\n if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {\n isContentEmpty = false\n }\n })\n\n return isContentEmpty\n }\n\n return false\n}\n","import { NodeSelection } from '@tiptap/pm/state'\n\nexport function isNodeSelection(value: unknown): value is NodeSelection {\n return value instanceof NodeSelection\n}\n","import type { Transaction } from '@tiptap/pm/state'\nimport type { MapResult } from '@tiptap/pm/transform'\n\n/**\n * A class that represents a mappable position in the editor. It can be extended\n * by other extensions to add additional position mapping capabilities.\n */\nexport class MappablePosition {\n /**\n * The absolute position in the editor.\n */\n public position: number\n\n constructor(position: number) {\n this.position = position\n }\n\n /**\n * Creates a MappablePosition from a JSON object.\n */\n static fromJSON(json: any): MappablePosition {\n return new MappablePosition(json.position)\n }\n\n /**\n * Converts the MappablePosition to a JSON object.\n */\n toJSON(): any {\n return {\n position: this.position,\n }\n }\n}\n\n/**\n * The result of the getUpdatedPosition function.\n */\nexport interface GetUpdatedPositionResult {\n position: MappablePosition\n mapResult: MapResult | null\n}\n\n/**\n * Calculates the new position after applying a transaction.\n *\n * @returns The new mappable position and the map result.\n */\nexport function getUpdatedPosition(position: MappablePosition, transaction: Transaction): GetUpdatedPositionResult {\n const mapResult = transaction.mapping.mapResult(position.position)\n return {\n position: new MappablePosition(mapResult.pos),\n mapResult,\n }\n}\n\n/**\n * Creates a MappablePosition from a position number. This is the default\n * implementation for Tiptap core. It can be overridden by other Tiptap\n * extensions.\n *\n * @param position The position (as a number) where the MappablePosition will be created.\n * @returns A new MappablePosition instance at the given position.\n */\nexport function createMappablePosition(position: number): MappablePosition {\n return new MappablePosition(position)\n}\n","import type { EditorView } from '@tiptap/pm/view'\n\nimport { minMax } from '../utilities/minMax.js'\n\nexport function posToDOMRect(view: EditorView, from: number, to: number): DOMRect {\n const minPos = 0\n const maxPos = view.state.doc.content.size\n const resolvedFrom = minMax(from, minPos, maxPos)\n const resolvedEnd = minMax(to, minPos, maxPos)\n const start = view.coordsAtPos(resolvedFrom)\n const end = view.coordsAtPos(resolvedEnd, -1)\n const top = Math.min(start.top, end.top)\n const bottom = Math.max(start.bottom, end.bottom)\n const left = Math.min(start.left, end.left)\n const right = Math.max(start.right, end.right)\n const width = right - left\n const height = bottom - top\n const x = left\n const y = top\n const data = {\n top,\n bottom,\n left,\n right,\n width,\n height,\n x,\n y,\n }\n\n return {\n ...data,\n toJSON: () => data,\n }\n}\n","import type { Schema } from '@tiptap/pm/model'\n\nimport type { JSONContent } from '../types.js'\n\ntype RewriteUnknownContentOptions = {\n /**\n * If true, unknown nodes will be treated as paragraphs\n * @default true\n */\n fallbackToParagraph?: boolean\n}\n\ntype RewrittenContent = {\n /**\n * The original JSON content that was rewritten\n */\n original: JSONContent\n /**\n * The name of the node or mark that was unsupported\n */\n unsupported: string\n}[]\n\n/**\n * The actual implementation of the rewriteUnknownContent function\n */\nfunction rewriteUnknownContentInner({\n json,\n validMarks,\n validNodes,\n options,\n rewrittenContent = [],\n}: {\n json: JSONContent\n validMarks: Set<string>\n validNodes: Set<string>\n options?: RewriteUnknownContentOptions\n rewrittenContent?: RewrittenContent\n}): {\n /**\n * The cleaned JSON content\n */\n json: JSONContent | null\n /**\n * The array of nodes and marks that were rewritten\n */\n rewrittenContent: RewrittenContent\n} {\n if (json.marks && Array.isArray(json.marks)) {\n json.marks = json.marks.filter(mark => {\n const name = typeof mark === 'string' ? mark : mark.type\n\n if (validMarks.has(name)) {\n return true\n }\n\n rewrittenContent.push({\n original: JSON.parse(JSON.stringify(mark)),\n unsupported: name,\n })\n // Just ignore any unknown marks\n return false\n })\n }\n\n if (json.content && Array.isArray(json.content)) {\n json.content = json.content\n .map(\n value =>\n rewriteUnknownContentInner({\n json: value,\n validMarks,\n validNodes,\n options,\n rewrittenContent,\n }).json,\n )\n .filter(a => a !== null && a !== undefined)\n }\n\n if (json.type && !validNodes.has(json.type)) {\n rewrittenContent.push({\n original: JSON.parse(JSON.stringify(json)),\n unsupported: json.type,\n })\n\n if (json.content && Array.isArray(json.content) && options?.fallbackToParagraph !== false) {\n // Just treat it like a paragraph and hope for the best\n json.type = 'paragraph'\n\n return {\n json,\n rewrittenContent,\n }\n }\n\n // or just omit it entirely\n return {\n json: null,\n rewrittenContent,\n }\n }\n\n return { json, rewrittenContent }\n}\n\n/**\n * Rewrite unknown nodes and marks within JSON content\n * Allowing for user within the editor\n */\nexport function rewriteUnknownContent(\n /**\n * The JSON content to clean of unknown nodes and marks\n */\n json: JSONContent,\n /**\n * The schema to use for validation\n */\n schema: Schema,\n /**\n * Options for the cleaning process\n */\n options?: RewriteUnknownContentOptions,\n): {\n /**\n * The cleaned JSON content\n */\n json: JSONContent | null\n /**\n * The array of nodes and marks that were rewritten\n */\n rewrittenContent: {\n /**\n * The original JSON content that was rewritten\n */\n original: JSONContent\n /**\n * The name of the node or mark that was unsupported\n */\n unsupported: string\n }[]\n} {\n return rewriteUnknownContentInner({\n json,\n validNodes: new Set(Object.keys(schema.nodes)),\n validMarks: new Set(Object.keys(schema.marks)),\n options,\n })\n}\n","import type { MarkType, ResolvedPos } from '@tiptap/pm/model'\nimport type { EditorState, Transaction } from '@tiptap/pm/state'\n\nimport { getMarkAttributes } from '../helpers/getMarkAttributes.js'\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { isTextSelection } from '../helpers/index.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setMark: {\n /**\n * Add a mark with new attributes.\n * @param typeOrName The mark type or name.\n * @example editor.commands.setMark('bold', { level: 1 })\n */\n setMark: (typeOrName: string | MarkType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nfunction canSetMark(state: EditorState, tr: Transaction, newMarkType: MarkType) {\n const { selection } = tr\n let cursor: ResolvedPos | null = null\n\n if (isTextSelection(selection)) {\n cursor = selection.$cursor\n }\n\n if (cursor) {\n const currentMarks = state.storedMarks ?? cursor.marks()\n const parentAllowsMarkType = cursor.parent.type.allowsMarkType(newMarkType)\n\n // There can be no current marks that exclude the new mark, and the parent must allow this mark type\n return (\n parentAllowsMarkType &&\n (!!newMarkType.isInSet(currentMarks) || !currentMarks.some(mark => mark.type.excludes(newMarkType)))\n )\n }\n\n const { ranges } = selection\n\n return ranges.some(({ $from, $to }) => {\n let someNodeSupportsMark =\n $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false\n\n state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {\n // If we already found a mark that we can enable, return false to bypass the remaining search\n if (someNodeSupportsMark) {\n return false\n }\n\n if (node.isInline) {\n const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType)\n const currentMarksAllowMarkType =\n !!newMarkType.isInSet(node.marks) || !node.marks.some(otherMark => otherMark.type.excludes(newMarkType))\n\n someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType\n }\n return !someNodeSupportsMark\n })\n\n return someNodeSupportsMark\n })\n}\nexport const setMark: RawCommands['setMark'] =\n (typeOrName, attributes = {}) =>\n ({ tr, state, dispatch }) => {\n const { selection } = tr\n const { empty, ranges } = selection\n const type = getMarkType(typeOrName, state.schema)\n\n if (dispatch) {\n if (empty) {\n const oldAttributes = getMarkAttributes(state, type)\n\n tr.addStoredMark(\n type.create({\n ...oldAttributes,\n ...attributes,\n }),\n )\n } else {\n ranges.forEach(range => {\n const from = range.$from.pos\n const to = range.$to.pos\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n const trimmedFrom = Math.max(pos, from)\n const trimmedTo = Math.min(pos + node.nodeSize, to)\n const someHasMark = node.marks.find(mark => mark.type === type)\n\n // if there is already a mark of this type\n // we know that we have to merge its attributes\n // otherwise we add a fresh new mark\n if (someHasMark) {\n node.marks.forEach(mark => {\n if (type === mark.type) {\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n type.create({\n ...mark.attrs,\n ...attributes,\n }),\n )\n }\n })\n } else {\n tr.addMark(trimmedFrom, trimmedTo, type.create(attributes))\n }\n })\n })\n }\n }\n\n return canSetMark(state, tr, type)\n }\n","import type { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setMeta: {\n /**\n * Store a metadata property in the current transaction.\n * @param key The key of the metadata property.\n * @param value The value to store.\n * @example editor.commands.setMeta('foo', 'bar')\n */\n setMeta: (key: string | Plugin | PluginKey, value: any) => ReturnType\n }\n }\n}\n\nexport const setMeta: RawCommands['setMeta'] =\n (key, value) =>\n ({ tr }) => {\n tr.setMeta(key, value)\n\n return true\n }\n","import { setBlockType } from '@tiptap/pm/commands'\nimport type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setNode: {\n /**\n * Replace a given range with a node.\n * @param typeOrName The type or name of the node\n * @param attributes The attributes of the node\n * @example editor.commands.setNode('paragraph')\n */\n setNode: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const setNode: RawCommands['setNode'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch, chain }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n let attributesToCopy: Record<string, any> | undefined\n\n if (state.selection.$anchor.sameParent(state.selection.$head)) {\n // only copy attributes if the selection is pointing to a node of the same type\n attributesToCopy = state.selection.$anchor.parent.attrs\n }\n\n // TODO: use a fallback like insertContent?\n if (!type.isTextblock) {\n console.warn('[tiptap warn]: Currently \"setNode()\" only supports text block nodes.')\n\n return false\n }\n\n return (\n chain()\n // try to convert node to default node if needed\n .command(({ commands }) => {\n const canSetBlock = setBlockType(type, { ...attributesToCopy, ...attributes })(state)\n\n if (canSetBlock) {\n return true\n }\n\n return commands.clearNodes()\n })\n .command(({ state: updatedState }) => {\n return setBlockType(type, { ...attributesToCopy, ...attributes })(updatedState, dispatch)\n })\n .run()\n )\n }\n","import { NodeSelection } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\nimport { minMax } from '../utilities/minMax.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setNodeSelection: {\n /**\n * Creates a NodeSelection.\n * @param position - Position of the node.\n * @example editor.commands.setNodeSelection(10)\n */\n setNodeSelection: (position: number) => ReturnType\n }\n }\n}\n\nexport const setNodeSelection: RawCommands['setNodeSelection'] =\n position =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n const { doc } = tr\n const from = minMax(position, 0, doc.content.size)\n const selection = NodeSelection.create(doc, from)\n\n tr.setSelection(selection)\n }\n\n return true\n }\n","import type { Range, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setTextDirection: {\n /**\n * Set the text direction for nodes.\n * If no position is provided, it will use the current selection.\n * @param direction The text direction to set ('ltr', 'rtl', or 'auto')\n * @param position Optional position or range to apply the direction to\n * @example editor.commands.setTextDirection('rtl')\n * @example editor.commands.setTextDirection('ltr', { from: 0, to: 10 })\n */\n setTextDirection: (direction: 'ltr' | 'rtl' | 'auto', position?: number | Range) => ReturnType\n }\n }\n}\n\nexport const setTextDirection: RawCommands['setTextDirection'] =\n (direction, position) =>\n ({ tr, state, dispatch }) => {\n const { selection } = state\n let from: number\n let to: number\n\n if (typeof position === 'number') {\n from = position\n to = position\n } else if (position && 'from' in position && 'to' in position) {\n from = position.from\n to = position.to\n } else {\n from = selection.from\n to = selection.to\n }\n\n if (dispatch) {\n tr.doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n return\n }\n\n tr.setNodeMarkup(pos, undefined, {\n ...node.attrs,\n dir: direction,\n })\n })\n }\n\n return true\n }\n","import { TextSelection } from '@tiptap/pm/state'\n\nimport type { Range, RawCommands } from '../types.js'\nimport { minMax } from '../utilities/minMax.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setTextSelection: {\n /**\n * Creates a TextSelection.\n * @param position The position of the selection.\n * @example editor.commands.setTextSelection(10)\n */\n setTextSelection: (position: number | Range) => ReturnType\n }\n }\n}\n\nexport const setTextSelection: RawCommands['setTextSelection'] =\n position =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n const { doc } = tr\n const { from, to } = typeof position === 'number' ? { from: position, to: position } : position\n const minPos = TextSelection.atStart(doc).from\n const maxPos = TextSelection.atEnd(doc).to\n const resolvedFrom = minMax(from, minPos, maxPos)\n const resolvedEnd = minMax(to, minPos, maxPos)\n const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd)\n\n tr.setSelection(selection)\n }\n\n return true\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport { sinkListItem as originalSinkListItem } from '@tiptap/pm/schema-list'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n sinkListItem: {\n /**\n * Sink the list item down into an inner list.\n * @param typeOrName The type or name of the node.\n * @example editor.commands.sinkListItem('listItem')\n */\n sinkListItem: (typeOrName: string | NodeType) => ReturnType\n }\n }\n}\n\nexport const sinkListItem: RawCommands['sinkListItem'] =\n typeOrName =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalSinkListItem(type)(state, dispatch)\n }\n","import type { EditorState } from '@tiptap/pm/state'\nimport { NodeSelection, TextSelection } from '@tiptap/pm/state'\nimport { canSplit } from '@tiptap/pm/transform'\n\nimport { defaultBlockAt } from '../helpers/defaultBlockAt.js'\nimport { getSplittedAttributes } from '../helpers/getSplittedAttributes.js'\nimport type { RawCommands } from '../types.js'\n\nfunction ensureMarks(state: EditorState, splittableMarks?: string[]) {\n const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks())\n\n if (marks) {\n const filteredMarks = marks.filter(mark => splittableMarks?.includes(mark.type.name))\n\n state.tr.ensureMarks(filteredMarks)\n }\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n splitBlock: {\n /**\n * Forks a new node from an existing node.\n * @param options.keepMarks Keep marks from the previous node.\n * @example editor.commands.splitBlock()\n * @example editor.commands.splitBlock({ keepMarks: true })\n */\n splitBlock: (options?: { keepMarks?: boolean }) => ReturnType\n }\n }\n}\n\nexport const splitBlock: RawCommands['splitBlock'] =\n ({ keepMarks = true } = {}) =>\n ({ tr, state, dispatch, editor }) => {\n const { selection, doc } = tr\n const { $from, $to } = selection\n const extensionAttributes = editor.extensionManager.attributes\n const newAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs)\n\n if (selection instanceof NodeSelection && selection.node.isBlock) {\n if (!$from.parentOffset || !canSplit(doc, $from.pos)) {\n return false\n }\n\n if (dispatch) {\n if (keepMarks) {\n ensureMarks(state, editor.extensionManager.splittableMarks)\n }\n\n tr.split($from.pos).scrollIntoView()\n }\n\n return true\n }\n\n if (!$from.parent.isBlock) {\n return false\n }\n\n const atEnd = $to.parentOffset === $to.parent.content.size\n\n const deflt = $from.depth === 0 ? undefined : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)))\n\n let types =\n atEnd && deflt\n ? [\n {\n type: deflt,\n attrs: newAttributes,\n },\n ]\n : undefined\n\n let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types)\n\n if (!types && !can && canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)) {\n can = true\n types = deflt\n ? [\n {\n type: deflt,\n attrs: newAttributes,\n },\n ]\n : undefined\n }\n\n if (dispatch) {\n if (can) {\n if (selection instanceof TextSelection) {\n tr.deleteSelection()\n }\n\n tr.split(tr.mapping.map($from.pos), 1, types)\n\n if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {\n const first = tr.mapping.map($from.before())\n const $first = tr.doc.resolve(first)\n\n if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {\n tr.setNodeMarkup(tr.mapping.map($from.before()), deflt)\n }\n }\n }\n\n if (keepMarks) {\n ensureMarks(state, editor.extensionManager.splittableMarks)\n }\n\n tr.scrollIntoView()\n }\n\n return can\n }\n","import type { Node as ProseMirrorNode, NodeType } from '@tiptap/pm/model'\nimport { Fragment, Slice } from '@tiptap/pm/model'\nimport { TextSelection } from '@tiptap/pm/state'\nimport { canSplit } from '@tiptap/pm/transform'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { getSplittedAttributes } from '../helpers/getSplittedAttributes.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n splitListItem: {\n /**\n * Splits one list item into two list items.\n * @param typeOrName The type or name of the node.\n * @param overrideAttrs The attributes to ensure on the new node.\n * @example editor.commands.splitListItem('listItem')\n */\n splitListItem: (typeOrName: string | NodeType, overrideAttrs?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const splitListItem: RawCommands['splitListItem'] =\n (typeOrName, overrideAttrs = {}) =>\n ({ tr, state, dispatch, editor }) => {\n const type = getNodeType(typeOrName, state.schema)\n const { $from, $to } = state.selection\n\n // @ts-ignore\n // eslint-disable-next-line\n const node: ProseMirrorNode = state.selection.node\n\n if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) {\n return false\n }\n\n const grandParent = $from.node(-1)\n\n if (grandParent.type !== type) {\n return false\n }\n\n const extensionAttributes = editor.extensionManager.attributes\n\n if ($from.parent.content.size === 0 && $from.node(-1).childCount === $from.indexAfter(-1)) {\n // In an empty block. If this is a nested list, the wrapping\n // list item should be split. Otherwise, bail out and let next\n // command handle lifting.\n if ($from.depth === 2 || $from.node(-3).type !== type || $from.index(-2) !== $from.node(-2).childCount - 1) {\n return false\n }\n\n if (dispatch) {\n let wrap = Fragment.empty\n // eslint-disable-next-line\n const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3\n\n // Build a fragment containing empty versions of the structure\n // from the outer list item to the parent node of the cursor\n for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {\n wrap = Fragment.from($from.node(d).copy(wrap))\n }\n\n const depthAfter =\n // eslint-disable-next-line no-nested-ternary\n $from.indexAfter(-1) < $from.node(-2).childCount\n ? 1\n : $from.indexAfter(-2) < $from.node(-3).childCount\n ? 2\n : 3\n\n // Add a second list item with an empty default start node\n const newNextTypeAttributes = {\n ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),\n ...overrideAttrs,\n }\n const nextType = type.contentMatch.defaultType?.createAndFill(newNextTypeAttributes) || undefined\n\n wrap = wrap.append(Fragment.from(type.createAndFill(null, nextType) || undefined))\n\n const start = $from.before($from.depth - (depthBefore - 1))\n\n tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0))\n\n let sel = -1\n\n tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {\n if (sel > -1) {\n return false\n }\n\n if (n.isTextblock && n.content.size === 0) {\n sel = pos + 1\n }\n })\n\n if (sel > -1) {\n tr.setSelection(TextSelection.near(tr.doc.resolve(sel)))\n }\n\n tr.scrollIntoView()\n }\n\n return true\n }\n\n const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null\n\n const newTypeAttributes = {\n ...getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs),\n ...overrideAttrs,\n }\n const newNextTypeAttributes = {\n ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),\n ...overrideAttrs,\n }\n\n tr.delete($from.pos, $to.pos)\n\n const types = nextType\n ? [\n { type, attrs: newTypeAttributes },\n { type: nextType, attrs: newNextTypeAttributes },\n ]\n : [{ type, attrs: newTypeAttributes }]\n\n if (!canSplit(tr.doc, $from.pos, 2)) {\n return false\n }\n\n if (dispatch) {\n const { selection, storedMarks } = state\n const { splittableMarks } = editor.extensionManager\n const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks())\n\n tr.split($from.pos, 2, types).scrollIntoView()\n\n if (!marks || !dispatch) {\n return true\n }\n\n const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name))\n\n tr.ensureMarks(filteredMarks)\n }\n\n return true\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport type { Transaction } from '@tiptap/pm/state'\nimport { canJoin } from '@tiptap/pm/transform'\n\nimport { findParentNode } from '../helpers/findParentNode.js'\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isList } from '../helpers/isList.js'\nimport type { RawCommands } from '../types.js'\n\nconst joinListBackwards = (tr: Transaction, listType: NodeType): boolean => {\n const list = findParentNode(node => node.type === listType)(tr.selection)\n\n if (!list) {\n return true\n }\n\n const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth)\n\n if (before === undefined) {\n return true\n }\n\n const nodeBefore = tr.doc.nodeAt(before)\n const canJoinBackwards = list.node.type === nodeBefore?.type && canJoin(tr.doc, list.pos)\n\n if (!canJoinBackwards) {\n return true\n }\n\n tr.join(list.pos)\n\n return true\n}\n\nconst joinListForwards = (tr: Transaction, listType: NodeType): boolean => {\n const list = findParentNode(node => node.type === listType)(tr.selection)\n\n if (!list) {\n return true\n }\n\n const after = tr.doc.resolve(list.start).after(list.depth)\n\n if (after === undefined) {\n return true\n }\n\n const nodeAfter = tr.doc.nodeAt(after)\n const canJoinForwards = list.node.type === nodeAfter?.type && canJoin(tr.doc, after)\n\n if (!canJoinForwards) {\n return true\n }\n\n tr.join(after)\n\n return true\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleList: {\n /**\n * Toggle between different list types.\n * @param listTypeOrName The type or name of the list.\n * @param itemTypeOrName The type or name of the list item.\n * @param keepMarks Keep marks when toggling.\n * @param attributes Attributes for the new list.\n * @example editor.commands.toggleList('bulletList', 'listItem')\n */\n toggleList: (\n listTypeOrName: string | NodeType,\n itemTypeOrName: string | NodeType,\n keepMarks?: boolean,\n attributes?: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const toggleList: RawCommands['toggleList'] =\n (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) =>\n ({ editor, tr, state, dispatch, chain, commands, can }) => {\n const { extensions, splittableMarks } = editor.extensionManager\n const listType = getNodeType(listTypeOrName, state.schema)\n const itemType = getNodeType(itemTypeOrName, state.schema)\n const { selection, storedMarks } = state\n const { $from, $to } = selection\n const range = $from.blockRange($to)\n\n const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks())\n\n if (!range) {\n return false\n }\n\n const parentList = findParentNode(node => isList(node.type.name, extensions))(selection)\n\n if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {\n // remove list\n if (parentList.node.type === listType) {\n return commands.liftListItem(itemType)\n }\n\n // change list type\n if (isList(parentList.node.type.name, extensions) && listType.validContent(parentList.node.content) && dispatch) {\n return chain()\n .command(() => {\n tr.setNodeMarkup(parentList.pos, listType)\n\n return true\n })\n .command(() => joinListBackwards(tr, listType))\n .command(() => joinListForwards(tr, listType))\n .run()\n }\n }\n if (!keepMarks || !marks || !dispatch) {\n return (\n chain()\n // try to convert node to default node if needed\n .command(() => {\n const canWrapInList = can().wrapInList(listType, attributes)\n\n if (canWrapInList) {\n return true\n }\n\n return commands.clearNodes()\n })\n .wrapInList(listType, attributes)\n .command(() => joinListBackwards(tr, listType))\n .command(() => joinListForwards(tr, listType))\n .run()\n )\n }\n\n return (\n chain()\n // try to convert node to default node if needed\n .command(() => {\n const canWrapInList = can().wrapInList(listType, attributes)\n\n const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name))\n\n tr.ensureMarks(filteredMarks)\n\n if (canWrapInList) {\n return true\n }\n\n return commands.clearNodes()\n })\n .wrapInList(listType, attributes)\n .command(() => joinListBackwards(tr, listType))\n .command(() => joinListForwards(tr, listType))\n .run()\n )\n }\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { isMarkActive } from '../helpers/isMarkActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleMark: {\n /**\n * Toggle a mark on and off.\n * @param typeOrName The mark type or name.\n * @param attributes The attributes of the mark.\n * @param options.extendEmptyMarkRange Removes the mark even across the current selection. Defaults to `false`.\n * @example editor.commands.toggleMark('bold')\n */\n toggleMark: (\n /**\n * The mark type or name.\n */\n typeOrName: string | MarkType,\n\n /**\n * The attributes of the mark.\n */\n attributes?: Record<string, any>,\n\n options?: {\n /**\n * Removes the mark even across the current selection. Defaults to `false`.\n */\n extendEmptyMarkRange?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\nexport const toggleMark: RawCommands['toggleMark'] =\n (typeOrName, attributes = {}, options = {}) =>\n ({ state, commands }) => {\n const { extendEmptyMarkRange = false } = options\n const type = getMarkType(typeOrName, state.schema)\n const isActive = isMarkActive(state, type, attributes)\n\n if (isActive) {\n return commands.unsetMark(type, { extendEmptyMarkRange })\n }\n\n return commands.setMark(type, attributes)\n }\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isNodeActive } from '../helpers/isNodeActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleNode: {\n /**\n * Toggle a node with another node.\n * @param typeOrName The type or name of the node.\n * @param toggleTypeOrName The type or name of the node to toggle.\n * @param attributes The attributes of the node.\n * @example editor.commands.toggleNode('heading', 'paragraph')\n */\n toggleNode: (\n typeOrName: string | NodeType,\n toggleTypeOrName: string | NodeType,\n attributes?: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const toggleNode: RawCommands['toggleNode'] =\n (typeOrName, toggleTypeOrName, attributes = {}) =>\n ({ state, commands }) => {\n const type = getNodeType(typeOrName, state.schema)\n const toggleType = getNodeType(toggleTypeOrName, state.schema)\n const isActive = isNodeActive(state, type, attributes)\n\n let attributesToCopy: Record<string, any> | undefined\n\n if (state.selection.$anchor.sameParent(state.selection.$head)) {\n // only copy attributes if the selection is pointing to a node of the same type\n attributesToCopy = state.selection.$anchor.parent.attrs\n }\n\n if (isActive) {\n return commands.setNode(toggleType, attributesToCopy)\n }\n\n // If the node is not active, we want to set the new node type with the given attributes\n // Copying over the attributes from the current node if the selection is pointing to a node of the same type\n return commands.setNode(type, { ...attributesToCopy, ...attributes })\n }\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isNodeActive } from '../helpers/isNodeActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleWrap: {\n /**\n * Wraps nodes in another node, or removes an existing wrap.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.toggleWrap('blockquote')\n */\n toggleWrap: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const toggleWrap: RawCommands['toggleWrap'] =\n (typeOrName, attributes = {}) =>\n ({ state, commands }) => {\n const type = getNodeType(typeOrName, state.schema)\n const isActive = isNodeActive(state, type, attributes)\n\n if (isActive) {\n return commands.lift(type)\n }\n\n return commands.wrapIn(type, attributes)\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoInputRule: {\n /**\n * Undo an input rule.\n * @example editor.commands.undoInputRule()\n */\n undoInputRule: () => ReturnType\n }\n }\n}\n\nexport const undoInputRule: RawCommands['undoInputRule'] =\n () =>\n ({ state, dispatch }) => {\n const plugins = state.plugins\n\n for (let i = 0; i < plugins.length; i += 1) {\n const plugin = plugins[i]\n let undoable\n\n // @ts-ignore\n // eslint-disable-next-line\n if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {\n if (dispatch) {\n const tr = state.tr\n const toUndo = undoable.transform\n\n for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {\n tr.step(toUndo.steps[j].invert(toUndo.docs[j]))\n }\n\n if (undoable.text) {\n const marks = tr.doc.resolve(undoable.from).marks()\n\n tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks))\n } else {\n tr.delete(undoable.from, undoable.to)\n }\n }\n\n return true\n }\n }\n\n return false\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n unsetAllMarks: {\n /**\n * Remove all marks in the current selection.\n * @example editor.commands.unsetAllMarks()\n */\n unsetAllMarks: () => ReturnType\n }\n }\n}\n\nexport const unsetAllMarks: RawCommands['unsetAllMarks'] =\n () =>\n ({ tr, dispatch }) => {\n const { selection } = tr\n const { empty, ranges } = selection\n\n if (empty) {\n return true\n }\n\n if (dispatch) {\n ranges.forEach(range => {\n tr.removeMark(range.$from.pos, range.$to.pos)\n })\n }\n\n return true\n }\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarkRange } from '../helpers/getMarkRange.js'\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n unsetMark: {\n /**\n * Remove all marks in the current selection.\n * @param typeOrName The mark type or name.\n * @param options.extendEmptyMarkRange Removes the mark even across the current selection. Defaults to `false`.\n * @example editor.commands.unsetMark('bold')\n */\n unsetMark: (\n /**\n * The mark type or name.\n */\n typeOrName: string | MarkType,\n\n options?: {\n /**\n * Removes the mark even across the current selection. Defaults to `false`.\n */\n extendEmptyMarkRange?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\nexport const unsetMark: RawCommands['unsetMark'] =\n (typeOrName, options = {}) =>\n ({ tr, state, dispatch }) => {\n const { extendEmptyMarkRange = false } = options\n const { selection } = tr\n const type = getMarkType(typeOrName, state.schema)\n const { $from, empty, ranges } = selection\n\n if (!dispatch) {\n return true\n }\n\n if (empty && extendEmptyMarkRange) {\n let { from, to } = selection\n const attrs = $from.marks().find(mark => mark.type === type)?.attrs\n const range = getMarkRange($from, type, attrs)\n\n if (range) {\n from = range.from\n to = range.to\n }\n\n tr.removeMark(from, to, type)\n } else {\n ranges.forEach(range => {\n tr.removeMark(range.$from.pos, range.$to.pos, type)\n })\n }\n\n tr.removeStoredMark(type)\n\n return true\n }\n","import type { Range, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n unsetTextDirection: {\n /**\n * Remove the text direction attribute from nodes.\n * If no position is provided, it will use the current selection.\n * @param position Optional position or range to remove the direction from\n * @example editor.commands.unsetTextDirection()\n * @example editor.commands.unsetTextDirection({ from: 0, to: 10 })\n */\n unsetTextDirection: (position?: number | Range) => ReturnType\n }\n }\n}\n\nexport const unsetTextDirection: RawCommands['unsetTextDirection'] =\n position =>\n ({ tr, state, dispatch }) => {\n const { selection } = state\n let from: number\n let to: number\n\n if (typeof position === 'number') {\n from = position\n to = position\n } else if (position && 'from' in position && 'to' in position) {\n from = position.from\n to = position.to\n } else {\n from = selection.from\n to = selection.to\n }\n\n if (dispatch) {\n tr.doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n return\n }\n\n const newAttrs = { ...node.attrs }\n\n delete newAttrs.dir\n\n tr.setNodeMarkup(pos, undefined, newAttrs)\n })\n }\n\n return true\n }\n","import type { Mark, MarkType, Node, NodeType } from '@tiptap/pm/model'\nimport type { SelectionRange } from '@tiptap/pm/state'\n\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { getSchemaTypeNameByName } from '../helpers/getSchemaTypeNameByName.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n updateAttributes: {\n /**\n * Update attributes of a node or mark.\n * @param typeOrName The type or name of the node or mark.\n * @param attributes The attributes of the node or mark.\n * @example editor.commands.updateAttributes('mention', { userId: \"2\" })\n */\n updateAttributes: (\n /**\n * The type or name of the node or mark.\n */\n typeOrName: string | NodeType | MarkType,\n\n /**\n * The attributes of the node or mark.\n */\n attributes: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const updateAttributes: RawCommands['updateAttributes'] =\n (typeOrName, attributes = {}) =>\n ({ tr, state, dispatch }) => {\n let nodeType: NodeType | null = null\n let markType: MarkType | null = null\n\n const schemaType = getSchemaTypeNameByName(\n typeof typeOrName === 'string' ? typeOrName : typeOrName.name,\n state.schema,\n )\n\n if (!schemaType) {\n return false\n }\n\n if (schemaType === 'node') {\n nodeType = getNodeType(typeOrName as NodeType, state.schema)\n }\n\n if (schemaType === 'mark') {\n markType = getMarkType(typeOrName as MarkType, state.schema)\n }\n\n let canUpdate = false\n\n tr.selection.ranges.forEach((range: SelectionRange) => {\n const from = range.$from.pos\n const to = range.$to.pos\n\n let lastPos: number | undefined\n let lastNode: Node | undefined\n let trimmedFrom: number\n let trimmedTo: number\n\n if (tr.selection.empty) {\n state.doc.nodesBetween(from, to, (node: Node, pos: number) => {\n if (nodeType && nodeType === node.type) {\n canUpdate = true\n trimmedFrom = Math.max(pos, from)\n trimmedTo = Math.min(pos + node.nodeSize, to)\n lastPos = pos\n lastNode = node\n }\n })\n } else {\n state.doc.nodesBetween(from, to, (node: Node, pos: number) => {\n if (pos < from && nodeType && nodeType === node.type) {\n canUpdate = true\n trimmedFrom = Math.max(pos, from)\n trimmedTo = Math.min(pos + node.nodeSize, to)\n lastPos = pos\n lastNode = node\n }\n\n if (pos >= from && pos <= to) {\n if (nodeType && nodeType === node.type) {\n canUpdate = true\n\n if (dispatch) {\n tr.setNodeMarkup(pos, undefined, {\n ...node.attrs,\n ...attributes,\n })\n }\n }\n\n if (markType && node.marks.length) {\n node.marks.forEach((mark: Mark) => {\n if (markType === mark.type) {\n canUpdate = true\n\n if (dispatch) {\n const trimmedFrom2 = Math.max(pos, from)\n const trimmedTo2 = Math.min(pos + node.nodeSize, to)\n\n tr.addMark(\n trimmedFrom2,\n trimmedTo2,\n markType.create({\n ...mark.attrs,\n ...attributes,\n }),\n )\n }\n }\n })\n }\n }\n })\n }\n\n if (lastNode) {\n if (lastPos !== undefined && dispatch) {\n tr.setNodeMarkup(lastPos, undefined, {\n ...lastNode.attrs,\n ...attributes,\n })\n }\n\n if (markType && lastNode.marks.length) {\n lastNode.marks.forEach((mark: Mark) => {\n if (markType === mark.type && dispatch) {\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n markType.create({\n ...mark.attrs,\n ...attributes,\n }),\n )\n }\n })\n }\n }\n })\n\n return canUpdate\n }\n","import { wrapIn as originalWrapIn } from '@tiptap/pm/commands'\nimport type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n wrapIn: {\n /**\n * Wraps nodes in another node.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.wrapIn('blockquote')\n */\n wrapIn: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const wrapIn: RawCommands['wrapIn'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalWrapIn(type, attributes)(state, dispatch)\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport { wrapInList as originalWrapInList } from '@tiptap/pm/schema-list'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n wrapInList: {\n /**\n * Wrap a node in a list.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.wrapInList('bulletList')\n */\n wrapInList: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const wrapInList: RawCommands['wrapInList'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalWrapInList(type, attributes)(state, dispatch)\n }\n","/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport type { MarkType, Node as ProseMirrorNode, NodeType, Schema } from '@tiptap/pm/model'\nimport type { Plugin, PluginKey, Transaction } from '@tiptap/pm/state'\nimport { EditorState } from '@tiptap/pm/state'\nimport { EditorView } from '@tiptap/pm/view'\n\nimport { CommandManager } from './CommandManager.js'\nimport { EventEmitter } from './EventEmitter.js'\nimport { ExtensionManager } from './ExtensionManager.js'\nimport {\n ClipboardTextSerializer,\n Commands,\n Delete,\n Drop,\n Editable,\n FocusEvents,\n Keymap,\n Paste,\n Tabindex,\n TextDirection,\n} from './extensions/index.js'\nimport { createDocument } from './helpers/createDocument.js'\nimport { getAttributes } from './helpers/getAttributes.js'\nimport { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'\nimport { getText } from './helpers/getText.js'\nimport { getTextSerializersFromSchema } from './helpers/getTextSerializersFromSchema.js'\nimport { isActive } from './helpers/isActive.js'\nimport { isNodeEmpty } from './helpers/isNodeEmpty.js'\nimport { createMappablePosition, getUpdatedPosition } from './helpers/MappablePosition.js'\nimport { resolveFocusPosition } from './helpers/resolveFocusPosition.js'\nimport type { Storage } from './index.js'\nimport { NodePos } from './NodePos.js'\nimport { style } from './style.js'\nimport type {\n CanCommands,\n ChainedCommands,\n DocumentType,\n EditorEvents,\n EditorOptions,\n NodeType as TNodeType,\n SingleCommands,\n TextSerializer,\n TextType as TTextType,\n Utils,\n} from './types.js'\nimport { createStyleTag } from './utilities/createStyleTag.js'\nimport { isFunction } from './utilities/isFunction.js'\n\nexport * as extensions from './extensions/index.js'\n\n// @ts-ignore\nexport interface TiptapEditorHTMLElement extends HTMLElement {\n editor?: Editor\n}\n\nexport class Editor extends EventEmitter<EditorEvents> {\n private commandManager!: CommandManager\n\n public extensionManager!: ExtensionManager\n\n private css: HTMLStyleElement | null = null\n\n private className = 'tiptap'\n\n public schema!: Schema\n\n private editorView: EditorView | null = null\n\n public isFocused = false\n\n private editorState!: EditorState\n\n /**\n * The editor is considered initialized after the `create` event has been emitted.\n */\n public isInitialized = false\n\n public extensionStorage: Storage = {} as Storage\n\n /**\n * A unique ID for this editor instance.\n */\n public instanceId = Math.random().toString(36).slice(2, 9)\n\n public options: EditorOptions = {\n element: typeof document !== 'undefined' ? document.createElement('div') : null,\n content: '',\n injectCSS: true,\n injectNonce: undefined,\n extensions: [],\n autofocus: false,\n editable: true,\n textDirection: undefined,\n editorProps: {},\n parseOptions: {},\n coreExtensionOptions: {},\n enableInputRules: true,\n enablePasteRules: true,\n enableCoreExtensions: true,\n enableContentCheck: false,\n emitContentError: false,\n onBeforeCreate: () => null,\n onCreate: () => null,\n onMount: () => null,\n onUnmount: () => null,\n onUpdate: () => null,\n onSelectionUpdate: () => null,\n onTransaction: () => null,\n onFocus: () => null,\n onBlur: () => null,\n onDestroy: () => null,\n onContentError: ({ error }) => {\n throw error\n },\n onPaste: () => null,\n onDrop: () => null,\n onDelete: () => null,\n enableExtensionDispatchTransaction: true,\n }\n\n constructor(options: Partial<EditorOptions> = {}) {\n super()\n this.setOptions(options)\n this.createExtensionManager()\n this.createCommandManager()\n this.createSchema()\n this.on('beforeCreate', this.options.onBeforeCreate)\n this.emit('beforeCreate', { editor: this })\n this.on('mount', this.options.onMount)\n this.on('unmount', this.options.onUnmount)\n this.on('contentError', this.options.onContentError)\n this.on('create', this.options.onCreate)\n this.on('update', this.options.onUpdate)\n this.on('selectionUpdate', this.options.onSelectionUpdate)\n this.on('transaction', this.options.onTransaction)\n this.on('focus', this.options.onFocus)\n this.on('blur', this.options.onBlur)\n this.on('destroy', this.options.onDestroy)\n this.on('drop', ({ event, slice, moved }) => this.options.onDrop(event, slice, moved))\n this.on('paste', ({ event, slice }) => this.options.onPaste(event, slice))\n this.on('delete', this.options.onDelete)\n\n const initialDoc = this.createDoc()\n const selection = resolveFocusPosition(initialDoc, this.options.autofocus)\n\n // Set editor state immediately, so that it's available independently from the view\n this.editorState = EditorState.create({\n doc: initialDoc,\n schema: this.schema,\n selection: selection || undefined,\n })\n\n if (this.options.element) {\n this.mount(this.options.element)\n }\n }\n\n /**\n * Attach the editor to the DOM, creating a new editor view.\n */\n public mount(el: NonNullable<EditorOptions['element']> & {}) {\n if (typeof document === 'undefined') {\n throw new Error(\n `[tiptap error]: The editor cannot be mounted because there is no 'document' defined in this environment.`,\n )\n }\n this.createView(el)\n this.emit('mount', { editor: this })\n\n if (this.css && !document.head.contains(this.css)) {\n document.head.appendChild(this.css)\n }\n\n window.setTimeout(() => {\n if (this.isDestroyed) {\n return\n }\n\n if (this.options.autofocus !== false && this.options.autofocus !== null) {\n this.commands.focus(this.options.autofocus)\n }\n this.emit('create', { editor: this })\n this.isInitialized = true\n }, 0)\n }\n\n /**\n * Remove the editor from the DOM, but still allow remounting at a different point in time\n */\n public unmount() {\n if (this.editorView) {\n // Cleanup our reference to prevent circular references which caused memory leaks\n // @ts-ignore\n const dom = this.editorView.dom as TiptapEditorHTMLElement\n\n if (dom?.editor) {\n delete dom.editor\n }\n this.editorView.destroy()\n }\n this.editorView = null\n this.isInitialized = false\n\n // Safely remove CSS element with fallback for test environments\n // Only remove CSS if no other editors exist in the document after unmount\n if (this.css && !document.querySelectorAll(`.${this.className}`).length) {\n try {\n if (typeof this.css.remove === 'function') {\n this.css.remove()\n } else if (this.css.parentNode) {\n this.css.parentNode.removeChild(this.css)\n }\n } catch (error) {\n // Silently handle any unexpected DOM removal errors in test environments\n console.warn('Failed to remove CSS element:', error)\n }\n }\n this.css = null\n this.emit('unmount', { editor: this })\n }\n\n /**\n * Returns the editor storage.\n */\n public get storage(): Storage {\n return this.extensionStorage\n }\n\n /**\n * An object of all registered commands.\n */\n public get commands(): SingleCommands {\n return this.commandManager.commands\n }\n\n /**\n * Create a command chain to call multiple commands at once.\n */\n public chain(): ChainedCommands {\n return this.commandManager.chain()\n }\n\n /**\n * Check if a command or a command chain can be executed. Without executing it.\n */\n public can(): CanCommands {\n return this.commandManager.can()\n }\n\n /**\n * Inject CSS styles.\n */\n private injectCSS(): void {\n if (this.options.injectCSS && typeof document !== 'undefined') {\n this.css = createStyleTag(style, this.options.injectNonce)\n }\n }\n\n /**\n * Update editor options.\n *\n * @param options A list of options\n */\n public setOptions(options: Partial<EditorOptions> = {}): void {\n this.options = {\n ...this.options,\n ...options,\n }\n\n if (!this.editorView || !this.state || this.isDestroyed) {\n return\n }\n\n if (this.options.editorProps) {\n this.view.setProps(this.options.editorProps)\n }\n\n this.view.updateState(this.state)\n }\n\n /**\n * Update editable state of the editor.\n */\n public setEditable(editable: boolean, emitUpdate = true): void {\n this.setOptions({ editable })\n\n if (emitUpdate) {\n this.emit('update', { editor: this, transaction: this.state.tr, appendedTransactions: [] })\n }\n }\n\n /**\n * Returns whether the editor is editable.\n */\n public get isEditable(): boolean {\n // since plugins are applied after creating the view\n // `editable` is always `true` for one tick.\n // that’s why we also have to check for `options.editable`\n return this.options.editable && this.view && this.view.editable\n }\n\n /**\n * Returns the editor state.\n */\n public get view(): EditorView {\n if (this.editorView) {\n return this.editorView\n }\n\n return new Proxy(\n {\n state: this.editorState,\n updateState: (state: EditorState): ReturnType<EditorView['updateState']> => {\n this.editorState = state\n },\n dispatch: (tr: Transaction): ReturnType<EditorView['dispatch']> => {\n this.dispatchTransaction(tr)\n },\n\n // Stub some commonly accessed properties to prevent errors\n composing: false,\n dragging: null,\n editable: true,\n isDestroyed: false,\n } as EditorView,\n {\n get: (obj, key) => {\n if (this.editorView) {\n // If the editor view is available, but the caller has a stale reference to the proxy,\n // Just return what the editor view has.\n return this.editorView[key as keyof EditorView]\n }\n // Specifically always return the most recent editorState\n if (key === 'state') {\n return this.editorState\n }\n if (key in obj) {\n return Reflect.get(obj, key)\n }\n\n // We throw an error here, because we know the view is not available\n throw new Error(\n `[tiptap error]: The editor view is not available. Cannot access view['${key as string}']. The editor may not be mounted yet.`,\n )\n },\n },\n ) as EditorView\n }\n\n /**\n * Returns the editor state.\n */\n public get state(): EditorState {\n if (this.editorView) {\n this.editorState = this.view.state\n }\n\n return this.editorState\n }\n\n /**\n * Register a ProseMirror plugin.\n *\n * @param plugin A ProseMirror plugin\n * @param handlePlugins Control how to merge the plugin into the existing plugins.\n * @returns The new editor state\n */\n public registerPlugin(\n plugin: Plugin,\n handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[],\n ): EditorState {\n const plugins = isFunction(handlePlugins)\n ? handlePlugins(plugin, [...this.state.plugins])\n : [...this.state.plugins, plugin]\n\n const state = this.state.reconfigure({ plugins })\n\n this.view.updateState(state)\n\n return state\n }\n\n /**\n * Unregister a ProseMirror plugin.\n *\n * @param nameOrPluginKeyToRemove The plugins name\n * @returns The new editor state or undefined if the editor is destroyed\n */\n public unregisterPlugin(\n nameOrPluginKeyToRemove: string | PluginKey | (string | PluginKey)[],\n ): EditorState | undefined {\n if (this.isDestroyed) {\n return undefined\n }\n\n const prevPlugins = this.state.plugins\n let plugins = prevPlugins\n\n ;([] as (string | PluginKey)[]).concat(nameOrPluginKeyToRemove).forEach(nameOrPluginKey => {\n // @ts-ignore\n const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key\n\n // @ts-ignore\n plugins = plugins.filter(plugin => !plugin.key.startsWith(name))\n })\n\n if (prevPlugins.length === plugins.length) {\n // No plugin was removed, so we don’t need to update the state\n return undefined\n }\n\n const state = this.state.reconfigure({\n plugins,\n })\n\n this.view.updateState(state)\n\n return state\n }\n\n /**\n * Creates an extension manager.\n */\n private createExtensionManager(): void {\n const coreExtensions = this.options.enableCoreExtensions\n ? [\n Editable,\n ClipboardTextSerializer.configure({\n blockSeparator: this.options.coreExtensionOptions?.clipboardTextSerializer?.blockSeparator,\n }),\n Commands,\n FocusEvents,\n Keymap,\n Tabindex,\n Drop,\n Paste,\n Delete,\n TextDirection.configure({\n direction: this.options.textDirection,\n }),\n ].filter(ext => {\n if (typeof this.options.enableCoreExtensions === 'object') {\n return (\n this.options.enableCoreExtensions[ext.name as keyof typeof this.options.enableCoreExtensions] !== false\n )\n }\n return true\n })\n : []\n const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {\n return ['extension', 'node', 'mark'].includes(extension?.type)\n })\n\n this.extensionManager = new ExtensionManager(allExtensions, this)\n }\n\n /**\n * Creates an command manager.\n */\n private createCommandManager(): void {\n this.commandManager = new CommandManager({\n editor: this,\n })\n }\n\n /**\n * Creates a ProseMirror schema.\n */\n private createSchema(): void {\n this.schema = this.extensionManager.schema\n }\n\n /**\n * Creates the initial document.\n */\n private createDoc(): ProseMirrorNode {\n let doc: ProseMirrorNode\n\n try {\n doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {\n errorOnInvalidContent: this.options.enableContentCheck,\n })\n } catch (e) {\n if (\n !(e instanceof Error) ||\n !['[tiptap error]: Invalid JSON content', '[tiptap error]: Invalid HTML content'].includes(e.message)\n ) {\n // Not the content error we were expecting\n throw e\n }\n this.emit('contentError', {\n editor: this,\n error: e as Error,\n disableCollaboration: () => {\n if (\n 'collaboration' in this.storage &&\n typeof this.storage.collaboration === 'object' &&\n this.storage.collaboration\n ) {\n ;(this.storage.collaboration as any).isDisabled = true\n }\n // To avoid syncing back invalid content, reinitialize the extensions without the collaboration extension\n this.options.extensions = this.options.extensions.filter(extension => extension.name !== 'collaboration')\n\n // Restart the initialization process by recreating the extension manager with the new set of extensions\n this.createExtensionManager()\n },\n })\n\n // Content is invalid, but attempt to create it anyway, stripping out the invalid parts\n doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {\n errorOnInvalidContent: false,\n })\n }\n return doc\n }\n\n /**\n * Creates a ProseMirror view.\n */\n private createView(element: NonNullable<EditorOptions['element']>): void {\n const { editorProps, enableExtensionDispatchTransaction } = this.options\n // If a user provided a custom `dispatchTransaction` through `editorProps`,\n // we use that as the base dispatch function.\n // Otherwise, we use Tiptap's internal `dispatchTransaction` method.\n const baseDispatch = (editorProps as any).dispatchTransaction || this.dispatchTransaction.bind(this)\n const dispatch = enableExtensionDispatchTransaction\n ? this.extensionManager.dispatchTransaction(baseDispatch)\n : baseDispatch\n\n this.editorView = new EditorView(element, {\n ...editorProps,\n attributes: {\n // add `role=\"textbox\"` to the editor element\n role: 'textbox',\n ...editorProps?.attributes,\n },\n dispatchTransaction: dispatch,\n state: this.editorState,\n markViews: this.extensionManager.markViews,\n nodeViews: this.extensionManager.nodeViews,\n })\n\n // `editor.view` is not yet available at this time.\n // Therefore we will add all plugins and node views directly afterwards.\n const newState = this.state.reconfigure({\n plugins: this.extensionManager.plugins,\n })\n\n this.view.updateState(newState)\n\n this.prependClass()\n this.injectCSS()\n\n // Let’s store the editor instance in the DOM element.\n // So we’ll have access to it for tests.\n // @ts-ignore\n const dom = this.view.dom as TiptapEditorHTMLElement\n\n dom.editor = this\n }\n\n /**\n * Creates all node and mark views.\n */\n public createNodeViews(): void {\n if (this.view.isDestroyed) {\n return\n }\n\n this.view.setProps({\n markViews: this.extensionManager.markViews,\n nodeViews: this.extensionManager.nodeViews,\n })\n }\n\n /**\n * Prepend class name to element.\n */\n public prependClass(): void {\n this.view.dom.className = `${this.className} ${this.view.dom.className}`\n }\n\n public isCapturingTransaction = false\n\n private capturedTransaction: Transaction | null = null\n\n public captureTransaction(fn: () => void) {\n this.isCapturingTransaction = true\n fn()\n this.isCapturingTransaction = false\n\n const tr = this.capturedTransaction\n\n this.capturedTransaction = null\n\n return tr\n }\n\n /**\n * The callback over which to send transactions (state updates) produced by the view.\n *\n * @param transaction An editor state transaction\n */\n private dispatchTransaction(transaction: Transaction): void {\n // if the editor / the view of the editor was destroyed\n // the transaction should not be dispatched as there is no view anymore.\n if (this.view.isDestroyed) {\n return\n }\n\n if (this.isCapturingTransaction) {\n if (!this.capturedTransaction) {\n this.capturedTransaction = transaction\n\n return\n }\n\n transaction.steps.forEach(step => this.capturedTransaction?.step(step))\n\n return\n }\n\n // Apply transaction and get resulting state and transactions\n const { state, transactions } = this.state.applyTransaction(transaction)\n const selectionHasChanged = !this.state.selection.eq(state.selection)\n const rootTrWasApplied = transactions.includes(transaction)\n const prevState = this.state\n\n this.emit('beforeTransaction', {\n editor: this,\n transaction,\n nextState: state,\n })\n\n // If transaction was filtered out, we can return early\n if (!rootTrWasApplied) {\n return\n }\n\n this.view.updateState(state)\n\n // Emit transaction event with appended transactions info\n this.emit('transaction', {\n editor: this,\n transaction,\n appendedTransactions: transactions.slice(1),\n })\n\n if (selectionHasChanged) {\n this.emit('selectionUpdate', {\n editor: this,\n transaction,\n })\n }\n\n // Only emit the latest between focus and blur events\n const mostRecentFocusTr = transactions.findLast(tr => tr.getMeta('focus') || tr.getMeta('blur'))\n const focus = mostRecentFocusTr?.getMeta('focus')\n const blur = mostRecentFocusTr?.getMeta('blur')\n\n if (focus) {\n this.emit('focus', {\n editor: this,\n event: focus.event,\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n transaction: mostRecentFocusTr!,\n })\n }\n\n if (blur) {\n this.emit('blur', {\n editor: this,\n event: blur.event,\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n transaction: mostRecentFocusTr!,\n })\n }\n\n // Compare states for update event\n if (\n transaction.getMeta('preventUpdate') ||\n !transactions.some(tr => tr.docChanged) ||\n prevState.doc.eq(state.doc)\n ) {\n return\n }\n\n this.emit('update', {\n editor: this,\n transaction,\n appendedTransactions: transactions.slice(1),\n })\n }\n\n /**\n * Get attributes of the currently selected node or mark.\n */\n public getAttributes(nameOrType: string | NodeType | MarkType): Record<string, any> {\n return getAttributes(this.state, nameOrType)\n }\n\n /**\n * Returns if the currently selected node or mark is active.\n *\n * @param name Name of the node or mark\n * @param attributes Attributes of the node or mark\n */\n public isActive(name: string, attributes?: {}): boolean\n public isActive(attributes: {}): boolean\n public isActive(nameOrAttributes: string, attributesOrUndefined?: {}): boolean {\n const name = typeof nameOrAttributes === 'string' ? nameOrAttributes : null\n\n const attributes = typeof nameOrAttributes === 'string' ? attributesOrUndefined : nameOrAttributes\n\n return isActive(this.state, name, attributes)\n }\n\n /**\n * Get the document as JSON.\n */\n public getJSON(): DocumentType<\n Record<string, any> | undefined,\n TNodeType<string, undefined | Record<string, any>, any, (TNodeType | TTextType)[]>[]\n > {\n return this.state.doc.toJSON()\n }\n\n /**\n * Get the document as HTML.\n */\n public getHTML(): string {\n return getHTMLFromFragment(this.state.doc.content, this.schema)\n }\n\n /**\n * Get the document as text.\n */\n public getText(options?: { blockSeparator?: string; textSerializers?: Record<string, TextSerializer> }): string {\n const { blockSeparator = '\\n\\n', textSerializers = {} } = options || {}\n\n return getText(this.state.doc, {\n blockSeparator,\n textSerializers: {\n ...getTextSerializersFromSchema(this.schema),\n ...textSerializers,\n },\n })\n }\n\n /**\n * Check if there is no content.\n */\n public get isEmpty(): boolean {\n return isNodeEmpty(this.state.doc)\n }\n\n /**\n * Destroy the editor.\n */\n public destroy(): void {\n this.emit('destroy')\n\n this.unmount()\n\n this.removeAllListeners()\n }\n\n /**\n * Check if the editor is already destroyed.\n */\n public get isDestroyed(): boolean {\n return this.editorView?.isDestroyed ?? true\n }\n\n public $node(selector: string, attributes?: { [key: string]: any }): NodePos | null {\n return this.$doc?.querySelector(selector, attributes) || null\n }\n\n public $nodes(selector: string, attributes?: { [key: string]: any }): NodePos[] | null {\n return this.$doc?.querySelectorAll(selector, attributes) || null\n }\n\n public $pos(pos: number) {\n const $pos = this.state.doc.resolve(pos)\n\n return new NodePos($pos, this)\n }\n\n get $doc() {\n return this.$pos(0)\n }\n\n /**\n * Returns a set of utilities for working with positions and ranges.\n */\n public utils: Utils = {\n getUpdatedPosition,\n createMappablePosition,\n }\n}\n","type StringKeyOf<T> = Extract<keyof T, string>\ntype CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]\n ? T[EventName]\n : [T[EventName]]\ntype CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (\n ...props: CallbackType<T, EventName>\n) => any\n\nexport class EventEmitter<T extends Record<string, any>> {\n private callbacks: { [key: string]: Array<(...args: any[]) => void> } = {}\n\n public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this {\n if (!this.callbacks[event]) {\n this.callbacks[event] = []\n }\n\n this.callbacks[event].push(fn)\n\n return this\n }\n\n public emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>): this {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n callbacks.forEach(callback => callback.apply(this, args))\n }\n\n return this\n }\n\n public off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>): this {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n if (fn) {\n this.callbacks[event] = callbacks.filter(callback => callback !== fn)\n } else {\n delete this.callbacks[event]\n }\n }\n\n return this\n }\n\n public once<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this {\n const onceFn = (...args: CallbackType<T, EventName>) => {\n this.off(event, onceFn)\n fn.apply(this, args)\n }\n\n return this.on(event, onceFn)\n }\n\n public removeAllListeners(): void {\n this.callbacks = {}\n }\n}\n","import { keymap } from '@tiptap/pm/keymap'\nimport type { Schema } from '@tiptap/pm/model'\nimport type { Plugin, Transaction } from '@tiptap/pm/state'\nimport type { MarkViewConstructor, NodeViewConstructor } from '@tiptap/pm/view'\n\nimport type { Editor } from './Editor.js'\nimport {\n flattenExtensions,\n getAttributesFromExtensions,\n getExtensionField,\n getNodeType,\n getRenderedAttributes,\n getSchemaByResolvedExtensions,\n getSchemaTypeByName,\n isExtensionRulesEnabled,\n resolveExtensions,\n sortExtensions,\n splitExtensions,\n} from './helpers/index.js'\nimport { type MarkConfig, type NodeConfig, type Storage, getMarkType, updateMarkViewAttributes } from './index.js'\nimport { inputRulesPlugin } from './InputRule.js'\nimport { Mark } from './Mark.js'\nimport { pasteRulesPlugin } from './PasteRule.js'\nimport type { AnyConfig, Extensions, RawCommands } from './types.js'\nimport { callOrReturn } from './utilities/callOrReturn.js'\n\nexport class ExtensionManager {\n editor: Editor\n\n schema: Schema\n\n /**\n * A flattened and sorted array of all extensions\n */\n extensions: Extensions\n\n /**\n * A non-flattened array of base extensions (no sub-extensions)\n */\n baseExtensions: Extensions\n\n splittableMarks: string[] = []\n\n constructor(extensions: Extensions, editor: Editor) {\n this.editor = editor\n this.baseExtensions = extensions\n this.extensions = resolveExtensions(extensions)\n this.schema = getSchemaByResolvedExtensions(this.extensions, editor)\n this.setupExtensions()\n }\n\n static resolve = resolveExtensions\n\n static sort = sortExtensions\n\n static flatten = flattenExtensions\n\n /**\n * Get all commands from the extensions.\n * @returns An object with all commands where the key is the command name and the value is the command function\n */\n get commands(): RawCommands {\n return this.extensions.reduce((commands, extension) => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor: this.editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n const addCommands = getExtensionField<AnyConfig['addCommands']>(extension, 'addCommands', context)\n\n if (!addCommands) {\n return commands\n }\n\n return {\n ...commands,\n ...addCommands(),\n }\n }, {} as RawCommands)\n }\n\n /**\n * Get all registered Prosemirror plugins from the extensions.\n * @returns An array of Prosemirror plugins\n */\n get plugins(): Plugin[] {\n const { editor } = this\n\n // With ProseMirror, first plugins within an array are executed first.\n // In Tiptap, we provide the ability to override plugins,\n // so it feels more natural to run plugins at the end of an array first.\n // That’s why we have to reverse the `extensions` array and sort again\n // based on the `priority` option.\n const extensions = sortExtensions([...this.extensions].reverse())\n\n const allPlugins = extensions.flatMap(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n const plugins: Plugin[] = []\n\n const addKeyboardShortcuts = getExtensionField<AnyConfig['addKeyboardShortcuts']>(\n extension,\n 'addKeyboardShortcuts',\n context,\n )\n\n let defaultBindings: Record<string, () => boolean> = {}\n\n // bind exit handling\n if (extension.type === 'mark' && getExtensionField<MarkConfig['exitable']>(extension, 'exitable', context)) {\n defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: extension as Mark })\n }\n\n if (addKeyboardShortcuts) {\n const bindings = Object.fromEntries(\n Object.entries(addKeyboardShortcuts()).map(([shortcut, method]) => {\n return [shortcut, () => method({ editor })]\n }),\n )\n\n defaultBindings = { ...defaultBindings, ...bindings }\n }\n\n const keyMapPlugin = keymap(defaultBindings)\n\n plugins.push(keyMapPlugin)\n\n const addInputRules = getExtensionField<AnyConfig['addInputRules']>(extension, 'addInputRules', context)\n\n if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {\n const rules = addInputRules()\n\n if (rules && rules.length) {\n const inputResult = inputRulesPlugin({\n editor,\n rules,\n })\n\n const inputPlugins = Array.isArray(inputResult) ? inputResult : [inputResult]\n\n plugins.push(...inputPlugins)\n }\n }\n\n const addPasteRules = getExtensionField<AnyConfig['addPasteRules']>(extension, 'addPasteRules', context)\n\n if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {\n const rules = addPasteRules()\n\n if (rules && rules.length) {\n const pasteRules = pasteRulesPlugin({ editor, rules })\n\n plugins.push(...pasteRules)\n }\n }\n\n const addProseMirrorPlugins = getExtensionField<AnyConfig['addProseMirrorPlugins']>(\n extension,\n 'addProseMirrorPlugins',\n context,\n )\n\n if (addProseMirrorPlugins) {\n const proseMirrorPlugins = addProseMirrorPlugins()\n\n plugins.push(...proseMirrorPlugins)\n }\n\n return plugins\n })\n\n return allPlugins\n }\n\n /**\n * Get all attributes from the extensions.\n * @returns An array of attributes\n */\n get attributes() {\n return getAttributesFromExtensions(this.extensions)\n }\n\n /**\n * Get all node views from the extensions.\n * @returns An object with all node views where the key is the node name and the value is the node view function\n */\n get nodeViews(): Record<string, NodeViewConstructor> {\n const { editor } = this\n const { nodeExtensions } = splitExtensions(this.extensions)\n\n return Object.fromEntries(\n nodeExtensions\n .filter(extension => !!getExtensionField(extension, 'addNodeView'))\n .map(extension => {\n const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getNodeType(extension.name, this.schema),\n }\n const addNodeView = getExtensionField<NodeConfig['addNodeView']>(extension, 'addNodeView', context)\n\n if (!addNodeView) {\n return []\n }\n\n const nodeViewResult = addNodeView()\n\n if (!nodeViewResult) {\n return []\n }\n\n const nodeview: NodeViewConstructor = (node, view, getPos, decorations, innerDecorations) => {\n const HTMLAttributes = getRenderedAttributes(node, extensionAttributes)\n\n return nodeViewResult({\n // pass-through\n node,\n view,\n getPos: getPos as () => number,\n decorations,\n innerDecorations,\n // tiptap-specific\n editor,\n extension,\n HTMLAttributes,\n })\n }\n\n return [extension.name, nodeview]\n }),\n )\n }\n\n /**\n * Get the composed dispatchTransaction function from all extensions.\n * @param baseDispatch The base dispatch function (e.g. from the editor or user props)\n * @returns A composed dispatch function\n */\n dispatchTransaction(baseDispatch: (tr: Transaction) => void): (tr: Transaction) => void {\n const { editor } = this\n const extensions = sortExtensions([...this.extensions].reverse())\n\n return extensions.reduceRight((next, extension) => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n const dispatchTransaction = getExtensionField<AnyConfig['dispatchTransaction']>(\n extension,\n 'dispatchTransaction',\n context,\n )\n\n if (!dispatchTransaction) {\n return next\n }\n\n return (transaction: Transaction) => {\n dispatchTransaction.call(context, { transaction, next })\n }\n }, baseDispatch)\n }\n\n get markViews(): Record<string, MarkViewConstructor> {\n const { editor } = this\n const { markExtensions } = splitExtensions(this.extensions)\n\n return Object.fromEntries(\n markExtensions\n .filter(extension => !!getExtensionField(extension, 'addMarkView'))\n .map(extension => {\n const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getMarkType(extension.name, this.schema),\n }\n const addMarkView = getExtensionField<MarkConfig['addMarkView']>(extension, 'addMarkView', context)\n\n if (!addMarkView) {\n return []\n }\n\n const markView: MarkViewConstructor = (mark, view, inline) => {\n const HTMLAttributes = getRenderedAttributes(mark, extensionAttributes)\n\n return addMarkView()({\n // pass-through\n mark,\n view,\n inline,\n // tiptap-specific\n editor,\n extension,\n HTMLAttributes,\n updateAttributes: (attrs: Record<string, any>) => {\n updateMarkViewAttributes(mark, editor, attrs)\n },\n })\n }\n\n return [extension.name, markView]\n }),\n )\n }\n\n /**\n * Go through all extensions, create extension storages & setup marks\n * & bind editor event listener.\n */\n private setupExtensions() {\n const extensions = this.extensions\n // re-initialize the extension storage object instance\n this.editor.extensionStorage = Object.fromEntries(\n extensions.map(extension => [extension.name, extension.storage]),\n ) as unknown as Storage\n\n extensions.forEach(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor: this.editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n if (extension.type === 'mark') {\n const keepOnSplit = callOrReturn(getExtensionField(extension, 'keepOnSplit', context)) ?? true\n\n if (keepOnSplit) {\n this.splittableMarks.push(extension.name)\n }\n }\n\n const onBeforeCreate = getExtensionField<AnyConfig['onBeforeCreate']>(extension, 'onBeforeCreate', context)\n const onCreate = getExtensionField<AnyConfig['onCreate']>(extension, 'onCreate', context)\n const onUpdate = getExtensionField<AnyConfig['onUpdate']>(extension, 'onUpdate', context)\n const onSelectionUpdate = getExtensionField<AnyConfig['onSelectionUpdate']>(\n extension,\n 'onSelectionUpdate',\n context,\n )\n const onTransaction = getExtensionField<AnyConfig['onTransaction']>(extension, 'onTransaction', context)\n const onFocus = getExtensionField<AnyConfig['onFocus']>(extension, 'onFocus', context)\n const onBlur = getExtensionField<AnyConfig['onBlur']>(extension, 'onBlur', context)\n const onDestroy = getExtensionField<AnyConfig['onDestroy']>(extension, 'onDestroy', context)\n\n if (onBeforeCreate) {\n this.editor.on('beforeCreate', onBeforeCreate)\n }\n\n if (onCreate) {\n this.editor.on('create', onCreate)\n }\n\n if (onUpdate) {\n this.editor.on('update', onUpdate)\n }\n\n if (onSelectionUpdate) {\n this.editor.on('selectionUpdate', onSelectionUpdate)\n }\n\n if (onTransaction) {\n this.editor.on('transaction', onTransaction)\n }\n\n if (onFocus) {\n this.editor.on('focus', onFocus)\n }\n\n if (onBlur) {\n this.editor.on('blur', onBlur)\n }\n\n if (onDestroy) {\n this.editor.on('destroy', onDestroy)\n }\n })\n }\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Fragment } from '@tiptap/pm/model'\nimport type { EditorState, TextSelection } from '@tiptap/pm/state'\nimport { Plugin } from '@tiptap/pm/state'\n\nimport { CommandManager } from './CommandManager.js'\nimport type { Editor } from './Editor.js'\nimport { createChainableState } from './helpers/createChainableState.js'\nimport { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'\nimport { getTextContentFromNodes } from './helpers/getTextContentFromNodes.js'\nimport type { CanCommands, ChainedCommands, ExtendedRegExpMatchArray, Range, SingleCommands } from './types.js'\nimport { isRegExp } from './utilities/isRegExp.js'\n\nexport type InputRuleMatch = {\n index: number\n text: string\n replaceWith?: string\n match?: RegExpMatchArray\n data?: Record<string, any>\n}\n\nexport type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null)\n\nexport class InputRule {\n find: InputRuleFinder\n\n handler: (props: {\n state: EditorState\n range: Range\n match: ExtendedRegExpMatchArray\n commands: SingleCommands\n chain: () => ChainedCommands\n can: () => CanCommands\n }) => void | null\n\n undoable: boolean\n\n constructor(config: {\n find: InputRuleFinder\n handler: (props: {\n state: EditorState\n range: Range\n match: ExtendedRegExpMatchArray\n commands: SingleCommands\n chain: () => ChainedCommands\n can: () => CanCommands\n }) => void | null\n undoable?: boolean\n }) {\n this.find = config.find\n this.handler = config.handler\n this.undoable = config.undoable ?? true\n }\n}\n\nconst inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedRegExpMatchArray | null => {\n if (isRegExp(find)) {\n return find.exec(text)\n }\n\n const inputRuleMatch = find(text)\n\n if (!inputRuleMatch) {\n return null\n }\n\n const result: ExtendedRegExpMatchArray = [inputRuleMatch.text]\n\n result.index = inputRuleMatch.index\n result.input = text\n result.data = inputRuleMatch.data\n\n if (inputRuleMatch.replaceWith) {\n if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {\n console.warn('[tiptap warn]: \"inputRuleMatch.replaceWith\" must be part of \"inputRuleMatch.text\".')\n }\n\n result.push(inputRuleMatch.replaceWith)\n }\n\n return result\n}\n\nfunction run(config: {\n editor: Editor\n from: number\n to: number\n text: string\n rules: InputRule[]\n plugin: Plugin\n}): boolean {\n const { editor, from, to, text, rules, plugin } = config\n const { view } = editor\n\n if (view.composing) {\n return false\n }\n\n const $from = view.state.doc.resolve(from)\n\n if (\n // check for code node\n $from.parent.type.spec.code ||\n // check for code mark\n !!($from.nodeBefore || $from.nodeAfter)?.marks.find(mark => mark.type.spec.code)\n ) {\n return false\n }\n\n let matched = false\n\n const textBefore = getTextContentFromNodes($from) + text\n\n rules.forEach(rule => {\n if (matched) {\n return\n }\n\n const match = inputRuleMatcherHandler(textBefore, rule.find)\n\n if (!match) {\n return\n }\n\n const tr = view.state.tr\n const state = createChainableState({\n state: view.state,\n transaction: tr,\n })\n const range = {\n from: from - (match[0].length - text.length),\n to,\n }\n\n const { commands, chain, can } = new CommandManager({\n editor,\n state,\n })\n\n const handler = rule.handler({\n state,\n range,\n match,\n commands,\n chain,\n can,\n })\n\n // stop if there are no changes\n if (handler === null || !tr.steps.length) {\n return\n }\n\n // store transform as meta data\n // so we can undo input rules within the `undoInputRules` command\n if (rule.undoable) {\n tr.setMeta(plugin, {\n transform: tr,\n from,\n to,\n text,\n })\n }\n\n view.dispatch(tr)\n matched = true\n })\n\n return matched\n}\n\n/**\n * Create an input rules plugin. When enabled, it will cause text\n * input that matches any of the given rules to trigger the rule’s\n * action.\n */\nexport function inputRulesPlugin(props: { editor: Editor; rules: InputRule[] }): Plugin {\n const { editor, rules } = props\n const plugin = new Plugin({\n state: {\n init() {\n return null\n },\n apply(tr, prev, state) {\n const stored = tr.getMeta(plugin)\n\n if (stored) {\n return stored\n }\n\n // if InputRule is triggered by insertContent()\n const simulatedInputMeta = tr.getMeta('applyInputRules') as\n | undefined\n | {\n from: number\n text: string | ProseMirrorNode | Fragment\n }\n const isSimulatedInput = !!simulatedInputMeta\n\n if (isSimulatedInput) {\n setTimeout(() => {\n let { text } = simulatedInputMeta\n\n if (typeof text === 'string') {\n text = text as string\n } else {\n text = getHTMLFromFragment(Fragment.from(text), state.schema)\n }\n\n const { from } = simulatedInputMeta\n const to = from + text.length\n\n run({\n editor,\n from,\n to,\n text,\n rules,\n plugin,\n })\n })\n }\n\n return tr.selectionSet || tr.docChanged ? null : prev\n },\n },\n\n props: {\n handleTextInput(view, from, to, text) {\n return run({\n editor,\n from,\n to,\n text,\n rules,\n plugin,\n })\n },\n\n handleDOMEvents: {\n compositionend: view => {\n setTimeout(() => {\n const { $cursor } = view.state.selection as TextSelection\n\n if ($cursor) {\n run({\n editor,\n from: $cursor.pos,\n to: $cursor.pos,\n text: '',\n rules,\n plugin,\n })\n }\n })\n\n return false\n },\n },\n\n // add support for input rules to trigger on enter\n // this is useful for example for code blocks\n handleKeyDown(view, event) {\n if (event.key !== 'Enter') {\n return false\n }\n\n const { $cursor } = view.state.selection as TextSelection\n\n if ($cursor) {\n return run({\n editor,\n from: $cursor.pos,\n to: $cursor.pos,\n text: '\\n',\n rules,\n plugin,\n })\n }\n\n return false\n },\n },\n\n // @ts-ignore\n isInputRules: true,\n }) as Plugin\n\n return plugin\n}\n","// see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts\n\nfunction getType(value: any): string {\n return Object.prototype.toString.call(value).slice(8, -1)\n}\n\nexport function isPlainObject(value: any): value is Record<string, any> {\n if (getType(value) !== 'Object') {\n return false\n }\n\n return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype\n}\n","import { isPlainObject } from './isPlainObject.js'\n\nexport function mergeDeep(target: Record<string, any>, source: Record<string, any>): Record<string, any> {\n const output = { ...target }\n\n if (isPlainObject(target) && isPlainObject(source)) {\n Object.keys(source).forEach(key => {\n if (isPlainObject(source[key]) && isPlainObject(target[key])) {\n output[key] = mergeDeep(target[key], source[key])\n } else {\n output[key] = source[key]\n }\n })\n }\n\n return output\n}\n","import type { Plugin } from '@tiptap/pm/state'\n\nimport type { Editor } from './Editor.js'\nimport { getExtensionField } from './helpers/getExtensionField.js'\nimport type { ExtensionConfig, MarkConfig, NodeConfig } from './index.js'\nimport type { InputRule } from './InputRule.js'\nimport type { Mark } from './Mark.js'\nimport type { Node } from './Node.js'\nimport type { PasteRule } from './PasteRule.js'\nimport type {\n AnyConfig,\n DispatchTransactionProps,\n EditorEvents,\n Extensions,\n GlobalAttributes,\n JSONContent,\n KeyboardShortcutCommand,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownRendererHelpers,\n MarkdownToken,\n MarkdownTokenizer,\n ParentConfig,\n RawCommands,\n RenderContext,\n} from './types.js'\nimport { callOrReturn } from './utilities/callOrReturn.js'\nimport { mergeDeep } from './utilities/mergeDeep.js'\n\nexport interface ExtendableConfig<\n Options = any,\n Storage = any,\n Config extends\n | ExtensionConfig<Options, Storage>\n | NodeConfig<Options, Storage>\n | MarkConfig<Options, Storage>\n | ExtendableConfig<Options, Storage> = ExtendableConfig<Options, Storage, any, any>,\n PMType = any,\n> {\n /**\n * The extension name - this must be unique.\n * It will be used to identify the extension.\n *\n * @example 'myExtension'\n */\n name: string\n\n /**\n * The priority of your extension. The higher, the earlier it will be called\n * and will take precedence over other extensions with a lower priority.\n * @default 100\n * @example 101\n */\n priority?: number\n\n /**\n * This method will add options to this extension\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#settings\n * @example\n * addOptions() {\n * return {\n * myOption: 'foo',\n * myOtherOption: 10,\n * }\n */\n addOptions?: (this: { name: string; parent: ParentConfig<Config>['addOptions'] }) => Options\n\n /**\n * The default storage this extension can save data to.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#storage\n * @example\n * defaultStorage: {\n * prefetchedUsers: [],\n * loading: false,\n * }\n */\n addStorage?: (this: { name: string; options: Options; parent: ParentConfig<Config>['addStorage'] }) => Storage\n\n /**\n * This function adds globalAttributes to specific nodes.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#global-attributes\n * @example\n * addGlobalAttributes() {\n * return [\n * {\n // Extend the following extensions\n * types: [\n * 'heading',\n * 'paragraph',\n * ],\n * // … with those attributes\n * attributes: {\n * textAlign: {\n * default: 'left',\n * renderHTML: attributes => ({\n * style: `text-align: ${attributes.textAlign}`,\n * }),\n * parseHTML: element => element.style.textAlign || 'left',\n * },\n * },\n * },\n * ]\n * }\n */\n addGlobalAttributes?: (this: {\n name: string\n options: Options\n storage: Storage\n extensions: (Node | Mark)[]\n parent: ParentConfig<Config>['addGlobalAttributes']\n }) => GlobalAttributes\n\n /**\n * This function adds commands to the editor\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#commands\n * @example\n * addCommands() {\n * return {\n * myCommand: () => ({ chain }) => chain().setMark('type', 'foo').run(),\n * }\n * }\n */\n addCommands?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addCommands']\n }) => Partial<RawCommands>\n\n /**\n * This function registers keyboard shortcuts.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#keyboard-shortcuts\n * @example\n * addKeyboardShortcuts() {\n * return {\n * 'Mod-l': () => this.editor.commands.toggleBulletList(),\n * }\n * },\n */\n addKeyboardShortcuts?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addKeyboardShortcuts']\n }) => {\n [key: string]: KeyboardShortcutCommand\n }\n\n /**\n * This function adds input rules to the editor.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#input-rules\n * @example\n * addInputRules() {\n * return [\n * markInputRule({\n * find: inputRegex,\n * type: this.type,\n * }),\n * ]\n * },\n */\n addInputRules?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addInputRules']\n }) => InputRule[]\n\n /**\n * This function adds paste rules to the editor.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#paste-rules\n * @example\n * addPasteRules() {\n * return [\n * markPasteRule({\n * find: pasteRegex,\n * type: this.type,\n * }),\n * ]\n * },\n */\n addPasteRules?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addPasteRules']\n }) => PasteRule[]\n\n /**\n * This function adds Prosemirror plugins to the editor\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#prosemirror-plugins\n * @example\n * addProseMirrorPlugins() {\n * return [\n * customPlugin(),\n * ]\n * }\n */\n addProseMirrorPlugins?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addProseMirrorPlugins']\n }) => Plugin[]\n\n /**\n * This function adds additional extensions to the editor. This is useful for\n * building extension kits.\n * @example\n * addExtensions() {\n * return [\n * BulletList,\n * OrderedList,\n * ListItem\n * ]\n * }\n */\n addExtensions?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<Config>['addExtensions']\n }) => Extensions\n\n /**\n * The markdown token name\n *\n * This is the name of the token that this extension uses to parse and render markdown and comes from the Marked Lexer.\n *\n * @see https://github.com/markedjs/marked/blob/master/src/Tokens.ts\n *\n */\n markdownTokenName?: string\n\n /**\n * The parse function used by the markdown parser to convert markdown tokens to ProseMirror nodes.\n */\n parseMarkdown?: (token: MarkdownToken, helpers: MarkdownParseHelpers) => MarkdownParseResult\n\n /**\n * The serializer function used by the markdown serializer to convert ProseMirror nodes to markdown tokens.\n */\n renderMarkdown?: (node: JSONContent, helpers: MarkdownRendererHelpers, ctx: RenderContext) => string\n\n /**\n * The markdown tokenizer responsible for turning a markdown string into tokens\n *\n * Custom tokenizers are only needed when you want to parse non-standard markdown token.\n */\n markdownTokenizer?: MarkdownTokenizer\n\n /**\n * Optional markdown options for indentation\n */\n markdownOptions?: {\n /**\n * Defines if this markdown element should indent it's child elements\n */\n indentsContent?: boolean\n }\n\n /**\n * This function extends the schema of the node.\n * @example\n * extendNodeSchema() {\n * return {\n * group: 'inline',\n * selectable: false,\n * }\n * }\n */\n extendNodeSchema?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<Config>['extendNodeSchema']\n },\n extension: Node,\n ) => Record<string, any>)\n | null\n\n /**\n * This function extends the schema of the mark.\n * @example\n * extendMarkSchema() {\n * return {\n * group: 'inline',\n * selectable: false,\n * }\n * }\n */\n extendMarkSchema?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<Config>['extendMarkSchema']\n },\n extension: Mark,\n ) => Record<string, any>)\n | null\n\n /**\n * The editor is not ready yet.\n */\n onBeforeCreate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onBeforeCreate']\n },\n event: EditorEvents['beforeCreate'],\n ) => void)\n | null\n\n /**\n * The editor is ready.\n */\n onCreate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onCreate']\n },\n event: EditorEvents['create'],\n ) => void)\n | null\n\n /**\n * The content has changed.\n */\n onUpdate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onUpdate']\n },\n event: EditorEvents['update'],\n ) => void)\n | null\n\n /**\n * The selection has changed.\n */\n onSelectionUpdate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onSelectionUpdate']\n },\n event: EditorEvents['selectionUpdate'],\n ) => void)\n | null\n\n /**\n * The editor state has changed.\n */\n onTransaction?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onTransaction']\n },\n event: EditorEvents['transaction'],\n ) => void)\n | null\n\n /**\n * The editor is focused.\n */\n onFocus?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onFocus']\n },\n event: EditorEvents['focus'],\n ) => void)\n | null\n\n /**\n * The editor isn’t focused anymore.\n */\n onBlur?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onBlur']\n },\n event: EditorEvents['blur'],\n ) => void)\n | null\n\n /**\n * The editor is destroyed.\n */\n onDestroy?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onDestroy']\n },\n event: EditorEvents['destroy'],\n ) => void)\n | null\n\n /**\n * This hook allows you to intercept and modify transactions before they are dispatched.\n *\n * Example\n * ```ts\n * dispatchTransaction({ transaction, next }) {\n * console.log('Dispatching transaction:', transaction)\n * next(transaction)\n * }\n * ```\n *\n * @param props - The dispatch transaction props\n */\n dispatchTransaction?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['dispatchTransaction']\n },\n props: DispatchTransactionProps,\n ) => void)\n | null\n}\n\nexport class Extendable<\n Options = any,\n Storage = any,\n Config = ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage>,\n> {\n type = 'extendable'\n parent: Extendable | null = null\n\n child: Extendable | null = null\n\n name = ''\n\n config: Config = {\n name: this.name,\n } as Config\n\n constructor(config: Partial<Config> = {}) {\n this.config = {\n ...this.config,\n ...config,\n }\n\n this.name = (this.config as any).name\n }\n\n get options(): Options {\n return {\n ...(callOrReturn(\n getExtensionField<AnyConfig['addOptions']>(this as any, 'addOptions', {\n name: this.name,\n }),\n ) || {}),\n }\n }\n\n get storage(): Readonly<Storage> {\n return {\n ...(callOrReturn(\n getExtensionField<AnyConfig['addStorage']>(this as any, 'addStorage', {\n name: this.name,\n options: this.options,\n }),\n ) || {}),\n }\n }\n\n configure(options: Partial<Options> = {}) {\n const extension = this.extend<Options, Storage, Config>({\n ...this.config,\n addOptions: () => {\n return mergeDeep(this.options as Record<string, any>, options) as Options\n },\n })\n\n extension.name = this.name\n extension.parent = this.parent\n\n return extension\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig =\n | ExtensionConfig<ExtendedOptions, ExtendedStorage>\n | NodeConfig<ExtendedOptions, ExtendedStorage>\n | MarkConfig<ExtendedOptions, ExtendedStorage>,\n >(extendedConfig: Partial<ExtendedConfig> = {}): Extendable<ExtendedOptions, ExtendedStorage> {\n const extension = new (this.constructor as any)({ ...this.config, ...extendedConfig })\n\n extension.parent = this\n this.child = extension\n extension.name = 'name' in extendedConfig ? extendedConfig.name : extension.parent.name\n\n return extension\n }\n}\n","import type { DOMOutputSpec, Mark as ProseMirrorMark, MarkSpec, MarkType } from '@tiptap/pm/model'\n\nimport type { Editor } from './Editor.js'\nimport type { ExtendableConfig } from './Extendable.js'\nimport { Extendable } from './Extendable.js'\nimport type { Attributes, MarkViewRenderer, ParentConfig } from './types.js'\n\nexport interface MarkConfig<Options = any, Storage = any>\n extends ExtendableConfig<Options, Storage, MarkConfig<Options, Storage>, MarkType> {\n /**\n * Mark View\n */\n addMarkView?:\n | ((this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: MarkType\n parent: ParentConfig<MarkConfig<Options, Storage>>['addMarkView']\n }) => MarkViewRenderer)\n | null\n\n /**\n * Keep mark after split node\n */\n keepOnSplit?: boolean | (() => boolean)\n\n /**\n * Inclusive\n */\n inclusive?:\n | MarkSpec['inclusive']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive']\n editor?: Editor\n }) => MarkSpec['inclusive'])\n\n /**\n * Excludes\n */\n excludes?:\n | MarkSpec['excludes']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['excludes']\n editor?: Editor\n }) => MarkSpec['excludes'])\n\n /**\n * Marks this Mark as exitable\n */\n exitable?: boolean | (() => boolean)\n\n /**\n * Group\n */\n group?:\n | MarkSpec['group']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['group']\n editor?: Editor\n }) => MarkSpec['group'])\n\n /**\n * Spanning\n */\n spanning?:\n | MarkSpec['spanning']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['spanning']\n editor?: Editor\n }) => MarkSpec['spanning'])\n\n /**\n * Code\n */\n code?:\n | boolean\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['code']\n editor?: Editor\n }) => boolean)\n\n /**\n * Parse HTML\n */\n parseHTML?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML']\n editor?: Editor\n }) => MarkSpec['parseDOM']\n\n /**\n * Render HTML\n */\n renderHTML?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML']\n editor?: Editor\n },\n props: {\n mark: ProseMirrorMark\n HTMLAttributes: Record<string, any>\n },\n ) => DOMOutputSpec)\n | null\n\n /**\n * Attributes\n */\n addAttributes?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes']\n editor?: Editor\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n }) => Attributes | {}\n}\n\n/**\n * The Mark class is used to create custom mark extensions.\n * @see https://tiptap.dev/api/extensions#create-a-new-extension\n */\nexport class Mark<Options = any, Storage = any> extends Extendable<Options, Storage, MarkConfig<Options, Storage>> {\n type = 'mark'\n\n /**\n * Create a new Mark instance\n * @param config - Mark configuration object or a function that returns a configuration object\n */\n static create<O = any, S = any>(config: Partial<MarkConfig<O, S>> | (() => Partial<MarkConfig<O, S>>) = {}) {\n // If the config is a function, execute it to get the configuration object\n const resolvedConfig = typeof config === 'function' ? config() : config\n return new Mark<O, S>(resolvedConfig)\n }\n\n static handleExit({ editor, mark }: { editor: Editor; mark: Mark }) {\n const { tr } = editor.state\n const currentPos = editor.state.selection.$from\n const isAtEnd = currentPos.pos === currentPos.end()\n\n if (isAtEnd) {\n const currentMarks = currentPos.marks()\n const isInMark = !!currentMarks.find(m => m?.type.name === mark.name)\n\n if (!isInMark) {\n return false\n }\n\n const removeMark = currentMarks.find(m => m?.type.name === mark.name)\n\n if (removeMark) {\n tr.removeStoredMark(removeMark)\n }\n tr.insertText(' ', currentPos.pos)\n\n editor.view.dispatch(tr)\n\n return true\n }\n\n return false\n }\n\n configure(options?: Partial<Options>) {\n return super.configure(options) as Mark<Options, Storage>\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig extends MarkConfig<ExtendedOptions, ExtendedStorage> = MarkConfig<ExtendedOptions, ExtendedStorage>,\n >(\n extendedConfig?:\n | (() => Partial<ExtendedConfig>)\n | (Partial<ExtendedConfig> &\n ThisType<{\n name: string\n options: ExtendedOptions\n storage: ExtendedStorage\n editor: Editor\n type: MarkType\n }>),\n ): Mark<ExtendedOptions, ExtendedStorage> {\n // If the extended config is a function, execute it to get the configuration object\n const resolvedConfig = typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig\n return super.extend(resolvedConfig) as Mark<ExtendedOptions, ExtendedStorage>\n }\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Fragment } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\nimport { Plugin } from '@tiptap/pm/state'\n\nimport { CommandManager } from './CommandManager.js'\nimport type { Editor } from './Editor.js'\nimport { createChainableState } from './helpers/createChainableState.js'\nimport { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'\nimport type { CanCommands, ChainedCommands, ExtendedRegExpMatchArray, Range, SingleCommands } from './types.js'\nimport { isNumber } from './utilities/isNumber.js'\nimport { isRegExp } from './utilities/isRegExp.js'\n\nexport type PasteRuleMatch = {\n index: number\n text: string\n replaceWith?: string\n match?: RegExpMatchArray\n data?: Record<string, any>\n}\n\nexport type PasteRuleFinder =\n | RegExp\n | ((text: string, event?: ClipboardEvent | null) => PasteRuleMatch[] | null | undefined)\n\n/**\n * Paste rules are used to react to pasted content.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules\n */\nexport class PasteRule {\n find: PasteRuleFinder\n\n handler: (props: {\n state: EditorState\n range: Range\n match: ExtendedRegExpMatchArray\n commands: SingleCommands\n chain: () => ChainedCommands\n can: () => CanCommands\n pasteEvent: ClipboardEvent | null\n dropEvent: DragEvent | null\n }) => void | null\n\n constructor(config: {\n find: PasteRuleFinder\n handler: (props: {\n can: () => CanCommands\n chain: () => ChainedCommands\n commands: SingleCommands\n dropEvent: DragEvent | null\n match: ExtendedRegExpMatchArray\n pasteEvent: ClipboardEvent | null\n range: Range\n state: EditorState\n }) => void | null\n }) {\n this.find = config.find\n this.handler = config.handler\n }\n}\n\nconst pasteRuleMatcherHandler = (\n text: string,\n find: PasteRuleFinder,\n event?: ClipboardEvent | null,\n): ExtendedRegExpMatchArray[] => {\n if (isRegExp(find)) {\n return [...text.matchAll(find)]\n }\n\n const matches = find(text, event)\n\n if (!matches) {\n return []\n }\n\n return matches.map(pasteRuleMatch => {\n const result: ExtendedRegExpMatchArray = [pasteRuleMatch.text]\n\n result.index = pasteRuleMatch.index\n result.input = text\n result.data = pasteRuleMatch.data\n\n if (pasteRuleMatch.replaceWith) {\n if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {\n console.warn('[tiptap warn]: \"pasteRuleMatch.replaceWith\" must be part of \"pasteRuleMatch.text\".')\n }\n\n result.push(pasteRuleMatch.replaceWith)\n }\n\n return result\n })\n}\n\nfunction run(config: {\n editor: Editor\n state: EditorState\n from: number\n to: number\n rule: PasteRule\n pasteEvent: ClipboardEvent | null\n dropEvent: DragEvent | null\n}): boolean {\n const { editor, state, from, to, rule, pasteEvent, dropEvent } = config\n\n const { commands, chain, can } = new CommandManager({\n editor,\n state,\n })\n\n const handlers: (void | null)[] = []\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n // Skip code blocks and non-textual nodes.\n // Be defensive: `node` may be a Fragment without a `type`. Only text,\n // inline, or textblock nodes are processed by paste rules.\n if (node.type?.spec?.code || !(node.isText || node.isTextblock || node.isInline)) {\n return\n }\n\n // For textblock and inline/text nodes, compute the range relative to the node.\n // Prefer `node.nodeSize` when available (some Node shapes expose this),\n // otherwise fall back to `node.content?.size`. Default to 0 if neither exists.\n const contentSize = node.content?.size ?? node.nodeSize ?? 0\n const resolvedFrom = Math.max(from, pos)\n const resolvedTo = Math.min(to, pos + contentSize)\n\n // If the resolved range is empty or invalid for this node, skip it. This\n // avoids calling `textBetween` with start > end which can cause internal\n // Fragment/Node traversal to access undefined `nodeSize` values.\n if (resolvedFrom >= resolvedTo) {\n return\n }\n\n const textToMatch = node.isText\n ? node.text || ''\n : node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\\ufffc')\n\n const matches = pasteRuleMatcherHandler(textToMatch, rule.find, pasteEvent)\n\n matches.forEach(match => {\n if (match.index === undefined) {\n return\n }\n\n const start = resolvedFrom + match.index + 1\n const end = start + match[0].length\n const range = {\n from: state.tr.mapping.map(start),\n to: state.tr.mapping.map(end),\n }\n\n const handler = rule.handler({\n state,\n range,\n match,\n commands,\n chain,\n can,\n pasteEvent,\n dropEvent,\n })\n\n handlers.push(handler)\n })\n })\n\n const success = handlers.every(handler => handler !== null)\n\n return success\n}\n\n// When dragging across editors, must get another editor instance to delete selection content.\nlet tiptapDragFromOtherEditor: Editor | null = null\n\nconst createClipboardPasteEvent = (text: string) => {\n const event = new ClipboardEvent('paste', {\n clipboardData: new DataTransfer(),\n })\n\n event.clipboardData?.setData('text/html', text)\n\n return event\n}\n\n/**\n * Create an paste rules plugin. When enabled, it will cause pasted\n * text that matches any of the given rules to trigger the rule’s\n * action.\n */\nexport function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }): Plugin[] {\n const { editor, rules } = props\n let dragSourceElement: Element | null = null\n let isPastedFromProseMirror = false\n let isDroppedFromProseMirror = false\n let pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null\n let dropEvent: DragEvent | null\n\n try {\n dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null\n } catch {\n dropEvent = null\n }\n\n const processEvent = ({\n state,\n from,\n to,\n rule,\n pasteEvt,\n }: {\n state: EditorState\n from: number\n to: { b: number }\n rule: PasteRule\n pasteEvt: ClipboardEvent | null\n }) => {\n const tr = state.tr\n const chainableState = createChainableState({\n state,\n transaction: tr,\n })\n\n const handler = run({\n editor,\n state: chainableState,\n from: Math.max(from - 1, 0),\n to: to.b - 1,\n rule,\n pasteEvent: pasteEvt,\n dropEvent,\n })\n\n if (!handler || !tr.steps.length) {\n return\n }\n\n try {\n dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null\n } catch {\n dropEvent = null\n }\n pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null\n\n return tr\n }\n\n const plugins = rules.map(rule => {\n return new Plugin({\n // we register a global drag handler to track the current drag source element\n view(view) {\n const handleDragstart = (event: DragEvent) => {\n dragSourceElement = view.dom.parentElement?.contains(event.target as Element) ? view.dom.parentElement : null\n\n if (dragSourceElement) {\n tiptapDragFromOtherEditor = editor\n }\n }\n\n const handleDragend = () => {\n if (tiptapDragFromOtherEditor) {\n tiptapDragFromOtherEditor = null\n }\n }\n\n window.addEventListener('dragstart', handleDragstart)\n window.addEventListener('dragend', handleDragend)\n\n return {\n destroy() {\n window.removeEventListener('dragstart', handleDragstart)\n window.removeEventListener('dragend', handleDragend)\n },\n }\n },\n\n props: {\n handleDOMEvents: {\n drop: (view, event: Event) => {\n isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement\n dropEvent = event as DragEvent\n\n if (!isDroppedFromProseMirror) {\n const dragFromOtherEditor = tiptapDragFromOtherEditor\n\n if (dragFromOtherEditor?.isEditable) {\n // setTimeout to avoid the wrong content after drop, timeout arg can't be empty or 0\n setTimeout(() => {\n const selection = dragFromOtherEditor.state.selection\n\n if (selection) {\n dragFromOtherEditor.commands.deleteRange({ from: selection.from, to: selection.to })\n }\n }, 10)\n }\n }\n return false\n },\n\n paste: (_view, event: Event) => {\n const html = (event as ClipboardEvent).clipboardData?.getData('text/html')\n\n pasteEvent = event as ClipboardEvent\n\n isPastedFromProseMirror = !!html?.includes('data-pm-slice')\n\n return false\n },\n },\n },\n\n appendTransaction: (transactions, oldState, state) => {\n const transaction = transactions[0]\n const isPaste = transaction.getMeta('uiEvent') === 'paste' && !isPastedFromProseMirror\n const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror\n\n // if PasteRule is triggered by insertContent()\n const simulatedPasteMeta = transaction.getMeta('applyPasteRules') as\n | undefined\n | { from: number; text: string | ProseMirrorNode | Fragment }\n const isSimulatedPaste = !!simulatedPasteMeta\n\n if (!isPaste && !isDrop && !isSimulatedPaste) {\n return\n }\n\n // Handle simulated paste\n if (isSimulatedPaste) {\n let { text } = simulatedPasteMeta\n\n if (typeof text === 'string') {\n text = text as string\n } else {\n text = getHTMLFromFragment(Fragment.from(text), state.schema)\n }\n\n const { from } = simulatedPasteMeta\n const to = from + text.length\n\n const pasteEvt = createClipboardPasteEvent(text)\n\n return processEvent({\n rule,\n state,\n from,\n to: { b: to },\n pasteEvt,\n })\n }\n\n // handle actual paste/drop\n const from = oldState.doc.content.findDiffStart(state.doc.content)\n const to = oldState.doc.content.findDiffEnd(state.doc.content)\n\n // stop if there is no changed range\n if (!isNumber(from) || !to || from === to.b) {\n return\n }\n\n return processEvent({\n rule,\n state,\n from,\n to,\n pasteEvt: pasteEvent,\n })\n },\n })\n })\n\n return plugins\n}\n","export function isNumber(value: any): value is number {\n return typeof value === 'number'\n}\n","export { ClipboardTextSerializer } from './clipboardTextSerializer.js'\nexport { Commands } from './commands.js'\nexport { Delete } from './delete.js'\nexport { Drop } from './drop.js'\nexport { Editable } from './editable.js'\nexport { FocusEvents, focusEventsPluginKey } from './focusEvents.js'\nexport { Keymap } from './keymap.js'\nexport { Paste } from './paste.js'\nexport { Tabindex } from './tabindex.js'\nexport { TextDirection } from './textDirection.js'\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\nimport { getTextBetween } from '../helpers/getTextBetween.js'\nimport { getTextSerializersFromSchema } from '../helpers/getTextSerializersFromSchema.js'\n\nexport type ClipboardTextSerializerOptions = {\n blockSeparator?: string\n}\n\nexport const ClipboardTextSerializer = Extension.create<ClipboardTextSerializerOptions>({\n name: 'clipboardTextSerializer',\n\n addOptions() {\n return {\n blockSeparator: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('clipboardTextSerializer'),\n props: {\n clipboardTextSerializer: () => {\n const { editor } = this\n const { state, schema } = editor\n const { doc, selection } = state\n const { ranges } = selection\n const from = Math.min(...ranges.map(range => range.$from.pos))\n const to = Math.max(...ranges.map(range => range.$to.pos))\n const textSerializers = getTextSerializersFromSchema(schema)\n const range = { from, to }\n\n return getTextBetween(doc, range, {\n ...(this.options.blockSeparator !== undefined ? { blockSeparator: this.options.blockSeparator } : {}),\n textSerializers,\n })\n },\n },\n }),\n ]\n },\n})\n","import type { Editor } from './Editor.js'\nimport { type ExtendableConfig, Extendable } from './Extendable.js'\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ExtensionConfig<Options = any, Storage = any>\n extends ExtendableConfig<Options, Storage, ExtensionConfig<Options, Storage>, null> {}\n\n/**\n * The Extension class is the base class for all extensions.\n * @see https://tiptap.dev/api/extensions#create-a-new-extension\n */\nexport class Extension<Options = any, Storage = any> extends Extendable<\n Options,\n Storage,\n ExtensionConfig<Options, Storage>\n> {\n type = 'extension'\n\n /**\n * Create a new Extension instance\n * @param config - Extension configuration object or a function that returns a configuration object\n */\n static create<O = any, S = any>(\n config: Partial<ExtensionConfig<O, S>> | (() => Partial<ExtensionConfig<O, S>>) = {},\n ) {\n // If the config is a function, execute it to get the configuration object\n const resolvedConfig = typeof config === 'function' ? config() : config\n return new Extension<O, S>(resolvedConfig)\n }\n\n configure(options?: Partial<Options>) {\n return super.configure(options) as Extension<Options, Storage>\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig = ExtensionConfig<ExtendedOptions, ExtendedStorage>,\n >(\n extendedConfig?:\n | (() => Partial<ExtendedConfig>)\n | (Partial<ExtendedConfig> &\n ThisType<{\n name: string\n options: ExtendedOptions\n storage: ExtendedStorage\n editor: Editor\n type: null\n }>),\n ): Extension<ExtendedOptions, ExtendedStorage> {\n // If the extended config is a function, execute it to get the configuration object\n const resolvedConfig = typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig\n return super.extend(resolvedConfig) as Extension<ExtendedOptions, ExtendedStorage>\n }\n}\n","import * as commands from '../commands/index.js'\nimport { Extension } from '../Extension.js'\n\nexport * from '../commands/index.js'\n\nexport const Commands = Extension.create({\n name: 'commands',\n\n addCommands() {\n return {\n ...commands,\n }\n },\n})\n","import { RemoveMarkStep } from '@tiptap/pm/transform'\n\nimport { Extension } from '../Extension.js'\nimport { combineTransactionSteps, getChangedRanges } from '../helpers/index.js'\n\n/**\n * This extension allows you to be notified when the user deletes content you are interested in.\n */\nexport const Delete = Extension.create({\n name: 'delete',\n\n onUpdate({ transaction, appendedTransactions }) {\n const callback = () => {\n if (\n this.editor.options.coreExtensionOptions?.delete?.filterTransaction?.(transaction) ??\n transaction.getMeta('y-sync$')\n ) {\n return\n }\n const nextTransaction = combineTransactionSteps(transaction.before, [transaction, ...appendedTransactions])\n const changes = getChangedRanges(nextTransaction)\n\n changes.forEach(change => {\n if (\n nextTransaction.mapping.mapResult(change.oldRange.from).deletedAfter &&\n nextTransaction.mapping.mapResult(change.oldRange.to).deletedBefore\n ) {\n nextTransaction.before.nodesBetween(change.oldRange.from, change.oldRange.to, (node, from) => {\n const to = from + node.nodeSize - 2\n const isFullyWithinRange = change.oldRange.from <= from && to <= change.oldRange.to\n\n this.editor.emit('delete', {\n type: 'node',\n node,\n from,\n to,\n newFrom: nextTransaction.mapping.map(from),\n newTo: nextTransaction.mapping.map(to),\n deletedRange: change.oldRange,\n newRange: change.newRange,\n partial: !isFullyWithinRange,\n editor: this.editor,\n transaction,\n combinedTransform: nextTransaction,\n })\n })\n }\n })\n\n const mapping = nextTransaction.mapping\n nextTransaction.steps.forEach((step, index) => {\n if (step instanceof RemoveMarkStep) {\n const newStart = mapping.slice(index).map(step.from, -1)\n const newEnd = mapping.slice(index).map(step.to)\n const oldStart = mapping.invert().map(newStart, -1)\n const oldEnd = mapping.invert().map(newEnd)\n\n const foundBeforeMark = nextTransaction.doc.nodeAt(newStart - 1)?.marks.some(mark => mark.eq(step.mark))\n const foundAfterMark = nextTransaction.doc.nodeAt(newEnd)?.marks.some(mark => mark.eq(step.mark))\n\n this.editor.emit('delete', {\n type: 'mark',\n mark: step.mark,\n from: step.from,\n to: step.to,\n deletedRange: {\n from: oldStart,\n to: oldEnd,\n },\n newRange: {\n from: newStart,\n to: newEnd,\n },\n partial: Boolean(foundAfterMark || foundBeforeMark),\n editor: this.editor,\n transaction,\n combinedTransform: nextTransaction,\n })\n }\n })\n }\n\n if (this.editor.options.coreExtensionOptions?.delete?.async ?? true) {\n setTimeout(callback, 0)\n } else {\n callback()\n }\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Drop = Extension.create({\n name: 'drop',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('tiptapDrop'),\n\n props: {\n handleDrop: (_, e, slice, moved) => {\n this.editor.emit('drop', {\n editor: this.editor,\n event: e,\n slice,\n moved,\n })\n },\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Editable = Extension.create({\n name: 'editable',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('editable'),\n props: {\n editable: () => this.editor.options.editable,\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const focusEventsPluginKey = new PluginKey('focusEvents')\n\nexport const FocusEvents = Extension.create({\n name: 'focusEvents',\n\n addProseMirrorPlugins() {\n const { editor } = this\n\n return [\n new Plugin({\n key: focusEventsPluginKey,\n props: {\n handleDOMEvents: {\n focus: (view, event: Event) => {\n editor.isFocused = true\n\n const transaction = editor.state.tr.setMeta('focus', { event }).setMeta('addToHistory', false)\n\n view.dispatch(transaction)\n\n return false\n },\n blur: (view, event: Event) => {\n editor.isFocused = false\n\n const transaction = editor.state.tr.setMeta('blur', { event }).setMeta('addToHistory', false)\n\n view.dispatch(transaction)\n\n return false\n },\n },\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey, Selection } from '@tiptap/pm/state'\n\nimport { CommandManager } from '../CommandManager.js'\nimport { Extension } from '../Extension.js'\nimport { createChainableState } from '../helpers/createChainableState.js'\nimport { isNodeEmpty } from '../helpers/isNodeEmpty.js'\nimport { isiOS } from '../utilities/isiOS.js'\nimport { isMacOS } from '../utilities/isMacOS.js'\n\nexport const Keymap = Extension.create({\n name: 'keymap',\n\n addKeyboardShortcuts() {\n const handleBackspace = () =>\n this.editor.commands.first(({ commands }) => [\n () => commands.undoInputRule(),\n\n // maybe convert first text block node to default node\n () =>\n commands.command(({ tr }) => {\n const { selection, doc } = tr\n const { empty, $anchor } = selection\n const { pos, parent } = $anchor\n const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor\n const parentIsIsolating = $parentPos.parent.type.spec.isolating\n\n const parentPos = $anchor.pos - $anchor.parentOffset\n\n const isAtStart =\n parentIsIsolating && $parentPos.parent.childCount === 1\n ? parentPos === $anchor.pos\n : Selection.atStart(doc).from === pos\n\n if (\n !empty ||\n !parent.type.isTextblock ||\n parent.textContent.length ||\n !isAtStart ||\n (isAtStart && $anchor.parent.type.name === 'paragraph') // prevent clearNodes when no nodes to clear, otherwise history stack is appended\n ) {\n return false\n }\n\n return commands.clearNodes()\n }),\n\n () => commands.deleteSelection(),\n () => commands.joinBackward(),\n () => commands.selectNodeBackward(),\n ])\n\n const handleDelete = () =>\n this.editor.commands.first(({ commands }) => [\n () => commands.deleteSelection(),\n () => commands.deleteCurrentNode(),\n () => commands.joinForward(),\n () => commands.selectNodeForward(),\n ])\n\n const handleEnter = () =>\n this.editor.commands.first(({ commands }) => [\n () => commands.newlineInCode(),\n () => commands.createParagraphNear(),\n () => commands.liftEmptyBlock(),\n () => commands.splitBlock(),\n ])\n\n const baseKeymap = {\n Enter: handleEnter,\n 'Mod-Enter': () => this.editor.commands.exitCode(),\n Backspace: handleBackspace,\n 'Mod-Backspace': handleBackspace,\n 'Shift-Backspace': handleBackspace,\n Delete: handleDelete,\n 'Mod-Delete': handleDelete,\n 'Mod-a': () => this.editor.commands.selectAll(),\n }\n\n const pcKeymap = {\n ...baseKeymap,\n }\n\n const macKeymap = {\n ...baseKeymap,\n 'Ctrl-h': handleBackspace,\n 'Alt-Backspace': handleBackspace,\n 'Ctrl-d': handleDelete,\n 'Ctrl-Alt-Backspace': handleDelete,\n 'Alt-Delete': handleDelete,\n 'Alt-d': handleDelete,\n 'Ctrl-a': () => this.editor.commands.selectTextblockStart(),\n 'Ctrl-e': () => this.editor.commands.selectTextblockEnd(),\n }\n\n if (isiOS() || isMacOS()) {\n return macKeymap\n }\n\n return pcKeymap\n },\n\n addProseMirrorPlugins() {\n return [\n // With this plugin we check if the whole document was selected and deleted.\n // In this case we will additionally call `clearNodes()` to convert e.g. a heading\n // to a paragraph if necessary.\n // This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well\n // with many other commands.\n new Plugin({\n key: new PluginKey('clearDocument'),\n appendTransaction: (transactions, oldState, newState) => {\n if (transactions.some(tr => tr.getMeta('composition'))) {\n return\n }\n\n const docChanges = transactions.some(transaction => transaction.docChanged) && !oldState.doc.eq(newState.doc)\n\n const ignoreTr = transactions.some(transaction => transaction.getMeta('preventClearDocument'))\n\n if (!docChanges || ignoreTr) {\n return\n }\n\n const { empty, from, to } = oldState.selection\n const allFrom = Selection.atStart(oldState.doc).from\n const allEnd = Selection.atEnd(oldState.doc).to\n const allWasSelected = from === allFrom && to === allEnd\n\n if (empty || !allWasSelected) {\n return\n }\n\n const isEmpty = isNodeEmpty(newState.doc)\n\n if (!isEmpty) {\n return\n }\n\n const tr = newState.tr\n const state = createChainableState({\n state: newState,\n transaction: tr,\n })\n const { commands } = new CommandManager({\n editor: this.editor,\n state,\n })\n\n commands.clearNodes()\n\n if (!tr.steps.length) {\n return\n }\n\n return tr\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Paste = Extension.create({\n name: 'paste',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('tiptapPaste'),\n\n props: {\n handlePaste: (_view, e, slice) => {\n this.editor.emit('paste', {\n editor: this.editor,\n event: e,\n slice,\n })\n },\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Tabindex = Extension.create({\n name: 'tabindex',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('tabindex'),\n props: {\n attributes: (): { [name: string]: string } => (this.editor.isEditable ? { tabindex: '0' } : {}),\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\nimport { splitExtensions } from '../helpers/splitExtensions.js'\n\nexport interface TextDirectionOptions {\n direction: 'ltr' | 'rtl' | 'auto' | undefined\n}\n\n/**\n * The TextDirection extension adds support for setting text direction (LTR/RTL/auto)\n * on all nodes in the editor.\n *\n * This extension adds a global `dir` attribute to all node types, which can be used\n * to control bidirectional text rendering. The direction can be set globally via\n * editor options or per-node using commands.\n */\nexport const TextDirection = Extension.create<TextDirectionOptions>({\n name: 'textDirection',\n\n addOptions() {\n return {\n direction: undefined,\n }\n },\n\n addGlobalAttributes() {\n // Only add the dir attribute to nodes if text direction is configured\n // This prevents null/undefined values from appearing in JSON exports\n if (!this.options.direction) {\n return []\n }\n\n const { nodeExtensions } = splitExtensions(this.extensions)\n\n return [\n {\n types: nodeExtensions.filter(extension => extension.name !== 'text').map(extension => extension.name),\n attributes: {\n dir: {\n default: this.options.direction,\n parseHTML: element => {\n const dir = element.getAttribute('dir')\n\n if (dir && (dir === 'ltr' || dir === 'rtl' || dir === 'auto')) {\n return dir\n }\n\n return this.options.direction\n },\n renderHTML: attributes => {\n if (!attributes.dir) {\n return {}\n }\n\n return {\n dir: attributes.dir,\n }\n },\n },\n },\n },\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('textDirection'),\n props: {\n attributes: (): { [name: string]: string } => {\n const direction = this.options.direction\n\n if (!direction) {\n return {}\n }\n\n return {\n dir: direction,\n }\n },\n },\n }),\n ]\n },\n})\n","import type { Fragment, Node, ResolvedPos } from '@tiptap/pm/model'\n\nimport type { Editor } from './Editor.js'\nimport type { Content, Range } from './types.js'\n\nexport class NodePos {\n private resolvedPos: ResolvedPos\n\n private isBlock: boolean\n\n private editor: Editor\n\n private get name(): string {\n return this.node.type.name\n }\n\n constructor(pos: ResolvedPos, editor: Editor, isBlock = false, node: Node | null = null) {\n this.isBlock = isBlock\n this.resolvedPos = pos\n this.editor = editor\n this.currentNode = node\n }\n\n private currentNode: Node | null = null\n\n get node(): Node {\n return this.currentNode || this.resolvedPos.node()\n }\n\n get element(): HTMLElement {\n return this.editor.view.domAtPos(this.pos).node as HTMLElement\n }\n\n public actualDepth: number | null = null\n\n get depth(): number {\n return this.actualDepth ?? this.resolvedPos.depth\n }\n\n get pos(): number {\n return this.resolvedPos.pos\n }\n\n get content(): Fragment {\n return this.node.content\n }\n\n set content(content: Content) {\n let from = this.from\n let to = this.to\n\n if (this.isBlock) {\n if (this.content.size === 0) {\n console.error(`You can’t set content on a block node. Tried to set content on ${this.name} at ${this.pos}`)\n return\n }\n\n from = this.from + 1\n to = this.to - 1\n }\n\n this.editor.commands.insertContentAt({ from, to }, content)\n }\n\n get attributes(): { [key: string]: any } {\n return this.node.attrs\n }\n\n get textContent(): string {\n return this.node.textContent\n }\n\n get size(): number {\n return this.node.nodeSize\n }\n\n get from(): number {\n if (this.isBlock) {\n return this.pos\n }\n\n return this.resolvedPos.start(this.resolvedPos.depth)\n }\n\n get range(): Range {\n return {\n from: this.from,\n to: this.to,\n }\n }\n\n get to(): number {\n if (this.isBlock) {\n return this.pos + this.size\n }\n\n return this.resolvedPos.end(this.resolvedPos.depth) + (this.node.isText ? 0 : 1)\n }\n\n get parent(): NodePos | null {\n if (this.depth === 0) {\n return null\n }\n\n const parentPos = this.resolvedPos.start(this.resolvedPos.depth - 1)\n const $pos = this.resolvedPos.doc.resolve(parentPos)\n\n return new NodePos($pos, this.editor)\n }\n\n get before(): NodePos | null {\n let $pos = this.resolvedPos.doc.resolve(this.from - (this.isBlock ? 1 : 2))\n\n if ($pos.depth !== this.depth) {\n $pos = this.resolvedPos.doc.resolve(this.from - 3)\n }\n\n return new NodePos($pos, this.editor)\n }\n\n get after(): NodePos | null {\n let $pos = this.resolvedPos.doc.resolve(this.to + (this.isBlock ? 2 : 1))\n\n if ($pos.depth !== this.depth) {\n $pos = this.resolvedPos.doc.resolve(this.to + 3)\n }\n\n return new NodePos($pos, this.editor)\n }\n\n get children(): NodePos[] {\n const children: NodePos[] = []\n\n this.node.content.forEach((node, offset) => {\n const isBlock = node.isBlock && !node.isTextblock\n const isNonTextAtom = node.isAtom && !node.isText\n\n const targetPos = this.pos + offset + (isNonTextAtom ? 0 : 1)\n\n // Check if targetPos is within valid document range\n if (targetPos < 0 || targetPos > this.resolvedPos.doc.nodeSize - 2) {\n return\n }\n\n const $pos = this.resolvedPos.doc.resolve(targetPos)\n\n if (!isBlock && $pos.depth <= this.depth) {\n return\n }\n\n const childNodePos = new NodePos($pos, this.editor, isBlock, isBlock ? node : null)\n\n if (isBlock) {\n childNodePos.actualDepth = this.depth + 1\n }\n\n children.push(new NodePos($pos, this.editor, isBlock, isBlock ? node : null))\n })\n\n return children\n }\n\n get firstChild(): NodePos | null {\n return this.children[0] || null\n }\n\n get lastChild(): NodePos | null {\n const children = this.children\n\n return children[children.length - 1] || null\n }\n\n closest(selector: string, attributes: { [key: string]: any } = {}): NodePos | null {\n let node: NodePos | null = null\n let currentNode = this.parent\n\n while (currentNode && !node) {\n if (currentNode.node.type.name === selector) {\n if (Object.keys(attributes).length > 0) {\n const nodeAttributes = currentNode.node.attrs\n const attrKeys = Object.keys(attributes)\n\n for (let index = 0; index < attrKeys.length; index += 1) {\n const key = attrKeys[index]\n\n if (nodeAttributes[key] !== attributes[key]) {\n break\n }\n }\n } else {\n node = currentNode\n }\n }\n\n currentNode = currentNode.parent\n }\n\n return node\n }\n\n querySelector(selector: string, attributes: { [key: string]: any } = {}): NodePos | null {\n return this.querySelectorAll(selector, attributes, true)[0] || null\n }\n\n querySelectorAll(selector: string, attributes: { [key: string]: any } = {}, firstItemOnly = false): NodePos[] {\n let nodes: NodePos[] = []\n\n if (!this.children || this.children.length === 0) {\n return nodes\n }\n const attrKeys = Object.keys(attributes)\n\n /**\n * Finds all children recursively that match the selector and attributes\n * If firstItemOnly is true, it will return the first item found\n */\n this.children.forEach(childPos => {\n // If we already found a node and we only want the first item, we dont need to keep going\n if (firstItemOnly && nodes.length > 0) {\n return\n }\n\n if (childPos.node.type.name === selector) {\n const doesAllAttributesMatch = attrKeys.every(key => attributes[key] === childPos.node.attrs[key])\n\n if (doesAllAttributesMatch) {\n nodes.push(childPos)\n }\n }\n\n // If we already found a node and we only want the first item, we can stop here and skip the recursion\n if (firstItemOnly && nodes.length > 0) {\n return\n }\n\n nodes = nodes.concat(childPos.querySelectorAll(selector, attributes, firstItemOnly))\n })\n\n return nodes\n }\n\n setAttribute(attributes: { [key: string]: any }) {\n const { tr } = this.editor.state\n\n tr.setNodeMarkup(this.from, undefined, {\n ...this.node.attrs,\n ...attributes,\n })\n\n this.editor.view.dispatch(tr)\n }\n}\n","export const style = `.ProseMirror {\n position: relative;\n}\n\n.ProseMirror {\n word-wrap: break-word;\n white-space: pre-wrap;\n white-space: break-spaces;\n -webkit-font-variant-ligatures: none;\n font-variant-ligatures: none;\n font-feature-settings: \"liga\" 0; /* the above doesn't seem to work in Edge */\n}\n\n.ProseMirror [contenteditable=\"false\"] {\n white-space: normal;\n}\n\n.ProseMirror [contenteditable=\"false\"] [contenteditable=\"true\"] {\n white-space: pre-wrap;\n}\n\n.ProseMirror pre {\n white-space: pre-wrap;\n}\n\nimg.ProseMirror-separator {\n display: inline !important;\n border: none !important;\n margin: 0 !important;\n width: 0 !important;\n height: 0 !important;\n}\n\n.ProseMirror-gapcursor {\n display: none;\n pointer-events: none;\n position: absolute;\n margin: 0;\n}\n\n.ProseMirror-gapcursor:after {\n content: \"\";\n display: block;\n position: absolute;\n top: -2px;\n width: 20px;\n border-top: 1px solid black;\n animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;\n}\n\n@keyframes ProseMirror-cursor-blink {\n to {\n visibility: hidden;\n }\n}\n\n.ProseMirror-hideselection *::selection {\n background: transparent;\n}\n\n.ProseMirror-hideselection *::-moz-selection {\n background: transparent;\n}\n\n.ProseMirror-hideselection * {\n caret-color: transparent;\n}\n\n.ProseMirror-focused .ProseMirror-gapcursor {\n display: block;\n}`\n","export function createStyleTag(style: string, nonce?: string, suffix?: string): HTMLStyleElement {\n const tiptapStyleTag = <HTMLStyleElement>(\n document.querySelector(`style[data-tiptap-style${suffix ? `-${suffix}` : ''}]`)\n )\n\n if (tiptapStyleTag !== null) {\n return tiptapStyleTag\n }\n\n const styleNode = document.createElement('style')\n\n if (nonce) {\n styleNode.setAttribute('nonce', nonce)\n }\n\n styleNode.setAttribute(`data-tiptap-style${suffix ? `-${suffix}` : ''}`, '')\n styleNode.innerHTML = style\n document.getElementsByTagName('head')[0].appendChild(styleNode)\n\n return styleNode\n}\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarksBetween } from '../helpers/getMarksBetween.js'\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule that adds a mark when the\n * matched text is typed into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function markInputRule(config: {\n find: InputRuleFinder\n type: MarkType\n undoable?: boolean\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match)\n\n if (attributes === false || attributes === null) {\n return null\n }\n\n const { tr } = state\n const captureGroup = match[match.length - 1]\n const fullMatch = match[0]\n\n if (captureGroup) {\n const startSpaces = fullMatch.search(/\\S/)\n const textStart = range.from + fullMatch.indexOf(captureGroup)\n const textEnd = textStart + captureGroup.length\n\n const excludedMarks = getMarksBetween(range.from, range.to, state.doc)\n .filter(item => {\n // @ts-ignore\n const excluded = item.mark.type.excluded as MarkType[]\n\n return excluded.find(type => type === config.type && type !== item.mark.type)\n })\n .filter(item => item.to > textStart)\n\n if (excludedMarks.length) {\n return null\n }\n\n if (textEnd < range.to) {\n tr.delete(textEnd, range.to)\n }\n\n if (textStart > range.from) {\n tr.delete(range.from + startSpaces, textStart)\n }\n\n const markEnd = range.from + startSpaces + captureGroup.length\n\n tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}))\n\n tr.removeStoredMark(config.type)\n }\n },\n undoable: config.undoable,\n })\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule that adds a node when the\n * matched text is typed into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function nodeInputRule(config: {\n /**\n * The regex to match.\n */\n find: InputRuleFinder\n\n /**\n * The node type to add.\n */\n type: NodeType\n\n /**\n * Whether the input rule should be undoable\n * when the user presses backspace.\n */\n undoable?: boolean\n\n /**\n * A function that returns the attributes for the node\n * can also be an object of attributes\n */\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match) || {}\n const { tr } = state\n const start = range.from\n let end = range.to\n\n const newNode = config.type.create(attributes)\n\n if (match[1]) {\n const offset = match[0].lastIndexOf(match[1])\n let matchStart = start + offset\n\n if (matchStart > end) {\n matchStart = end\n } else {\n end = matchStart + match[1].length\n }\n\n // insert last typed character\n const lastChar = match[0][match[0].length - 1]\n\n tr.insertText(lastChar, start + match[0].length - 1)\n\n // insert node from input rule\n tr.replaceWith(matchStart, end, newNode)\n } else if (match[0]) {\n const insertionStart = config.type.isInline ? start : start - 1\n\n tr.insert(insertionStart, config.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end))\n }\n\n tr.scrollIntoView()\n },\n undoable: config.undoable,\n })\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule that changes the type of a textblock when the\n * matched text is typed into it. When using a regular expresion you’ll\n * probably want the regexp to start with `^`, so that the pattern can\n * only occur at the start of a textblock.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function textblockTypeInputRule(config: {\n find: InputRuleFinder\n type: NodeType\n undoable?: boolean\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n const $start = state.doc.resolve(range.from)\n const attributes = callOrReturn(config.getAttributes, undefined, match) || {}\n\n if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {\n return null\n }\n\n state.tr.delete(range.from, range.to).setBlockType(range.from, range.from, config.type, attributes)\n },\n undoable: config.undoable,\n })\n}\n","import type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\n\n/**\n * Build an input rule that replaces text when the\n * matched text is typed into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function textInputRule(config: { find: InputRuleFinder; replace: string; undoable?: boolean }) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n let insert = config.replace\n let start = range.from\n const end = range.to\n\n if (match[1]) {\n const offset = match[0].lastIndexOf(match[1])\n\n insert += match[0].slice(offset + match[1].length)\n start += offset\n\n const cutOff = start - end\n\n if (cutOff > 0) {\n insert = match[0].slice(offset - cutOff, offset) + insert\n start = end\n }\n }\n\n state.tr.insertText(insert, start, end)\n },\n undoable: config.undoable,\n })\n}\n","import type { Node as ProseMirrorNode, NodeType } from '@tiptap/pm/model'\nimport { canJoin, findWrapping } from '@tiptap/pm/transform'\n\nimport type { Editor } from '../Editor.js'\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule for automatically wrapping a textblock when a\n * given string is typed. When using a regular expresion you’ll\n * probably want the regexp to start with `^`, so that the pattern can\n * only occur at the start of a textblock.\n *\n * `type` is the type of node to wrap in.\n *\n * By default, if there’s a node with the same type above the newly\n * wrapped node, the rule will try to join those\n * two nodes. You can pass a join predicate, which takes a regular\n * expression match and the node before the wrapped node, and can\n * return a boolean to indicate whether a join should happen.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function wrappingInputRule(config: {\n find: InputRuleFinder\n type: NodeType\n keepMarks?: boolean\n keepAttributes?: boolean\n editor?: Editor\n undoable?: boolean\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n joinPredicate?: (match: ExtendedRegExpMatchArray, node: ProseMirrorNode) => boolean\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match, chain }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match) || {}\n const tr = state.tr.delete(range.from, range.to)\n const $start = tr.doc.resolve(range.from)\n const blockRange = $start.blockRange()\n const wrapping = blockRange && findWrapping(blockRange, config.type, attributes)\n\n if (!wrapping) {\n return null\n }\n\n tr.wrap(blockRange, wrapping)\n\n if (config.keepMarks && config.editor) {\n const { selection, storedMarks } = state\n const { splittableMarks } = config.editor.extensionManager\n const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks())\n\n if (marks) {\n const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name))\n\n tr.ensureMarks(filteredMarks)\n }\n }\n if (config.keepAttributes) {\n /** If the nodeType is `bulletList` or `orderedList` set the `nodeType` as `listItem` */\n const nodeType =\n config.type.name === 'bulletList' || config.type.name === 'orderedList' ? 'listItem' : 'taskList'\n\n chain().updateAttributes(nodeType, attributes).run()\n }\n\n const before = tr.doc.resolve(range.from - 1).nodeBefore\n\n if (\n before &&\n before.type === config.type &&\n canJoin(tr.doc, range.from - 1) &&\n (!config.joinPredicate || config.joinPredicate(match, before))\n ) {\n tr.join(range.from - 1)\n }\n },\n undoable: config.undoable,\n })\n}\n","export type Attributes = Record<string, any>\n\nexport type DOMOutputSpecElement = 0 | Attributes | DOMOutputSpecArray\n/**\n * Better describes the output of a `renderHTML` function in prosemirror\n * @see https://prosemirror.net/docs/ref/#model.DOMOutputSpec\n */\nexport type DOMOutputSpecArray =\n | [string]\n | [string, Attributes]\n | [string, 0]\n | [string, Attributes, 0]\n | [string, Attributes, DOMOutputSpecArray | 0]\n | [string, DOMOutputSpecArray]\n\n// JSX types for Tiptap's JSX runtime\n// These types only apply when using @jsxImportSource @tiptap/core\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace JSX {\n export type Element = DOMOutputSpecArray\n export interface IntrinsicElements {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: any\n }\n export interface ElementChildrenAttribute {\n children: unknown\n }\n}\n\nexport type JSXRenderer = (\n tag: 'slot' | string | ((props?: Attributes) => DOMOutputSpecArray | DOMOutputSpecElement),\n props?: Attributes,\n ...children: JSXRenderer[]\n) => DOMOutputSpecArray | DOMOutputSpecElement\n\nexport function Fragment(props: { children: JSXRenderer[] }) {\n return props.children\n}\n\nexport const h: JSXRenderer = (tag, attributes) => {\n // Treat the slot tag as the Prosemirror hole to render content into\n if (tag === 'slot') {\n return 0\n }\n\n // If the tag is a function, call it with the props\n if (tag instanceof Function) {\n return tag(attributes)\n }\n\n const { children, ...rest } = attributes ?? {}\n\n if (tag === 'svg') {\n throw new Error('SVG elements are not supported in the JSX syntax, use the array syntax instead')\n }\n\n // Otherwise, return the tag, attributes, and children\n return [tag, rest, children]\n}\n\n// See\n// https://esbuild.github.io/api/#jsx-import-source\n// https://www.typescriptlang.org/tsconfig/#jsxImportSource\n\nexport { h as createElement, h as jsx, h as jsxDEV, h as jsxs }\n","import type { Node as PMNode } from '@tiptap/pm/model'\nimport type { Decoration, DecorationSource, NodeView } from '@tiptap/pm/view'\n\nimport type { Editor } from '../Editor.js'\n\nconst isTouchEvent = (e: MouseEvent | TouchEvent): e is TouchEvent => {\n return 'touches' in e\n}\n\n/**\n * Directions where resize handles can be placed\n *\n * @example\n * - `'top'` - Top edge handle\n * - `'bottom-right'` - Bottom-right corner handle\n */\nexport type ResizableNodeViewDirection =\n | 'top'\n | 'right'\n | 'bottom'\n | 'left'\n | 'top-right'\n | 'top-left'\n | 'bottom-right'\n | 'bottom-left'\n\n/**\n * Dimensions for the resizable node in pixels\n */\nexport type ResizableNodeDimensions = {\n /** Width in pixels */\n width: number\n /** Height in pixels */\n height: number\n}\n\n/**\n * Configuration options for creating a ResizableNodeView\n *\n * @example\n * ```ts\n * new ResizableNodeView({\n * element: imgElement,\n * node,\n * getPos,\n * onResize: (width, height) => {\n * imgElement.style.width = `${width}px`\n * imgElement.style.height = `${height}px`\n * },\n * onCommit: (width, height) => {\n * editor.commands.updateAttributes('image', { width, height })\n * },\n * onUpdate: (node) => true,\n * options: {\n * directions: ['bottom-right', 'bottom-left'],\n * min: { width: 100, height: 100 },\n * preserveAspectRatio: true\n * }\n * })\n * ```\n */\nexport type ResizableNodeViewOptions = {\n /**\n * The DOM element to make resizable (e.g., an img, video, or iframe element)\n */\n element: HTMLElement\n\n /**\n * The DOM element that will hold the editable content element\n */\n contentElement?: HTMLElement\n\n /**\n * The ProseMirror node instance\n */\n node: PMNode\n\n /**\n * The Tiptap editor instance\n */\n editor: Editor\n\n /**\n * Function that returns the current position of the node in the document\n */\n getPos: () => number | undefined\n\n /**\n * Callback fired continuously during resize with current dimensions.\n * Use this to update the element's visual size in real-time.\n *\n * @param width - Current width in pixels\n * @param height - Current height in pixels\n *\n * @example\n * ```ts\n * onResize: (width, height) => {\n * element.style.width = `${width}px`\n * element.style.height = `${height}px`\n * }\n * ```\n */\n onResize?: (width: number, height: number) => void\n\n /**\n * Callback fired once when resize completes with final dimensions.\n * Use this to persist the new size to the node's attributes.\n *\n * @param width - Final width in pixels\n * @param height - Final height in pixels\n *\n * @example\n * ```ts\n * onCommit: (width, height) => {\n * const pos = getPos()\n * if (pos !== undefined) {\n * editor.commands.updateAttributes('image', { width, height })\n * }\n * }\n * ```\n */\n onCommit: (width: number, height: number) => void\n\n /**\n * Callback for handling node updates.\n * Return `true` to accept the update, `false` to reject it.\n *\n * @example\n * ```ts\n * onUpdate: (node, decorations, innerDecorations) => {\n * if (node.type !== this.node.type) return false\n * return true\n * }\n * ```\n */\n onUpdate: NodeView['update']\n\n /**\n * Optional configuration for resize behavior and styling\n */\n options?: {\n /**\n * Which resize handles to display.\n * @default ['bottom-left', 'bottom-right', 'top-left', 'top-right']\n *\n * @example\n * ```ts\n * // Only show corner handles\n * directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right']\n *\n * // Only show right edge handle\n * directions: ['right']\n * ```\n */\n directions?: ResizableNodeViewDirection[]\n\n /**\n * Minimum dimensions in pixels\n * @default { width: 8, height: 8 }\n *\n * @example\n * ```ts\n * min: { width: 100, height: 50 }\n * ```\n */\n min?: Partial<ResizableNodeDimensions>\n\n /**\n * Maximum dimensions in pixels\n * @default undefined (no maximum)\n *\n * @example\n * ```ts\n * max: { width: 1000, height: 800 }\n * ```\n */\n max?: Partial<ResizableNodeDimensions>\n\n /**\n * Always preserve aspect ratio when resizing.\n * When `false`, aspect ratio is preserved only when Shift key is pressed.\n * @default false\n *\n * @example\n * ```ts\n * preserveAspectRatio: true // Always lock aspect ratio\n * ```\n */\n preserveAspectRatio?: boolean\n\n /**\n * Custom CSS class names for styling\n *\n * @example\n * ```ts\n * className: {\n * container: 'resize-container',\n * wrapper: 'resize-wrapper',\n * handle: 'resize-handle',\n * resizing: 'is-resizing'\n * }\n * ```\n */\n className?: {\n /** Class for the outer container element */\n container?: string\n /** Class for the wrapper element that contains the resizable element */\n wrapper?: string\n /** Class applied to all resize handles */\n handle?: string\n /** Class added to container while actively resizing */\n resizing?: string\n }\n\n /**\n * Optional callback for creating custom resize handle elements.\n *\n * This function allows developers to define their own handle element\n * (e.g., custom icons, classes, or styles) for a given resize direction.\n * It is called internally for each handle direction.\n *\n * @param direction - The direction of the handle being created (e.g., 'top', 'bottom-right').\n * @returns The custom handle HTMLElement.\n *\n * @example\n * ```ts\n * createCustomHandle: (direction) => {\n * const handle = document.createElement('div')\n * handle.dataset.resizeHandle = direction\n * handle.style.position = 'absolute'\n * handle.className = 'tiptap-custom-handle'\n *\n * const isTop = direction.includes('top')\n * const isBottom = direction.includes('bottom')\n * const isLeft = direction.includes('left')\n * const isRight = direction.includes('right')\n *\n * if (isTop) handle.style.top = '0'\n * if (isBottom) handle.style.bottom = '0'\n * if (isLeft) handle.style.left = '0'\n * if (isRight) handle.style.right = '0'\n *\n * // Edge handles span the full width or height\n * if (direction === 'top' || direction === 'bottom') {\n * handle.style.left = '0'\n * handle.style.right = '0'\n * }\n *\n * if (direction === 'left' || direction === 'right') {\n * handle.style.top = '0'\n * handle.style.bottom = '0'\n * }\n *\n * return handle\n * }\n * ```\n */\n createCustomHandle?: (direction: ResizableNodeViewDirection) => HTMLElement\n }\n}\n\n/**\n * A NodeView implementation that adds resize handles to any DOM element.\n *\n * This class creates a resizable node view for Tiptap/ProseMirror editors.\n * It wraps your element with resize handles and manages the resize interaction,\n * including aspect ratio preservation, min/max constraints, and keyboard modifiers.\n *\n * @example\n * ```ts\n * // Basic usage in a Tiptap extension\n * addNodeView() {\n * return ({ node, getPos }) => {\n * const img = document.createElement('img')\n * img.src = node.attrs.src\n *\n * return new ResizableNodeView({\n * element: img,\n * node,\n * getPos,\n * onResize: (width, height) => {\n * img.style.width = `${width}px`\n * img.style.height = `${height}px`\n * },\n * onCommit: (width, height) => {\n * this.editor.commands.updateAttributes('image', { width, height })\n * },\n * onUpdate: () => true,\n * options: {\n * min: { width: 100, height: 100 },\n * preserveAspectRatio: true\n * }\n * })\n * }\n * }\n * ```\n */\nexport class ResizableNodeView {\n /** The ProseMirror node instance */\n node: PMNode\n\n /** The Tiptap editor instance */\n editor: Editor\n\n /** The DOM element being made resizable */\n element: HTMLElement\n\n /** The editable DOM element inside the DOM */\n contentElement?: HTMLElement\n\n /** The outer container element (returned as NodeView.dom) */\n container: HTMLElement\n\n /** The wrapper element that contains the element and handles */\n wrapper: HTMLElement\n\n /** Function to get the current node position */\n getPos: () => number | undefined\n\n /** Callback fired during resize */\n onResize?: (width: number, height: number) => void\n\n /** Callback fired when resize completes */\n onCommit: (width: number, height: number) => void\n\n /** Callback for node updates */\n onUpdate?: NodeView['update']\n\n /** Active resize handle directions */\n directions: ResizableNodeViewDirection[] = ['bottom-left', 'bottom-right', 'top-left', 'top-right']\n\n /** Minimum allowed dimensions */\n minSize: ResizableNodeDimensions = {\n height: 8,\n width: 8,\n }\n\n /** Maximum allowed dimensions (optional) */\n maxSize?: Partial<ResizableNodeDimensions>\n\n /** Whether to always preserve aspect ratio */\n preserveAspectRatio: boolean = false\n\n /** CSS class names for elements */\n classNames = {\n container: '',\n wrapper: '',\n handle: '',\n resizing: '',\n }\n\n /** Optional callback for creating custom resize handles */\n createCustomHandle?: (direction: ResizableNodeViewDirection) => HTMLElement\n\n /** Initial width of the element (for aspect ratio calculation) */\n private initialWidth: number = 0\n\n /** Initial height of the element (for aspect ratio calculation) */\n private initialHeight: number = 0\n\n /** Calculated aspect ratio (width / height) */\n private aspectRatio: number = 1\n\n /** Whether a resize operation is currently active */\n private isResizing: boolean = false\n\n /** The handle currently being dragged */\n private activeHandle: ResizableNodeViewDirection | null = null\n\n /** Starting mouse X position when resize began */\n private startX: number = 0\n\n /** Starting mouse Y position when resize began */\n private startY: number = 0\n\n /** Element width when resize began */\n private startWidth: number = 0\n\n /** Element height when resize began */\n private startHeight: number = 0\n\n /** Whether Shift key is currently pressed (for temporary aspect ratio lock) */\n private isShiftKeyPressed: boolean = false\n\n /** Last known editable state of the editor */\n private lastEditableState: boolean | undefined = undefined\n\n /** Map of handle elements by direction */\n private handleMap = new Map<ResizableNodeViewDirection, HTMLElement>()\n\n /**\n * Creates a new ResizableNodeView instance.\n *\n * The constructor sets up the resize handles, applies initial sizing from\n * node attributes, and configures all resize behavior options.\n *\n * @param options - Configuration options for the resizable node view\n */\n constructor(options: ResizableNodeViewOptions) {\n this.node = options.node\n this.editor = options.editor\n this.element = options.element\n this.contentElement = options.contentElement\n\n this.getPos = options.getPos\n\n this.onResize = options.onResize\n this.onCommit = options.onCommit\n this.onUpdate = options.onUpdate\n\n if (options.options?.min) {\n this.minSize = {\n ...this.minSize,\n ...options.options.min,\n }\n }\n\n if (options.options?.max) {\n this.maxSize = options.options.max\n }\n\n if (options?.options?.directions) {\n this.directions = options.options.directions\n }\n\n if (options.options?.preserveAspectRatio) {\n this.preserveAspectRatio = options.options.preserveAspectRatio\n }\n\n if (options.options?.className) {\n this.classNames = {\n container: options.options.className.container || '',\n wrapper: options.options.className.wrapper || '',\n handle: options.options.className.handle || '',\n resizing: options.options.className.resizing || '',\n }\n }\n\n if (options.options?.createCustomHandle) {\n this.createCustomHandle = options.options.createCustomHandle\n }\n\n this.wrapper = this.createWrapper()\n this.container = this.createContainer()\n\n this.applyInitialSize()\n this.attachHandles()\n\n this.editor.on('update', this.handleEditorUpdate.bind(this))\n }\n\n /**\n * Returns the top-level DOM node that should be placed in the editor.\n *\n * This is required by the ProseMirror NodeView interface. The container\n * includes the wrapper, handles, and the actual content element.\n *\n * @returns The container element to be inserted into the editor\n */\n get dom() {\n return this.container\n }\n\n get contentDOM() {\n return this.contentElement\n }\n\n private handleEditorUpdate() {\n const isEditable = this.editor.isEditable\n\n // Only if state actually changed\n if (isEditable === this.lastEditableState) {\n return\n }\n\n this.lastEditableState = isEditable\n\n if (!isEditable) {\n this.removeHandles()\n } else if (isEditable && this.handleMap.size === 0) {\n this.attachHandles()\n }\n }\n\n /**\n * Called when the node's content or attributes change.\n *\n * Updates the internal node reference. If a custom `onUpdate` callback\n * was provided, it will be called to handle additional update logic.\n *\n * @param node - The new/updated node\n * @param decorations - Node decorations\n * @param innerDecorations - Inner decorations\n * @returns `false` if the node type has changed (requires full rebuild), otherwise the result of `onUpdate` or `true`\n */\n update(node: PMNode, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean {\n if (node.type !== this.node.type) {\n return false\n }\n\n this.node = node\n\n if (this.onUpdate) {\n return this.onUpdate(node, decorations, innerDecorations)\n }\n\n return true\n }\n\n /**\n * Cleanup method called when the node view is being removed.\n *\n * Removes all event listeners to prevent memory leaks. This is required\n * by the ProseMirror NodeView interface. If a resize is active when\n * destroy is called, it will be properly cancelled.\n */\n destroy() {\n if (this.isResizing) {\n this.container.dataset.resizeState = 'false'\n\n if (this.classNames.resizing) {\n this.container.classList.remove(this.classNames.resizing)\n }\n\n document.removeEventListener('mousemove', this.handleMouseMove)\n document.removeEventListener('mouseup', this.handleMouseUp)\n document.removeEventListener('keydown', this.handleKeyDown)\n document.removeEventListener('keyup', this.handleKeyUp)\n this.isResizing = false\n this.activeHandle = null\n }\n\n this.editor.off('update', this.handleEditorUpdate.bind(this))\n\n this.container.remove()\n }\n\n /**\n * Creates the outer container element.\n *\n * The container is the top-level element returned by the NodeView and\n * wraps the entire resizable node. It's set up with flexbox to handle\n * alignment and includes data attributes for styling and identification.\n *\n * @returns The container element\n */\n createContainer() {\n const element = document.createElement('div')\n element.dataset.resizeContainer = ''\n element.dataset.node = this.node.type.name\n element.style.display = 'flex'\n\n if (this.classNames.container) {\n element.className = this.classNames.container\n }\n\n element.appendChild(this.wrapper)\n\n return element\n }\n\n /**\n * Creates the wrapper element that contains the content and handles.\n *\n * The wrapper uses relative positioning so that resize handles can be\n * positioned absolutely within it. This is the direct parent of the\n * content element being made resizable.\n *\n * @returns The wrapper element\n */\n createWrapper() {\n const element = document.createElement('div')\n element.style.position = 'relative'\n element.style.display = 'block'\n element.dataset.resizeWrapper = ''\n\n if (this.classNames.wrapper) {\n element.className = this.classNames.wrapper\n }\n\n element.appendChild(this.element)\n\n return element\n }\n\n /**\n * Creates a resize handle element for a specific direction.\n *\n * Each handle is absolutely positioned and includes a data attribute\n * identifying its direction for styling purposes.\n *\n * @param direction - The resize direction for this handle\n * @returns The handle element\n */\n private createHandle(direction: ResizableNodeViewDirection): HTMLElement {\n const handle = document.createElement('div')\n handle.dataset.resizeHandle = direction\n handle.style.position = 'absolute'\n\n if (this.classNames.handle) {\n handle.className = this.classNames.handle\n }\n\n return handle\n }\n\n /**\n * Positions a handle element according to its direction.\n *\n * Corner handles (e.g., 'top-left') are positioned at the intersection\n * of two edges. Edge handles (e.g., 'top') span the full width or height.\n *\n * @param handle - The handle element to position\n * @param direction - The direction determining the position\n */\n private positionHandle(handle: HTMLElement, direction: ResizableNodeViewDirection): void {\n const isTop = direction.includes('top')\n const isBottom = direction.includes('bottom')\n const isLeft = direction.includes('left')\n const isRight = direction.includes('right')\n\n if (isTop) {\n handle.style.top = '0'\n }\n\n if (isBottom) {\n handle.style.bottom = '0'\n }\n\n if (isLeft) {\n handle.style.left = '0'\n }\n\n if (isRight) {\n handle.style.right = '0'\n }\n\n // Edge handles span the full width or height\n if (direction === 'top' || direction === 'bottom') {\n handle.style.left = '0'\n handle.style.right = '0'\n }\n\n if (direction === 'left' || direction === 'right') {\n handle.style.top = '0'\n handle.style.bottom = '0'\n }\n }\n\n /**\n * Creates and attaches all resize handles to the wrapper.\n *\n * Iterates through the configured directions, creates a handle for each,\n * positions it, attaches the mousedown listener, and appends it to the DOM.\n */\n private attachHandles(): void {\n this.directions.forEach(direction => {\n let handle: HTMLElement\n\n if (this.createCustomHandle) {\n handle = this.createCustomHandle(direction)\n } else {\n handle = this.createHandle(direction)\n }\n\n if (!(handle instanceof HTMLElement)) {\n console.warn(\n `[ResizableNodeView] createCustomHandle(\"${direction}\") did not return an HTMLElement. Falling back to default handle.`,\n )\n handle = this.createHandle(direction)\n }\n\n if (!this.createCustomHandle) {\n this.positionHandle(handle, direction)\n }\n\n handle.addEventListener('mousedown', event => this.handleResizeStart(event, direction))\n handle.addEventListener('touchstart', event => this.handleResizeStart(event as unknown as MouseEvent, direction))\n\n this.handleMap.set(direction, handle)\n\n this.wrapper.appendChild(handle)\n })\n }\n\n /**\n * Removes all resize handles from the wrapper.\n *\n * Cleans up the handle map and removes each handle element from the DOM.\n */\n private removeHandles(): void {\n this.handleMap.forEach(el => el.remove())\n this.handleMap.clear()\n }\n\n /**\n * Applies initial sizing from node attributes to the element.\n *\n * If width/height attributes exist on the node, they're applied to the element.\n * Otherwise, the element's natural/current dimensions are measured. The aspect\n * ratio is calculated for later use in aspect-ratio-preserving resizes.\n */\n private applyInitialSize(): void {\n const width = this.node.attrs.width as number | undefined\n const height = this.node.attrs.height as number | undefined\n\n if (width) {\n this.element.style.width = `${width}px`\n this.initialWidth = width\n } else {\n this.initialWidth = this.element.offsetWidth\n }\n\n if (height) {\n this.element.style.height = `${height}px`\n this.initialHeight = height\n } else {\n this.initialHeight = this.element.offsetHeight\n }\n\n // Calculate aspect ratio for use during resizing\n if (this.initialWidth > 0 && this.initialHeight > 0) {\n this.aspectRatio = this.initialWidth / this.initialHeight\n }\n }\n\n /**\n * Initiates a resize operation when a handle is clicked.\n *\n * Captures the starting mouse position and element dimensions, sets up\n * the resize state, adds the resizing class and state attribute, and\n * attaches document-level listeners for mouse movement and keyboard input.\n *\n * @param event - The mouse down event\n * @param direction - The direction of the handle being dragged\n */\n private handleResizeStart(event: MouseEvent | TouchEvent, direction: ResizableNodeViewDirection): void {\n event.preventDefault()\n event.stopPropagation()\n\n // Capture initial state\n this.isResizing = true\n this.activeHandle = direction\n\n if (isTouchEvent(event)) {\n this.startX = event.touches[0].clientX\n this.startY = event.touches[0].clientY\n } else {\n this.startX = event.clientX\n this.startY = event.clientY\n }\n\n this.startWidth = this.element.offsetWidth\n this.startHeight = this.element.offsetHeight\n\n // Recalculate aspect ratio at resize start for accuracy\n if (this.startWidth > 0 && this.startHeight > 0) {\n this.aspectRatio = this.startWidth / this.startHeight\n }\n\n const pos = this.getPos()\n if (pos !== undefined) {\n // TODO: Select the node in the editor\n }\n\n // Update UI state\n this.container.dataset.resizeState = 'true'\n\n if (this.classNames.resizing) {\n this.container.classList.add(this.classNames.resizing)\n }\n\n // Attach document-level listeners for resize\n document.addEventListener('mousemove', this.handleMouseMove)\n document.addEventListener('touchmove', this.handleTouchMove)\n document.addEventListener('mouseup', this.handleMouseUp)\n document.addEventListener('keydown', this.handleKeyDown)\n document.addEventListener('keyup', this.handleKeyUp)\n }\n\n /**\n * Handles mouse movement during an active resize.\n *\n * Calculates the delta from the starting position, computes new dimensions\n * based on the active handle direction, applies constraints and aspect ratio,\n * then updates the element's style and calls the onResize callback.\n *\n * @param event - The mouse move event\n */\n private handleMouseMove = (event: MouseEvent): void => {\n if (!this.isResizing || !this.activeHandle) {\n return\n }\n\n const deltaX = event.clientX - this.startX\n const deltaY = event.clientY - this.startY\n\n this.handleResize(deltaX, deltaY)\n }\n\n private handleTouchMove = (event: TouchEvent): void => {\n if (!this.isResizing || !this.activeHandle) {\n return\n }\n\n const touch = event.touches[0]\n if (!touch) {\n return\n }\n\n const deltaX = touch.clientX - this.startX\n const deltaY = touch.clientY - this.startY\n\n this.handleResize(deltaX, deltaY)\n }\n\n private handleResize(deltaX: number, deltaY: number) {\n if (!this.activeHandle) {\n return\n }\n\n const shouldPreserveAspectRatio = this.preserveAspectRatio || this.isShiftKeyPressed\n const { width, height } = this.calculateNewDimensions(this.activeHandle, deltaX, deltaY)\n const constrained = this.applyConstraints(width, height, shouldPreserveAspectRatio)\n\n this.element.style.width = `${constrained.width}px`\n this.element.style.height = `${constrained.height}px`\n\n if (this.onResize) {\n this.onResize(constrained.width, constrained.height)\n }\n }\n\n /**\n * Completes the resize operation when the mouse button is released.\n *\n * Captures final dimensions, calls the onCommit callback to persist changes,\n * removes the resizing state and class, and cleans up document-level listeners.\n */\n private handleMouseUp = (): void => {\n if (!this.isResizing) {\n return\n }\n\n const finalWidth = this.element.offsetWidth\n const finalHeight = this.element.offsetHeight\n\n this.onCommit(finalWidth, finalHeight)\n\n this.isResizing = false\n this.activeHandle = null\n\n // Remove UI state\n this.container.dataset.resizeState = 'false'\n\n if (this.classNames.resizing) {\n this.container.classList.remove(this.classNames.resizing)\n }\n\n // Clean up document-level listeners\n document.removeEventListener('mousemove', this.handleMouseMove)\n document.removeEventListener('mouseup', this.handleMouseUp)\n document.removeEventListener('keydown', this.handleKeyDown)\n document.removeEventListener('keyup', this.handleKeyUp)\n }\n\n /**\n * Tracks Shift key state to enable temporary aspect ratio locking.\n *\n * When Shift is pressed during resize, aspect ratio is preserved even if\n * preserveAspectRatio is false.\n *\n * @param event - The keyboard event\n */\n private handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Shift') {\n this.isShiftKeyPressed = true\n }\n }\n\n /**\n * Tracks Shift key release to disable temporary aspect ratio locking.\n *\n * @param event - The keyboard event\n */\n private handleKeyUp = (event: KeyboardEvent): void => {\n if (event.key === 'Shift') {\n this.isShiftKeyPressed = false\n }\n }\n\n /**\n * Calculates new dimensions based on mouse delta and resize direction.\n *\n * Takes the starting dimensions and applies the mouse movement delta\n * according to the handle direction. For corner handles, both dimensions\n * are affected. For edge handles, only one dimension changes. If aspect\n * ratio should be preserved, delegates to applyAspectRatio.\n *\n * @param direction - The active resize handle direction\n * @param deltaX - Horizontal mouse movement since resize start\n * @param deltaY - Vertical mouse movement since resize start\n * @returns The calculated width and height\n */\n private calculateNewDimensions(\n direction: ResizableNodeViewDirection,\n deltaX: number,\n deltaY: number,\n ): ResizableNodeDimensions {\n let newWidth = this.startWidth\n let newHeight = this.startHeight\n\n const isRight = direction.includes('right')\n const isLeft = direction.includes('left')\n const isBottom = direction.includes('bottom')\n const isTop = direction.includes('top')\n\n // Apply horizontal delta\n if (isRight) {\n newWidth = this.startWidth + deltaX\n } else if (isLeft) {\n newWidth = this.startWidth - deltaX\n }\n\n // Apply vertical delta\n if (isBottom) {\n newHeight = this.startHeight + deltaY\n } else if (isTop) {\n newHeight = this.startHeight - deltaY\n }\n\n // For pure horizontal/vertical handles, only one dimension changes\n if (direction === 'right' || direction === 'left') {\n newWidth = this.startWidth + (isRight ? deltaX : -deltaX)\n }\n\n if (direction === 'top' || direction === 'bottom') {\n newHeight = this.startHeight + (isBottom ? deltaY : -deltaY)\n }\n\n const shouldPreserveAspectRatio = this.preserveAspectRatio || this.isShiftKeyPressed\n\n if (shouldPreserveAspectRatio) {\n return this.applyAspectRatio(newWidth, newHeight, direction)\n }\n\n return { width: newWidth, height: newHeight }\n }\n\n /**\n * Applies min/max constraints to dimensions.\n *\n * When aspect ratio is NOT preserved, constraints are applied independently\n * to width and height. When aspect ratio IS preserved, constraints are\n * applied while maintaining the aspect ratio—if one dimension hits a limit,\n * the other is recalculated proportionally.\n *\n * This ensures that aspect ratio is never broken when constrained.\n *\n * @param width - The unconstrained width\n * @param height - The unconstrained height\n * @param preserveAspectRatio - Whether to maintain aspect ratio while constraining\n * @returns The constrained dimensions\n */\n private applyConstraints(width: number, height: number, preserveAspectRatio: boolean): ResizableNodeDimensions {\n if (!preserveAspectRatio) {\n // Independent constraints for each dimension\n let constrainedWidth = Math.max(this.minSize.width, width)\n let constrainedHeight = Math.max(this.minSize.height, height)\n\n if (this.maxSize?.width) {\n constrainedWidth = Math.min(this.maxSize.width, constrainedWidth)\n }\n\n if (this.maxSize?.height) {\n constrainedHeight = Math.min(this.maxSize.height, constrainedHeight)\n }\n\n return { width: constrainedWidth, height: constrainedHeight }\n }\n\n // Aspect-ratio-aware constraints: adjust both dimensions proportionally\n let constrainedWidth = width\n let constrainedHeight = height\n\n // Check minimum constraints\n if (constrainedWidth < this.minSize.width) {\n constrainedWidth = this.minSize.width\n constrainedHeight = constrainedWidth / this.aspectRatio\n }\n\n if (constrainedHeight < this.minSize.height) {\n constrainedHeight = this.minSize.height\n constrainedWidth = constrainedHeight * this.aspectRatio\n }\n\n // Check maximum constraints\n if (this.maxSize?.width && constrainedWidth > this.maxSize.width) {\n constrainedWidth = this.maxSize.width\n constrainedHeight = constrainedWidth / this.aspectRatio\n }\n\n if (this.maxSize?.height && constrainedHeight > this.maxSize.height) {\n constrainedHeight = this.maxSize.height\n constrainedWidth = constrainedHeight * this.aspectRatio\n }\n\n return { width: constrainedWidth, height: constrainedHeight }\n }\n\n /**\n * Adjusts dimensions to maintain the original aspect ratio.\n *\n * For horizontal handles (left/right), uses width as the primary dimension\n * and calculates height from it. For vertical handles (top/bottom), uses\n * height as primary and calculates width. For corner handles, uses width\n * as the primary dimension.\n *\n * @param width - The new width\n * @param height - The new height\n * @param direction - The active resize direction\n * @returns Dimensions adjusted to preserve aspect ratio\n */\n private applyAspectRatio(\n width: number,\n height: number,\n direction: ResizableNodeViewDirection,\n ): ResizableNodeDimensions {\n const isHorizontal = direction === 'left' || direction === 'right'\n const isVertical = direction === 'top' || direction === 'bottom'\n\n if (isHorizontal) {\n // For horizontal resize, width is primary\n return {\n width,\n height: width / this.aspectRatio,\n }\n }\n\n if (isVertical) {\n // For vertical resize, height is primary\n return {\n width: height * this.aspectRatio,\n height,\n }\n }\n\n // For corner resize, width is primary\n return {\n width,\n height: width / this.aspectRatio,\n }\n }\n}\n\n/**\n * Alias for ResizableNodeView to maintain consistent naming.\n * @deprecated Use ResizableNodeView instead - will be removed in future versions.\n */\nexport const ResizableNodeview = ResizableNodeView\n","import type { NodeType } from '@tiptap/pm/model'\nimport { type EditorState, NodeSelection } from '@tiptap/pm/state'\n\nexport function canInsertNode(state: EditorState, nodeType: NodeType): boolean {\n const { selection } = state\n const { $from } = selection\n\n // Special handling for NodeSelection\n if (selection instanceof NodeSelection) {\n const index = $from.index()\n const parent = $from.parent\n\n // Can we replace the selected node with the horizontal rule?\n return parent.canReplaceWith(index, index + 1, nodeType)\n }\n\n // Default: check if we can insert at the current position\n let depth = $from.depth\n\n while (depth >= 0) {\n const index = $from.index(depth)\n const parent = $from.node(depth)\n const match = parent.contentMatchAt(index)\n if (match.matchType(nodeType)) {\n return true\n }\n depth -= 1\n }\n return false\n}\n","// source: https://stackoverflow.com/a/6969486\nexport function escapeForRegEx(string: string): string {\n return string.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')\n}\n","export function isString(value: any): value is string {\n return typeof value === 'string'\n}\n","/**\n * @fileoverview Markdown utilities for creating standardized markdown specs.\n *\n * This module provides utilities for creating complete markdown specifications\n * for different types of nodes using unified syntax patterns.\n */\n\nexport * from './attributeUtils.js'\nexport * from './createAtomBlockMarkdownSpec.js'\nexport * from './createBlockMarkdownSpec.js'\nexport * from './createInlineMarkdownSpec.js'\nexport * from './parseIndentedBlocks.js'\nexport * from './renderNestedMarkdownContent.js'\n","/**\n * @fileoverview Utility functions for parsing and serializing markdown attributes.\n *\n * These utilities handle the common patterns for parsing attribute strings\n * in various markdown syntaxes like Pandoc attributes.\n */\n\n/**\n * Parses a Pandoc-style attribute string into an object.\n *\n * Supports the following patterns:\n * - Classes: `.className` → `{ class: 'className' }`\n * - IDs: `#myId` → `{ id: 'myId' }`\n * - Key-value pairs: `key=\"value\"` → `{ key: 'value' }`\n * - Boolean attributes: `disabled` → `{ disabled: true }`\n *\n * @param attrString - The attribute string to parse\n * @returns Parsed attributes object\n *\n * @example\n * ```ts\n * parseAttributes('.btn #submit disabled type=\"button\"')\n * // → { class: 'btn', id: 'submit', disabled: true, type: 'button' }\n * ```\n */\nexport function parseAttributes(attrString: string): Record<string, any> {\n if (!attrString?.trim()) {\n return {}\n }\n\n const attributes: Record<string, any> = {}\n\n // First, extract and remove quoted strings to avoid parsing content inside them\n const quotedStrings: string[] = []\n const tempString = attrString.replace(/[\"']([^\"']*)[\"']/g, match => {\n quotedStrings.push(match)\n return `__QUOTED_${quotedStrings.length - 1}__`\n })\n\n // Parse classes (.className) - only outside of quoted strings\n const classMatches = tempString.match(/(?:^|\\s)\\.([a-zA-Z][\\w-]*)/g)\n if (classMatches) {\n const classes = classMatches.map(match => match.trim().slice(1)) // Remove the dot\n attributes.class = classes.join(' ')\n }\n\n // Parse IDs (#myId) - only outside of quoted strings\n const idMatch = tempString.match(/(?:^|\\s)#([a-zA-Z][\\w-]*)/)\n if (idMatch) {\n attributes.id = idMatch[1]\n }\n\n // Parse key-value pairs (key=\"value\" or key='value') - restore quoted strings\n const kvRegex = /([a-zA-Z][\\w-]*)\\s*=\\s*(__QUOTED_\\d+__)/g\n const kvMatches = Array.from(tempString.matchAll(kvRegex))\n kvMatches.forEach(([, key, quotedRef]) => {\n const quotedIndex = parseInt(quotedRef.match(/__QUOTED_(\\d+)__/)?.[1] || '0', 10)\n const quotedValue = quotedStrings[quotedIndex]\n if (quotedValue) {\n // Remove the outer quotes\n attributes[key] = quotedValue.slice(1, -1)\n }\n })\n\n // Parse boolean attributes (standalone words that aren't classes/IDs)\n const cleanString = tempString\n .replace(/(?:^|\\s)\\.([a-zA-Z][\\w-]*)/g, '') // Remove classes\n .replace(/(?:^|\\s)#([a-zA-Z][\\w-]*)/g, '') // Remove IDs\n .replace(/([a-zA-Z][\\w-]*)\\s*=\\s*__QUOTED_\\d+__/g, '') // Remove key-value pairs\n .trim()\n\n if (cleanString) {\n const booleanAttrs = cleanString.split(/\\s+/).filter(Boolean)\n booleanAttrs.forEach(attr => {\n if (attr.match(/^[a-zA-Z][\\w-]*$/)) {\n attributes[attr] = true\n }\n })\n }\n\n return attributes\n}\n\n/**\n * Serializes an attributes object back to a Pandoc-style attribute string.\n *\n * @param attributes - The attributes object to serialize\n * @returns Serialized attribute string\n *\n * @example\n * ```ts\n * serializeAttributes({ class: 'btn primary', id: 'submit', disabled: true, type: 'button' })\n * // → '.btn.primary #submit disabled type=\"button\"'\n * ```\n */\nexport function serializeAttributes(attributes: Record<string, any>): string {\n if (!attributes || Object.keys(attributes).length === 0) {\n return ''\n }\n\n const parts: string[] = []\n\n // Handle classes\n if (attributes.class) {\n const classes = String(attributes.class).split(/\\s+/).filter(Boolean)\n classes.forEach(cls => parts.push(`.${cls}`))\n }\n\n // Handle ID\n if (attributes.id) {\n parts.push(`#${attributes.id}`)\n }\n\n // Handle other attributes\n Object.entries(attributes).forEach(([key, value]) => {\n if (key === 'class' || key === 'id') {\n return // Already handled\n }\n\n if (value === true) {\n // Boolean attribute\n parts.push(key)\n } else if (value !== false && value != null) {\n // Key-value attribute\n parts.push(`${key}=\"${String(value)}\"`)\n }\n })\n\n return parts.join(' ')\n}\n","import type {\n JSONContent,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownToken,\n MarkdownTokenizer,\n} from '../../types.js'\nimport {\n parseAttributes as defaultParseAttributes,\n serializeAttributes as defaultSerializeAttributes,\n} from './attributeUtils.js'\n\nexport interface AtomBlockMarkdownSpecOptions {\n /** The Tiptap node name this spec is for */\n nodeName: string\n /** The markdown syntax name (defaults to nodeName if not provided) */\n name?: string\n /** Function to parse attributes from token attribute string */\n parseAttributes?: (attrString: string) => Record<string, any>\n /** Function to serialize attributes back to string for rendering */\n serializeAttributes?: (attrs: Record<string, any>) => string\n /** Default attributes to apply when parsing */\n defaultAttributes?: Record<string, any>\n /** Required attributes that must be present for successful parsing */\n requiredAttributes?: string[]\n /** Attributes that are allowed to be rendered back to markdown (whitelist) */\n allowedAttributes?: string[]\n}\n\n/**\n * Creates a complete markdown spec for atomic block nodes using Pandoc syntax.\n *\n * The generated spec handles:\n * - Parsing self-closing blocks with `:::blockName {attributes}`\n * - Extracting and parsing attributes\n * - Validating required attributes\n * - Rendering blocks back to markdown\n *\n * @param options - Configuration for the atomic block markdown spec\n * @returns Complete markdown specification object\n *\n * @example\n * ```ts\n * const youtubeSpec = createAtomBlockMarkdownSpec({\n * nodeName: 'youtube',\n * requiredAttributes: ['src'],\n * defaultAttributes: { start: 0 },\n * allowedAttributes: ['src', 'start', 'width', 'height'] // Only these get rendered to markdown\n * })\n *\n * // Usage in extension:\n * export const Youtube = Node.create({\n * // ... other config\n * markdown: youtubeSpec\n * })\n * ```\n */\nexport function createAtomBlockMarkdownSpec(options: AtomBlockMarkdownSpecOptions): {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => MarkdownParseResult\n markdownTokenizer: MarkdownTokenizer\n renderMarkdown: (node: JSONContent) => string\n} {\n const {\n nodeName,\n name: markdownName,\n parseAttributes = defaultParseAttributes,\n serializeAttributes = defaultSerializeAttributes,\n defaultAttributes = {},\n requiredAttributes = [],\n allowedAttributes,\n } = options\n\n // Use markdownName for syntax, fallback to nodeName\n const blockName = markdownName || nodeName\n\n // Helper function to filter attributes based on allowlist\n const filterAttributes = (attrs: Record<string, any>) => {\n if (!allowedAttributes) {\n return attrs\n }\n\n const filtered: Record<string, any> = {}\n allowedAttributes.forEach(key => {\n if (key in attrs) {\n filtered[key] = attrs[key]\n }\n })\n return filtered\n }\n\n return {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => {\n const attrs = { ...defaultAttributes, ...token.attributes }\n return h.createNode(nodeName, attrs, [])\n },\n\n markdownTokenizer: {\n name: nodeName,\n level: 'block' as const,\n start(src: string) {\n const regex = new RegExp(`^:::${blockName}(?:\\\\s|$)`, 'm')\n const index = src.match(regex)?.index\n return index !== undefined ? index : -1\n },\n tokenize(src, _tokens, _lexer) {\n // Use non-global regex to match from the start of the string\n // Include optional newline to ensure we consume the entire line\n const regex = new RegExp(`^:::${blockName}(?:\\\\s+\\\\{([^}]*)\\\\})?\\\\s*:::(?:\\\\n|$)`)\n const match = src.match(regex)\n\n if (!match) {\n return undefined\n }\n\n // Parse attributes if present\n const attrString = match[1] || ''\n const attributes = parseAttributes(attrString)\n\n // Validate required attributes\n const missingRequired = requiredAttributes.find(required => !(required in attributes))\n if (missingRequired) {\n return undefined\n }\n\n return {\n type: nodeName,\n raw: match[0],\n attributes,\n }\n },\n },\n\n renderMarkdown: node => {\n const filteredAttrs = filterAttributes(node.attrs || {})\n const attrs = serializeAttributes(filteredAttrs)\n const attrString = attrs ? ` {${attrs}}` : ''\n\n return `:::${blockName}${attrString} :::`\n },\n }\n}\n","import type {\n JSONContent,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownRendererHelpers,\n MarkdownToken,\n MarkdownTokenizer,\n} from '../../types.js'\nimport {\n parseAttributes as defaultParseAttributes,\n serializeAttributes as defaultSerializeAttributes,\n} from './attributeUtils.js'\n\nexport interface BlockMarkdownSpecOptions {\n /** The Tiptap node name this spec is for */\n nodeName: string\n /** The markdown syntax name (defaults to nodeName if not provided) */\n name?: string\n /** Function to extract content from the node for serialization */\n getContent?: (token: MarkdownToken) => string\n /** Function to parse attributes from the attribute string */\n parseAttributes?: (attrString: string) => Record<string, any>\n /** Function to serialize attributes to string */\n serializeAttributes?: (attrs: Record<string, any>) => string\n /** Default attributes to apply when parsing */\n defaultAttributes?: Record<string, any>\n /** Content type: 'block' allows paragraphs/lists/etc, 'inline' only allows bold/italic/links/etc */\n content?: 'block' | 'inline'\n /** Allowlist of attributes to include in markdown (if not provided, all attributes are included) */\n allowedAttributes?: string[]\n}\n\n/**\n * Creates a complete markdown spec for block-level nodes using Pandoc syntax.\n *\n * The generated spec handles:\n * - Parsing blocks with `:::blockName {attributes}` syntax\n * - Extracting and parsing attributes\n * - Rendering blocks back to markdown with proper formatting\n * - Nested content support\n *\n * @param options - Configuration for the block markdown spec\n * @returns Complete markdown specification object\n *\n * @example\n * ```ts\n * const calloutSpec = createBlockMarkdownSpec({\n * nodeName: 'callout',\n * defaultAttributes: { type: 'info' },\n * allowedAttributes: ['type', 'title'] // Only these get rendered to markdown\n * })\n *\n * // Usage in extension:\n * export const Callout = Node.create({\n * // ... other config\n * markdown: calloutSpec\n * })\n * ```\n */\nexport function createBlockMarkdownSpec(options: BlockMarkdownSpecOptions): {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => MarkdownParseResult\n markdownTokenizer: MarkdownTokenizer\n renderMarkdown: (node: JSONContent, h: MarkdownRendererHelpers) => string\n} {\n const {\n nodeName,\n name: markdownName,\n getContent,\n parseAttributes = defaultParseAttributes,\n serializeAttributes = defaultSerializeAttributes,\n defaultAttributes = {},\n content = 'block',\n allowedAttributes,\n } = options\n\n // Use markdownName for syntax, fallback to nodeName\n const blockName = markdownName || nodeName\n\n // Helper function to filter attributes based on allowlist\n const filterAttributes = (attrs: Record<string, any>) => {\n if (!allowedAttributes) {\n return attrs\n }\n\n const filtered: Record<string, any> = {}\n allowedAttributes.forEach(key => {\n if (key in attrs) {\n filtered[key] = attrs[key]\n }\n })\n return filtered\n }\n\n return {\n parseMarkdown: (token, h) => {\n let nodeContent: JSONContent[]\n\n if (getContent) {\n const contentResult = getContent(token)\n // If getContent returns a string, wrap it in a text node\n nodeContent = typeof contentResult === 'string' ? [{ type: 'text', text: contentResult }] : contentResult\n } else if (content === 'block') {\n nodeContent = h.parseChildren(token.tokens || [])\n } else {\n nodeContent = h.parseInline(token.tokens || [])\n }\n\n const attrs = { ...defaultAttributes, ...token.attributes }\n\n return h.createNode(nodeName, attrs, nodeContent)\n },\n\n markdownTokenizer: {\n name: nodeName,\n level: 'block' as const,\n start(src) {\n const regex = new RegExp(`^:::${blockName}`, 'm')\n const index = src.match(regex)?.index\n return index !== undefined ? index : -1\n },\n tokenize(src, _tokens, lexer) {\n // Match the opening tag with optional attributes\n const openingRegex = new RegExp(`^:::${blockName}(?:\\\\s+\\\\{([^}]*)\\\\})?\\\\s*\\\\n`)\n const openingMatch = src.match(openingRegex)\n\n if (!openingMatch) {\n return undefined\n }\n\n const [openingTag, attrString = ''] = openingMatch\n const attributes = parseAttributes(attrString)\n\n // Find the matching closing tag by tracking nesting level\n let level = 1\n const position = openingTag.length\n let matchedContent = ''\n\n // Pattern to match any block opening (:::word) or closing (:::)\n const blockPattern = /^:::([\\w-]*)(\\s.*)?/gm\n const remaining = src.slice(position)\n\n blockPattern.lastIndex = 0\n\n // run until no more matches are found\n for (;;) {\n const match = blockPattern.exec(remaining)\n if (match === null) {\n break\n }\n const matchPos = match.index\n const blockType = match[1] // Empty string for closing tag, block name for opening\n\n if (match[2]?.endsWith(':::')) {\n // this is an atom ::: node, we skip it\n continue\n }\n\n if (blockType) {\n // Opening tag found - increase level\n level += 1\n } else {\n // Closing tag found - decrease level\n level -= 1\n\n if (level === 0) {\n // Found our matching closing tag\n // Don't trim yet - keep newlines for tokenizer regex matching\n const rawContent = remaining.slice(0, matchPos)\n matchedContent = rawContent.trim()\n const fullMatch = src.slice(0, position + matchPos + match[0].length)\n\n // Tokenize the content\n let contentTokens: MarkdownToken[] = []\n if (matchedContent) {\n if (content === 'block') {\n // Use rawContent for tokenization to preserve line boundaries for regex matching\n contentTokens = lexer.blockTokens(rawContent)\n\n // Parse inline tokens for any token that has text content but no tokens\n contentTokens.forEach(token => {\n if (token.text && (!token.tokens || token.tokens.length === 0)) {\n token.tokens = lexer.inlineTokens(token.text)\n }\n })\n\n // Clean up empty trailing paragraphs\n while (contentTokens.length > 0) {\n const lastToken = contentTokens[contentTokens.length - 1]\n if (lastToken.type === 'paragraph' && (!lastToken.text || lastToken.text.trim() === '')) {\n contentTokens.pop()\n } else {\n break\n }\n }\n } else {\n contentTokens = lexer.inlineTokens(matchedContent)\n }\n }\n\n return {\n type: nodeName,\n raw: fullMatch,\n attributes,\n content: matchedContent,\n tokens: contentTokens,\n }\n }\n }\n }\n\n // No matching closing tag found\n return undefined\n },\n },\n\n renderMarkdown: (node, h) => {\n const filteredAttrs = filterAttributes(node.attrs || {})\n const attrs = serializeAttributes(filteredAttrs)\n const attrString = attrs ? ` {${attrs}}` : ''\n const renderedContent = h.renderChildren(node.content || [], '\\n\\n')\n\n return `:::${blockName}${attrString}\\n\\n${renderedContent}\\n\\n:::`\n },\n }\n}\n","import type {\n JSONContent,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownToken,\n MarkdownTokenizer,\n} from '../../types.js'\n\n/**\n * Parse shortcode attributes like 'id=\"madonna\" handle=\"john\" name=\"John Doe\"'\n * Requires all values to be quoted with either single or double quotes\n */\nfunction parseShortcodeAttributes(attrString: string): Record<string, any> {\n if (!attrString.trim()) {\n return {}\n }\n\n const attributes: Record<string, any> = {}\n // Match key=value pairs, only accepting quoted values\n const regex = /(\\w+)=(?:\"([^\"]*)\"|'([^']*)')/g\n let match = regex.exec(attrString)\n\n while (match !== null) {\n const [, key, doubleQuoted, singleQuoted] = match\n attributes[key] = doubleQuoted || singleQuoted\n match = regex.exec(attrString)\n }\n\n return attributes\n}\n\n/**\n * Serialize attributes back to shortcode format\n * Always quotes all values with double quotes\n */\nfunction serializeShortcodeAttributes(attrs: Record<string, any>): string {\n return Object.entries(attrs)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ')\n}\n\n/**\n * Configuration for an allowed attribute in markdown serialization.\n * Can be a simple string (attribute name) or an object with additional options.\n */\nexport type AllowedAttribute =\n | string\n | {\n /** The attribute name */\n name: string\n /**\n * If provided, the attribute will be skipped during serialization when its value\n * equals this default value. This keeps markdown output clean by omitting\n * attributes that have their default values.\n */\n skipIfDefault?: any\n }\n\nexport interface InlineMarkdownSpecOptions {\n /** The Tiptap node name this spec is for */\n nodeName: string\n /** The shortcode name (defaults to nodeName if not provided) */\n name?: string\n /** Function to extract content from the node for serialization */\n getContent?: (node: any) => string\n /** Function to parse attributes from the attribute string */\n parseAttributes?: (attrString: string) => Record<string, any>\n /** Function to serialize attributes to string */\n serializeAttributes?: (attrs: Record<string, any>) => string\n /** Default attributes to apply when parsing */\n defaultAttributes?: Record<string, any>\n /** Whether this is a self-closing shortcode (no content, like [emoji name=party]) */\n selfClosing?: boolean\n /**\n * Allowlist of attributes to include in markdown serialization.\n * If not provided, all attributes are included.\n *\n * Each item can be either:\n * - A string: the attribute name (always included if present)\n * - An object: `{ name: string, skipIfDefault?: any }` for conditional inclusion\n *\n * @example\n * // Simple string attributes (backward compatible)\n * allowedAttributes: ['id', 'label']\n *\n * // Mixed with conditional attributes\n * allowedAttributes: [\n * 'id',\n * 'label',\n * { name: 'mentionSuggestionChar', skipIfDefault: '@' }\n * ]\n */\n allowedAttributes?: AllowedAttribute[]\n}\n\n/**\n * Creates a complete markdown spec for inline nodes using attribute syntax.\n *\n * The generated spec handles:\n * - Parsing shortcode syntax with `[nodeName attributes]content[/nodeName]` format\n * - Self-closing shortcodes like `[emoji name=party_popper]`\n * - Extracting and parsing attributes from the opening tag\n * - Rendering inline elements back to shortcode markdown\n * - Supporting both content-based and self-closing inline elements\n *\n * @param options - Configuration for the inline markdown spec\n * @returns Complete markdown specification object\n *\n * @example\n * ```ts\n * // Self-closing mention: [mention id=\"madonna\" label=\"Madonna\"]\n * const mentionSpec = createInlineMarkdownSpec({\n * nodeName: 'mention',\n * selfClosing: true,\n * defaultAttributes: { type: 'user' },\n * allowedAttributes: ['id', 'label'] // Only these get rendered to markdown\n * })\n *\n * // Self-closing emoji: [emoji name=\"party_popper\"]\n * const emojiSpec = createInlineMarkdownSpec({\n * nodeName: 'emoji',\n * selfClosing: true,\n * allowedAttributes: ['name']\n * })\n *\n * // With content: [highlight color=\"yellow\"]text[/highlight]\n * const highlightSpec = createInlineMarkdownSpec({\n * nodeName: 'highlight',\n * selfClosing: false,\n * allowedAttributes: ['color', 'style']\n * })\n *\n * // Usage in extension:\n * export const Mention = Node.create({\n * name: 'mention', // Must match nodeName\n * // ... other config\n * markdown: mentionSpec\n * })\n * ```\n */\nexport function createInlineMarkdownSpec(options: InlineMarkdownSpecOptions): {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => MarkdownParseResult\n markdownTokenizer: MarkdownTokenizer\n renderMarkdown: (node: JSONContent) => string\n} {\n const {\n nodeName,\n name: shortcodeName,\n getContent,\n parseAttributes = parseShortcodeAttributes,\n serializeAttributes = serializeShortcodeAttributes,\n defaultAttributes = {},\n selfClosing = false,\n allowedAttributes,\n } = options\n\n // Use shortcodeName for markdown syntax, fallback to nodeName\n const shortcode = shortcodeName || nodeName\n\n // Helper function to filter attributes based on allowlist\n const filterAttributes = (attrs: Record<string, any>) => {\n if (!allowedAttributes) {\n return attrs\n }\n\n const filtered: Record<string, any> = {}\n allowedAttributes.forEach(attr => {\n // Handle both string and object formats for backward compatibility\n const attrName = typeof attr === 'string' ? attr : attr.name\n const skipIfDefault = typeof attr === 'string' ? undefined : attr.skipIfDefault\n\n if (attrName in attrs) {\n const value = attrs[attrName]\n\n // Skip if value equals the default (when skipIfDefault is specified)\n if (skipIfDefault !== undefined && value === skipIfDefault) {\n return\n }\n\n filtered[attrName] = value\n }\n })\n return filtered\n }\n\n // Escape special regex characters in shortcode name\n const escapedShortcode = shortcode.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n return {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => {\n const attrs = { ...defaultAttributes, ...token.attributes }\n\n if (selfClosing) {\n // Self-closing nodes like mentions are atomic - no content\n return h.createNode(nodeName, attrs)\n }\n\n // Nodes with content\n const content = getContent ? getContent(token) : token.content || ''\n if (content) {\n // For inline content, create text nodes using the proper helper\n return h.createNode(nodeName, attrs, [h.createTextNode(content)])\n }\n return h.createNode(nodeName, attrs, [])\n },\n\n markdownTokenizer: {\n name: nodeName,\n level: 'inline' as const,\n start(src: string) {\n // Create a non-global version for finding the start position\n const startPattern = selfClosing\n ? new RegExp(`\\\\[${escapedShortcode}\\\\s*[^\\\\]]*\\\\]`)\n : new RegExp(`\\\\[${escapedShortcode}\\\\s*[^\\\\]]*\\\\][\\\\s\\\\S]*?\\\\[\\\\/${escapedShortcode}\\\\]`)\n\n const match = src.match(startPattern)\n const index = match?.index\n return index !== undefined ? index : -1\n },\n tokenize(src, _tokens, _lexer) {\n // Use non-global regex to match from the start of the string\n const tokenPattern = selfClosing\n ? new RegExp(`^\\\\[${escapedShortcode}\\\\s*([^\\\\]]*)\\\\]`)\n : new RegExp(`^\\\\[${escapedShortcode}\\\\s*([^\\\\]]*)\\\\]([\\\\s\\\\S]*?)\\\\[\\\\/${escapedShortcode}\\\\]`)\n\n const match = src.match(tokenPattern)\n\n if (!match) {\n return undefined\n }\n\n let content = ''\n let attrString = ''\n\n if (selfClosing) {\n // Self-closing: [shortcode attr=\"value\"]\n const [, attrs] = match\n attrString = attrs\n } else {\n // With content: [shortcode attr=\"value\"]content[/shortcode]\n const [, attrs, contentMatch] = match\n attrString = attrs\n content = contentMatch || ''\n }\n\n // Parse attributes from the attribute string\n const attributes = parseAttributes(attrString.trim())\n\n return {\n type: nodeName,\n raw: match[0],\n content: content.trim(),\n attributes,\n }\n },\n },\n\n renderMarkdown: (node: JSONContent) => {\n let content = ''\n if (getContent) {\n content = getContent(node)\n } else if (node.content && node.content.length > 0) {\n // Extract text from content array for inline nodes\n content = node.content\n .filter((child: any) => child.type === 'text')\n .map((child: any) => child.text)\n .join('')\n }\n\n const filteredAttrs = filterAttributes(node.attrs || {})\n const attrs = serializeAttributes(filteredAttrs)\n const attrString = attrs ? ` ${attrs}` : ''\n\n if (selfClosing) {\n return `[${shortcode}${attrString}]`\n }\n\n return `[${shortcode}${attrString}]${content}[/${shortcode}]`\n },\n }\n}\n","/**\n * @fileoverview Utility for parsing indented markdown blocks with hierarchical nesting.\n *\n * This utility handles the complex logic of parsing markdown blocks that can contain\n * nested content based on indentation levels, maintaining proper hierarchical structure\n * for lists, task lists, and other indented block types.\n */\n\nexport interface ParsedBlock {\n type: string\n raw: string\n mainContent: string\n indentLevel: number\n nestedContent?: string\n nestedTokens?: any[]\n [key: string]: any\n}\n\nexport interface BlockParserConfig {\n /** Regex pattern to match block items */\n itemPattern: RegExp\n /** Function to extract data from regex match */\n extractItemData: (match: RegExpMatchArray) => {\n mainContent: string\n indentLevel: number\n [key: string]: any\n }\n /** Function to create the final token */\n createToken: (data: any, nestedTokens?: any[]) => ParsedBlock\n /** Base indentation to remove from nested content (default: 2 spaces) */\n baseIndentSize?: number\n /**\n * Custom parser for nested content. If provided, this will be called instead\n * of the default lexer.blockTokens() for parsing nested content.\n * This allows recursive parsing of the same block type.\n */\n customNestedParser?: (dedentedContent: string) => any[] | undefined\n}\n\n/**\n * Parses markdown text into hierarchical indented blocks with proper nesting.\n *\n * This utility handles:\n * - Line-by-line parsing with pattern matching\n * - Hierarchical nesting based on indentation levels\n * - Nested content collection and parsing\n * - Empty line handling\n * - Content dedenting for nested blocks\n *\n * The key difference from flat parsing is that this maintains the hierarchical\n * structure where nested items become `nestedTokens` of their parent items,\n * rather than being flattened into a single array.\n *\n * @param src - The markdown source text to parse\n * @param config - Configuration object defining how to parse and create tokens\n * @param lexer - Markdown lexer for parsing nested content\n * @returns Parsed result with hierarchical items, or undefined if no matches\n *\n * @example\n * ```ts\n * const result = parseIndentedBlocks(src, {\n * itemPattern: /^(\\s*)([-+*])\\s+\\[([ xX])\\]\\s+(.*)$/,\n * extractItemData: (match) => ({\n * indentLevel: match[1].length,\n * mainContent: match[4],\n * checked: match[3].toLowerCase() === 'x'\n * }),\n * createToken: (data, nestedTokens) => ({\n * type: 'taskItem',\n * checked: data.checked,\n * text: data.mainContent,\n * nestedTokens\n * })\n * }, lexer)\n * ```\n */\nexport function parseIndentedBlocks(\n src: string,\n config: BlockParserConfig,\n lexer: {\n inlineTokens: (src: string) => any[]\n blockTokens: (src: string) => any[]\n },\n):\n | {\n items: ParsedBlock[]\n raw: string\n }\n | undefined {\n const lines = src.split('\\n')\n const items: ParsedBlock[] = []\n let totalRaw = ''\n let i = 0\n const baseIndentSize = config.baseIndentSize || 2\n\n while (i < lines.length) {\n const currentLine = lines[i]\n const itemMatch = currentLine.match(config.itemPattern)\n\n if (!itemMatch) {\n // Not a matching item - stop if we have items, otherwise this isn't our block type\n if (items.length > 0) {\n break\n } else if (currentLine.trim() === '') {\n i += 1\n totalRaw = `${totalRaw}${currentLine}\\n`\n continue\n } else {\n return undefined\n }\n }\n\n const itemData = config.extractItemData(itemMatch)\n const { indentLevel, mainContent } = itemData\n totalRaw = `${totalRaw}${currentLine}\\n`\n\n // Collect content for this item (including nested items)\n const itemContent = [mainContent] // Start with the main text\n i += 1\n\n // Look ahead for nested content (indented more than current item)\n while (i < lines.length) {\n const nextLine = lines[i]\n\n if (nextLine.trim() === '') {\n // Empty line - might be end of nested content\n const nextNonEmptyIndex = lines.slice(i + 1).findIndex(l => l.trim() !== '')\n if (nextNonEmptyIndex === -1) {\n // No more content\n break\n }\n\n const nextNonEmpty = lines[i + 1 + nextNonEmptyIndex]\n const nextIndent = nextNonEmpty.match(/^(\\s*)/)?.[1]?.length || 0\n\n if (nextIndent > indentLevel) {\n // Nested content continues after empty line\n itemContent.push(nextLine)\n totalRaw = `${totalRaw}${nextLine}\\n`\n i += 1\n continue\n } else {\n // End of nested content\n break\n }\n }\n\n const nextIndent = nextLine.match(/^(\\s*)/)?.[1]?.length || 0\n\n if (nextIndent > indentLevel) {\n // This is nested content for the current item\n itemContent.push(nextLine)\n totalRaw = `${totalRaw}${nextLine}\\n`\n i += 1\n } else {\n // Same or less indentation - this belongs to parent level\n break\n }\n }\n\n // Parse nested content if present\n let nestedTokens: any[] | undefined\n const nestedContent = itemContent.slice(1)\n\n if (nestedContent.length > 0) {\n // Remove the base indentation from nested content\n const dedentedNested = nestedContent\n .map(nestedLine => nestedLine.slice(indentLevel + baseIndentSize)) // Remove base indent + 2 spaces\n .join('\\n')\n\n if (dedentedNested.trim()) {\n // Use custom nested parser if provided, otherwise fall back to default\n if (config.customNestedParser) {\n nestedTokens = config.customNestedParser(dedentedNested)\n } else {\n nestedTokens = lexer.blockTokens(dedentedNested)\n }\n }\n }\n\n // Create the token using the provided factory function\n const token = config.createToken(itemData, nestedTokens)\n items.push(token)\n }\n\n if (items.length === 0) {\n return undefined\n }\n\n return {\n items,\n raw: totalRaw,\n }\n}\n","import type { JSONContent } from '@tiptap/core'\n\n/**\n * @fileoverview Utility functions for rendering nested content in markdown.\n *\n * This module provides reusable utilities for extensions that need to render\n * content with a prefix on the main line and properly indented nested content.\n */\n\n/**\n * Utility function for rendering content with a main line prefix and nested indented content.\n *\n * This function handles the common pattern of rendering content with:\n * 1. A main line with a prefix (like \"- \" for lists, \"> \" for blockquotes, etc.)\n * 2. Nested content that gets indented properly\n *\n * @param node - The ProseMirror node representing the content\n * @param h - The markdown renderer helper\n * @param prefixOrGenerator - Either a string prefix or a function that generates the prefix from context\n * @param ctx - Optional context object (used when prefixOrGenerator is a function)\n * @returns The rendered markdown string\n *\n * @example\n * ```ts\n * // For a bullet list item with static prefix\n * return renderNestedMarkdownContent(node, h, '- ')\n *\n * // For a task item with static prefix\n * const prefix = `- [${node.attrs?.checked ? 'x' : ' '}] `\n * return renderNestedMarkdownContent(node, h, prefix)\n *\n * // For a blockquote with static prefix\n * return renderNestedMarkdownContent(node, h, '> ')\n *\n * // For content with dynamic prefix based on context\n * return renderNestedMarkdownContent(node, h, ctx => {\n * if (ctx.parentType === 'orderedList') {\n * return `${ctx.index + 1}. `\n * }\n * return '- '\n * }, ctx)\n *\n * // Custom extension example\n * const CustomContainer = Node.create({\n * name: 'customContainer',\n * // ... other config\n * markdown: {\n * render: (node, h) => {\n * const type = node.attrs?.type || 'info'\n * return renderNestedMarkdownContent(node, h, `[${type}] `)\n * }\n * }\n * })\n * ```\n */\nexport function renderNestedMarkdownContent(\n node: JSONContent,\n h: {\n renderChildren: (nodes: JSONContent[]) => string\n indent: (text: string) => string\n },\n prefixOrGenerator: string | ((ctx: any) => string),\n ctx?: any,\n): string {\n if (!node || !Array.isArray(node.content)) {\n return ''\n }\n\n // Determine the prefix based on the input\n const prefix = typeof prefixOrGenerator === 'function' ? prefixOrGenerator(ctx) : prefixOrGenerator\n\n const [content, ...children] = node.content\n\n // Render the main content (typically a paragraph)\n const mainContent = h.renderChildren([content])\n const output = [`${prefix}${mainContent}`]\n\n // Handle nested children with proper indentation\n if (children && children.length > 0) {\n children.forEach(child => {\n const childContent = h.renderChildren([child])\n if (childContent) {\n // Split the child content by lines and indent each line\n const indentedChild = childContent\n .split('\\n')\n .map(line => (line ? h.indent(line) : ''))\n .join('\\n')\n output.push(indentedChild)\n }\n })\n }\n\n return output.join('\\n')\n}\n","import type { Mark } from '@tiptap/pm/model'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nimport type { Editor } from './Editor.js'\nimport type { MarkViewProps, MarkViewRendererOptions } from './types.js'\nimport { isAndroid, isiOS } from './utilities/index.js'\n\nexport function updateMarkViewAttributes(checkMark: Mark, editor: Editor, attrs: Record<string, any> = {}): void {\n const { state } = editor\n const { doc, tr } = state\n const thisMark = checkMark\n\n doc.descendants((node, pos) => {\n const from = tr.mapping.map(pos)\n const to = tr.mapping.map(pos) + node.nodeSize\n let foundMark: Mark | null = null\n\n // find the mark on the current node\n node.marks.forEach(mark => {\n if (mark !== thisMark) {\n return false\n }\n\n foundMark = mark\n })\n\n if (!foundMark) {\n return\n }\n\n // check if we need to update given the attributes\n let needsUpdate = false\n Object.keys(attrs).forEach(k => {\n if (attrs[k] !== foundMark!.attrs[k]) {\n needsUpdate = true\n }\n })\n\n if (needsUpdate) {\n const updatedMark = checkMark.type.create({\n ...checkMark.attrs,\n ...attrs,\n })\n\n tr.removeMark(from, to, checkMark.type)\n tr.addMark(from, to, updatedMark)\n }\n })\n\n if (tr.docChanged) {\n editor.view.dispatch(tr)\n }\n}\n\nexport class MarkView<Component, Options extends MarkViewRendererOptions = MarkViewRendererOptions> {\n component: Component\n editor: Editor\n options: Options\n mark: MarkViewProps['mark']\n HTMLAttributes: MarkViewProps['HTMLAttributes']\n\n constructor(component: Component, props: MarkViewProps, options?: Partial<Options>) {\n this.component = component\n this.editor = props.editor\n this.options = { ...options } as Options\n this.mark = props.mark\n this.HTMLAttributes = props.HTMLAttributes\n }\n\n get dom(): HTMLElement {\n return this.editor.view.dom\n }\n\n get contentDOM(): HTMLElement | null {\n return null\n }\n\n /**\n * Update the attributes of the mark in the document.\n * @param attrs The attributes to update.\n */\n updateAttributes(attrs: Record<string, any>, checkMark?: Mark): void {\n updateMarkViewAttributes(checkMark || this.mark, this.editor, attrs)\n }\n\n ignoreMutation(mutation: ViewMutationRecord): boolean {\n if (!this.dom || !this.contentDOM) {\n return true\n }\n\n if (typeof this.options.ignoreMutation === 'function') {\n return this.options.ignoreMutation({ mutation })\n }\n\n if (mutation.type === 'selection') {\n return false\n }\n\n if (\n this.dom.contains(mutation.target) &&\n mutation.type === 'childList' &&\n (isiOS() || isAndroid()) &&\n this.editor.isFocused\n ) {\n const changedNodes = [...Array.from(mutation.addedNodes), ...Array.from(mutation.removedNodes)] as HTMLElement[]\n\n if (changedNodes.every(node => node.isContentEditable)) {\n return false\n }\n }\n\n if (this.contentDOM === mutation.target && mutation.type === 'attributes') {\n return true\n }\n\n if (this.contentDOM.contains(mutation.target)) {\n return false\n }\n\n return true\n }\n}\n","import type { DOMOutputSpec, Node as ProseMirrorNode, NodeSpec, NodeType } from '@tiptap/pm/model'\n\nimport type { Editor } from './Editor.js'\nimport type { ExtendableConfig } from './Extendable.js'\nimport { Extendable } from './Extendable.js'\nimport type { Attributes, NodeViewRenderer, ParentConfig } from './types.js'\n\nexport interface NodeConfig<Options = any, Storage = any>\n extends ExtendableConfig<Options, Storage, NodeConfig<Options, Storage>, NodeType> {\n /**\n * Node View\n */\n addNodeView?:\n | ((this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: NodeType\n parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView']\n }) => NodeViewRenderer | null)\n | null\n\n /**\n * Defines if this node should be a top level node (doc)\n * @default false\n * @example true\n */\n topNode?: boolean\n\n /**\n * The content expression for this node, as described in the [schema\n * guide](/docs/guide/#schema.content_expressions). When not given,\n * the node does not allow any content.\n *\n * You can read more about it on the Prosemirror documentation here\n * @see https://prosemirror.net/docs/guide/#schema.content_expressions\n * @default undefined\n * @example content: 'block+'\n * @example content: 'headline paragraph block*'\n */\n content?:\n | NodeSpec['content']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['content']\n editor?: Editor\n }) => NodeSpec['content'])\n\n /**\n * The marks that are allowed inside of this node. May be a\n * space-separated string referring to mark names or groups, `\"_\"`\n * to explicitly allow all marks, or `\"\"` to disallow marks. When\n * not given, nodes with inline content default to allowing all\n * marks, other nodes default to not allowing marks.\n *\n * @example marks: 'strong em'\n */\n marks?:\n | NodeSpec['marks']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['marks']\n editor?: Editor\n }) => NodeSpec['marks'])\n\n /**\n * The group or space-separated groups to which this node belongs,\n * which can be referred to in the content expressions for the\n * schema.\n *\n * By default Tiptap uses the groups 'block' and 'inline' for nodes. You\n * can also use custom groups if you want to group specific nodes together\n * and handle them in your schema.\n * @example group: 'block'\n * @example group: 'inline'\n * @example group: 'customBlock' // this uses a custom group\n */\n group?:\n | NodeSpec['group']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['group']\n editor?: Editor\n }) => NodeSpec['group'])\n\n /**\n * Should be set to true for inline nodes. (Implied for text nodes.)\n */\n inline?:\n | NodeSpec['inline']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['inline']\n editor?: Editor\n }) => NodeSpec['inline'])\n\n /**\n * Can be set to true to indicate that, though this isn't a [leaf\n * node](https://prosemirror.net/docs/ref/#model.NodeType.isLeaf), it doesn't have directly editable\n * content and should be treated as a single unit in the view.\n *\n * @example atom: true\n */\n atom?:\n | NodeSpec['atom']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['atom']\n editor?: Editor\n }) => NodeSpec['atom'])\n\n /**\n * Controls whether nodes of this type can be selected as a [node\n * selection](https://prosemirror.net/docs/ref/#state.NodeSelection). Defaults to true for non-text\n * nodes.\n *\n * @default true\n * @example selectable: false\n */\n selectable?:\n | NodeSpec['selectable']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['selectable']\n editor?: Editor\n }) => NodeSpec['selectable'])\n\n /**\n * Determines whether nodes of this type can be dragged without\n * being selected. Defaults to false.\n *\n * @default: false\n * @example: draggable: true\n */\n draggable?:\n | NodeSpec['draggable']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['draggable']\n editor?: Editor\n }) => NodeSpec['draggable'])\n\n /**\n * Can be used to indicate that this node contains code, which\n * causes some commands to behave differently.\n */\n code?:\n | NodeSpec['code']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['code']\n editor?: Editor\n }) => NodeSpec['code'])\n\n /**\n * Controls way whitespace in this a node is parsed. The default is\n * `\"normal\"`, which causes the [DOM parser](https://prosemirror.net/docs/ref/#model.DOMParser) to\n * collapse whitespace in normal mode, and normalize it (replacing\n * newlines and such with spaces) otherwise. `\"pre\"` causes the\n * parser to preserve spaces inside the node. When this option isn't\n * given, but [`code`](https://prosemirror.net/docs/ref/#model.NodeSpec.code) is true, `whitespace`\n * will default to `\"pre\"`. Note that this option doesn't influence\n * the way the node is rendered—that should be handled by `toDOM`\n * and/or styling.\n */\n whitespace?:\n | NodeSpec['whitespace']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace']\n editor?: Editor\n }) => NodeSpec['whitespace'])\n\n /**\n * Allows a **single** node to be set as linebreak equivalent (e.g. hardBreak).\n * When converting between block types that have whitespace set to \"pre\"\n * and don't support the linebreak node (e.g. codeBlock) and other block types\n * that do support the linebreak node (e.g. paragraphs) - this node will be used\n * as the linebreak instead of stripping the newline.\n *\n * See [linebreakReplacement](https://prosemirror.net/docs/ref/#model.NodeSpec.linebreakReplacement).\n */\n linebreakReplacement?:\n | NodeSpec['linebreakReplacement']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['linebreakReplacement']\n editor?: Editor\n }) => NodeSpec['linebreakReplacement'])\n\n /**\n * When enabled, enables both\n * [`definingAsContext`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingAsContext) and\n * [`definingForContent`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingForContent).\n *\n * @default false\n * @example isolating: true\n */\n defining?:\n | NodeSpec['defining']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['defining']\n editor?: Editor\n }) => NodeSpec['defining'])\n\n /**\n * When enabled (default is false), the sides of nodes of this type\n * count as boundaries that regular editing operations, like\n * backspacing or lifting, won't cross. An example of a node that\n * should probably have this enabled is a table cell.\n */\n isolating?:\n | NodeSpec['isolating']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['isolating']\n editor?: Editor\n }) => NodeSpec['isolating'])\n\n /**\n * Associates DOM parser information with this node, which can be\n * used by [`DOMParser.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMParser^fromSchema) to\n * automatically derive a parser. The `node` field in the rules is\n * implied (the name of this node will be filled in automatically).\n * If you supply your own parser, you do not need to also specify\n * parsing rules in your schema.\n *\n * @example parseHTML: [{ tag: 'div', attrs: { 'data-id': 'my-block' } }]\n */\n parseHTML?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML']\n editor?: Editor\n }) => NodeSpec['parseDOM']\n\n /**\n * A description of a DOM structure. Can be either a string, which is\n * interpreted as a text node, a DOM node, which is interpreted as\n * itself, a `{dom, contentDOM}` object, or an array.\n *\n * An array describes a DOM element. The first value in the array\n * should be a string—the name of the DOM element, optionally prefixed\n * by a namespace URL and a space. If the second element is plain\n * object, it is interpreted as a set of attributes for the element.\n * Any elements after that (including the 2nd if it's not an attribute\n * object) are interpreted as children of the DOM elements, and must\n * either be valid `DOMOutputSpec` values, or the number zero.\n *\n * The number zero (pronounced “hole”) is used to indicate the place\n * where a node's child nodes should be inserted. If it occurs in an\n * output spec, it should be the only child element in its parent\n * node.\n *\n * @example toDOM: ['div[data-id=\"my-block\"]', { class: 'my-block' }, 0]\n */\n renderHTML?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML']\n editor?: Editor\n },\n props: {\n node: ProseMirrorNode\n HTMLAttributes: Record<string, any>\n },\n ) => DOMOutputSpec)\n | null\n\n /**\n * renders the node as text\n * @example renderText: () => 'foo\n */\n renderText?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['renderText']\n editor?: Editor\n },\n props: {\n node: ProseMirrorNode\n pos: number\n parent: ProseMirrorNode\n index: number\n },\n ) => string)\n | null\n\n /**\n * Add attributes to the node\n * @example addAttributes: () => ({ class: 'foo' })\n */\n addAttributes?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes']\n editor?: Editor\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n }) => Attributes | {}\n}\n\n/**\n * The Node class is used to create custom node extensions.\n * @see https://tiptap.dev/api/extensions#create-a-new-extension\n */\nexport class Node<Options = any, Storage = any> extends Extendable<Options, Storage, NodeConfig<Options, Storage>> {\n type = 'node'\n\n /**\n * Create a new Node instance\n * @param config - Node configuration object or a function that returns a configuration object\n */\n static create<O = any, S = any>(config: Partial<NodeConfig<O, S>> | (() => Partial<NodeConfig<O, S>>) = {}) {\n // If the config is a function, execute it to get the configuration object\n const resolvedConfig = typeof config === 'function' ? config() : config\n return new Node<O, S>(resolvedConfig)\n }\n\n configure(options?: Partial<Options>) {\n return super.configure(options) as Node<Options, Storage>\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig extends NodeConfig<ExtendedOptions, ExtendedStorage> = NodeConfig<ExtendedOptions, ExtendedStorage>,\n >(\n extendedConfig?:\n | (() => Partial<ExtendedConfig>)\n | (Partial<ExtendedConfig> &\n ThisType<{\n name: string\n options: ExtendedOptions\n storage: ExtendedStorage\n editor: Editor\n type: NodeType\n }>),\n ): Node<ExtendedOptions, ExtendedStorage> {\n // If the extended config is a function, execute it to get the configuration object\n const resolvedConfig = typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig\n return super.extend(resolvedConfig) as Node<ExtendedOptions, ExtendedStorage>\n }\n}\n","import { NodeSelection } from '@tiptap/pm/state'\nimport type { NodeView as ProseMirrorNodeView, ViewMutationRecord } from '@tiptap/pm/view'\n\nimport type { Editor as CoreEditor } from './Editor.js'\nimport type { DecorationWithType, NodeViewRendererOptions, NodeViewRendererProps } from './types.js'\nimport { isAndroid } from './utilities/isAndroid.js'\nimport { isiOS } from './utilities/isiOS.js'\n\n/**\n * Node views are used to customize the rendered DOM structure of a node.\n * @see https://tiptap.dev/guide/node-views\n */\nexport class NodeView<\n Component,\n NodeEditor extends CoreEditor = CoreEditor,\n Options extends NodeViewRendererOptions = NodeViewRendererOptions,\n> implements ProseMirrorNodeView\n{\n component: Component\n\n editor: NodeEditor\n\n options: Options\n\n extension: NodeViewRendererProps['extension']\n\n node: NodeViewRendererProps['node']\n\n decorations: NodeViewRendererProps['decorations']\n\n innerDecorations: NodeViewRendererProps['innerDecorations']\n\n view: NodeViewRendererProps['view']\n\n getPos: NodeViewRendererProps['getPos']\n\n HTMLAttributes: NodeViewRendererProps['HTMLAttributes']\n\n isDragging = false\n\n constructor(component: Component, props: NodeViewRendererProps, options?: Partial<Options>) {\n this.component = component\n this.editor = props.editor as NodeEditor\n this.options = {\n stopEvent: null,\n ignoreMutation: null,\n ...options,\n } as Options\n this.extension = props.extension\n this.node = props.node\n this.decorations = props.decorations as DecorationWithType[]\n this.innerDecorations = props.innerDecorations\n this.view = props.view\n this.HTMLAttributes = props.HTMLAttributes\n this.getPos = props.getPos\n this.mount()\n }\n\n mount() {\n // eslint-disable-next-line\n return\n }\n\n get dom(): HTMLElement {\n return this.editor.view.dom as HTMLElement\n }\n\n get contentDOM(): HTMLElement | null {\n return null\n }\n\n onDragStart(event: DragEvent) {\n const { view } = this.editor\n const target = event.target as HTMLElement\n\n // get the drag handle element\n // `closest` is not available for text nodes so we may have to use its parent\n const dragHandle =\n target.nodeType === 3 ? target.parentElement?.closest('[data-drag-handle]') : target.closest('[data-drag-handle]')\n\n if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {\n return\n }\n\n let x = 0\n let y = 0\n\n // calculate offset for drag element if we use a different drag handle element\n if (this.dom !== dragHandle) {\n const domBox = this.dom.getBoundingClientRect()\n const handleBox = dragHandle.getBoundingClientRect()\n\n // In React, we have to go through nativeEvent to reach offsetX/offsetY.\n const offsetX = event.offsetX ?? (event as any).nativeEvent?.offsetX\n const offsetY = event.offsetY ?? (event as any).nativeEvent?.offsetY\n\n x = handleBox.x - domBox.x + offsetX\n y = handleBox.y - domBox.y + offsetY\n }\n\n const clonedNode = this.dom.cloneNode(true) as HTMLElement\n\n // Preserve the visual size of the original when using the clone as\n // the drag image.\n try {\n const domBox = this.dom.getBoundingClientRect()\n clonedNode.style.width = `${Math.round(domBox.width)}px`\n clonedNode.style.height = `${Math.round(domBox.height)}px`\n clonedNode.style.boxSizing = 'border-box'\n // Ensure the clone doesn't capture pointer events while offscreen\n clonedNode.style.pointerEvents = 'none'\n } catch {\n // ignore measurement errors (e.g. if element not in DOM)\n }\n\n // Some browsers (notably Safari) require the element passed to\n // setDragImage to be present in the DOM. Using a detached node can\n // cause the drag to immediately end.\n let dragImageWrapper: HTMLElement | null = null\n\n try {\n dragImageWrapper = document.createElement('div')\n dragImageWrapper.style.position = 'absolute'\n dragImageWrapper.style.top = '-9999px'\n dragImageWrapper.style.left = '-9999px'\n dragImageWrapper.style.pointerEvents = 'none'\n dragImageWrapper.appendChild(clonedNode)\n document.body.appendChild(dragImageWrapper)\n\n event.dataTransfer?.setDragImage(clonedNode, x, y)\n } finally {\n // Remove the wrapper on the next tick so the browser can use the\n // element as the drag image. A 0ms timeout is enough in practice.\n if (dragImageWrapper) {\n setTimeout(() => {\n try {\n dragImageWrapper?.remove()\n } catch {\n // ignore removal errors\n }\n }, 0)\n }\n }\n\n const pos = this.getPos()\n\n if (typeof pos !== 'number') {\n return\n }\n // we need to tell ProseMirror that we want to move the whole node\n // so we create a NodeSelection\n const selection = NodeSelection.create(view.state.doc, pos)\n const transaction = view.state.tr.setSelection(selection)\n\n view.dispatch(transaction)\n }\n\n stopEvent(event: Event) {\n if (!this.dom) {\n return false\n }\n\n if (typeof this.options.stopEvent === 'function') {\n return this.options.stopEvent({ event })\n }\n\n const target = event.target as HTMLElement\n const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target)\n\n // any event from child nodes should be handled by ProseMirror\n if (!isInElement) {\n return false\n }\n\n const isDragEvent = event.type.startsWith('drag')\n const isDropEvent = event.type === 'drop'\n const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable\n\n // any input event within node views should be ignored by ProseMirror\n if (isInput && !isDropEvent && !isDragEvent) {\n return true\n }\n\n const { isEditable } = this.editor\n const { isDragging } = this\n const isDraggable = !!this.node.type.spec.draggable\n const isSelectable = NodeSelection.isSelectable(this.node)\n const isCopyEvent = event.type === 'copy'\n const isPasteEvent = event.type === 'paste'\n const isCutEvent = event.type === 'cut'\n const isClickEvent = event.type === 'mousedown'\n\n // ProseMirror tries to drag selectable nodes\n // even if `draggable` is set to `false`\n // this fix prevents that\n if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {\n event.preventDefault()\n }\n\n if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {\n event.preventDefault()\n return false\n }\n\n // we have to store that dragging started\n if (isDraggable && isEditable && !isDragging && isClickEvent) {\n const dragHandle = target.closest('[data-drag-handle]')\n const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle))\n\n if (isValidDragHandle) {\n this.isDragging = true\n\n document.addEventListener(\n 'dragend',\n () => {\n this.isDragging = false\n },\n { once: true },\n )\n\n document.addEventListener(\n 'drop',\n () => {\n this.isDragging = false\n },\n { once: true },\n )\n\n document.addEventListener(\n 'mouseup',\n () => {\n this.isDragging = false\n },\n { once: true },\n )\n }\n }\n\n // these events are handled by prosemirror\n if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || (isClickEvent && isSelectable)) {\n return false\n }\n\n return true\n }\n\n /**\n * Called when a DOM [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or a selection change happens within the view.\n * @return `false` if the editor should re-read the selection or re-parse the range around the mutation\n * @return `true` if it can safely be ignored.\n */\n ignoreMutation(mutation: ViewMutationRecord) {\n if (!this.dom || !this.contentDOM) {\n return true\n }\n\n if (typeof this.options.ignoreMutation === 'function') {\n return this.options.ignoreMutation({ mutation })\n }\n\n // a leaf/atom node is like a black box for ProseMirror\n // and should be fully handled by the node view\n if (this.node.isLeaf || this.node.isAtom) {\n return true\n }\n\n // ProseMirror should handle any selections\n if (mutation.type === 'selection') {\n return false\n }\n\n // try to prevent a bug on iOS and Android that will break node views on enter\n // this is because ProseMirror can’t preventDispatch on enter\n // this will lead to a re-render of the node view on enter\n // see: https://github.com/ueberdosis/tiptap/issues/1214\n // see: https://github.com/ueberdosis/tiptap/issues/2534\n if (\n this.dom.contains(mutation.target) &&\n mutation.type === 'childList' &&\n (isiOS() || isAndroid()) &&\n this.editor.isFocused\n ) {\n const changedNodes = [...Array.from(mutation.addedNodes), ...Array.from(mutation.removedNodes)] as HTMLElement[]\n\n // we’ll check if every changed node is contentEditable\n // to make sure it’s probably mutated by ProseMirror\n if (changedNodes.every(node => node.isContentEditable)) {\n return false\n }\n }\n\n // we will allow mutation contentDOM with attributes\n // so we can for example adding classes within our node view\n if (this.contentDOM === mutation.target && mutation.type === 'attributes') {\n return true\n }\n\n // ProseMirror should handle any changes within contentDOM\n if (this.contentDOM.contains(mutation.target)) {\n return false\n }\n\n return true\n }\n\n /**\n * Update the attributes of the prosemirror node.\n */\n updateAttributes(attributes: Record<string, any>): void {\n this.editor.commands.command(({ tr }) => {\n const pos = this.getPos()\n\n if (typeof pos !== 'number') {\n return false\n }\n\n tr.setNodeMarkup(pos, undefined, {\n ...this.node.attrs,\n ...attributes,\n })\n\n return true\n })\n }\n\n /**\n * Delete the node.\n */\n deleteNode(): void {\n const from = this.getPos()\n\n if (typeof from !== 'number') {\n return\n }\n const to = from + this.node.nodeSize\n\n this.editor.commands.deleteRange({ from, to })\n }\n}\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarksBetween } from '../helpers/getMarksBetween.js'\nimport type { PasteRuleFinder } from '../PasteRule.js'\nimport { PasteRule } from '../PasteRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an paste rule that adds a mark when the\n * matched text is pasted into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules\n */\nexport function markPasteRule(config: {\n find: PasteRuleFinder\n type: MarkType\n getAttributes?:\n | Record<string, any>\n | ((match: ExtendedRegExpMatchArray, event: ClipboardEvent) => Record<string, any>)\n | false\n | null\n}) {\n return new PasteRule({\n find: config.find,\n handler: ({ state, range, match, pasteEvent }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match, pasteEvent)\n\n if (attributes === false || attributes === null) {\n return null\n }\n\n const { tr } = state\n const captureGroup = match[match.length - 1]\n const fullMatch = match[0]\n let markEnd = range.to\n\n if (captureGroup) {\n const startSpaces = fullMatch.search(/\\S/)\n const textStart = range.from + fullMatch.indexOf(captureGroup)\n const textEnd = textStart + captureGroup.length\n\n const excludedMarks = getMarksBetween(range.from, range.to, state.doc)\n .filter(item => {\n // @ts-ignore\n const excluded = item.mark.type.excluded as MarkType[]\n\n return excluded.find(type => type === config.type && type !== item.mark.type)\n })\n .filter(item => item.to > textStart)\n\n if (excludedMarks.length) {\n return null\n }\n\n if (textEnd < range.to) {\n tr.delete(textEnd, range.to)\n }\n\n if (textStart > range.from) {\n tr.delete(range.from + startSpaces, textStart)\n }\n\n markEnd = range.from + startSpaces + captureGroup.length\n\n tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}))\n\n tr.removeStoredMark(config.type)\n }\n },\n })\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport type { PasteRuleFinder } from '../PasteRule.js'\nimport { PasteRule } from '../PasteRule.js'\nimport type { ExtendedRegExpMatchArray, JSONContent } from '../types.js'\nimport { callOrReturn } from '../utilities/index.js'\n\n/**\n * Build an paste rule that adds a node when the\n * matched text is pasted into it.\n * @see https://tiptap.dev/docs/editor/api/paste-rules\n */\nexport function nodePasteRule(config: {\n find: PasteRuleFinder\n type: NodeType\n getAttributes?:\n | Record<string, any>\n | ((match: ExtendedRegExpMatchArray, event: ClipboardEvent) => Record<string, any>)\n | false\n | null\n getContent?: JSONContent[] | ((attrs: Record<string, any>) => JSONContent[]) | false | null\n}) {\n return new PasteRule({\n find: config.find,\n handler({ match, chain, range, pasteEvent }) {\n const attributes = callOrReturn(config.getAttributes, undefined, match, pasteEvent)\n const content = callOrReturn(config.getContent, undefined, attributes)\n\n if (attributes === false || attributes === null) {\n return null\n }\n\n const node = { type: config.type.name, attrs: attributes } as JSONContent\n\n if (content) {\n node.content = content\n }\n\n if (match.input) {\n chain().deleteRange(range).insertContentAt(range.from, node)\n }\n },\n })\n}\n","import type { PasteRuleFinder } from '../PasteRule.js'\nimport { PasteRule } from '../PasteRule.js'\n\n/**\n * Build an paste rule that replaces text when the\n * matched text is pasted into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules\n */\nexport function textPasteRule(config: { find: PasteRuleFinder; replace: string }) {\n return new PasteRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n let insert = config.replace\n let start = range.from\n const end = range.to\n\n if (match[1]) {\n const offset = match[0].lastIndexOf(match[1])\n\n insert += match[0].slice(offset + match[1].length)\n start += offset\n\n const cutOff = start - end\n\n if (cutOff > 0) {\n insert = match[0].slice(offset - cutOff, offset) + insert\n start = end\n }\n }\n\n state.tr.insertText(insert, start, end)\n },\n })\n}\n","import type { Transaction } from '@tiptap/pm/state'\n\nexport interface TrackerResult {\n position: number\n deleted: boolean\n}\n\nexport class Tracker {\n transaction: Transaction\n\n currentStep: number\n\n constructor(transaction: Transaction) {\n this.transaction = transaction\n this.currentStep = this.transaction.steps.length\n }\n\n map(position: number): TrackerResult {\n let deleted = false\n\n const mappedPosition = this.transaction.steps.slice(this.currentStep).reduce((newPosition, step) => {\n const mapResult = step.getMap().mapResult(newPosition)\n\n if (mapResult.deleted) {\n deleted = true\n }\n\n return mapResult.pos\n }, position)\n\n return {\n position: mappedPosition,\n deleted,\n }\n }\n}\n","import { Mark, mergeAttributes } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport type { Editor } from '@tiptap/core'\nimport type { CommentWithUser } from './comments'\n\nexport interface CommentMarkOptions {\n onCommentClick?: (commentId: string) => void\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n comment: {\n setComment: (commentId: string) => ReturnType\n unsetComment: (commentId: string) => ReturnType\n }\n }\n}\n\nexport const CommentMark = Mark.create<CommentMarkOptions>({\n name: 'comment',\n\n addOptions() {\n return {\n onCommentClick: undefined,\n }\n },\n\n addAttributes() {\n return {\n commentId: {\n default: null,\n parseHTML: (element) => element.getAttribute('data-comment-id'),\n renderHTML: (attributes) => ({\n 'data-comment-id': attributes.commentId,\n }),\n },\n }\n },\n\n parseHTML() {\n return [{ tag: 'mark[data-comment-id]' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'mark',\n mergeAttributes(HTMLAttributes, {\n class:\n 'bg-yellow-200/50 dark:bg-yellow-500/30 dark:text-foreground cursor-pointer hover:bg-yellow-300/60 dark:hover:bg-yellow-500/40 transition-colors rounded-sm',\n }),\n 0,\n ]\n },\n\n addCommands() {\n return {\n setComment:\n (commentId: string) =>\n ({ commands }) => {\n return commands.setMark(this.name, { commentId })\n },\n unsetComment:\n (commentId: string) =>\n ({ tr, state }) => {\n const { doc } = state\n let found = false\n\n doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (mark.type.name === this.name && mark.attrs.commentId === commentId) {\n tr.removeMark(pos, pos + node.nodeSize, mark.type)\n found = true\n }\n })\n })\n\n return found\n },\n }\n },\n\n addProseMirrorPlugins() {\n const { onCommentClick } = this.options\n\n return [\n new Plugin({\n key: new PluginKey('commentClick'),\n props: {\n handleClick(view, pos) {\n if (!onCommentClick) return false\n\n const { state } = view\n const $pos = state.doc.resolve(pos)\n const marks = $pos.marks()\n\n const commentMark = marks.find((mark) => mark.type.name === 'comment')\n if (commentMark && commentMark.attrs.commentId) {\n // Blur the editor to prevent keyboard popup on mobile\n ;(view.dom as HTMLElement).blur()\n onCommentClick(commentMark.attrs.commentId)\n return true\n }\n\n return false\n },\n },\n }),\n ]\n },\n})\n\n/**\n * Apply comment mark to the current selection\n */\nexport function addCommentMark(\n editor: Editor,\n commentId: string,\n from: number,\n to: number\n): void {\n // Guard against editor not being ready\n if (!editor.view || editor.isDestroyed) {\n console.warn('Cannot add comment mark: editor not ready')\n return\n }\n \n editor\n .chain()\n .setTextSelection({ from, to })\n .setComment(commentId)\n .run()\n}\n\n/**\n * Remove comment mark from the document\n */\nexport function removeCommentMark(editor: Editor, commentId: string): void {\n if (!editor.view || editor.isDestroyed) return\n editor.chain().unsetComment(commentId).run()\n}\n\n/**\n * Re-apply comment marks based on quoted text matching.\n * Called when loading a post with existing comments.\n */\nexport function applyCommentMarks(\n editor: Editor,\n comments: CommentWithUser[]\n): void {\n if (!editor.view || editor.isDestroyed) return\n \n const { doc } = editor.state\n const textContent = doc.textContent\n\n comments.forEach((comment) => {\n if (!comment.quotedText || comment.parentId) return // Skip replies\n\n const index = textContent.indexOf(comment.quotedText)\n if (index === -1) return // Text not found\n\n // Find the actual position in the document\n let currentPos = 0\n let startPos: number | null = null\n let endPos: number | null = null\n\n doc.descendants((node, pos) => {\n if (startPos !== null && endPos !== null) return false\n\n if (node.isText && node.text) {\n const nodeStart = currentPos\n const nodeEnd = currentPos + node.text.length\n\n if (startPos === null && nodeEnd > index) {\n // Start is in this node\n const offsetInNode = index - nodeStart\n startPos = pos + offsetInNode\n }\n\n if (startPos !== null && endPos === null) {\n const targetEnd = index + comment.quotedText.length\n if (nodeEnd >= targetEnd) {\n // End is in this node\n const offsetInNode = targetEnd - nodeStart\n endPos = pos + offsetInNode\n }\n }\n\n currentPos = nodeEnd\n }\n\n return true\n })\n\n if (startPos !== null && endPos !== null) {\n editor\n .chain()\n .setTextSelection({ from: startPos, to: endPos })\n .setComment(comment.id)\n .setTextSelection(endPos) // Deselect\n .run()\n }\n })\n}\n\n/**\n * Scroll to a comment mark in the editor\n */\nexport function scrollToComment(editor: Editor, commentId: string): void {\n if (!editor.view || editor.isDestroyed) return\n \n const { doc } = editor.state\n\n doc.descendants((node, pos) => {\n const commentMark = node.marks.find(\n (mark) => mark.type.name === 'comment' && mark.attrs.commentId === commentId\n )\n\n if (commentMark) {\n editor.chain().setTextSelection(pos).run()\n\n // Scroll to the selection\n const view = editor.view\n const coords = view.coordsAtPos(pos)\n const editorRect = view.dom.getBoundingClientRect()\n\n if (coords.top < editorRect.top || coords.bottom > editorRect.bottom) {\n view.dom.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }\n\n return false // Stop iteration\n }\n\n return true\n })\n}\n","'use client'\n\nimport { useState, useRef, useEffect, useCallback } from 'react'\nimport { createPortal } from 'react-dom'\nimport { X, MessageSquare, ArrowUp, Loader2, ChevronDown, ChevronRight } from 'lucide-react'\nimport { cn } from '../../lib/cn'\nimport { CommentWithUser } from '../../lib/comments'\nimport { CommentThread } from './CommentThread'\n\ninterface CommentsPanelProps {\n comments: CommentWithUser[]\n currentUserEmail: string\n isAdmin: boolean\n selectedText: string | null\n onCreateComment: (content: string) => Promise<void>\n onReply: (parentId: string, content: string) => Promise<void>\n onEdit: (commentId: string, content: string) => Promise<void>\n onDelete: (commentId: string) => Promise<void>\n onResolve: (commentId: string) => Promise<void>\n onCommentClick: (commentId: string) => void\n activeCommentId: string | null\n isOpen: boolean\n onClose: () => void\n onClearSelection: () => void\n}\n\nexport function CommentsPanel({\n comments,\n currentUserEmail,\n isAdmin,\n selectedText,\n onCreateComment,\n onReply,\n onEdit,\n onDelete,\n onResolve,\n onCommentClick,\n activeCommentId,\n isOpen,\n onClose,\n onClearSelection,\n}: CommentsPanelProps) {\n const [newComment, setNewComment] = useState('')\n const [isAnimating, setIsAnimating] = useState(false)\n const [isVisible, setIsVisible] = useState(false)\n const [mounted, setMounted] = useState(false)\n const [creating, setCreating] = useState(false)\n const [showResolved, setShowResolved] = useState(false)\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const commentsEndRef = useRef<HTMLDivElement>(null)\n\n // Separate open and resolved comments\n const openComments = comments.filter((c) => !c.resolved)\n const resolvedComments = comments.filter((c) => c.resolved)\n\n // Client-side only for portal\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Handle open/close animation and body scroll lock\n useEffect(() => {\n if (isOpen) {\n setIsVisible(true)\n document.body.style.overflow = 'hidden'\n document.body.style.position = 'fixed'\n document.body.style.width = '100%'\n document.body.style.top = `-${window.scrollY}px`\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setIsAnimating(true)\n })\n })\n } else {\n setIsAnimating(false)\n const scrollY = document.body.style.top\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.width = ''\n document.body.style.top = ''\n window.scrollTo(0, parseInt(scrollY || '0') * -1)\n const timer = setTimeout(() => {\n setIsVisible(false)\n }, 200)\n return () => clearTimeout(timer)\n }\n }, [isOpen])\n\n // Focus textarea when selected text changes\n useEffect(() => {\n if (selectedText && isOpen) {\n textareaRef.current?.focus()\n }\n }, [selectedText, isOpen])\n\n // Auto-resize textarea as content grows\n useEffect(() => {\n const textarea = textareaRef.current\n if (textarea) {\n textarea.style.height = 'auto'\n textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`\n }\n }, [newComment])\n\n const handleCreateComment = useCallback(async () => {\n if (!newComment.trim() || !selectedText) return\n setCreating(true)\n try {\n await onCreateComment(newComment.trim())\n setNewComment('')\n onClearSelection()\n } finally {\n setCreating(false)\n }\n }, [newComment, selectedText, onCreateComment, onClearSelection])\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.stopPropagation()\n handleCreateComment()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n if (selectedText) {\n onClearSelection()\n setNewComment('')\n } else {\n onClose()\n }\n }\n }\n\n if (!isVisible || !mounted) return null\n\n return createPortal(\n <>\n {/* Backdrop */}\n <div\n className={cn(\n 'fixed inset-0 h-[100dvh] bg-black/20 z-[60] transition-opacity duration-200',\n isAnimating ? 'opacity-100' : 'opacity-0'\n )}\n onClick={onClose}\n />\n\n {/* Panel */}\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Comments\"\n className={cn(\n 'fixed z-[70] flex flex-col bg-white dark:bg-gray-900 shadow-xl transition-transform duration-200 ease-out overflow-hidden',\n 'inset-x-0 top-0 h-[100dvh]',\n 'md:left-auto md:w-full md:max-w-[380px] md:border-l md:border-gray-200 md:dark:border-gray-700',\n isAnimating ? 'translate-x-0' : 'translate-x-full'\n )}\n >\n {/* Header */}\n <div className=\"flex-shrink-0 border-b border-gray-200 dark:border-gray-700 px-4 py-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-4 h-4 text-gray-500\" />\n <h2 className=\"font-medium\">Comments</h2>\n {openComments.length > 0 && (\n <span className=\"text-xs text-gray-500\">\n ({openComments.length})\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"w-8 h-8 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center justify-center text-gray-500\"\n aria-label=\"Close comments\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n {/* New comment section - shown when text is selected */}\n {selectedText && (\n <div className=\"flex-shrink-0 border-b border-gray-200 dark:border-gray-700 px-4 py-5 bg-gray-50/50 dark:bg-gray-800/30\">\n <div className=\"flex items-center justify-between mb-3\">\n <div className=\"flex-1 px-2 py-1 bg-yellow-100/50 dark:bg-yellow-900/30 rounded text-sm italic text-gray-600 dark:text-gray-400 line-clamp-2\">\n \"{selectedText}\"\n </div>\n <button\n type=\"button\"\n onClick={() => {\n onClearSelection()\n setNewComment('')\n }}\n disabled={creating}\n className=\"ml-2 text-xs text-gray-500 hover:text-gray-900 dark:hover:text-white disabled:opacity-50\"\n >\n Cancel\n </button>\n </div>\n <div className=\"flex items-end gap-2\">\n <textarea\n ref={textareaRef}\n value={newComment}\n onChange={(e) => setNewComment(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Add a comment...\"\n className=\"flex-1 min-h-[60px] max-h-[120px] px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 resize-none text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\"\n rows={2}\n enterKeyHint=\"send\"\n />\n <button\n type=\"button\"\n onClick={handleCreateComment}\n disabled={creating || !newComment.trim()}\n className=\"w-10 h-10 flex-shrink-0 rounded-full bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center disabled:opacity-50 hover:bg-gray-200 dark:hover:bg-gray-700\"\n >\n {creating ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <ArrowUp className=\"h-5 w-5\" />\n )}\n </button>\n </div>\n </div>\n )}\n\n {/* Comments list - hidden when composing new comment */}\n <div className={cn(\"flex-1 overflow-y-auto\", selectedText && \"hidden\")}>\n {comments.length === 0 ? (\n <div className=\"h-full flex items-center justify-center\">\n <div className=\"text-center max-w-xs px-6\">\n <MessageSquare className=\"w-8 h-8 mx-auto mb-2 text-gray-400\" />\n <p className=\"text-gray-500 text-sm\">\n No comments yet. Select text and click the comment button to add one.\n </p>\n </div>\n </div>\n ) : (\n <div className=\"p-4 space-y-3\">\n {/* Open comments */}\n {openComments.map((comment) => (\n <CommentThread\n key={comment.id}\n comment={comment}\n currentUserEmail={currentUserEmail}\n isAdmin={isAdmin}\n isActive={activeCommentId === comment.id}\n onReply={(content) => onReply(comment.id, content)}\n onEdit={(content) => onEdit(comment.id, content)}\n onDelete={() => onDelete(comment.id)}\n onResolve={() => onResolve(comment.id)}\n onClick={() => onCommentClick(comment.id)}\n />\n ))}\n\n {/* Resolved comments section */}\n {resolvedComments.length > 0 && (\n <div className=\"pt-2\">\n <button\n type=\"button\"\n onClick={() => setShowResolved(!showResolved)}\n className=\"w-full text-left text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors py-2\"\n >\n <span className=\"inline-flex items-center gap-1\">\n {showResolved ? <ChevronDown className=\"w-4 h-4\" /> : <ChevronRight className=\"w-4 h-4\" />}\n Resolved ({resolvedComments.length})\n </span>\n </button>\n {showResolved && (\n <div className=\"space-y-3 mt-2\">\n {resolvedComments.map((comment) => (\n <CommentThread\n key={comment.id}\n comment={comment}\n currentUserEmail={currentUserEmail}\n isAdmin={isAdmin}\n isActive={activeCommentId === comment.id}\n onReply={(content) => onReply(comment.id, content)}\n onEdit={(content) => onEdit(comment.id, content)}\n onDelete={() => onDelete(comment.id)}\n onResolve={() => onResolve(comment.id)}\n onClick={() => onCommentClick(comment.id)}\n />\n ))}\n </div>\n )}\n </div>\n )}\n\n <div ref={commentsEndRef} />\n </div>\n )}\n </div>\n </div>\n </>,\n document.body\n )\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { MoreHorizontal, Reply, Check, Trash2, Pencil } from 'lucide-react'\nimport { cn } from '../../lib/cn'\nimport { CommentWithUser, canEditComment, canDeleteComment } from '../../lib/comments'\nimport { Dropdown, DropdownItem, DropdownDivider } from './Dropdown'\n\n// Simple relative time formatter\nfunction formatRelativeTime(dateStr: string): string {\n const date = new Date(dateStr)\n const now = new Date()\n const diff = now.getTime() - date.getTime()\n const minutes = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n if (hours < 24) return `${hours}h ago`\n if (days < 7) return `${days}d ago`\n return date.toLocaleDateString()\n}\n\ninterface CommentThreadProps {\n comment: CommentWithUser\n currentUserEmail: string\n isAdmin: boolean\n isActive: boolean\n onReply: (content: string) => Promise<void>\n onEdit: (content: string) => Promise<void>\n onDelete: () => Promise<void>\n onResolve: () => Promise<void>\n onClick: () => void\n}\n\nexport function CommentThread({\n comment,\n currentUserEmail,\n isAdmin,\n isActive,\n onReply,\n onEdit,\n onDelete,\n onResolve,\n onClick,\n}: CommentThreadProps) {\n const [isReplying, setIsReplying] = useState(false)\n const [isEditing, setIsEditing] = useState(false)\n const [replyContent, setReplyContent] = useState('')\n const [editContent, setEditContent] = useState(comment.content)\n const [loading, setLoading] = useState(false)\n\n const canEdit = canEditComment(comment, currentUserEmail)\n const canDelete = canDeleteComment(comment, currentUserEmail, isAdmin)\n const isOwn = comment.user.email === currentUserEmail\n\n const handleReply = async () => {\n if (!replyContent.trim()) return\n setLoading(true)\n try {\n await onReply(replyContent.trim())\n setReplyContent('')\n setIsReplying(false)\n } finally {\n setLoading(false)\n }\n }\n\n const handleEdit = async () => {\n if (!editContent.trim()) return\n setLoading(true)\n try {\n await onEdit(editContent.trim())\n setIsEditing(false)\n } finally {\n setLoading(false)\n }\n }\n\n const handleDelete = async () => {\n if (!confirm('Delete this comment?')) return\n setLoading(true)\n try {\n await onDelete()\n } finally {\n setLoading(false)\n }\n }\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onClick()\n }\n }}\n className={cn(\n 'rounded-lg border p-3 transition-colors cursor-pointer',\n isActive\n ? 'border-yellow-400 bg-yellow-50/50 dark:border-yellow-600 dark:bg-yellow-900/20'\n : 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900',\n comment.resolved && 'opacity-60'\n )}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between gap-2 mb-2\">\n <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"font-medium\">\n {isOwn ? 'You' : comment.user.name || comment.user.email.split('@')[0]}\n </span>\n <span className=\"text-gray-500 dark:text-gray-400\">\n {formatRelativeTime(comment.createdAt)}\n </span>\n {comment.resolved && (\n <span className=\"inline-flex items-center gap-1 text-xs text-green-600 dark:text-green-400\">\n <Check className=\"w-3 h-3\" />\n Resolved\n </span>\n )}\n </div>\n\n {/* Dropdown menu */}\n <div onClick={(e) => e.stopPropagation()}>\n <Dropdown\n trigger={\n <button\n type=\"button\"\n className=\"w-6 h-6 rounded hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center justify-center text-gray-500\"\n >\n <MoreHorizontal className=\"w-4 h-4\" />\n </button>\n }\n align=\"right\"\n className=\"min-w-[140px]\"\n >\n <DropdownItem onClick={onResolve}>\n <span className=\"flex items-center gap-2\">\n <Check className=\"w-4 h-4\" />\n {comment.resolved ? 'Unresolve' : 'Resolve'}\n </span>\n </DropdownItem>\n {canEdit && (\n <DropdownItem onClick={() => setIsEditing(true)}>\n <span className=\"flex items-center gap-2\">\n <Pencil className=\"w-4 h-4\" />\n Edit\n </span>\n </DropdownItem>\n )}\n {canDelete && (\n <>\n <DropdownDivider />\n <DropdownItem onClick={handleDelete} destructive>\n <span className=\"flex items-center gap-2\">\n <Trash2 className=\"w-4 h-4\" />\n Delete\n </span>\n </DropdownItem>\n </>\n )}\n </Dropdown>\n </div>\n </div>\n\n {/* Quoted text */}\n {comment.quotedText && (\n <div className=\"mb-2 px-2 py-1 bg-yellow-100/50 dark:bg-yellow-900/30 rounded text-sm italic text-gray-600 dark:text-gray-400 line-clamp-2\">\n \"{comment.quotedText}\"\n </div>\n )}\n\n {/* Content */}\n {isEditing ? (\n <div className=\"space-y-2\">\n <textarea\n value={editContent}\n onChange={(e) => {\n setEditContent(e.target.value)\n e.target.style.height = 'auto'\n e.target.style.height = `${Math.min(e.target.scrollHeight, 120)}px`\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.stopPropagation()\n handleEdit()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n setIsEditing(false)\n setEditContent(comment.content)\n }\n }}\n onClick={(e) => e.stopPropagation()}\n className=\"w-full min-h-[60px] max-h-[120px] px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 resize-none text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\"\n />\n <div className=\"flex gap-2 justify-end\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setIsEditing(false)\n setEditContent(comment.content)\n }}\n disabled={loading}\n className=\"px-3 py-1.5 text-sm rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n handleEdit()\n }}\n disabled={loading || !editContent.trim()}\n className=\"px-3 py-1.5 text-sm rounded-md bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50\"\n >\n Save\n </button>\n </div>\n </div>\n ) : (\n <p className=\"text-sm whitespace-pre-wrap\">{comment.content}</p>\n )}\n\n {/* Replies */}\n {comment.replies && comment.replies.length > 0 && (\n <div className=\"mt-3 pl-3 border-l-2 border-gray-200 dark:border-gray-700 space-y-3\">\n {comment.replies.map((reply) => (\n <ReplyItem\n key={reply.id}\n reply={reply}\n currentUserEmail={currentUserEmail}\n />\n ))}\n </div>\n )}\n\n {/* Reply button / form */}\n {!isEditing && (\n <div className=\"mt-3\">\n {isReplying ? (\n <div className=\"space-y-2\">\n <textarea\n value={replyContent}\n onChange={(e) => {\n setReplyContent(e.target.value)\n e.target.style.height = 'auto'\n e.target.style.height = `${Math.min(e.target.scrollHeight, 120)}px`\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.stopPropagation()\n handleReply()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n setIsReplying(false)\n setReplyContent('')\n }\n }}\n placeholder=\"Write a reply...\"\n className=\"w-full min-h-[60px] max-h-[120px] px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 resize-none text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\"\n onClick={(e) => e.stopPropagation()}\n />\n <div className=\"flex gap-2 justify-end\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setIsReplying(false)\n setReplyContent('')\n }}\n disabled={loading}\n className=\"px-3 py-1.5 text-sm rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n handleReply()\n }}\n disabled={loading || !replyContent.trim()}\n className=\"px-3 py-1.5 text-sm rounded-md bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50\"\n >\n Reply\n </button>\n </div>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setIsReplying(true)\n }}\n className=\"inline-flex items-center gap-1 text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors\"\n >\n <Reply className=\"w-3.5 h-3.5\" />\n Reply\n </button>\n )}\n </div>\n )}\n </div>\n )\n}\n\n// Simple reply display (no nested replies allowed)\nfunction ReplyItem({\n reply,\n currentUserEmail,\n}: {\n reply: CommentWithUser\n currentUserEmail: string\n}) {\n const isOwn = reply.user.email === currentUserEmail\n\n return (\n <div className=\"text-sm\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"font-medium\">\n {isOwn ? 'You' : reply.user.name || reply.user.email.split('@')[0]}\n </span>\n <span className=\"text-gray-500 dark:text-gray-400 text-xs\">\n {formatRelativeTime(reply.createdAt)}\n </span>\n </div>\n <p className=\"whitespace-pre-wrap text-gray-600 dark:text-gray-400\">{reply.content}</p>\n </div>\n )\n}\n","/**\n * Comment types and client-side API helpers for the editor commenting system.\n * Used for collaborative inline comments on posts.\n */\n\n// Types matching Prisma schema + includes\nexport interface CommentUser {\n id: string\n name: string | null\n email: string\n}\n\nexport interface CommentWithUser {\n id: string\n postId: string\n userId: string\n quotedText: string\n content: string\n parentId: string | null\n resolved: boolean\n createdAt: string\n updatedAt: string\n user: CommentUser\n replies?: CommentWithUser[]\n}\n\nexport interface CreateCommentData {\n quotedText: string\n content: string\n parentId?: string\n}\n\nexport interface SelectionState {\n text: string\n from: number\n to: number\n hasExistingComment?: boolean\n}\n\n// Permission helpers (compare by email since that's what we have in session)\nexport function canDeleteComment(\n comment: CommentWithUser,\n currentUserEmail: string,\n isAdmin: boolean\n): boolean {\n return comment.user.email === currentUserEmail || isAdmin\n}\n\nexport function canEditComment(\n comment: CommentWithUser,\n currentUserEmail: string\n): boolean {\n return comment.user.email === currentUserEmail\n}\n\n// API client factory - creates functions bound to a specific API base path\nexport function createCommentsClient(apiBasePath: string = '/api/cms') {\n return {\n async fetchComments(postId: string): Promise<CommentWithUser[]> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments`)\n if (!res.ok) throw new Error('Failed to fetch comments')\n const json = await res.json()\n return json.data || json\n },\n\n async createComment(\n postId: string,\n data: CreateCommentData\n ): Promise<CommentWithUser> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to create comment')\n }\n const json = await res.json()\n return json.data || json\n },\n\n async updateComment(\n postId: string,\n commentId: string,\n content: string\n ): Promise<CommentWithUser> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/${commentId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ content }),\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to update comment')\n }\n const json = await res.json()\n return json.data || json\n },\n\n async deleteComment(\n postId: string,\n commentId: string\n ): Promise<void> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/${commentId}`, {\n method: 'DELETE',\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to delete comment')\n }\n },\n\n async toggleResolve(\n postId: string,\n commentId: string\n ): Promise<CommentWithUser> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/${commentId}/resolve`, {\n method: 'POST',\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to toggle resolve')\n }\n const json = await res.json()\n return json.data || json\n },\n\n async resolveAllComments(\n postId: string\n ): Promise<{ resolved: number }> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/resolve-all`, {\n method: 'POST',\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to resolve all comments')\n }\n const json = await res.json()\n return json.data || json\n },\n }\n}\n","'use client'\n\nimport { useState, useEffect, useRef } from 'react'\n\ninterface Tag {\n id: string\n name: string\n}\n\ninterface TagsSectionProps {\n tags: Tag[]\n onTagsChange: (tags: Tag[]) => void\n apiBasePath: string\n disabled?: boolean\n}\n\nexport function TagsSection({\n tags,\n onTagsChange,\n apiBasePath,\n disabled = false,\n}: TagsSectionProps) {\n const [isExpanded, setIsExpanded] = useState(false)\n const [availableTags, setAvailableTags] = useState<Tag[]>([])\n const [loading, setLoading] = useState(false)\n const [dropdownOpen, setDropdownOpen] = useState(false)\n const dropdownRef = useRef<HTMLDivElement>(null)\n\n // Fetch available tags when section expands\n useEffect(() => {\n if (isExpanded && availableTags.length === 0) {\n setLoading(true)\n fetch(`${apiBasePath}/tags`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(data => {\n setAvailableTags(data.data || [])\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }\n }, [isExpanded, availableTags.length, apiBasePath])\n\n // Close dropdown when clicking outside\n useEffect(() => {\n if (!dropdownOpen) return\n function handleClick(e: MouseEvent) {\n if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {\n setDropdownOpen(false)\n }\n }\n document.addEventListener('click', handleClick)\n return () => document.removeEventListener('click', handleClick)\n }, [dropdownOpen])\n\n const handleAddTag = (tagId: string) => {\n const tagToAdd = availableTags.find(t => t.id === tagId)\n if (tagToAdd && !tags.some(t => t.id === tagId)) {\n onTagsChange([...tags, tagToAdd])\n }\n setDropdownOpen(false)\n }\n\n const handleRemoveTag = (tagId: string) => {\n onTagsChange(tags.filter(t => t.id !== tagId))\n }\n\n // Tags not already selected\n const unselectedTags = availableTags.filter(\n at => !tags.some(t => t.id === at.id)\n )\n\n const tagSummary = tags.length === 0\n ? 'no tags'\n : tags.length === 1\n ? '1 tag'\n : `${tags.length} tags`\n\n return (\n <div className=\"border-t border-gray-200 dark:border-gray-800 pt-4\">\n <button\n type=\"button\"\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"flex items-center gap-2 text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors w-full\"\n >\n <svg \n className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-90' : ''}`} \n fill=\"none\" \n viewBox=\"0 0 24 24\" \n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n <span>Tags</span>\n <span className=\"ml-auto text-xs text-gray-400\">{tagSummary}</span>\n </button>\n\n {isExpanded && (\n <div className=\"mt-4 space-y-3 pl-6\">\n {/* Selected tags */}\n {tags.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {tags.map(tag => (\n <span\n key={tag.id}\n className=\"inline-flex items-center gap-1 px-2 py-1 rounded-md bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-sm\"\n >\n {tag.name}\n {!disabled && (\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(tag.id)}\n className=\"hover:text-red-500 transition-colors\"\n >\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </span>\n ))}\n </div>\n )}\n\n {/* Add tag dropdown */}\n {!disabled && (\n <>\n {loading ? (\n <p className=\"text-sm text-gray-500\">Loading tags...</p>\n ) : unselectedTags.length > 0 ? (\n <div className=\"relative max-w-[200px]\" ref={dropdownRef}>\n <button\n type=\"button\"\n onClick={() => setDropdownOpen(!dropdownOpen)}\n className=\"w-full h-8 px-3 text-sm text-left border border-gray-200 dark:border-gray-700 rounded-md bg-transparent flex items-center justify-between\"\n >\n <span className=\"text-gray-500\">Add tag...</span>\n <svg className=\"h-4 w-4 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n {dropdownOpen && (\n <div \n className=\"absolute z-50 top-full left-0 mt-1 w-full bg-popover border border-border rounded-md shadow-lg max-h-48 overflow-auto overscroll-contain p-1\"\n onWheel={(e) => e.stopPropagation()}\n >\n {unselectedTags.map(tag => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => handleAddTag(tag.id)}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n {tag.name}\n </button>\n ))}\n </div>\n )}\n </div>\n ) : availableTags.length === 0 ? (\n <p className=\"text-sm text-gray-500\">\n No tags available. Create tags in Settings.\n </p>\n ) : (\n <p className=\"text-sm text-gray-500\">All tags added</p>\n )}\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport { useState, useCallback, useEffect } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport {\n CommentWithUser,\n SelectionState,\n createCommentsClient,\n} from '../../lib/comments'\nimport {\n addCommentMark,\n removeCommentMark,\n applyCommentMarks,\n scrollToComment,\n} from '../../lib/comment-mark'\n\nexport interface UseCommentsOptions {\n postId: string | null\n editor: Editor | null\n apiBasePath?: string\n onSave?: () => Promise<string | null> // Called to auto-save post, returns postId\n}\n\nexport interface CommentsState {\n list: CommentWithUser[]\n loading: boolean\n activeId: string | null\n setActiveId: (id: string | null) => void\n selectedText: SelectionState | null\n setSelectedText: (selection: SelectionState | null) => void\n postId: string | null\n create: (content: string) => Promise<void>\n reply: (parentId: string, content: string) => Promise<void>\n edit: (commentId: string, content: string) => Promise<void>\n remove: (commentId: string) => Promise<void>\n resolve: (commentId: string) => Promise<void>\n resolveAll: () => Promise<void>\n scrollTo: (commentId: string) => void\n openCount: number\n}\n\nexport function useComments({\n postId: initialPostId,\n editor,\n apiBasePath = '/api/cms',\n onSave,\n}: UseCommentsOptions): CommentsState {\n const [postId, setPostId] = useState<string | null>(initialPostId)\n const [comments, setComments] = useState<CommentWithUser[]>([])\n const [loading, setLoading] = useState(false)\n const [activeCommentId, setActiveCommentId] = useState<string | null>(null)\n const [selectedText, setSelectedText] = useState<SelectionState | null>(null)\n\n const client = createCommentsClient(apiBasePath)\n\n // Update postId when it changes\n useEffect(() => {\n setPostId(initialPostId)\n }, [initialPostId])\n\n // Fetch comments when postId changes\n useEffect(() => {\n if (!postId) {\n setComments([])\n return\n }\n\n setLoading(true)\n client.fetchComments(postId)\n .then((data) => {\n setComments(data)\n // Apply comment marks to editor\n if (editor) {\n applyCommentMarks(editor, data)\n }\n })\n .catch(console.error)\n .finally(() => setLoading(false))\n }, [postId, editor])\n\n // Create a new comment\n const createComment = useCallback(async (content: string) => {\n if (!selectedText) return\n\n // Auto-save the post first if it hasn't been saved yet\n let effectivePostId = postId\n if (!effectivePostId && onSave) {\n effectivePostId = await onSave()\n if (effectivePostId) {\n setPostId(effectivePostId)\n }\n }\n\n if (!effectivePostId) {\n console.error('Cannot create comment: no post ID')\n return\n }\n\n const comment = await client.createComment(effectivePostId, {\n quotedText: selectedText.text,\n content,\n })\n\n // Add highlight mark to editor\n if (editor) {\n addCommentMark(editor, comment.id, selectedText.from, selectedText.to)\n }\n\n // Add to list with empty replies array\n setComments((prev) => [{ ...comment, replies: [] }, ...prev])\n setSelectedText(null)\n }, [postId, selectedText, editor, onSave, client])\n\n // Reply to a comment\n const replyToComment = useCallback(async (parentId: string, content: string) => {\n if (!postId) return\n\n const reply = await client.createComment(postId, {\n quotedText: '',\n content,\n parentId,\n })\n\n // Add reply to parent's replies array\n setComments((prev) =>\n prev.map((c) =>\n c.id === parentId\n ? { ...c, replies: [...(c.replies || []), reply] }\n : c\n )\n )\n }, [postId, client])\n\n // Edit a comment\n const editComment = useCallback(async (commentId: string, content: string) => {\n if (!postId) return\n\n const updated = await client.updateComment(postId, commentId, content)\n\n // Update in list (could be top-level or reply)\n setComments((prev) =>\n prev.map((c) => {\n if (c.id === commentId) {\n return { ...c, ...updated }\n }\n // Check in replies\n if (c.replies) {\n return {\n ...c,\n replies: c.replies.map((r) =>\n r.id === commentId ? { ...r, ...updated } : r\n ),\n }\n }\n return c\n })\n )\n }, [postId, client])\n\n // Remove a comment\n const removeComment = useCallback(async (commentId: string) => {\n if (!postId) return\n\n await client.deleteComment(postId, commentId)\n\n // Remove highlight mark from editor\n if (editor) {\n removeCommentMark(editor, commentId)\n }\n\n // Remove from list (could be top-level or reply)\n setComments((prev) => {\n // Check if it's a top-level comment\n const isTopLevel = prev.some((c) => c.id === commentId)\n if (isTopLevel) {\n return prev.filter((c) => c.id !== commentId)\n }\n // It's a reply\n return prev.map((c) => ({\n ...c,\n replies: c.replies?.filter((r) => r.id !== commentId),\n }))\n })\n }, [postId, editor, client])\n\n // Resolve/unresolve a comment\n const resolveComment = useCallback(async (commentId: string) => {\n if (!postId) return\n\n const updated = await client.toggleResolve(postId, commentId)\n\n // Toggle highlight: remove when resolved, re-add when unresolved\n if (editor) {\n const comment = comments.find((c) => c.id === commentId)\n if (updated.resolved) {\n removeCommentMark(editor, commentId)\n } else if (comment && selectedText) {\n // Re-apply mark if unresolving\n addCommentMark(editor, commentId, selectedText.from, selectedText.to)\n }\n }\n\n // Update in list\n setComments((prev) =>\n prev.map((c) => (c.id === commentId ? { ...c, resolved: updated.resolved } : c))\n )\n }, [postId, editor, comments, selectedText, client])\n\n // Resolve all comments\n const resolveAllComments = useCallback(async () => {\n if (!postId) return\n\n await client.resolveAllComments(postId)\n\n // Remove all highlight marks\n if (editor) {\n comments.filter((c) => !c.resolved && !c.parentId).forEach((c) => {\n removeCommentMark(editor, c.id)\n })\n }\n\n // Update all comments to resolved\n setComments((prev) =>\n prev.map((c) => ({ ...c, resolved: true }))\n )\n }, [postId, editor, comments, client])\n\n // Scroll to a comment in the editor\n const scrollToCommentMark = useCallback((commentId: string) => {\n if (editor) {\n scrollToComment(editor, commentId)\n }\n setActiveCommentId(commentId)\n }, [editor])\n\n // Count open comments (non-resolved, non-replies)\n const openCount = comments.filter((c) => !c.resolved && !c.parentId).length\n\n return {\n list: comments,\n loading,\n activeId: activeCommentId,\n setActiveId: setActiveCommentId,\n selectedText,\n setSelectedText,\n postId,\n create: createComment,\n reply: replyToComment,\n edit: editComment,\n remove: removeComment,\n resolve: resolveComment,\n resolveAll: resolveAllComments,\n scrollTo: scrollToCommentMark,\n openCount,\n }\n}\n","'use client'\n\nimport { useState, useEffect } from 'react'\nimport { ChevronDown, ChevronLeft, ChevronRight, RotateCcw, Plus, Pencil, Trash2, Play, X, MoreVertical, MoreHorizontal, Loader2 } from 'lucide-react'\nimport { useDashboardContext } from '../context'\n\ninterface SettingsLink {\n path: string\n label: string\n description: string\n countKey?: string\n}\n\ninterface User {\n id: string\n email: string\n name: string | null\n role: string\n createdAt: string\n}\n\ninterface Tag {\n id: string\n name: string\n createdAt?: string\n _count?: { posts: number }\n}\n\ninterface Topic {\n id: string\n name: string\n isActive: boolean\n keywords: string\n rssFeeds: string\n frequency: string\n maxPerPeriod: number\n essayFocus: string | null\n useKeywordFilter: boolean\n lastRunAt: string | null\n _count?: { posts: number; newsItems: number }\n}\n\ninterface Revision {\n id: string\n postId: string\n title: string | null\n markdown: string\n createdAt: string\n post: { id: string; title: string; slug: string; markdown: string }\n}\n\n\nexport function SettingsPage({ subPath }: { subPath: string }) {\n const { navigate, sharedData, sharedDataLoading } = useDashboardContext()\n const counts = sharedData?.counts || {}\n const autoDraftEnabled = sharedData?.settings?.autoDraftEnabled ?? false\n const loading = sharedDataLoading\n\n const allSettingsLinks: SettingsLink[] = [\n { path: '/settings/users', label: 'Users', description: 'Manage who can access the CMS', countKey: 'users' },\n { path: '/settings/posts', label: 'All Posts', description: 'Manage all posts', countKey: 'posts' },\n { path: '/settings/tags', label: 'Tags', description: 'Organize posts with tags', countKey: 'tags' },\n { path: '/settings/ai', label: 'AI Settings', description: 'Configure AI models and rules' },\n { path: '/settings/revisions', label: 'Revisions', description: 'View revision history' },\n { path: '/settings/comments', label: 'Comments', description: 'Manage post comments' },\n { path: '/settings/topics', label: 'Topics', description: 'RSS subscriptions for auto-draft', countKey: 'topics' },\n ]\n\n // Filter out Topics if autoDraftEnabled is off\n const settingsLinks = autoDraftEnabled \n ? allSettingsLinks \n : allSettingsLinks.filter(link => link.path !== '/settings/topics')\n\n // Main settings overview\n if (!subPath || subPath === '/') {\n if (loading) {\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n <div className=\"h-7 w-24 bg-muted rounded animate-pulse mb-6\" />\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4\">\n {[1, 2, 3, 4, 5, 6].map(i => (\n <div key={i} className=\"p-4 sm:p-6 border border-border rounded-lg\">\n <div className=\"h-4 w-16 bg-muted rounded animate-pulse\" />\n <div className=\"h-8 w-12 bg-muted rounded animate-pulse mt-2\" />\n </div>\n ))}\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n <h2 className=\"text-lg font-semibold pb-4 mb-6 border-b border-border\">Settings</h2>\n \n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4\">\n {settingsLinks.map((item) => (\n <button\n key={item.path}\n onClick={() => navigate(item.path)}\n className=\"p-4 sm:p-6 border border-border rounded-lg text-left hover:bg-accent transition-colors\"\n >\n <p className=\"text-sm text-muted-foreground\">{item.label}</p>\n {item.countKey ? (\n <p className=\"text-2xl font-bold mt-1\">{counts[item.countKey] ?? 0}</p>\n ) : (\n <p className=\"text-sm text-muted-foreground mt-1\">Configure →</p>\n )}\n </button>\n ))}\n </div>\n </div>\n )\n }\n\n // Sub-pages - each component renders its own header\n const pageName = subPath.slice(1) // Remove leading /\n \n // Handle revision detail page: /revisions/:id\n const revisionDetailMatch = pageName.match(/^revisions\\/(.+)$/)\n \n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n {pageName === 'users' && <UsersSettingsContent />}\n {pageName === 'ai' && <AISettingsContent />}\n {pageName === 'tags' && <TagsSettingsContent />}\n {pageName === 'topics' && <TopicsSettingsContent />}\n {pageName === 'posts' && <PostsSettingsContent />}\n {pageName === 'revisions' && <RevisionsSettingsContent />}\n {revisionDetailMatch && <RevisionDetailContent revisionId={revisionDetailMatch[1]} />}\n {pageName === 'comments' && <CommentsSettingsContent />}\n </div>\n )\n}\n\nfunction UsersSettingsContent() {\n const { apiBasePath } = useDashboardContext()\n const [users, setUsers] = useState<User[]>([])\n const [loading, setLoading] = useState(true)\n const [showForm, setShowForm] = useState(false)\n const [editingUser, setEditingUser] = useState<User | null>(null)\n const [formEmail, setFormEmail] = useState('')\n const [formName, setFormName] = useState('')\n const [formRole, setFormRole] = useState('writer')\n const [formError, setFormError] = useState('')\n const [saving, setSaving] = useState(false)\n const [menuOpen, setMenuOpen] = useState<string | null>(null)\n\n useEffect(() => {\n fetch(`${apiBasePath}/users`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => { setUsers(res.data || []); setLoading(false) })\n .catch(() => setLoading(false))\n }, [apiBasePath])\n\n function resetForm() {\n setFormEmail('')\n setFormName('')\n setFormRole('writer')\n setFormError('')\n setEditingUser(null)\n }\n\n function openNewForm() {\n resetForm()\n setShowForm(true)\n }\n\n function openEditForm(user: User) {\n setEditingUser(user)\n setFormEmail(user.email)\n setFormName(user.name || '')\n setFormRole(user.role)\n setFormError('')\n setShowForm(true)\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!formEmail) return\n setSaving(true)\n setFormError('')\n\n const url = editingUser ? `${apiBasePath}/users/${editingUser.id}` : `${apiBasePath}/users`\n const method = editingUser ? 'PATCH' : 'POST'\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: formEmail, name: formName || null, role: formRole }),\n })\n\n if (res.ok) {\n const json = await res.json()\n if (editingUser) {\n setUsers(users.map(u => u.id === editingUser.id ? json.data : u))\n } else {\n setUsers([json.data, ...users])\n }\n setShowForm(false)\n resetForm()\n } else {\n const result = await res.json().catch(() => ({}))\n setFormError(result.error || `Failed to ${editingUser ? 'update' : 'create'} user`)\n }\n setSaving(false)\n }\n\n async function handleDeleteUser(id: string, email: string) {\n if (!confirm(`Delete user \"${email}\"?`)) return\n const res = await fetch(`${apiBasePath}/users/${id}`, { method: 'DELETE' })\n if (res.ok) setUsers(users.filter(u => u.id !== id))\n setMenuOpen(null)\n }\n\n function getRoleBadgeClasses(role: string) {\n if (role === 'admin') return 'bg-primary text-primary-foreground'\n if (role === 'drafter') return 'border border-border text-foreground'\n return 'bg-secondary text-secondary-foreground'\n }\n\n if (loading) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div className=\"space-y-6\">\n {/* Add/Edit Form - shown as separate view like old interface */}\n {showForm ? (\n <div className=\"max-w-md\">\n <div className=\"flex items-center justify-between mb-6\">\n <h2 className=\"text-lg font-semibold\">{editingUser ? 'Edit User' : 'Add User'}</h2>\n </div>\n\n {formError && (\n <div className=\"bg-destructive/10 text-destructive p-3 rounded-lg text-sm mb-4\">\n {formError}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"email\" className=\"block text-sm font-medium\">Email *</label>\n <input\n type=\"email\"\n id=\"email\"\n value={formEmail}\n onChange={e => setFormEmail(e.target.value)}\n required\n placeholder={editingUser ? undefined : 'user@example.com'}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium\">Name</label>\n <input\n type=\"text\"\n id=\"name\"\n value={formName}\n onChange={e => setFormName(e.target.value)}\n placeholder={editingUser ? undefined : 'John Doe'}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label htmlFor=\"role\" className=\"block text-sm font-medium\">Role</label>\n <select\n id=\"role\"\n value={formRole}\n onChange={e => setFormRole(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n >\n <option value=\"drafter\">Drafter</option>\n <option value=\"writer\">Writer</option>\n <option value=\"admin\">Admin</option>\n </select>\n </div>\n\n <div className=\"flex gap-4 pt-2\">\n <button type=\"submit\" disabled={saving} className=\"px-4 py-2 bg-primary text-primary-foreground rounded-md disabled:opacity-50\">\n {saving ? (editingUser ? 'Saving...' : 'Creating...') : (editingUser ? 'Save Changes' : 'Create User')}\n </button>\n <button type=\"button\" onClick={() => { setShowForm(false); resetForm() }} className=\"px-4 py-2 text-muted-foreground hover:text-foreground\">\n Cancel\n </button>\n </div>\n </form>\n </div>\n ) : (\n <>\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">Users</h2>\n <button onClick={openNewForm} className=\"px-4 py-2 bg-primary text-primary-foreground rounded-md text-sm\">\n Add User\n </button>\n </div>\n\n {/* Desktop Table */}\n <div className=\"hidden md:block rounded-md border border-border\">\n <table className=\"w-full\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Email</th>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Name</th>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Role</th>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Created</th>\n <th className=\"px-4 py-3 text-right text-sm font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {users.map(user => (\n <tr key={user.id}>\n <td className=\"px-4 py-3 text-sm max-w-[250px] truncate\">{user.email}</td>\n <td className=\"px-4 py-3 text-sm text-muted-foreground max-w-[150px] truncate\">{user.name || '—'}</td>\n <td className=\"px-4 py-3 text-sm\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getRoleBadgeClasses(user.role)}`}>\n {user.role}\n </span>\n </td>\n <td className=\"px-4 py-3 text-sm text-muted-foreground\">{new Date(user.createdAt).toLocaleDateString()}</td>\n <td className=\"px-4 py-3 text-right\">\n <div className=\"relative inline-block\">\n <button\n onClick={() => setMenuOpen(menuOpen === user.id ? null : user.id)}\n className=\"p-1.5 text-muted-foreground hover:text-foreground rounded hover:bg-accent\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === user.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[100px] py-1\">\n <button\n onClick={() => { openEditForm(user); setMenuOpen(null) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"h-px bg-border my-1\" />\n <button\n onClick={() => handleDeleteUser(user.id, user.email)}\n className=\"w-full px-3 py-2 text-left text-sm text-red-500 hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n {users.length === 0 && (\n <p className=\"p-8 text-center text-muted-foreground\">No users yet. Add one to get started.</p>\n )}\n </div>\n\n {/* Mobile List */}\n <div className=\"md:hidden divide-y divide-border rounded-md border border-border bg-background\">\n {users.map(user => (\n <div key={user.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">{user.email}</span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getRoleBadgeClasses(user.role)}`}>\n {user.role}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {user.name || 'No name'} · Created {new Date(user.createdAt).toLocaleDateString()}\n </p>\n </div>\n <div className=\"relative\">\n <button\n onClick={() => setMenuOpen(menuOpen === user.id ? null : user.id)}\n className=\"p-1.5 text-muted-foreground hover:text-foreground rounded hover:bg-accent\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === user.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[100px] py-1\">\n <button\n onClick={() => { openEditForm(user); setMenuOpen(null) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"h-px bg-border my-1\" />\n <button\n onClick={() => handleDeleteUser(user.id, user.email)}\n className=\"w-full px-3 py-2 text-left text-sm text-red-500 hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n {users.length === 0 && (\n <p className=\"p-8 text-center text-muted-foreground\">No users yet. Add one to get started.</p>\n )}\n </div>\n </>\n )}\n </div>\n )\n}\n\nfunction CollapsibleTemplate({ \n label, \n value, \n defaultValue, \n onChange, \n onReset, \n placeholders, \n disabled \n}: { \n label: string\n value: string | null\n defaultValue: string\n onChange: (v: string) => void\n onReset: () => void\n placeholders: string\n disabled?: boolean\n}) {\n const [open, setOpen] = useState(false)\n const isCustom = value !== null\n const displayValue = value ?? defaultValue\n\n return (\n <div className=\"mt-2\">\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n className=\"flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground\"\n >\n <ChevronDown className={`h-4 w-4 transition-transform ${open ? '' : '-rotate-90'}`} />\n {isCustom ? `Edit prompt template (customized)` : `Edit prompt template`}\n </button>\n {open && (\n <div className=\"mt-2 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-xs text-muted-foreground\">Placeholders: {placeholders}</p>\n {isCustom && (\n <button type=\"button\" onClick={onReset} className=\"flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground\">\n <RotateCcw className=\"h-3 w-3\" /> Reset to default\n </button>\n )}\n </div>\n <textarea\n value={displayValue}\n onChange={e => onChange(e.target.value)}\n rows={10}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent resize-none font-mono text-xs\"\n disabled={disabled}\n />\n </div>\n )}\n </div>\n )\n}\n\nfunction AISettingsContent() {\n const { apiBasePath, navigate } = useDashboardContext()\n const [rules, setRules] = useState('')\n const [chatRules, setChatRules] = useState('')\n const [rewriteRules, setRewriteRules] = useState('')\n const [autoDraftRules, setAutoDraftRules] = useState('')\n const [planRules, setPlanRules] = useState('')\n const [autoDraftWordCount, setAutoDraftWordCount] = useState(800)\n const [autoDraftEnabled, setAutoDraftEnabled] = useState(false)\n const [defaultModel, setDefaultModel] = useState('claude-sonnet')\n const [models, setModels] = useState<{ id: string; name: string; description: string }[]>([])\n const [generateTemplate, setGenerateTemplate] = useState<string | null>(null)\n const [chatTemplate, setChatTemplate] = useState<string | null>(null)\n const [rewriteTemplate, setRewriteTemplate] = useState<string | null>(null)\n const [autoDraftTemplate, setAutoDraftTemplate] = useState<string | null>(null)\n const [planTemplate, setPlanTemplate] = useState<string | null>(null)\n const [expandPlanTemplate, setExpandPlanTemplate] = useState<string | null>(null)\n const [defaultGenerateTemplate, setDefaultGenerateTemplate] = useState('')\n const [defaultChatTemplate, setDefaultChatTemplate] = useState('')\n const [defaultRewriteTemplate, setDefaultRewriteTemplate] = useState('')\n const [defaultAutoDraftTemplate, setDefaultAutoDraftTemplate] = useState('')\n const [defaultPlanRules, setDefaultPlanRules] = useState('')\n const [defaultPlanTemplate, setDefaultPlanTemplate] = useState('')\n const [defaultExpandPlanTemplate, setDefaultExpandPlanTemplate] = useState('')\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [saved, setSaved] = useState(false)\n\n useEffect(() => {\n Promise.all([\n fetch(`${apiBasePath}/ai/settings`).then(res => res.ok ? res.json() : Promise.reject()),\n fetch(`${apiBasePath}/settings`).then(res => res.ok ? res.json() : Promise.reject()),\n ])\n .then(([aiRes, settingsRes]) => {\n // Handle both wrapped { data: {...} } and unwrapped {...} response formats\n const data = aiRes.data || aiRes || {}\n setRules(data.rules || '')\n setChatRules(data.chatRules || '')\n setRewriteRules(data.rewriteRules || '')\n setAutoDraftRules(data.autoDraftRules || '')\n setPlanRules(data.planRules || '')\n setAutoDraftWordCount(data.autoDraftWordCount ?? 800)\n setDefaultModel(data.defaultModel || 'claude-sonnet')\n setModels(data.availableModels || [\n { id: 'claude-sonnet', name: 'Sonnet 4.5', description: 'Fast, capable, best value' },\n { id: 'claude-opus', name: 'Opus 4.5', description: 'Highest quality, slower' },\n { id: 'gpt-5.2', name: 'GPT-5.2', description: 'Latest OpenAI flagship' },\n { id: 'gpt-5-mini', name: 'GPT-5 Mini', description: 'Fast and cost-efficient' },\n ])\n setGenerateTemplate(data.generateTemplate ?? null)\n setChatTemplate(data.chatTemplate ?? null)\n setRewriteTemplate(data.rewriteTemplate ?? null)\n setAutoDraftTemplate(data.autoDraftTemplate ?? null)\n setPlanTemplate(data.planTemplate ?? null)\n setExpandPlanTemplate(data.expandPlanTemplate ?? null)\n setDefaultGenerateTemplate(data.defaultGenerateTemplate || '')\n setDefaultChatTemplate(data.defaultChatTemplate || '')\n setDefaultRewriteTemplate(data.defaultRewriteTemplate || '')\n setDefaultAutoDraftTemplate(data.defaultAutoDraftTemplate || '')\n setDefaultPlanRules(data.defaultPlanRules || '')\n setDefaultPlanTemplate(data.defaultPlanTemplate || '')\n setDefaultExpandPlanTemplate(data.defaultExpandPlanTemplate || '')\n // Settings\n setAutoDraftEnabled(settingsRes.data?.autoDraftEnabled ?? false)\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath])\n\n\n async function handleSave() {\n setSaving(true)\n setSaved(false)\n await Promise.all([\n fetch(`${apiBasePath}/ai/settings`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n rules, chatRules, rewriteRules, autoDraftRules, planRules, autoDraftWordCount, defaultModel,\n generateTemplate, chatTemplate, rewriteTemplate, autoDraftTemplate, planTemplate, expandPlanTemplate\n }),\n }),\n fetch(`${apiBasePath}/settings`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ autoDraftEnabled }),\n }),\n ])\n setSaving(false)\n setSaved(true)\n setTimeout(() => setSaved(false), 2000)\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header - matches other settings pages */}\n <div>\n <h2 className=\"text-lg font-semibold\">AI Settings</h2>\n <p className=\"text-sm text-muted-foreground\">Configure your AI writing assistant.</p>\n </div>\n\n {/* Card container */}\n <div className=\"rounded-lg border bg-card text-card-foreground shadow-sm\">\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center gap-4\">\n <label className=\"text-sm font-medium leading-none shrink-0\">Default Model</label>\n <div className=\"relative max-w-sm flex-1\">\n <select\n value={defaultModel}\n onChange={e => setDefaultModel(e.target.value)}\n className=\"h-10 w-full appearance-none rounded-md border border-input bg-background pl-3 pr-10 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer\"\n >\n {models.map(model => (\n <option key={model.id} value={model.id}>{model.name} — {model.description}</option>\n ))}\n </select>\n <ChevronDown className=\"absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 opacity-50 pointer-events-none\" />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Essay Writing Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Style and format rules for generated essays. Applied when generating or rewriting content.\n </p>\n <textarea\n value={rules}\n onChange={e => setRules(e.target.value)}\n placeholder={`- Never use \"utilize\" — always say \"use\"\n- Avoid passive voice\n- Start with concrete scenes, not abstractions\n- Short paragraphs (3-4 sentences max)\n- Use em-dashes sparingly\n- End with forward motion, not tidy conclusions`}\n className=\"flex min-h-[180px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <CollapsibleTemplate\n label=\"Generate\"\n value={generateTemplate}\n defaultValue={defaultGenerateTemplate}\n onChange={setGenerateTemplate}\n onReset={() => setGenerateTemplate(null)}\n placeholders=\"{{RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Chat Behavior Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n How the assistant should behave during brainstorming conversations. Controls personality and interaction style.\n </p>\n <textarea\n value={chatRules}\n onChange={e => setChatRules(e.target.value)}\n placeholder={`- Be direct and concise\n- Push back on vague ideas\n- Ask clarifying questions before drafting\n- Challenge my assumptions`}\n className=\"flex min-h-[140px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <CollapsibleTemplate\n label=\"Chat\"\n value={chatTemplate}\n defaultValue={defaultChatTemplate}\n onChange={setChatTemplate}\n onReset={() => setChatTemplate(null)}\n placeholders=\"{{RULES}}, {{CHAT_RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Rewrite Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Rules for cleaning up selected text with the rewrite tool.\n </p>\n <textarea\n value={rewriteRules}\n onChange={e => setRewriteRules(e.target.value)}\n placeholder={`- Keep the same meaning, improve clarity\n- Maintain sentence length variety\n- Remove filler words\n- Don't add new ideas`}\n className=\"flex min-h-[140px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <CollapsibleTemplate\n label=\"Rewrite\"\n value={rewriteTemplate}\n defaultValue={defaultRewriteTemplate}\n onChange={setRewriteTemplate}\n onReset={() => setRewriteTemplate(null)}\n placeholders=\"{{RULES}}, {{REWRITE_RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Auto-Draft Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Rules for generating essays from news articles via RSS feeds. Controls how topics are transformed into original essays.\n </p>\n <textarea\n value={autoDraftRules}\n onChange={e => setAutoDraftRules(e.target.value)}\n placeholder={`- Write original perspectives, don't summarize\n- Take a contrarian angle when appropriate\n- Include personal insights and experiences\n- Focus on implications, not just facts`}\n className=\"flex min-h-[140px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <div className=\"flex items-center gap-4 pt-2\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm whitespace-nowrap\">Target word count:</label>\n <input\n type=\"number\"\n min={200}\n max={3000}\n value={autoDraftWordCount}\n onChange={e => setAutoDraftWordCount(parseInt(e.target.value) || 800)}\n className=\"flex h-10 w-24 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n disabled={saving}\n />\n </div>\n </div>\n <CollapsibleTemplate\n label=\"Auto-Draft\"\n value={autoDraftTemplate}\n defaultValue={defaultAutoDraftTemplate}\n onChange={setAutoDraftTemplate}\n onReset={() => setAutoDraftTemplate(null)}\n placeholders=\"{{AUTO_DRAFT_RULES}}, {{AUTO_DRAFT_WORD_COUNT}}, {{RULES}}, {{STYLE_EXAMPLES}}, {{TOPIC_NAME}}, {{ARTICLE_TITLE}}, {{ARTICLE_SUMMARY}}, {{ARTICLE_URL}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Plan Format Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Rules for essay plan structure and format. Controls how outlines are organized in Plan mode.\n </p>\n <div className=\"flex items-center justify-end\">\n {planRules && (\n <button\n type=\"button\"\n onClick={() => setPlanRules('')}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-7 px-2 hover:bg-accent hover:text-accent-foreground\"\n >\n <RotateCcw className=\"h-3 w-3 mr-1\" />\n Reset to default\n </button>\n )}\n </div>\n <textarea\n value={planRules || defaultPlanRules}\n onChange={e => setPlanRules(e.target.value)}\n className=\"flex min-h-[250px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Plan Mode Template</label>\n <p className=\"text-sm text-muted-foreground\">\n Prompt template for Plan mode in chat. Controls the full system prompt.\n </p>\n <CollapsibleTemplate\n label=\"Plan\"\n value={planTemplate}\n defaultValue={defaultPlanTemplate}\n onChange={setPlanTemplate}\n onReset={() => setPlanTemplate(null)}\n placeholders=\"{{PLAN_RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Expand Plan Template</label>\n <p className=\"text-sm text-muted-foreground\">\n Prompt template for expanding a plan outline into a full essay draft.\n </p>\n <CollapsibleTemplate\n label=\"Expand Plan\"\n value={expandPlanTemplate}\n defaultValue={defaultExpandPlanTemplate}\n onChange={setExpandPlanTemplate}\n onReset={() => setExpandPlanTemplate(null)}\n placeholders=\"{{RULES}}, {{STYLE_EXAMPLES}}, {{PLAN}}\"\n disabled={saving}\n />\n </div>\n\n {/* Auto-Draft Feature Toggle */}\n <div className=\"flex items-center justify-between py-4 border-t border-border\">\n <div className=\"space-y-0.5\">\n <label className=\"text-sm font-medium leading-none\">Auto-Draft Feature</label>\n <p className=\"text-sm text-muted-foreground\">\n Enable RSS topic subscriptions and automatic draft generation.\n </p>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={autoDraftEnabled}\n onClick={() => setAutoDraftEnabled(!autoDraftEnabled)}\n className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background ${autoDraftEnabled ? 'bg-primary' : 'bg-input'}`}\n >\n <span\n className={`pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform ${autoDraftEnabled ? 'translate-x-5' : 'translate-x-0'}`}\n />\n </button>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <button\n onClick={handleSave}\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {saving && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {saving ? 'Saving...' : 'Save'}\n </button>\n {saved && (\n <span className=\"text-sm text-green-600 dark:text-green-400\">\n Saved!\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\nfunction TagsSettingsContent() {\n const { apiBasePath } = useDashboardContext()\n const [tags, setTags] = useState<Tag[]>([])\n const [loading, setLoading] = useState(true)\n const [dialogOpen, setDialogOpen] = useState(false)\n const [editingTag, setEditingTag] = useState<Tag | null>(null)\n const [tagName, setTagName] = useState('')\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState('')\n const [menuOpen, setMenuOpen] = useState<string | null>(null)\n\n useEffect(() => {\n fetchTags()\n }, [apiBasePath])\n\n async function fetchTags() {\n const res = await fetch(`${apiBasePath}/tags`)\n if (res.ok) {\n const json = await res.json()\n setTags(json.data || [])\n }\n setLoading(false)\n }\n\n function openCreateDialog() {\n setEditingTag(null)\n setTagName('')\n setError('')\n setDialogOpen(true)\n }\n\n function openEditDialog(tag: Tag) {\n setEditingTag(tag)\n setTagName(tag.name)\n setError('')\n setDialogOpen(true)\n setMenuOpen(null)\n }\n\n async function handleDelete(tag: Tag) {\n if (!confirm(`Delete tag \"${tag.name}\"? This will remove it from all posts.`)) return\n const res = await fetch(`${apiBasePath}/tags/${tag.id}`, { method: 'DELETE' })\n if (res.ok) {\n fetchTags()\n }\n setMenuOpen(null)\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!tagName.trim()) {\n setError('Tag name is required')\n return\n }\n\n setSaving(true)\n setError('')\n\n const url = editingTag\n ? `${apiBasePath}/tags/${editingTag.id}`\n : `${apiBasePath}/tags`\n const method = editingTag ? 'PATCH' : 'POST'\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: tagName.trim() }),\n })\n\n if (res.ok) {\n setDialogOpen(false)\n fetchTags()\n } else {\n const data = await res.json().catch(() => ({}))\n setError(data.error || 'Failed to save tag')\n }\n\n setSaving(false)\n }\n\n if (loading) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header */}\n <div className=\"flex items-center justify-between mb-6 md:mb-8\">\n <h2 className=\"text-lg font-semibold\">Tags</h2>\n <button\n onClick={openCreateDialog}\n className=\"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n Add Tag\n </button>\n </div>\n\n {tags.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No tags yet. Create one to get started.</div>\n ) : (\n <>\n {/* Desktop Table */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Name</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Posts</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Created</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {tags.map(tag => (\n <tr key={tag.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[250px]\">{tag.name}</span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">{tag._count?.posts ?? 0}</td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {tag.createdAt ? new Date(tag.createdAt).toLocaleDateString() : '—'}\n </td>\n <td className=\"p-4 align-middle text-right\">\n <div className=\"relative inline-block\">\n <button\n onClick={() => setMenuOpen(menuOpen === tag.id ? null : tag.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === tag.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => openEditDialog(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {tags.map(tag => (\n <div key={tag.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <span className=\"font-medium truncate block\">{tag.name}</span>\n <p className=\"text-sm text-muted-foreground truncate\">\n {tag._count?.posts ?? 0} posts · Created {tag.createdAt ? new Date(tag.createdAt).toLocaleDateString() : '—'}\n </p>\n </div>\n <div className=\"relative\">\n <button\n onClick={() => setMenuOpen(menuOpen === tag.id ? null : tag.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === tag.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => openEditDialog(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </>\n )}\n\n {/* Dialog for Create/Edit */}\n {dialogOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div \n className=\"fixed inset-0 bg-black/80\" \n onClick={() => setDialogOpen(false)}\n />\n {/* Dialog */}\n <div className=\"relative z-50 w-full max-w-sm bg-background border border-border rounded-lg shadow-lg\">\n <div className=\"flex flex-col space-y-1.5 p-6\">\n <h3 className=\"text-lg font-semibold leading-none tracking-tight\">\n {editingTag ? 'Edit Tag' : 'Create Tag'}\n </h3>\n </div>\n <form onSubmit={handleSubmit}>\n <div className=\"px-6 pb-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"tagName\" className=\"text-sm font-medium leading-none\">Name</label>\n <input\n id=\"tagName\"\n type=\"text\"\n value={tagName}\n onChange={(e) => setTagName(e.target.value)}\n placeholder=\"e.g. technology\"\n autoFocus\n className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n />\n {error && (\n <p className=\"text-sm text-destructive\">{error}</p>\n )}\n </div>\n </div>\n <div className=\"flex items-center justify-end gap-2 p-6 pt-0\">\n <button\n type=\"button\"\n onClick={() => setDialogOpen(false)}\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {saving ? 'Saving...' : editingTag ? 'Save' : 'Create'}\n </button>\n </div>\n </form>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction TopicsSettingsContent() {\n const { apiBasePath } = useDashboardContext()\n const [topics, setTopics] = useState<Topic[]>([])\n const [loading, setLoading] = useState(true)\n const [showForm, setShowForm] = useState(false)\n const [editingTopic, setEditingTopic] = useState<Topic | null>(null)\n const [generating, setGenerating] = useState<string | null>(null)\n\n // Form state\n const [formName, setFormName] = useState('')\n const [formKeywords, setFormKeywords] = useState('')\n const [formFeeds, setFormFeeds] = useState('')\n const [formFrequency, setFormFrequency] = useState('daily')\n const [formMaxPerPeriod, setFormMaxPerPeriod] = useState(3)\n const [formEssayFocus, setFormEssayFocus] = useState('')\n const [formIsActive, setFormIsActive] = useState(true)\n\n useEffect(() => {\n fetchTopics()\n }, [apiBasePath])\n\n async function fetchTopics() {\n const res = await fetch(`${apiBasePath}/topics`)\n if (res.ok) {\n const json = await res.json()\n setTopics(json.data || [])\n }\n setLoading(false)\n }\n\n function resetForm() {\n setFormName('')\n setFormKeywords('')\n setFormFeeds('')\n setFormFrequency('daily')\n setFormMaxPerPeriod(3)\n setFormEssayFocus('')\n setFormIsActive(true)\n }\n\n function openEditForm(topic: Topic) {\n setEditingTopic(topic)\n setFormName(topic.name)\n setFormKeywords(JSON.parse(topic.keywords).join(', '))\n setFormFeeds(JSON.parse(topic.rssFeeds).join('\\n'))\n setFormFrequency(topic.frequency)\n setFormMaxPerPeriod(topic.maxPerPeriod)\n setFormEssayFocus(topic.essayFocus || '')\n setFormIsActive(topic.isActive)\n setShowForm(true)\n }\n\n function openNewForm() {\n setEditingTopic(null)\n resetForm()\n setShowForm(true)\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n const data = {\n name: formName,\n keywords: formKeywords.split(',').map(k => k.trim()).filter(Boolean),\n rssFeeds: formFeeds.split('\\n').map(f => f.trim()).filter(Boolean),\n frequency: formFrequency,\n maxPerPeriod: formMaxPerPeriod,\n essayFocus: formEssayFocus || null,\n isActive: formIsActive,\n }\n\n const url = editingTopic ? `${apiBasePath}/topics/${editingTopic.id}` : `${apiBasePath}/topics`\n const method = editingTopic ? 'PATCH' : 'POST'\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n })\n\n if (res.ok) {\n setShowForm(false)\n resetForm()\n setEditingTopic(null)\n fetchTopics()\n }\n }\n\n async function handleDelete(id: string) {\n if (!confirm('Delete this topic? This will also delete associated news items.')) return\n const res = await fetch(`${apiBasePath}/topics/${id}`, { method: 'DELETE' })\n if (res.ok) setTopics(topics.filter(t => t.id !== id))\n }\n\n async function handleGenerate(topicId: string) {\n setGenerating(topicId)\n try {\n const res = await fetch(`${apiBasePath}/topics/${topicId}/generate`, { method: 'POST' })\n if (res.ok) {\n fetchTopics()\n }\n } finally {\n setGenerating(null)\n }\n }\n\n if (loading) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">Topics</h2>\n <button onClick={openNewForm} className=\"flex items-center gap-1 px-3 py-1.5 bg-primary text-primary-foreground rounded-md text-sm\">\n <Plus className=\"h-4 w-4\" /> New Topic\n </button>\n </div>\n <p className=\"text-muted-foreground text-sm -mt-2\">RSS topic subscriptions for auto-generating draft posts.</p>\n\n {showForm && (\n <div className=\"border border-border rounded-lg p-4 space-y-4 bg-muted/30\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-medium\">{editingTopic ? 'Edit Topic' : 'New Topic'}</h3>\n <button onClick={() => { setShowForm(false); resetForm(); setEditingTopic(null) }} className=\"text-muted-foreground hover:text-foreground\">\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">Name</label>\n <input\n type=\"text\"\n value={formName}\n onChange={e => setFormName(e.target.value)}\n required\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n placeholder=\"e.g. School Lunch Policy\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">Keywords (comma-separated)</label>\n <input\n type=\"text\"\n value={formKeywords}\n onChange={e => setFormKeywords(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n placeholder=\"school lunch, USDA, nutrition\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">RSS Feed URLs (one per line)</label>\n <textarea\n value={formFeeds}\n onChange={e => setFormFeeds(e.target.value)}\n rows={3}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent resize-none font-mono text-sm\"\n placeholder=\"https://example.com/feed.xml\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">Frequency</label>\n <select\n value={formFrequency}\n onChange={e => setFormFrequency(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n >\n <option value=\"daily\">Daily</option>\n <option value=\"weekly\">Weekly</option>\n <option value=\"hourly\">Hourly</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">Max per period</label>\n <input\n type=\"number\"\n min={1}\n max={20}\n value={formMaxPerPeriod}\n onChange={e => setFormMaxPerPeriod(parseInt(e.target.value) || 3)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n />\n </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">Essay Focus (optional)</label>\n <textarea\n value={formEssayFocus}\n onChange={e => setFormEssayFocus(e.target.value)}\n rows={2}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent resize-none\"\n placeholder=\"Specific angle or perspective for essays on this topic\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n id=\"isActive\"\n checked={formIsActive}\n onChange={e => setFormIsActive(e.target.checked)}\n />\n <label htmlFor=\"isActive\" className=\"text-sm\">Active</label>\n </div>\n <div className=\"flex gap-2\">\n <button type=\"submit\" className=\"px-4 py-2 bg-primary text-primary-foreground rounded-md\">\n {editingTopic ? 'Update' : 'Create'}\n </button>\n <button type=\"button\" onClick={() => { setShowForm(false); resetForm(); setEditingTopic(null) }} className=\"px-4 py-2 border border-input rounded-md\">\n Cancel\n </button>\n </div>\n </form>\n </div>\n )}\n \n <div className=\"border border-border rounded-lg divide-y divide-border\">\n {topics.map(topic => {\n const keywords = JSON.parse(topic.keywords) as string[]\n const feeds = JSON.parse(topic.rssFeeds) as string[]\n return (\n <div key={topic.id} className=\"p-4\">\n <div className=\"flex items-start justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium\">{topic.name}</p>\n {!topic.isActive && <span className=\"text-xs bg-muted px-1.5 py-0.5 rounded\">Paused</span>}\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {keywords.slice(0, 3).join(', ')}{keywords.length > 3 && ` +${keywords.length - 3}`}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {feeds.length} feed{feeds.length !== 1 ? 's' : ''} · {topic.frequency} · {topic._count?.posts ?? 0} posts\n {topic.lastRunAt && ` · Last run: ${new Date(topic.lastRunAt).toLocaleDateString()}`}\n </p>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => handleGenerate(topic.id)}\n disabled={generating !== null}\n className=\"p-1.5 text-muted-foreground hover:text-foreground disabled:opacity-50\"\n title=\"Generate now\"\n >\n <Play className={`h-4 w-4 ${generating === topic.id ? 'animate-pulse' : ''}`} />\n </button>\n <button onClick={() => openEditForm(topic)} className=\"p-1.5 text-muted-foreground hover:text-foreground\">\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button onClick={() => handleDelete(topic.id)} className=\"p-1.5 text-red-500 hover:text-red-600\">\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n </div>\n )\n })}\n {topics.length === 0 && (\n <p className=\"p-4 text-muted-foreground text-center\">No topics configured</p>\n )}\n </div>\n </div>\n )\n}\n\ninterface PostWithRevisions {\n id: string\n title: string\n slug: string\n status: string\n updatedAt: string\n _count?: { revisions: number }\n}\n\nconst POSTS_PER_PAGE = 25\n\nfunction PostsSettingsContent() {\n const { apiBasePath, navigate } = useDashboardContext()\n const [posts, setPosts] = useState<PostWithRevisions[]>([])\n const [totalCount, setTotalCount] = useState(0)\n const [loading, setLoading] = useState(true)\n const [currentPage, setCurrentPage] = useState(1)\n const [totalPages, setTotalPages] = useState(1)\n const [menuOpen, setMenuOpen] = useState<string | null>(null)\n\n useEffect(() => {\n setLoading(true)\n fetch(`${apiBasePath}/posts?all=1&page=${currentPage}&limit=${POSTS_PER_PAGE}&includeRevisionCount=1`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setPosts(res.data || [])\n setTotalCount(res.total ?? res.data?.length ?? 0)\n setTotalPages(Math.ceil((res.total ?? res.data?.length ?? 0) / POSTS_PER_PAGE))\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, currentPage])\n\n async function handleDelete(post: PostWithRevisions) {\n if (!confirm(`Delete \"${post.title || 'Untitled'}\"? This will also delete all revisions.`)) return\n const res = await fetch(`${apiBasePath}/posts/${post.id}`, { method: 'DELETE' })\n if (res.ok) {\n setPosts(posts.filter(p => p.id !== post.id))\n setTotalCount(c => c - 1)\n }\n setMenuOpen(null)\n }\n\n function getStatusBadgeClasses(status: string) {\n if (status === 'published') return 'bg-primary text-primary-foreground'\n if (status === 'deleted') return 'bg-destructive text-destructive-foreground'\n return 'bg-secondary text-secondary-foreground'\n }\n\n // Pagination component matching blog's shadcn style\n const PaginationControls = ({ position }: { position: 'top' | 'bottom' }) => {\n if (totalPages <= 1) return null\n \n const getPageNumbers = () => {\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = []\n if (totalPages <= 7) {\n for (let i = 1; i <= totalPages; i++) pages.push(i)\n } else {\n pages.push(1)\n if (currentPage > 3) pages.push('ellipsis-start')\n const start = Math.max(2, currentPage - 1)\n const end = Math.min(totalPages - 1, currentPage + 1)\n for (let i = start; i <= end; i++) {\n if (!pages.includes(i)) pages.push(i)\n }\n if (currentPage < totalPages - 2) pages.push('ellipsis-end')\n if (!pages.includes(totalPages)) pages.push(totalPages)\n }\n return pages\n }\n\n const spacingClass = position === 'bottom' ? 'mt-4' : ''\n \n return (\n <nav role=\"navigation\" aria-label=\"pagination\" className={`mx-auto flex w-full justify-end ${spacingClass}`}>\n <ul className=\"flex flex-row items-center gap-1\">\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.max(1, p - 1))}\n disabled={currentPage <= 1}\n aria-label=\"Go to previous page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:block\">Previous</span>\n </button>\n </li>\n {getPageNumbers().map((page) => \n typeof page === 'string' ? (\n <li key={page}>\n <span aria-hidden className=\"flex h-9 w-9 items-center justify-center\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </span>\n </li>\n ) : (\n <li key={page}>\n <button\n onClick={() => setCurrentPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n className={`inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-9 w-9 ${\n page === currentPage \n ? 'border border-input bg-background hover:bg-accent hover:text-accent-foreground' \n : 'hover:bg-accent hover:text-accent-foreground'\n }`}\n >\n {page}\n </button>\n </li>\n )\n )}\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}\n disabled={currentPage >= totalPages}\n aria-label=\"Go to next page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <span className=\"hidden sm:block\">Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </li>\n </ul>\n </nav>\n )\n }\n\n if (loading && posts.length === 0) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header - matches AdminPageHeader pattern */}\n <div className=\"flex items-end justify-between gap-4 mb-6 md:mb-8\">\n <div className=\"shrink-0\">\n <h1 className=\"text-lg font-bold\">Posts</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{totalCount} total post{totalCount !== 1 ? 's' : ''}</p>\n </div>\n <div className=\"flex items-center gap-4\">\n <PaginationControls position=\"top\" />\n <button\n onClick={() => navigate('/editor')}\n className=\"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n New Post\n </button>\n </div>\n </div>\n\n {posts.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No posts yet.</div>\n ) : (\n <>\n {/* Desktop Table - matches AdminTable */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Title</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Slug</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Status</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Revisions</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Updated</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {posts.map(post => (\n <tr key={post.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[200px]\">{post.title || 'Untitled'}</span>\n </td>\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[250px] text-muted-foreground font-mono\">{post.slug}</span>\n </td>\n <td className=\"p-4 align-middle\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getStatusBadgeClasses(post.status)}`}>\n {post.status}\n </span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">{post._count?.revisions ?? 0}</td>\n <td className=\"p-4 align-middle text-muted-foreground\">{new Date(post.updatedAt).toLocaleDateString()}</td>\n <td className=\"p-4 align-middle text-right\">\n <div className=\"relative inline-block\">\n <button\n onClick={() => setMenuOpen(menuOpen === post.id ? null : post.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === post.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => { navigate(`/editor/${post.slug}`); setMenuOpen(null) }}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n {post.status === 'published' && (\n <a\n href={`/e/${post.slug}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n onClick={() => setMenuOpen(null)}\n >\n View\n </a>\n )}\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(post)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List - matches AdminTable mobile view */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {posts.map(post => (\n <div key={post.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">{post.title || 'Untitled'}</span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold shrink-0 ${getStatusBadgeClasses(post.status)}`}>\n {post.status}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {post.slug} · {post._count?.revisions ?? 0} rev · {new Date(post.updatedAt).toLocaleDateString()}\n </p>\n </div>\n <div className=\"relative\">\n <button\n onClick={() => setMenuOpen(menuOpen === post.id ? null : post.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === post.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => { navigate(`/editor/${post.slug}`); setMenuOpen(null) }}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n {post.status === 'published' && (\n <a\n href={`/e/${post.slug}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n onClick={() => setMenuOpen(null)}\n >\n View\n </a>\n )}\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(post)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </>\n )}\n\n {/* Bottom Pagination */}\n <PaginationControls position=\"bottom\" />\n </div>\n )\n}\n\nconst REVISIONS_PER_PAGE = 25\n\nfunction RevisionsSettingsContent() {\n const { apiBasePath, navigate } = useDashboardContext()\n const [revisions, setRevisions] = useState<Revision[]>([])\n const [totalCount, setTotalCount] = useState(0)\n const [loading, setLoading] = useState(true)\n const [currentPage, setCurrentPage] = useState(1)\n const [totalPages, setTotalPages] = useState(1)\n\n useEffect(() => {\n setLoading(true)\n fetch(`${apiBasePath}/revisions?page=${currentPage}&limit=${REVISIONS_PER_PAGE}`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setRevisions(res.data || [])\n setTotalCount(res.total ?? res.data?.length ?? 0)\n setTotalPages(res.totalPages || Math.ceil((res.total ?? res.data?.length ?? 0) / REVISIONS_PER_PAGE))\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, currentPage])\n\n function getStatusBadgeClasses(isCurrent: boolean) {\n if (isCurrent) return 'bg-primary text-primary-foreground'\n return 'bg-secondary text-secondary-foreground'\n }\n\n // Pagination component matching blog's shadcn style\n const PaginationControls = ({ position }: { position: 'top' | 'bottom' }) => {\n if (totalPages <= 1) return null\n \n const getPageNumbers = () => {\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = []\n if (totalPages <= 7) {\n for (let i = 1; i <= totalPages; i++) pages.push(i)\n } else {\n pages.push(1)\n if (currentPage > 3) pages.push('ellipsis-start')\n const start = Math.max(2, currentPage - 1)\n const end = Math.min(totalPages - 1, currentPage + 1)\n for (let i = start; i <= end; i++) {\n if (!pages.includes(i)) pages.push(i)\n }\n if (currentPage < totalPages - 2) pages.push('ellipsis-end')\n if (!pages.includes(totalPages)) pages.push(totalPages)\n }\n return pages\n }\n\n const spacingClass = position === 'bottom' ? 'mt-4' : ''\n \n return (\n <nav role=\"navigation\" aria-label=\"pagination\" className={`mx-auto flex w-full justify-end ${spacingClass}`}>\n <ul className=\"flex flex-row items-center gap-1\">\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.max(1, p - 1))}\n disabled={currentPage <= 1}\n aria-label=\"Go to previous page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:block\">Previous</span>\n </button>\n </li>\n {getPageNumbers().map((page) => \n typeof page === 'string' ? (\n <li key={page}>\n <span aria-hidden className=\"flex h-9 w-9 items-center justify-center\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </span>\n </li>\n ) : (\n <li key={page}>\n <button\n onClick={() => setCurrentPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n className={`inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-9 w-9 ${\n page === currentPage \n ? 'border border-input bg-background hover:bg-accent hover:text-accent-foreground' \n : 'hover:bg-accent hover:text-accent-foreground'\n }`}\n >\n {page}\n </button>\n </li>\n )\n )}\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}\n disabled={currentPage >= totalPages}\n aria-label=\"Go to next page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <span className=\"hidden sm:block\">Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </li>\n </ul>\n </nav>\n )\n }\n\n if (loading && revisions.length === 0) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header - matches AdminPageHeader pattern */}\n <div className=\"flex items-end justify-between gap-4 mb-6 md:mb-8\">\n <div className=\"shrink-0\">\n <h1 className=\"text-lg font-bold\">Revisions</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{totalCount} total revision{totalCount !== 1 ? 's' : ''}</p>\n </div>\n <PaginationControls position=\"top\" />\n </div>\n\n {revisions.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No revisions yet.</div>\n ) : (\n <>\n {/* Desktop Table - matches AdminTable */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Post</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Content Preview</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Created</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Status</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {revisions.map(revision => {\n const isCurrent = revision.post.markdown === revision.markdown\n return (\n <tr key={revision.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <button\n onClick={() => navigate(`/editor/${revision.post.slug}`)}\n className=\"block truncate max-w-[200px] hover:underline text-left\"\n >\n {revision.post.title || 'Untitled'}\n </button>\n </td>\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[300px] text-muted-foreground\">\n {revision.markdown.slice(0, 80)}{revision.markdown.length > 80 ? '...' : ''}\n </span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {new Date(revision.createdAt).toLocaleString()}\n </td>\n <td className=\"p-4 align-middle\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getStatusBadgeClasses(isCurrent)}`}>\n {isCurrent ? 'current' : 'past'}\n </span>\n </td>\n <td className=\"p-4 align-middle text-right\">\n <button\n onClick={() => navigate(`/settings/revisions/${revision.id}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground\"\n >\n View\n </button>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List - matches AdminTable mobile view */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {revisions.map(revision => {\n const isCurrent = revision.post.markdown === revision.markdown\n return (\n <div key={revision.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">{revision.post.title || 'Untitled'}</span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold shrink-0 ${getStatusBadgeClasses(isCurrent)}`}>\n {isCurrent ? 'current' : 'past'}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {revision.markdown.slice(0, 40)}{revision.markdown.length > 40 ? '...' : ''} · {new Date(revision.createdAt).toLocaleDateString()}\n </p>\n </div>\n <button\n onClick={() => navigate(`/settings/revisions/${revision.id}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground shrink-0\"\n >\n View\n </button>\n </div>\n )\n })}\n </div>\n </>\n )}\n\n {/* Bottom Pagination */}\n <PaginationControls position=\"bottom\" />\n </div>\n )\n}\n\ninterface RevisionDetail {\n id: string\n postId: string\n title: string | null\n subtitle: string | null\n markdown: string\n createdAt: string\n post: { id: string; title: string; slug: string; markdown: string }\n}\n\nfunction RevisionDetailContent({ revisionId }: { revisionId: string }) {\n const { apiBasePath, navigate } = useDashboardContext()\n const [revision, setRevision] = useState<RevisionDetail | null>(null)\n const [loading, setLoading] = useState(true)\n const [restoring, setRestoring] = useState(false)\n\n useEffect(() => {\n fetch(`${apiBasePath}/revisions/${revisionId}`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setRevision(res.data)\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, revisionId])\n\n async function handleRestore() {\n if (!revision) return\n if (!confirm('Restore this revision? This will replace the current post content.')) return\n \n setRestoring(true)\n const res = await fetch(`${apiBasePath}/revisions/${revisionId}/restore`, { method: 'POST' })\n if (res.ok) {\n navigate(`/editor/${revision.post.slug}`)\n }\n setRestoring(false)\n }\n\n if (loading) {\n return (\n <div className=\"space-y-6\">\n <div className=\"h-6 w-32 bg-muted rounded animate-pulse\" />\n <div className=\"h-64 bg-muted rounded animate-pulse\" />\n </div>\n )\n }\n\n if (!revision) {\n return (\n <div className=\"space-y-6\">\n <button\n onClick={() => navigate('/settings/revisions')}\n className=\"text-sm text-muted-foreground hover:text-foreground flex items-center gap-1\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Back to Revisions\n </button>\n <p className=\"text-muted-foreground\">Revision not found.</p>\n </div>\n )\n }\n\n const isCurrent = revision.post.markdown === revision.markdown\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-1\">\n <button\n onClick={() => navigate('/settings/revisions')}\n className=\"text-sm text-muted-foreground hover:text-foreground flex items-center gap-1\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Back to Revisions\n </button>\n <h2 className=\"text-lg font-semibold\">Revision Detail</h2>\n </div>\n <div className=\"flex items-center gap-2\">\n {isCurrent ? (\n <span className=\"inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold bg-primary text-primary-foreground\">\n current\n </span>\n ) : (\n <button\n onClick={handleRestore}\n disabled={restoring}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n <RotateCcw className=\"h-4 w-4 mr-2\" />\n {restoring ? 'Restoring...' : 'Restore This Revision'}\n </button>\n )}\n </div>\n </div>\n\n {/* Metadata */}\n <div className=\"rounded-lg border bg-card p-4 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Post</span>\n <button\n onClick={() => navigate(`/editor/${revision.post.slug}`)}\n className=\"text-sm hover:underline\"\n >\n {revision.post.title || 'Untitled'}\n </button>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Title at revision</span>\n <span className=\"text-sm\">{revision.title || '—'}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Created</span>\n <span className=\"text-sm\">{new Date(revision.createdAt).toLocaleString()}</span>\n </div>\n </div>\n\n {/* Content Preview */}\n <div className=\"space-y-2\">\n <h3 className=\"text-sm font-medium\">Content</h3>\n <div className=\"rounded-lg border bg-muted/30 p-4 max-h-96 overflow-auto\">\n <pre className=\"text-sm whitespace-pre-wrap font-mono\">{revision.markdown}</pre>\n </div>\n </div>\n </div>\n )\n}\n\ninterface CommentWithDetails {\n id: string\n content: string\n createdAt: string\n resolved: boolean\n deletedAt: string | null\n parentId: string | null\n post: { id: string; title: string; slug: string }\n user: { name: string | null; email: string }\n}\n\nconst COMMENTS_PER_PAGE = 25\n\nfunction CommentsSettingsContent() {\n const { apiBasePath, navigate, basePath } = useDashboardContext()\n const [comments, setComments] = useState<CommentWithDetails[]>([])\n const [totalCount, setTotalCount] = useState(0)\n const [loading, setLoading] = useState(true)\n const [currentPage, setCurrentPage] = useState(1)\n const [totalPages, setTotalPages] = useState(1)\n\n useEffect(() => {\n setLoading(true)\n fetch(`${apiBasePath}/comments?page=${currentPage}&limit=${COMMENTS_PER_PAGE}`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setComments(res.data || [])\n setTotalCount(res.total ?? res.data?.length ?? 0)\n setTotalPages(res.totalPages || Math.ceil((res.total ?? res.data?.length ?? 0) / COMMENTS_PER_PAGE))\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, currentPage])\n\n function getStatusBadge(comment: CommentWithDetails) {\n const isDeleted = comment.deletedAt !== null\n const isResolved = comment.resolved\n \n if (isDeleted) {\n return { label: 'deleted', classes: 'bg-destructive text-destructive-foreground' }\n }\n if (isResolved) {\n return { label: 'resolved', classes: 'bg-secondary text-secondary-foreground' }\n }\n return { label: 'active', classes: 'bg-primary text-primary-foreground' }\n }\n\n // Pagination component matching other settings pages\n const PaginationControls = ({ position }: { position: 'top' | 'bottom' }) => {\n if (totalPages <= 1) return null\n \n const getPageNumbers = () => {\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = []\n if (totalPages <= 7) {\n for (let i = 1; i <= totalPages; i++) pages.push(i)\n } else {\n pages.push(1)\n if (currentPage > 3) pages.push('ellipsis-start')\n const start = Math.max(2, currentPage - 1)\n const end = Math.min(totalPages - 1, currentPage + 1)\n for (let i = start; i <= end; i++) {\n if (!pages.includes(i)) pages.push(i)\n }\n if (currentPage < totalPages - 2) pages.push('ellipsis-end')\n if (!pages.includes(totalPages)) pages.push(totalPages)\n }\n return pages\n }\n\n const spacingClass = position === 'bottom' ? 'mt-4' : ''\n \n return (\n <nav role=\"navigation\" aria-label=\"pagination\" className={`mx-auto flex w-full justify-end ${spacingClass}`}>\n <ul className=\"flex flex-row items-center gap-1\">\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.max(1, p - 1))}\n disabled={currentPage <= 1}\n aria-label=\"Go to previous page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:block\">Previous</span>\n </button>\n </li>\n {getPageNumbers().map((page) => \n typeof page === 'string' ? (\n <li key={page}>\n <span aria-hidden className=\"flex h-9 w-9 items-center justify-center\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </span>\n </li>\n ) : (\n <li key={page}>\n <button\n onClick={() => setCurrentPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n className={`inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-9 w-9 ${\n page === currentPage \n ? 'border border-input bg-background hover:bg-accent hover:text-accent-foreground' \n : 'hover:bg-accent hover:text-accent-foreground'\n }`}\n >\n {page}\n </button>\n </li>\n )\n )}\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}\n disabled={currentPage >= totalPages}\n aria-label=\"Go to next page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <span className=\"hidden sm:block\">Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </li>\n </ul>\n </nav>\n )\n }\n\n if (loading && comments.length === 0) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header - matches AdminPageHeader pattern */}\n <div className=\"flex items-end justify-between gap-4 mb-6 md:mb-8\">\n <div className=\"shrink-0\">\n <h1 className=\"text-lg font-bold\">Comments</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{totalCount} total comment{totalCount !== 1 ? 's' : ''}</p>\n </div>\n <PaginationControls position=\"top\" />\n </div>\n\n {comments.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No comments yet.</div>\n ) : (\n <>\n {/* Desktop Table - matches AdminTable */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground max-w-[200px]\">Post</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Author</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground max-w-[300px]\">Comment</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Created</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Status</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {comments.map(comment => {\n const status = getStatusBadge(comment)\n const isReply = comment.parentId !== null\n const commentIdToOpen = comment.parentId || comment.id\n \n return (\n <tr key={comment.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <button\n onClick={() => navigate(`/editor/${comment.post.slug}`)}\n className=\"block truncate max-w-[200px] hover:underline text-left\"\n >\n {comment.post.title || 'Untitled'}\n </button>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {comment.user.name || comment.user.email}\n </td>\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[300px] text-muted-foreground\">\n {isReply && <span className=\"text-xs mr-1\">↳</span>}\n {comment.content.slice(0, 60)}{comment.content.length > 60 ? '...' : ''}\n </span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {new Date(comment.createdAt).toLocaleString()}\n </td>\n <td className=\"p-4 align-middle\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${status.classes}`}>\n {status.label}\n </span>\n </td>\n <td className=\"p-4 align-middle text-right\">\n <button\n onClick={() => navigate(`/editor/${comment.post.slug}?comment=${commentIdToOpen}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground\"\n >\n View\n </button>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List - matches AdminTable mobile view */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {comments.map(comment => {\n const status = getStatusBadge(comment)\n const isReply = comment.parentId !== null\n const commentIdToOpen = comment.parentId || comment.id\n \n return (\n <div key={comment.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">\n {isReply && <span className=\"text-xs mr-1\">↳</span>}\n {comment.content.slice(0, 40)}{comment.content.length > 40 ? '...' : ''}\n </span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold shrink-0 ${status.classes}`}>\n {status.label}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {comment.user.name || comment.user.email} · {new Date(comment.createdAt).toLocaleDateString()}\n </p>\n </div>\n <button\n onClick={() => navigate(`/editor/${comment.post.slug}?comment=${commentIdToOpen}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground shrink-0\"\n >\n View\n </button>\n </div>\n )\n })}\n </div>\n </>\n )}\n\n {/* Bottom Pagination */}\n <PaginationControls position=\"bottom\" />\n </div>\n )\n}\n","'use client'\n\nimport { ChevronLeft, Moon, Sun } from 'lucide-react'\nimport { useDashboardContext } from '../context'\nimport { Dropdown, DropdownItem, DropdownDivider } from './Dropdown'\n\nexport interface NavbarProps {\n // Callbacks\n onSignOut?: () => void\n onThemeToggle?: () => void\n // Current theme for icon display\n theme?: 'light' | 'dark'\n // Extra slot for host-app specific buttons (e.g., chat toggle)\n rightSlot?: React.ReactNode\n}\n\nexport function Navbar({\n onSignOut,\n onThemeToggle,\n theme,\n rightSlot,\n}: NavbarProps) {\n const { session, currentPath, navigate, goBack, basePath } = useDashboardContext()\n\n // Detect current route state\n const isRoot = currentPath === '/' || currentPath === ''\n const isSettings = currentPath.startsWith('/settings')\n\n const handleBack = (e: React.MouseEvent<HTMLButtonElement>) => {\n // Blur to clear focus/active state (prevents sticky highlight on mobile)\n e.currentTarget.blur()\n goBack()\n }\n\n const avatarTrigger = (\n <button\n type=\"button\"\n className=\"relative w-9 h-9 rounded-full bg-secondary flex items-center justify-center text-sm font-medium text-secondary-foreground hover:ring-2 hover:ring-ring transition-shadow\"\n >\n {session?.user?.email?.charAt(0).toUpperCase() || '?'}\n <span className=\"absolute bottom-0 right-0 w-2.5 h-2.5 bg-green-500 rounded-full border-2 border-background\" />\n </button>\n )\n\n return (\n <header className=\"sticky top-0 z-50 border-b border-border bg-background\">\n <div className=\"max-w-5xl mx-auto px-6 py-4 flex items-center justify-between\">\n {/* Left side: Logo or back button */}\n {isRoot ? (\n <a href={basePath} className=\"font-medium flex items-center gap-1.5\">\n Writer\n <span className=\"text-xs px-1.5 py-0.5 bg-primary text-primary-foreground rounded\">AI</span>\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"h-9 px-3 -ml-3 gap-1.5 inline-flex items-center justify-center text-sm font-medium rounded-md hover:bg-accent hover:text-accent-foreground touch-manipulation\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:inline\">Back</span>\n </button>\n )}\n\n {/* Right side: icons and user menu */}\n <div className=\"flex items-center gap-2\">\n {/* Extra slot from host app */}\n {rightSlot}\n\n {/* Theme toggle */}\n {onThemeToggle && (\n <button\n type=\"button\"\n onClick={onThemeToggle}\n className=\"w-9 h-9 rounded-md border border-border hover:bg-accent text-muted-foreground flex items-center justify-center\"\n aria-label=\"Toggle theme\"\n >\n {theme === 'dark' ? <Sun className=\"h-4 w-4\" /> : <Moon className=\"h-4 w-4\" />}\n </button>\n )}\n\n {/* User menu */}\n {session && (\n <Dropdown trigger={avatarTrigger} align=\"right\" className=\"min-w-[180px]\">\n {session.user?.role === 'admin' && (\n <>\n {/* Settings toggle */}\n {!isSettings ? (\n <DropdownItem onClick={() => navigate('/settings')}>\n Go to settings\n </DropdownItem>\n ) : (\n <DropdownItem onClick={() => navigate('/')}>\n Back to writer\n </DropdownItem>\n )}\n <DropdownDivider />\n </>\n )}\n\n {/* Back to site */}\n <DropdownItem onClick={() => { window.location.href = '/' }}>\n Back to site\n </DropdownItem>\n\n {/* Logout */}\n {onSignOut && (\n <DropdownItem onClick={onSignOut}>\n Logout\n </DropdownItem>\n )}\n </Dropdown>\n )}\n </div>\n </div>\n </header>\n )\n}\n","import { useEffect, useRef } from 'react'\n\ninterface KeyboardShortcut {\n key: string\n metaKey?: boolean\n ctrlKey?: boolean\n shiftKey?: boolean\n action: () => void\n description?: string\n}\n\nexport function useKeyboard(shortcuts: KeyboardShortcut[], enabled: boolean = true) {\n // Use ref to avoid effect re-running when shortcuts array reference changes\n const shortcutsRef = useRef(shortcuts)\n shortcutsRef.current = shortcuts\n\n useEffect(() => {\n if (!enabled) return\n\n function handleKeyDown(event: KeyboardEvent) {\n // Don't trigger shortcuts when typing in inputs\n const target = event.target as HTMLElement\n const isTyping = \n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable\n \n if (isTyping) {\n // Allow Escape always, and Cmd+/ or Cmd+' for navigation\n const hasMeta = event.metaKey || event.ctrlKey\n const isEscape = event.key === 'Escape'\n const isNavShortcut = hasMeta && (event.key === '/' || event.key === \"'\")\n if (!isEscape && !isNavShortcut) {\n return\n }\n }\n\n for (const shortcut of shortcutsRef.current) {\n const metaMatch = shortcut.metaKey ? event.metaKey || event.ctrlKey : true\n const ctrlMatch = shortcut.ctrlKey ? event.ctrlKey : true\n const shiftMatch = shortcut.shiftKey ? event.shiftKey : !event.shiftKey\n const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase()\n\n if (keyMatch && metaMatch && ctrlMatch && shiftMatch) {\n event.preventDefault()\n shortcut.action()\n break\n }\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n return () => window.removeEventListener('keydown', handleKeyDown)\n }, [enabled])\n}\n\n// Dashboard-specific shortcuts\nexport function useDashboardKeyboard(options: {\n basePath: string\n onToggleView?: () => void\n onToggleSettings?: () => void\n onNewPost?: () => void\n onEscape?: () => void\n}) {\n const shortcuts: KeyboardShortcut[] = [\n {\n key: '/',\n metaKey: true,\n action: () => options.onToggleView?.(),\n description: 'Toggle view',\n },\n {\n key: \"'\",\n metaKey: true,\n action: () => options.onToggleSettings?.(),\n description: 'Toggle settings',\n },\n {\n key: 'n',\n action: () => options.onNewPost?.(),\n description: 'New post',\n },\n {\n key: 'Escape',\n action: () => options.onEscape?.(),\n description: 'Go back',\n },\n ]\n\n useKeyboard(shortcuts, true)\n}\n","'use client'\n\nimport { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo, type ReactNode } from 'react'\n\nexport interface EssaySnapshot {\n title: string\n subtitle: string\n markdown: string\n}\n\nexport interface Message {\n role: 'user' | 'assistant'\n content: string\n mode?: ChatMode\n appliedEdits?: boolean\n previousState?: EssaySnapshot\n}\n\nexport interface EssayContext {\n title: string\n subtitle?: string\n markdown: string\n}\n\nexport type ChatMode = 'ask' | 'agent' | 'plan' | 'search'\n\nexport interface EssayEdit {\n type: 'replace_all' | 'replace_section' | 'insert' | 'delete'\n title?: string\n subtitle?: string\n markdown?: string\n find?: string\n replace?: string\n position?: 'before' | 'after' | 'start' | 'end'\n}\n\nexport type EditHandler = (edit: EssayEdit) => boolean\nexport type ExpandPlanHandler = (plan: string, wordCount: number) => void\n\ninterface ChatContextValue {\n messages: Message[]\n essayContext: EssayContext | null\n isStreaming: boolean\n isOpen: boolean\n mode: ChatMode\n webSearchEnabled: boolean\n thinkingEnabled: boolean\n selectedModel: string\n \n setEssayContext: (context: EssayContext | null) => void\n sendMessage: (content: string) => Promise<void>\n stopStreaming: () => void\n addMessage: (role: 'user' | 'assistant', content: string) => void\n clearMessages: () => void\n setIsOpen: (open: boolean) => void\n setMode: (mode: ChatMode) => void\n setWebSearchEnabled: (enabled: boolean) => void\n setThinkingEnabled: (enabled: boolean) => void\n setSelectedModel: (modelId: string) => void\n registerEditHandler: (handler: EditHandler | null) => void\n undoEdit: (messageIndex: number) => void\n registerExpandPlanHandler: (handler: ExpandPlanHandler | null) => void\n expandPlan: (wordCount?: number) => void\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null)\n\n// Parse edit blocks from agent mode responses\nfunction parseEditBlocks(content: string): { edits: EssayEdit[], cleanContent: string } {\n const editRegex = /:::edit\\s*([\\s\\S]*?)\\s*:::/g\n const edits: EssayEdit[] = []\n let cleanContent = content\n \n let match\n while ((match = editRegex.exec(content)) !== null) {\n try {\n const edit = JSON.parse(match[1]) as EssayEdit\n edits.push(edit)\n cleanContent = cleanContent.replace(match[0], '')\n } catch {\n console.warn('Failed to parse edit block:', match[1])\n }\n }\n \n cleanContent = cleanContent.replace(/\\n{3,}/g, '\\n\\n').trim()\n \n return { edits, cleanContent }\n}\n\n/**\n * Clean plan mode output by extracting content from <plan> tags and stripping trailing text.\n */\nfunction cleanPlanOutput(content: string): string {\n let cleaned = content\n \n // Extract content from <plan> tags if present\n const planMatch = cleaned.match(/<plan>([\\s\\S]*?)<\\/plan>/i)\n if (planMatch) {\n cleaned = planMatch[1]\n } else {\n const openTagMatch = cleaned.match(/<plan>([\\s\\S]*)/i)\n if (openTagMatch) {\n cleaned = openTagMatch[1]\n }\n }\n \n // Strip any text after the last bullet point\n const lines = cleaned.split('\\n')\n let lastBulletIndex = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n if (lines[i].trim().startsWith('- ')) {\n lastBulletIndex = i\n break\n }\n }\n \n if (lastBulletIndex === -1) {\n return cleaned.trim()\n }\n \n return lines.slice(0, lastBulletIndex + 1).join('\\n').trim()\n}\n\ninterface ChatProviderProps {\n children: ReactNode\n apiBasePath?: string\n chatApiPath?: string // Custom path for chat API (defaults to apiBasePath + '/ai/chat' or '/api/ai/chat')\n historyApiPath?: string // Custom path for history API (defaults to '/api/chat/history')\n}\n\nexport function ChatProvider({ \n children, \n apiBasePath = '/api/cms',\n chatApiPath,\n historyApiPath = '/api/chat/history',\n}: ChatProviderProps) {\n const [messages, setMessages] = useState<Message[]>([])\n const [essayContext, setEssayContext] = useState<EssayContext | null>(null)\n const [isStreaming, setIsStreaming] = useState(false)\n const [isOpen, setIsOpen] = useState(false)\n const [mode, setMode] = useState<ChatMode>('ask')\n const [webSearchEnabled, setWebSearchEnabled] = useState(false)\n const [thinkingEnabled, setThinkingEnabled] = useState(false)\n const [selectedModel, setSelectedModel] = useState('claude-sonnet')\n \n const editHandlerRef = useRef<EditHandler | null>(null)\n const expandPlanHandlerRef = useRef<ExpandPlanHandler | null>(null)\n const historyLoadedRef = useRef(false)\n const abortControllerRef = useRef<AbortController | null>(null)\n\n // Resolve chat API path\n const resolvedChatApiPath = chatApiPath || '/api/ai/chat'\n \n const registerEditHandler = useCallback((handler: EditHandler | null) => {\n editHandlerRef.current = handler\n }, [])\n\n const registerExpandPlanHandler = useCallback((handler: ExpandPlanHandler | null) => {\n expandPlanHandlerRef.current = handler\n }, [])\n\n const stopStreaming = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort()\n abortControllerRef.current = null\n }\n setIsStreaming(false)\n }, [])\n\n // Preload history on mount\n useEffect(() => {\n if (historyLoadedRef.current) return\n historyLoadedRef.current = true\n \n fetch(historyApiPath)\n .then(res => res.ok ? res.json() : [])\n .then((data: { role: 'user' | 'assistant'; content: string }[]) => {\n if (data.length > 0) {\n setMessages(data.map(m => ({ role: m.role, content: m.content })))\n }\n })\n .catch(() => {})\n }, [historyApiPath])\n\n const saveMessage = useCallback((role: 'user' | 'assistant', content: string) => {\n fetch(historyApiPath, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ role, content }),\n }).catch(() => {})\n }, [historyApiPath])\n\n const sendMessage = useCallback(async (content: string) => {\n if (!content.trim() || isStreaming) return\n\n abortControllerRef.current = new AbortController()\n const signal = abortControllerRef.current.signal\n\n const userMessage: Message = { role: 'user', content: content.trim() }\n const newMessages = [...messages, userMessage]\n setMessages(newMessages)\n setIsStreaming(true)\n\n saveMessage('user', content.trim())\n\n const assistantMessage: Message = { role: 'assistant', content: '', mode }\n setMessages([...newMessages, assistantMessage])\n\n try {\n const response = await fetch(resolvedChatApiPath, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n messages: newMessages,\n essayContext: essayContext,\n mode: mode,\n modelId: selectedModel,\n useWebSearch: webSearchEnabled,\n useThinking: thinkingEnabled,\n }),\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 503) {\n throw new Error('AI service temporarily unavailable. Please try again.')\n }\n if (response.status === 429) {\n throw new Error('Rate limit exceeded. Please wait before trying again.')\n }\n const errorText = await response.text()\n try {\n const error = JSON.parse(errorText)\n throw new Error(error.error || 'Failed to send message')\n } catch {\n throw new Error(`Server error (${response.status}). Please try again.`)\n }\n }\n\n const reader = response.body?.getReader()\n if (!reader) throw new Error('No response body')\n\n const decoder = new TextDecoder()\n let assistantContent = ''\n let appliedEdits = false\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n assistantContent += chunk\n\n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { role: 'assistant', content: assistantContent, mode }\n return updated\n })\n }\n \n // Process agent mode edits\n if (mode === 'agent' && editHandlerRef.current && essayContext) {\n const { edits, cleanContent } = parseEditBlocks(assistantContent)\n \n const previousState: EssaySnapshot = {\n title: essayContext.title,\n subtitle: essayContext.subtitle || '',\n markdown: essayContext.markdown,\n }\n \n for (const edit of edits) {\n const success = editHandlerRef.current(edit)\n if (success) appliedEdits = true\n }\n \n if (edits.length > 0) {\n const finalContent = cleanContent || 'Edit applied.'\n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { \n role: 'assistant', \n content: finalContent,\n mode,\n appliedEdits,\n previousState: appliedEdits ? previousState : undefined,\n }\n return updated\n })\n saveMessage('assistant', finalContent)\n } else {\n saveMessage('assistant', assistantContent)\n }\n } else if (mode === 'plan') {\n const cleanedContent = cleanPlanOutput(assistantContent)\n \n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { \n role: 'assistant', \n content: cleanedContent,\n mode,\n }\n return updated\n })\n \n saveMessage('assistant', cleanedContent)\n } else {\n saveMessage('assistant', assistantContent)\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n return\n }\n console.error('Chat error:', error)\n const errorMessage = error instanceof Error ? error.message : 'Something went wrong'\n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { role: 'assistant', content: `Error: ${errorMessage}` }\n return updated\n })\n } finally {\n abortControllerRef.current = null\n setIsStreaming(false)\n }\n }, [messages, isStreaming, essayContext, mode, webSearchEnabled, thinkingEnabled, selectedModel, saveMessage, resolvedChatApiPath])\n\n const clearMessages = useCallback(() => {\n setMessages([])\n }, [])\n\n const addMessage = useCallback((role: 'user' | 'assistant', content: string) => {\n const message: Message = { role, content }\n setMessages(prev => [...prev, message])\n saveMessage(role, content)\n }, [saveMessage])\n\n const undoEdit = useCallback((messageIndex: number) => {\n const message = messages[messageIndex]\n if (!message?.previousState || !editHandlerRef.current) return\n \n const success = editHandlerRef.current({\n type: 'replace_all',\n title: message.previousState.title,\n subtitle: message.previousState.subtitle,\n markdown: message.previousState.markdown,\n })\n \n if (success) {\n setMessages(prev => {\n const updated = [...prev]\n updated[messageIndex] = {\n ...updated[messageIndex],\n appliedEdits: false,\n previousState: undefined,\n }\n return updated\n })\n }\n }, [messages])\n\n const expandPlan = useCallback((wordCount: number = 800) => {\n if (!expandPlanHandlerRef.current) return\n \n const lastAssistantMessage = [...messages].reverse().find(m => m.role === 'assistant')\n if (!lastAssistantMessage?.content) return\n \n expandPlanHandlerRef.current(lastAssistantMessage.content, wordCount)\n \n setIsOpen(false)\n }, [messages])\n\n const value = useMemo<ChatContextValue>(() => ({\n messages,\n essayContext,\n isStreaming,\n isOpen,\n mode,\n webSearchEnabled,\n thinkingEnabled,\n selectedModel,\n setEssayContext,\n sendMessage,\n stopStreaming,\n addMessage,\n clearMessages,\n setIsOpen,\n setMode,\n setWebSearchEnabled,\n setThinkingEnabled,\n setSelectedModel,\n registerEditHandler,\n undoEdit,\n registerExpandPlanHandler,\n expandPlan,\n }), [\n messages,\n essayContext,\n isStreaming,\n isOpen,\n mode,\n webSearchEnabled,\n thinkingEnabled,\n selectedModel,\n sendMessage,\n stopStreaming,\n addMessage,\n clearMessages,\n registerEditHandler,\n undoEdit,\n registerExpandPlanHandler,\n expandPlan,\n ])\n\n return (\n <ChatContext.Provider value={value}>\n {children}\n </ChatContext.Provider>\n )\n}\n\nexport function useChatContext() {\n const context = useContext(ChatContext)\n if (!context) {\n throw new Error('useChatContext must be used within a ChatProvider')\n }\n return context\n}\n\nexport { ChatContext }\n","'use client'\n\nimport { useState, useRef, useEffect, useCallback } from 'react'\nimport { createPortal } from 'react-dom'\nimport { Loader2, X, Copy, Check, ArrowUp, Pencil, Undo2, ChevronDown, MessageSquare, Globe, Brain, Square, List } from 'lucide-react'\nimport { useChatContext, type ChatMode } from '../hooks/useChat'\nimport { useAIModels } from '../hooks/useAIModels'\nimport { ControlButton } from './ControlButton'\nimport { ModelSelector } from './ModelSelector'\nimport { markdownToHtml } from '../../lib/markdown'\n\n/** Default prose classes for chat messages */\nconst DEFAULT_PROSE_CLASSES = 'prose prose-gray dark:prose-invert max-w-none prose-p:leading-relaxed prose-a:underline'\n\n/** Strip <plan> tags for display during streaming */\nfunction stripPlanTags(content: string): string {\n return content\n .replace(/<plan>/gi, '')\n .replace(/<\\/plan>/gi, '')\n}\n\ninterface ChatPanelProps {\n /** Optional prose classes for message rendering */\n proseClasses?: string\n /** Optional callback when navigating (e.g., for expandPlan navigation) */\n onNavigate?: (path: string) => void\n /** Whether currently on an editor page (controls Draft Essay behavior) */\n isOnEditor?: boolean\n /** API path for fetching models (defaults to /api/cms/ai/settings) */\n modelsApiPath?: string\n}\n\nexport function ChatPanel({ \n proseClasses = DEFAULT_PROSE_CLASSES,\n onNavigate,\n isOnEditor: isOnEditorProp,\n}: ChatPanelProps) {\n const { \n messages, \n isStreaming, \n isOpen: open, \n setIsOpen,\n sendMessage: contextSendMessage,\n stopStreaming,\n essayContext,\n mode,\n setMode,\n undoEdit,\n webSearchEnabled,\n setWebSearchEnabled,\n thinkingEnabled,\n setThinkingEnabled,\n selectedModel,\n setSelectedModel,\n expandPlan,\n } = useChatContext()\n \n // Use essayContext presence to detect if we're editing (works with autoblogger)\n const isOnEditor = isOnEditorProp ?? !!essayContext\n \n const [input, setInput] = useState('')\n const [isAnimating, setIsAnimating] = useState(false)\n const [isVisible, setIsVisible] = useState(false)\n const [mounted, setMounted] = useState(false)\n const [copiedIndex, setCopiedIndex] = useState<number | null>(null)\n const [modeMenuOpen, setModeMenuOpen] = useState(false)\n const modeMenuRef = useRef<HTMLDivElement>(null)\n const messagesEndRef = useRef<HTMLDivElement>(null)\n const messagesContainerRef = useRef<HTMLDivElement>(null)\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const prevMessageCountRef = useRef(0)\n const savedScrollPositionRef = useRef<number | null>(null)\n const lastUserMessageRef = useRef<HTMLDivElement>(null)\n \n // Fetch models, use context for selection state\n const { models, currentModel } = useAIModels({\n externalSelectedModel: selectedModel,\n externalSetSelectedModel: setSelectedModel,\n })\n \n const onClose = useCallback(() => setIsOpen(false), [setIsOpen])\n \n const copyToClipboard = useCallback(async (text: string, index: number) => {\n await navigator.clipboard.writeText(text)\n setCopiedIndex(index)\n setTimeout(() => setCopiedIndex(null), 2000)\n }, [])\n \n // Handle Draft Essay button - either expand in current editor or navigate to new editor\n const handleDraftEssay = useCallback(() => {\n // Find the last assistant message (the plan)\n const lastAssistantMessage = [...messages].reverse().find(m => m.role === 'assistant')\n if (!lastAssistantMessage?.content) return\n \n if (isOnEditor) {\n // Already on editor page - use the registered handler and switch to agent mode\n expandPlan()\n setMode('agent')\n } else if (onNavigate) {\n // Not on editor - store plan in sessionStorage and navigate\n sessionStorage.setItem('pendingPlan', lastAssistantMessage.content)\n setIsOpen(false)\n onNavigate('/editor?fromPlan=1')\n }\n }, [messages, isOnEditor, expandPlan, setIsOpen, setMode, onNavigate])\n \n // Close mode menu on outside click\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (modeMenuRef.current && !modeMenuRef.current.contains(e.target as Node)) {\n setModeMenuOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [])\n \n // Client-side only for portal\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Handle open/close - just set visibility\n useEffect(() => {\n if (open) {\n setIsVisible(true)\n // Lock body scroll on iOS\n document.body.style.overflow = 'hidden'\n document.body.style.position = 'fixed'\n document.body.style.width = '100%'\n document.body.style.top = `-${window.scrollY}px`\n } else {\n setIsAnimating(false)\n // Restore body scroll\n const scrollY = document.body.style.top\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.width = ''\n document.body.style.top = ''\n window.scrollTo(0, parseInt(scrollY || '0') * -1)\n // Wait for animation to complete before hiding\n const timer = setTimeout(() => {\n setIsVisible(false)\n }, 200)\n return () => clearTimeout(timer)\n }\n }, [open])\n\n // Start animation AFTER visibility is set (separate render cycle)\n useEffect(() => {\n if (isVisible && open && !isAnimating) {\n // Element is now in DOM with translate-x-full, trigger transition\n requestAnimationFrame(() => {\n setIsAnimating(true)\n })\n }\n }, [isVisible, open, isAnimating])\n\n // Save scroll position when panel closes\n useEffect(() => {\n if (!open && messagesContainerRef.current) {\n savedScrollPositionRef.current = messagesContainerRef.current.scrollTop\n }\n }, [open])\n\n // Restore scroll position or scroll to bottom for new messages\n useEffect(() => {\n if (!open || !isVisible) return\n \n const container = messagesContainerRef.current\n if (!container) return\n \n const prevCount = prevMessageCountRef.current\n const currentCount = messages.length\n \n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (currentCount > prevCount) {\n // Scroll when new messages added (user sends message)\n const behavior = prevCount === 0 ? 'instant' : 'smooth'\n messagesEndRef.current?.scrollIntoView({ behavior })\n } else if (savedScrollPositionRef.current !== null) {\n // Re-opening panel - restore scroll position\n container.scrollTop = savedScrollPositionRef.current\n }\n \n prevMessageCountRef.current = currentCount\n })\n })\n }, [messages.length, open, isVisible])\n\n // During streaming, scroll until user's message reaches top of container\n useEffect(() => {\n if (!isStreaming) return\n \n const container = messagesContainerRef.current\n const userMessage = lastUserMessageRef.current\n if (!container || !userMessage) return\n \n // Get positions\n const containerRect = container.getBoundingClientRect()\n const messageRect = userMessage.getBoundingClientRect()\n \n // If user message is still below the top of container, scroll down\n const distanceFromTop = messageRect.top - containerRect.top\n if (distanceFromTop > 10) {\n // Scroll by a portion of the distance, creates smooth catching up\n container.scrollTop += Math.min(distanceFromTop * 0.3, 30)\n }\n }, [messages, isStreaming])\n\n // Auto-resize textarea\n useEffect(() => {\n const textarea = textareaRef.current\n if (textarea) {\n textarea.style.height = 'auto'\n textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`\n }\n }, [input])\n\n\n const sendMessage = useCallback(async () => {\n if (!input.trim() || isStreaming) return\n const content = input.trim()\n setInput('')\n await contextSendMessage(content)\n }, [input, isStreaming, contextSendMessage])\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n sendMessage()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n onClose()\n }\n }\n \n // Keyboard shortcut for agent mode (Cmd/Ctrl + Shift + A)\n useEffect(() => {\n const handleGlobalKeyDown = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === 'a') {\n e.preventDefault()\n if (!open) {\n setIsOpen(true)\n }\n // Only switch to agent mode if there's an essay context\n if (essayContext) {\n setMode('agent')\n }\n }\n }\n \n document.addEventListener('keydown', handleGlobalKeyDown)\n return () => document.removeEventListener('keydown', handleGlobalKeyDown)\n }, [open, setIsOpen, setMode, essayContext])\n\n if (!isVisible || !mounted) return null\n \n const getModeStyles = (m: ChatMode) => {\n if (m === 'ask') return 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'\n if (m === 'agent') return 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'\n return 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400'\n }\n\n return createPortal(\n <>\n {/* Backdrop */}\n <div \n className={`fixed inset-0 h-[100dvh] bg-black/20 z-[60] transition-opacity duration-200 ${isAnimating ? 'opacity-100' : 'opacity-0'}`}\n onClick={onClose}\n />\n \n {/* Panel */}\n <div \n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Chat\"\n className={`fixed z-[70] flex flex-col bg-background shadow-xl transition-transform duration-200 ease-out overflow-hidden inset-x-0 top-0 h-[100dvh] md:left-auto md:w-full md:max-w-[380px] md:border-l md:border-border ${isAnimating ? 'translate-x-0' : 'translate-x-full'}`}\n >\n {/* Header */}\n <div className=\"flex-shrink-0 border-b border-border px-4 py-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"font-medium\">Chat</h2>\n {essayContext && (\n <span className=\"inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-full bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400 truncate max-w-[120px]\">\n {essayContext.title || 'Untitled'}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"w-8 h-8 rounded-md hover:bg-accent flex items-center justify-center text-muted-foreground\"\n aria-label=\"Close chat\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n {/* Messages */}\n <div ref={messagesContainerRef} className=\"flex-1 overflow-y-auto\">\n {messages.length === 0 ? (\n <div className=\"h-full flex items-center justify-center\">\n <div className=\"text-center max-w-xs px-6\">\n <p className=\"text-muted-foreground text-sm\">\n {mode === 'plan'\n ? \"Describe your essay idea and I'll create a structured outline with section headers and key points.\"\n : essayContext \n ? \"Chat about your essay — ask for feedback, discuss ideas, or get help with specific sections.\"\n : \"Chat with AI to brainstorm ideas, get feedback, or explore topics.\"\n }\n </p>\n </div>\n </div>\n ) : (\n <div className=\"px-4 py-4 space-y-4\">\n {messages.map((message, index) => {\n // Track last user message for scroll behavior\n const isLastUserMessage = message.role === 'user' && \n !messages.slice(index + 1).some(m => m.role === 'user')\n \n return (\n <div\n key={index}\n ref={isLastUserMessage ? lastUserMessageRef : undefined}\n className={`flex gap-3 group ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}\n >\n <div\n className={`max-w-[85%] rounded-2xl px-3 py-2 text-sm relative ${message.role === 'user' ? 'bg-primary text-primary-foreground' : 'bg-muted'}`}\n >\n {message.role === 'assistant' ? (\n <div \n className={`${proseClasses} [&>*:first-child]:mt-0 [&>*:last-child]:mb-0`}\n dangerouslySetInnerHTML={{ __html: markdownToHtml(stripPlanTags(message.content)) }}\n />\n ) : (\n <div className=\"whitespace-pre-wrap break-words\">\n {message.content}\n </div>\n )}\n {isStreaming && index === messages.length - 1 && message.role === 'assistant' && (\n <span className=\"inline-block w-1.5 h-3 bg-current ml-0.5 animate-pulse\" />\n )}\n {/* Applied edit indicator with undo button */}\n {message.appliedEdits && message.previousState && (\n <div className=\"flex items-center gap-2 mt-1.5\">\n <div className=\"flex items-center gap-1 text-xs text-green-600 dark:text-green-400\">\n <Pencil className=\"w-3 h-3\" />\n <span>Edit applied</span>\n </div>\n <button\n onClick={() => undoEdit(index)}\n className=\"flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"Undo edit\"\n >\n <Undo2 className=\"w-3 h-3\" />\n <span>Undo</span>\n </button>\n </div>\n )}\n {/* Action buttons for assistant messages */}\n {message.role === 'assistant' && !isStreaming && (\n <div className=\"absolute -bottom-6 left-0 flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity\">\n <button\n onClick={() => copyToClipboard(message.content, index)}\n className=\"text-muted-foreground hover:text-foreground p-1 rounded\"\n aria-label=\"Copy message\"\n >\n {copiedIndex === index ? (\n <Check className=\"w-3.5 h-3.5 text-green-500\" />\n ) : (\n <Copy className=\"w-3.5 h-3.5\" />\n )}\n </button>\n {/* Draft Essay button for plan mode */}\n {message.mode === 'plan' && index === messages.length - 1 && message.content && (\n <button\n onClick={handleDraftEssay}\n className=\"text-xs text-muted-foreground hover:text-foreground px-1 rounded\"\n >\n Draft Essay\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n )})}\n <div ref={messagesEndRef} />\n </div>\n )}\n </div>\n\n {/* Input */}\n <form \n onSubmit={(e) => { e.preventDefault(); sendMessage(); }}\n className=\"flex-shrink-0 border-t border-border bg-background p-3 pb-[calc(1rem+env(safe-area-inset-bottom,0px))]\"\n >\n {/* Controls Row: Mode Dropdown, Globe Toggle, Model Dropdown */}\n <div className=\"mb-2 flex items-center gap-3\">\n {/* Mode Dropdown */}\n <div ref={modeMenuRef} className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setModeMenuOpen(!modeMenuOpen)}\n className={`inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full transition-colors hover:opacity-80 ${getModeStyles(mode)}`}\n title=\"Switch mode (⌘⇧A)\"\n >\n {mode === 'ask' && <MessageSquare className=\"w-3 h-3\" />}\n {mode === 'agent' && <Pencil className=\"w-3 h-3\" />}\n {mode === 'plan' && <List className=\"w-3 h-3\" />}\n {mode === 'ask' ? 'Ask' : mode === 'agent' ? 'Agent' : 'Plan'}\n <ChevronDown className=\"w-3 h-3 opacity-60\" />\n </button>\n {modeMenuOpen && (\n <div className=\"absolute top-full left-0 mt-1 min-w-[140px] bg-popover border border-border rounded-md shadow-md z-[80]\">\n <button\n type=\"button\"\n onClick={() => { setMode('agent'); setModeMenuOpen(false); textareaRef.current?.focus() }}\n disabled={!essayContext}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <span className=\"flex items-center gap-2\">\n <Pencil className=\"w-4 h-4\" />\n Agent\n </span>\n <span className=\"flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">⌘⇧A</span>\n {mode === 'agent' && <Check className=\"w-4 h-4\" />}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => { setMode('plan'); setModeMenuOpen(false); textareaRef.current?.focus() }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between\"\n >\n <span className=\"flex items-center gap-2\">\n <List className=\"w-4 h-4\" />\n Plan\n </span>\n {mode === 'plan' && <Check className=\"w-4 h-4\" />}\n </button>\n <button\n type=\"button\"\n onClick={() => { setMode('ask'); setModeMenuOpen(false); textareaRef.current?.focus() }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between\"\n >\n <span className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-4 h-4\" />\n Ask\n </span>\n {mode === 'ask' && <Check className=\"w-4 h-4\" />}\n </button>\n </div>\n )}\n </div>\n \n {/* Web Search Toggle */}\n <ControlButton\n onClick={() => { setWebSearchEnabled(!webSearchEnabled); textareaRef.current?.focus() }}\n active={webSearchEnabled}\n title={webSearchEnabled ? \"Web search enabled\" : \"Enable web search\"}\n tabIndex={-1}\n >\n <Globe className=\"w-4 h-4\" />\n </ControlButton>\n \n {/* Thinking Mode Toggle */}\n <ControlButton\n onClick={() => { setThinkingEnabled(!thinkingEnabled); textareaRef.current?.focus() }}\n active={thinkingEnabled}\n title={thinkingEnabled ? \"Thinking mode enabled\" : \"Enable thinking mode\"}\n tabIndex={-1}\n >\n <Brain className=\"w-4 h-4\" />\n </ControlButton>\n \n {/* Model Dropdown */}\n <ModelSelector\n models={models}\n selectedModel={selectedModel}\n onModelChange={(id) => { setSelectedModel(id); textareaRef.current?.focus() }}\n currentModel={currentModel}\n />\n </div>\n <div className=\"flex items-end gap-2\">\n <textarea\n ref={textareaRef}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={\n mode === 'plan'\n ? \"Describe your essay idea...\"\n : mode === 'agent' && essayContext\n ? \"Ask me to edit your essay...\"\n : essayContext \n ? \"Ask about your essay...\" \n : \"Ask anything...\"\n }\n className=\"flex-1 min-h-[40px] max-h-[120px] resize-none px-3 py-2 border border-input rounded-md bg-transparent text-sm focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\"\n rows={1}\n autoFocus\n />\n <button\n type={isStreaming ? 'button' : 'submit'}\n onClick={isStreaming ? stopStreaming : undefined}\n disabled={!isStreaming && !input.trim()}\n className=\"rounded-full w-10 h-10 flex-shrink-0 border border-input bg-secondary text-secondary-foreground hover:bg-secondary/80 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center touch-manipulation\"\n >\n {isStreaming ? (\n <Square className=\"h-4 w-4 fill-current\" />\n ) : (\n <ArrowUp className=\"h-5 w-5\" />\n )}\n </button>\n </div>\n </form>\n </div>\n </>,\n document.body\n )\n}\n","'use client'\n\nimport { useState, useEffect } from 'react'\nimport type { AIModelOption } from '../../lib/models'\n\ninterface UseAIModelsOptions {\n /** External selected model state (for context-managed selection) */\n externalSelectedModel?: string\n /** External setter (for context-managed selection) */\n externalSetSelectedModel?: (id: string) => void\n /** Custom API path for settings (defaults to /api/cms/ai/settings) */\n apiPath?: string\n}\n\ninterface UseAIModelsResult {\n models: AIModelOption[]\n selectedModel: string\n setSelectedModel: (id: string) => void\n currentModel: AIModelOption | undefined\n isLoading: boolean\n}\n\n/**\n * Hook to fetch available AI models and manage selection.\n * Fetches models from AI settings endpoint and sets default model on mount.\n * \n * Can use internal state (default) or external state (for context-managed selection).\n */\nexport function useAIModels(options?: UseAIModelsOptions): UseAIModelsResult {\n const [models, setModels] = useState<AIModelOption[]>([])\n const [internalSelectedModel, setInternalSelectedModel] = useState('')\n const [isLoading, setIsLoading] = useState(true)\n\n // Use external state if provided, otherwise internal\n const selectedModel = options?.externalSelectedModel ?? internalSelectedModel\n const setSelectedModel = options?.externalSetSelectedModel ?? setInternalSelectedModel\n \n const apiPath = options?.apiPath ?? '/api/cms/ai/settings'\n\n useEffect(() => {\n fetch(apiPath)\n .then(res => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n return res.json()\n })\n .then(data => {\n // Handle both wrapped { data: {...} } and unwrapped response formats\n const settings = data.data || data\n setModels(settings.availableModels || [])\n if (settings.defaultModel && !selectedModel) {\n setSelectedModel(settings.defaultModel)\n }\n })\n .catch(console.error)\n .finally(() => setIsLoading(false))\n }, [apiPath]) // eslint-disable-line react-hooks/exhaustive-deps\n // Intentionally omit selectedModel/setSelectedModel - we only want to set default on mount\n\n const currentModel = models.find(m => m.id === selectedModel)\n\n return {\n models,\n selectedModel,\n setSelectedModel,\n currentModel,\n isLoading,\n }\n}\n"],"mappings":"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","names":["useState","Loader2","useState","useEffect","useMemo","useRef","ChevronDown","Check","jsx","useState","useEffect","jsx","useState","useEffect","jsx","jsxs","useState","useEffect","useMemo","ChevronDown","Check","useRef","useState","useEffect","useCallback","useRef","jsx","jsx","useState","useEffect","useCallback","jsx","jsxs","useState","useEffect","useCallback","Fragment","jsx","jsxs","useRef","useCallback","Fragment","jsx","jsxs","useRef","useCallback","useCallback","useState","Loader2","useState","useRef","useEffect","useCallback","createContext","useContext","Fragment","jsx","jsxs","createContext","useState","useRef","useCallback","useEffect","useContext","jsx","jsxs","useState","Loader2","Fragment","jsx","jsxs","useCallback","jsx","jsxs","useEffect","useMemo","Image","TextSelection","Fragment","Selection","joinPoint","originalCommand","Schema","DOMParser","Node","NodeSelection","canSplit","Plugin","PluginKey","canJoin","command","run","mark","isTextSelection","isActive","document","style","Schema","range","NodeSelection","TextSelection","first","Fragment","newNextTypeAttributes","nextType","canSplit","isActive","Fragment","run","Plugin","Fragment","from","to","range","_a","_b","_c","PluginKey","Selection","tr","parseAttributes","serializeAttributes","h","nextIndent","Plugin","PluginKey","jsx","useMemo","Image","editor","useEffect","useState","useRef","useEffect","useCallback","createPortal","X","MessageSquare","ArrowUp","Loader2","ChevronDown","useState","Check","Fragment","jsx","jsxs","formatRelativeTime","useState","Check","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","createPortal","MessageSquare","X","Loader2","ArrowUp","ChevronDown","useState","useEffect","useRef","Fragment","jsx","jsxs","useState","useCallback","useEffect","useState","useEffect","useCallback","jsx","jsxs","Skeleton","useRef","useEffect","useState","useCallback","lines","useState","useEffect","ChevronDown","ChevronRight","Plus","Pencil","Trash2","X","MoreVertical","MoreHorizontal","Loader2","Fragment","jsx","jsxs","useState","useEffect","MoreVertical","ChevronDown","Loader2","Plus","X","Pencil","Trash2","MoreHorizontal","ChevronRight","ChevronLeft","Fragment","jsx","jsxs","ChevronLeft","useEffect","useRef","Fragment","jsx","jsxs","useState","Loader2","createContext","useContext","useState","useCallback","useRef","useEffect","useMemo","jsx","value","useState","useRef","useEffect","useCallback","createPortal","X","Check","ArrowUp","Pencil","Undo2","ChevronDown","MessageSquare","Globe","Brain","List","useState","useEffect","Fragment","jsx","jsxs","DEFAULT_PROSE_CLASSES","useState","useRef","useCallback","useEffect","createPortal","X","Pencil","Undo2","Check","MessageSquare","List","ChevronDown","Globe","Brain","ArrowUp"]}
|
|
1
|
+
{"version":3,"sources":["../src/ui/dashboard.tsx","../src/ui/context.tsx","../src/ui/pages/WriterDashboard.tsx","../src/ui/components/ControlButton.tsx","../src/ui/components/ModelSelector.tsx","../src/lib/models.ts","../src/lib/format.ts","../src/ui/pages/EditorPage.tsx","../src/ui/components/EditorToolbar.tsx","../src/lib/cn.ts","../src/ui/components/Skeleton.tsx","../src/ui/components/toolbar/ToolbarButton.tsx","../src/ui/components/toolbar/FormatButtons.tsx","../src/lib/markdown-helpers.ts","../src/ui/components/toolbar/BlockButtons.tsx","../src/ui/components/toolbar/MediaButtons.tsx","../src/ui/components/toolbar/HistoryButtons.tsx","../src/ui/components/RevisionHistoryDropdown.tsx","../src/ui/components/Dropdown.tsx","../src/ui/components/TiptapEditor.tsx","../src/lib/markdown.ts","../node_modules/@tiptap/core/src/helpers/createChainableState.ts","../node_modules/@tiptap/core/src/CommandManager.ts","../node_modules/@tiptap/core/src/commands/index.ts","../node_modules/@tiptap/core/src/commands/blur.ts","../node_modules/@tiptap/core/src/commands/clearContent.ts","../node_modules/@tiptap/core/src/commands/clearNodes.ts","../node_modules/@tiptap/core/src/commands/command.ts","../node_modules/@tiptap/core/src/commands/createParagraphNear.ts","../node_modules/@tiptap/core/src/commands/cut.ts","../node_modules/@tiptap/core/src/commands/deleteCurrentNode.ts","../node_modules/@tiptap/core/src/helpers/getNodeType.ts","../node_modules/@tiptap/core/src/commands/deleteNode.ts","../node_modules/@tiptap/core/src/commands/deleteRange.ts","../node_modules/@tiptap/core/src/commands/deleteSelection.ts","../node_modules/@tiptap/core/src/commands/enter.ts","../node_modules/@tiptap/core/src/commands/exitCode.ts","../node_modules/@tiptap/core/src/commands/extendMarkRange.ts","../node_modules/@tiptap/core/src/utilities/isRegExp.ts","../node_modules/@tiptap/core/src/utilities/objectIncludes.ts","../node_modules/@tiptap/core/src/helpers/getMarkRange.ts","../node_modules/@tiptap/core/src/helpers/getMarkType.ts","../node_modules/@tiptap/core/src/commands/first.ts","../node_modules/@tiptap/core/src/helpers/isTextSelection.ts","../node_modules/@tiptap/core/src/helpers/resolveFocusPosition.ts","../node_modules/@tiptap/core/src/utilities/minMax.ts","../node_modules/@tiptap/core/src/utilities/isAndroid.ts","../node_modules/@tiptap/core/src/utilities/isiOS.ts","../node_modules/@tiptap/core/src/utilities/isSafari.ts","../node_modules/@tiptap/core/src/commands/focus.ts","../node_modules/@tiptap/core/src/commands/forEach.ts","../node_modules/@tiptap/core/src/commands/insertContent.ts","../node_modules/@tiptap/core/src/commands/insertContentAt.ts","../node_modules/@tiptap/core/src/helpers/createNodeFromContent.ts","../node_modules/@tiptap/core/src/utilities/elementFromString.ts","../node_modules/@tiptap/core/src/helpers/selectionToInsertionEnd.ts","../node_modules/@tiptap/core/src/commands/join.ts","../node_modules/@tiptap/core/src/commands/joinItemBackward.ts","../node_modules/@tiptap/core/src/commands/joinItemForward.ts","../node_modules/@tiptap/core/src/commands/joinTextblockBackward.ts","../node_modules/@tiptap/core/src/commands/joinTextblockForward.ts","../node_modules/@tiptap/core/src/utilities/isMacOS.ts","../node_modules/@tiptap/core/src/commands/keyboardShortcut.ts","../node_modules/@tiptap/core/src/commands/lift.ts","../node_modules/@tiptap/core/src/helpers/isNodeActive.ts","../node_modules/@tiptap/core/src/commands/liftEmptyBlock.ts","../node_modules/@tiptap/core/src/commands/liftListItem.ts","../node_modules/@tiptap/core/src/commands/newlineInCode.ts","../node_modules/@tiptap/core/src/helpers/getSchemaTypeNameByName.ts","../node_modules/@tiptap/core/src/utilities/deleteProps.ts","../node_modules/@tiptap/core/src/commands/resetAttributes.ts","../node_modules/@tiptap/core/src/commands/scrollIntoView.ts","../node_modules/@tiptap/core/src/commands/selectAll.ts","../node_modules/@tiptap/core/src/commands/selectNodeBackward.ts","../node_modules/@tiptap/core/src/commands/selectNodeForward.ts","../node_modules/@tiptap/core/src/commands/selectParentNode.ts","../node_modules/@tiptap/core/src/commands/selectTextblockEnd.ts","../node_modules/@tiptap/core/src/commands/selectTextblockStart.ts","../node_modules/@tiptap/core/src/helpers/createDocument.ts","../node_modules/@tiptap/core/src/commands/setContent.ts","../node_modules/@tiptap/core/src/helpers/getMarkAttributes.ts","../node_modules/@tiptap/core/src/helpers/combineTransactionSteps.ts","../node_modules/@tiptap/core/src/helpers/defaultBlockAt.ts","../node_modules/@tiptap/core/src/helpers/findChildren.ts","../node_modules/@tiptap/core/src/helpers/findChildrenInRange.ts","../node_modules/@tiptap/core/src/helpers/findParentNodeClosestToPos.ts","../node_modules/@tiptap/core/src/helpers/findParentNode.ts","../node_modules/@tiptap/core/src/helpers/getExtensionField.ts","../node_modules/@tiptap/core/src/helpers/flattenExtensions.ts","../node_modules/@tiptap/core/src/helpers/generateHTML.ts","../node_modules/@tiptap/core/src/helpers/getHTMLFromFragment.ts","../node_modules/@tiptap/core/src/helpers/getSchemaByResolvedExtensions.ts","../node_modules/@tiptap/core/src/utilities/isFunction.ts","../node_modules/@tiptap/core/src/utilities/callOrReturn.ts","../node_modules/@tiptap/core/src/utilities/isEmptyObject.ts","../node_modules/@tiptap/core/src/helpers/splitExtensions.ts","../node_modules/@tiptap/core/src/helpers/getAttributesFromExtensions.ts","../node_modules/@tiptap/core/src/utilities/mergeAttributes.ts","../node_modules/@tiptap/core/src/helpers/getRenderedAttributes.ts","../node_modules/@tiptap/core/src/utilities/fromString.ts","../node_modules/@tiptap/core/src/helpers/injectExtensionAttributesToParseRule.ts","../node_modules/@tiptap/core/src/utilities/findDuplicates.ts","../node_modules/@tiptap/core/src/helpers/sortExtensions.ts","../node_modules/@tiptap/core/src/helpers/resolveExtensions.ts","../node_modules/@tiptap/core/src/helpers/getSchema.ts","../node_modules/@tiptap/core/src/helpers/generateJSON.ts","../node_modules/@tiptap/core/src/helpers/generateText.ts","../node_modules/@tiptap/core/src/helpers/getTextBetween.ts","../node_modules/@tiptap/core/src/helpers/getText.ts","../node_modules/@tiptap/core/src/helpers/getTextSerializersFromSchema.ts","../node_modules/@tiptap/core/src/helpers/getNodeAttributes.ts","../node_modules/@tiptap/core/src/helpers/getAttributes.ts","../node_modules/@tiptap/core/src/utilities/removeDuplicates.ts","../node_modules/@tiptap/core/src/helpers/getChangedRanges.ts","../node_modules/@tiptap/core/src/helpers/getDebugJSON.ts","../node_modules/@tiptap/core/src/helpers/getMarksBetween.ts","../node_modules/@tiptap/core/src/helpers/getNodeAtPosition.ts","../node_modules/@tiptap/core/src/helpers/getSchemaTypeByName.ts","../node_modules/@tiptap/core/src/helpers/getSplittedAttributes.ts","../node_modules/@tiptap/core/src/helpers/getTextContentFromNodes.ts","../node_modules/@tiptap/core/src/helpers/isMarkActive.ts","../node_modules/@tiptap/core/src/helpers/isActive.ts","../node_modules/@tiptap/core/src/helpers/isAtEndOfNode.ts","../node_modules/@tiptap/core/src/helpers/isAtStartOfNode.ts","../node_modules/@tiptap/core/src/helpers/isExtensionRulesEnabled.ts","../node_modules/@tiptap/core/src/helpers/isList.ts","../node_modules/@tiptap/core/src/helpers/isNodeEmpty.ts","../node_modules/@tiptap/core/src/helpers/isNodeSelection.ts","../node_modules/@tiptap/core/src/helpers/MappablePosition.ts","../node_modules/@tiptap/core/src/helpers/posToDOMRect.ts","../node_modules/@tiptap/core/src/helpers/rewriteUnknownContent.ts","../node_modules/@tiptap/core/src/commands/setMark.ts","../node_modules/@tiptap/core/src/commands/setMeta.ts","../node_modules/@tiptap/core/src/commands/setNode.ts","../node_modules/@tiptap/core/src/commands/setNodeSelection.ts","../node_modules/@tiptap/core/src/commands/setTextDirection.ts","../node_modules/@tiptap/core/src/commands/setTextSelection.ts","../node_modules/@tiptap/core/src/commands/sinkListItem.ts","../node_modules/@tiptap/core/src/commands/splitBlock.ts","../node_modules/@tiptap/core/src/commands/splitListItem.ts","../node_modules/@tiptap/core/src/commands/toggleList.ts","../node_modules/@tiptap/core/src/commands/toggleMark.ts","../node_modules/@tiptap/core/src/commands/toggleNode.ts","../node_modules/@tiptap/core/src/commands/toggleWrap.ts","../node_modules/@tiptap/core/src/commands/undoInputRule.ts","../node_modules/@tiptap/core/src/commands/unsetAllMarks.ts","../node_modules/@tiptap/core/src/commands/unsetMark.ts","../node_modules/@tiptap/core/src/commands/unsetTextDirection.ts","../node_modules/@tiptap/core/src/commands/updateAttributes.ts","../node_modules/@tiptap/core/src/commands/wrapIn.ts","../node_modules/@tiptap/core/src/commands/wrapInList.ts","../node_modules/@tiptap/core/src/Editor.ts","../node_modules/@tiptap/core/src/EventEmitter.ts","../node_modules/@tiptap/core/src/ExtensionManager.ts","../node_modules/@tiptap/core/src/InputRule.ts","../node_modules/@tiptap/core/src/utilities/isPlainObject.ts","../node_modules/@tiptap/core/src/utilities/mergeDeep.ts","../node_modules/@tiptap/core/src/Extendable.ts","../node_modules/@tiptap/core/src/Mark.ts","../node_modules/@tiptap/core/src/PasteRule.ts","../node_modules/@tiptap/core/src/utilities/isNumber.ts","../node_modules/@tiptap/core/src/extensions/index.ts","../node_modules/@tiptap/core/src/extensions/clipboardTextSerializer.ts","../node_modules/@tiptap/core/src/Extension.ts","../node_modules/@tiptap/core/src/extensions/commands.ts","../node_modules/@tiptap/core/src/extensions/delete.ts","../node_modules/@tiptap/core/src/extensions/drop.ts","../node_modules/@tiptap/core/src/extensions/editable.ts","../node_modules/@tiptap/core/src/extensions/focusEvents.ts","../node_modules/@tiptap/core/src/extensions/keymap.ts","../node_modules/@tiptap/core/src/extensions/paste.ts","../node_modules/@tiptap/core/src/extensions/tabindex.ts","../node_modules/@tiptap/core/src/extensions/textDirection.ts","../node_modules/@tiptap/core/src/NodePos.ts","../node_modules/@tiptap/core/src/style.ts","../node_modules/@tiptap/core/src/utilities/createStyleTag.ts","../node_modules/@tiptap/core/src/inputRules/markInputRule.ts","../node_modules/@tiptap/core/src/inputRules/nodeInputRule.ts","../node_modules/@tiptap/core/src/inputRules/textblockTypeInputRule.ts","../node_modules/@tiptap/core/src/inputRules/textInputRule.ts","../node_modules/@tiptap/core/src/inputRules/wrappingInputRule.ts","../node_modules/@tiptap/core/src/jsx-runtime.ts","../node_modules/@tiptap/core/src/lib/ResizableNodeView.ts","../node_modules/@tiptap/core/src/utilities/canInsertNode.ts","../node_modules/@tiptap/core/src/utilities/escapeForRegEx.ts","../node_modules/@tiptap/core/src/utilities/isString.ts","../node_modules/@tiptap/core/src/utilities/markdown/index.ts","../node_modules/@tiptap/core/src/utilities/markdown/attributeUtils.ts","../node_modules/@tiptap/core/src/utilities/markdown/createAtomBlockMarkdownSpec.ts","../node_modules/@tiptap/core/src/utilities/markdown/createBlockMarkdownSpec.ts","../node_modules/@tiptap/core/src/utilities/markdown/createInlineMarkdownSpec.ts","../node_modules/@tiptap/core/src/utilities/markdown/parseIndentedBlocks.ts","../node_modules/@tiptap/core/src/utilities/markdown/renderNestedMarkdownContent.ts","../node_modules/@tiptap/core/src/MarkView.ts","../node_modules/@tiptap/core/src/Node.ts","../node_modules/@tiptap/core/src/NodeView.ts","../node_modules/@tiptap/core/src/pasteRules/markPasteRule.ts","../node_modules/@tiptap/core/src/pasteRules/nodePasteRule.ts","../node_modules/@tiptap/core/src/pasteRules/textPasteRule.ts","../node_modules/@tiptap/core/src/Tracker.ts","../src/lib/comment-mark.ts","../src/ui/components/CommentsPanel.tsx","../src/ui/components/CommentThread.tsx","../src/lib/comments.ts","../src/ui/components/TagsSection.tsx","../src/ui/hooks/useComments.ts","../src/ui/pages/SettingsPage.tsx","../src/ui/components/Navbar.tsx","../src/ui/hooks/useKeyboard.ts","../src/ui/hooks/useChat.tsx","../src/ui/components/ChatPanel.tsx","../src/ui/hooks/useAIModels.ts"],"sourcesContent":["'use client'\n\nimport { useState, type ReactNode } from 'react'\nimport { Save, Loader2 } from 'lucide-react'\nimport type { CustomFieldConfig, StylesConfig } from './types'\nimport { DashboardProvider, useDashboardContext, type Session, type EditorState, type EditHandler } from './context'\nimport { WriterDashboard } from './pages/WriterDashboard'\nimport { EditorPage } from './pages/EditorPage'\nimport { SettingsPage } from './pages/SettingsPage'\nimport { Navbar } from './components/Navbar'\nimport { useDashboardKeyboard } from './hooks/useKeyboard'\n\ninterface AutobloggerDashboardProps {\n basePath?: string\n apiBasePath?: string\n styles?: StylesConfig\n fields?: CustomFieldConfig[]\n session?: Session | null\n onEditorStateChange?: (state: EditorState | null) => void\n onRegisterEditHandler?: (handler: EditHandler | null) => void\n onToggleView?: (currentPath: string, slug?: string) => void\n // Navbar props\n onSignOut?: () => void\n onThemeToggle?: () => void\n theme?: 'light' | 'dark'\n navbarRightSlot?: ReactNode\n}\n\nexport function AutobloggerDashboard({\n basePath = '/writer',\n apiBasePath = '/api/cms',\n styles,\n fields = [],\n session = null,\n onEditorStateChange,\n onRegisterEditHandler,\n onToggleView,\n onSignOut,\n onThemeToggle,\n theme,\n navbarRightSlot,\n}: AutobloggerDashboardProps) {\n return (\n <DashboardProvider basePath={basePath} apiBasePath={apiBasePath} styles={styles} fields={fields} session={session} onEditorStateChange={onEditorStateChange} onRegisterEditHandler={onRegisterEditHandler}>\n <DashboardLayout \n onToggleView={onToggleView}\n onSignOut={onSignOut}\n onThemeToggle={onThemeToggle}\n theme={theme}\n navbarRightSlot={navbarRightSlot}\n />\n </DashboardProvider>\n )\n}\n\ninterface DashboardLayoutProps {\n onToggleView?: (currentPath: string, slug?: string) => void\n onSignOut?: () => void\n onThemeToggle?: () => void\n theme?: 'light' | 'dark'\n navbarRightSlot?: ReactNode\n}\n\nfunction DashboardLayout({ \n onToggleView,\n onSignOut,\n onThemeToggle,\n theme,\n navbarRightSlot,\n}: DashboardLayoutProps) {\n const { basePath, currentPath, navigate, onEditorStateChange } = useDashboardContext()\n const [editorState, setEditorState] = useState<EditorState | null>(null)\n\n // Extract slug from editor path\n const editorSlug = currentPath.startsWith('/editor/') \n ? currentPath.replace('/editor/', '') \n : currentPath === '/editor' ? undefined : undefined\n \n const isEditorPage = currentPath.startsWith('/editor')\n\n // Handle editor state changes - track internally AND forward to host app\n const handleEditorStateChange = (state: EditorState | null) => {\n setEditorState(state)\n onEditorStateChange?.(state)\n }\n\n useDashboardKeyboard({\n basePath,\n onToggleView: onToggleView ? () => onToggleView(currentPath, editorSlug) : undefined,\n onToggleSettings: () => {\n if (currentPath.startsWith('/settings')) navigate('/')\n else navigate('/settings')\n },\n onNewPost: () => {\n if (currentPath === '/' || currentPath === '') navigate('/editor')\n },\n onEscape: () => {\n if (currentPath !== '/' && currentPath !== '') navigate('/')\n },\n })\n\n // Build the right slot with save button + any custom slot from host app\n const rightSlotWithSave = (\n <>\n {/* Save button - only show on editor page */}\n {isEditorPage && editorState && (\n <button\n type=\"button\"\n onClick={() => editorState.onSave('draft')}\n disabled={!editorState.hasUnsavedChanges || !!editorState.savingAs}\n className=\"w-9 h-9 rounded-md border border-border hover:bg-accent text-muted-foreground flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed\"\n aria-label=\"Save\"\n title={editorState.hasUnsavedChanges ? 'Save changes (⌘S)' : 'No unsaved changes'}\n >\n {editorState.savingAs ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"h-4 w-4\" />\n )}\n </button>\n )}\n {/* Extra slot from host app */}\n {navbarRightSlot}\n </>\n )\n\n return (\n <div className=\"min-h-screen bg-background flex flex-col\">\n <Navbar\n onSignOut={onSignOut}\n onThemeToggle={onThemeToggle}\n theme={theme}\n rightSlot={rightSlotWithSave}\n />\n <main className=\"flex-1\">\n <DashboardRouter path={currentPath} onEditorStateChange={handleEditorStateChange} />\n </main>\n </div>\n )\n}\n\nfunction DashboardRouter({ path, onEditorStateChange }: { path: string; onEditorStateChange?: (state: EditorState | null) => void }) {\n // Strip query params from path for routing\n const pathWithoutQuery = path.split('?')[0]\n \n if (pathWithoutQuery === '/' || pathWithoutQuery === '') return <WriterDashboard />\n if (pathWithoutQuery.startsWith('/editor')) {\n const slug = pathWithoutQuery.replace('/editor/', '').replace('/editor', '')\n // Use path as key to force remount when navigating to same editor with different query params\n return <EditorPage key={path} slug={slug || undefined} onEditorStateChange={onEditorStateChange} />\n }\n if (pathWithoutQuery.startsWith('/settings')) return <SettingsPage subPath={pathWithoutQuery.replace('/settings', '')} />\n return <div className=\"max-w-4xl mx-auto px-6 py-8\"><p className=\"text-muted-foreground\">Page not found: {path}</p></div>\n}\n\nexport type { AutobloggerDashboardProps }\n","'use client'\n\nimport { createContext, useContext, useState, useEffect, useCallback, useMemo, type ReactNode } from 'react'\nimport type { CustomFieldConfig, StylesConfig } from './types'\n\nexport interface DashboardConfig {\n fields: CustomFieldConfig[]\n styles: Required<StylesConfig>\n}\n\n// Session info (provided by the host app)\nexport interface SessionUser {\n id?: string\n name?: string | null\n email?: string\n role?: string\n}\n\nexport interface Session {\n user?: SessionUser\n}\n\n// Shared data fetched once by the provider\nexport interface SharedData {\n counts: Record<string, number>\n settings: { autoDraftEnabled: boolean }\n posts: unknown[]\n suggestedPosts: unknown[]\n aiSettings: { defaultModel: string; availableModels: unknown[] }\n}\n\n// Editor content for parent app integration (e.g., chat context)\nexport interface EditorContent {\n title: string\n subtitle: string\n markdown: string\n}\n\n// Edit command from parent app (e.g., AI agent mode)\nexport interface EditCommand {\n type: 'replace_all' | 'replace_section' | 'insert' | 'delete'\n title?: string\n subtitle?: string\n markdown?: string\n find?: string\n replace?: string\n position?: 'before' | 'after' | 'start' | 'end'\n}\n\nexport type EditHandler = (edit: EditCommand) => boolean\n\n// Editor state exposed to parent app\nexport interface EditorState {\n hasUnsavedChanges: boolean\n status: 'draft' | 'published'\n savingAs: 'draft' | 'published' | null\n onSave: (status: 'draft' | 'published') => void\n confirmLeave: () => boolean\n // Content for chat integration\n content: EditorContent\n}\n\nexport interface DashboardContextValue {\n basePath: string\n apiBasePath: string\n styles: Required<StylesConfig>\n fields: CustomFieldConfig[]\n currentPath: string\n navigate: (path: string) => void\n goBack: () => void\n canGoBack: boolean\n config: DashboardConfig\n // Session\n session: Session | null\n // Shared data\n sharedData: SharedData | null\n sharedDataLoading: boolean\n refetchSharedData: () => Promise<void>\n // Editor state callback (for parent app integration)\n onEditorStateChange?: (state: EditorState | null) => void\n // Edit handler registration (for AI agent mode)\n onRegisterEditHandler?: (handler: EditHandler | null) => void\n}\n\nconst DashboardContext = createContext<DashboardContextValue | null>(null)\n\nexport function useDashboardContext() {\n const ctx = useContext(DashboardContext)\n if (!ctx) throw new Error('useDashboardContext must be used within DashboardProvider')\n return ctx\n}\n\nconst DEFAULT_STYLES: Required<StylesConfig> = {\n container: 'max-w-[680px] mx-auto px-6',\n title: 'text-2xl font-bold',\n subtitle: 'text-lg text-muted-foreground',\n byline: 'text-sm text-muted-foreground',\n prose: 'prose dark:prose-invert max-w-none',\n}\n\nfunction extractPath(pathname: string, basePath: string): string {\n const normalized = pathname.replace(/\\/$/, '')\n const base = basePath.replace(/\\/$/, '')\n if (normalized === base) return '/'\n if (normalized.startsWith(base + '/')) return normalized.slice(base.length) || '/'\n return '/'\n}\n\ninterface DashboardProviderProps {\n basePath?: string\n apiBasePath?: string\n styles?: StylesConfig\n fields?: CustomFieldConfig[]\n session?: Session | null\n onEditorStateChange?: (state: EditorState | null) => void\n onRegisterEditHandler?: (handler: EditHandler | null) => void\n children: ReactNode\n}\n\nexport function DashboardProvider({\n basePath = '/writer',\n apiBasePath = '/api/cms',\n styles,\n fields = [],\n session = null,\n onEditorStateChange,\n onRegisterEditHandler,\n children,\n}: DashboardProviderProps) {\n const [currentPath, setCurrentPath] = useState('/')\n const [sharedData, setSharedData] = useState<SharedData | null>(null)\n const [sharedDataLoading, setSharedDataLoading] = useState(true)\n // Track navigation history within the dashboard\n const [historyDepth, setHistoryDepth] = useState(0)\n \n useEffect(() => {\n if (typeof window !== 'undefined') {\n setCurrentPath(extractPath(window.location.pathname, basePath))\n const handlePopState = () => {\n setCurrentPath(extractPath(window.location.pathname, basePath))\n // Decrement history depth on back navigation\n setHistoryDepth(d => Math.max(0, d - 1))\n }\n window.addEventListener('popstate', handlePopState)\n return () => window.removeEventListener('popstate', handlePopState)\n }\n }, [basePath])\n\n const navigate = useCallback((path: string) => {\n const fullPath = path.startsWith('/') ? basePath + path : basePath + '/' + path\n window.history.pushState({}, '', fullPath)\n setCurrentPath(path.startsWith('/') ? path : '/' + path)\n // Increment history depth on navigation\n setHistoryDepth(d => d + 1)\n }, [basePath])\n\n const goBack = useCallback(() => {\n if (historyDepth > 0) {\n // We have navigation history, use browser back\n window.history.back()\n } else {\n // No history (user landed directly on subpage), navigate to root\n navigate('/')\n }\n }, [historyDepth, navigate])\n\n const canGoBack = historyDepth > 0\n\n // Fetch all shared data once on mount\n const fetchSharedData = useCallback(async () => {\n setSharedDataLoading(true)\n try {\n const [countsRes, settingsRes, postsRes, aiSettingsRes] = await Promise.all([\n fetch(`${apiBasePath}/admin/counts`).then(r => r.ok ? r.json() : null),\n fetch(`${apiBasePath}/settings`).then(r => r.ok ? r.json() : null),\n fetch(`${apiBasePath}/posts`).then(r => r.ok ? r.json() : null),\n fetch(`${apiBasePath}/ai/settings`).then(r => r.ok ? r.json() : null),\n ])\n\n const autoDraftEnabled = settingsRes?.data?.autoDraftEnabled ?? false\n \n // Fetch suggested posts only if autoDraft is enabled\n let suggestedPosts: unknown[] = []\n if (autoDraftEnabled) {\n const suggestedRes = await fetch(`${apiBasePath}/posts?status=suggested`).then(r => r.ok ? r.json() : null)\n suggestedPosts = suggestedRes?.data || []\n }\n\n setSharedData({\n counts: countsRes?.data || {},\n settings: { autoDraftEnabled },\n posts: postsRes?.data || [],\n suggestedPosts,\n aiSettings: {\n defaultModel: aiSettingsRes?.data?.defaultModel || 'claude-sonnet',\n availableModels: aiSettingsRes?.data?.availableModels || [],\n },\n })\n } catch (err) {\n console.error('Failed to fetch shared data:', err)\n setSharedData({\n counts: {},\n settings: { autoDraftEnabled: false },\n posts: [],\n suggestedPosts: [],\n aiSettings: { defaultModel: 'claude-sonnet', availableModels: [] },\n })\n } finally {\n setSharedDataLoading(false)\n }\n }, [apiBasePath])\n\n useEffect(() => {\n fetchSharedData()\n }, [fetchSharedData])\n\n const mergedStyles = useMemo<Required<StylesConfig>>(\n () => ({ ...DEFAULT_STYLES, ...styles }),\n [styles]\n )\n \n const config = useMemo<DashboardConfig>(\n () => ({ fields, styles: mergedStyles }),\n [fields, mergedStyles]\n )\n\n const contextValue = useMemo<DashboardContextValue>(() => ({\n basePath, \n apiBasePath, \n styles: mergedStyles, \n fields, \n currentPath, \n navigate,\n goBack,\n canGoBack,\n config,\n session,\n sharedData,\n sharedDataLoading,\n refetchSharedData: fetchSharedData,\n onEditorStateChange,\n onRegisterEditHandler,\n }), [\n basePath,\n apiBasePath,\n mergedStyles,\n fields,\n currentPath,\n navigate,\n goBack,\n canGoBack,\n config,\n session,\n sharedData,\n sharedDataLoading,\n fetchSharedData,\n onEditorStateChange,\n onRegisterEditHandler,\n ])\n\n return (\n <DashboardContext.Provider value={contextValue}>\n {children}\n </DashboardContext.Provider>\n )\n}\n\nexport { DashboardContext }\n","'use client'\n\nimport { useState, useEffect, useMemo, useRef } from 'react'\nimport { Globe, Brain, ArrowUp, ChevronDown, Check, X, Plus, Search, MoreVertical, ExternalLink } from 'lucide-react'\nimport { useDashboardContext } from '../context'\nimport { ControlButton } from '../components/ControlButton'\nimport { ModelSelector } from '../components/ModelSelector'\nimport { LENGTH_OPTIONS, DEFAULT_MODELS, type AIModelOption } from '../../lib/models'\nimport { formatRelativeTime } from '../../lib/format'\n\ninterface Post {\n id: string\n title: string\n slug: string\n status: 'draft' | 'published' | 'suggested' | 'deleted'\n wordCount?: number\n updatedAt: string\n publishedAt: string | null\n}\n\ninterface SuggestedPost {\n id: string\n title: string\n subtitle: string | null\n slug: string\n sourceUrl: string | null\n createdAt: string\n topic: { id: string; name: string } | null\n}\n\ntype TabType = 'all' | 'drafts' | 'published'\n\nexport function WriterDashboard() {\n const { apiBasePath, navigate, sharedData, sharedDataLoading } = useDashboardContext()\n const [posts, setPosts] = useState<Post[]>([])\n const [loading, setLoading] = useState(true)\n const [searchQuery, setSearchQuery] = useState('')\n const [searchOpen, setSearchOpen] = useState(false)\n const [activeTab, setActiveTab] = useState<TabType>('all')\n const [suggestedPosts, setSuggestedPosts] = useState<SuggestedPost[]>([])\n const [autoDraftEnabled, setAutoDraftEnabled] = useState(false)\n const [suggestedOpen, setSuggestedOpen] = useState(false)\n const [actionLoading, setActionLoading] = useState<string | null>(null)\n \n const [models, setModels] = useState<AIModelOption[]>(DEFAULT_MODELS)\n const [modelId, setModelId] = useState('claude-sonnet')\n const [length, setLength] = useState<number>(500)\n const [webEnabled, setWebEnabled] = useState(false)\n const [thinkingEnabled, setThinkingEnabled] = useState(false)\n const [lengthOpen, setLengthOpen] = useState(false)\n\n // Sync with shared data from context\n useEffect(() => {\n if (sharedData) {\n setPosts(sharedData.posts as Post[])\n setSuggestedPosts(sharedData.suggestedPosts as SuggestedPost[])\n setAutoDraftEnabled(sharedData.settings.autoDraftEnabled)\n if (sharedData.aiSettings.availableModels.length > 0) {\n setModels(sharedData.aiSettings.availableModels as AIModelOption[])\n }\n if (sharedData.aiSettings.defaultModel) {\n setModelId(sharedData.aiSettings.defaultModel)\n }\n setLoading(false)\n }\n }, [sharedData])\n\n // Also stop loading when sharedDataLoading becomes false (even if data is empty)\n useEffect(() => {\n if (!sharedDataLoading) {\n setLoading(false)\n }\n }, [sharedDataLoading])\n\n const currentModel = models.find(m => m.id === modelId)\n const draftCount = useMemo(() => posts.filter(p => p.status === 'draft').length, [posts])\n const publishedCount = useMemo(() => posts.filter(p => p.status === 'published').length, [posts])\n\n const filteredPosts = useMemo(() => {\n let result = posts.filter(p => p.status !== 'suggested' && p.status !== 'deleted')\n if (searchQuery) {\n result = result.filter(p => p.title.toLowerCase().includes(searchQuery.toLowerCase()))\n }\n if (activeTab === 'drafts') result = result.filter(p => p.status === 'draft')\n else if (activeTab === 'published') result = result.filter(p => p.status === 'published')\n return result.sort((a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime())\n }, [posts, searchQuery, activeTab])\n\n async function handleDelete(id: string) {\n if (!confirm('Delete this post?')) return\n await fetch(`${apiBasePath}/posts/${id}`, { method: 'DELETE' })\n setPosts(posts.filter(p => p.id !== id))\n }\n\n async function handlePublish(id: string) {\n await fetch(`${apiBasePath}/posts/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'published' }),\n })\n setPosts(posts.map(p => p.id === id ? { ...p, status: 'published' as const } : p))\n }\n\n async function handleUnpublish(id: string) {\n await fetch(`${apiBasePath}/posts/${id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'draft' }),\n })\n setPosts(posts.map(p => p.id === id ? { ...p, status: 'draft' as const } : p))\n }\n\n async function handleAcceptSuggested(post: SuggestedPost) {\n setActionLoading(post.id)\n try {\n const res = await fetch(`${apiBasePath}/posts/${post.id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'draft' }),\n })\n if (res.ok) navigate(`/editor/${post.slug}`)\n } finally {\n setActionLoading(null)\n }\n }\n\n async function handleRejectSuggested(post: SuggestedPost) {\n if (!confirm(`Reject \"${post.title}\"?`)) return\n setActionLoading(post.id)\n try {\n const res = await fetch(`${apiBasePath}/posts/${post.id}`, { method: 'DELETE' })\n if (res.ok) setSuggestedPosts(suggestedPosts.filter(p => p.id !== post.id))\n } finally {\n setActionLoading(null)\n }\n }\n\n function handleIdeaSubmit(e: React.FormEvent<HTMLFormElement>) {\n e.preventDefault()\n const formData = new FormData(e.currentTarget)\n const idea = (formData.get('idea') as string)?.trim()\n if (idea) {\n const params = new URLSearchParams({\n idea,\n model: modelId,\n length: String(length),\n ...(webEnabled && { web: '1' }),\n ...(thinkingEnabled && { thinking: '1' })\n })\n navigate(`/editor?${params}`)\n }\n }\n\n if (loading) {\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n <div className=\"mt-4 mb-8\">\n <div className=\"h-6 w-48 bg-muted rounded animate-pulse mb-4\" />\n <div className=\"h-24 w-full bg-muted rounded animate-pulse\" />\n </div>\n <div className=\"flex border-b border-border mb-6 gap-4\">\n <div className=\"h-10 w-16 bg-muted rounded animate-pulse\" />\n <div className=\"h-10 w-20 bg-muted rounded animate-pulse\" />\n <div className=\"h-10 w-24 bg-muted rounded animate-pulse\" />\n </div>\n {[1,2,3,4,5].map(i => (\n <div key={i} className=\"h-16 bg-muted rounded animate-pulse mb-2\" />\n ))}\n </div>\n )\n }\n\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8 pb-16\">\n <button\n onClick={() => navigate('/editor')}\n className=\"sm:hidden fixed bottom-6 right-6 w-14 h-14 rounded-full shadow-lg z-50 bg-primary text-primary-foreground flex items-center justify-center\"\n >\n <Plus className=\"w-6 h-6\" />\n </button>\n\n <div className=\"mt-4 mb-8\">\n <h2 className=\"text-lg font-semibold mb-4\">What's on your mind?</h2>\n <form onSubmit={handleIdeaSubmit}>\n <div className=\"relative\">\n <textarea\n name=\"idea\"\n placeholder=\"Describe your idea...\"\n rows={3}\n className=\"w-full min-h-[100px] px-3 py-2 pr-14 border border-input rounded-md bg-transparent resize-none text-base focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring placeholder:text-muted-foreground\"\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.currentTarget.form?.requestSubmit()\n }\n }}\n />\n <button\n type=\"submit\"\n className=\"absolute bottom-3 right-3 rounded-full w-10 h-10 bg-secondary text-secondary-foreground flex items-center justify-center hover:bg-secondary/80\"\n >\n <ArrowUp className=\"w-5 h-5\" />\n </button>\n </div>\n \n <div className=\"flex items-center gap-3\">\n <div className=\"relative\">\n <ControlButton onClick={() => setLengthOpen(!lengthOpen)}>\n {length} words\n <ChevronDown className=\"w-3.5 h-3.5\" />\n </ControlButton>\n {lengthOpen && (\n <div className=\"absolute top-full left-0 mt-1 bg-popover border border-border rounded-md shadow-md z-50\">\n {LENGTH_OPTIONS.map(len => (\n <button\n key={len}\n onClick={() => { setLength(len); setLengthOpen(false) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between min-w-[120px]\"\n >\n {len} words\n {length === len && <Check className=\"w-4 h-4\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n \n <ControlButton onClick={() => setWebEnabled(!webEnabled)} active={webEnabled} title=\"Search the web\">\n <Globe className=\"w-4 h-4\" />\n </ControlButton>\n \n <ControlButton onClick={() => setThinkingEnabled(!thinkingEnabled)} active={thinkingEnabled} title=\"Enable thinking mode\">\n <Brain className=\"w-4 h-4\" />\n </ControlButton>\n \n <ModelSelector models={models} selectedModel={modelId} onModelChange={setModelId} currentModel={currentModel} />\n </div>\n </form>\n </div>\n\n {autoDraftEnabled && (\n <section className=\"mb-8\">\n <button onClick={() => setSuggestedOpen(!suggestedOpen)} className=\"flex items-center gap-2 text-lg font-semibold mb-4\">\n <ChevronDown className={`h-4 w-4 text-muted-foreground transition-transform ${suggestedOpen ? '' : '-rotate-90'}`} />\n Suggested\n {suggestedPosts.length > 0 && (\n <span className=\"bg-primary text-primary-foreground text-xs px-1.5 py-0.5 rounded-full min-w-[1.25rem] text-center\">\n {suggestedPosts.length}\n </span>\n )}\n </button>\n {suggestedOpen && (\n suggestedPosts.length > 0 ? (\n <div className=\"space-y-2\">\n {suggestedPosts.map(post => (\n <div key={post.id} className=\"flex items-center justify-between gap-4 py-3 border-b border-border\">\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium truncate\">{post.title}</p>\n <div className=\"flex items-center gap-3 text-sm text-muted-foreground\">\n {post.topic && <span className=\"bg-muted px-2 py-0.5 rounded text-xs\">{post.topic.name}</span>}\n <span>{new Date(post.createdAt).toLocaleDateString()}</span>\n {post.sourceUrl && (\n <a\n href={post.sourceUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"flex items-center gap-1 hover:text-foreground\"\n onClick={(e) => e.stopPropagation()}\n >\n <ExternalLink className=\"h-3 w-3\" />\n Source\n </a>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-2 flex-shrink-0\">\n <button\n onClick={() => handleRejectSuggested(post)}\n disabled={actionLoading === post.id}\n className=\"p-2 text-muted-foreground hover:text-foreground hover:bg-accent rounded-md\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n <button\n onClick={() => handleAcceptSuggested(post)}\n disabled={actionLoading === post.id}\n className=\"px-3 py-1.5 bg-primary text-primary-foreground rounded text-sm flex items-center gap-1\"\n >\n <Check className=\"h-4 w-4\" />\n Accept\n </button>\n </div>\n </div>\n ))}\n </div>\n ) : (\n <p className=\"text-muted-foreground text-sm\">\n No suggested essays — configure topics to generate drafts\n </p>\n )\n )}\n </section>\n )}\n\n <div className=\"relative flex items-center justify-between border-b border-border mb-6\">\n <div className={`flex ${searchOpen ? 'invisible sm:visible' : ''}`}>\n {(['all', 'drafts', 'published'] as TabType[]).map(tab => (\n <button\n key={tab}\n onClick={() => setActiveTab(tab)}\n className={`px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors ${\n activeTab === tab ? 'border-foreground text-foreground' : 'border-transparent text-muted-foreground hover:text-foreground'\n }`}\n >\n {tab.charAt(0).toUpperCase() + tab.slice(1)}\n <span className=\"text-muted-foreground ml-1\">\n ({tab === 'all' ? posts.filter(p => p.status !== 'suggested' && p.status !== 'deleted').length : tab === 'drafts' ? draftCount : publishedCount})\n </span>\n </button>\n ))}\n </div>\n \n <div className={`flex items-center gap-2 pb-2 ${searchOpen ? 'absolute inset-x-0 sm:relative' : ''}`}>\n <button onClick={() => navigate('/editor')} className=\"hidden sm:flex p-2 text-muted-foreground hover:text-foreground rounded-md hover:bg-accent\">\n <Plus className=\"w-4 h-4\" />\n </button>\n {searchOpen ? (\n <div className=\"flex items-center gap-2 flex-1 sm:flex-initial\">\n <input\n type=\"search\"\n placeholder=\"Search...\"\n value={searchQuery}\n onChange={e => setSearchQuery(e.target.value)}\n autoFocus\n className=\"flex-1 sm:w-48 px-3 py-1.5 text-base bg-background border border-border rounded-md outline-none focus-visible:border-ring\"\n />\n <button onClick={() => { setSearchOpen(false); setSearchQuery('') }} className=\"p-2 text-muted-foreground hover:text-foreground sm:hidden\">\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n ) : (\n <button onClick={() => setSearchOpen(true)} className=\"p-2 text-muted-foreground hover:text-foreground rounded-md hover:bg-accent\">\n <Search className=\"w-4 h-4\" />\n </button>\n )}\n </div>\n </div>\n\n <section>\n {filteredPosts.length > 0 ? (\n <div className=\"space-y-0\">\n {filteredPosts.map(post => (\n <PostItem\n key={post.id}\n post={post}\n onNavigate={() => navigate(`/editor/${post.slug}`)}\n onDelete={() => handleDelete(post.id)}\n onPublish={() => handlePublish(post.id)}\n onUnpublish={() => handleUnpublish(post.id)}\n showStatus={activeTab === 'all'}\n />\n ))}\n </div>\n ) : (\n <p className=\"text-muted-foreground py-8 text-center\">\n {searchQuery ? 'No matching articles' : 'No articles yet'}\n </p>\n )}\n </section>\n </div>\n )\n}\n\n// PostItem component matching the original design\ninterface PostItemProps {\n post: Post\n onNavigate: () => void\n onDelete: () => void\n onPublish: () => void\n onUnpublish: () => void\n showStatus?: boolean\n}\n\nfunction PostItem({ post, onNavigate, onDelete, onPublish, onUnpublish, showStatus }: PostItemProps) {\n const [menuOpen, setMenuOpen] = useState(false)\n const menuRef = useRef<HTMLDivElement>(null)\n\n // Close menu when clicking outside\n useEffect(() => {\n if (!menuOpen) return\n function handleClick(e: MouseEvent) {\n if (menuRef.current && !menuRef.current.contains(e.target as Node)) {\n setMenuOpen(false)\n }\n }\n document.addEventListener('click', handleClick)\n return () => document.removeEventListener('click', handleClick)\n }, [menuOpen])\n\n return (\n <div className=\"flex items-center justify-between py-4 border-b border-border group\">\n <div className=\"flex-1 min-w-0\">\n <button onClick={onNavigate} className=\"block text-left w-full\">\n <h3 className=\"font-medium truncate group-hover:text-muted-foreground\">\n {post.title || 'Untitled'}\n </h3>\n <p className=\"text-sm text-muted-foreground mt-1 flex items-center gap-2\">\n {showStatus && (\n <span className={`text-xs px-1.5 py-0.5 rounded uppercase font-medium ${\n post.status === 'draft' \n ? 'bg-amber-500/20 text-amber-600 dark:text-amber-400'\n : 'bg-green-500/20 text-green-600 dark:text-green-400'\n }`}>\n {post.status}\n </span>\n )}\n <span>{formatRelativeTime(post.updatedAt)}{post.wordCount ? ` · ${post.wordCount} words` : ''}</span>\n </p>\n </button>\n </div>\n \n <div className=\"relative\" ref={menuRef}>\n <button\n onClick={() => setMenuOpen(!menuOpen)}\n className=\"p-2 text-muted-foreground hover:text-foreground rounded-md hover:bg-accent\"\n >\n <MoreVertical className=\"w-4 h-4\" />\n </button>\n {menuOpen && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-lg z-50 min-w-[12rem] md:min-w-[8rem] p-1\">\n <button\n onClick={() => { onNavigate(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n Edit\n </button>\n {post.status === 'draft' && (\n <button\n onClick={() => { onPublish(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n Publish\n </button>\n )}\n {post.status === 'published' && (\n <button\n onClick={() => { onUnpublish(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n Unpublish\n </button>\n )}\n <div className=\"h-px bg-border my-1\" />\n <button\n onClick={() => { onDelete(); setMenuOpen(false) }}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm text-destructive hover:bg-accent cursor-default\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n )\n}\n","'use client'\n\nimport { forwardRef } from 'react'\n\ninterface ControlButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {\n active?: boolean\n}\n\nexport const ControlButton = forwardRef<HTMLButtonElement, ControlButtonProps>(\n ({ className = '', active, disabled, children, type = 'button', ...props }, ref) => {\n const baseClasses = \"inline-flex items-center gap-1 text-sm transition-colors focus:outline-none\"\n const stateClasses = disabled\n ? \"text-muted-foreground/30 cursor-not-allowed\"\n : active\n ? \"text-blue-500 dark:text-blue-400\"\n : \"text-muted-foreground hover:text-foreground\"\n \n return (\n <button\n ref={ref}\n type={type}\n disabled={disabled}\n className={`${baseClasses} ${stateClasses} ${className}`}\n {...props}\n >\n {children}\n </button>\n )\n }\n)\n\nControlButton.displayName = 'ControlButton'\n","'use client'\n\nimport { useState, useRef, useEffect } from 'react'\nimport { ChevronDown, Check } from 'lucide-react'\nimport type { AIModelOption } from '../../lib/models'\nimport { ControlButton } from './ControlButton'\n\ninterface ModelSelectorProps {\n models: AIModelOption[]\n selectedModel: string\n onModelChange: (id: string) => void\n currentModel?: AIModelOption\n}\n\nexport function ModelSelector({\n models,\n selectedModel,\n onModelChange,\n currentModel,\n}: ModelSelectorProps) {\n const [open, setOpen] = useState(false)\n const ref = useRef<HTMLDivElement>(null)\n const displayModel = currentModel ?? models.find(m => m.id === selectedModel)\n\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (ref.current && !ref.current.contains(e.target as Node)) {\n setOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [])\n\n return (\n <div ref={ref} className=\"relative\">\n <ControlButton onClick={() => setOpen(!open)}>\n {displayModel?.name || 'Select model'}\n <ChevronDown className=\"w-3.5 h-3.5\" />\n </ControlButton>\n {open && (\n <div className=\"absolute top-full left-0 mt-1 min-w-[180px] bg-popover border border-border rounded-md shadow-md z-50\">\n {models.map(model => (\n <button\n key={model.id}\n onClick={() => { onModelChange(model.id); setOpen(false) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between\"\n >\n <span>{model.name}</span>\n {selectedModel === model.id && <Check className=\"w-4 h-4\" />}\n </button>\n ))}\n </div>\n )}\n </div>\n )\n}\n","/** Word count options for essay generation */\nexport const LENGTH_OPTIONS = [300, 500, 800, 1000] as const\nexport type LengthOption = (typeof LENGTH_OPTIONS)[number]\n\n/** AI model option for UI dropdowns */\nexport interface AIModelOption {\n id: string\n name: string\n description: string\n hasNativeSearch: boolean\n}\n\n/** Default models - can be overridden by API */\nexport const DEFAULT_MODELS: AIModelOption[] = [\n { id: 'claude-sonnet', name: 'Sonnet 4.5', description: 'Fast, capable', hasNativeSearch: false },\n { id: 'claude-opus', name: 'Opus 4.5', description: 'Highest quality', hasNativeSearch: false },\n { id: 'gpt-5.2', name: 'GPT-5.2', description: 'Latest OpenAI', hasNativeSearch: true },\n { id: 'gpt-5-mini', name: 'GPT-5 Mini', description: 'Fast and efficient', hasNativeSearch: true },\n]\n","const MINUTE = 60_000, HOUR = 3_600_000, DAY = 86_400_000\n\n/**\n * Format a date as relative time (e.g., \"5m ago\", \"2h ago\", \"Yesterday\")\n */\nexport function formatRelativeTime(isoDate: string): string {\n const date = new Date(isoDate)\n const diffMs = Date.now() - date.getTime()\n const mins = Math.floor(diffMs / MINUTE)\n const hours = Math.floor(diffMs / HOUR)\n const days = Math.floor(diffMs / DAY)\n \n if (mins < 1) return 'Just now'\n if (mins < 60) return `${mins}m ago`\n if (hours < 24) return `${hours}h ago`\n if (days === 1) return 'Yesterday'\n if (days < 7) return `${days}d ago`\n return date.toLocaleDateString()\n}\n\n/**\n * Format a date for display\n */\nexport function formatDate(date: Date | string, options?: Intl.DateTimeFormatOptions): string {\n const d = typeof date === 'string' ? new Date(date) : date\n \n const defaultOptions: Intl.DateTimeFormatOptions = {\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n }\n \n return d.toLocaleDateString('en-US', options || defaultOptions)\n}\n\n/**\n * Format saved time for display (e.g., \"just now\", \"5m ago\", \"2:30 PM\")\n */\nexport function formatSavedTime(date: Date): string {\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffSecs = Math.floor(diffMs / 1000)\n const diffMins = Math.floor(diffMs / MINUTE)\n \n // Show relative time for recent saves\n if (diffSecs < 10) return 'just now'\n if (diffSecs < 60) return `${diffSecs}s ago`\n if (diffMins < 60) return `${diffMins}m ago`\n \n // Show clock time for today\n const isToday = date.toDateString() === now.toDateString()\n if (isToday) {\n return date.toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })\n }\n \n return date.toLocaleDateString('en-US', { \n month: 'short', \n day: 'numeric',\n hour: 'numeric',\n minute: '2-digit',\n })\n}\n\n/**\n * Count words in text\n */\nexport function countWords(text?: string | null): number {\n if (!text) return 0\n return text.split(/\\s+/).filter(Boolean).length\n}\n\n/**\n * Truncate text to a maximum length\n */\nexport function truncate(text: string, maxLength: number): string {\n // Strip markdown formatting for description\n const stripped = text\n .replace(/#+\\s/g, '')\n .replace(/\\*\\*/g, '')\n .replace(/\\*/g, '')\n .replace(/`/g, '')\n .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n .replace(/\\n+/g, ' ')\n .trim()\n \n if (stripped.length <= maxLength) return stripped\n \n return stripped.slice(0, maxLength - 3).trim() + '...'\n}\n","'use client'\n\nimport { useState, useEffect, useCallback, useRef } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport { useDashboardContext, type EditCommand } from '../context'\nimport { EditorToolbar } from '../components/EditorToolbar'\nimport { TiptapEditor, type SelectionState } from '../components/TiptapEditor'\nimport { CommentsPanel } from '../components/CommentsPanel'\nimport { TagsSection } from '../components/TagsSection'\nimport { useComments } from '../hooks/useComments'\nimport { formatSavedTime, countWords } from '../../lib/format'\n\ninterface Tag {\n id: string\n name: string\n}\n\ninterface Post {\n id?: string\n title: string\n subtitle: string\n slug: string\n markdown: string\n status: 'draft' | 'published'\n tags?: Tag[]\n [key: string]: unknown\n}\n\ninterface Revision {\n id: string\n createdAt: string\n title: string | null\n subtitle: string | null\n markdown: string\n polyhedraShape?: string | null\n}\n\n// Skeleton component\nfunction Skeleton({ className }: { className?: string }) {\n return <div className={`bg-gray-200 dark:bg-gray-800 animate-pulse rounded ${className || ''}`} />\n}\n\n// Content skeleton matching the editor layout\nfunction ContentSkeleton({ styles }: { styles: { container: string } }) {\n return (\n <div className={`${styles.container} pt-12 pb-24 mx-auto`}>\n <div className=\"space-y-2 mb-8\">\n <Skeleton className=\"h-8 w-4/5\" />\n <Skeleton className=\"h-5 w-3/5\" />\n <div className=\"!mt-4\">\n <Skeleton className=\"h-3 w-24\" />\n </div>\n </div>\n <div className=\"space-y-3\">\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-5/6\" />\n </div>\n </div>\n )\n}\n\n// Auto-resizing textarea for title/subtitle\nfunction AutoResizeTextarea({\n value,\n onChange,\n placeholder,\n disabled,\n className,\n}: {\n value: string\n onChange: (value: string) => void\n placeholder?: string\n disabled?: boolean\n className?: string\n}) {\n const ref = useRef<HTMLTextAreaElement>(null)\n\n useEffect(() => {\n if (ref.current) {\n ref.current.style.height = 'auto'\n ref.current.style.height = `${ref.current.scrollHeight}px`\n }\n }, [value])\n\n return (\n <textarea\n ref={ref}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n disabled={disabled}\n rows={1}\n onKeyDown={(e) => { if (e.key === 'Enter') e.preventDefault() }}\n className={`resize-none overflow-hidden ${className || ''}`}\n />\n )\n}\n\ninterface EditorPageProps {\n slug?: string\n onEditorStateChange?: (state: import('../context').EditorState | null) => void\n}\n\nexport function EditorPage({ slug, onEditorStateChange: onEditorStateChangeProp }: EditorPageProps) {\n const { apiBasePath, styles, fields, navigate, basePath, onRegisterEditHandler } = useDashboardContext()\n // Use prop callback (passed from DashboardLayout for internal save button)\n const onEditorStateChange = onEditorStateChangeProp\n const [post, setPost] = useState<Post>({\n title: '',\n subtitle: '',\n slug: '',\n markdown: '',\n status: 'draft',\n tags: [],\n })\n const [loading, setLoading] = useState(!!slug)\n const [saving, setSaving] = useState(false)\n const [savingAs, setSavingAs] = useState<'draft' | 'published' | null>(null)\n const [lastSaved, setLastSaved] = useState<Date | null>(null)\n const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false)\n const [generating, setGenerating] = useState(false)\n const savedContent = useRef<string>('')\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const hasTriggeredGeneration = useRef(false)\n \n // Rich text vs markdown mode\n const [showMarkdown, setShowMarkdown] = useState(false)\n const [editor, setEditor] = useState<Editor | null>(null)\n\n // Revision state\n const [revisions, setRevisions] = useState<Revision[]>([])\n const [revisionsLoading, setRevisionsLoading] = useState(false)\n const [previewingRevision, setPreviewingRevision] = useState<Revision | null>(null)\n const [originalPost, setOriginalPost] = useState<Post | null>(null)\n\n // Comments state\n const [commentsOpen, setCommentsOpen] = useState(false)\n \n // Get session info from context (if provided)\n const { session } = useDashboardContext()\n const currentUserEmail = session?.user?.email || ''\n const isAdmin = session?.user?.role === 'admin'\n\n // Define savePost and handlePublish early (before hooks that depend on them)\n const savePost = useCallback(async (silent = false) => {\n if (!silent) {\n setSaving(true)\n setSavingAs('draft')\n }\n try {\n const method = post.id ? 'PATCH' : 'POST'\n const url = post.id ? `${apiBasePath}/posts/${post.id}` : `${apiBasePath}/posts`\n \n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n title: post.title || 'Untitled Draft',\n subtitle: post.subtitle || null,\n slug: post.slug || undefined,\n markdown: post.markdown,\n status: post.status,\n tagIds: post.tags?.map(t => t.id),\n ...Object.fromEntries(fields.map(f => [f.name, post[f.name]]))\n }),\n })\n \n const data = await res.json()\n if (data.data) {\n setPost(prev => ({ ...prev, ...data.data }))\n savedContent.current = JSON.stringify({ title: data.data.title, subtitle: data.data.subtitle, markdown: data.data.markdown })\n setLastSaved(new Date())\n setHasUnsavedChanges(false)\n if (!post.id && data.data.slug) {\n navigate(`/editor/${data.data.slug}`)\n }\n }\n } catch (err) {\n console.error('Save failed:', err)\n throw err\n } finally {\n if (!silent) {\n setSaving(false)\n setSavingAs(null)\n }\n }\n }, [post.id, post.title, post.subtitle, post.slug, post.markdown, post.status, post.tags, apiBasePath, fields, navigate])\n\n const handlePublish = useCallback(async () => {\n if (!confirm('Publish this essay?')) return\n setSaving(true)\n setSavingAs('published')\n try {\n const method = post.id ? 'PATCH' : 'POST'\n const url = post.id ? `${apiBasePath}/posts/${post.id}` : `${apiBasePath}/posts`\n\n // Use setPost's callback form to get current post state without adding post to deps\n let currentPost: typeof post\n setPost(p => { currentPost = p; return p })\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n ...currentPost!,\n title: currentPost!.title || 'Untitled',\n status: 'published',\n ...Object.fromEntries(fields.map(f => [f.name, currentPost![f.name]]))\n }),\n })\n\n if (res.ok) {\n navigate('/')\n }\n } finally {\n setSaving(false)\n setSavingAs(null)\n }\n }, [post.id, apiBasePath, fields, navigate])\n\n // Comments hook - handles all comment logic\n const comments = useComments({\n postId: post.id || null,\n editor,\n apiBasePath,\n onSave: async () => {\n // Auto-save the post and return the new post ID\n await savePost(true)\n return post.id || null\n },\n })\n\n // URL params for AI generation\n const [urlParams, setUrlParams] = useState<{\n idea?: string\n model?: string\n length?: string\n web?: string\n thinking?: string\n comment?: string\n }>({})\n\n useEffect(() => {\n if (typeof window !== 'undefined') {\n const params = new URLSearchParams(window.location.search)\n setUrlParams({\n idea: params.get('idea') || undefined,\n model: params.get('model') || undefined,\n length: params.get('length') || undefined,\n web: params.get('web') || undefined,\n thinking: params.get('thinking') || undefined,\n comment: params.get('comment') || undefined,\n })\n }\n }, [])\n\n // Handle comment URL param - open panel and scroll to comment\n useEffect(() => {\n if (urlParams.comment && !comments.loading && comments.list.length > 0) {\n // Open the comments panel\n setCommentsOpen(true)\n // Set active comment and scroll to it\n comments.setActiveId(urlParams.comment)\n // Small delay to ensure panel is open before scrolling\n setTimeout(() => {\n comments.scrollTo(urlParams.comment!)\n }, 100)\n }\n }, [urlParams.comment, comments.loading, comments.list.length])\n\n // Load post if editing existing\n useEffect(() => {\n if (slug) {\n fetch(`${apiBasePath}/posts`)\n .then(r => r.json())\n .then(d => {\n const found = d.data?.find((p: Post) => p.slug === slug)\n if (found) {\n setPost(found)\n savedContent.current = JSON.stringify({ title: found.title, subtitle: found.subtitle, markdown: found.markdown })\n }\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }\n }, [slug, apiBasePath])\n\n // Track unsaved changes\n useEffect(() => {\n const current = JSON.stringify({ title: post.title, subtitle: post.subtitle, markdown: post.markdown })\n // For new posts (savedContent is empty), any content is unsaved\n // For existing posts, compare against saved content\n if (savedContent.current === '') {\n setHasUnsavedChanges(!!(post.title || post.subtitle || post.markdown))\n } else {\n setHasUnsavedChanges(current !== savedContent.current)\n }\n }, [post.title, post.subtitle, post.markdown])\n\n // Report editor state to parent app (for navbar save button etc.)\n // Use refs for callbacks to avoid triggering effect re-runs\n const savePostRef = useRef(savePost)\n const handlePublishRef = useRef(handlePublish)\n const onEditorStateChangeRef = useRef(onEditorStateChange)\n useEffect(() => { savePostRef.current = savePost }, [savePost])\n useEffect(() => { handlePublishRef.current = handlePublish }, [handlePublish])\n useEffect(() => { onEditorStateChangeRef.current = onEditorStateChange }, [onEditorStateChange])\n \n useEffect(() => {\n if (!onEditorStateChangeRef.current) return\n\n const confirmLeave = () => {\n if (hasUnsavedChanges) {\n return confirm('You have unsaved changes. Leave anyway?')\n }\n return true\n }\n\n onEditorStateChangeRef.current({\n hasUnsavedChanges,\n status: post.status,\n savingAs,\n onSave: (status) => {\n if (status === 'draft') {\n savePostRef.current()\n } else {\n handlePublishRef.current()\n }\n },\n confirmLeave,\n // Include content for chat integration\n content: {\n title: post.title,\n subtitle: post.subtitle,\n markdown: post.markdown,\n },\n })\n\n return () => {\n onEditorStateChangeRef.current?.(null)\n }\n }, [hasUnsavedChanges, post.status, savingAs, post.title, post.subtitle, post.markdown])\n\n // Register edit handler for AI agent mode\n useEffect(() => {\n if (!onRegisterEditHandler) return\n\n const handleEdit = (edit: EditCommand): boolean => {\n if (edit.type === 'replace_all') {\n setPost(prev => ({\n ...prev,\n title: edit.title ?? prev.title,\n subtitle: edit.subtitle ?? prev.subtitle,\n markdown: edit.markdown ?? prev.markdown,\n }))\n return true\n }\n\n if (edit.type === 'replace_section' && edit.find && edit.replace !== undefined) {\n if (post.markdown.includes(edit.find)) {\n setPost(prev => ({\n ...prev,\n markdown: prev.markdown.replace(edit.find!, edit.replace!),\n }))\n return true\n }\n return false\n }\n\n if (edit.type === 'insert' && edit.replace !== undefined) {\n if (edit.position === 'start') {\n setPost(prev => ({ ...prev, markdown: edit.replace + prev.markdown }))\n return true\n }\n if (edit.position === 'end') {\n setPost(prev => ({ ...prev, markdown: prev.markdown + edit.replace }))\n return true\n }\n if (edit.find && post.markdown.includes(edit.find)) {\n const idx = post.markdown.indexOf(edit.find)\n const insertPoint = edit.position === 'before' ? idx : idx + edit.find.length\n setPost(prev => ({\n ...prev,\n markdown: prev.markdown.slice(0, insertPoint) + edit.replace + prev.markdown.slice(insertPoint),\n }))\n return true\n }\n return false\n }\n\n if (edit.type === 'delete' && edit.find) {\n if (post.markdown.includes(edit.find)) {\n setPost(prev => ({\n ...prev,\n markdown: prev.markdown.replace(edit.find!, ''),\n }))\n return true\n }\n return false\n }\n\n return false\n }\n\n onRegisterEditHandler(handleEdit)\n\n return () => {\n onRegisterEditHandler(null)\n }\n }, [post.markdown, onRegisterEditHandler])\n\n // Auto-save drafts (3s debounce)\n // Deps include content fields to reset timer on each keystroke (debounce behavior)\n useEffect(() => {\n if (!post.id || post.status === 'published' || !hasUnsavedChanges || previewingRevision) return\n const timeout = setTimeout(() => savePostRef.current(true), 3000)\n return () => clearTimeout(timeout)\n }, [post.id, post.status, post.title, post.subtitle, post.markdown, hasUnsavedChanges, previewingRevision])\n\n // Warn on navigation with unsaved changes\n useEffect(() => {\n const handler = (e: BeforeUnloadEvent) => {\n if (hasUnsavedChanges) {\n e.preventDefault()\n e.returnValue = ''\n }\n }\n window.addEventListener('beforeunload', handler)\n return () => window.removeEventListener('beforeunload', handler)\n }, [hasUnsavedChanges])\n\n // Keyboard shortcuts\n useEffect(() => {\n const handler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n if (previewingRevision) {\n cancelRevisionPreview()\n return\n }\n if (generating) return\n if (hasUnsavedChanges && !confirm('You have unsaved changes. Leave anyway?')) return\n navigate('/')\n }\n }\n window.addEventListener('keydown', handler)\n return () => window.removeEventListener('keydown', handler)\n }, [hasUnsavedChanges, generating, navigate, previewingRevision])\n\n // Auto-generate from idea param\n useEffect(() => {\n if (urlParams.idea && !slug && !loading && !hasTriggeredGeneration.current) {\n hasTriggeredGeneration.current = true\n setGenerating(true)\n\n const wordCount = urlParams.length ? parseInt(urlParams.length) : 500\n\n // Clear the URL params immediately\n if (typeof window !== 'undefined') {\n window.history.replaceState({}, '', `${basePath}/editor`)\n }\n\n const runGenerate = async () => {\n try {\n const res = await fetch(`${apiBasePath}/ai/generate`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n prompt: urlParams.idea,\n wordCount,\n model: urlParams.model,\n }),\n })\n\n if (!res.ok) {\n const error = await res.json().catch(() => ({ error: 'Generation failed' }))\n console.error('Generation failed:', error)\n return\n }\n\n // Consume SSE stream with real-time title/subtitle parsing\n const reader = res.body?.getReader()\n if (!reader) return\n\n const decoder = new TextDecoder()\n let fullContent = ''\n let titleExtracted = false\n let subtitleExtracted = false\n let bodyStartIndex = 0\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n const sseLines = chunk.split('\\n')\n\n for (const line of sseLines) {\n if (line.startsWith('data: ')) {\n const data = line.slice(6)\n if (data === '[DONE]') continue\n try {\n const parsed = JSON.parse(data)\n if (parsed.text) {\n fullContent += parsed.text\n \n // Parse title as soon as first line is complete\n if (!titleExtracted && fullContent.includes('\\n')) {\n const firstLine = fullContent.split('\\n')[0]\n if (firstLine.startsWith('# ')) {\n const title = firstLine.slice(2).trim()\n setPost(prev => ({ ...prev, title }))\n titleExtracted = true\n bodyStartIndex = firstLine.length + 1 // +1 for newline\n }\n }\n \n // Parse subtitle as soon as second line is complete\n if (titleExtracted && !subtitleExtracted) {\n const afterTitle = fullContent.slice(bodyStartIndex)\n if (afterTitle.includes('\\n')) {\n const secondLine = afterTitle.split('\\n')[0].trim()\n const italicMatch = secondLine.match(/^\\*(.+)\\*$/) || secondLine.match(/^_(.+)_$/)\n if (italicMatch) {\n const subtitle = italicMatch[1]\n setPost(prev => ({ ...prev, subtitle }))\n subtitleExtracted = true\n bodyStartIndex += secondLine.length + 1 // +1 for newline\n } else if (secondLine) {\n // Second line exists but isn't a subtitle - body starts here\n subtitleExtracted = true\n }\n }\n }\n \n // Update markdown with body content only (excluding title/subtitle)\n const bodyContent = fullContent.slice(bodyStartIndex).trim()\n setPost(prev => ({ ...prev, markdown: bodyContent }))\n }\n } catch {\n // Ignore parse errors for incomplete chunks\n }\n }\n }\n }\n \n // Final cleanup - ensure body doesn't include title/subtitle\n const finalBody = fullContent.slice(bodyStartIndex).trim()\n setPost(prev => ({\n ...prev,\n markdown: finalBody,\n }))\n } catch (err) {\n console.error('Generation error:', err)\n } finally {\n setGenerating(false)\n }\n }\n\n runGenerate()\n }\n }, [urlParams, slug, loading, apiBasePath, basePath])\n\n // Fetch revisions\n const fetchRevisions = useCallback(async () => {\n if (!post.id) return\n setRevisionsLoading(true)\n try {\n const res = await fetch(`${apiBasePath}/revisions?postId=${post.id}`)\n const data = await res.json()\n setRevisions(data.data || [])\n } catch (err) {\n console.error('Failed to fetch revisions:', err)\n } finally {\n setRevisionsLoading(false)\n }\n }, [post.id, apiBasePath])\n\n // Preview a revision\n const previewRevision = useCallback(async (revisionId: string) => {\n const revision = revisions.find(r => r.id === revisionId)\n if (!revision) return\n \n // Store original post state\n if (!originalPost) {\n setOriginalPost({ ...post })\n }\n \n setPreviewingRevision(revision)\n setPost(prev => ({\n ...prev,\n title: revision.title || prev.title,\n subtitle: revision.subtitle || prev.subtitle,\n markdown: revision.markdown,\n }))\n }, [revisions, post, originalPost])\n\n // Cancel revision preview\n const cancelRevisionPreview = useCallback(() => {\n if (originalPost) {\n setPost(originalPost)\n setOriginalPost(null)\n }\n setPreviewingRevision(null)\n }, [originalPost])\n\n // Restore revision\n const restoreRevision = useCallback(async () => {\n if (!previewingRevision) return\n setOriginalPost(null)\n setPreviewingRevision(null)\n // The post is already set to the revision content, just save it\n await savePost()\n }, [previewingRevision, savePost])\n\n const handleUnpublish = async () => {\n if (!confirm('Unpublish this essay?')) return\n await fetch(`${apiBasePath}/posts/${post.id}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ status: 'draft' }),\n })\n setPost(prev => ({ ...prev, status: 'draft' }))\n }\n\n const words = countWords(post.markdown)\n const isPublished = post.status === 'published'\n\n // Loading skeleton\n if (loading) {\n return (\n <div className=\"flex flex-col h-full\">\n <EditorToolbar\n textareaRef={textareaRef}\n markdown=\"\"\n onMarkdownChange={() => {}}\n loading={true}\n />\n <main className=\"flex-1 overflow-auto pb-20\">\n <ContentSkeleton styles={styles} />\n </main>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col h-full\">\n {/* Revision Preview Banner */}\n {previewingRevision && (\n <div className=\"bg-amber-100 dark:bg-amber-900/30 border-b border-amber-200 dark:border-amber-800 px-4 py-2 flex items-center justify-between\">\n <span className=\"text-sm text-amber-800 dark:text-amber-200\">\n Previewing revision from {new Date(previewingRevision.createdAt).toLocaleString()}\n </span>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={cancelRevisionPreview}\n className=\"px-3 py-1 text-sm border border-amber-300 dark:border-amber-700 rounded hover:bg-amber-200 dark:hover:bg-amber-800\"\n >\n Cancel\n </button>\n <button\n onClick={restoreRevision}\n className=\"px-3 py-1 text-sm bg-amber-600 text-white rounded hover:bg-amber-700\"\n >\n Restore\n </button>\n </div>\n </div>\n )}\n\n {/* Toolbar */}\n {!previewingRevision && (\n <EditorToolbar\n editor={showMarkdown ? null : editor}\n textareaRef={showMarkdown ? textareaRef : undefined}\n markdown={post.markdown}\n onMarkdownChange={(md) => setPost(prev => ({ ...prev, markdown: md }))}\n showMarkdown={showMarkdown}\n setShowMarkdown={setShowMarkdown}\n aiGenerating={generating}\n postSlug={slug}\n revisions={post.id ? {\n list: revisions,\n loading: revisionsLoading,\n previewLoading: false,\n previewing: previewingRevision,\n fetch: fetchRevisions,\n preview: previewRevision,\n cancel: cancelRevisionPreview,\n restore: restoreRevision,\n } : undefined}\n apiBasePath={apiBasePath}\n hasSelection={!!comments.selectedText && !comments.selectedText.hasExistingComment}\n selectionHasComment={comments.selectedText?.hasExistingComment}\n onAddComment={() => setCommentsOpen(true)}\n commentsCount={comments.list.filter(c => !c.resolved).length}\n onViewComments={() => setCommentsOpen(true)}\n />\n )}\n\n {/* Editor Content */}\n <main className=\"flex-1 overflow-auto pb-20 overscroll-contain\">\n <article className={`${styles.container} pt-12 pb-24 mx-auto`}>\n {/* Header - Title & Subtitle */}\n <header className=\"space-y-2 mb-8\">\n {generating && !post.title ? (\n <Skeleton className=\"h-8 w-4/5\" />\n ) : (\n <AutoResizeTextarea\n value={post.title}\n onChange={(val) => setPost(prev => ({ ...prev, title: val }))}\n placeholder=\"Title\"\n disabled={generating || !!previewingRevision}\n className={`${styles.title} w-full bg-transparent border-none outline-none placeholder-gray-300 dark:placeholder-gray-700 ${(generating || previewingRevision) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n )}\n {generating && !post.subtitle ? (\n <Skeleton className=\"h-5 w-3/5\" />\n ) : (\n <AutoResizeTextarea\n value={post.subtitle}\n onChange={(val) => setPost(prev => ({ ...prev, subtitle: val }))}\n placeholder=\"Subtitle\"\n disabled={generating || !!previewingRevision}\n className={`${styles.subtitle} w-full bg-transparent border-none outline-none placeholder-gray-300 dark:placeholder-gray-700 ${(generating || previewingRevision) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n )}\n <div className=\"!mt-4\">\n <span className={`${styles.byline} underline ${generating ? 'opacity-60' : ''}`}>\n {session?.user?.name || session?.user?.email || 'Author'}\n </span>\n </div>\n </header>\n\n {/* Body */}\n <div className=\"mt-8\">\n {generating && !post.markdown ? (\n <div className=\"space-y-3\">\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-3/4\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-5/6\" />\n </div>\n ) : showMarkdown ? (\n <textarea\n ref={textareaRef}\n value={post.markdown}\n onChange={e => setPost(prev => ({ ...prev, markdown: e.target.value }))}\n placeholder=\"Start writing...\"\n disabled={generating || !!previewingRevision}\n className={`${styles.prose} w-full min-h-[400px] bg-transparent border-none outline-none resize-none placeholder-gray-400 leading-relaxed font-mono text-sm ${(generating || previewingRevision) ? 'opacity-60 cursor-not-allowed' : ''}`}\n />\n ) : (\n <TiptapEditor\n content={post.markdown}\n onChange={(md) => setPost(prev => ({ ...prev, markdown: md }))}\n onEditorReady={setEditor}\n autoFocus={!slug}\n proseClasses={styles.prose}\n onSelectionChange={(sel: SelectionState | null) => {\n if (sel?.hasSelection) {\n comments.setSelectedText({\n text: sel.text,\n from: sel.from,\n to: sel.to,\n hasExistingComment: sel.hasExistingComment,\n })\n } else {\n comments.setSelectedText(null)\n }\n }}\n onCommentClick={(commentId: string) => {\n comments.setActiveId(commentId)\n setCommentsOpen(true)\n }}\n />\n )}\n </div>\n\n {/* Footer Metadata */}\n {!previewingRevision && (\n <div className=\"mt-12 pt-8 border-t border-gray-200 dark:border-gray-800 space-y-4\">\n {/* URL */}\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-gray-500 w-14\">URL</span>\n <span className=\"text-gray-400\">/e/</span>\n {isPublished ? (\n <span className=\"flex items-center gap-1.5 text-gray-600 dark:text-gray-400\">\n {post.slug}\n <svg className=\"w-3 h-3 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z\" />\n </svg>\n </span>\n ) : (\n <input\n type=\"text\"\n value={post.slug}\n onChange={(e) => setPost(prev => ({ ...prev, slug: e.target.value }))}\n placeholder=\"post-slug\"\n className=\"flex-1 bg-transparent border-none outline-none placeholder-gray-400 text-gray-600 dark:text-gray-400\"\n />\n )}\n </div>\n </div>\n\n {/* Custom Fields (footer position) */}\n {fields.filter(f => f.position === 'footer').map(field => {\n const handleFieldChange = (name: string, value: unknown) => {\n setPost(prev => ({ ...prev, [name]: value }))\n }\n \n // If no label, render component full-width (for section-style fields like SEO)\n if (!field.label) {\n return (\n <field.component\n key={field.name}\n value={post[field.name] as any}\n onChange={(val: unknown) => setPost(prev => ({ ...prev, [field.name]: val }))}\n onFieldChange={handleFieldChange}\n post={post as any}\n disabled={saving || generating}\n />\n )\n }\n \n // With label, render inline row style\n return (\n <div key={field.name} className=\"flex items-center justify-between text-sm gap-2\">\n <div className=\"flex items-center gap-2 flex-1\">\n <span className=\"text-gray-500 w-14 flex-shrink-0\">{field.label}</span>\n <div className=\"flex-1\">\n <field.component\n value={post[field.name] as any}\n onChange={(val: unknown) => setPost(prev => ({ ...prev, [field.name]: val }))}\n onFieldChange={handleFieldChange}\n post={post as any}\n disabled={saving || generating}\n />\n </div>\n </div>\n </div>\n )\n })}\n\n {/* Tags */}\n <TagsSection\n tags={post.tags || []}\n onTagsChange={(tags) => setPost(prev => ({ ...prev, tags }))}\n apiBasePath={apiBasePath}\n disabled={saving || generating}\n />\n\n {/* Status */}\n <div className=\"flex items-center justify-between text-sm\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-gray-500 w-14\">Status</span>\n <span className={isPublished ? \"text-green-600 dark:text-green-400\" : \"text-gray-500\"}>\n {isPublished ? 'Published' : 'Draft'}\n </span>\n </div>\n \n {/* Publish/Unpublish/Update button */}\n {isPublished ? (\n hasUnsavedChanges ? (\n <button\n onClick={handlePublish}\n disabled={saving || generating}\n className=\"px-3 py-1.5 text-sm bg-primary text-primary-foreground rounded-md hover:opacity-90 disabled:opacity-50 flex items-center gap-1\"\n >\n {savingAs === 'published' && (\n <svg className=\"w-3 h-3 animate-spin\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" fill=\"none\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\" />\n </svg>\n )}\n Update\n </button>\n ) : (\n <button\n onClick={handleUnpublish}\n className=\"px-2.5 py-1 text-xs rounded bg-gray-100 dark:bg-gray-800 text-gray-500 hover:text-red-500 transition-colors\"\n >\n Unpublish\n </button>\n )\n ) : (\n <button\n onClick={handlePublish}\n disabled={saving || generating}\n className=\"px-3 py-1.5 text-sm bg-primary text-primary-foreground rounded-md hover:opacity-90 disabled:opacity-50 flex items-center gap-1\"\n >\n {savingAs === 'published' && (\n <svg className=\"w-3 h-3 animate-spin\" viewBox=\"0 0 24 24\">\n <circle className=\"opacity-25\" cx=\"12\" cy=\"12\" r=\"10\" stroke=\"currentColor\" strokeWidth=\"4\" fill=\"none\" />\n <path className=\"opacity-75\" fill=\"currentColor\" d=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\" />\n </svg>\n )}\n Publish\n </button>\n )}\n </div>\n\n {/* Word count */}\n <div className=\"text-sm text-gray-500 pt-2 border-t border-gray-200 dark:border-gray-800\">\n {words.toLocaleString()} words · ~{Math.ceil(words / 200)} min read\n </div>\n </div>\n )}\n </article>\n </main>\n\n {/* Footer */}\n <footer className=\"fixed bottom-0 left-0 right-0 border-t border-gray-200 dark:border-gray-800 px-4 py-3 bg-white dark:bg-black touch-none\">\n <div className=\"flex items-center justify-end text-sm text-gray-500\">\n {generating ? (\n <button className=\"hover:text-gray-900 dark:hover:text-white transition-colors\">\n Press Esc to stop generating\n </button>\n ) : previewingRevision ? (\n <button \n onClick={cancelRevisionPreview}\n className=\"hover:text-gray-900 dark:hover:text-white transition-colors\"\n >\n Press Esc to cancel\n </button>\n ) : lastSaved ? (\n <span>Saved {formatSavedTime(lastSaved)}</span>\n ) : (\n <span>Not saved yet</span>\n )}\n </div>\n </footer>\n\n {/* Comments Panel */}\n {currentUserEmail && (\n <CommentsPanel\n comments={comments.list}\n currentUserEmail={currentUserEmail}\n isAdmin={isAdmin}\n selectedText={comments.selectedText?.text ?? null}\n onCreateComment={comments.create}\n onReply={comments.reply}\n onEdit={comments.edit}\n onDelete={comments.remove}\n onResolve={comments.resolve}\n onCommentClick={comments.scrollTo}\n activeCommentId={comments.activeId}\n isOpen={commentsOpen}\n onClose={() => setCommentsOpen(false)}\n onClearSelection={() => comments.setSelectedText(null)}\n />\n )}\n </div>\n )\n}\n","'use client'\n\nimport type { RefObject } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport { MessageSquarePlus, MessageSquare } from 'lucide-react'\nimport { ToolbarButton, Divider, SkeletonButton } from './toolbar/ToolbarButton'\nimport { FormatButtons } from './toolbar/FormatButtons'\nimport { BlockButtons } from './toolbar/BlockButtons'\nimport { MediaButtons } from './toolbar/MediaButtons'\nimport { HistoryButtons } from './toolbar/HistoryButtons'\nimport type { RevisionState } from '../../lib/editor-types'\n\ninterface EditorToolbarProps {\n editor?: Editor | null\n // For markdown mode\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n // Mode toggle\n showMarkdown?: boolean\n setShowMarkdown?: (show: boolean) => void\n // Revision history\n postSlug?: string\n revisions?: RevisionState\n // AI generation state (disables all toolbar buttons during generation)\n aiGenerating?: boolean\n // Comments\n hasSelection?: boolean\n selectionHasComment?: boolean\n onAddComment?: () => void\n commentsCount?: number\n onViewComments?: () => void\n // Loading state\n loading?: boolean\n // API base path\n apiBasePath?: string\n}\n\nexport function EditorToolbar({\n editor,\n textareaRef,\n markdown,\n onMarkdownChange,\n showMarkdown,\n setShowMarkdown,\n postSlug,\n revisions,\n aiGenerating,\n hasSelection,\n selectionHasComment,\n onAddComment,\n commentsCount,\n onViewComments,\n loading = false,\n apiBasePath = '/api/cms',\n}: EditorToolbarProps) {\n // Navbar height: py-4 (32px) + h-9 content (36px) + border (1px) = 69px\n // Skeleton state - each button group renders its own skeletons\n if (loading) {\n return (\n <div className=\"sticky top-[69px] z-40 flex items-center justify-start lg:justify-center gap-0.5 px-4 py-2 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-black overflow-x-auto\">\n <FormatButtons loading={true} />\n <Divider />\n <BlockButtons loading={true} />\n <Divider />\n <MediaButtons loading={true} />\n <Divider />\n <HistoryButtons loading={true} />\n {/* Comment buttons skeleton */}\n <Divider />\n <SkeletonButton />\n <SkeletonButton />\n </div>\n )\n }\n\n return (\n <div className=\"sticky top-[69px] z-40 flex items-center justify-start lg:justify-center gap-0.5 px-4 py-2 border-b border-gray-200 dark:border-gray-800 bg-white dark:bg-black overflow-x-auto\">\n <FormatButtons\n editor={editor}\n textareaRef={textareaRef}\n markdown={markdown}\n onMarkdownChange={onMarkdownChange}\n aiGenerating={aiGenerating}\n apiBasePath={apiBasePath}\n />\n\n <Divider />\n\n <BlockButtons\n editor={editor}\n textareaRef={textareaRef}\n markdown={markdown}\n onMarkdownChange={onMarkdownChange}\n aiGenerating={aiGenerating}\n />\n\n <Divider />\n\n <MediaButtons\n editor={editor}\n textareaRef={textareaRef}\n markdown={markdown}\n onMarkdownChange={onMarkdownChange}\n aiGenerating={aiGenerating}\n apiBasePath={apiBasePath}\n />\n\n <Divider />\n\n <HistoryButtons\n editor={editor}\n textareaRef={textareaRef}\n showMarkdown={showMarkdown}\n setShowMarkdown={setShowMarkdown}\n postSlug={postSlug}\n revisions={revisions}\n aiGenerating={aiGenerating}\n />\n\n {/* Comments - always visible */}\n <Divider />\n <ToolbarButton\n onClick={onAddComment ?? (() => {})}\n disabled={aiGenerating || !hasSelection || !onAddComment}\n title={\n hasSelection \n ? 'New comment (⌘⌥M)' \n : selectionHasComment \n ? 'Text already has a comment' \n : 'Select text to comment'\n }\n >\n <MessageSquarePlus className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={onViewComments ?? (() => {})}\n disabled={aiGenerating || !onViewComments}\n title=\"View all comments\"\n >\n <span className=\"flex items-center gap-1\">\n <MessageSquare className=\"w-4 h-4\" />\n {commentsCount !== undefined && commentsCount > 0 && (\n <span className=\"text-xs tabular-nums\">{commentsCount}</span>\n )}\n </span>\n </ToolbarButton>\n </div>\n )\n}\n","import { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","'use client'\n\nimport { cn } from '../../lib/cn'\n\ninterface SkeletonProps {\n className?: string\n}\n\n/**\n * Base skeleton component with pulse animation.\n */\nexport function Skeleton({ className }: SkeletonProps) {\n return <div className={cn('animate-pulse bg-muted rounded', className)} />\n}\n\n/**\n * Skeleton for text content with multiple lines.\n */\nexport function SkeletonText({ \n lines = 1, \n className \n}: { \n lines?: number\n className?: string \n}) {\n return (\n <div className={cn('space-y-2', className)}>\n {Array.from({ length: lines }).map((_, i) => (\n <Skeleton \n key={i} \n className={cn(\n 'h-4',\n // Make last line shorter for more natural look\n i === lines - 1 && lines > 1 ? 'w-3/4' : 'w-full'\n )} \n />\n ))}\n </div>\n )\n}\n\n/**\n * Skeleton for toolbar buttons.\n */\nexport function SkeletonButton({ className }: SkeletonProps) {\n return <Skeleton className={cn('h-8 w-8', className)} />\n}\n\n/**\n * Skeleton for cards/panels.\n */\nexport function SkeletonCard({ className }: SkeletonProps) {\n return <Skeleton className={cn('h-24 w-full rounded-lg', className)} />\n}\n\n/**\n * Skeleton for table rows.\n */\nexport function SkeletonTableRow({ columns = 4 }: { columns?: number }) {\n return (\n <tr>\n {Array.from({ length: columns }).map((_, i) => (\n <td key={i} className=\"px-4 py-3\">\n <Skeleton className=\"h-4 w-full\" />\n </td>\n ))}\n </tr>\n )\n}\n","'use client'\n\nimport { cn } from '../../../lib/cn'\nimport { Skeleton } from '../Skeleton'\n\ninterface ToolbarButtonProps {\n onClick: () => void\n active?: boolean\n disabled?: boolean\n children: React.ReactNode\n title?: string\n}\n\nexport function ToolbarButton({ onClick, active, disabled, children, title }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n disabled={disabled}\n title={title}\n className={cn(\n 'px-2.5 py-1.5 text-sm font-medium rounded transition-colors',\n 'flex items-center justify-center',\n 'hover:bg-gray-100 dark:hover:bg-gray-800',\n 'disabled:opacity-50 disabled:cursor-not-allowed',\n active && 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white',\n !active && 'text-gray-600 dark:text-gray-400'\n )}\n >\n {children}\n </button>\n )\n}\n\nexport function Divider() {\n return <div className=\"w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1\" />\n}\n\n/** Skeleton placeholder for a toolbar button - matches ToolbarButton dimensions */\nexport function SkeletonButton() {\n return <Skeleton className=\"h-7 w-7 shrink-0 bg-gray-200 dark:bg-gray-800\" />\n}\n","'use client'\n\nimport { useState, useEffect, useCallback } from 'react'\nimport type { Editor } from '@tiptap/react'\n\n// Cast editor to any for StarterKit command support\ntype AnyEditor = Editor & { chain: () => any; isActive: (name: string, attrs?: any) => boolean }\nimport type { RefObject } from 'react'\nimport { Loader2, Wand2 } from 'lucide-react'\nimport { ToolbarButton, Divider, SkeletonButton } from './ToolbarButton'\nimport { setHeadingAtCursor, insertAtCursor } from '../../../lib/markdown-helpers'\n\ninterface FormatButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n aiGenerating?: boolean\n loading?: boolean\n apiBasePath?: string\n}\n\nexport function FormatButtons({ editor: editorProp, textareaRef, markdown, onMarkdownChange, aiGenerating, loading, apiBasePath = '/api/cms' }: FormatButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n const isMarkdownMode = !editor && textareaRef && markdown !== undefined && onMarkdownChange\n const [hasSelection, setHasSelection] = useState(false)\n const [isRewriting, setIsRewriting] = useState(false)\n\n // Track text selection in the editor\n useEffect(() => {\n if (!editor) {\n setHasSelection(false)\n return\n }\n\n const updateSelection = () => {\n const { from, to } = editor.state.selection\n setHasSelection(from !== to)\n }\n\n editor.on('selectionUpdate', updateSelection)\n editor.on('transaction', updateSelection)\n \n return () => {\n editor.off('selectionUpdate', updateSelection)\n editor.off('transaction', updateSelection)\n }\n }, [editor])\n\n // Handle rewrite selected text\n const handleRewrite = useCallback(async () => {\n if (!editor || isRewriting) return\n \n const { from, to } = editor.state.selection\n if (from === to) return\n\n const selectedText = editor.state.doc.textBetween(from, to, ' ')\n if (!selectedText.trim()) return\n\n setIsRewriting(true)\n try {\n const res = await fetch(`${apiBasePath}/ai/rewrite`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text: selectedText }),\n })\n\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Rewrite failed')\n }\n\n const result = await res.json()\n \n // Replace the selected text with the rewritten version\n editor.chain().focus().deleteSelection().insertContent(result.text).run()\n } catch (err) {\n console.error('Rewrite error:', err)\n alert(err instanceof Error ? err.message : 'Failed to rewrite text')\n } finally {\n setIsRewriting(false)\n }\n }, [editor, isRewriting, apiBasePath])\n\n const setHeading = (level: number) => {\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n setHeadingAtCursor(textareaRef.current, level, markdown, onMarkdownChange)\n }\n }\n\n const wrapSelection = (before: string, after: string) => {\n if (editor) return\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n insertAtCursor(textareaRef.current, before, after, markdown, onMarkdownChange)\n }\n }\n\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* H1, H2, H3 */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n <Divider />\n {/* Bold, Italic, Strikethrough */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n {/* AI Rewrite (shown in rich text mode) */}\n <Divider />\n <SkeletonButton />\n </>\n )\n }\n\n return (\n <>\n {/* Headings */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleHeading({ level: 1 }).run() : setHeading(1)}\n active={editor?.isActive('heading', { level: 1 })}\n disabled={aiGenerating}\n title=\"Heading 1\"\n >\n H1\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleHeading({ level: 2 }).run() : setHeading(2)}\n active={editor?.isActive('heading', { level: 2 })}\n disabled={aiGenerating}\n title=\"Heading 2\"\n >\n H2\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleHeading({ level: 3 }).run() : setHeading(3)}\n active={editor?.isActive('heading', { level: 3 })}\n disabled={aiGenerating}\n title=\"Heading 3\"\n >\n H3\n </ToolbarButton>\n\n <Divider />\n\n {/* Text formatting */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleBold().run() : wrapSelection('**', '**')}\n active={editor?.isActive('bold')}\n disabled={aiGenerating}\n title=\"Bold (⌘B)\"\n >\n <span className=\"font-bold\">B</span>\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleItalic().run() : wrapSelection('*', '*')}\n active={editor?.isActive('italic')}\n disabled={aiGenerating}\n title=\"Italic (⌘I)\"\n >\n <span className=\"italic\">I</span>\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleStrike().run() : wrapSelection('~~', '~~')}\n active={editor?.isActive('strike')}\n disabled={aiGenerating}\n title=\"Strikethrough\"\n >\n <span className=\"line-through\">S</span>\n </ToolbarButton>\n\n {/* AI Rewrite (only in rich text mode, but always visible when not in markdown mode) */}\n {!isMarkdownMode && (\n <>\n <Divider />\n <ToolbarButton\n onClick={handleRewrite}\n disabled={!editor || !hasSelection || isRewriting || aiGenerating}\n title={\n !editor \n ? 'Editor loading...' \n : hasSelection \n ? 'Rewrite selection with AI' \n : 'Select text to rewrite'\n }\n >\n {isRewriting ? (\n <Loader2 className=\"w-4 h-4 animate-spin\" />\n ) : (\n <Wand2 className=\"w-4 h-4\" />\n )}\n </ToolbarButton>\n </>\n )}\n </>\n )\n}\n","/**\n * Helper functions for markdown mode in the editor toolbar.\n * Handles cursor-based text manipulation in textareas.\n */\n\n/**\n * Insert text before and after the current selection in a textarea.\n * Used for inline formatting like bold (**), italic (*), code (`), etc.\n */\nexport function insertAtCursor(\n textarea: HTMLTextAreaElement,\n before: string,\n after: string,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n const selected = markdown.substring(start, end)\n const newText = markdown.substring(0, start) + before + selected + after + markdown.substring(end)\n onMarkdownChange(newText)\n \n // Restore focus and selection after React re-render\n requestAnimationFrame(() => {\n textarea.focus()\n const newCursorPos = start + before.length + selected.length\n textarea.setSelectionRange(newCursorPos, newCursorPos)\n })\n}\n\n/**\n * Insert or toggle a block prefix at the start of the current line.\n * Used for lists (- ), blockquotes (> ), etc.\n */\nexport function insertBlockAtCursor(\n textarea: HTMLTextAreaElement,\n prefix: string,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n \n // Find the start of the current line\n let lineStart = start\n while (lineStart > 0 && markdown[lineStart - 1] !== '\\n') {\n lineStart--\n }\n \n // Get current line content\n let lineEnd = end\n while (lineEnd < markdown.length && markdown[lineEnd] !== '\\n') {\n lineEnd++\n }\n const lineContent = markdown.substring(lineStart, lineEnd)\n \n // Check if already has prefix, toggle off\n if (lineContent.startsWith(prefix)) {\n const newText = markdown.substring(0, lineStart) + lineContent.substring(prefix.length) + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n } else {\n const newText = markdown.substring(0, lineStart) + prefix + lineContent + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n }\n \n requestAnimationFrame(() => {\n textarea.focus()\n })\n}\n\n/**\n * Set or toggle a heading level on the current line.\n * Replaces any existing heading prefix with the new level.\n */\nexport function setHeadingAtCursor(\n textarea: HTMLTextAreaElement,\n level: number,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n \n // Find the start of the current line\n let lineStart = start\n while (lineStart > 0 && markdown[lineStart - 1] !== '\\n') {\n lineStart--\n }\n \n // Get current line content\n let lineEnd = end\n while (lineEnd < markdown.length && markdown[lineEnd] !== '\\n') {\n lineEnd++\n }\n const lineContent = markdown.substring(lineStart, lineEnd)\n \n // Remove any existing heading prefix\n const withoutHeading = lineContent.replace(/^#{1,6}\\s*/, '')\n const newPrefix = '#'.repeat(level) + ' '\n \n // If the line already has this exact heading level, toggle it off\n const currentHeadingMatch = lineContent.match(/^(#{1,6})\\s/)\n if (currentHeadingMatch && currentHeadingMatch[1].length === level) {\n // Toggle off - remove heading\n const newText = markdown.substring(0, lineStart) + withoutHeading + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n } else {\n // Set new heading level\n const newText = markdown.substring(0, lineStart) + newPrefix + withoutHeading + markdown.substring(lineEnd)\n onMarkdownChange(newText)\n }\n \n requestAnimationFrame(() => {\n textarea.focus()\n })\n}\n\n/**\n * Strip common markdown syntax from selected text.\n * Removes bold, italic, strikethrough, code, and links.\n */\nexport function clearMarkdownFormatting(\n textarea: HTMLTextAreaElement,\n markdown: string,\n onMarkdownChange: (md: string) => void\n) {\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n \n if (start === end) return // No selection\n \n const selected = markdown.substring(start, end)\n const cleaned = selected\n .replace(/\\*\\*(.+?)\\*\\*/g, '$1') // bold\n .replace(/\\*(.+?)\\*/g, '$1') // italic\n .replace(/~~(.+?)~~/g, '$1') // strikethrough\n .replace(/`(.+?)`/g, '$1') // inline code\n .replace(/\\[(.+?)\\]\\(.+?\\)/g, '$1') // links\n \n const newText = markdown.substring(0, start) + cleaned + markdown.substring(end)\n onMarkdownChange(newText)\n \n requestAnimationFrame(() => {\n textarea.focus()\n textarea.setSelectionRange(start, start + cleaned.length)\n })\n}\n","'use client'\n\nimport type { Editor } from '@tiptap/react'\nimport type { RefObject } from 'react'\nimport { List, ListOrdered, Quote, Code2, Minus } from 'lucide-react'\nimport { ToolbarButton, SkeletonButton } from './ToolbarButton'\nimport { insertBlockAtCursor, insertAtCursor } from '../../../lib/markdown-helpers'\n\ninterface BlockButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n aiGenerating?: boolean\n loading?: boolean\n}\n\nexport function BlockButtons({ editor: editorProp, textareaRef, markdown, onMarkdownChange, aiGenerating, loading }: BlockButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* Bullet list, Numbered list */}\n <SkeletonButton />\n <SkeletonButton />\n {/* Blockquote, Code block, Horizontal rule */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n </>\n )\n }\n const isMarkdownMode = !editor && textareaRef && markdown !== undefined && onMarkdownChange\n\n const insertBlock = (prefix: string) => {\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n insertBlockAtCursor(textareaRef.current, prefix, markdown, onMarkdownChange)\n }\n }\n\n const wrapSelection = (before: string, after: string) => {\n if (editor) return\n if (textareaRef?.current && markdown !== undefined && onMarkdownChange) {\n insertAtCursor(textareaRef.current, before, after, markdown, onMarkdownChange)\n }\n }\n\n const handleHorizontalRule = () => {\n if (editor) {\n editor.chain().focus().setHorizontalRule().run()\n } else if (isMarkdownMode && textareaRef?.current) {\n const textarea = textareaRef.current\n const start = textarea.selectionStart\n const newText = markdown!.substring(0, start) + '\\n---\\n' + markdown!.substring(start)\n onMarkdownChange!(newText)\n requestAnimationFrame(() => textarea.focus())\n }\n }\n\n return (\n <>\n {/* Lists */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleBulletList().run() : insertBlock('- ')}\n active={editor?.isActive('bulletList')}\n disabled={aiGenerating}\n title=\"Bullet list\"\n >\n <List className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleOrderedList().run() : insertBlock('1. ')}\n active={editor?.isActive('orderedList')}\n disabled={aiGenerating}\n title=\"Numbered list\"\n >\n <ListOrdered className=\"w-4 h-4\" />\n </ToolbarButton>\n\n {/* Block elements */}\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleBlockquote().run() : insertBlock('> ')}\n active={editor?.isActive('blockquote')}\n disabled={aiGenerating}\n title=\"Blockquote\"\n >\n <Quote className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={() => editor ? editor.chain().focus().toggleCodeBlock().run() : wrapSelection('```\\n', '\\n```')}\n active={editor?.isActive('codeBlock')}\n disabled={aiGenerating}\n title=\"Code block\"\n >\n <Code2 className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton onClick={handleHorizontalRule} disabled={aiGenerating} title=\"Horizontal rule\">\n <Minus className=\"w-4 h-4\" />\n </ToolbarButton>\n </>\n )\n}\n","'use client'\n\nimport { useRef, useCallback } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport type { RefObject } from 'react'\nimport { Link2, Image, RemoveFormatting } from 'lucide-react'\nimport { ToolbarButton, SkeletonButton } from './ToolbarButton'\nimport { clearMarkdownFormatting } from '../../../lib/markdown-helpers'\n\ninterface MediaButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n markdown?: string\n onMarkdownChange?: (markdown: string) => void\n aiGenerating?: boolean\n loading?: boolean\n apiBasePath?: string\n}\n\nexport function MediaButtons({ editor: editorProp, textareaRef, markdown, onMarkdownChange, aiGenerating, loading, apiBasePath = '/api/cms' }: MediaButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n const fileInputRef = useRef<HTMLInputElement>(null)\n const isMarkdownMode = !editor && textareaRef && markdown !== undefined && onMarkdownChange\n\n const handleLinkClick = useCallback(() => {\n if (editor) {\n if (editor.isActive('link')) {\n editor.chain().focus().unsetLink().run()\n return\n }\n const url = window.prompt('Enter URL:')\n if (url) {\n editor.chain().focus().extendMarkRange('link').setLink({ href: url }).run()\n }\n } else if (isMarkdownMode) {\n const url = window.prompt('Enter URL:')\n if (url && textareaRef?.current) {\n const textarea = textareaRef.current\n const start = textarea.selectionStart\n const end = textarea.selectionEnd\n const selected = markdown!.substring(start, end) || 'link text'\n const newText = markdown!.substring(0, start) + `[${selected}](${url})` + markdown!.substring(end)\n onMarkdownChange!(newText)\n requestAnimationFrame(() => textarea.focus())\n }\n }\n }, [editor, isMarkdownMode, textareaRef, markdown, onMarkdownChange])\n\n const handleImageUpload = useCallback(async (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0]\n if (!file) return\n\n // Client-side validation\n const maxSize = 4 * 1024 * 1024 // 4MB\n if (file.size > maxSize) {\n alert(`Image too large (${(file.size / 1024 / 1024).toFixed(1)}MB). Maximum size is 4MB.`)\n e.target.value = ''\n return\n }\n\n const validTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']\n if (!validTypes.includes(file.type)) {\n alert(`Invalid file type: ${file.type}. Supported types: JPEG, PNG, GIF, WebP.`)\n e.target.value = ''\n return\n }\n\n const formData = new FormData()\n formData.append('image', file)\n\n try {\n const res = await fetch(`${apiBasePath}/upload`, {\n method: 'POST',\n body: formData,\n })\n \n const data = await res.json()\n \n if (!res.ok) {\n alert(data.error || 'Failed to upload image')\n e.target.value = ''\n return\n }\n\n // Handle both { url: ... } and { data: { url: ... } } response formats\n const url = data.url || data.data?.url\n if (url) {\n if (editor) {\n editor.chain().focus().setImage({ src: url }).run()\n } else if (isMarkdownMode && textareaRef?.current) {\n const textarea = textareaRef.current\n const start = textarea.selectionStart\n const newText = markdown!.substring(0, start) + `` + markdown!.substring(start)\n onMarkdownChange!(newText)\n requestAnimationFrame(() => textarea.focus())\n }\n } else {\n alert('Upload succeeded but no URL returned')\n }\n } catch (err) {\n alert('Failed to upload image. Please try again.')\n console.error('Image upload error:', err)\n }\n\n e.target.value = ''\n }, [editor, isMarkdownMode, textareaRef, markdown, onMarkdownChange, apiBasePath])\n\n const handleClearFormatting = useCallback(() => {\n if (editor) {\n editor.chain().focus().unsetAllMarks().clearNodes().run()\n } else if (isMarkdownMode && textareaRef?.current && markdown && onMarkdownChange) {\n clearMarkdownFormatting(textareaRef.current, markdown, onMarkdownChange)\n }\n }, [editor, isMarkdownMode, textareaRef, markdown, onMarkdownChange])\n\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* Link, Image, Clear formatting */}\n <SkeletonButton />\n <SkeletonButton />\n <SkeletonButton />\n </>\n )\n }\n\n return (\n <>\n <ToolbarButton\n onClick={handleLinkClick}\n active={editor?.isActive('link')}\n disabled={aiGenerating}\n title=\"Insert link\"\n >\n <Link2 className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton onClick={() => fileInputRef.current?.click()} disabled={aiGenerating} title=\"Insert image\">\n <Image className=\"w-4 h-4\" />\n </ToolbarButton>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleImageUpload}\n className=\"hidden\"\n />\n <ToolbarButton onClick={handleClearFormatting} disabled={aiGenerating} title=\"Clear formatting\">\n <RemoveFormatting className=\"w-4 h-4\" />\n </ToolbarButton>\n </>\n )\n}\n","'use client'\n\nimport { useCallback } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport type { RefObject } from 'react'\nimport { Undo2, Redo2 } from 'lucide-react'\nimport { ToolbarButton, Divider, SkeletonButton } from './ToolbarButton'\nimport { RevisionHistoryDropdown } from '../RevisionHistoryDropdown'\nimport type { RevisionState } from '../../../lib/editor-types'\n\ninterface HistoryButtonsProps {\n editor?: Editor | null\n textareaRef?: RefObject<HTMLTextAreaElement | null>\n showMarkdown?: boolean\n setShowMarkdown?: (show: boolean) => void\n postSlug?: string\n revisions?: RevisionState\n aiGenerating?: boolean\n loading?: boolean\n}\n\nexport function HistoryButtons({\n editor: editorProp,\n textareaRef,\n showMarkdown,\n setShowMarkdown,\n postSlug,\n revisions,\n aiGenerating,\n loading,\n}: HistoryButtonsProps) {\n // Cast editor to any to support StarterKit commands\n const editor = editorProp as any\n \n const handleUndo = useCallback(() => {\n if (editor) {\n editor.chain().focus().undo().run()\n } else if (textareaRef?.current) {\n textareaRef.current.focus()\n document.execCommand('undo')\n }\n }, [editor, textareaRef])\n\n const handleRedo = useCallback(() => {\n if (editor) {\n editor.chain().focus().redo().run()\n } else if (textareaRef?.current) {\n textareaRef.current.focus()\n document.execCommand('redo')\n }\n }, [editor, textareaRef])\n\n // Skeleton state - render placeholders matching actual button layout\n if (loading) {\n return (\n <>\n {/* Undo, Redo */}\n <SkeletonButton />\n <SkeletonButton />\n {/* MD toggle */}\n <Divider />\n <SkeletonButton />\n {/* Revision dropdown */}\n <Divider />\n <SkeletonButton />\n </>\n )\n }\n\n return (\n <>\n {/* Undo/Redo */}\n <ToolbarButton\n onClick={handleUndo}\n disabled={aiGenerating || (editor ? !editor.can().undo() : false)}\n title=\"Undo (⌘Z)\"\n >\n <Undo2 className=\"w-4 h-4\" />\n </ToolbarButton>\n <ToolbarButton\n onClick={handleRedo}\n disabled={aiGenerating || (editor ? !editor.can().redo() : false)}\n title=\"Redo (⌘⇧Z)\"\n >\n <Redo2 className=\"w-4 h-4\" />\n </ToolbarButton>\n\n {/* Markdown/Rich Text mode toggle */}\n {setShowMarkdown && (\n <>\n <Divider />\n <ToolbarButton\n onClick={() => setShowMarkdown(!showMarkdown)}\n active={showMarkdown}\n disabled={aiGenerating}\n title={showMarkdown ? 'Switch to rich text editor' : 'Switch to markdown mode'}\n >\n <span className=\"font-mono text-xs\">MD</span>\n </ToolbarButton>\n </>\n )}\n\n {/* Revision History */}\n {revisions && (\n <>\n <Divider />\n <RevisionHistoryDropdown\n revisions={revisions.list}\n loading={revisions.loading}\n previewLoading={revisions.previewLoading}\n disabled={aiGenerating || !postSlug}\n isPreviewMode={!!revisions.previewing}\n onOpen={revisions.fetch}\n onSelect={revisions.preview}\n />\n </>\n )}\n </>\n )\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { Loader2, History } from 'lucide-react'\nimport { formatRelativeTime } from '../../lib/format'\nimport type { RevisionSummary } from '../../lib/editor-types'\nimport { Dropdown, DropdownItem, DropdownLabel } from './Dropdown'\n\ninterface Props {\n revisions: RevisionSummary[]\n loading: boolean\n previewLoading: boolean\n disabled: boolean\n isPreviewMode: boolean\n onOpen: () => void\n onSelect: (id: string) => void\n}\n\nexport function RevisionHistoryDropdown({\n revisions,\n loading,\n previewLoading,\n disabled,\n isPreviewMode,\n onOpen,\n onSelect,\n}: Props) {\n const [open, setOpen] = useState(false)\n\n const handleOpenChange = (isOpen: boolean) => {\n if (isOpen && !open) {\n onOpen()\n }\n setOpen(isOpen)\n }\n\n const trigger = (\n <button\n type=\"button\"\n disabled={disabled || isPreviewMode || previewLoading}\n title={disabled ? 'Save post to enable history' : 'Revision history'}\n className=\"px-2.5 py-1.5 text-sm font-medium rounded transition-colors flex items-center justify-center hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed text-gray-600 dark:text-gray-400\"\n >\n {previewLoading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <History className=\"w-4 h-4\" />\n )}\n </button>\n )\n\n return (\n <Dropdown\n trigger={trigger}\n open={open}\n onOpenChange={handleOpenChange}\n disabled={disabled || isPreviewMode || previewLoading}\n align=\"right\"\n className=\"w-64 max-h-80 overflow-y-auto p-0\"\n >\n <DropdownLabel>Revision History</DropdownLabel>\n <div className=\"border-t border-border\" />\n \n {loading ? (\n <div className=\"flex items-center justify-center py-4\">\n <Loader2 className=\"h-4 w-4 animate-spin text-gray-500\" />\n </div>\n ) : revisions.length === 0 ? (\n <div className=\"py-4 text-center text-sm text-gray-500\">\n No revisions yet\n </div>\n ) : (\n revisions.map((rev) => (\n <DropdownItem key={rev.id} onClick={() => onSelect(rev.id)}>\n <div className=\"flex flex-col\">\n <span className=\"text-sm truncate\">{rev.title || 'Untitled'}</span>\n <span className=\"text-xs text-gray-500\">\n {formatRelativeTime(rev.createdAt)}\n </span>\n </div>\n </DropdownItem>\n ))\n )}\n </Dropdown>\n )\n}\n","'use client'\n\nimport { useState, useRef, useEffect, useCallback, createContext, useContext } from 'react'\nimport { createPortal } from 'react-dom'\nimport { cn } from '../../lib/cn'\n\n// ============================================\n// DROPDOWN CONTEXT\n// ============================================\n\nconst DropdownContext = createContext<{ close: () => void } | null>(null)\n\n// ============================================\n// DROPDOWN CONTAINER\n// ============================================\n\ninterface DropdownProps {\n /** The trigger element (button, avatar, etc.) */\n trigger: React.ReactNode\n /** Dropdown content */\n children: React.ReactNode\n /** Alignment relative to trigger */\n align?: 'left' | 'right'\n /** Custom width class */\n className?: string\n /** Controlled open state */\n open?: boolean\n /** Callback when open state changes */\n onOpenChange?: (open: boolean) => void\n /** Disable the dropdown */\n disabled?: boolean\n}\n\nexport function Dropdown({\n trigger,\n children,\n align = 'right',\n className,\n open: controlledOpen,\n onOpenChange,\n disabled,\n}: DropdownProps) {\n const [internalOpen, setInternalOpen] = useState(false)\n const [position, setPosition] = useState({ top: 0, left: 0, right: 0 })\n const [mounted, setMounted] = useState(false)\n \n const triggerRef = useRef<HTMLDivElement>(null)\n const menuRef = useRef<HTMLDivElement>(null)\n \n // Controlled vs uncontrolled\n const isControlled = controlledOpen !== undefined\n const isOpen = isControlled ? controlledOpen : internalOpen\n \n const setOpen = useCallback((value: boolean) => {\n if (!isControlled) {\n setInternalOpen(value)\n }\n onOpenChange?.(value)\n }, [isControlled, onOpenChange])\n\n // Mount check for portal\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Calculate position when opening\n const updatePosition = useCallback(() => {\n if (triggerRef.current) {\n const rect = triggerRef.current.getBoundingClientRect()\n setPosition({\n top: rect.bottom + 4, // 4px gap\n left: rect.left,\n right: window.innerWidth - rect.right,\n })\n }\n }, [])\n\n // Update position on open\n useEffect(() => {\n if (isOpen) {\n updatePosition()\n }\n }, [isOpen, updatePosition])\n\n // Close on click outside\n useEffect(() => {\n if (!isOpen) return\n \n const handleClick = (e: MouseEvent) => {\n const target = e.target as Node\n if (\n menuRef.current && !menuRef.current.contains(target) &&\n triggerRef.current && !triggerRef.current.contains(target)\n ) {\n setOpen(false)\n }\n }\n \n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [isOpen, setOpen])\n\n // Close on escape\n useEffect(() => {\n if (!isOpen) return\n \n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n setOpen(false)\n }\n }\n \n document.addEventListener('keydown', handleKeyDown)\n return () => document.removeEventListener('keydown', handleKeyDown)\n }, [isOpen, setOpen])\n\n // Update position on scroll/resize\n useEffect(() => {\n if (!isOpen) return\n \n const handleUpdate = () => updatePosition()\n window.addEventListener('scroll', handleUpdate, true)\n window.addEventListener('resize', handleUpdate)\n return () => {\n window.removeEventListener('scroll', handleUpdate, true)\n window.removeEventListener('resize', handleUpdate)\n }\n }, [isOpen, updatePosition])\n\n const handleTriggerClick = () => {\n if (disabled) return\n setOpen(!isOpen)\n }\n\n const close = useCallback(() => setOpen(false), [setOpen])\n\n const menu = isOpen && mounted ? createPortal(\n <DropdownContext.Provider value={{ close }}>\n <div\n ref={menuRef}\n style={{\n position: 'fixed',\n top: position.top,\n ...(align === 'right' \n ? { right: position.right } \n : { left: position.left }\n ),\n }}\n className={cn(\n 'z-50 min-w-[160px] bg-popover border border-border rounded-md shadow-lg p-1 overscroll-contain',\n className\n )}\n onWheel={(e) => e.stopPropagation()}\n >\n {children}\n </div>\n </DropdownContext.Provider>,\n document.body\n ) : null\n\n return (\n <>\n <div ref={triggerRef} onClick={handleTriggerClick}>\n {trigger}\n </div>\n {menu}\n </>\n )\n}\n\n// ============================================\n// DROPDOWN ITEM\n// ============================================\n\ninterface DropdownItemProps {\n onClick?: () => void\n destructive?: boolean\n disabled?: boolean\n children: React.ReactNode\n className?: string\n}\n\nexport function DropdownItem({\n onClick,\n destructive,\n disabled,\n children,\n className,\n}: DropdownItemProps) {\n const context = useContext(DropdownContext)\n \n const handleClick = () => {\n if (!disabled) {\n onClick?.()\n context?.close()\n }\n }\n\n return (\n <button\n type=\"button\"\n onClick={handleClick}\n disabled={disabled}\n className={cn(\n 'w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0',\n 'text-left text-sm rounded-sm cursor-default',\n 'hover:bg-accent focus:bg-accent focus:outline-none',\n destructive && 'text-destructive',\n disabled && 'opacity-50 cursor-not-allowed',\n className\n )}\n >\n {children}\n </button>\n )\n}\n\n// ============================================\n// DROPDOWN DIVIDER\n// ============================================\n\nexport function DropdownDivider() {\n return <div className=\"h-px bg-border my-1\" />\n}\n\n// ============================================\n// DROPDOWN LABEL (non-interactive header)\n// ============================================\n\nexport function DropdownLabel({ children }: { children: React.ReactNode }) {\n return (\n <div className=\"px-3 py-1.5 md:px-2 md:py-1 text-xs font-medium text-muted-foreground\">\n {children}\n </div>\n )\n}\n","'use client'\n\nimport { useEffect, useMemo, useCallback } from 'react'\nimport { useEditor, EditorContent, Editor } from '@tiptap/react'\nimport StarterKit from '@tiptap/starter-kit'\nimport Placeholder from '@tiptap/extension-placeholder'\nimport Link from '@tiptap/extension-link'\nimport Image from '@tiptap/extension-image'\nimport { renderMarkdown, htmlToMarkdown } from '../../lib/markdown'\nimport { CommentMark } from '../../lib/comment-mark'\n\nexport interface SelectionState {\n hasSelection: boolean\n text: string\n from: number\n to: number\n hasExistingComment: boolean\n}\n\ninterface TiptapEditorProps {\n content: string // markdown\n onChange: (markdown: string) => void\n placeholder?: string\n autoFocus?: boolean\n onEditorReady?: (editor: Editor) => void\n onSelectionChange?: (selection: SelectionState | null) => void\n onCommentClick?: (commentId: string) => void\n proseClasses?: string\n}\n\nconst DEFAULT_PROSE_CLASSES = 'prose prose-gray dark:prose-invert max-w-none'\n\nexport function TiptapEditor({ \n content, \n onChange, \n placeholder = 'Start writing...',\n autoFocus = false,\n onEditorReady,\n onSelectionChange,\n onCommentClick,\n proseClasses = DEFAULT_PROSE_CLASSES,\n}: TiptapEditorProps) {\n // Convert initial content to HTML once\n const initialHtml = useMemo(() => content ? renderMarkdown(content) : '', [content])\n\n // Memoize extensions\n const extensions = useMemo(() => [\n StarterKit.configure({\n heading: {\n levels: [1, 2, 3],\n },\n }),\n Placeholder.configure({\n placeholder,\n }),\n Link.configure({\n openOnClick: false,\n HTMLAttributes: {\n class: 'text-blue-600 dark:text-blue-400 underline',\n },\n }),\n Image.configure({\n HTMLAttributes: {\n class: 'rounded-lg max-w-full',\n },\n }),\n CommentMark.configure({\n onCommentClick,\n }),\n ], [placeholder, onCommentClick])\n\n const editor = useEditor({\n immediatelyRender: false, // Prevent SSR hydration mismatch\n extensions,\n content: initialHtml,\n onUpdate: ({ editor }) => {\n const html = editor.getHTML()\n const markdown = htmlToMarkdown(html)\n onChange(markdown)\n },\n onSelectionUpdate: ({ editor }) => {\n if (!onSelectionChange) return\n \n const { from, to, empty } = editor.state.selection\n if (empty) {\n onSelectionChange(null)\n } else {\n const text = editor.state.doc.textBetween(from, to, ' ')\n \n // Check if selection contains any comment marks\n let hasExistingComment = false\n editor.state.doc.nodesBetween(from, to, (node) => {\n if (node.marks.some(mark => mark.type.name === 'comment')) {\n hasExistingComment = true\n return false // stop iteration\n }\n })\n \n onSelectionChange({\n hasSelection: true,\n text,\n from,\n to,\n hasExistingComment,\n })\n }\n },\n editorProps: {\n attributes: {\n class: `${proseClasses} min-h-[500px] outline-none`,\n },\n },\n })\n\n // Notify parent when editor is ready and auto-focus if requested\n useEffect(() => {\n if (editor) {\n if (onEditorReady) {\n onEditorReady(editor)\n }\n if (autoFocus) {\n // Small delay to ensure DOM is ready\n setTimeout(() => editor.commands.focus(), 0)\n }\n }\n }, [editor, onEditorReady, autoFocus])\n\n // Sync external content changes (e.g., loading saved post)\n useEffect(() => {\n if (editor && content !== undefined) {\n const currentMarkdown = htmlToMarkdown(editor.getHTML())\n // Only update if content is actually different to avoid cursor jumping\n if (currentMarkdown !== content) {\n const html = content ? renderMarkdown(content) : ''\n editor.commands.setContent(html, { emitUpdate: false })\n }\n }\n }, [editor, content])\n\n return <EditorContent editor={editor} />\n}\n","import { marked } from 'marked'\nimport TurndownService from 'turndown'\n\n// Configure marked for consistent rendering\nmarked.setOptions({\n gfm: true,\n breaks: false,\n})\n\n/**\n * Render markdown to HTML\n */\nexport function renderMarkdown(markdown: string): string {\n return marked.parse(markdown) as string\n}\n\n/**\n * Convert markdown to HTML with GFM and line breaks enabled.\n * Safe for client-side use in components like ChatPanel.\n */\nexport function markdownToHtml(markdown: string): string {\n return marked.parse(markdown, { gfm: true, breaks: true }) as string\n}\n\n/**\n * Parse markdown to tokens (AST)\n */\nexport function parseMarkdown(markdown: string) {\n return marked.lexer(markdown)\n}\n\n// Configure Turndown for HTML to markdown conversion\nconst turndownService = new TurndownService({\n headingStyle: 'atx',\n codeBlockStyle: 'fenced',\n bulletListMarker: '-',\n})\n\n/**\n * Convert HTML to markdown\n */\nexport function htmlToMarkdown(html: string): string {\n return turndownService.turndown(html)\n}\n","import type { EditorState, Transaction } from '@tiptap/pm/state'\n\n/**\n * Takes a Transaction & Editor State and turns it into a chainable state object\n * @param config The transaction and state to create the chainable state from\n * @returns A chainable Editor state object\n */\nexport function createChainableState(config: { transaction: Transaction; state: EditorState }): EditorState {\n const { state, transaction } = config\n let { selection } = transaction\n let { doc } = transaction\n let { storedMarks } = transaction\n\n return {\n ...state,\n apply: state.apply.bind(state),\n applyTransaction: state.applyTransaction.bind(state),\n plugins: state.plugins,\n schema: state.schema,\n reconfigure: state.reconfigure.bind(state),\n toJSON: state.toJSON.bind(state),\n get storedMarks() {\n return storedMarks\n },\n get selection() {\n return selection\n },\n get doc() {\n return doc\n },\n get tr() {\n selection = transaction.selection\n doc = transaction.doc\n storedMarks = transaction.storedMarks\n\n return transaction\n },\n }\n}\n","import type { EditorState, Transaction } from '@tiptap/pm/state'\n\nimport type { Editor } from './Editor.js'\nimport { createChainableState } from './helpers/createChainableState.js'\nimport type { AnyCommands, CanCommands, ChainedCommands, CommandProps, SingleCommands } from './types.js'\n\nexport class CommandManager {\n editor: Editor\n\n rawCommands: AnyCommands\n\n customState?: EditorState\n\n constructor(props: { editor: Editor; state?: EditorState }) {\n this.editor = props.editor\n this.rawCommands = this.editor.extensionManager.commands\n this.customState = props.state\n }\n\n get hasCustomState(): boolean {\n return !!this.customState\n }\n\n get state(): EditorState {\n return this.customState || this.editor.state\n }\n\n get commands(): SingleCommands {\n const { rawCommands, editor, state } = this\n const { view } = editor\n const { tr } = state\n const props = this.buildProps(tr)\n\n return Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n const method = (...args: any[]) => {\n const callback = command(...args)(props)\n\n if (!tr.getMeta('preventDispatch') && !this.hasCustomState) {\n view.dispatch(tr)\n }\n\n return callback\n }\n\n return [name, method]\n }),\n ) as unknown as SingleCommands\n }\n\n get chain(): () => ChainedCommands {\n return () => this.createChain()\n }\n\n get can(): () => CanCommands {\n return () => this.createCan()\n }\n\n public createChain(startTr?: Transaction, shouldDispatch = true): ChainedCommands {\n const { rawCommands, editor, state } = this\n const { view } = editor\n const callbacks: boolean[] = []\n const hasStartTransaction = !!startTr\n const tr = startTr || state.tr\n\n const run = () => {\n if (!hasStartTransaction && shouldDispatch && !tr.getMeta('preventDispatch') && !this.hasCustomState) {\n view.dispatch(tr)\n }\n\n return callbacks.every(callback => callback === true)\n }\n\n const chain = {\n ...Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n const chainedCommand = (...args: never[]) => {\n const props = this.buildProps(tr, shouldDispatch)\n const callback = command(...args)(props)\n\n callbacks.push(callback)\n\n return chain\n }\n\n return [name, chainedCommand]\n }),\n ),\n run,\n } as unknown as ChainedCommands\n\n return chain\n }\n\n public createCan(startTr?: Transaction): CanCommands {\n const { rawCommands, state } = this\n const dispatch = false\n const tr = startTr || state.tr\n const props = this.buildProps(tr, dispatch)\n const formattedCommands = Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n return [name, (...args: never[]) => command(...args)({ ...props, dispatch: undefined })]\n }),\n ) as unknown as SingleCommands\n\n return {\n ...formattedCommands,\n chain: () => this.createChain(tr, dispatch),\n } as CanCommands\n }\n\n public buildProps(tr: Transaction, shouldDispatch = true): CommandProps {\n const { rawCommands, editor, state } = this\n const { view } = editor\n\n const props: CommandProps = {\n tr,\n editor,\n view,\n state: createChainableState({\n state,\n transaction: tr,\n }),\n dispatch: shouldDispatch ? () => undefined : undefined,\n chain: () => this.createChain(tr, shouldDispatch),\n can: () => this.createCan(tr),\n get commands() {\n return Object.fromEntries(\n Object.entries(rawCommands).map(([name, command]) => {\n return [name, (...args: never[]) => command(...args)(props)]\n }),\n ) as unknown as SingleCommands\n },\n }\n\n return props\n }\n}\n","export * from './blur.js'\nexport * from './clearContent.js'\nexport * from './clearNodes.js'\nexport * from './command.js'\nexport * from './createParagraphNear.js'\nexport * from './cut.js'\nexport * from './deleteCurrentNode.js'\nexport * from './deleteNode.js'\nexport * from './deleteRange.js'\nexport * from './deleteSelection.js'\nexport * from './enter.js'\nexport * from './exitCode.js'\nexport * from './extendMarkRange.js'\nexport * from './first.js'\nexport * from './focus.js'\nexport * from './forEach.js'\nexport * from './insertContent.js'\nexport * from './insertContentAt.js'\nexport * from './join.js'\nexport * from './joinItemBackward.js'\nexport * from './joinItemForward.js'\nexport * from './joinTextblockBackward.js'\nexport * from './joinTextblockForward.js'\nexport * from './keyboardShortcut.js'\nexport * from './lift.js'\nexport * from './liftEmptyBlock.js'\nexport * from './liftListItem.js'\nexport * from './newlineInCode.js'\nexport * from './resetAttributes.js'\nexport * from './scrollIntoView.js'\nexport * from './selectAll.js'\nexport * from './selectNodeBackward.js'\nexport * from './selectNodeForward.js'\nexport * from './selectParentNode.js'\nexport * from './selectTextblockEnd.js'\nexport * from './selectTextblockStart.js'\nexport * from './setContent.js'\nexport * from './setMark.js'\nexport * from './setMeta.js'\nexport * from './setNode.js'\nexport * from './setNodeSelection.js'\nexport * from './setTextDirection.js'\nexport * from './setTextSelection.js'\nexport * from './sinkListItem.js'\nexport * from './splitBlock.js'\nexport * from './splitListItem.js'\nexport * from './toggleList.js'\nexport * from './toggleMark.js'\nexport * from './toggleNode.js'\nexport * from './toggleWrap.js'\nexport * from './undoInputRule.js'\nexport * from './unsetAllMarks.js'\nexport * from './unsetMark.js'\nexport * from './unsetTextDirection.js'\nexport * from './updateAttributes.js'\nexport * from './wrapIn.js'\nexport * from './wrapInList.js'\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n blur: {\n /**\n * Removes focus from the editor.\n * @example editor.commands.blur()\n */\n blur: () => ReturnType\n }\n }\n}\n\nexport const blur: RawCommands['blur'] =\n () =>\n ({ editor, view }) => {\n requestAnimationFrame(() => {\n if (!editor.isDestroyed) {\n ;(view.dom as HTMLElement).blur()\n\n // Browsers should remove the caret on blur but safari does not.\n // See: https://github.com/ueberdosis/tiptap/issues/2405\n window?.getSelection()?.removeAllRanges()\n }\n })\n\n return true\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n clearContent: {\n /**\n * Clear the whole document.\n * @example editor.commands.clearContent()\n */\n clearContent: (\n /**\n * Whether to emit an update event.\n * @default true\n */\n emitUpdate?: boolean,\n ) => ReturnType\n }\n }\n}\n\nexport const clearContent: RawCommands['clearContent'] =\n (emitUpdate = true) =>\n ({ commands }) => {\n return commands.setContent('', { emitUpdate })\n }\n","import { liftTarget } from '@tiptap/pm/transform'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n clearNodes: {\n /**\n * Normalize nodes to a simple paragraph.\n * @example editor.commands.clearNodes()\n */\n clearNodes: () => ReturnType\n }\n }\n}\n\nexport const clearNodes: RawCommands['clearNodes'] =\n () =>\n ({ state, tr, dispatch }) => {\n const { selection } = tr\n const { ranges } = selection\n\n if (!dispatch) {\n return true\n }\n\n ranges.forEach(({ $from, $to }) => {\n state.doc.nodesBetween($from.pos, $to.pos, (node, pos) => {\n if (node.type.isText) {\n return\n }\n\n const { doc, mapping } = tr\n const $mappedFrom = doc.resolve(mapping.map(pos))\n const $mappedTo = doc.resolve(mapping.map(pos + node.nodeSize))\n const nodeRange = $mappedFrom.blockRange($mappedTo)\n\n if (!nodeRange) {\n return\n }\n\n const targetLiftDepth = liftTarget(nodeRange)\n\n if (node.type.isTextblock) {\n const { defaultType } = $mappedFrom.parent.contentMatchAt($mappedFrom.index())\n\n tr.setNodeMarkup(nodeRange.start, defaultType)\n }\n\n if (targetLiftDepth || targetLiftDepth === 0) {\n tr.lift(nodeRange, targetLiftDepth)\n }\n })\n })\n\n return true\n }\n","import type { Command, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n command: {\n /**\n * Define a command inline.\n * @param fn The command function.\n * @example\n * editor.commands.command(({ tr, state }) => {\n * ...\n * return true\n * })\n */\n command: (fn: (props: Parameters<Command>[0]) => boolean) => ReturnType\n }\n }\n}\n\nexport const command: RawCommands['command'] = fn => props => {\n return fn(props)\n}\n","import { createParagraphNear as originalCreateParagraphNear } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n createParagraphNear: {\n /**\n * Create a paragraph nearby.\n * @example editor.commands.createParagraphNear()\n */\n createParagraphNear: () => ReturnType\n }\n }\n}\n\nexport const createParagraphNear: RawCommands['createParagraphNear'] =\n () =>\n ({ state, dispatch }) => {\n return originalCreateParagraphNear(state, dispatch)\n }\n","import { TextSelection } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n cut: {\n /**\n * Cuts content from a range and inserts it at a given position.\n * @param range The range to cut.\n * @param range.from The start position of the range.\n * @param range.to The end position of the range.\n * @param targetPos The position to insert the content at.\n * @example editor.commands.cut({ from: 1, to: 3 }, 5)\n */\n cut: ({ from, to }: { from: number; to: number }, targetPos: number) => ReturnType\n }\n }\n}\n\nexport const cut: RawCommands['cut'] =\n (originRange, targetPos) =>\n ({ editor, tr }) => {\n const { state } = editor\n\n const contentSlice = state.doc.slice(originRange.from, originRange.to)\n\n tr.deleteRange(originRange.from, originRange.to)\n const newPos = tr.mapping.map(targetPos)\n\n tr.insert(newPos, contentSlice.content)\n\n tr.setSelection(new TextSelection(tr.doc.resolve(Math.max(newPos - 1, 0))))\n\n return true\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteCurrentNode: {\n /**\n * Delete the node that currently has the selection anchor.\n * @example editor.commands.deleteCurrentNode()\n */\n deleteCurrentNode: () => ReturnType\n }\n }\n}\n\nexport const deleteCurrentNode: RawCommands['deleteCurrentNode'] =\n () =>\n ({ tr, dispatch }) => {\n const { selection } = tr\n const currentNode = selection.$anchor.node()\n\n // if there is content inside the current node, break out of this command\n if (currentNode.content.size > 0) {\n return false\n }\n\n const $pos = tr.selection.$anchor\n\n for (let depth = $pos.depth; depth > 0; depth -= 1) {\n const node = $pos.node(depth)\n\n if (node.type === currentNode.type) {\n if (dispatch) {\n const from = $pos.before(depth)\n const to = $pos.after(depth)\n\n tr.delete(from, to).scrollIntoView()\n }\n\n return true\n }\n }\n\n return false\n }\n","import type { NodeType, Schema } from '@tiptap/pm/model'\n\nexport function getNodeType(nameOrType: string | NodeType, schema: Schema): NodeType {\n if (typeof nameOrType === 'string') {\n if (!schema.nodes[nameOrType]) {\n throw Error(`There is no node type named '${nameOrType}'. Maybe you forgot to add the extension?`)\n }\n\n return schema.nodes[nameOrType]\n }\n\n return nameOrType\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteNode: {\n /**\n * Delete a node with a given type or name.\n * @param typeOrName The type or name of the node.\n * @example editor.commands.deleteNode('paragraph')\n */\n deleteNode: (typeOrName: string | NodeType) => ReturnType\n }\n }\n}\n\nexport const deleteNode: RawCommands['deleteNode'] =\n typeOrName =>\n ({ tr, state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n const $pos = tr.selection.$anchor\n\n for (let depth = $pos.depth; depth > 0; depth -= 1) {\n const node = $pos.node(depth)\n\n if (node.type === type) {\n if (dispatch) {\n const from = $pos.before(depth)\n const to = $pos.after(depth)\n\n tr.delete(from, to).scrollIntoView()\n }\n\n return true\n }\n }\n\n return false\n }\n","import type { Range, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteRange: {\n /**\n * Delete a given range.\n * @param range The range to delete.\n * @example editor.commands.deleteRange({ from: 1, to: 3 })\n */\n deleteRange: (range: Range) => ReturnType\n }\n }\n}\n\nexport const deleteRange: RawCommands['deleteRange'] =\n range =>\n ({ tr, dispatch }) => {\n const { from, to } = range\n\n if (dispatch) {\n tr.delete(from, to)\n }\n\n return true\n }\n","import { deleteSelection as originalDeleteSelection } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n deleteSelection: {\n /**\n * Delete the selection, if there is one.\n * @example editor.commands.deleteSelection()\n */\n deleteSelection: () => ReturnType\n }\n }\n}\n\nexport const deleteSelection: RawCommands['deleteSelection'] =\n () =>\n ({ state, dispatch }) => {\n return originalDeleteSelection(state, dispatch)\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n enter: {\n /**\n * Trigger enter.\n * @example editor.commands.enter()\n */\n enter: () => ReturnType\n }\n }\n}\n\nexport const enter: RawCommands['enter'] =\n () =>\n ({ commands }) => {\n return commands.keyboardShortcut('Enter')\n }\n","import { exitCode as originalExitCode } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n exitCode: {\n /**\n * Exit from a code block.\n * @example editor.commands.exitCode()\n */\n exitCode: () => ReturnType\n }\n }\n}\n\nexport const exitCode: RawCommands['exitCode'] =\n () =>\n ({ state, dispatch }) => {\n return originalExitCode(state, dispatch)\n }\n","import type { MarkType } from '@tiptap/pm/model'\nimport { TextSelection } from '@tiptap/pm/state'\n\nimport { getMarkRange } from '../helpers/getMarkRange.js'\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n extendMarkRange: {\n /**\n * Extends the text selection to the current mark by type or name.\n * @param typeOrName The type or name of the mark.\n * @param attributes The attributes of the mark.\n * @example editor.commands.extendMarkRange('bold')\n * @example editor.commands.extendMarkRange('mention', { userId: \"1\" })\n */\n extendMarkRange: (\n /**\n * The type or name of the mark.\n */\n typeOrName: string | MarkType,\n\n /**\n * The attributes of the mark.\n */\n attributes?: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const extendMarkRange: RawCommands['extendMarkRange'] =\n (typeOrName, attributes = {}) =>\n ({ tr, state, dispatch }) => {\n const type = getMarkType(typeOrName, state.schema)\n const { doc, selection } = tr\n const { $from, from, to } = selection\n\n if (dispatch) {\n const range = getMarkRange($from, type, attributes)\n\n if (range && range.from <= from && range.to >= to) {\n const newSelection = TextSelection.create(doc, range.from, range.to)\n\n tr.setSelection(newSelection)\n }\n }\n\n return true\n }\n","export function isRegExp(value: any): value is RegExp {\n return Object.prototype.toString.call(value) === '[object RegExp]'\n}\n","import { isRegExp } from './isRegExp.js'\n\n/**\n * Check if object1 includes object2\n * @param object1 Object\n * @param object2 Object\n */\nexport function objectIncludes(\n object1: Record<string, any>,\n object2: Record<string, any>,\n options: { strict: boolean } = { strict: true },\n): boolean {\n const keys = Object.keys(object2)\n\n if (!keys.length) {\n return true\n }\n\n return keys.every(key => {\n if (options.strict) {\n return object2[key] === object1[key]\n }\n\n if (isRegExp(object2[key])) {\n return object2[key].test(object1[key])\n }\n\n return object2[key] === object1[key]\n })\n}\n","import type { Mark as ProseMirrorMark, MarkType, ResolvedPos } from '@tiptap/pm/model'\n\nimport type { Range } from '../types.js'\nimport { objectIncludes } from '../utilities/objectIncludes.js'\n\nfunction findMarkInSet(\n marks: ProseMirrorMark[],\n type: MarkType,\n attributes: Record<string, any> = {},\n): ProseMirrorMark | undefined {\n return marks.find(item => {\n return (\n item.type === type &&\n objectIncludes(\n // Only check equality for the attributes that are provided\n Object.fromEntries(Object.keys(attributes).map(k => [k, item.attrs[k]])),\n attributes,\n )\n )\n })\n}\n\nfunction isMarkInSet(marks: ProseMirrorMark[], type: MarkType, attributes: Record<string, any> = {}): boolean {\n return !!findMarkInSet(marks, type, attributes)\n}\n\n/**\n * Get the range of a mark at a resolved position.\n */\nexport function getMarkRange(\n /**\n * The position to get the mark range for.\n */\n $pos: ResolvedPos,\n /**\n * The mark type to get the range for.\n */\n type: MarkType,\n /**\n * The attributes to match against.\n * If not provided, only the first mark at the position will be matched.\n */\n attributes?: Record<string, any>,\n): Range | void {\n if (!$pos || !type) {\n return\n }\n let start = $pos.parent.childAfter($pos.parentOffset)\n\n // If the cursor is at the start of a text node that does not have the mark, look backward\n if (!start.node || !start.node.marks.some(mark => mark.type === type)) {\n start = $pos.parent.childBefore($pos.parentOffset)\n }\n\n // If there is no text node with the mark even backward, return undefined\n if (!start.node || !start.node.marks.some(mark => mark.type === type)) {\n return\n }\n\n // Default to only matching against the first mark's attributes\n attributes = attributes || start.node.marks[0]?.attrs\n\n // We now know that the cursor is either at the start, middle or end of a text node with the specified mark\n // so we can look it up on the targeted mark\n const mark = findMarkInSet([...start.node.marks], type, attributes)\n\n if (!mark) {\n return\n }\n\n let startIndex = start.index\n let startPos = $pos.start() + start.offset\n let endIndex = startIndex + 1\n let endPos = startPos + start.node.nodeSize\n\n while (startIndex > 0 && isMarkInSet([...$pos.parent.child(startIndex - 1).marks], type, attributes)) {\n startIndex -= 1\n startPos -= $pos.parent.child(startIndex).nodeSize\n }\n\n while (endIndex < $pos.parent.childCount && isMarkInSet([...$pos.parent.child(endIndex).marks], type, attributes)) {\n endPos += $pos.parent.child(endIndex).nodeSize\n endIndex += 1\n }\n\n return {\n from: startPos,\n to: endPos,\n }\n}\n","import type { MarkType, Schema } from '@tiptap/pm/model'\n\nexport function getMarkType(nameOrType: string | MarkType, schema: Schema): MarkType {\n if (typeof nameOrType === 'string') {\n if (!schema.marks[nameOrType]) {\n throw Error(`There is no mark type named '${nameOrType}'. Maybe you forgot to add the extension?`)\n }\n\n return schema.marks[nameOrType]\n }\n\n return nameOrType\n}\n","import type { Command, CommandProps, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n first: {\n /**\n * Runs one command after the other and stops at the first which returns true.\n * @param commands The commands to run.\n * @example editor.commands.first([command1, command2])\n */\n first: (commands: Command[] | ((props: CommandProps) => Command[])) => ReturnType\n }\n }\n}\n\nexport const first: RawCommands['first'] = commands => props => {\n const items = typeof commands === 'function' ? commands(props) : commands\n\n for (let i = 0; i < items.length; i += 1) {\n if (items[i](props)) {\n return true\n }\n }\n\n return false\n}\n","import { TextSelection } from '@tiptap/pm/state'\n\nexport function isTextSelection(value: unknown): value is TextSelection {\n return value instanceof TextSelection\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Selection, TextSelection } from '@tiptap/pm/state'\n\nimport type { FocusPosition } from '../types.js'\nimport { minMax } from '../utilities/minMax.js'\n\nexport function resolveFocusPosition(doc: ProseMirrorNode, position: FocusPosition = null): Selection | null {\n if (!position) {\n return null\n }\n\n const selectionAtStart = Selection.atStart(doc)\n const selectionAtEnd = Selection.atEnd(doc)\n\n if (position === 'start' || position === true) {\n return selectionAtStart\n }\n\n if (position === 'end') {\n return selectionAtEnd\n }\n\n const minPos = selectionAtStart.from\n const maxPos = selectionAtEnd.to\n\n if (position === 'all') {\n return TextSelection.create(doc, minMax(0, minPos, maxPos), minMax(doc.content.size, minPos, maxPos))\n }\n\n return TextSelection.create(doc, minMax(position, minPos, maxPos), minMax(position, minPos, maxPos))\n}\n","export function minMax(value = 0, min = 0, max = 0): number {\n return Math.min(Math.max(value, min), max)\n}\n","export function isAndroid(): boolean {\n return navigator.platform === 'Android' || /android/i.test(navigator.userAgent)\n}\n","export function isiOS(): boolean {\n return (\n ['iPad Simulator', 'iPhone Simulator', 'iPod Simulator', 'iPad', 'iPhone', 'iPod'].includes(navigator.platform) ||\n // iPad on iOS 13 detection\n (navigator.userAgent.includes('Mac') && 'ontouchend' in document)\n )\n}\n","/**\n * Detects if the current browser is Safari (but not iOS Safari or Chrome).\n * @returns `true` if the browser is Safari, `false` otherwise.\n * @example\n * if (isSafari()) {\n * // Safari-specific handling\n * }\n */\nexport function isSafari(): boolean {\n return typeof navigator !== 'undefined' ? /^((?!chrome|android).)*safari/i.test(navigator.userAgent) : false\n}\n","import { isTextSelection } from '../helpers/isTextSelection.js'\nimport { resolveFocusPosition } from '../helpers/resolveFocusPosition.js'\nimport type { FocusPosition, RawCommands } from '../types.js'\nimport { isAndroid } from '../utilities/isAndroid.js'\nimport { isiOS } from '../utilities/isiOS.js'\nimport { isSafari } from '../utilities/isSafari.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n focus: {\n /**\n * Focus the editor at the given position.\n * @param position The position to focus at.\n * @param options.scrollIntoView Scroll the focused position into view after focusing\n * @example editor.commands.focus()\n * @example editor.commands.focus(32, { scrollIntoView: false })\n */\n focus: (\n /**\n * The position to focus at.\n */\n position?: FocusPosition,\n\n /**\n * Optional options\n * @default { scrollIntoView: true }\n */\n options?: {\n scrollIntoView?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\nexport const focus: RawCommands['focus'] =\n (position = null, options = {}) =>\n ({ editor, view, tr, dispatch }) => {\n options = {\n scrollIntoView: true,\n ...options,\n }\n\n const delayedFocus = () => {\n // focus within `requestAnimationFrame` breaks focus on iOS and Android\n // so we have to call this\n if (isiOS() || isAndroid()) {\n ;(view.dom as HTMLElement).focus()\n }\n\n // Safari requires preventScroll to avoid the browser scrolling to the\n // top of the editor when focus is called before the selection is set.\n // We exclude iOS and Android since they are already handled above.\n // see: https://github.com/ueberdosis/tiptap/issues/7318\n if (isSafari() && !isiOS() && !isAndroid()) {\n ;(view.dom as HTMLElement).focus({ preventScroll: true })\n }\n\n // For React we have to focus asynchronously. Otherwise wild things happen.\n // see: https://github.com/ueberdosis/tiptap/issues/1520\n requestAnimationFrame(() => {\n if (!editor.isDestroyed) {\n view.focus()\n\n if (options?.scrollIntoView) {\n editor.commands.scrollIntoView()\n }\n }\n })\n }\n\n if ((view.hasFocus() && position === null) || position === false) {\n return true\n }\n\n // we don’t try to resolve a NodeSelection or CellSelection\n if (dispatch && position === null && !isTextSelection(editor.state.selection)) {\n delayedFocus()\n return true\n }\n\n // pass through tr.doc instead of editor.state.doc\n // since transactions could change the editors state before this command has been run\n const selection = resolveFocusPosition(tr.doc, position) || editor.state.selection\n const isSameSelection = editor.state.selection.eq(selection)\n\n if (dispatch) {\n if (!isSameSelection) {\n tr.setSelection(selection)\n }\n\n // `tr.setSelection` resets the stored marks\n // so we’ll restore them if the selection is the same as before\n if (isSameSelection && tr.storedMarks) {\n tr.setStoredMarks(tr.storedMarks)\n }\n\n delayedFocus()\n }\n\n return true\n }\n","import type { CommandProps, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n forEach: {\n /**\n * Loop through an array of items.\n */\n forEach: <T>(\n items: T[],\n fn: (\n item: T,\n props: CommandProps & {\n index: number\n },\n ) => boolean,\n ) => ReturnType\n }\n }\n}\n\nexport const forEach: RawCommands['forEach'] = (items, fn) => props => {\n return items.every((item, index) => fn(item, { ...props, index }))\n}\n","import type { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'\n\nimport type { Content, RawCommands } from '../types.js'\n\nexport interface InsertContentOptions {\n /**\n * Options for parsing the content.\n */\n parseOptions?: ParseOptions\n\n /**\n * Whether to update the selection after inserting the content.\n */\n updateSelection?: boolean\n applyInputRules?: boolean\n applyPasteRules?: boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n insertContent: {\n /**\n * Insert a node or string of HTML at the current position.\n * @example editor.commands.insertContent('<h1>Example</h1>')\n * @example editor.commands.insertContent('<h1>Example</h1>', { updateSelection: false })\n */\n insertContent: (\n /**\n * The ProseMirror content to insert.\n */\n value: Content | ProseMirrorNode | Fragment,\n\n /**\n * Optional options\n */\n options?: InsertContentOptions,\n ) => ReturnType\n }\n }\n}\n\nexport const insertContent: RawCommands['insertContent'] =\n (value, options) =>\n ({ tr, commands }) => {\n return commands.insertContentAt({ from: tr.selection.from, to: tr.selection.to }, value, options)\n }\n","import type { Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'\nimport { Fragment } from '@tiptap/pm/model'\n\nimport { createNodeFromContent } from '../helpers/createNodeFromContent.js'\nimport { selectionToInsertionEnd } from '../helpers/selectionToInsertionEnd.js'\nimport type { Content, Range, RawCommands } from '../types.js'\n\nexport interface InsertContentAtOptions {\n /**\n * Options for parsing the content.\n */\n parseOptions?: ParseOptions\n\n /**\n * Whether to update the selection after inserting the content.\n */\n updateSelection?: boolean\n\n /**\n * Whether to apply input rules after inserting the content.\n */\n applyInputRules?: boolean\n\n /**\n * Whether to apply paste rules after inserting the content.\n */\n applyPasteRules?: boolean\n\n /**\n * Whether to throw an error if the content is invalid.\n */\n errorOnInvalidContent?: boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n insertContentAt: {\n /**\n * Insert a node or string of HTML at a specific position.\n * @example editor.commands.insertContentAt(0, '<h1>Example</h1>')\n */\n insertContentAt: (\n /**\n * The position to insert the content at.\n */\n position: number | Range,\n\n /**\n * The ProseMirror content to insert.\n */\n value: Content | ProseMirrorNode | Fragment,\n\n /**\n * Optional options\n */\n options?: InsertContentAtOptions,\n ) => ReturnType\n }\n }\n}\n\nconst isFragment = (nodeOrFragment: ProseMirrorNode | Fragment): nodeOrFragment is Fragment => {\n return !('type' in nodeOrFragment)\n}\n\nexport const insertContentAt: RawCommands['insertContentAt'] =\n (position, value, options) =>\n ({ tr, dispatch, editor }) => {\n if (dispatch) {\n options = {\n parseOptions: editor.options.parseOptions,\n updateSelection: true,\n applyInputRules: false,\n applyPasteRules: false,\n ...options,\n }\n\n let content: Fragment | ProseMirrorNode\n\n const emitContentError = (error: Error) => {\n editor.emit('contentError', {\n editor,\n error,\n disableCollaboration: () => {\n if (\n 'collaboration' in editor.storage &&\n typeof editor.storage.collaboration === 'object' &&\n editor.storage.collaboration\n ) {\n ;(editor.storage.collaboration as any).isDisabled = true\n }\n },\n })\n }\n\n const parseOptions: ParseOptions = {\n preserveWhitespace: 'full',\n ...options.parseOptions,\n }\n\n // If `emitContentError` is enabled, we want to check the content for errors\n // but ignore them (do not remove the invalid content from the document)\n if (!options.errorOnInvalidContent && !editor.options.enableContentCheck && editor.options.emitContentError) {\n try {\n createNodeFromContent(value, editor.schema, {\n parseOptions,\n errorOnInvalidContent: true,\n })\n } catch (e) {\n emitContentError(e as Error)\n }\n }\n\n try {\n content = createNodeFromContent(value, editor.schema, {\n parseOptions,\n errorOnInvalidContent: options.errorOnInvalidContent ?? editor.options.enableContentCheck,\n })\n } catch (e) {\n emitContentError(e as Error)\n return false\n }\n\n let { from, to } =\n typeof position === 'number' ? { from: position, to: position } : { from: position.from, to: position.to }\n\n let isOnlyTextContent = true\n let isOnlyBlockContent = true\n const nodes = isFragment(content) ? content : [content]\n\n nodes.forEach(node => {\n // check if added node is valid\n node.check()\n\n isOnlyTextContent = isOnlyTextContent ? node.isText && node.marks.length === 0 : false\n\n isOnlyBlockContent = isOnlyBlockContent ? node.isBlock : false\n })\n\n // check if we can replace the wrapping node by\n // the newly inserted content\n // example:\n // replace an empty paragraph by an inserted image\n // instead of inserting the image below the paragraph\n if (from === to && isOnlyBlockContent) {\n const { parent } = tr.doc.resolve(from)\n const isEmptyTextBlock = parent.isTextblock && !parent.type.spec.code && !parent.childCount\n\n if (isEmptyTextBlock) {\n from -= 1\n to += 1\n }\n }\n\n let newContent\n\n // if there is only plain text we have to use `insertText`\n // because this will keep the current marks\n if (isOnlyTextContent) {\n // if value is string, we can use it directly\n // otherwise if it is an array, we have to join it\n if (Array.isArray(value)) {\n newContent = value.map(v => v.text || '').join('')\n } else if (value instanceof Fragment) {\n let text = ''\n\n value.forEach(node => {\n if (node.text) {\n text += node.text\n }\n })\n\n newContent = text\n } else if (typeof value === 'object' && !!value && !!value.text) {\n newContent = value.text\n } else {\n newContent = value as string\n }\n\n tr.insertText(newContent, from, to)\n } else {\n newContent = content\n\n const $from = tr.doc.resolve(from)\n const $fromNode = $from.node()\n const fromSelectionAtStart = $from.parentOffset === 0\n const isTextSelection = $fromNode.isText || $fromNode.isTextblock\n const hasContent = $fromNode.content.size > 0\n\n if (fromSelectionAtStart && isTextSelection && hasContent) {\n from = Math.max(0, from - 1)\n }\n\n tr.replaceWith(from, to, newContent)\n }\n\n // set cursor at end of inserted content\n if (options.updateSelection) {\n selectionToInsertionEnd(tr, tr.steps.length - 1, -1)\n }\n\n if (options.applyInputRules) {\n tr.setMeta('applyInputRules', { from, text: newContent })\n }\n\n if (options.applyPasteRules) {\n tr.setMeta('applyPasteRules', { from, text: newContent })\n }\n }\n\n return true\n }\n","import type { ParseOptions } from '@tiptap/pm/model'\nimport { DOMParser, Fragment, Node as ProseMirrorNode, Schema } from '@tiptap/pm/model'\n\nimport type { Content } from '../types.js'\nimport { elementFromString } from '../utilities/elementFromString.js'\n\nexport type CreateNodeFromContentOptions = {\n slice?: boolean\n parseOptions?: ParseOptions\n errorOnInvalidContent?: boolean\n}\n\n/**\n * Takes a JSON or HTML content and creates a Prosemirror node or fragment from it.\n * @param content The JSON or HTML content to create the node from\n * @param schema The Prosemirror schema to use for the node\n * @param options Options for the parser\n * @returns The created Prosemirror node or fragment\n */\nexport function createNodeFromContent(\n content: Content | ProseMirrorNode | Fragment,\n schema: Schema,\n options?: CreateNodeFromContentOptions,\n): ProseMirrorNode | Fragment {\n if (content instanceof ProseMirrorNode || content instanceof Fragment) {\n return content\n }\n options = {\n slice: true,\n parseOptions: {},\n ...options,\n }\n\n const isJSONContent = typeof content === 'object' && content !== null\n const isTextContent = typeof content === 'string'\n\n if (isJSONContent) {\n try {\n const isArrayContent = Array.isArray(content) && content.length > 0\n\n // if the JSON Content is an array of nodes, create a fragment for each node\n if (isArrayContent) {\n return Fragment.fromArray(content.map(item => schema.nodeFromJSON(item)))\n }\n\n const node = schema.nodeFromJSON(content)\n\n if (options.errorOnInvalidContent) {\n node.check()\n }\n\n return node\n } catch (error) {\n if (options.errorOnInvalidContent) {\n throw new Error('[tiptap error]: Invalid JSON content', { cause: error as Error })\n }\n\n console.warn('[tiptap warn]: Invalid content.', 'Passed value:', content, 'Error:', error)\n\n return createNodeFromContent('', schema, options)\n }\n }\n\n if (isTextContent) {\n // Check for invalid content\n if (options.errorOnInvalidContent) {\n let hasInvalidContent = false\n let invalidContent = ''\n\n // A copy of the current schema with a catch-all node at the end\n const contentCheckSchema = new Schema({\n topNode: schema.spec.topNode,\n marks: schema.spec.marks,\n // Prosemirror's schemas are executed such that: the last to execute, matches last\n // This means that we can add a catch-all node at the end of the schema to catch any content that we don't know how to handle\n nodes: schema.spec.nodes.append({\n __tiptap__private__unknown__catch__all__node: {\n content: 'inline*',\n group: 'block',\n parseDOM: [\n {\n tag: '*',\n getAttrs: e => {\n // If this is ever called, we know that the content has something that we don't know how to handle in the schema\n hasInvalidContent = true\n // Try to stringify the element for a more helpful error message\n invalidContent = typeof e === 'string' ? e : e.outerHTML\n return null\n },\n },\n ],\n },\n }),\n })\n\n if (options.slice) {\n DOMParser.fromSchema(contentCheckSchema).parseSlice(elementFromString(content), options.parseOptions)\n } else {\n DOMParser.fromSchema(contentCheckSchema).parse(elementFromString(content), options.parseOptions)\n }\n\n if (options.errorOnInvalidContent && hasInvalidContent) {\n throw new Error('[tiptap error]: Invalid HTML content', {\n cause: new Error(`Invalid element found: ${invalidContent}`),\n })\n }\n }\n\n const parser = DOMParser.fromSchema(schema)\n\n if (options.slice) {\n return parser.parseSlice(elementFromString(content), options.parseOptions).content\n }\n\n return parser.parse(elementFromString(content), options.parseOptions)\n }\n\n return createNodeFromContent('', schema, options)\n}\n","const removeWhitespaces = (node: HTMLElement) => {\n const children = node.childNodes\n\n for (let i = children.length - 1; i >= 0; i -= 1) {\n const child = children[i]\n\n if (child.nodeType === 3 && child.nodeValue && /^(\\n\\s\\s|\\n)$/.test(child.nodeValue)) {\n node.removeChild(child)\n } else if (child.nodeType === 1) {\n removeWhitespaces(child as HTMLElement)\n }\n }\n\n return node\n}\n\nexport function elementFromString(value: string): HTMLElement {\n if (typeof window === 'undefined') {\n throw new Error('[tiptap error]: there is no window object available, so this function cannot be used')\n }\n // add a wrapper to preserve leading and trailing whitespace\n const wrappedValue = `<body>${value}</body>`\n\n const html = new window.DOMParser().parseFromString(wrappedValue, 'text/html').body\n\n return removeWhitespaces(html)\n}\n","import type { Transaction } from '@tiptap/pm/state'\nimport { Selection } from '@tiptap/pm/state'\nimport { ReplaceAroundStep, ReplaceStep } from '@tiptap/pm/transform'\n\n// source: https://github.com/ProseMirror/prosemirror-state/blob/master/src/selection.js#L466\nexport function selectionToInsertionEnd(tr: Transaction, startLen: number, bias: number) {\n const last = tr.steps.length - 1\n\n if (last < startLen) {\n return\n }\n\n const step = tr.steps[last]\n\n if (!(step instanceof ReplaceStep || step instanceof ReplaceAroundStep)) {\n return\n }\n\n const map = tr.mapping.maps[last]\n let end = 0\n\n map.forEach((_from, _to, _newFrom, newTo) => {\n if (end === 0) {\n end = newTo\n }\n })\n\n tr.setSelection(Selection.near(tr.doc.resolve(end), bias))\n}\n","import {\n joinBackward as originalJoinBackward,\n joinDown as originalJoinDown,\n joinForward as originalJoinForward,\n joinUp as originalJoinUp,\n} from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinUp: {\n /**\n * Join the selected block or, if there is a text selection, the closest ancestor block of the selection that can be joined, with the sibling above it.\n * @example editor.commands.joinUp()\n */\n joinUp: () => ReturnType\n }\n joinDown: {\n /**\n * Join the selected block, or the closest ancestor of the selection that can be joined, with the sibling after it.\n * @example editor.commands.joinDown()\n */\n joinDown: () => ReturnType\n }\n joinBackward: {\n /**\n * If the selection is empty and at the start of a textblock, try to reduce the distance between that block and the one before it—if there's a block directly before it that can be joined, join them.\n * If not, try to move the selected block closer to the next one in the document structure by lifting it out of its\n * parent or moving it into a parent of the previous block. Will use the view for accurate (bidi-aware) start-of-textblock detection if given.\n * @example editor.commands.joinBackward()\n */\n joinBackward: () => ReturnType\n }\n joinForward: {\n /**\n * If the selection is empty and the cursor is at the end of a textblock, try to reduce or remove the boundary between that block and the one after it,\n * either by joining them or by moving the other block closer to this one in the tree structure.\n * Will use the view for accurate start-of-textblock detection if given.\n * @example editor.commands.joinForward()\n */\n joinForward: () => ReturnType\n }\n }\n}\n\nexport const joinUp: RawCommands['joinUp'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinUp(state, dispatch)\n }\n\nexport const joinDown: RawCommands['joinDown'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinDown(state, dispatch)\n }\n\nexport const joinBackward: RawCommands['joinBackward'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinBackward(state, dispatch)\n }\n\nexport const joinForward: RawCommands['joinForward'] =\n () =>\n ({ state, dispatch }) => {\n return originalJoinForward(state, dispatch)\n }\n","import { joinPoint } from '@tiptap/pm/transform'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinItemBackward: {\n /**\n * Join two items backward.\n * @example editor.commands.joinItemBackward()\n */\n joinItemBackward: () => ReturnType\n }\n }\n}\n\nexport const joinItemBackward: RawCommands['joinItemBackward'] =\n () =>\n ({ state, dispatch, tr }) => {\n try {\n const point = joinPoint(state.doc, state.selection.$from.pos, -1)\n\n if (point === null || point === undefined) {\n return false\n }\n\n tr.join(point, 2)\n\n if (dispatch) {\n dispatch(tr)\n }\n\n return true\n } catch {\n return false\n }\n }\n","import { joinPoint } from '@tiptap/pm/transform'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinItemForward: {\n /**\n * Join two items Forwards.\n * @example editor.commands.joinItemForward()\n */\n joinItemForward: () => ReturnType\n }\n }\n}\n\nexport const joinItemForward: RawCommands['joinItemForward'] =\n () =>\n ({ state, dispatch, tr }) => {\n try {\n const point = joinPoint(state.doc, state.selection.$from.pos, +1)\n\n if (point === null || point === undefined) {\n return false\n }\n\n tr.join(point, 2)\n\n if (dispatch) {\n dispatch(tr)\n }\n\n return true\n } catch {\n return false\n }\n }\n","import { joinTextblockBackward as originalCommand } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinTextblockBackward: {\n /**\n * A more limited form of joinBackward that only tries to join the current textblock to the one before it, if the cursor is at the start of a textblock.\n */\n joinTextblockBackward: () => ReturnType\n }\n }\n}\n\nexport const joinTextblockBackward: RawCommands['joinTextblockBackward'] =\n () =>\n ({ state, dispatch }) => {\n return originalCommand(state, dispatch)\n }\n","import { joinTextblockForward as originalCommand } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n joinTextblockForward: {\n /**\n * A more limited form of joinForward that only tries to join the current textblock to the one after it, if the cursor is at the end of a textblock.\n */\n joinTextblockForward: () => ReturnType\n }\n }\n}\n\nexport const joinTextblockForward: RawCommands['joinTextblockForward'] =\n () =>\n ({ state, dispatch }) => {\n return originalCommand(state, dispatch)\n }\n","export function isMacOS(): boolean {\n return typeof navigator !== 'undefined' ? /Mac/.test(navigator.platform) : false\n}\n","import type { RawCommands } from '../types.js'\nimport { isiOS } from '../utilities/isiOS.js'\nimport { isMacOS } from '../utilities/isMacOS.js'\n\nfunction normalizeKeyName(name: string) {\n const parts = name.split(/-(?!$)/)\n let result = parts[parts.length - 1]\n\n if (result === 'Space') {\n result = ' '\n }\n\n let alt\n let ctrl\n let shift\n let meta\n\n for (let i = 0; i < parts.length - 1; i += 1) {\n const mod = parts[i]\n\n if (/^(cmd|meta|m)$/i.test(mod)) {\n meta = true\n } else if (/^a(lt)?$/i.test(mod)) {\n alt = true\n } else if (/^(c|ctrl|control)$/i.test(mod)) {\n ctrl = true\n } else if (/^s(hift)?$/i.test(mod)) {\n shift = true\n } else if (/^mod$/i.test(mod)) {\n if (isiOS() || isMacOS()) {\n meta = true\n } else {\n ctrl = true\n }\n } else {\n throw new Error(`Unrecognized modifier name: ${mod}`)\n }\n }\n\n if (alt) {\n result = `Alt-${result}`\n }\n\n if (ctrl) {\n result = `Ctrl-${result}`\n }\n\n if (meta) {\n result = `Meta-${result}`\n }\n\n if (shift) {\n result = `Shift-${result}`\n }\n\n return result\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n keyboardShortcut: {\n /**\n * Trigger a keyboard shortcut.\n * @param name The name of the keyboard shortcut.\n * @example editor.commands.keyboardShortcut('Mod-b')\n */\n keyboardShortcut: (name: string) => ReturnType\n }\n }\n}\n\nexport const keyboardShortcut: RawCommands['keyboardShortcut'] =\n name =>\n ({ editor, view, tr, dispatch }) => {\n const keys = normalizeKeyName(name).split(/-(?!$)/)\n const key = keys.find(item => !['Alt', 'Ctrl', 'Meta', 'Shift'].includes(item))\n const event = new KeyboardEvent('keydown', {\n key: key === 'Space' ? ' ' : key,\n altKey: keys.includes('Alt'),\n ctrlKey: keys.includes('Ctrl'),\n metaKey: keys.includes('Meta'),\n shiftKey: keys.includes('Shift'),\n bubbles: true,\n cancelable: true,\n })\n\n const capturedTransaction = editor.captureTransaction(() => {\n view.someProp('handleKeyDown', f => f(view, event))\n })\n\n capturedTransaction?.steps.forEach(step => {\n const newStep = step.map(tr.mapping)\n\n if (newStep && dispatch) {\n tr.maybeStep(newStep)\n }\n })\n\n return true\n }\n","import { lift as originalLift } from '@tiptap/pm/commands'\nimport type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isNodeActive } from '../helpers/isNodeActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n lift: {\n /**\n * Removes an existing wrap if possible lifting the node out of it\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.lift('paragraph')\n * @example editor.commands.lift('heading', { level: 1 })\n */\n lift: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const lift: RawCommands['lift'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n const isActive = isNodeActive(state, type, attributes)\n\n if (!isActive) {\n return false\n }\n\n return originalLift(state, dispatch)\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport type { NodeRange } from '../types.js'\nimport { objectIncludes } from '../utilities/objectIncludes.js'\nimport { getNodeType } from './getNodeType.js'\n\nexport function isNodeActive(\n state: EditorState,\n typeOrName: NodeType | string | null,\n attributes: Record<string, any> = {},\n): boolean {\n const { from, to, empty } = state.selection\n const type = typeOrName ? getNodeType(typeOrName, state.schema) : null\n\n const nodeRanges: NodeRange[] = []\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n return\n }\n\n const relativeFrom = Math.max(from, pos)\n const relativeTo = Math.min(to, pos + node.nodeSize)\n\n nodeRanges.push({\n node,\n from: relativeFrom,\n to: relativeTo,\n })\n })\n\n const selectionRange = to - from\n const matchedNodeRanges = nodeRanges\n .filter(nodeRange => {\n if (!type) {\n return true\n }\n\n return type.name === nodeRange.node.type.name\n })\n .filter(nodeRange => objectIncludes(nodeRange.node.attrs, attributes, { strict: false }))\n\n if (empty) {\n return !!matchedNodeRanges.length\n }\n\n const range = matchedNodeRanges.reduce((sum, nodeRange) => sum + nodeRange.to - nodeRange.from, 0)\n\n return range >= selectionRange\n}\n","import { liftEmptyBlock as originalLiftEmptyBlock } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n liftEmptyBlock: {\n /**\n * If the cursor is in an empty textblock that can be lifted, lift the block.\n * @example editor.commands.liftEmptyBlock()\n */\n liftEmptyBlock: () => ReturnType\n }\n }\n}\n\nexport const liftEmptyBlock: RawCommands['liftEmptyBlock'] =\n () =>\n ({ state, dispatch }) => {\n return originalLiftEmptyBlock(state, dispatch)\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport { liftListItem as originalLiftListItem } from '@tiptap/pm/schema-list'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n liftListItem: {\n /**\n * Create a command to lift the list item around the selection up into a wrapping list.\n * @param typeOrName The type or name of the node.\n * @example editor.commands.liftListItem('listItem')\n */\n liftListItem: (typeOrName: string | NodeType) => ReturnType\n }\n }\n}\n\nexport const liftListItem: RawCommands['liftListItem'] =\n typeOrName =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalLiftListItem(type)(state, dispatch)\n }\n","import { newlineInCode as originalNewlineInCode } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n newlineInCode: {\n /**\n * Add a newline character in code.\n * @example editor.commands.newlineInCode()\n */\n newlineInCode: () => ReturnType\n }\n }\n}\n\nexport const newlineInCode: RawCommands['newlineInCode'] =\n () =>\n ({ state, dispatch }) => {\n return originalNewlineInCode(state, dispatch)\n }\n","import type { Schema } from '@tiptap/pm/model'\n\n/**\n * Get the type of a schema item by its name.\n * @param name The name of the schema item\n * @param schema The Prosemiror schema to search in\n * @returns The type of the schema item (`node` or `mark`), or null if it doesn't exist\n */\nexport function getSchemaTypeNameByName(name: string, schema: Schema): 'node' | 'mark' | null {\n if (schema.nodes[name]) {\n return 'node'\n }\n\n if (schema.marks[name]) {\n return 'mark'\n }\n\n return null\n}\n","/**\n * Remove a property or an array of properties from an object\n * @param obj Object\n * @param key Key to remove\n */\nexport function deleteProps(obj: Record<string, any>, propOrProps: string | string[]): Record<string, any> {\n const props = typeof propOrProps === 'string' ? [propOrProps] : propOrProps\n\n return Object.keys(obj).reduce((newObj: Record<string, any>, prop) => {\n if (!props.includes(prop)) {\n newObj[prop] = obj[prop]\n }\n\n return newObj\n }, {})\n}\n","import type { MarkType, NodeType } from '@tiptap/pm/model'\n\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { getSchemaTypeNameByName } from '../helpers/getSchemaTypeNameByName.js'\nimport type { RawCommands } from '../types.js'\nimport { deleteProps } from '../utilities/deleteProps.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n resetAttributes: {\n /**\n * Resets some node attributes to the default value.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node to reset.\n * @example editor.commands.resetAttributes('heading', 'level')\n */\n resetAttributes: (typeOrName: string | NodeType | MarkType, attributes: string | string[]) => ReturnType\n }\n }\n}\n\nexport const resetAttributes: RawCommands['resetAttributes'] =\n (typeOrName, attributes) =>\n ({ tr, state, dispatch }) => {\n let nodeType: NodeType | null = null\n let markType: MarkType | null = null\n\n const schemaType = getSchemaTypeNameByName(\n typeof typeOrName === 'string' ? typeOrName : typeOrName.name,\n state.schema,\n )\n\n if (!schemaType) {\n return false\n }\n\n if (schemaType === 'node') {\n nodeType = getNodeType(typeOrName as NodeType, state.schema)\n }\n\n if (schemaType === 'mark') {\n markType = getMarkType(typeOrName as MarkType, state.schema)\n }\n\n let canReset = false\n\n tr.selection.ranges.forEach(range => {\n state.doc.nodesBetween(range.$from.pos, range.$to.pos, (node, pos) => {\n if (nodeType && nodeType === node.type) {\n canReset = true\n\n if (dispatch) {\n tr.setNodeMarkup(pos, undefined, deleteProps(node.attrs, attributes))\n }\n }\n\n if (markType && node.marks.length) {\n node.marks.forEach(mark => {\n if (markType === mark.type) {\n canReset = true\n\n if (dispatch) {\n tr.addMark(pos, pos + node.nodeSize, markType.create(deleteProps(mark.attrs, attributes)))\n }\n }\n })\n }\n })\n })\n\n return canReset\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n scrollIntoView: {\n /**\n * Scroll the selection into view.\n * @example editor.commands.scrollIntoView()\n */\n scrollIntoView: () => ReturnType\n }\n }\n}\n\nexport const scrollIntoView: RawCommands['scrollIntoView'] =\n () =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n tr.scrollIntoView()\n }\n\n return true\n }\n","import { AllSelection } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectAll: {\n /**\n * Select the whole document.\n * @example editor.commands.selectAll()\n */\n selectAll: () => ReturnType\n }\n }\n}\n\nexport const selectAll: RawCommands['selectAll'] =\n () =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n const selection = new AllSelection(tr.doc)\n\n tr.setSelection(selection)\n }\n\n return true\n }\n","import { selectNodeBackward as originalSelectNodeBackward } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectNodeBackward: {\n /**\n * Select a node backward.\n * @example editor.commands.selectNodeBackward()\n */\n selectNodeBackward: () => ReturnType\n }\n }\n}\n\nexport const selectNodeBackward: RawCommands['selectNodeBackward'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectNodeBackward(state, dispatch)\n }\n","import { selectNodeForward as originalSelectNodeForward } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectNodeForward: {\n /**\n * Select a node forward.\n * @example editor.commands.selectNodeForward()\n */\n selectNodeForward: () => ReturnType\n }\n }\n}\n\nexport const selectNodeForward: RawCommands['selectNodeForward'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectNodeForward(state, dispatch)\n }\n","import { selectParentNode as originalSelectParentNode } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectParentNode: {\n /**\n * Select the parent node.\n * @example editor.commands.selectParentNode()\n */\n selectParentNode: () => ReturnType\n }\n }\n}\n\nexport const selectParentNode: RawCommands['selectParentNode'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectParentNode(state, dispatch)\n }\n","// @ts-ignore\n// TODO: add types to @types/prosemirror-commands\nimport { selectTextblockEnd as originalSelectTextblockEnd } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectTextblockEnd: {\n /**\n * Moves the cursor to the end of current text block.\n * @example editor.commands.selectTextblockEnd()\n */\n selectTextblockEnd: () => ReturnType\n }\n }\n}\n\nexport const selectTextblockEnd: RawCommands['selectTextblockEnd'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectTextblockEnd(state, dispatch)\n }\n","// @ts-ignore\n// TODO: add types to @types/prosemirror-commands\nimport { selectTextblockStart as originalSelectTextblockStart } from '@tiptap/pm/commands'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n selectTextblockStart: {\n /**\n * Moves the cursor to the start of current text block.\n * @example editor.commands.selectTextblockStart()\n */\n selectTextblockStart: () => ReturnType\n }\n }\n}\n\nexport const selectTextblockStart: RawCommands['selectTextblockStart'] =\n () =>\n ({ state, dispatch }) => {\n return originalSelectTextblockStart(state, dispatch)\n }\n","import type { Fragment, Node as ProseMirrorNode, ParseOptions, Schema } from '@tiptap/pm/model'\n\nimport type { Content } from '../types.js'\nimport { createNodeFromContent } from './createNodeFromContent.js'\n\n/**\n * Create a new Prosemirror document node from content.\n * @param content The JSON or HTML content to create the document from\n * @param schema The Prosemirror schema to use for the document\n * @param parseOptions Options for the parser\n * @returns The created Prosemirror document node\n */\nexport function createDocument(\n content: Content | ProseMirrorNode | Fragment,\n schema: Schema,\n parseOptions: ParseOptions = {},\n options: { errorOnInvalidContent?: boolean } = {},\n): ProseMirrorNode {\n return createNodeFromContent(content, schema, {\n slice: false,\n parseOptions,\n errorOnInvalidContent: options.errorOnInvalidContent,\n }) as ProseMirrorNode\n}\n","import type { Fragment, Node as ProseMirrorNode, ParseOptions } from '@tiptap/pm/model'\n\nimport { createDocument } from '../helpers/createDocument.js'\nimport type { Content, RawCommands } from '../types.js'\n\nexport interface SetContentOptions {\n /**\n * Options for parsing the content.\n * @default {}\n */\n parseOptions?: ParseOptions\n\n /**\n * Whether to throw an error if the content is invalid.\n */\n errorOnInvalidContent?: boolean\n\n /**\n * Whether to emit an update event.\n * @default true\n */\n emitUpdate?: boolean\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setContent: {\n /**\n * Replace the whole document with new content.\n * @param content The new content.\n * @param emitUpdate Whether to emit an update event.\n * @param parseOptions Options for parsing the content.\n * @example editor.commands.setContent('<p>Example text</p>')\n */\n setContent: (\n /**\n * The new content.\n */\n content: Content | Fragment | ProseMirrorNode,\n\n /**\n * Options for `setContent`.\n */\n options?: SetContentOptions,\n ) => ReturnType\n }\n }\n}\n\nexport const setContent: RawCommands['setContent'] =\n (content, { errorOnInvalidContent, emitUpdate = true, parseOptions = {} } = {}) =>\n ({ editor, tr, dispatch, commands }) => {\n const { doc } = tr\n\n // This is to keep backward compatibility with the previous behavior\n // TODO remove this in the next major version\n if (parseOptions.preserveWhitespace !== 'full') {\n const document = createDocument(content, editor.schema, parseOptions, {\n errorOnInvalidContent: errorOnInvalidContent ?? editor.options.enableContentCheck,\n })\n\n if (dispatch) {\n tr.replaceWith(0, doc.content.size, document).setMeta('preventUpdate', !emitUpdate)\n }\n return true\n }\n\n if (dispatch) {\n tr.setMeta('preventUpdate', !emitUpdate)\n }\n\n return commands.insertContentAt({ from: 0, to: doc.content.size }, content, {\n parseOptions,\n errorOnInvalidContent: errorOnInvalidContent ?? editor.options.enableContentCheck,\n })\n }\n","import type { Mark, MarkType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { getMarkType } from './getMarkType.js'\n\nexport function getMarkAttributes(state: EditorState, typeOrName: string | MarkType): Record<string, any> {\n const type = getMarkType(typeOrName, state.schema)\n const { from, to, empty } = state.selection\n const marks: Mark[] = []\n\n if (empty) {\n if (state.storedMarks) {\n marks.push(...state.storedMarks)\n }\n\n marks.push(...state.selection.$head.marks())\n } else {\n state.doc.nodesBetween(from, to, node => {\n marks.push(...node.marks)\n })\n }\n\n const mark = marks.find(markItem => markItem.type.name === type.name)\n\n if (!mark) {\n return {}\n }\n\n return { ...mark.attrs }\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport type { Transaction } from '@tiptap/pm/state'\nimport { Transform } from '@tiptap/pm/transform'\n\n/**\n * Returns a new `Transform` based on all steps of the passed transactions.\n * @param oldDoc The Prosemirror node to start from\n * @param transactions The transactions to combine\n * @returns A new `Transform` with all steps of the passed transactions\n */\nexport function combineTransactionSteps(oldDoc: ProseMirrorNode, transactions: Transaction[]): Transform {\n const transform = new Transform(oldDoc)\n\n transactions.forEach(transaction => {\n transaction.steps.forEach(step => {\n transform.step(step)\n })\n })\n\n return transform\n}\n","import type { ContentMatch, NodeType } from '@tiptap/pm/model'\n\n/**\n * Gets the default block type at a given match\n * @param match The content match to get the default block type from\n * @returns The default block type or null\n */\nexport function defaultBlockAt(match: ContentMatch): NodeType | null {\n for (let i = 0; i < match.edgeCount; i += 1) {\n const { type } = match.edge(i)\n\n if (type.isTextblock && !type.hasRequiredAttrs()) {\n return type\n }\n }\n\n return null\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { NodeWithPos, Predicate } from '../types.js'\n\n/**\n * Find children inside a Prosemirror node that match a predicate.\n * @param node The Prosemirror node to search in\n * @param predicate The predicate to match\n * @returns An array of nodes with their positions\n */\nexport function findChildren(node: ProseMirrorNode, predicate: Predicate): NodeWithPos[] {\n const nodesWithPos: NodeWithPos[] = []\n\n node.descendants((child, pos) => {\n if (predicate(child)) {\n nodesWithPos.push({\n node: child,\n pos,\n })\n }\n })\n\n return nodesWithPos\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { NodeWithPos, Predicate, Range } from '../types.js'\n\n/**\n * Same as `findChildren` but searches only within a `range`.\n * @param node The Prosemirror node to search in\n * @param range The range to search in\n * @param predicate The predicate to match\n * @returns An array of nodes with their positions\n */\nexport function findChildrenInRange(node: ProseMirrorNode, range: Range, predicate: Predicate): NodeWithPos[] {\n const nodesWithPos: NodeWithPos[] = []\n\n // if (range.from === range.to) {\n // const nodeAt = node.nodeAt(range.from)\n\n // if (nodeAt) {\n // nodesWithPos.push({\n // node: nodeAt,\n // pos: range.from,\n // })\n // }\n // }\n\n node.nodesBetween(range.from, range.to, (child, pos) => {\n if (predicate(child)) {\n nodesWithPos.push({\n node: child,\n pos,\n })\n }\n })\n\n return nodesWithPos\n}\n","import type { Node as ProseMirrorNode, ResolvedPos } from '@tiptap/pm/model'\n\nimport type { Predicate } from '../types.js'\n\n/**\n * Finds the closest parent node to a resolved position that matches a predicate.\n * @param $pos The resolved position to search from\n * @param predicate The predicate to match\n * @returns The closest parent node to the resolved position that matches the predicate\n * @example ```js\n * findParentNodeClosestToPos($from, node => node.type.name === 'paragraph')\n * ```\n */\nexport function findParentNodeClosestToPos(\n $pos: ResolvedPos,\n predicate: Predicate,\n):\n | {\n pos: number\n start: number\n depth: number\n node: ProseMirrorNode\n }\n | undefined {\n for (let i = $pos.depth; i > 0; i -= 1) {\n const node = $pos.node(i)\n\n if (predicate(node)) {\n return {\n pos: i > 0 ? $pos.before(i) : 0,\n start: $pos.start(i),\n depth: i,\n node,\n }\n }\n }\n}\n","import type { Selection } from '@tiptap/pm/state'\n\nimport type { Predicate } from '../types.js'\nimport { findParentNodeClosestToPos } from './findParentNodeClosestToPos.js'\n\n/**\n * Finds the closest parent node to the current selection that matches a predicate.\n * @param predicate The predicate to match\n * @returns A command that finds the closest parent node to the current selection that matches the predicate\n * @example ```js\n * findParentNode(node => node.type.name === 'paragraph')\n * ```\n */\nexport function findParentNode(\n predicate: Predicate,\n): (selection: Selection) => ReturnType<typeof findParentNodeClosestToPos> {\n return (selection: Selection) => findParentNodeClosestToPos(selection.$from, predicate)\n}\n","import type { ExtensionConfig } from '../Extension.js'\nimport type { MarkConfig } from '../Mark.js'\nimport type { NodeConfig } from '../Node.js'\nimport type { AnyExtension, MaybeThisParameterType, RemoveThis } from '../types.js'\n\n/**\n * Returns a field from an extension\n * @param extension The Tiptap extension\n * @param field The field, for example `renderHTML` or `priority`\n * @param context The context object that should be passed as `this` into the function\n * @returns The field value\n */\nexport function getExtensionField<T = any, E extends AnyExtension = any>(\n extension: E,\n field: keyof ExtensionConfig | keyof MarkConfig | keyof NodeConfig,\n context?: Omit<MaybeThisParameterType<T>, 'parent'>,\n): RemoveThis<T> {\n if (extension.config[field as keyof typeof extension.config] === undefined && extension.parent) {\n return getExtensionField(extension.parent, field, context)\n }\n\n if (typeof extension.config[field as keyof typeof extension.config] === 'function') {\n const value = (extension.config[field as keyof typeof extension.config] as any).bind({\n ...context,\n parent: extension.parent ? getExtensionField(extension.parent, field, context) : null,\n })\n\n return value\n }\n\n return extension.config[field as keyof typeof extension.config] as RemoveThis<T>\n}\n","import type { AnyConfig, Extensions } from '../types.js'\nimport { getExtensionField } from './getExtensionField.js'\n\n/**\n * Create a flattened array of extensions by traversing the `addExtensions` field.\n * @param extensions An array of Tiptap extensions\n * @returns A flattened array of Tiptap extensions\n */\nexport function flattenExtensions(extensions: Extensions): Extensions {\n return (\n extensions\n .map(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n const addExtensions = getExtensionField<AnyConfig['addExtensions']>(extension, 'addExtensions', context)\n\n if (addExtensions) {\n return [extension, ...flattenExtensions(addExtensions())]\n }\n\n return extension\n })\n // `Infinity` will break TypeScript so we set a number that is probably high enough\n .flat(10)\n )\n}\n","import { Node } from '@tiptap/pm/model'\n\nimport type { Extensions, JSONContent } from '../types.js'\nimport { getHTMLFromFragment } from './getHTMLFromFragment.js'\nimport { getSchema } from './getSchema.js'\n\n/**\n * Generate HTML from a JSONContent\n * @param doc The JSONContent to generate HTML from\n * @param extensions The extensions to use for the schema\n * @returns The generated HTML\n */\nexport function generateHTML(doc: JSONContent, extensions: Extensions): string {\n const schema = getSchema(extensions)\n const contentNode = Node.fromJSON(schema, doc)\n\n return getHTMLFromFragment(contentNode.content, schema)\n}\n","import type { Fragment, Schema } from '@tiptap/pm/model'\nimport { DOMSerializer } from '@tiptap/pm/model'\n\nexport function getHTMLFromFragment(fragment: Fragment, schema: Schema): string {\n const documentFragment = DOMSerializer.fromSchema(schema).serializeFragment(fragment)\n\n const temporaryDocument = document.implementation.createHTMLDocument()\n const container = temporaryDocument.createElement('div')\n\n container.appendChild(documentFragment)\n\n return container.innerHTML\n}\n","import type { MarkSpec, NodeSpec, TagParseRule } from '@tiptap/pm/model'\nimport { Schema } from '@tiptap/pm/model'\n\nimport type { Editor, MarkConfig, NodeConfig } from '../index.js'\nimport type { AnyConfig, Extensions } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\nimport { isEmptyObject } from '../utilities/isEmptyObject.js'\nimport { getAttributesFromExtensions } from './getAttributesFromExtensions.js'\nimport { getExtensionField } from './getExtensionField.js'\nimport { getRenderedAttributes } from './getRenderedAttributes.js'\nimport { injectExtensionAttributesToParseRule } from './injectExtensionAttributesToParseRule.js'\nimport { splitExtensions } from './splitExtensions.js'\n\nfunction cleanUpSchemaItem<T>(data: T) {\n return Object.fromEntries(\n // @ts-ignore\n Object.entries(data).filter(([key, value]) => {\n if (key === 'attrs' && isEmptyObject(value as object | undefined)) {\n return false\n }\n\n return value !== null && value !== undefined\n }),\n ) as T\n}\n\n/**\n * Builds an attribute spec tuple for ProseMirror schema from an extension attribute.\n * @param extensionAttribute The extension attribute to build the spec for\n * @returns A tuple of [attributeName, spec]\n */\nfunction buildAttributeSpec(\n extensionAttribute: ReturnType<typeof getAttributesFromExtensions>[number],\n): [string, Record<string, any>] {\n const spec: Record<string, any> = {}\n\n // Only include 'default' if the attribute is not required and default is set on the attribute\n if (!extensionAttribute?.attribute?.isRequired && 'default' in (extensionAttribute?.attribute || {})) {\n spec.default = extensionAttribute.attribute.default\n }\n\n // Only include 'validate' if it's defined\n if (extensionAttribute?.attribute?.validate !== undefined) {\n spec.validate = extensionAttribute.attribute.validate\n }\n\n return [extensionAttribute.name, spec]\n}\n\n/**\n * Creates a new Prosemirror schema based on the given extensions.\n * @param extensions An array of Tiptap extensions\n * @param editor The editor instance\n * @returns A Prosemirror schema\n */\nexport function getSchemaByResolvedExtensions(extensions: Extensions, editor?: Editor): Schema {\n const allAttributes = getAttributesFromExtensions(extensions)\n const { nodeExtensions, markExtensions } = splitExtensions(extensions)\n const topNode = nodeExtensions.find(extension => getExtensionField(extension, 'topNode'))?.name\n\n const nodes = Object.fromEntries(\n nodeExtensions.map(extension => {\n const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n editor,\n }\n\n const extraNodeFields = extensions.reduce((fields, e) => {\n const extendNodeSchema = getExtensionField<AnyConfig['extendNodeSchema']>(e, 'extendNodeSchema', context)\n\n return {\n ...fields,\n ...(extendNodeSchema ? extendNodeSchema(extension) : {}),\n }\n }, {})\n\n const schema: NodeSpec = cleanUpSchemaItem({\n ...extraNodeFields,\n content: callOrReturn(getExtensionField<NodeConfig['content']>(extension, 'content', context)),\n marks: callOrReturn(getExtensionField<NodeConfig['marks']>(extension, 'marks', context)),\n group: callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context)),\n inline: callOrReturn(getExtensionField<NodeConfig['inline']>(extension, 'inline', context)),\n atom: callOrReturn(getExtensionField<NodeConfig['atom']>(extension, 'atom', context)),\n selectable: callOrReturn(getExtensionField<NodeConfig['selectable']>(extension, 'selectable', context)),\n draggable: callOrReturn(getExtensionField<NodeConfig['draggable']>(extension, 'draggable', context)),\n code: callOrReturn(getExtensionField<NodeConfig['code']>(extension, 'code', context)),\n whitespace: callOrReturn(getExtensionField<NodeConfig['whitespace']>(extension, 'whitespace', context)),\n linebreakReplacement: callOrReturn(\n getExtensionField<NodeConfig['linebreakReplacement']>(extension, 'linebreakReplacement', context),\n ),\n defining: callOrReturn(getExtensionField<NodeConfig['defining']>(extension, 'defining', context)),\n isolating: callOrReturn(getExtensionField<NodeConfig['isolating']>(extension, 'isolating', context)),\n attrs: Object.fromEntries(extensionAttributes.map(buildAttributeSpec)),\n })\n\n const parseHTML = callOrReturn(getExtensionField<NodeConfig['parseHTML']>(extension, 'parseHTML', context))\n\n if (parseHTML) {\n schema.parseDOM = parseHTML.map(parseRule =>\n injectExtensionAttributesToParseRule(parseRule, extensionAttributes),\n ) as TagParseRule[]\n }\n\n const renderHTML = getExtensionField<NodeConfig['renderHTML']>(extension, 'renderHTML', context)\n\n if (renderHTML) {\n schema.toDOM = node =>\n renderHTML({\n node,\n HTMLAttributes: getRenderedAttributes(node, extensionAttributes),\n })\n }\n\n const renderText = getExtensionField<NodeConfig['renderText']>(extension, 'renderText', context)\n\n if (renderText) {\n schema.toText = renderText\n }\n\n return [extension.name, schema]\n }),\n )\n\n const marks = Object.fromEntries(\n markExtensions.map(extension => {\n const extensionAttributes = allAttributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n editor,\n }\n\n const extraMarkFields = extensions.reduce((fields, e) => {\n const extendMarkSchema = getExtensionField<AnyConfig['extendMarkSchema']>(e, 'extendMarkSchema', context)\n\n return {\n ...fields,\n ...(extendMarkSchema ? extendMarkSchema(extension as any) : {}),\n }\n }, {})\n\n const schema: MarkSpec = cleanUpSchemaItem({\n ...extraMarkFields,\n inclusive: callOrReturn(getExtensionField<MarkConfig['inclusive']>(extension, 'inclusive', context)),\n excludes: callOrReturn(getExtensionField<MarkConfig['excludes']>(extension, 'excludes', context)),\n group: callOrReturn(getExtensionField<MarkConfig['group']>(extension, 'group', context)),\n spanning: callOrReturn(getExtensionField<MarkConfig['spanning']>(extension, 'spanning', context)),\n code: callOrReturn(getExtensionField<MarkConfig['code']>(extension, 'code', context)),\n attrs: Object.fromEntries(extensionAttributes.map(buildAttributeSpec)),\n })\n\n const parseHTML = callOrReturn(getExtensionField<MarkConfig['parseHTML']>(extension, 'parseHTML', context))\n\n if (parseHTML) {\n schema.parseDOM = parseHTML.map(parseRule =>\n injectExtensionAttributesToParseRule(parseRule, extensionAttributes),\n )\n }\n\n const renderHTML = getExtensionField<MarkConfig['renderHTML']>(extension, 'renderHTML', context)\n\n if (renderHTML) {\n schema.toDOM = mark =>\n renderHTML({\n mark,\n HTMLAttributes: getRenderedAttributes(mark, extensionAttributes),\n })\n }\n\n return [extension.name, schema]\n }),\n )\n\n return new Schema({\n topNode,\n nodes,\n marks,\n })\n}\n","// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type\nexport function isFunction(value: any): value is Function {\n return typeof value === 'function'\n}\n","import type { MaybeReturnType } from '../types.js'\nimport { isFunction } from './isFunction.js'\n\n/**\n * Optionally calls `value` as a function.\n * Otherwise it is returned directly.\n * @param value Function or any value.\n * @param context Optional context to bind to function.\n * @param props Optional props to pass to function.\n */\nexport function callOrReturn<T>(value: T, context: any = undefined, ...props: any[]): MaybeReturnType<T> {\n if (isFunction(value)) {\n if (context) {\n return value.bind(context)(...props)\n }\n\n return value(...props)\n }\n\n return value as MaybeReturnType<T>\n}\n","export function isEmptyObject(value = {}): boolean {\n return Object.keys(value).length === 0 && value.constructor === Object\n}\n","import type { Extension } from '../Extension.js'\nimport type { Mark } from '../Mark.js'\nimport type { Node } from '../Node.js'\nimport type { Extensions } from '../types.js'\n\nexport function splitExtensions(extensions: Extensions) {\n const baseExtensions = extensions.filter(extension => extension.type === 'extension') as Extension[]\n const nodeExtensions = extensions.filter(extension => extension.type === 'node') as Node[]\n const markExtensions = extensions.filter(extension => extension.type === 'mark') as Mark[]\n\n return {\n baseExtensions,\n nodeExtensions,\n markExtensions,\n }\n}\n","import type { MarkConfig, NodeConfig } from '../index.js'\nimport type { AnyConfig, Attribute, Attributes, ExtensionAttribute, Extensions } from '../types.js'\nimport { getExtensionField } from './getExtensionField.js'\nimport { splitExtensions } from './splitExtensions.js'\n\n/**\n * Get a list of all extension attributes defined in `addAttribute` and `addGlobalAttribute`.\n * @param extensions List of extensions\n */\nexport function getAttributesFromExtensions(extensions: Extensions): ExtensionAttribute[] {\n const extensionAttributes: ExtensionAttribute[] = []\n const { nodeExtensions, markExtensions } = splitExtensions(extensions)\n const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions]\n const defaultAttribute: Required<Omit<Attribute, 'validate'>> & Pick<Attribute, 'validate'> = {\n default: null,\n validate: undefined,\n rendered: true,\n renderHTML: null,\n parseHTML: null,\n keepOnSplit: true,\n isRequired: false,\n }\n\n extensions.forEach(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n extensions: nodeAndMarkExtensions,\n }\n\n const addGlobalAttributes = getExtensionField<AnyConfig['addGlobalAttributes']>(\n extension,\n 'addGlobalAttributes',\n context,\n )\n\n if (!addGlobalAttributes) {\n return\n }\n\n const globalAttributes = addGlobalAttributes()\n\n globalAttributes.forEach(globalAttribute => {\n globalAttribute.types.forEach(type => {\n Object.entries(globalAttribute.attributes).forEach(([name, attribute]) => {\n extensionAttributes.push({\n type,\n name,\n attribute: {\n ...defaultAttribute,\n ...attribute,\n },\n })\n })\n })\n })\n })\n\n nodeAndMarkExtensions.forEach(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n\n const addAttributes = getExtensionField<NodeConfig['addAttributes'] | MarkConfig['addAttributes']>(\n extension,\n 'addAttributes',\n context,\n )\n\n if (!addAttributes) {\n return\n }\n\n // TODO: remove `as Attributes`\n const attributes = addAttributes() as Attributes\n\n Object.entries(attributes).forEach(([name, attribute]) => {\n const mergedAttr = {\n ...defaultAttribute,\n ...attribute,\n }\n\n if (typeof mergedAttr?.default === 'function') {\n mergedAttr.default = mergedAttr.default()\n }\n\n if (mergedAttr?.isRequired && mergedAttr?.default === undefined) {\n delete mergedAttr.default\n }\n\n extensionAttributes.push({\n type: extension.name,\n name,\n attribute: mergedAttr,\n })\n })\n })\n\n return extensionAttributes\n}\n","export function mergeAttributes(...objects: Record<string, any>[]): Record<string, any> {\n return objects\n .filter(item => !!item)\n .reduce((items, item) => {\n const mergedAttributes = { ...items }\n\n Object.entries(item).forEach(([key, value]) => {\n const exists = mergedAttributes[key]\n\n if (!exists) {\n mergedAttributes[key] = value\n\n return\n }\n\n if (key === 'class') {\n const valueClasses: string[] = value ? String(value).split(' ') : []\n const existingClasses: string[] = mergedAttributes[key] ? mergedAttributes[key].split(' ') : []\n\n const insertClasses = valueClasses.filter(valueClass => !existingClasses.includes(valueClass))\n\n mergedAttributes[key] = [...existingClasses, ...insertClasses].join(' ')\n } else if (key === 'style') {\n const newStyles: string[] = value\n ? value\n .split(';')\n .map((style: string) => style.trim())\n .filter(Boolean)\n : []\n const existingStyles: string[] = mergedAttributes[key]\n ? mergedAttributes[key]\n .split(';')\n .map((style: string) => style.trim())\n .filter(Boolean)\n : []\n\n const styleMap = new Map<string, string>()\n\n existingStyles.forEach(style => {\n const [property, val] = style.split(':').map(part => part.trim())\n\n styleMap.set(property, val)\n })\n\n newStyles.forEach(style => {\n const [property, val] = style.split(':').map(part => part.trim())\n\n styleMap.set(property, val)\n })\n\n mergedAttributes[key] = Array.from(styleMap.entries())\n .map(([property, val]) => `${property}: ${val}`)\n .join('; ')\n } else {\n mergedAttributes[key] = value\n }\n })\n\n return mergedAttributes\n }, {})\n}\n","import type { Mark, Node } from '@tiptap/pm/model'\n\nimport type { ExtensionAttribute } from '../types.js'\nimport { mergeAttributes } from '../utilities/mergeAttributes.js'\n\nexport function getRenderedAttributes(\n nodeOrMark: Node | Mark,\n extensionAttributes: ExtensionAttribute[],\n): Record<string, any> {\n return extensionAttributes\n .filter(attribute => attribute.type === nodeOrMark.type.name)\n .filter(item => item.attribute.rendered)\n .map(item => {\n if (!item.attribute.renderHTML) {\n return {\n [item.name]: nodeOrMark.attrs[item.name],\n }\n }\n\n return item.attribute.renderHTML(nodeOrMark.attrs) || {}\n })\n .reduce((attributes, attribute) => mergeAttributes(attributes, attribute), {})\n}\n","export function fromString(value: any): any {\n if (typeof value !== 'string') {\n return value\n }\n\n if (value.match(/^[+-]?(?:\\d*\\.)?\\d+$/)) {\n return Number(value)\n }\n\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n return value\n}\n","import type { ParseRule } from '@tiptap/pm/model'\n\nimport type { ExtensionAttribute } from '../types.js'\nimport { fromString } from '../utilities/fromString.js'\n\n/**\n * This function merges extension attributes into parserule attributes (`attrs` or `getAttrs`).\n * Cancels when `getAttrs` returned `false`.\n * @param parseRule ProseMirror ParseRule\n * @param extensionAttributes List of attributes to inject\n */\nexport function injectExtensionAttributesToParseRule(\n parseRule: ParseRule,\n extensionAttributes: ExtensionAttribute[],\n): ParseRule {\n if ('style' in parseRule) {\n return parseRule\n }\n\n return {\n ...parseRule,\n getAttrs: (node: HTMLElement) => {\n const oldAttributes = parseRule.getAttrs ? parseRule.getAttrs(node) : parseRule.attrs\n\n if (oldAttributes === false) {\n return false\n }\n\n const newAttributes = extensionAttributes.reduce((items, item) => {\n const value = item.attribute.parseHTML\n ? item.attribute.parseHTML(node)\n : fromString(node.getAttribute(item.name))\n\n if (value === null || value === undefined) {\n return items\n }\n\n return {\n ...items,\n [item.name]: value,\n }\n }, {})\n\n return { ...oldAttributes, ...newAttributes }\n },\n }\n}\n","/**\n * Find duplicates in an array.\n */\nexport function findDuplicates<T>(items: T[]): T[] {\n const filtered = items.filter((el, index) => items.indexOf(el) !== index)\n\n return Array.from(new Set(filtered))\n}\n","import type { AnyConfig, Extensions } from '../types.js'\nimport { getExtensionField } from './getExtensionField.js'\n\n/**\n * Sort extensions by priority.\n * @param extensions An array of Tiptap extensions\n * @returns A sorted array of Tiptap extensions by priority\n */\nexport function sortExtensions(extensions: Extensions): Extensions {\n const defaultPriority = 100\n\n return extensions.sort((a, b) => {\n const priorityA = getExtensionField<AnyConfig['priority']>(a, 'priority') || defaultPriority\n const priorityB = getExtensionField<AnyConfig['priority']>(b, 'priority') || defaultPriority\n\n if (priorityA > priorityB) {\n return -1\n }\n\n if (priorityA < priorityB) {\n return 1\n }\n\n return 0\n })\n}\n","import type { Extensions } from '../types.js'\nimport { findDuplicates } from '../utilities/findDuplicates.js'\nimport { flattenExtensions } from './flattenExtensions.js'\nimport { sortExtensions } from './sortExtensions.js'\n\n/**\n * Returns a flattened and sorted extension list while\n * also checking for duplicated extensions and warns the user.\n * @param extensions An array of Tiptap extensions\n * @returns An flattened and sorted array of Tiptap extensions\n */\nexport function resolveExtensions(extensions: Extensions): Extensions {\n const resolvedExtensions = sortExtensions(flattenExtensions(extensions))\n const duplicatedNames = findDuplicates(resolvedExtensions.map(extension => extension.name))\n\n if (duplicatedNames.length) {\n console.warn(\n `[tiptap warn]: Duplicate extension names found: [${duplicatedNames\n .map(item => `'${item}'`)\n .join(', ')}]. This can lead to issues.`,\n )\n }\n\n return resolvedExtensions\n}\n","import type { Schema } from '@tiptap/pm/model'\n\nimport type { Editor } from '../Editor.js'\nimport type { Extensions } from '../types.js'\nimport { getSchemaByResolvedExtensions } from './getSchemaByResolvedExtensions.js'\nimport { resolveExtensions } from './resolveExtensions.js'\n\nexport function getSchema(extensions: Extensions, editor?: Editor): Schema {\n const resolvedExtensions = resolveExtensions(extensions)\n\n return getSchemaByResolvedExtensions(resolvedExtensions, editor)\n}\n","import { DOMParser } from '@tiptap/pm/model'\n\nimport type { Extensions } from '../types.js'\nimport { elementFromString } from '../utilities/elementFromString.js'\nimport { getSchema } from './getSchema.js'\n\n/**\n * Generate JSONContent from HTML\n * @param html The HTML to generate JSONContent from\n * @param extensions The extensions to use for the schema\n * @returns The generated JSONContent\n */\nexport function generateJSON(html: string, extensions: Extensions): Record<string, any> {\n const schema = getSchema(extensions)\n const dom = elementFromString(html)\n\n return DOMParser.fromSchema(schema).parse(dom).toJSON()\n}\n","import { Node } from '@tiptap/pm/model'\n\nimport type { Extensions, JSONContent, TextSerializer } from '../types.js'\nimport { getSchema } from './getSchema.js'\nimport { getText } from './getText.js'\nimport { getTextSerializersFromSchema } from './getTextSerializersFromSchema.js'\n\n/**\n * Generate raw text from a JSONContent\n * @param doc The JSONContent to generate text from\n * @param extensions The extensions to use for the schema\n * @param options Options for the text generation f.e. blockSeparator or textSerializers\n * @returns The generated text\n */\nexport function generateText(\n doc: JSONContent,\n extensions: Extensions,\n options?: {\n blockSeparator?: string\n textSerializers?: Record<string, TextSerializer>\n },\n): string {\n const { blockSeparator = '\\n\\n', textSerializers = {} } = options || {}\n const schema = getSchema(extensions)\n const contentNode = Node.fromJSON(schema, doc)\n\n return getText(contentNode, {\n blockSeparator,\n textSerializers: {\n ...getTextSerializersFromSchema(schema),\n ...textSerializers,\n },\n })\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { Range, TextSerializer } from '../types.js'\n\n/**\n * Gets the text between two positions in a Prosemirror node\n * and serializes it using the given text serializers and block separator (see getText)\n * @param startNode The Prosemirror node to start from\n * @param range The range of the text to get\n * @param options Options for the text serializer & block separator\n * @returns The text between the two positions\n */\nexport function getTextBetween(\n startNode: ProseMirrorNode,\n range: Range,\n options?: {\n blockSeparator?: string\n textSerializers?: Record<string, TextSerializer>\n },\n): string {\n const { from, to } = range\n const { blockSeparator = '\\n\\n', textSerializers = {} } = options || {}\n let text = ''\n\n startNode.nodesBetween(from, to, (node, pos, parent, index) => {\n if (node.isBlock && pos > from) {\n text += blockSeparator\n }\n\n const textSerializer = textSerializers?.[node.type.name]\n\n if (textSerializer) {\n if (parent) {\n text += textSerializer({\n node,\n pos,\n parent,\n index,\n range,\n })\n }\n // do not descend into child nodes when there exists a serializer\n return false\n }\n\n if (node.isText) {\n text += node?.text?.slice(Math.max(from, pos) - pos, to - pos) // eslint-disable-line\n }\n })\n\n return text\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { TextSerializer } from '../types.js'\nimport { getTextBetween } from './getTextBetween.js'\n\n/**\n * Gets the text of a Prosemirror node\n * @param node The Prosemirror node\n * @param options Options for the text serializer & block separator\n * @returns The text of the node\n * @example ```js\n * const text = getText(node, { blockSeparator: '\\n' })\n * ```\n */\nexport function getText(\n node: ProseMirrorNode,\n options?: {\n blockSeparator?: string\n textSerializers?: Record<string, TextSerializer>\n },\n) {\n const range = {\n from: 0,\n to: node.content.size,\n }\n\n return getTextBetween(node, range, options)\n}\n","import type { Schema } from '@tiptap/pm/model'\n\nimport type { TextSerializer } from '../types.js'\n\n/**\n * Find text serializers `toText` in a Prosemirror schema\n * @param schema The Prosemirror schema to search in\n * @returns A record of text serializers by node name\n */\nexport function getTextSerializersFromSchema(schema: Schema): Record<string, TextSerializer> {\n return Object.fromEntries(\n Object.entries(schema.nodes)\n .filter(([, node]) => node.spec.toText)\n .map(([name, node]) => [name, node.spec.toText]),\n )\n}\n","import type { Node, NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { getNodeType } from './getNodeType.js'\n\nexport function getNodeAttributes(state: EditorState, typeOrName: string | NodeType): Record<string, any> {\n const type = getNodeType(typeOrName, state.schema)\n const { from, to } = state.selection\n const nodes: Node[] = []\n\n state.doc.nodesBetween(from, to, node => {\n nodes.push(node)\n })\n\n const node = nodes.reverse().find(nodeItem => nodeItem.type.name === type.name)\n\n if (!node) {\n return {}\n }\n\n return { ...node.attrs }\n}\n","import type { MarkType, NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport { getMarkAttributes } from './getMarkAttributes.js'\nimport { getNodeAttributes } from './getNodeAttributes.js'\nimport { getSchemaTypeNameByName } from './getSchemaTypeNameByName.js'\n\n/**\n * Get node or mark attributes by type or name on the current editor state\n * @param state The current editor state\n * @param typeOrName The node or mark type or name\n * @returns The attributes of the node or mark or an empty object\n */\nexport function getAttributes(state: EditorState, typeOrName: string | NodeType | MarkType): Record<string, any> {\n const schemaType = getSchemaTypeNameByName(\n typeof typeOrName === 'string' ? typeOrName : typeOrName.name,\n state.schema,\n )\n\n if (schemaType === 'node') {\n return getNodeAttributes(state, typeOrName as NodeType)\n }\n\n if (schemaType === 'mark') {\n return getMarkAttributes(state, typeOrName as MarkType)\n }\n\n return {}\n}\n","/**\n * Removes duplicated values within an array.\n * Supports numbers, strings and objects.\n */\nexport function removeDuplicates<T>(array: T[], by = JSON.stringify): T[] {\n const seen: Record<any, any> = {}\n\n return array.filter(item => {\n const key = by(item)\n\n return Object.prototype.hasOwnProperty.call(seen, key) ? false : (seen[key] = true)\n })\n}\n","import type { Step, Transform } from '@tiptap/pm/transform'\n\nimport type { Range } from '../types.js'\nimport { removeDuplicates } from '../utilities/removeDuplicates.js'\n\nexport type ChangedRange = {\n oldRange: Range\n newRange: Range\n}\n\n/**\n * Removes duplicated ranges and ranges that are\n * fully captured by other ranges.\n */\nfunction simplifyChangedRanges(changes: ChangedRange[]): ChangedRange[] {\n const uniqueChanges = removeDuplicates(changes)\n\n return uniqueChanges.length === 1\n ? uniqueChanges\n : uniqueChanges.filter((change, index) => {\n const rest = uniqueChanges.filter((_, i) => i !== index)\n\n return !rest.some(otherChange => {\n return (\n change.oldRange.from >= otherChange.oldRange.from &&\n change.oldRange.to <= otherChange.oldRange.to &&\n change.newRange.from >= otherChange.newRange.from &&\n change.newRange.to <= otherChange.newRange.to\n )\n })\n })\n}\n\n/**\n * Returns a list of changed ranges\n * based on the first and last state of all steps.\n */\nexport function getChangedRanges(transform: Transform): ChangedRange[] {\n const { mapping, steps } = transform\n const changes: ChangedRange[] = []\n\n mapping.maps.forEach((stepMap, index) => {\n const ranges: Range[] = []\n\n // This accounts for step changes where no range was actually altered\n // e.g. when setting a mark, node attribute, etc.\n // @ts-ignore\n if (!stepMap.ranges.length) {\n const { from, to } = steps[index] as Step & {\n from?: number\n to?: number\n }\n\n if (from === undefined || to === undefined) {\n return\n }\n\n ranges.push({ from, to })\n } else {\n stepMap.forEach((from, to) => {\n ranges.push({ from, to })\n })\n }\n\n ranges.forEach(({ from, to }) => {\n const newStart = mapping.slice(index).map(from, -1)\n const newEnd = mapping.slice(index).map(to)\n const oldStart = mapping.invert().map(newStart, -1)\n const oldEnd = mapping.invert().map(newEnd)\n\n changes.push({\n oldRange: {\n from: oldStart,\n to: oldEnd,\n },\n newRange: {\n from: newStart,\n to: newEnd,\n },\n })\n })\n })\n\n return simplifyChangedRanges(changes)\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { JSONContent } from '../types.js'\n\ninterface DebugJSONContent extends JSONContent {\n from: number\n to: number\n}\n\nexport function getDebugJSON(node: ProseMirrorNode, startOffset = 0): DebugJSONContent {\n const isTopNode = node.type === node.type.schema.topNodeType\n const increment = isTopNode ? 0 : 1\n const from = startOffset\n const to = from + node.nodeSize\n const marks = node.marks.map(mark => {\n const output: { type: string; attrs?: Record<string, any> } = {\n type: mark.type.name,\n }\n\n if (Object.keys(mark.attrs).length) {\n output.attrs = { ...mark.attrs }\n }\n\n return output\n })\n const attrs = { ...node.attrs }\n const output: DebugJSONContent = {\n type: node.type.name,\n from,\n to,\n }\n\n if (Object.keys(attrs).length) {\n output.attrs = attrs\n }\n\n if (marks.length) {\n output.marks = marks\n }\n\n if (node.content.childCount) {\n output.content = []\n\n node.forEach((child, offset) => {\n output.content?.push(getDebugJSON(child, startOffset + offset + increment))\n })\n }\n\n if (node.text) {\n output.text = node.text\n }\n\n return output\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\nimport type { MarkRange } from '../types.js'\nimport { getMarkRange } from './getMarkRange.js'\n\nexport function getMarksBetween(from: number, to: number, doc: ProseMirrorNode): MarkRange[] {\n const marks: MarkRange[] = []\n\n // get all inclusive marks on empty selection\n if (from === to) {\n doc\n .resolve(from)\n .marks()\n .forEach(mark => {\n const $pos = doc.resolve(from)\n const range = getMarkRange($pos, mark.type)\n\n if (!range) {\n return\n }\n\n marks.push({\n mark,\n ...range,\n })\n })\n } else {\n doc.nodesBetween(from, to, (node, pos) => {\n if (!node || node?.nodeSize === undefined) {\n return\n }\n\n marks.push(\n ...node.marks.map(mark => ({\n from: pos,\n to: pos + node.nodeSize,\n mark,\n })),\n )\n })\n }\n\n return marks\n}\n","import type { Node, NodeType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\n/**\n * Finds the first node of a given type or name in the current selection.\n * @param state The editor state.\n * @param typeOrName The node type or name.\n * @param pos The position to start searching from.\n * @param maxDepth The maximum depth to search.\n * @returns The node and the depth as an array.\n */\nexport const getNodeAtPosition = (state: EditorState, typeOrName: string | NodeType, pos: number, maxDepth = 20) => {\n const $pos = state.doc.resolve(pos)\n\n let currentDepth = maxDepth\n let node: Node | null = null\n\n while (currentDepth > 0 && node === null) {\n const currentNode = $pos.node(currentDepth)\n\n if (currentNode?.type.name === typeOrName) {\n node = currentNode\n } else {\n currentDepth -= 1\n }\n }\n\n return [node, currentDepth] as [Node | null, number]\n}\n","import type { MarkType, NodeType, Schema } from '@tiptap/pm/model'\n\n/**\n * Tries to get a node or mark type by its name.\n * @param name The name of the node or mark type\n * @param schema The Prosemiror schema to search in\n * @returns The node or mark type, or null if it doesn't exist\n */\nexport function getSchemaTypeByName(name: string, schema: Schema): NodeType | MarkType | null {\n return schema.nodes[name] || schema.marks[name] || null\n}\n","import type { ExtensionAttribute } from '../types.js'\n\n/**\n * Return attributes of an extension that should be splitted by keepOnSplit flag\n * @param extensionAttributes Array of extension attributes\n * @param typeName The type of the extension\n * @param attributes The attributes of the extension\n * @returns The splitted attributes\n */\nexport function getSplittedAttributes(\n extensionAttributes: ExtensionAttribute[],\n typeName: string,\n attributes: Record<string, any>,\n): Record<string, any> {\n return Object.fromEntries(\n Object.entries(attributes).filter(([name]) => {\n const extensionAttribute = extensionAttributes.find(item => {\n return item.type === typeName && item.name === name\n })\n\n if (!extensionAttribute) {\n return false\n }\n\n return extensionAttribute.attribute.keepOnSplit\n }),\n )\n}\n","import type { ResolvedPos } from '@tiptap/pm/model'\n\n/**\n * Returns the text content of a resolved prosemirror position\n * @param $from The resolved position to get the text content from\n * @param maxMatch The maximum number of characters to match\n * @returns The text content\n */\nexport const getTextContentFromNodes = ($from: ResolvedPos, maxMatch = 500) => {\n let textBefore = ''\n\n const sliceEndPos = $from.parentOffset\n\n $from.parent.nodesBetween(Math.max(0, sliceEndPos - maxMatch), sliceEndPos, (node, pos, parent, index) => {\n const chunk =\n node.type.spec.toText?.({\n node,\n pos,\n parent,\n index,\n }) ||\n node.textContent ||\n '%leaf%'\n\n textBefore += node.isAtom && !node.isText ? chunk : chunk.slice(0, Math.max(0, sliceEndPos - pos))\n })\n\n return textBefore\n}\n","import type { MarkType } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\n\nimport type { MarkRange } from '../types.js'\nimport { objectIncludes } from '../utilities/objectIncludes.js'\nimport { getMarkType } from './getMarkType.js'\n\nexport function isMarkActive(\n state: EditorState,\n typeOrName: MarkType | string | null,\n attributes: Record<string, any> = {},\n): boolean {\n const { empty, ranges } = state.selection\n const type = typeOrName ? getMarkType(typeOrName, state.schema) : null\n\n if (empty) {\n return !!(state.storedMarks || state.selection.$from.marks())\n .filter(mark => {\n if (!type) {\n return true\n }\n\n return type.name === mark.type.name\n })\n .find(mark => objectIncludes(mark.attrs, attributes, { strict: false }))\n }\n\n let selectionRange = 0\n const markRanges: MarkRange[] = []\n\n ranges.forEach(({ $from, $to }) => {\n const from = $from.pos\n const to = $to.pos\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n if (!node.isText && !node.marks.length) {\n return\n }\n\n const relativeFrom = Math.max(from, pos)\n const relativeTo = Math.min(to, pos + node.nodeSize)\n const range = relativeTo - relativeFrom\n\n selectionRange += range\n\n markRanges.push(\n ...node.marks.map(mark => ({\n mark,\n from: relativeFrom,\n to: relativeTo,\n })),\n )\n })\n })\n\n if (selectionRange === 0) {\n return false\n }\n\n // calculate range of matched mark\n const matchedRange = markRanges\n .filter(markRange => {\n if (!type) {\n return true\n }\n\n return type.name === markRange.mark.type.name\n })\n .filter(markRange => objectIncludes(markRange.mark.attrs, attributes, { strict: false }))\n .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0)\n\n // calculate range of marks that excludes the searched mark\n // for example `code` doesn’t allow any other marks\n const excludedRange = markRanges\n .filter(markRange => {\n if (!type) {\n return true\n }\n\n return markRange.mark.type !== type && markRange.mark.type.excludes(type)\n })\n .reduce((sum, markRange) => sum + markRange.to - markRange.from, 0)\n\n // we only include the result of `excludedRange`\n // if there is a match at all\n const range = matchedRange > 0 ? matchedRange + excludedRange : matchedRange\n\n return range >= selectionRange\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { getSchemaTypeNameByName } from './getSchemaTypeNameByName.js'\nimport { isMarkActive } from './isMarkActive.js'\nimport { isNodeActive } from './isNodeActive.js'\n\nexport function isActive(state: EditorState, name: string | null, attributes: Record<string, any> = {}): boolean {\n if (!name) {\n return isNodeActive(state, null, attributes) || isMarkActive(state, null, attributes)\n }\n\n const schemaType = getSchemaTypeNameByName(name, state.schema)\n\n if (schemaType === 'node') {\n return isNodeActive(state, name, attributes)\n }\n\n if (schemaType === 'mark') {\n return isMarkActive(state, name, attributes)\n }\n\n return false\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nimport { findParentNode } from './findParentNode.js'\n\nexport const isAtEndOfNode = (state: EditorState, nodeType?: string) => {\n const { $from, $to, $anchor } = state.selection\n\n if (nodeType) {\n const parentNode = findParentNode(node => node.type.name === nodeType)(state.selection)\n\n if (!parentNode) {\n return false\n }\n\n const $parentPos = state.doc.resolve(parentNode.pos + 1)\n\n if ($anchor.pos + 1 === $parentPos.end()) {\n return true\n }\n\n return false\n }\n\n if ($to.parentOffset < $to.parent.nodeSize - 2 || $from.pos !== $to.pos) {\n return false\n }\n\n return true\n}\n","import type { EditorState } from '@tiptap/pm/state'\n\nexport const isAtStartOfNode = (state: EditorState) => {\n const { $from, $to } = state.selection\n\n if ($from.parentOffset > 0 || $from.pos !== $to.pos) {\n return false\n }\n\n return true\n}\n","import type { AnyExtension, EnableRules } from '../types.js'\n\nexport function isExtensionRulesEnabled(extension: AnyExtension, enabled: EnableRules): boolean {\n if (Array.isArray(enabled)) {\n return enabled.some(enabledExtension => {\n const name = typeof enabledExtension === 'string' ? enabledExtension : enabledExtension.name\n\n return name === extension.name\n })\n }\n\n return enabled\n}\n","import { getExtensionField } from '../helpers/getExtensionField.js'\nimport type { NodeConfig } from '../index.js'\nimport type { Extensions } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\nimport { splitExtensions } from './splitExtensions.js'\n\nexport function isList(name: string, extensions: Extensions): boolean {\n const { nodeExtensions } = splitExtensions(extensions)\n const extension = nodeExtensions.find(item => item.name === name)\n\n if (!extension) {\n return false\n }\n\n const context = {\n name: extension.name,\n options: extension.options,\n storage: extension.storage,\n }\n const group = callOrReturn(getExtensionField<NodeConfig['group']>(extension, 'group', context))\n\n if (typeof group !== 'string') {\n return false\n }\n\n return group.split(' ').includes('list')\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\n\n/**\n * Returns true if the given prosemirror node is empty.\n */\nexport function isNodeEmpty(\n node: ProseMirrorNode,\n {\n checkChildren = true,\n ignoreWhitespace = false,\n }: {\n /**\n * When true (default), it will also check if all children are empty.\n */\n checkChildren?: boolean\n /**\n * When true, it will ignore whitespace when checking for emptiness.\n */\n ignoreWhitespace?: boolean\n } = {},\n): boolean {\n if (ignoreWhitespace) {\n if (node.type.name === 'hardBreak') {\n // Hard breaks are considered empty\n return true\n }\n if (node.isText) {\n return /^\\s*$/m.test(node.text ?? '')\n }\n }\n\n if (node.isText) {\n return !node.text\n }\n\n if (node.isAtom || node.isLeaf) {\n return false\n }\n\n if (node.content.childCount === 0) {\n return true\n }\n\n if (checkChildren) {\n let isContentEmpty = true\n\n node.content.forEach(childNode => {\n if (isContentEmpty === false) {\n // Exit early for perf\n return\n }\n\n if (!isNodeEmpty(childNode, { ignoreWhitespace, checkChildren })) {\n isContentEmpty = false\n }\n })\n\n return isContentEmpty\n }\n\n return false\n}\n","import { NodeSelection } from '@tiptap/pm/state'\n\nexport function isNodeSelection(value: unknown): value is NodeSelection {\n return value instanceof NodeSelection\n}\n","import type { Transaction } from '@tiptap/pm/state'\nimport type { MapResult } from '@tiptap/pm/transform'\n\n/**\n * A class that represents a mappable position in the editor. It can be extended\n * by other extensions to add additional position mapping capabilities.\n */\nexport class MappablePosition {\n /**\n * The absolute position in the editor.\n */\n public position: number\n\n constructor(position: number) {\n this.position = position\n }\n\n /**\n * Creates a MappablePosition from a JSON object.\n */\n static fromJSON(json: any): MappablePosition {\n return new MappablePosition(json.position)\n }\n\n /**\n * Converts the MappablePosition to a JSON object.\n */\n toJSON(): any {\n return {\n position: this.position,\n }\n }\n}\n\n/**\n * The result of the getUpdatedPosition function.\n */\nexport interface GetUpdatedPositionResult {\n position: MappablePosition\n mapResult: MapResult | null\n}\n\n/**\n * Calculates the new position after applying a transaction.\n *\n * @returns The new mappable position and the map result.\n */\nexport function getUpdatedPosition(position: MappablePosition, transaction: Transaction): GetUpdatedPositionResult {\n const mapResult = transaction.mapping.mapResult(position.position)\n return {\n position: new MappablePosition(mapResult.pos),\n mapResult,\n }\n}\n\n/**\n * Creates a MappablePosition from a position number. This is the default\n * implementation for Tiptap core. It can be overridden by other Tiptap\n * extensions.\n *\n * @param position The position (as a number) where the MappablePosition will be created.\n * @returns A new MappablePosition instance at the given position.\n */\nexport function createMappablePosition(position: number): MappablePosition {\n return new MappablePosition(position)\n}\n","import type { EditorView } from '@tiptap/pm/view'\n\nimport { minMax } from '../utilities/minMax.js'\n\nexport function posToDOMRect(view: EditorView, from: number, to: number): DOMRect {\n const minPos = 0\n const maxPos = view.state.doc.content.size\n const resolvedFrom = minMax(from, minPos, maxPos)\n const resolvedEnd = minMax(to, minPos, maxPos)\n const start = view.coordsAtPos(resolvedFrom)\n const end = view.coordsAtPos(resolvedEnd, -1)\n const top = Math.min(start.top, end.top)\n const bottom = Math.max(start.bottom, end.bottom)\n const left = Math.min(start.left, end.left)\n const right = Math.max(start.right, end.right)\n const width = right - left\n const height = bottom - top\n const x = left\n const y = top\n const data = {\n top,\n bottom,\n left,\n right,\n width,\n height,\n x,\n y,\n }\n\n return {\n ...data,\n toJSON: () => data,\n }\n}\n","import type { Schema } from '@tiptap/pm/model'\n\nimport type { JSONContent } from '../types.js'\n\ntype RewriteUnknownContentOptions = {\n /**\n * If true, unknown nodes will be treated as paragraphs\n * @default true\n */\n fallbackToParagraph?: boolean\n}\n\ntype RewrittenContent = {\n /**\n * The original JSON content that was rewritten\n */\n original: JSONContent\n /**\n * The name of the node or mark that was unsupported\n */\n unsupported: string\n}[]\n\n/**\n * The actual implementation of the rewriteUnknownContent function\n */\nfunction rewriteUnknownContentInner({\n json,\n validMarks,\n validNodes,\n options,\n rewrittenContent = [],\n}: {\n json: JSONContent\n validMarks: Set<string>\n validNodes: Set<string>\n options?: RewriteUnknownContentOptions\n rewrittenContent?: RewrittenContent\n}): {\n /**\n * The cleaned JSON content\n */\n json: JSONContent | null\n /**\n * The array of nodes and marks that were rewritten\n */\n rewrittenContent: RewrittenContent\n} {\n if (json.marks && Array.isArray(json.marks)) {\n json.marks = json.marks.filter(mark => {\n const name = typeof mark === 'string' ? mark : mark.type\n\n if (validMarks.has(name)) {\n return true\n }\n\n rewrittenContent.push({\n original: JSON.parse(JSON.stringify(mark)),\n unsupported: name,\n })\n // Just ignore any unknown marks\n return false\n })\n }\n\n if (json.content && Array.isArray(json.content)) {\n json.content = json.content\n .map(\n value =>\n rewriteUnknownContentInner({\n json: value,\n validMarks,\n validNodes,\n options,\n rewrittenContent,\n }).json,\n )\n .filter(a => a !== null && a !== undefined)\n }\n\n if (json.type && !validNodes.has(json.type)) {\n rewrittenContent.push({\n original: JSON.parse(JSON.stringify(json)),\n unsupported: json.type,\n })\n\n if (json.content && Array.isArray(json.content) && options?.fallbackToParagraph !== false) {\n // Just treat it like a paragraph and hope for the best\n json.type = 'paragraph'\n\n return {\n json,\n rewrittenContent,\n }\n }\n\n // or just omit it entirely\n return {\n json: null,\n rewrittenContent,\n }\n }\n\n return { json, rewrittenContent }\n}\n\n/**\n * Rewrite unknown nodes and marks within JSON content\n * Allowing for user within the editor\n */\nexport function rewriteUnknownContent(\n /**\n * The JSON content to clean of unknown nodes and marks\n */\n json: JSONContent,\n /**\n * The schema to use for validation\n */\n schema: Schema,\n /**\n * Options for the cleaning process\n */\n options?: RewriteUnknownContentOptions,\n): {\n /**\n * The cleaned JSON content\n */\n json: JSONContent | null\n /**\n * The array of nodes and marks that were rewritten\n */\n rewrittenContent: {\n /**\n * The original JSON content that was rewritten\n */\n original: JSONContent\n /**\n * The name of the node or mark that was unsupported\n */\n unsupported: string\n }[]\n} {\n return rewriteUnknownContentInner({\n json,\n validNodes: new Set(Object.keys(schema.nodes)),\n validMarks: new Set(Object.keys(schema.marks)),\n options,\n })\n}\n","import type { MarkType, ResolvedPos } from '@tiptap/pm/model'\nimport type { EditorState, Transaction } from '@tiptap/pm/state'\n\nimport { getMarkAttributes } from '../helpers/getMarkAttributes.js'\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { isTextSelection } from '../helpers/index.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setMark: {\n /**\n * Add a mark with new attributes.\n * @param typeOrName The mark type or name.\n * @example editor.commands.setMark('bold', { level: 1 })\n */\n setMark: (typeOrName: string | MarkType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nfunction canSetMark(state: EditorState, tr: Transaction, newMarkType: MarkType) {\n const { selection } = tr\n let cursor: ResolvedPos | null = null\n\n if (isTextSelection(selection)) {\n cursor = selection.$cursor\n }\n\n if (cursor) {\n const currentMarks = state.storedMarks ?? cursor.marks()\n const parentAllowsMarkType = cursor.parent.type.allowsMarkType(newMarkType)\n\n // There can be no current marks that exclude the new mark, and the parent must allow this mark type\n return (\n parentAllowsMarkType &&\n (!!newMarkType.isInSet(currentMarks) || !currentMarks.some(mark => mark.type.excludes(newMarkType)))\n )\n }\n\n const { ranges } = selection\n\n return ranges.some(({ $from, $to }) => {\n let someNodeSupportsMark =\n $from.depth === 0 ? state.doc.inlineContent && state.doc.type.allowsMarkType(newMarkType) : false\n\n state.doc.nodesBetween($from.pos, $to.pos, (node, _pos, parent) => {\n // If we already found a mark that we can enable, return false to bypass the remaining search\n if (someNodeSupportsMark) {\n return false\n }\n\n if (node.isInline) {\n const parentAllowsMarkType = !parent || parent.type.allowsMarkType(newMarkType)\n const currentMarksAllowMarkType =\n !!newMarkType.isInSet(node.marks) || !node.marks.some(otherMark => otherMark.type.excludes(newMarkType))\n\n someNodeSupportsMark = parentAllowsMarkType && currentMarksAllowMarkType\n }\n return !someNodeSupportsMark\n })\n\n return someNodeSupportsMark\n })\n}\nexport const setMark: RawCommands['setMark'] =\n (typeOrName, attributes = {}) =>\n ({ tr, state, dispatch }) => {\n const { selection } = tr\n const { empty, ranges } = selection\n const type = getMarkType(typeOrName, state.schema)\n\n if (dispatch) {\n if (empty) {\n const oldAttributes = getMarkAttributes(state, type)\n\n tr.addStoredMark(\n type.create({\n ...oldAttributes,\n ...attributes,\n }),\n )\n } else {\n ranges.forEach(range => {\n const from = range.$from.pos\n const to = range.$to.pos\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n const trimmedFrom = Math.max(pos, from)\n const trimmedTo = Math.min(pos + node.nodeSize, to)\n const someHasMark = node.marks.find(mark => mark.type === type)\n\n // if there is already a mark of this type\n // we know that we have to merge its attributes\n // otherwise we add a fresh new mark\n if (someHasMark) {\n node.marks.forEach(mark => {\n if (type === mark.type) {\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n type.create({\n ...mark.attrs,\n ...attributes,\n }),\n )\n }\n })\n } else {\n tr.addMark(trimmedFrom, trimmedTo, type.create(attributes))\n }\n })\n })\n }\n }\n\n return canSetMark(state, tr, type)\n }\n","import type { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setMeta: {\n /**\n * Store a metadata property in the current transaction.\n * @param key The key of the metadata property.\n * @param value The value to store.\n * @example editor.commands.setMeta('foo', 'bar')\n */\n setMeta: (key: string | Plugin | PluginKey, value: any) => ReturnType\n }\n }\n}\n\nexport const setMeta: RawCommands['setMeta'] =\n (key, value) =>\n ({ tr }) => {\n tr.setMeta(key, value)\n\n return true\n }\n","import { setBlockType } from '@tiptap/pm/commands'\nimport type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setNode: {\n /**\n * Replace a given range with a node.\n * @param typeOrName The type or name of the node\n * @param attributes The attributes of the node\n * @example editor.commands.setNode('paragraph')\n */\n setNode: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const setNode: RawCommands['setNode'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch, chain }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n let attributesToCopy: Record<string, any> | undefined\n\n if (state.selection.$anchor.sameParent(state.selection.$head)) {\n // only copy attributes if the selection is pointing to a node of the same type\n attributesToCopy = state.selection.$anchor.parent.attrs\n }\n\n // TODO: use a fallback like insertContent?\n if (!type.isTextblock) {\n console.warn('[tiptap warn]: Currently \"setNode()\" only supports text block nodes.')\n\n return false\n }\n\n return (\n chain()\n // try to convert node to default node if needed\n .command(({ commands }) => {\n const canSetBlock = setBlockType(type, { ...attributesToCopy, ...attributes })(state)\n\n if (canSetBlock) {\n return true\n }\n\n return commands.clearNodes()\n })\n .command(({ state: updatedState }) => {\n return setBlockType(type, { ...attributesToCopy, ...attributes })(updatedState, dispatch)\n })\n .run()\n )\n }\n","import { NodeSelection } from '@tiptap/pm/state'\n\nimport type { RawCommands } from '../types.js'\nimport { minMax } from '../utilities/minMax.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setNodeSelection: {\n /**\n * Creates a NodeSelection.\n * @param position - Position of the node.\n * @example editor.commands.setNodeSelection(10)\n */\n setNodeSelection: (position: number) => ReturnType\n }\n }\n}\n\nexport const setNodeSelection: RawCommands['setNodeSelection'] =\n position =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n const { doc } = tr\n const from = minMax(position, 0, doc.content.size)\n const selection = NodeSelection.create(doc, from)\n\n tr.setSelection(selection)\n }\n\n return true\n }\n","import type { Range, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setTextDirection: {\n /**\n * Set the text direction for nodes.\n * If no position is provided, it will use the current selection.\n * @param direction The text direction to set ('ltr', 'rtl', or 'auto')\n * @param position Optional position or range to apply the direction to\n * @example editor.commands.setTextDirection('rtl')\n * @example editor.commands.setTextDirection('ltr', { from: 0, to: 10 })\n */\n setTextDirection: (direction: 'ltr' | 'rtl' | 'auto', position?: number | Range) => ReturnType\n }\n }\n}\n\nexport const setTextDirection: RawCommands['setTextDirection'] =\n (direction, position) =>\n ({ tr, state, dispatch }) => {\n const { selection } = state\n let from: number\n let to: number\n\n if (typeof position === 'number') {\n from = position\n to = position\n } else if (position && 'from' in position && 'to' in position) {\n from = position.from\n to = position.to\n } else {\n from = selection.from\n to = selection.to\n }\n\n if (dispatch) {\n tr.doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n return\n }\n\n tr.setNodeMarkup(pos, undefined, {\n ...node.attrs,\n dir: direction,\n })\n })\n }\n\n return true\n }\n","import { TextSelection } from '@tiptap/pm/state'\n\nimport type { Range, RawCommands } from '../types.js'\nimport { minMax } from '../utilities/minMax.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n setTextSelection: {\n /**\n * Creates a TextSelection.\n * @param position The position of the selection.\n * @example editor.commands.setTextSelection(10)\n */\n setTextSelection: (position: number | Range) => ReturnType\n }\n }\n}\n\nexport const setTextSelection: RawCommands['setTextSelection'] =\n position =>\n ({ tr, dispatch }) => {\n if (dispatch) {\n const { doc } = tr\n const { from, to } = typeof position === 'number' ? { from: position, to: position } : position\n const minPos = TextSelection.atStart(doc).from\n const maxPos = TextSelection.atEnd(doc).to\n const resolvedFrom = minMax(from, minPos, maxPos)\n const resolvedEnd = minMax(to, minPos, maxPos)\n const selection = TextSelection.create(doc, resolvedFrom, resolvedEnd)\n\n tr.setSelection(selection)\n }\n\n return true\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport { sinkListItem as originalSinkListItem } from '@tiptap/pm/schema-list'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n sinkListItem: {\n /**\n * Sink the list item down into an inner list.\n * @param typeOrName The type or name of the node.\n * @example editor.commands.sinkListItem('listItem')\n */\n sinkListItem: (typeOrName: string | NodeType) => ReturnType\n }\n }\n}\n\nexport const sinkListItem: RawCommands['sinkListItem'] =\n typeOrName =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalSinkListItem(type)(state, dispatch)\n }\n","import type { EditorState } from '@tiptap/pm/state'\nimport { NodeSelection, TextSelection } from '@tiptap/pm/state'\nimport { canSplit } from '@tiptap/pm/transform'\n\nimport { defaultBlockAt } from '../helpers/defaultBlockAt.js'\nimport { getSplittedAttributes } from '../helpers/getSplittedAttributes.js'\nimport type { RawCommands } from '../types.js'\n\nfunction ensureMarks(state: EditorState, splittableMarks?: string[]) {\n const marks = state.storedMarks || (state.selection.$to.parentOffset && state.selection.$from.marks())\n\n if (marks) {\n const filteredMarks = marks.filter(mark => splittableMarks?.includes(mark.type.name))\n\n state.tr.ensureMarks(filteredMarks)\n }\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n splitBlock: {\n /**\n * Forks a new node from an existing node.\n * @param options.keepMarks Keep marks from the previous node.\n * @example editor.commands.splitBlock()\n * @example editor.commands.splitBlock({ keepMarks: true })\n */\n splitBlock: (options?: { keepMarks?: boolean }) => ReturnType\n }\n }\n}\n\nexport const splitBlock: RawCommands['splitBlock'] =\n ({ keepMarks = true } = {}) =>\n ({ tr, state, dispatch, editor }) => {\n const { selection, doc } = tr\n const { $from, $to } = selection\n const extensionAttributes = editor.extensionManager.attributes\n const newAttributes = getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs)\n\n if (selection instanceof NodeSelection && selection.node.isBlock) {\n if (!$from.parentOffset || !canSplit(doc, $from.pos)) {\n return false\n }\n\n if (dispatch) {\n if (keepMarks) {\n ensureMarks(state, editor.extensionManager.splittableMarks)\n }\n\n tr.split($from.pos).scrollIntoView()\n }\n\n return true\n }\n\n if (!$from.parent.isBlock) {\n return false\n }\n\n const atEnd = $to.parentOffset === $to.parent.content.size\n\n const deflt = $from.depth === 0 ? undefined : defaultBlockAt($from.node(-1).contentMatchAt($from.indexAfter(-1)))\n\n let types =\n atEnd && deflt\n ? [\n {\n type: deflt,\n attrs: newAttributes,\n },\n ]\n : undefined\n\n let can = canSplit(tr.doc, tr.mapping.map($from.pos), 1, types)\n\n if (!types && !can && canSplit(tr.doc, tr.mapping.map($from.pos), 1, deflt ? [{ type: deflt }] : undefined)) {\n can = true\n types = deflt\n ? [\n {\n type: deflt,\n attrs: newAttributes,\n },\n ]\n : undefined\n }\n\n if (dispatch) {\n if (can) {\n if (selection instanceof TextSelection) {\n tr.deleteSelection()\n }\n\n tr.split(tr.mapping.map($from.pos), 1, types)\n\n if (deflt && !atEnd && !$from.parentOffset && $from.parent.type !== deflt) {\n const first = tr.mapping.map($from.before())\n const $first = tr.doc.resolve(first)\n\n if ($from.node(-1).canReplaceWith($first.index(), $first.index() + 1, deflt)) {\n tr.setNodeMarkup(tr.mapping.map($from.before()), deflt)\n }\n }\n }\n\n if (keepMarks) {\n ensureMarks(state, editor.extensionManager.splittableMarks)\n }\n\n tr.scrollIntoView()\n }\n\n return can\n }\n","import type { Node as ProseMirrorNode, NodeType } from '@tiptap/pm/model'\nimport { Fragment, Slice } from '@tiptap/pm/model'\nimport { TextSelection } from '@tiptap/pm/state'\nimport { canSplit } from '@tiptap/pm/transform'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { getSplittedAttributes } from '../helpers/getSplittedAttributes.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n splitListItem: {\n /**\n * Splits one list item into two list items.\n * @param typeOrName The type or name of the node.\n * @param overrideAttrs The attributes to ensure on the new node.\n * @example editor.commands.splitListItem('listItem')\n */\n splitListItem: (typeOrName: string | NodeType, overrideAttrs?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const splitListItem: RawCommands['splitListItem'] =\n (typeOrName, overrideAttrs = {}) =>\n ({ tr, state, dispatch, editor }) => {\n const type = getNodeType(typeOrName, state.schema)\n const { $from, $to } = state.selection\n\n // @ts-ignore\n // eslint-disable-next-line\n const node: ProseMirrorNode = state.selection.node\n\n if ((node && node.isBlock) || $from.depth < 2 || !$from.sameParent($to)) {\n return false\n }\n\n const grandParent = $from.node(-1)\n\n if (grandParent.type !== type) {\n return false\n }\n\n const extensionAttributes = editor.extensionManager.attributes\n\n if ($from.parent.content.size === 0 && $from.node(-1).childCount === $from.indexAfter(-1)) {\n // In an empty block. If this is a nested list, the wrapping\n // list item should be split. Otherwise, bail out and let next\n // command handle lifting.\n if ($from.depth === 2 || $from.node(-3).type !== type || $from.index(-2) !== $from.node(-2).childCount - 1) {\n return false\n }\n\n if (dispatch) {\n let wrap = Fragment.empty\n // eslint-disable-next-line\n const depthBefore = $from.index(-1) ? 1 : $from.index(-2) ? 2 : 3\n\n // Build a fragment containing empty versions of the structure\n // from the outer list item to the parent node of the cursor\n for (let d = $from.depth - depthBefore; d >= $from.depth - 3; d -= 1) {\n wrap = Fragment.from($from.node(d).copy(wrap))\n }\n\n const depthAfter =\n // eslint-disable-next-line no-nested-ternary\n $from.indexAfter(-1) < $from.node(-2).childCount\n ? 1\n : $from.indexAfter(-2) < $from.node(-3).childCount\n ? 2\n : 3\n\n // Add a second list item with an empty default start node\n const newNextTypeAttributes = {\n ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),\n ...overrideAttrs,\n }\n const nextType = type.contentMatch.defaultType?.createAndFill(newNextTypeAttributes) || undefined\n\n wrap = wrap.append(Fragment.from(type.createAndFill(null, nextType) || undefined))\n\n const start = $from.before($from.depth - (depthBefore - 1))\n\n tr.replace(start, $from.after(-depthAfter), new Slice(wrap, 4 - depthBefore, 0))\n\n let sel = -1\n\n tr.doc.nodesBetween(start, tr.doc.content.size, (n, pos) => {\n if (sel > -1) {\n return false\n }\n\n if (n.isTextblock && n.content.size === 0) {\n sel = pos + 1\n }\n })\n\n if (sel > -1) {\n tr.setSelection(TextSelection.near(tr.doc.resolve(sel)))\n }\n\n tr.scrollIntoView()\n }\n\n return true\n }\n\n const nextType = $to.pos === $from.end() ? grandParent.contentMatchAt(0).defaultType : null\n\n const newTypeAttributes = {\n ...getSplittedAttributes(extensionAttributes, grandParent.type.name, grandParent.attrs),\n ...overrideAttrs,\n }\n const newNextTypeAttributes = {\n ...getSplittedAttributes(extensionAttributes, $from.node().type.name, $from.node().attrs),\n ...overrideAttrs,\n }\n\n tr.delete($from.pos, $to.pos)\n\n const types = nextType\n ? [\n { type, attrs: newTypeAttributes },\n { type: nextType, attrs: newNextTypeAttributes },\n ]\n : [{ type, attrs: newTypeAttributes }]\n\n if (!canSplit(tr.doc, $from.pos, 2)) {\n return false\n }\n\n if (dispatch) {\n const { selection, storedMarks } = state\n const { splittableMarks } = editor.extensionManager\n const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks())\n\n tr.split($from.pos, 2, types).scrollIntoView()\n\n if (!marks || !dispatch) {\n return true\n }\n\n const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name))\n\n tr.ensureMarks(filteredMarks)\n }\n\n return true\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport type { Transaction } from '@tiptap/pm/state'\nimport { canJoin } from '@tiptap/pm/transform'\n\nimport { findParentNode } from '../helpers/findParentNode.js'\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isList } from '../helpers/isList.js'\nimport type { RawCommands } from '../types.js'\n\nconst joinListBackwards = (tr: Transaction, listType: NodeType): boolean => {\n const list = findParentNode(node => node.type === listType)(tr.selection)\n\n if (!list) {\n return true\n }\n\n const before = tr.doc.resolve(Math.max(0, list.pos - 1)).before(list.depth)\n\n if (before === undefined) {\n return true\n }\n\n const nodeBefore = tr.doc.nodeAt(before)\n const canJoinBackwards = list.node.type === nodeBefore?.type && canJoin(tr.doc, list.pos)\n\n if (!canJoinBackwards) {\n return true\n }\n\n tr.join(list.pos)\n\n return true\n}\n\nconst joinListForwards = (tr: Transaction, listType: NodeType): boolean => {\n const list = findParentNode(node => node.type === listType)(tr.selection)\n\n if (!list) {\n return true\n }\n\n const after = tr.doc.resolve(list.start).after(list.depth)\n\n if (after === undefined) {\n return true\n }\n\n const nodeAfter = tr.doc.nodeAt(after)\n const canJoinForwards = list.node.type === nodeAfter?.type && canJoin(tr.doc, after)\n\n if (!canJoinForwards) {\n return true\n }\n\n tr.join(after)\n\n return true\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleList: {\n /**\n * Toggle between different list types.\n * @param listTypeOrName The type or name of the list.\n * @param itemTypeOrName The type or name of the list item.\n * @param keepMarks Keep marks when toggling.\n * @param attributes Attributes for the new list.\n * @example editor.commands.toggleList('bulletList', 'listItem')\n */\n toggleList: (\n listTypeOrName: string | NodeType,\n itemTypeOrName: string | NodeType,\n keepMarks?: boolean,\n attributes?: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const toggleList: RawCommands['toggleList'] =\n (listTypeOrName, itemTypeOrName, keepMarks, attributes = {}) =>\n ({ editor, tr, state, dispatch, chain, commands, can }) => {\n const { extensions, splittableMarks } = editor.extensionManager\n const listType = getNodeType(listTypeOrName, state.schema)\n const itemType = getNodeType(itemTypeOrName, state.schema)\n const { selection, storedMarks } = state\n const { $from, $to } = selection\n const range = $from.blockRange($to)\n\n const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks())\n\n if (!range) {\n return false\n }\n\n const parentList = findParentNode(node => isList(node.type.name, extensions))(selection)\n\n if (range.depth >= 1 && parentList && range.depth - parentList.depth <= 1) {\n // remove list\n if (parentList.node.type === listType) {\n return commands.liftListItem(itemType)\n }\n\n // change list type\n if (isList(parentList.node.type.name, extensions) && listType.validContent(parentList.node.content) && dispatch) {\n return chain()\n .command(() => {\n tr.setNodeMarkup(parentList.pos, listType)\n\n return true\n })\n .command(() => joinListBackwards(tr, listType))\n .command(() => joinListForwards(tr, listType))\n .run()\n }\n }\n if (!keepMarks || !marks || !dispatch) {\n return (\n chain()\n // try to convert node to default node if needed\n .command(() => {\n const canWrapInList = can().wrapInList(listType, attributes)\n\n if (canWrapInList) {\n return true\n }\n\n return commands.clearNodes()\n })\n .wrapInList(listType, attributes)\n .command(() => joinListBackwards(tr, listType))\n .command(() => joinListForwards(tr, listType))\n .run()\n )\n }\n\n return (\n chain()\n // try to convert node to default node if needed\n .command(() => {\n const canWrapInList = can().wrapInList(listType, attributes)\n\n const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name))\n\n tr.ensureMarks(filteredMarks)\n\n if (canWrapInList) {\n return true\n }\n\n return commands.clearNodes()\n })\n .wrapInList(listType, attributes)\n .command(() => joinListBackwards(tr, listType))\n .command(() => joinListForwards(tr, listType))\n .run()\n )\n }\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { isMarkActive } from '../helpers/isMarkActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleMark: {\n /**\n * Toggle a mark on and off.\n * @param typeOrName The mark type or name.\n * @param attributes The attributes of the mark.\n * @param options.extendEmptyMarkRange Removes the mark even across the current selection. Defaults to `false`.\n * @example editor.commands.toggleMark('bold')\n */\n toggleMark: (\n /**\n * The mark type or name.\n */\n typeOrName: string | MarkType,\n\n /**\n * The attributes of the mark.\n */\n attributes?: Record<string, any>,\n\n options?: {\n /**\n * Removes the mark even across the current selection. Defaults to `false`.\n */\n extendEmptyMarkRange?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\nexport const toggleMark: RawCommands['toggleMark'] =\n (typeOrName, attributes = {}, options = {}) =>\n ({ state, commands }) => {\n const { extendEmptyMarkRange = false } = options\n const type = getMarkType(typeOrName, state.schema)\n const isActive = isMarkActive(state, type, attributes)\n\n if (isActive) {\n return commands.unsetMark(type, { extendEmptyMarkRange })\n }\n\n return commands.setMark(type, attributes)\n }\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isNodeActive } from '../helpers/isNodeActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleNode: {\n /**\n * Toggle a node with another node.\n * @param typeOrName The type or name of the node.\n * @param toggleTypeOrName The type or name of the node to toggle.\n * @param attributes The attributes of the node.\n * @example editor.commands.toggleNode('heading', 'paragraph')\n */\n toggleNode: (\n typeOrName: string | NodeType,\n toggleTypeOrName: string | NodeType,\n attributes?: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const toggleNode: RawCommands['toggleNode'] =\n (typeOrName, toggleTypeOrName, attributes = {}) =>\n ({ state, commands }) => {\n const type = getNodeType(typeOrName, state.schema)\n const toggleType = getNodeType(toggleTypeOrName, state.schema)\n const isActive = isNodeActive(state, type, attributes)\n\n let attributesToCopy: Record<string, any> | undefined\n\n if (state.selection.$anchor.sameParent(state.selection.$head)) {\n // only copy attributes if the selection is pointing to a node of the same type\n attributesToCopy = state.selection.$anchor.parent.attrs\n }\n\n if (isActive) {\n return commands.setNode(toggleType, attributesToCopy)\n }\n\n // If the node is not active, we want to set the new node type with the given attributes\n // Copying over the attributes from the current node if the selection is pointing to a node of the same type\n return commands.setNode(type, { ...attributesToCopy, ...attributes })\n }\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { isNodeActive } from '../helpers/isNodeActive.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n toggleWrap: {\n /**\n * Wraps nodes in another node, or removes an existing wrap.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.toggleWrap('blockquote')\n */\n toggleWrap: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const toggleWrap: RawCommands['toggleWrap'] =\n (typeOrName, attributes = {}) =>\n ({ state, commands }) => {\n const type = getNodeType(typeOrName, state.schema)\n const isActive = isNodeActive(state, type, attributes)\n\n if (isActive) {\n return commands.lift(type)\n }\n\n return commands.wrapIn(type, attributes)\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n undoInputRule: {\n /**\n * Undo an input rule.\n * @example editor.commands.undoInputRule()\n */\n undoInputRule: () => ReturnType\n }\n }\n}\n\nexport const undoInputRule: RawCommands['undoInputRule'] =\n () =>\n ({ state, dispatch }) => {\n const plugins = state.plugins\n\n for (let i = 0; i < plugins.length; i += 1) {\n const plugin = plugins[i]\n let undoable\n\n // @ts-ignore\n // eslint-disable-next-line\n if (plugin.spec.isInputRules && (undoable = plugin.getState(state))) {\n if (dispatch) {\n const tr = state.tr\n const toUndo = undoable.transform\n\n for (let j = toUndo.steps.length - 1; j >= 0; j -= 1) {\n tr.step(toUndo.steps[j].invert(toUndo.docs[j]))\n }\n\n if (undoable.text) {\n const marks = tr.doc.resolve(undoable.from).marks()\n\n tr.replaceWith(undoable.from, undoable.to, state.schema.text(undoable.text, marks))\n } else {\n tr.delete(undoable.from, undoable.to)\n }\n }\n\n return true\n }\n }\n\n return false\n }\n","import type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n unsetAllMarks: {\n /**\n * Remove all marks in the current selection.\n * @example editor.commands.unsetAllMarks()\n */\n unsetAllMarks: () => ReturnType\n }\n }\n}\n\nexport const unsetAllMarks: RawCommands['unsetAllMarks'] =\n () =>\n ({ tr, dispatch }) => {\n const { selection } = tr\n const { empty, ranges } = selection\n\n if (empty) {\n return true\n }\n\n if (dispatch) {\n ranges.forEach(range => {\n tr.removeMark(range.$from.pos, range.$to.pos)\n })\n }\n\n return true\n }\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarkRange } from '../helpers/getMarkRange.js'\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n unsetMark: {\n /**\n * Remove all marks in the current selection.\n * @param typeOrName The mark type or name.\n * @param options.extendEmptyMarkRange Removes the mark even across the current selection. Defaults to `false`.\n * @example editor.commands.unsetMark('bold')\n */\n unsetMark: (\n /**\n * The mark type or name.\n */\n typeOrName: string | MarkType,\n\n options?: {\n /**\n * Removes the mark even across the current selection. Defaults to `false`.\n */\n extendEmptyMarkRange?: boolean\n },\n ) => ReturnType\n }\n }\n}\n\nexport const unsetMark: RawCommands['unsetMark'] =\n (typeOrName, options = {}) =>\n ({ tr, state, dispatch }) => {\n const { extendEmptyMarkRange = false } = options\n const { selection } = tr\n const type = getMarkType(typeOrName, state.schema)\n const { $from, empty, ranges } = selection\n\n if (!dispatch) {\n return true\n }\n\n if (empty && extendEmptyMarkRange) {\n let { from, to } = selection\n const attrs = $from.marks().find(mark => mark.type === type)?.attrs\n const range = getMarkRange($from, type, attrs)\n\n if (range) {\n from = range.from\n to = range.to\n }\n\n tr.removeMark(from, to, type)\n } else {\n ranges.forEach(range => {\n tr.removeMark(range.$from.pos, range.$to.pos, type)\n })\n }\n\n tr.removeStoredMark(type)\n\n return true\n }\n","import type { Range, RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n unsetTextDirection: {\n /**\n * Remove the text direction attribute from nodes.\n * If no position is provided, it will use the current selection.\n * @param position Optional position or range to remove the direction from\n * @example editor.commands.unsetTextDirection()\n * @example editor.commands.unsetTextDirection({ from: 0, to: 10 })\n */\n unsetTextDirection: (position?: number | Range) => ReturnType\n }\n }\n}\n\nexport const unsetTextDirection: RawCommands['unsetTextDirection'] =\n position =>\n ({ tr, state, dispatch }) => {\n const { selection } = state\n let from: number\n let to: number\n\n if (typeof position === 'number') {\n from = position\n to = position\n } else if (position && 'from' in position && 'to' in position) {\n from = position.from\n to = position.to\n } else {\n from = selection.from\n to = selection.to\n }\n\n if (dispatch) {\n tr.doc.nodesBetween(from, to, (node, pos) => {\n if (node.isText) {\n return\n }\n\n const newAttrs = { ...node.attrs }\n\n delete newAttrs.dir\n\n tr.setNodeMarkup(pos, undefined, newAttrs)\n })\n }\n\n return true\n }\n","import type { Mark, MarkType, Node, NodeType } from '@tiptap/pm/model'\nimport type { SelectionRange } from '@tiptap/pm/state'\n\nimport { getMarkType } from '../helpers/getMarkType.js'\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport { getSchemaTypeNameByName } from '../helpers/getSchemaTypeNameByName.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n updateAttributes: {\n /**\n * Update attributes of a node or mark.\n * @param typeOrName The type or name of the node or mark.\n * @param attributes The attributes of the node or mark.\n * @example editor.commands.updateAttributes('mention', { userId: \"2\" })\n */\n updateAttributes: (\n /**\n * The type or name of the node or mark.\n */\n typeOrName: string | NodeType | MarkType,\n\n /**\n * The attributes of the node or mark.\n */\n attributes: Record<string, any>,\n ) => ReturnType\n }\n }\n}\n\nexport const updateAttributes: RawCommands['updateAttributes'] =\n (typeOrName, attributes = {}) =>\n ({ tr, state, dispatch }) => {\n let nodeType: NodeType | null = null\n let markType: MarkType | null = null\n\n const schemaType = getSchemaTypeNameByName(\n typeof typeOrName === 'string' ? typeOrName : typeOrName.name,\n state.schema,\n )\n\n if (!schemaType) {\n return false\n }\n\n if (schemaType === 'node') {\n nodeType = getNodeType(typeOrName as NodeType, state.schema)\n }\n\n if (schemaType === 'mark') {\n markType = getMarkType(typeOrName as MarkType, state.schema)\n }\n\n let canUpdate = false\n\n tr.selection.ranges.forEach((range: SelectionRange) => {\n const from = range.$from.pos\n const to = range.$to.pos\n\n let lastPos: number | undefined\n let lastNode: Node | undefined\n let trimmedFrom: number\n let trimmedTo: number\n\n if (tr.selection.empty) {\n state.doc.nodesBetween(from, to, (node: Node, pos: number) => {\n if (nodeType && nodeType === node.type) {\n canUpdate = true\n trimmedFrom = Math.max(pos, from)\n trimmedTo = Math.min(pos + node.nodeSize, to)\n lastPos = pos\n lastNode = node\n }\n })\n } else {\n state.doc.nodesBetween(from, to, (node: Node, pos: number) => {\n if (pos < from && nodeType && nodeType === node.type) {\n canUpdate = true\n trimmedFrom = Math.max(pos, from)\n trimmedTo = Math.min(pos + node.nodeSize, to)\n lastPos = pos\n lastNode = node\n }\n\n if (pos >= from && pos <= to) {\n if (nodeType && nodeType === node.type) {\n canUpdate = true\n\n if (dispatch) {\n tr.setNodeMarkup(pos, undefined, {\n ...node.attrs,\n ...attributes,\n })\n }\n }\n\n if (markType && node.marks.length) {\n node.marks.forEach((mark: Mark) => {\n if (markType === mark.type) {\n canUpdate = true\n\n if (dispatch) {\n const trimmedFrom2 = Math.max(pos, from)\n const trimmedTo2 = Math.min(pos + node.nodeSize, to)\n\n tr.addMark(\n trimmedFrom2,\n trimmedTo2,\n markType.create({\n ...mark.attrs,\n ...attributes,\n }),\n )\n }\n }\n })\n }\n }\n })\n }\n\n if (lastNode) {\n if (lastPos !== undefined && dispatch) {\n tr.setNodeMarkup(lastPos, undefined, {\n ...lastNode.attrs,\n ...attributes,\n })\n }\n\n if (markType && lastNode.marks.length) {\n lastNode.marks.forEach((mark: Mark) => {\n if (markType === mark.type && dispatch) {\n tr.addMark(\n trimmedFrom,\n trimmedTo,\n markType.create({\n ...mark.attrs,\n ...attributes,\n }),\n )\n }\n })\n }\n }\n })\n\n return canUpdate\n }\n","import { wrapIn as originalWrapIn } from '@tiptap/pm/commands'\nimport type { NodeType } from '@tiptap/pm/model'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n wrapIn: {\n /**\n * Wraps nodes in another node.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.wrapIn('blockquote')\n */\n wrapIn: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const wrapIn: RawCommands['wrapIn'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalWrapIn(type, attributes)(state, dispatch)\n }\n","import type { NodeType } from '@tiptap/pm/model'\nimport { wrapInList as originalWrapInList } from '@tiptap/pm/schema-list'\n\nimport { getNodeType } from '../helpers/getNodeType.js'\nimport type { RawCommands } from '../types.js'\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n wrapInList: {\n /**\n * Wrap a node in a list.\n * @param typeOrName The type or name of the node.\n * @param attributes The attributes of the node.\n * @example editor.commands.wrapInList('bulletList')\n */\n wrapInList: (typeOrName: string | NodeType, attributes?: Record<string, any>) => ReturnType\n }\n }\n}\n\nexport const wrapInList: RawCommands['wrapInList'] =\n (typeOrName, attributes = {}) =>\n ({ state, dispatch }) => {\n const type = getNodeType(typeOrName, state.schema)\n\n return originalWrapInList(type, attributes)(state, dispatch)\n }\n","/* eslint-disable @typescript-eslint/no-empty-object-type */\nimport type { MarkType, Node as ProseMirrorNode, NodeType, Schema } from '@tiptap/pm/model'\nimport type { Plugin, PluginKey, Transaction } from '@tiptap/pm/state'\nimport { EditorState } from '@tiptap/pm/state'\nimport { EditorView } from '@tiptap/pm/view'\n\nimport { CommandManager } from './CommandManager.js'\nimport { EventEmitter } from './EventEmitter.js'\nimport { ExtensionManager } from './ExtensionManager.js'\nimport {\n ClipboardTextSerializer,\n Commands,\n Delete,\n Drop,\n Editable,\n FocusEvents,\n Keymap,\n Paste,\n Tabindex,\n TextDirection,\n} from './extensions/index.js'\nimport { createDocument } from './helpers/createDocument.js'\nimport { getAttributes } from './helpers/getAttributes.js'\nimport { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'\nimport { getText } from './helpers/getText.js'\nimport { getTextSerializersFromSchema } from './helpers/getTextSerializersFromSchema.js'\nimport { isActive } from './helpers/isActive.js'\nimport { isNodeEmpty } from './helpers/isNodeEmpty.js'\nimport { createMappablePosition, getUpdatedPosition } from './helpers/MappablePosition.js'\nimport { resolveFocusPosition } from './helpers/resolveFocusPosition.js'\nimport type { Storage } from './index.js'\nimport { NodePos } from './NodePos.js'\nimport { style } from './style.js'\nimport type {\n CanCommands,\n ChainedCommands,\n DocumentType,\n EditorEvents,\n EditorOptions,\n NodeType as TNodeType,\n SingleCommands,\n TextSerializer,\n TextType as TTextType,\n Utils,\n} from './types.js'\nimport { createStyleTag } from './utilities/createStyleTag.js'\nimport { isFunction } from './utilities/isFunction.js'\n\nexport * as extensions from './extensions/index.js'\n\n// @ts-ignore\nexport interface TiptapEditorHTMLElement extends HTMLElement {\n editor?: Editor\n}\n\nexport class Editor extends EventEmitter<EditorEvents> {\n private commandManager!: CommandManager\n\n public extensionManager!: ExtensionManager\n\n private css: HTMLStyleElement | null = null\n\n private className = 'tiptap'\n\n public schema!: Schema\n\n private editorView: EditorView | null = null\n\n public isFocused = false\n\n private editorState!: EditorState\n\n /**\n * The editor is considered initialized after the `create` event has been emitted.\n */\n public isInitialized = false\n\n public extensionStorage: Storage = {} as Storage\n\n /**\n * A unique ID for this editor instance.\n */\n public instanceId = Math.random().toString(36).slice(2, 9)\n\n public options: EditorOptions = {\n element: typeof document !== 'undefined' ? document.createElement('div') : null,\n content: '',\n injectCSS: true,\n injectNonce: undefined,\n extensions: [],\n autofocus: false,\n editable: true,\n textDirection: undefined,\n editorProps: {},\n parseOptions: {},\n coreExtensionOptions: {},\n enableInputRules: true,\n enablePasteRules: true,\n enableCoreExtensions: true,\n enableContentCheck: false,\n emitContentError: false,\n onBeforeCreate: () => null,\n onCreate: () => null,\n onMount: () => null,\n onUnmount: () => null,\n onUpdate: () => null,\n onSelectionUpdate: () => null,\n onTransaction: () => null,\n onFocus: () => null,\n onBlur: () => null,\n onDestroy: () => null,\n onContentError: ({ error }) => {\n throw error\n },\n onPaste: () => null,\n onDrop: () => null,\n onDelete: () => null,\n enableExtensionDispatchTransaction: true,\n }\n\n constructor(options: Partial<EditorOptions> = {}) {\n super()\n this.setOptions(options)\n this.createExtensionManager()\n this.createCommandManager()\n this.createSchema()\n this.on('beforeCreate', this.options.onBeforeCreate)\n this.emit('beforeCreate', { editor: this })\n this.on('mount', this.options.onMount)\n this.on('unmount', this.options.onUnmount)\n this.on('contentError', this.options.onContentError)\n this.on('create', this.options.onCreate)\n this.on('update', this.options.onUpdate)\n this.on('selectionUpdate', this.options.onSelectionUpdate)\n this.on('transaction', this.options.onTransaction)\n this.on('focus', this.options.onFocus)\n this.on('blur', this.options.onBlur)\n this.on('destroy', this.options.onDestroy)\n this.on('drop', ({ event, slice, moved }) => this.options.onDrop(event, slice, moved))\n this.on('paste', ({ event, slice }) => this.options.onPaste(event, slice))\n this.on('delete', this.options.onDelete)\n\n const initialDoc = this.createDoc()\n const selection = resolveFocusPosition(initialDoc, this.options.autofocus)\n\n // Set editor state immediately, so that it's available independently from the view\n this.editorState = EditorState.create({\n doc: initialDoc,\n schema: this.schema,\n selection: selection || undefined,\n })\n\n if (this.options.element) {\n this.mount(this.options.element)\n }\n }\n\n /**\n * Attach the editor to the DOM, creating a new editor view.\n */\n public mount(el: NonNullable<EditorOptions['element']> & {}) {\n if (typeof document === 'undefined') {\n throw new Error(\n `[tiptap error]: The editor cannot be mounted because there is no 'document' defined in this environment.`,\n )\n }\n this.createView(el)\n this.emit('mount', { editor: this })\n\n if (this.css && !document.head.contains(this.css)) {\n document.head.appendChild(this.css)\n }\n\n window.setTimeout(() => {\n if (this.isDestroyed) {\n return\n }\n\n if (this.options.autofocus !== false && this.options.autofocus !== null) {\n this.commands.focus(this.options.autofocus)\n }\n this.emit('create', { editor: this })\n this.isInitialized = true\n }, 0)\n }\n\n /**\n * Remove the editor from the DOM, but still allow remounting at a different point in time\n */\n public unmount() {\n if (this.editorView) {\n // Cleanup our reference to prevent circular references which caused memory leaks\n // @ts-ignore\n const dom = this.editorView.dom as TiptapEditorHTMLElement\n\n if (dom?.editor) {\n delete dom.editor\n }\n this.editorView.destroy()\n }\n this.editorView = null\n this.isInitialized = false\n\n // Safely remove CSS element with fallback for test environments\n // Only remove CSS if no other editors exist in the document after unmount\n if (this.css && !document.querySelectorAll(`.${this.className}`).length) {\n try {\n if (typeof this.css.remove === 'function') {\n this.css.remove()\n } else if (this.css.parentNode) {\n this.css.parentNode.removeChild(this.css)\n }\n } catch (error) {\n // Silently handle any unexpected DOM removal errors in test environments\n console.warn('Failed to remove CSS element:', error)\n }\n }\n this.css = null\n this.emit('unmount', { editor: this })\n }\n\n /**\n * Returns the editor storage.\n */\n public get storage(): Storage {\n return this.extensionStorage\n }\n\n /**\n * An object of all registered commands.\n */\n public get commands(): SingleCommands {\n return this.commandManager.commands\n }\n\n /**\n * Create a command chain to call multiple commands at once.\n */\n public chain(): ChainedCommands {\n return this.commandManager.chain()\n }\n\n /**\n * Check if a command or a command chain can be executed. Without executing it.\n */\n public can(): CanCommands {\n return this.commandManager.can()\n }\n\n /**\n * Inject CSS styles.\n */\n private injectCSS(): void {\n if (this.options.injectCSS && typeof document !== 'undefined') {\n this.css = createStyleTag(style, this.options.injectNonce)\n }\n }\n\n /**\n * Update editor options.\n *\n * @param options A list of options\n */\n public setOptions(options: Partial<EditorOptions> = {}): void {\n this.options = {\n ...this.options,\n ...options,\n }\n\n if (!this.editorView || !this.state || this.isDestroyed) {\n return\n }\n\n if (this.options.editorProps) {\n this.view.setProps(this.options.editorProps)\n }\n\n this.view.updateState(this.state)\n }\n\n /**\n * Update editable state of the editor.\n */\n public setEditable(editable: boolean, emitUpdate = true): void {\n this.setOptions({ editable })\n\n if (emitUpdate) {\n this.emit('update', { editor: this, transaction: this.state.tr, appendedTransactions: [] })\n }\n }\n\n /**\n * Returns whether the editor is editable.\n */\n public get isEditable(): boolean {\n // since plugins are applied after creating the view\n // `editable` is always `true` for one tick.\n // that’s why we also have to check for `options.editable`\n return this.options.editable && this.view && this.view.editable\n }\n\n /**\n * Returns the editor state.\n */\n public get view(): EditorView {\n if (this.editorView) {\n return this.editorView\n }\n\n return new Proxy(\n {\n state: this.editorState,\n updateState: (state: EditorState): ReturnType<EditorView['updateState']> => {\n this.editorState = state\n },\n dispatch: (tr: Transaction): ReturnType<EditorView['dispatch']> => {\n this.dispatchTransaction(tr)\n },\n\n // Stub some commonly accessed properties to prevent errors\n composing: false,\n dragging: null,\n editable: true,\n isDestroyed: false,\n } as EditorView,\n {\n get: (obj, key) => {\n if (this.editorView) {\n // If the editor view is available, but the caller has a stale reference to the proxy,\n // Just return what the editor view has.\n return this.editorView[key as keyof EditorView]\n }\n // Specifically always return the most recent editorState\n if (key === 'state') {\n return this.editorState\n }\n if (key in obj) {\n return Reflect.get(obj, key)\n }\n\n // We throw an error here, because we know the view is not available\n throw new Error(\n `[tiptap error]: The editor view is not available. Cannot access view['${key as string}']. The editor may not be mounted yet.`,\n )\n },\n },\n ) as EditorView\n }\n\n /**\n * Returns the editor state.\n */\n public get state(): EditorState {\n if (this.editorView) {\n this.editorState = this.view.state\n }\n\n return this.editorState\n }\n\n /**\n * Register a ProseMirror plugin.\n *\n * @param plugin A ProseMirror plugin\n * @param handlePlugins Control how to merge the plugin into the existing plugins.\n * @returns The new editor state\n */\n public registerPlugin(\n plugin: Plugin,\n handlePlugins?: (newPlugin: Plugin, plugins: Plugin[]) => Plugin[],\n ): EditorState {\n const plugins = isFunction(handlePlugins)\n ? handlePlugins(plugin, [...this.state.plugins])\n : [...this.state.plugins, plugin]\n\n const state = this.state.reconfigure({ plugins })\n\n this.view.updateState(state)\n\n return state\n }\n\n /**\n * Unregister a ProseMirror plugin.\n *\n * @param nameOrPluginKeyToRemove The plugins name\n * @returns The new editor state or undefined if the editor is destroyed\n */\n public unregisterPlugin(\n nameOrPluginKeyToRemove: string | PluginKey | (string | PluginKey)[],\n ): EditorState | undefined {\n if (this.isDestroyed) {\n return undefined\n }\n\n const prevPlugins = this.state.plugins\n let plugins = prevPlugins\n\n ;([] as (string | PluginKey)[]).concat(nameOrPluginKeyToRemove).forEach(nameOrPluginKey => {\n // @ts-ignore\n const name = typeof nameOrPluginKey === 'string' ? `${nameOrPluginKey}$` : nameOrPluginKey.key\n\n // @ts-ignore\n plugins = plugins.filter(plugin => !plugin.key.startsWith(name))\n })\n\n if (prevPlugins.length === plugins.length) {\n // No plugin was removed, so we don’t need to update the state\n return undefined\n }\n\n const state = this.state.reconfigure({\n plugins,\n })\n\n this.view.updateState(state)\n\n return state\n }\n\n /**\n * Creates an extension manager.\n */\n private createExtensionManager(): void {\n const coreExtensions = this.options.enableCoreExtensions\n ? [\n Editable,\n ClipboardTextSerializer.configure({\n blockSeparator: this.options.coreExtensionOptions?.clipboardTextSerializer?.blockSeparator,\n }),\n Commands,\n FocusEvents,\n Keymap,\n Tabindex,\n Drop,\n Paste,\n Delete,\n TextDirection.configure({\n direction: this.options.textDirection,\n }),\n ].filter(ext => {\n if (typeof this.options.enableCoreExtensions === 'object') {\n return (\n this.options.enableCoreExtensions[ext.name as keyof typeof this.options.enableCoreExtensions] !== false\n )\n }\n return true\n })\n : []\n const allExtensions = [...coreExtensions, ...this.options.extensions].filter(extension => {\n return ['extension', 'node', 'mark'].includes(extension?.type)\n })\n\n this.extensionManager = new ExtensionManager(allExtensions, this)\n }\n\n /**\n * Creates an command manager.\n */\n private createCommandManager(): void {\n this.commandManager = new CommandManager({\n editor: this,\n })\n }\n\n /**\n * Creates a ProseMirror schema.\n */\n private createSchema(): void {\n this.schema = this.extensionManager.schema\n }\n\n /**\n * Creates the initial document.\n */\n private createDoc(): ProseMirrorNode {\n let doc: ProseMirrorNode\n\n try {\n doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {\n errorOnInvalidContent: this.options.enableContentCheck,\n })\n } catch (e) {\n if (\n !(e instanceof Error) ||\n !['[tiptap error]: Invalid JSON content', '[tiptap error]: Invalid HTML content'].includes(e.message)\n ) {\n // Not the content error we were expecting\n throw e\n }\n this.emit('contentError', {\n editor: this,\n error: e as Error,\n disableCollaboration: () => {\n if (\n 'collaboration' in this.storage &&\n typeof this.storage.collaboration === 'object' &&\n this.storage.collaboration\n ) {\n ;(this.storage.collaboration as any).isDisabled = true\n }\n // To avoid syncing back invalid content, reinitialize the extensions without the collaboration extension\n this.options.extensions = this.options.extensions.filter(extension => extension.name !== 'collaboration')\n\n // Restart the initialization process by recreating the extension manager with the new set of extensions\n this.createExtensionManager()\n },\n })\n\n // Content is invalid, but attempt to create it anyway, stripping out the invalid parts\n doc = createDocument(this.options.content, this.schema, this.options.parseOptions, {\n errorOnInvalidContent: false,\n })\n }\n return doc\n }\n\n /**\n * Creates a ProseMirror view.\n */\n private createView(element: NonNullable<EditorOptions['element']>): void {\n const { editorProps, enableExtensionDispatchTransaction } = this.options\n // If a user provided a custom `dispatchTransaction` through `editorProps`,\n // we use that as the base dispatch function.\n // Otherwise, we use Tiptap's internal `dispatchTransaction` method.\n const baseDispatch = (editorProps as any).dispatchTransaction || this.dispatchTransaction.bind(this)\n const dispatch = enableExtensionDispatchTransaction\n ? this.extensionManager.dispatchTransaction(baseDispatch)\n : baseDispatch\n\n this.editorView = new EditorView(element, {\n ...editorProps,\n attributes: {\n // add `role=\"textbox\"` to the editor element\n role: 'textbox',\n ...editorProps?.attributes,\n },\n dispatchTransaction: dispatch,\n state: this.editorState,\n markViews: this.extensionManager.markViews,\n nodeViews: this.extensionManager.nodeViews,\n })\n\n // `editor.view` is not yet available at this time.\n // Therefore we will add all plugins and node views directly afterwards.\n const newState = this.state.reconfigure({\n plugins: this.extensionManager.plugins,\n })\n\n this.view.updateState(newState)\n\n this.prependClass()\n this.injectCSS()\n\n // Let’s store the editor instance in the DOM element.\n // So we’ll have access to it for tests.\n // @ts-ignore\n const dom = this.view.dom as TiptapEditorHTMLElement\n\n dom.editor = this\n }\n\n /**\n * Creates all node and mark views.\n */\n public createNodeViews(): void {\n if (this.view.isDestroyed) {\n return\n }\n\n this.view.setProps({\n markViews: this.extensionManager.markViews,\n nodeViews: this.extensionManager.nodeViews,\n })\n }\n\n /**\n * Prepend class name to element.\n */\n public prependClass(): void {\n this.view.dom.className = `${this.className} ${this.view.dom.className}`\n }\n\n public isCapturingTransaction = false\n\n private capturedTransaction: Transaction | null = null\n\n public captureTransaction(fn: () => void) {\n this.isCapturingTransaction = true\n fn()\n this.isCapturingTransaction = false\n\n const tr = this.capturedTransaction\n\n this.capturedTransaction = null\n\n return tr\n }\n\n /**\n * The callback over which to send transactions (state updates) produced by the view.\n *\n * @param transaction An editor state transaction\n */\n private dispatchTransaction(transaction: Transaction): void {\n // if the editor / the view of the editor was destroyed\n // the transaction should not be dispatched as there is no view anymore.\n if (this.view.isDestroyed) {\n return\n }\n\n if (this.isCapturingTransaction) {\n if (!this.capturedTransaction) {\n this.capturedTransaction = transaction\n\n return\n }\n\n transaction.steps.forEach(step => this.capturedTransaction?.step(step))\n\n return\n }\n\n // Apply transaction and get resulting state and transactions\n const { state, transactions } = this.state.applyTransaction(transaction)\n const selectionHasChanged = !this.state.selection.eq(state.selection)\n const rootTrWasApplied = transactions.includes(transaction)\n const prevState = this.state\n\n this.emit('beforeTransaction', {\n editor: this,\n transaction,\n nextState: state,\n })\n\n // If transaction was filtered out, we can return early\n if (!rootTrWasApplied) {\n return\n }\n\n this.view.updateState(state)\n\n // Emit transaction event with appended transactions info\n this.emit('transaction', {\n editor: this,\n transaction,\n appendedTransactions: transactions.slice(1),\n })\n\n if (selectionHasChanged) {\n this.emit('selectionUpdate', {\n editor: this,\n transaction,\n })\n }\n\n // Only emit the latest between focus and blur events\n const mostRecentFocusTr = transactions.findLast(tr => tr.getMeta('focus') || tr.getMeta('blur'))\n const focus = mostRecentFocusTr?.getMeta('focus')\n const blur = mostRecentFocusTr?.getMeta('blur')\n\n if (focus) {\n this.emit('focus', {\n editor: this,\n event: focus.event,\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n transaction: mostRecentFocusTr!,\n })\n }\n\n if (blur) {\n this.emit('blur', {\n editor: this,\n event: blur.event,\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n transaction: mostRecentFocusTr!,\n })\n }\n\n // Compare states for update event\n if (\n transaction.getMeta('preventUpdate') ||\n !transactions.some(tr => tr.docChanged) ||\n prevState.doc.eq(state.doc)\n ) {\n return\n }\n\n this.emit('update', {\n editor: this,\n transaction,\n appendedTransactions: transactions.slice(1),\n })\n }\n\n /**\n * Get attributes of the currently selected node or mark.\n */\n public getAttributes(nameOrType: string | NodeType | MarkType): Record<string, any> {\n return getAttributes(this.state, nameOrType)\n }\n\n /**\n * Returns if the currently selected node or mark is active.\n *\n * @param name Name of the node or mark\n * @param attributes Attributes of the node or mark\n */\n public isActive(name: string, attributes?: {}): boolean\n public isActive(attributes: {}): boolean\n public isActive(nameOrAttributes: string, attributesOrUndefined?: {}): boolean {\n const name = typeof nameOrAttributes === 'string' ? nameOrAttributes : null\n\n const attributes = typeof nameOrAttributes === 'string' ? attributesOrUndefined : nameOrAttributes\n\n return isActive(this.state, name, attributes)\n }\n\n /**\n * Get the document as JSON.\n */\n public getJSON(): DocumentType<\n Record<string, any> | undefined,\n TNodeType<string, undefined | Record<string, any>, any, (TNodeType | TTextType)[]>[]\n > {\n return this.state.doc.toJSON()\n }\n\n /**\n * Get the document as HTML.\n */\n public getHTML(): string {\n return getHTMLFromFragment(this.state.doc.content, this.schema)\n }\n\n /**\n * Get the document as text.\n */\n public getText(options?: { blockSeparator?: string; textSerializers?: Record<string, TextSerializer> }): string {\n const { blockSeparator = '\\n\\n', textSerializers = {} } = options || {}\n\n return getText(this.state.doc, {\n blockSeparator,\n textSerializers: {\n ...getTextSerializersFromSchema(this.schema),\n ...textSerializers,\n },\n })\n }\n\n /**\n * Check if there is no content.\n */\n public get isEmpty(): boolean {\n return isNodeEmpty(this.state.doc)\n }\n\n /**\n * Destroy the editor.\n */\n public destroy(): void {\n this.emit('destroy')\n\n this.unmount()\n\n this.removeAllListeners()\n }\n\n /**\n * Check if the editor is already destroyed.\n */\n public get isDestroyed(): boolean {\n return this.editorView?.isDestroyed ?? true\n }\n\n public $node(selector: string, attributes?: { [key: string]: any }): NodePos | null {\n return this.$doc?.querySelector(selector, attributes) || null\n }\n\n public $nodes(selector: string, attributes?: { [key: string]: any }): NodePos[] | null {\n return this.$doc?.querySelectorAll(selector, attributes) || null\n }\n\n public $pos(pos: number) {\n const $pos = this.state.doc.resolve(pos)\n\n return new NodePos($pos, this)\n }\n\n get $doc() {\n return this.$pos(0)\n }\n\n /**\n * Returns a set of utilities for working with positions and ranges.\n */\n public utils: Utils = {\n getUpdatedPosition,\n createMappablePosition,\n }\n}\n","type StringKeyOf<T> = Extract<keyof T, string>\ntype CallbackType<T extends Record<string, any>, EventName extends StringKeyOf<T>> = T[EventName] extends any[]\n ? T[EventName]\n : [T[EventName]]\ntype CallbackFunction<T extends Record<string, any>, EventName extends StringKeyOf<T>> = (\n ...props: CallbackType<T, EventName>\n) => any\n\nexport class EventEmitter<T extends Record<string, any>> {\n private callbacks: { [key: string]: Array<(...args: any[]) => void> } = {}\n\n public on<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this {\n if (!this.callbacks[event]) {\n this.callbacks[event] = []\n }\n\n this.callbacks[event].push(fn)\n\n return this\n }\n\n public emit<EventName extends StringKeyOf<T>>(event: EventName, ...args: CallbackType<T, EventName>): this {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n callbacks.forEach(callback => callback.apply(this, args))\n }\n\n return this\n }\n\n public off<EventName extends StringKeyOf<T>>(event: EventName, fn?: CallbackFunction<T, EventName>): this {\n const callbacks = this.callbacks[event]\n\n if (callbacks) {\n if (fn) {\n this.callbacks[event] = callbacks.filter(callback => callback !== fn)\n } else {\n delete this.callbacks[event]\n }\n }\n\n return this\n }\n\n public once<EventName extends StringKeyOf<T>>(event: EventName, fn: CallbackFunction<T, EventName>): this {\n const onceFn = (...args: CallbackType<T, EventName>) => {\n this.off(event, onceFn)\n fn.apply(this, args)\n }\n\n return this.on(event, onceFn)\n }\n\n public removeAllListeners(): void {\n this.callbacks = {}\n }\n}\n","import { keymap } from '@tiptap/pm/keymap'\nimport type { Schema } from '@tiptap/pm/model'\nimport type { Plugin, Transaction } from '@tiptap/pm/state'\nimport type { MarkViewConstructor, NodeViewConstructor } from '@tiptap/pm/view'\n\nimport type { Editor } from './Editor.js'\nimport {\n flattenExtensions,\n getAttributesFromExtensions,\n getExtensionField,\n getNodeType,\n getRenderedAttributes,\n getSchemaByResolvedExtensions,\n getSchemaTypeByName,\n isExtensionRulesEnabled,\n resolveExtensions,\n sortExtensions,\n splitExtensions,\n} from './helpers/index.js'\nimport { type MarkConfig, type NodeConfig, type Storage, getMarkType, updateMarkViewAttributes } from './index.js'\nimport { inputRulesPlugin } from './InputRule.js'\nimport { Mark } from './Mark.js'\nimport { pasteRulesPlugin } from './PasteRule.js'\nimport type { AnyConfig, Extensions, RawCommands } from './types.js'\nimport { callOrReturn } from './utilities/callOrReturn.js'\n\nexport class ExtensionManager {\n editor: Editor\n\n schema: Schema\n\n /**\n * A flattened and sorted array of all extensions\n */\n extensions: Extensions\n\n /**\n * A non-flattened array of base extensions (no sub-extensions)\n */\n baseExtensions: Extensions\n\n splittableMarks: string[] = []\n\n constructor(extensions: Extensions, editor: Editor) {\n this.editor = editor\n this.baseExtensions = extensions\n this.extensions = resolveExtensions(extensions)\n this.schema = getSchemaByResolvedExtensions(this.extensions, editor)\n this.setupExtensions()\n }\n\n static resolve = resolveExtensions\n\n static sort = sortExtensions\n\n static flatten = flattenExtensions\n\n /**\n * Get all commands from the extensions.\n * @returns An object with all commands where the key is the command name and the value is the command function\n */\n get commands(): RawCommands {\n return this.extensions.reduce((commands, extension) => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor: this.editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n const addCommands = getExtensionField<AnyConfig['addCommands']>(extension, 'addCommands', context)\n\n if (!addCommands) {\n return commands\n }\n\n return {\n ...commands,\n ...addCommands(),\n }\n }, {} as RawCommands)\n }\n\n /**\n * Get all registered Prosemirror plugins from the extensions.\n * @returns An array of Prosemirror plugins\n */\n get plugins(): Plugin[] {\n const { editor } = this\n\n // With ProseMirror, first plugins within an array are executed first.\n // In Tiptap, we provide the ability to override plugins,\n // so it feels more natural to run plugins at the end of an array first.\n // That’s why we have to reverse the `extensions` array and sort again\n // based on the `priority` option.\n const extensions = sortExtensions([...this.extensions].reverse())\n\n const allPlugins = extensions.flatMap(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n const plugins: Plugin[] = []\n\n const addKeyboardShortcuts = getExtensionField<AnyConfig['addKeyboardShortcuts']>(\n extension,\n 'addKeyboardShortcuts',\n context,\n )\n\n let defaultBindings: Record<string, () => boolean> = {}\n\n // bind exit handling\n if (extension.type === 'mark' && getExtensionField<MarkConfig['exitable']>(extension, 'exitable', context)) {\n defaultBindings.ArrowRight = () => Mark.handleExit({ editor, mark: extension as Mark })\n }\n\n if (addKeyboardShortcuts) {\n const bindings = Object.fromEntries(\n Object.entries(addKeyboardShortcuts()).map(([shortcut, method]) => {\n return [shortcut, () => method({ editor })]\n }),\n )\n\n defaultBindings = { ...defaultBindings, ...bindings }\n }\n\n const keyMapPlugin = keymap(defaultBindings)\n\n plugins.push(keyMapPlugin)\n\n const addInputRules = getExtensionField<AnyConfig['addInputRules']>(extension, 'addInputRules', context)\n\n if (isExtensionRulesEnabled(extension, editor.options.enableInputRules) && addInputRules) {\n const rules = addInputRules()\n\n if (rules && rules.length) {\n const inputResult = inputRulesPlugin({\n editor,\n rules,\n })\n\n const inputPlugins = Array.isArray(inputResult) ? inputResult : [inputResult]\n\n plugins.push(...inputPlugins)\n }\n }\n\n const addPasteRules = getExtensionField<AnyConfig['addPasteRules']>(extension, 'addPasteRules', context)\n\n if (isExtensionRulesEnabled(extension, editor.options.enablePasteRules) && addPasteRules) {\n const rules = addPasteRules()\n\n if (rules && rules.length) {\n const pasteRules = pasteRulesPlugin({ editor, rules })\n\n plugins.push(...pasteRules)\n }\n }\n\n const addProseMirrorPlugins = getExtensionField<AnyConfig['addProseMirrorPlugins']>(\n extension,\n 'addProseMirrorPlugins',\n context,\n )\n\n if (addProseMirrorPlugins) {\n const proseMirrorPlugins = addProseMirrorPlugins()\n\n plugins.push(...proseMirrorPlugins)\n }\n\n return plugins\n })\n\n return allPlugins\n }\n\n /**\n * Get all attributes from the extensions.\n * @returns An array of attributes\n */\n get attributes() {\n return getAttributesFromExtensions(this.extensions)\n }\n\n /**\n * Get all node views from the extensions.\n * @returns An object with all node views where the key is the node name and the value is the node view function\n */\n get nodeViews(): Record<string, NodeViewConstructor> {\n const { editor } = this\n const { nodeExtensions } = splitExtensions(this.extensions)\n\n return Object.fromEntries(\n nodeExtensions\n .filter(extension => !!getExtensionField(extension, 'addNodeView'))\n .map(extension => {\n const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getNodeType(extension.name, this.schema),\n }\n const addNodeView = getExtensionField<NodeConfig['addNodeView']>(extension, 'addNodeView', context)\n\n if (!addNodeView) {\n return []\n }\n\n const nodeViewResult = addNodeView()\n\n if (!nodeViewResult) {\n return []\n }\n\n const nodeview: NodeViewConstructor = (node, view, getPos, decorations, innerDecorations) => {\n const HTMLAttributes = getRenderedAttributes(node, extensionAttributes)\n\n return nodeViewResult({\n // pass-through\n node,\n view,\n getPos: getPos as () => number,\n decorations,\n innerDecorations,\n // tiptap-specific\n editor,\n extension,\n HTMLAttributes,\n })\n }\n\n return [extension.name, nodeview]\n }),\n )\n }\n\n /**\n * Get the composed dispatchTransaction function from all extensions.\n * @param baseDispatch The base dispatch function (e.g. from the editor or user props)\n * @returns A composed dispatch function\n */\n dispatchTransaction(baseDispatch: (tr: Transaction) => void): (tr: Transaction) => void {\n const { editor } = this\n const extensions = sortExtensions([...this.extensions].reverse())\n\n return extensions.reduceRight((next, extension) => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n const dispatchTransaction = getExtensionField<AnyConfig['dispatchTransaction']>(\n extension,\n 'dispatchTransaction',\n context,\n )\n\n if (!dispatchTransaction) {\n return next\n }\n\n return (transaction: Transaction) => {\n dispatchTransaction.call(context, { transaction, next })\n }\n }, baseDispatch)\n }\n\n get markViews(): Record<string, MarkViewConstructor> {\n const { editor } = this\n const { markExtensions } = splitExtensions(this.extensions)\n\n return Object.fromEntries(\n markExtensions\n .filter(extension => !!getExtensionField(extension, 'addMarkView'))\n .map(extension => {\n const extensionAttributes = this.attributes.filter(attribute => attribute.type === extension.name)\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor,\n type: getMarkType(extension.name, this.schema),\n }\n const addMarkView = getExtensionField<MarkConfig['addMarkView']>(extension, 'addMarkView', context)\n\n if (!addMarkView) {\n return []\n }\n\n const markView: MarkViewConstructor = (mark, view, inline) => {\n const HTMLAttributes = getRenderedAttributes(mark, extensionAttributes)\n\n return addMarkView()({\n // pass-through\n mark,\n view,\n inline,\n // tiptap-specific\n editor,\n extension,\n HTMLAttributes,\n updateAttributes: (attrs: Record<string, any>) => {\n updateMarkViewAttributes(mark, editor, attrs)\n },\n })\n }\n\n return [extension.name, markView]\n }),\n )\n }\n\n /**\n * Go through all extensions, create extension storages & setup marks\n * & bind editor event listener.\n */\n private setupExtensions() {\n const extensions = this.extensions\n // re-initialize the extension storage object instance\n this.editor.extensionStorage = Object.fromEntries(\n extensions.map(extension => [extension.name, extension.storage]),\n ) as unknown as Storage\n\n extensions.forEach(extension => {\n const context = {\n name: extension.name,\n options: extension.options,\n storage: this.editor.extensionStorage[extension.name as keyof Storage],\n editor: this.editor,\n type: getSchemaTypeByName(extension.name, this.schema),\n }\n\n if (extension.type === 'mark') {\n const keepOnSplit = callOrReturn(getExtensionField(extension, 'keepOnSplit', context)) ?? true\n\n if (keepOnSplit) {\n this.splittableMarks.push(extension.name)\n }\n }\n\n const onBeforeCreate = getExtensionField<AnyConfig['onBeforeCreate']>(extension, 'onBeforeCreate', context)\n const onCreate = getExtensionField<AnyConfig['onCreate']>(extension, 'onCreate', context)\n const onUpdate = getExtensionField<AnyConfig['onUpdate']>(extension, 'onUpdate', context)\n const onSelectionUpdate = getExtensionField<AnyConfig['onSelectionUpdate']>(\n extension,\n 'onSelectionUpdate',\n context,\n )\n const onTransaction = getExtensionField<AnyConfig['onTransaction']>(extension, 'onTransaction', context)\n const onFocus = getExtensionField<AnyConfig['onFocus']>(extension, 'onFocus', context)\n const onBlur = getExtensionField<AnyConfig['onBlur']>(extension, 'onBlur', context)\n const onDestroy = getExtensionField<AnyConfig['onDestroy']>(extension, 'onDestroy', context)\n\n if (onBeforeCreate) {\n this.editor.on('beforeCreate', onBeforeCreate)\n }\n\n if (onCreate) {\n this.editor.on('create', onCreate)\n }\n\n if (onUpdate) {\n this.editor.on('update', onUpdate)\n }\n\n if (onSelectionUpdate) {\n this.editor.on('selectionUpdate', onSelectionUpdate)\n }\n\n if (onTransaction) {\n this.editor.on('transaction', onTransaction)\n }\n\n if (onFocus) {\n this.editor.on('focus', onFocus)\n }\n\n if (onBlur) {\n this.editor.on('blur', onBlur)\n }\n\n if (onDestroy) {\n this.editor.on('destroy', onDestroy)\n }\n })\n }\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Fragment } from '@tiptap/pm/model'\nimport type { EditorState, TextSelection } from '@tiptap/pm/state'\nimport { Plugin } from '@tiptap/pm/state'\n\nimport { CommandManager } from './CommandManager.js'\nimport type { Editor } from './Editor.js'\nimport { createChainableState } from './helpers/createChainableState.js'\nimport { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'\nimport { getTextContentFromNodes } from './helpers/getTextContentFromNodes.js'\nimport type { CanCommands, ChainedCommands, ExtendedRegExpMatchArray, Range, SingleCommands } from './types.js'\nimport { isRegExp } from './utilities/isRegExp.js'\n\nexport type InputRuleMatch = {\n index: number\n text: string\n replaceWith?: string\n match?: RegExpMatchArray\n data?: Record<string, any>\n}\n\nexport type InputRuleFinder = RegExp | ((text: string) => InputRuleMatch | null)\n\nexport class InputRule {\n find: InputRuleFinder\n\n handler: (props: {\n state: EditorState\n range: Range\n match: ExtendedRegExpMatchArray\n commands: SingleCommands\n chain: () => ChainedCommands\n can: () => CanCommands\n }) => void | null\n\n undoable: boolean\n\n constructor(config: {\n find: InputRuleFinder\n handler: (props: {\n state: EditorState\n range: Range\n match: ExtendedRegExpMatchArray\n commands: SingleCommands\n chain: () => ChainedCommands\n can: () => CanCommands\n }) => void | null\n undoable?: boolean\n }) {\n this.find = config.find\n this.handler = config.handler\n this.undoable = config.undoable ?? true\n }\n}\n\nconst inputRuleMatcherHandler = (text: string, find: InputRuleFinder): ExtendedRegExpMatchArray | null => {\n if (isRegExp(find)) {\n return find.exec(text)\n }\n\n const inputRuleMatch = find(text)\n\n if (!inputRuleMatch) {\n return null\n }\n\n const result: ExtendedRegExpMatchArray = [inputRuleMatch.text]\n\n result.index = inputRuleMatch.index\n result.input = text\n result.data = inputRuleMatch.data\n\n if (inputRuleMatch.replaceWith) {\n if (!inputRuleMatch.text.includes(inputRuleMatch.replaceWith)) {\n console.warn('[tiptap warn]: \"inputRuleMatch.replaceWith\" must be part of \"inputRuleMatch.text\".')\n }\n\n result.push(inputRuleMatch.replaceWith)\n }\n\n return result\n}\n\nfunction run(config: {\n editor: Editor\n from: number\n to: number\n text: string\n rules: InputRule[]\n plugin: Plugin\n}): boolean {\n const { editor, from, to, text, rules, plugin } = config\n const { view } = editor\n\n if (view.composing) {\n return false\n }\n\n const $from = view.state.doc.resolve(from)\n\n if (\n // check for code node\n $from.parent.type.spec.code ||\n // check for code mark\n !!($from.nodeBefore || $from.nodeAfter)?.marks.find(mark => mark.type.spec.code)\n ) {\n return false\n }\n\n let matched = false\n\n const textBefore = getTextContentFromNodes($from) + text\n\n rules.forEach(rule => {\n if (matched) {\n return\n }\n\n const match = inputRuleMatcherHandler(textBefore, rule.find)\n\n if (!match) {\n return\n }\n\n const tr = view.state.tr\n const state = createChainableState({\n state: view.state,\n transaction: tr,\n })\n const range = {\n from: from - (match[0].length - text.length),\n to,\n }\n\n const { commands, chain, can } = new CommandManager({\n editor,\n state,\n })\n\n const handler = rule.handler({\n state,\n range,\n match,\n commands,\n chain,\n can,\n })\n\n // stop if there are no changes\n if (handler === null || !tr.steps.length) {\n return\n }\n\n // store transform as meta data\n // so we can undo input rules within the `undoInputRules` command\n if (rule.undoable) {\n tr.setMeta(plugin, {\n transform: tr,\n from,\n to,\n text,\n })\n }\n\n view.dispatch(tr)\n matched = true\n })\n\n return matched\n}\n\n/**\n * Create an input rules plugin. When enabled, it will cause text\n * input that matches any of the given rules to trigger the rule’s\n * action.\n */\nexport function inputRulesPlugin(props: { editor: Editor; rules: InputRule[] }): Plugin {\n const { editor, rules } = props\n const plugin = new Plugin({\n state: {\n init() {\n return null\n },\n apply(tr, prev, state) {\n const stored = tr.getMeta(plugin)\n\n if (stored) {\n return stored\n }\n\n // if InputRule is triggered by insertContent()\n const simulatedInputMeta = tr.getMeta('applyInputRules') as\n | undefined\n | {\n from: number\n text: string | ProseMirrorNode | Fragment\n }\n const isSimulatedInput = !!simulatedInputMeta\n\n if (isSimulatedInput) {\n setTimeout(() => {\n let { text } = simulatedInputMeta\n\n if (typeof text === 'string') {\n text = text as string\n } else {\n text = getHTMLFromFragment(Fragment.from(text), state.schema)\n }\n\n const { from } = simulatedInputMeta\n const to = from + text.length\n\n run({\n editor,\n from,\n to,\n text,\n rules,\n plugin,\n })\n })\n }\n\n return tr.selectionSet || tr.docChanged ? null : prev\n },\n },\n\n props: {\n handleTextInput(view, from, to, text) {\n return run({\n editor,\n from,\n to,\n text,\n rules,\n plugin,\n })\n },\n\n handleDOMEvents: {\n compositionend: view => {\n setTimeout(() => {\n const { $cursor } = view.state.selection as TextSelection\n\n if ($cursor) {\n run({\n editor,\n from: $cursor.pos,\n to: $cursor.pos,\n text: '',\n rules,\n plugin,\n })\n }\n })\n\n return false\n },\n },\n\n // add support for input rules to trigger on enter\n // this is useful for example for code blocks\n handleKeyDown(view, event) {\n if (event.key !== 'Enter') {\n return false\n }\n\n const { $cursor } = view.state.selection as TextSelection\n\n if ($cursor) {\n return run({\n editor,\n from: $cursor.pos,\n to: $cursor.pos,\n text: '\\n',\n rules,\n plugin,\n })\n }\n\n return false\n },\n },\n\n // @ts-ignore\n isInputRules: true,\n }) as Plugin\n\n return plugin\n}\n","// see: https://github.com/mesqueeb/is-what/blob/88d6e4ca92fb2baab6003c54e02eedf4e729e5ab/src/index.ts\n\nfunction getType(value: any): string {\n return Object.prototype.toString.call(value).slice(8, -1)\n}\n\nexport function isPlainObject(value: any): value is Record<string, any> {\n if (getType(value) !== 'Object') {\n return false\n }\n\n return value.constructor === Object && Object.getPrototypeOf(value) === Object.prototype\n}\n","import { isPlainObject } from './isPlainObject.js'\n\nexport function mergeDeep(target: Record<string, any>, source: Record<string, any>): Record<string, any> {\n const output = { ...target }\n\n if (isPlainObject(target) && isPlainObject(source)) {\n Object.keys(source).forEach(key => {\n if (isPlainObject(source[key]) && isPlainObject(target[key])) {\n output[key] = mergeDeep(target[key], source[key])\n } else {\n output[key] = source[key]\n }\n })\n }\n\n return output\n}\n","import type { Plugin } from '@tiptap/pm/state'\n\nimport type { Editor } from './Editor.js'\nimport { getExtensionField } from './helpers/getExtensionField.js'\nimport type { ExtensionConfig, MarkConfig, NodeConfig } from './index.js'\nimport type { InputRule } from './InputRule.js'\nimport type { Mark } from './Mark.js'\nimport type { Node } from './Node.js'\nimport type { PasteRule } from './PasteRule.js'\nimport type {\n AnyConfig,\n DispatchTransactionProps,\n EditorEvents,\n Extensions,\n GlobalAttributes,\n JSONContent,\n KeyboardShortcutCommand,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownRendererHelpers,\n MarkdownToken,\n MarkdownTokenizer,\n ParentConfig,\n RawCommands,\n RenderContext,\n} from './types.js'\nimport { callOrReturn } from './utilities/callOrReturn.js'\nimport { mergeDeep } from './utilities/mergeDeep.js'\n\nexport interface ExtendableConfig<\n Options = any,\n Storage = any,\n Config extends\n | ExtensionConfig<Options, Storage>\n | NodeConfig<Options, Storage>\n | MarkConfig<Options, Storage>\n | ExtendableConfig<Options, Storage> = ExtendableConfig<Options, Storage, any, any>,\n PMType = any,\n> {\n /**\n * The extension name - this must be unique.\n * It will be used to identify the extension.\n *\n * @example 'myExtension'\n */\n name: string\n\n /**\n * The priority of your extension. The higher, the earlier it will be called\n * and will take precedence over other extensions with a lower priority.\n * @default 100\n * @example 101\n */\n priority?: number\n\n /**\n * This method will add options to this extension\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#settings\n * @example\n * addOptions() {\n * return {\n * myOption: 'foo',\n * myOtherOption: 10,\n * }\n */\n addOptions?: (this: { name: string; parent: ParentConfig<Config>['addOptions'] }) => Options\n\n /**\n * The default storage this extension can save data to.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#storage\n * @example\n * defaultStorage: {\n * prefetchedUsers: [],\n * loading: false,\n * }\n */\n addStorage?: (this: { name: string; options: Options; parent: ParentConfig<Config>['addStorage'] }) => Storage\n\n /**\n * This function adds globalAttributes to specific nodes.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#global-attributes\n * @example\n * addGlobalAttributes() {\n * return [\n * {\n // Extend the following extensions\n * types: [\n * 'heading',\n * 'paragraph',\n * ],\n * // … with those attributes\n * attributes: {\n * textAlign: {\n * default: 'left',\n * renderHTML: attributes => ({\n * style: `text-align: ${attributes.textAlign}`,\n * }),\n * parseHTML: element => element.style.textAlign || 'left',\n * },\n * },\n * },\n * ]\n * }\n */\n addGlobalAttributes?: (this: {\n name: string\n options: Options\n storage: Storage\n extensions: (Node | Mark)[]\n parent: ParentConfig<Config>['addGlobalAttributes']\n }) => GlobalAttributes\n\n /**\n * This function adds commands to the editor\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#commands\n * @example\n * addCommands() {\n * return {\n * myCommand: () => ({ chain }) => chain().setMark('type', 'foo').run(),\n * }\n * }\n */\n addCommands?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addCommands']\n }) => Partial<RawCommands>\n\n /**\n * This function registers keyboard shortcuts.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#keyboard-shortcuts\n * @example\n * addKeyboardShortcuts() {\n * return {\n * 'Mod-l': () => this.editor.commands.toggleBulletList(),\n * }\n * },\n */\n addKeyboardShortcuts?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addKeyboardShortcuts']\n }) => {\n [key: string]: KeyboardShortcutCommand\n }\n\n /**\n * This function adds input rules to the editor.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#input-rules\n * @example\n * addInputRules() {\n * return [\n * markInputRule({\n * find: inputRegex,\n * type: this.type,\n * }),\n * ]\n * },\n */\n addInputRules?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addInputRules']\n }) => InputRule[]\n\n /**\n * This function adds paste rules to the editor.\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#paste-rules\n * @example\n * addPasteRules() {\n * return [\n * markPasteRule({\n * find: pasteRegex,\n * type: this.type,\n * }),\n * ]\n * },\n */\n addPasteRules?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addPasteRules']\n }) => PasteRule[]\n\n /**\n * This function adds Prosemirror plugins to the editor\n * @see https://tiptap.dev/docs/editor/guide/custom-extensions#prosemirror-plugins\n * @example\n * addProseMirrorPlugins() {\n * return [\n * customPlugin(),\n * ]\n * }\n */\n addProseMirrorPlugins?: (this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['addProseMirrorPlugins']\n }) => Plugin[]\n\n /**\n * This function adds additional extensions to the editor. This is useful for\n * building extension kits.\n * @example\n * addExtensions() {\n * return [\n * BulletList,\n * OrderedList,\n * ListItem\n * ]\n * }\n */\n addExtensions?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<Config>['addExtensions']\n }) => Extensions\n\n /**\n * The markdown token name\n *\n * This is the name of the token that this extension uses to parse and render markdown and comes from the Marked Lexer.\n *\n * @see https://github.com/markedjs/marked/blob/master/src/Tokens.ts\n *\n */\n markdownTokenName?: string\n\n /**\n * The parse function used by the markdown parser to convert markdown tokens to ProseMirror nodes.\n */\n parseMarkdown?: (token: MarkdownToken, helpers: MarkdownParseHelpers) => MarkdownParseResult\n\n /**\n * The serializer function used by the markdown serializer to convert ProseMirror nodes to markdown tokens.\n */\n renderMarkdown?: (node: JSONContent, helpers: MarkdownRendererHelpers, ctx: RenderContext) => string\n\n /**\n * The markdown tokenizer responsible for turning a markdown string into tokens\n *\n * Custom tokenizers are only needed when you want to parse non-standard markdown token.\n */\n markdownTokenizer?: MarkdownTokenizer\n\n /**\n * Optional markdown options for indentation\n */\n markdownOptions?: {\n /**\n * Defines if this markdown element should indent it's child elements\n */\n indentsContent?: boolean\n }\n\n /**\n * This function extends the schema of the node.\n * @example\n * extendNodeSchema() {\n * return {\n * group: 'inline',\n * selectable: false,\n * }\n * }\n */\n extendNodeSchema?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<Config>['extendNodeSchema']\n },\n extension: Node,\n ) => Record<string, any>)\n | null\n\n /**\n * This function extends the schema of the mark.\n * @example\n * extendMarkSchema() {\n * return {\n * group: 'inline',\n * selectable: false,\n * }\n * }\n */\n extendMarkSchema?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<Config>['extendMarkSchema']\n },\n extension: Mark,\n ) => Record<string, any>)\n | null\n\n /**\n * The editor is not ready yet.\n */\n onBeforeCreate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onBeforeCreate']\n },\n event: EditorEvents['beforeCreate'],\n ) => void)\n | null\n\n /**\n * The editor is ready.\n */\n onCreate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onCreate']\n },\n event: EditorEvents['create'],\n ) => void)\n | null\n\n /**\n * The content has changed.\n */\n onUpdate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onUpdate']\n },\n event: EditorEvents['update'],\n ) => void)\n | null\n\n /**\n * The selection has changed.\n */\n onSelectionUpdate?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onSelectionUpdate']\n },\n event: EditorEvents['selectionUpdate'],\n ) => void)\n | null\n\n /**\n * The editor state has changed.\n */\n onTransaction?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onTransaction']\n },\n event: EditorEvents['transaction'],\n ) => void)\n | null\n\n /**\n * The editor is focused.\n */\n onFocus?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onFocus']\n },\n event: EditorEvents['focus'],\n ) => void)\n | null\n\n /**\n * The editor isn’t focused anymore.\n */\n onBlur?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onBlur']\n },\n event: EditorEvents['blur'],\n ) => void)\n | null\n\n /**\n * The editor is destroyed.\n */\n onDestroy?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['onDestroy']\n },\n event: EditorEvents['destroy'],\n ) => void)\n | null\n\n /**\n * This hook allows you to intercept and modify transactions before they are dispatched.\n *\n * Example\n * ```ts\n * dispatchTransaction({ transaction, next }) {\n * console.log('Dispatching transaction:', transaction)\n * next(transaction)\n * }\n * ```\n *\n * @param props - The dispatch transaction props\n */\n dispatchTransaction?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: PMType\n parent: ParentConfig<Config>['dispatchTransaction']\n },\n props: DispatchTransactionProps,\n ) => void)\n | null\n}\n\nexport class Extendable<\n Options = any,\n Storage = any,\n Config = ExtensionConfig<Options, Storage> | NodeConfig<Options, Storage> | MarkConfig<Options, Storage>,\n> {\n type = 'extendable'\n parent: Extendable | null = null\n\n child: Extendable | null = null\n\n name = ''\n\n config: Config = {\n name: this.name,\n } as Config\n\n constructor(config: Partial<Config> = {}) {\n this.config = {\n ...this.config,\n ...config,\n }\n\n this.name = (this.config as any).name\n }\n\n get options(): Options {\n return {\n ...(callOrReturn(\n getExtensionField<AnyConfig['addOptions']>(this as any, 'addOptions', {\n name: this.name,\n }),\n ) || {}),\n }\n }\n\n get storage(): Readonly<Storage> {\n return {\n ...(callOrReturn(\n getExtensionField<AnyConfig['addStorage']>(this as any, 'addStorage', {\n name: this.name,\n options: this.options,\n }),\n ) || {}),\n }\n }\n\n configure(options: Partial<Options> = {}) {\n const extension = this.extend<Options, Storage, Config>({\n ...this.config,\n addOptions: () => {\n return mergeDeep(this.options as Record<string, any>, options) as Options\n },\n })\n\n extension.name = this.name\n extension.parent = this.parent\n\n return extension\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig =\n | ExtensionConfig<ExtendedOptions, ExtendedStorage>\n | NodeConfig<ExtendedOptions, ExtendedStorage>\n | MarkConfig<ExtendedOptions, ExtendedStorage>,\n >(extendedConfig: Partial<ExtendedConfig> = {}): Extendable<ExtendedOptions, ExtendedStorage> {\n const extension = new (this.constructor as any)({ ...this.config, ...extendedConfig })\n\n extension.parent = this\n this.child = extension\n extension.name = 'name' in extendedConfig ? extendedConfig.name : extension.parent.name\n\n return extension\n }\n}\n","import type { DOMOutputSpec, Mark as ProseMirrorMark, MarkSpec, MarkType } from '@tiptap/pm/model'\n\nimport type { Editor } from './Editor.js'\nimport type { ExtendableConfig } from './Extendable.js'\nimport { Extendable } from './Extendable.js'\nimport type { Attributes, MarkViewRenderer, ParentConfig } from './types.js'\n\nexport interface MarkConfig<Options = any, Storage = any>\n extends ExtendableConfig<Options, Storage, MarkConfig<Options, Storage>, MarkType> {\n /**\n * Mark View\n */\n addMarkView?:\n | ((this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: MarkType\n parent: ParentConfig<MarkConfig<Options, Storage>>['addMarkView']\n }) => MarkViewRenderer)\n | null\n\n /**\n * Keep mark after split node\n */\n keepOnSplit?: boolean | (() => boolean)\n\n /**\n * Inclusive\n */\n inclusive?:\n | MarkSpec['inclusive']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['inclusive']\n editor?: Editor\n }) => MarkSpec['inclusive'])\n\n /**\n * Excludes\n */\n excludes?:\n | MarkSpec['excludes']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['excludes']\n editor?: Editor\n }) => MarkSpec['excludes'])\n\n /**\n * Marks this Mark as exitable\n */\n exitable?: boolean | (() => boolean)\n\n /**\n * Group\n */\n group?:\n | MarkSpec['group']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['group']\n editor?: Editor\n }) => MarkSpec['group'])\n\n /**\n * Spanning\n */\n spanning?:\n | MarkSpec['spanning']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['spanning']\n editor?: Editor\n }) => MarkSpec['spanning'])\n\n /**\n * Code\n */\n code?:\n | boolean\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['code']\n editor?: Editor\n }) => boolean)\n\n /**\n * Parse HTML\n */\n parseHTML?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['parseHTML']\n editor?: Editor\n }) => MarkSpec['parseDOM']\n\n /**\n * Render HTML\n */\n renderHTML?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['renderHTML']\n editor?: Editor\n },\n props: {\n mark: ProseMirrorMark\n HTMLAttributes: Record<string, any>\n },\n ) => DOMOutputSpec)\n | null\n\n /**\n * Attributes\n */\n addAttributes?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<MarkConfig<Options, Storage>>['addAttributes']\n editor?: Editor\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n }) => Attributes | {}\n}\n\n/**\n * The Mark class is used to create custom mark extensions.\n * @see https://tiptap.dev/api/extensions#create-a-new-extension\n */\nexport class Mark<Options = any, Storage = any> extends Extendable<Options, Storage, MarkConfig<Options, Storage>> {\n type = 'mark'\n\n /**\n * Create a new Mark instance\n * @param config - Mark configuration object or a function that returns a configuration object\n */\n static create<O = any, S = any>(config: Partial<MarkConfig<O, S>> | (() => Partial<MarkConfig<O, S>>) = {}) {\n // If the config is a function, execute it to get the configuration object\n const resolvedConfig = typeof config === 'function' ? config() : config\n return new Mark<O, S>(resolvedConfig)\n }\n\n static handleExit({ editor, mark }: { editor: Editor; mark: Mark }) {\n const { tr } = editor.state\n const currentPos = editor.state.selection.$from\n const isAtEnd = currentPos.pos === currentPos.end()\n\n if (isAtEnd) {\n const currentMarks = currentPos.marks()\n const isInMark = !!currentMarks.find(m => m?.type.name === mark.name)\n\n if (!isInMark) {\n return false\n }\n\n const removeMark = currentMarks.find(m => m?.type.name === mark.name)\n\n if (removeMark) {\n tr.removeStoredMark(removeMark)\n }\n tr.insertText(' ', currentPos.pos)\n\n editor.view.dispatch(tr)\n\n return true\n }\n\n return false\n }\n\n configure(options?: Partial<Options>) {\n return super.configure(options) as Mark<Options, Storage>\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig extends MarkConfig<ExtendedOptions, ExtendedStorage> = MarkConfig<ExtendedOptions, ExtendedStorage>,\n >(\n extendedConfig?:\n | (() => Partial<ExtendedConfig>)\n | (Partial<ExtendedConfig> &\n ThisType<{\n name: string\n options: ExtendedOptions\n storage: ExtendedStorage\n editor: Editor\n type: MarkType\n }>),\n ): Mark<ExtendedOptions, ExtendedStorage> {\n // If the extended config is a function, execute it to get the configuration object\n const resolvedConfig = typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig\n return super.extend(resolvedConfig) as Mark<ExtendedOptions, ExtendedStorage>\n }\n}\n","import type { Node as ProseMirrorNode } from '@tiptap/pm/model'\nimport { Fragment } from '@tiptap/pm/model'\nimport type { EditorState } from '@tiptap/pm/state'\nimport { Plugin } from '@tiptap/pm/state'\n\nimport { CommandManager } from './CommandManager.js'\nimport type { Editor } from './Editor.js'\nimport { createChainableState } from './helpers/createChainableState.js'\nimport { getHTMLFromFragment } from './helpers/getHTMLFromFragment.js'\nimport type { CanCommands, ChainedCommands, ExtendedRegExpMatchArray, Range, SingleCommands } from './types.js'\nimport { isNumber } from './utilities/isNumber.js'\nimport { isRegExp } from './utilities/isRegExp.js'\n\nexport type PasteRuleMatch = {\n index: number\n text: string\n replaceWith?: string\n match?: RegExpMatchArray\n data?: Record<string, any>\n}\n\nexport type PasteRuleFinder =\n | RegExp\n | ((text: string, event?: ClipboardEvent | null) => PasteRuleMatch[] | null | undefined)\n\n/**\n * Paste rules are used to react to pasted content.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules\n */\nexport class PasteRule {\n find: PasteRuleFinder\n\n handler: (props: {\n state: EditorState\n range: Range\n match: ExtendedRegExpMatchArray\n commands: SingleCommands\n chain: () => ChainedCommands\n can: () => CanCommands\n pasteEvent: ClipboardEvent | null\n dropEvent: DragEvent | null\n }) => void | null\n\n constructor(config: {\n find: PasteRuleFinder\n handler: (props: {\n can: () => CanCommands\n chain: () => ChainedCommands\n commands: SingleCommands\n dropEvent: DragEvent | null\n match: ExtendedRegExpMatchArray\n pasteEvent: ClipboardEvent | null\n range: Range\n state: EditorState\n }) => void | null\n }) {\n this.find = config.find\n this.handler = config.handler\n }\n}\n\nconst pasteRuleMatcherHandler = (\n text: string,\n find: PasteRuleFinder,\n event?: ClipboardEvent | null,\n): ExtendedRegExpMatchArray[] => {\n if (isRegExp(find)) {\n return [...text.matchAll(find)]\n }\n\n const matches = find(text, event)\n\n if (!matches) {\n return []\n }\n\n return matches.map(pasteRuleMatch => {\n const result: ExtendedRegExpMatchArray = [pasteRuleMatch.text]\n\n result.index = pasteRuleMatch.index\n result.input = text\n result.data = pasteRuleMatch.data\n\n if (pasteRuleMatch.replaceWith) {\n if (!pasteRuleMatch.text.includes(pasteRuleMatch.replaceWith)) {\n console.warn('[tiptap warn]: \"pasteRuleMatch.replaceWith\" must be part of \"pasteRuleMatch.text\".')\n }\n\n result.push(pasteRuleMatch.replaceWith)\n }\n\n return result\n })\n}\n\nfunction run(config: {\n editor: Editor\n state: EditorState\n from: number\n to: number\n rule: PasteRule\n pasteEvent: ClipboardEvent | null\n dropEvent: DragEvent | null\n}): boolean {\n const { editor, state, from, to, rule, pasteEvent, dropEvent } = config\n\n const { commands, chain, can } = new CommandManager({\n editor,\n state,\n })\n\n const handlers: (void | null)[] = []\n\n state.doc.nodesBetween(from, to, (node, pos) => {\n // Skip code blocks and non-textual nodes.\n // Be defensive: `node` may be a Fragment without a `type`. Only text,\n // inline, or textblock nodes are processed by paste rules.\n if (node.type?.spec?.code || !(node.isText || node.isTextblock || node.isInline)) {\n return\n }\n\n // For textblock and inline/text nodes, compute the range relative to the node.\n // Prefer `node.nodeSize` when available (some Node shapes expose this),\n // otherwise fall back to `node.content?.size`. Default to 0 if neither exists.\n const contentSize = node.content?.size ?? node.nodeSize ?? 0\n const resolvedFrom = Math.max(from, pos)\n const resolvedTo = Math.min(to, pos + contentSize)\n\n // If the resolved range is empty or invalid for this node, skip it. This\n // avoids calling `textBetween` with start > end which can cause internal\n // Fragment/Node traversal to access undefined `nodeSize` values.\n if (resolvedFrom >= resolvedTo) {\n return\n }\n\n const textToMatch = node.isText\n ? node.text || ''\n : node.textBetween(resolvedFrom - pos, resolvedTo - pos, undefined, '\\ufffc')\n\n const matches = pasteRuleMatcherHandler(textToMatch, rule.find, pasteEvent)\n\n matches.forEach(match => {\n if (match.index === undefined) {\n return\n }\n\n const start = resolvedFrom + match.index + 1\n const end = start + match[0].length\n const range = {\n from: state.tr.mapping.map(start),\n to: state.tr.mapping.map(end),\n }\n\n const handler = rule.handler({\n state,\n range,\n match,\n commands,\n chain,\n can,\n pasteEvent,\n dropEvent,\n })\n\n handlers.push(handler)\n })\n })\n\n const success = handlers.every(handler => handler !== null)\n\n return success\n}\n\n// When dragging across editors, must get another editor instance to delete selection content.\nlet tiptapDragFromOtherEditor: Editor | null = null\n\nconst createClipboardPasteEvent = (text: string) => {\n const event = new ClipboardEvent('paste', {\n clipboardData: new DataTransfer(),\n })\n\n event.clipboardData?.setData('text/html', text)\n\n return event\n}\n\n/**\n * Create an paste rules plugin. When enabled, it will cause pasted\n * text that matches any of the given rules to trigger the rule’s\n * action.\n */\nexport function pasteRulesPlugin(props: { editor: Editor; rules: PasteRule[] }): Plugin[] {\n const { editor, rules } = props\n let dragSourceElement: Element | null = null\n let isPastedFromProseMirror = false\n let isDroppedFromProseMirror = false\n let pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null\n let dropEvent: DragEvent | null\n\n try {\n dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null\n } catch {\n dropEvent = null\n }\n\n const processEvent = ({\n state,\n from,\n to,\n rule,\n pasteEvt,\n }: {\n state: EditorState\n from: number\n to: { b: number }\n rule: PasteRule\n pasteEvt: ClipboardEvent | null\n }) => {\n const tr = state.tr\n const chainableState = createChainableState({\n state,\n transaction: tr,\n })\n\n const handler = run({\n editor,\n state: chainableState,\n from: Math.max(from - 1, 0),\n to: to.b - 1,\n rule,\n pasteEvent: pasteEvt,\n dropEvent,\n })\n\n if (!handler || !tr.steps.length) {\n return\n }\n\n try {\n dropEvent = typeof DragEvent !== 'undefined' ? new DragEvent('drop') : null\n } catch {\n dropEvent = null\n }\n pasteEvent = typeof ClipboardEvent !== 'undefined' ? new ClipboardEvent('paste') : null\n\n return tr\n }\n\n const plugins = rules.map(rule => {\n return new Plugin({\n // we register a global drag handler to track the current drag source element\n view(view) {\n const handleDragstart = (event: DragEvent) => {\n dragSourceElement = view.dom.parentElement?.contains(event.target as Element) ? view.dom.parentElement : null\n\n if (dragSourceElement) {\n tiptapDragFromOtherEditor = editor\n }\n }\n\n const handleDragend = () => {\n if (tiptapDragFromOtherEditor) {\n tiptapDragFromOtherEditor = null\n }\n }\n\n window.addEventListener('dragstart', handleDragstart)\n window.addEventListener('dragend', handleDragend)\n\n return {\n destroy() {\n window.removeEventListener('dragstart', handleDragstart)\n window.removeEventListener('dragend', handleDragend)\n },\n }\n },\n\n props: {\n handleDOMEvents: {\n drop: (view, event: Event) => {\n isDroppedFromProseMirror = dragSourceElement === view.dom.parentElement\n dropEvent = event as DragEvent\n\n if (!isDroppedFromProseMirror) {\n const dragFromOtherEditor = tiptapDragFromOtherEditor\n\n if (dragFromOtherEditor?.isEditable) {\n // setTimeout to avoid the wrong content after drop, timeout arg can't be empty or 0\n setTimeout(() => {\n const selection = dragFromOtherEditor.state.selection\n\n if (selection) {\n dragFromOtherEditor.commands.deleteRange({ from: selection.from, to: selection.to })\n }\n }, 10)\n }\n }\n return false\n },\n\n paste: (_view, event: Event) => {\n const html = (event as ClipboardEvent).clipboardData?.getData('text/html')\n\n pasteEvent = event as ClipboardEvent\n\n isPastedFromProseMirror = !!html?.includes('data-pm-slice')\n\n return false\n },\n },\n },\n\n appendTransaction: (transactions, oldState, state) => {\n const transaction = transactions[0]\n const isPaste = transaction.getMeta('uiEvent') === 'paste' && !isPastedFromProseMirror\n const isDrop = transaction.getMeta('uiEvent') === 'drop' && !isDroppedFromProseMirror\n\n // if PasteRule is triggered by insertContent()\n const simulatedPasteMeta = transaction.getMeta('applyPasteRules') as\n | undefined\n | { from: number; text: string | ProseMirrorNode | Fragment }\n const isSimulatedPaste = !!simulatedPasteMeta\n\n if (!isPaste && !isDrop && !isSimulatedPaste) {\n return\n }\n\n // Handle simulated paste\n if (isSimulatedPaste) {\n let { text } = simulatedPasteMeta\n\n if (typeof text === 'string') {\n text = text as string\n } else {\n text = getHTMLFromFragment(Fragment.from(text), state.schema)\n }\n\n const { from } = simulatedPasteMeta\n const to = from + text.length\n\n const pasteEvt = createClipboardPasteEvent(text)\n\n return processEvent({\n rule,\n state,\n from,\n to: { b: to },\n pasteEvt,\n })\n }\n\n // handle actual paste/drop\n const from = oldState.doc.content.findDiffStart(state.doc.content)\n const to = oldState.doc.content.findDiffEnd(state.doc.content)\n\n // stop if there is no changed range\n if (!isNumber(from) || !to || from === to.b) {\n return\n }\n\n return processEvent({\n rule,\n state,\n from,\n to,\n pasteEvt: pasteEvent,\n })\n },\n })\n })\n\n return plugins\n}\n","export function isNumber(value: any): value is number {\n return typeof value === 'number'\n}\n","export { ClipboardTextSerializer } from './clipboardTextSerializer.js'\nexport { Commands } from './commands.js'\nexport { Delete } from './delete.js'\nexport { Drop } from './drop.js'\nexport { Editable } from './editable.js'\nexport { FocusEvents, focusEventsPluginKey } from './focusEvents.js'\nexport { Keymap } from './keymap.js'\nexport { Paste } from './paste.js'\nexport { Tabindex } from './tabindex.js'\nexport { TextDirection } from './textDirection.js'\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\nimport { getTextBetween } from '../helpers/getTextBetween.js'\nimport { getTextSerializersFromSchema } from '../helpers/getTextSerializersFromSchema.js'\n\nexport type ClipboardTextSerializerOptions = {\n blockSeparator?: string\n}\n\nexport const ClipboardTextSerializer = Extension.create<ClipboardTextSerializerOptions>({\n name: 'clipboardTextSerializer',\n\n addOptions() {\n return {\n blockSeparator: undefined,\n }\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('clipboardTextSerializer'),\n props: {\n clipboardTextSerializer: () => {\n const { editor } = this\n const { state, schema } = editor\n const { doc, selection } = state\n const { ranges } = selection\n const from = Math.min(...ranges.map(range => range.$from.pos))\n const to = Math.max(...ranges.map(range => range.$to.pos))\n const textSerializers = getTextSerializersFromSchema(schema)\n const range = { from, to }\n\n return getTextBetween(doc, range, {\n ...(this.options.blockSeparator !== undefined ? { blockSeparator: this.options.blockSeparator } : {}),\n textSerializers,\n })\n },\n },\n }),\n ]\n },\n})\n","import type { Editor } from './Editor.js'\nimport { type ExtendableConfig, Extendable } from './Extendable.js'\n\n// eslint-disable-next-line @typescript-eslint/no-empty-object-type\nexport interface ExtensionConfig<Options = any, Storage = any>\n extends ExtendableConfig<Options, Storage, ExtensionConfig<Options, Storage>, null> {}\n\n/**\n * The Extension class is the base class for all extensions.\n * @see https://tiptap.dev/api/extensions#create-a-new-extension\n */\nexport class Extension<Options = any, Storage = any> extends Extendable<\n Options,\n Storage,\n ExtensionConfig<Options, Storage>\n> {\n type = 'extension'\n\n /**\n * Create a new Extension instance\n * @param config - Extension configuration object or a function that returns a configuration object\n */\n static create<O = any, S = any>(\n config: Partial<ExtensionConfig<O, S>> | (() => Partial<ExtensionConfig<O, S>>) = {},\n ) {\n // If the config is a function, execute it to get the configuration object\n const resolvedConfig = typeof config === 'function' ? config() : config\n return new Extension<O, S>(resolvedConfig)\n }\n\n configure(options?: Partial<Options>) {\n return super.configure(options) as Extension<Options, Storage>\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig = ExtensionConfig<ExtendedOptions, ExtendedStorage>,\n >(\n extendedConfig?:\n | (() => Partial<ExtendedConfig>)\n | (Partial<ExtendedConfig> &\n ThisType<{\n name: string\n options: ExtendedOptions\n storage: ExtendedStorage\n editor: Editor\n type: null\n }>),\n ): Extension<ExtendedOptions, ExtendedStorage> {\n // If the extended config is a function, execute it to get the configuration object\n const resolvedConfig = typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig\n return super.extend(resolvedConfig) as Extension<ExtendedOptions, ExtendedStorage>\n }\n}\n","import * as commands from '../commands/index.js'\nimport { Extension } from '../Extension.js'\n\nexport * from '../commands/index.js'\n\nexport const Commands = Extension.create({\n name: 'commands',\n\n addCommands() {\n return {\n ...commands,\n }\n },\n})\n","import { RemoveMarkStep } from '@tiptap/pm/transform'\n\nimport { Extension } from '../Extension.js'\nimport { combineTransactionSteps, getChangedRanges } from '../helpers/index.js'\n\n/**\n * This extension allows you to be notified when the user deletes content you are interested in.\n */\nexport const Delete = Extension.create({\n name: 'delete',\n\n onUpdate({ transaction, appendedTransactions }) {\n const callback = () => {\n if (\n this.editor.options.coreExtensionOptions?.delete?.filterTransaction?.(transaction) ??\n transaction.getMeta('y-sync$')\n ) {\n return\n }\n const nextTransaction = combineTransactionSteps(transaction.before, [transaction, ...appendedTransactions])\n const changes = getChangedRanges(nextTransaction)\n\n changes.forEach(change => {\n if (\n nextTransaction.mapping.mapResult(change.oldRange.from).deletedAfter &&\n nextTransaction.mapping.mapResult(change.oldRange.to).deletedBefore\n ) {\n nextTransaction.before.nodesBetween(change.oldRange.from, change.oldRange.to, (node, from) => {\n const to = from + node.nodeSize - 2\n const isFullyWithinRange = change.oldRange.from <= from && to <= change.oldRange.to\n\n this.editor.emit('delete', {\n type: 'node',\n node,\n from,\n to,\n newFrom: nextTransaction.mapping.map(from),\n newTo: nextTransaction.mapping.map(to),\n deletedRange: change.oldRange,\n newRange: change.newRange,\n partial: !isFullyWithinRange,\n editor: this.editor,\n transaction,\n combinedTransform: nextTransaction,\n })\n })\n }\n })\n\n const mapping = nextTransaction.mapping\n nextTransaction.steps.forEach((step, index) => {\n if (step instanceof RemoveMarkStep) {\n const newStart = mapping.slice(index).map(step.from, -1)\n const newEnd = mapping.slice(index).map(step.to)\n const oldStart = mapping.invert().map(newStart, -1)\n const oldEnd = mapping.invert().map(newEnd)\n\n const foundBeforeMark = nextTransaction.doc.nodeAt(newStart - 1)?.marks.some(mark => mark.eq(step.mark))\n const foundAfterMark = nextTransaction.doc.nodeAt(newEnd)?.marks.some(mark => mark.eq(step.mark))\n\n this.editor.emit('delete', {\n type: 'mark',\n mark: step.mark,\n from: step.from,\n to: step.to,\n deletedRange: {\n from: oldStart,\n to: oldEnd,\n },\n newRange: {\n from: newStart,\n to: newEnd,\n },\n partial: Boolean(foundAfterMark || foundBeforeMark),\n editor: this.editor,\n transaction,\n combinedTransform: nextTransaction,\n })\n }\n })\n }\n\n if (this.editor.options.coreExtensionOptions?.delete?.async ?? true) {\n setTimeout(callback, 0)\n } else {\n callback()\n }\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Drop = Extension.create({\n name: 'drop',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('tiptapDrop'),\n\n props: {\n handleDrop: (_, e, slice, moved) => {\n this.editor.emit('drop', {\n editor: this.editor,\n event: e,\n slice,\n moved,\n })\n },\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Editable = Extension.create({\n name: 'editable',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('editable'),\n props: {\n editable: () => this.editor.options.editable,\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const focusEventsPluginKey = new PluginKey('focusEvents')\n\nexport const FocusEvents = Extension.create({\n name: 'focusEvents',\n\n addProseMirrorPlugins() {\n const { editor } = this\n\n return [\n new Plugin({\n key: focusEventsPluginKey,\n props: {\n handleDOMEvents: {\n focus: (view, event: Event) => {\n editor.isFocused = true\n\n const transaction = editor.state.tr.setMeta('focus', { event }).setMeta('addToHistory', false)\n\n view.dispatch(transaction)\n\n return false\n },\n blur: (view, event: Event) => {\n editor.isFocused = false\n\n const transaction = editor.state.tr.setMeta('blur', { event }).setMeta('addToHistory', false)\n\n view.dispatch(transaction)\n\n return false\n },\n },\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey, Selection } from '@tiptap/pm/state'\n\nimport { CommandManager } from '../CommandManager.js'\nimport { Extension } from '../Extension.js'\nimport { createChainableState } from '../helpers/createChainableState.js'\nimport { isNodeEmpty } from '../helpers/isNodeEmpty.js'\nimport { isiOS } from '../utilities/isiOS.js'\nimport { isMacOS } from '../utilities/isMacOS.js'\n\nexport const Keymap = Extension.create({\n name: 'keymap',\n\n addKeyboardShortcuts() {\n const handleBackspace = () =>\n this.editor.commands.first(({ commands }) => [\n () => commands.undoInputRule(),\n\n // maybe convert first text block node to default node\n () =>\n commands.command(({ tr }) => {\n const { selection, doc } = tr\n const { empty, $anchor } = selection\n const { pos, parent } = $anchor\n const $parentPos = $anchor.parent.isTextblock && pos > 0 ? tr.doc.resolve(pos - 1) : $anchor\n const parentIsIsolating = $parentPos.parent.type.spec.isolating\n\n const parentPos = $anchor.pos - $anchor.parentOffset\n\n const isAtStart =\n parentIsIsolating && $parentPos.parent.childCount === 1\n ? parentPos === $anchor.pos\n : Selection.atStart(doc).from === pos\n\n if (\n !empty ||\n !parent.type.isTextblock ||\n parent.textContent.length ||\n !isAtStart ||\n (isAtStart && $anchor.parent.type.name === 'paragraph') // prevent clearNodes when no nodes to clear, otherwise history stack is appended\n ) {\n return false\n }\n\n return commands.clearNodes()\n }),\n\n () => commands.deleteSelection(),\n () => commands.joinBackward(),\n () => commands.selectNodeBackward(),\n ])\n\n const handleDelete = () =>\n this.editor.commands.first(({ commands }) => [\n () => commands.deleteSelection(),\n () => commands.deleteCurrentNode(),\n () => commands.joinForward(),\n () => commands.selectNodeForward(),\n ])\n\n const handleEnter = () =>\n this.editor.commands.first(({ commands }) => [\n () => commands.newlineInCode(),\n () => commands.createParagraphNear(),\n () => commands.liftEmptyBlock(),\n () => commands.splitBlock(),\n ])\n\n const baseKeymap = {\n Enter: handleEnter,\n 'Mod-Enter': () => this.editor.commands.exitCode(),\n Backspace: handleBackspace,\n 'Mod-Backspace': handleBackspace,\n 'Shift-Backspace': handleBackspace,\n Delete: handleDelete,\n 'Mod-Delete': handleDelete,\n 'Mod-a': () => this.editor.commands.selectAll(),\n }\n\n const pcKeymap = {\n ...baseKeymap,\n }\n\n const macKeymap = {\n ...baseKeymap,\n 'Ctrl-h': handleBackspace,\n 'Alt-Backspace': handleBackspace,\n 'Ctrl-d': handleDelete,\n 'Ctrl-Alt-Backspace': handleDelete,\n 'Alt-Delete': handleDelete,\n 'Alt-d': handleDelete,\n 'Ctrl-a': () => this.editor.commands.selectTextblockStart(),\n 'Ctrl-e': () => this.editor.commands.selectTextblockEnd(),\n }\n\n if (isiOS() || isMacOS()) {\n return macKeymap\n }\n\n return pcKeymap\n },\n\n addProseMirrorPlugins() {\n return [\n // With this plugin we check if the whole document was selected and deleted.\n // In this case we will additionally call `clearNodes()` to convert e.g. a heading\n // to a paragraph if necessary.\n // This is an alternative to ProseMirror's `AllSelection`, which doesn’t work well\n // with many other commands.\n new Plugin({\n key: new PluginKey('clearDocument'),\n appendTransaction: (transactions, oldState, newState) => {\n if (transactions.some(tr => tr.getMeta('composition'))) {\n return\n }\n\n const docChanges = transactions.some(transaction => transaction.docChanged) && !oldState.doc.eq(newState.doc)\n\n const ignoreTr = transactions.some(transaction => transaction.getMeta('preventClearDocument'))\n\n if (!docChanges || ignoreTr) {\n return\n }\n\n const { empty, from, to } = oldState.selection\n const allFrom = Selection.atStart(oldState.doc).from\n const allEnd = Selection.atEnd(oldState.doc).to\n const allWasSelected = from === allFrom && to === allEnd\n\n if (empty || !allWasSelected) {\n return\n }\n\n const isEmpty = isNodeEmpty(newState.doc)\n\n if (!isEmpty) {\n return\n }\n\n const tr = newState.tr\n const state = createChainableState({\n state: newState,\n transaction: tr,\n })\n const { commands } = new CommandManager({\n editor: this.editor,\n state,\n })\n\n commands.clearNodes()\n\n if (!tr.steps.length) {\n return\n }\n\n return tr\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Paste = Extension.create({\n name: 'paste',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('tiptapPaste'),\n\n props: {\n handlePaste: (_view, e, slice) => {\n this.editor.emit('paste', {\n editor: this.editor,\n event: e,\n slice,\n })\n },\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\n\nexport const Tabindex = Extension.create({\n name: 'tabindex',\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('tabindex'),\n props: {\n attributes: (): { [name: string]: string } => (this.editor.isEditable ? { tabindex: '0' } : {}),\n },\n }),\n ]\n },\n})\n","import { Plugin, PluginKey } from '@tiptap/pm/state'\n\nimport { Extension } from '../Extension.js'\nimport { splitExtensions } from '../helpers/splitExtensions.js'\n\nexport interface TextDirectionOptions {\n direction: 'ltr' | 'rtl' | 'auto' | undefined\n}\n\n/**\n * The TextDirection extension adds support for setting text direction (LTR/RTL/auto)\n * on all nodes in the editor.\n *\n * This extension adds a global `dir` attribute to all node types, which can be used\n * to control bidirectional text rendering. The direction can be set globally via\n * editor options or per-node using commands.\n */\nexport const TextDirection = Extension.create<TextDirectionOptions>({\n name: 'textDirection',\n\n addOptions() {\n return {\n direction: undefined,\n }\n },\n\n addGlobalAttributes() {\n // Only add the dir attribute to nodes if text direction is configured\n // This prevents null/undefined values from appearing in JSON exports\n if (!this.options.direction) {\n return []\n }\n\n const { nodeExtensions } = splitExtensions(this.extensions)\n\n return [\n {\n types: nodeExtensions.filter(extension => extension.name !== 'text').map(extension => extension.name),\n attributes: {\n dir: {\n default: this.options.direction,\n parseHTML: element => {\n const dir = element.getAttribute('dir')\n\n if (dir && (dir === 'ltr' || dir === 'rtl' || dir === 'auto')) {\n return dir\n }\n\n return this.options.direction\n },\n renderHTML: attributes => {\n if (!attributes.dir) {\n return {}\n }\n\n return {\n dir: attributes.dir,\n }\n },\n },\n },\n },\n ]\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: new PluginKey('textDirection'),\n props: {\n attributes: (): { [name: string]: string } => {\n const direction = this.options.direction\n\n if (!direction) {\n return {}\n }\n\n return {\n dir: direction,\n }\n },\n },\n }),\n ]\n },\n})\n","import type { Fragment, Node, ResolvedPos } from '@tiptap/pm/model'\n\nimport type { Editor } from './Editor.js'\nimport type { Content, Range } from './types.js'\n\nexport class NodePos {\n private resolvedPos: ResolvedPos\n\n private isBlock: boolean\n\n private editor: Editor\n\n private get name(): string {\n return this.node.type.name\n }\n\n constructor(pos: ResolvedPos, editor: Editor, isBlock = false, node: Node | null = null) {\n this.isBlock = isBlock\n this.resolvedPos = pos\n this.editor = editor\n this.currentNode = node\n }\n\n private currentNode: Node | null = null\n\n get node(): Node {\n return this.currentNode || this.resolvedPos.node()\n }\n\n get element(): HTMLElement {\n return this.editor.view.domAtPos(this.pos).node as HTMLElement\n }\n\n public actualDepth: number | null = null\n\n get depth(): number {\n return this.actualDepth ?? this.resolvedPos.depth\n }\n\n get pos(): number {\n return this.resolvedPos.pos\n }\n\n get content(): Fragment {\n return this.node.content\n }\n\n set content(content: Content) {\n let from = this.from\n let to = this.to\n\n if (this.isBlock) {\n if (this.content.size === 0) {\n console.error(`You can’t set content on a block node. Tried to set content on ${this.name} at ${this.pos}`)\n return\n }\n\n from = this.from + 1\n to = this.to - 1\n }\n\n this.editor.commands.insertContentAt({ from, to }, content)\n }\n\n get attributes(): { [key: string]: any } {\n return this.node.attrs\n }\n\n get textContent(): string {\n return this.node.textContent\n }\n\n get size(): number {\n return this.node.nodeSize\n }\n\n get from(): number {\n if (this.isBlock) {\n return this.pos\n }\n\n return this.resolvedPos.start(this.resolvedPos.depth)\n }\n\n get range(): Range {\n return {\n from: this.from,\n to: this.to,\n }\n }\n\n get to(): number {\n if (this.isBlock) {\n return this.pos + this.size\n }\n\n return this.resolvedPos.end(this.resolvedPos.depth) + (this.node.isText ? 0 : 1)\n }\n\n get parent(): NodePos | null {\n if (this.depth === 0) {\n return null\n }\n\n const parentPos = this.resolvedPos.start(this.resolvedPos.depth - 1)\n const $pos = this.resolvedPos.doc.resolve(parentPos)\n\n return new NodePos($pos, this.editor)\n }\n\n get before(): NodePos | null {\n let $pos = this.resolvedPos.doc.resolve(this.from - (this.isBlock ? 1 : 2))\n\n if ($pos.depth !== this.depth) {\n $pos = this.resolvedPos.doc.resolve(this.from - 3)\n }\n\n return new NodePos($pos, this.editor)\n }\n\n get after(): NodePos | null {\n let $pos = this.resolvedPos.doc.resolve(this.to + (this.isBlock ? 2 : 1))\n\n if ($pos.depth !== this.depth) {\n $pos = this.resolvedPos.doc.resolve(this.to + 3)\n }\n\n return new NodePos($pos, this.editor)\n }\n\n get children(): NodePos[] {\n const children: NodePos[] = []\n\n this.node.content.forEach((node, offset) => {\n const isBlock = node.isBlock && !node.isTextblock\n const isNonTextAtom = node.isAtom && !node.isText\n\n const targetPos = this.pos + offset + (isNonTextAtom ? 0 : 1)\n\n // Check if targetPos is within valid document range\n if (targetPos < 0 || targetPos > this.resolvedPos.doc.nodeSize - 2) {\n return\n }\n\n const $pos = this.resolvedPos.doc.resolve(targetPos)\n\n if (!isBlock && $pos.depth <= this.depth) {\n return\n }\n\n const childNodePos = new NodePos($pos, this.editor, isBlock, isBlock ? node : null)\n\n if (isBlock) {\n childNodePos.actualDepth = this.depth + 1\n }\n\n children.push(new NodePos($pos, this.editor, isBlock, isBlock ? node : null))\n })\n\n return children\n }\n\n get firstChild(): NodePos | null {\n return this.children[0] || null\n }\n\n get lastChild(): NodePos | null {\n const children = this.children\n\n return children[children.length - 1] || null\n }\n\n closest(selector: string, attributes: { [key: string]: any } = {}): NodePos | null {\n let node: NodePos | null = null\n let currentNode = this.parent\n\n while (currentNode && !node) {\n if (currentNode.node.type.name === selector) {\n if (Object.keys(attributes).length > 0) {\n const nodeAttributes = currentNode.node.attrs\n const attrKeys = Object.keys(attributes)\n\n for (let index = 0; index < attrKeys.length; index += 1) {\n const key = attrKeys[index]\n\n if (nodeAttributes[key] !== attributes[key]) {\n break\n }\n }\n } else {\n node = currentNode\n }\n }\n\n currentNode = currentNode.parent\n }\n\n return node\n }\n\n querySelector(selector: string, attributes: { [key: string]: any } = {}): NodePos | null {\n return this.querySelectorAll(selector, attributes, true)[0] || null\n }\n\n querySelectorAll(selector: string, attributes: { [key: string]: any } = {}, firstItemOnly = false): NodePos[] {\n let nodes: NodePos[] = []\n\n if (!this.children || this.children.length === 0) {\n return nodes\n }\n const attrKeys = Object.keys(attributes)\n\n /**\n * Finds all children recursively that match the selector and attributes\n * If firstItemOnly is true, it will return the first item found\n */\n this.children.forEach(childPos => {\n // If we already found a node and we only want the first item, we dont need to keep going\n if (firstItemOnly && nodes.length > 0) {\n return\n }\n\n if (childPos.node.type.name === selector) {\n const doesAllAttributesMatch = attrKeys.every(key => attributes[key] === childPos.node.attrs[key])\n\n if (doesAllAttributesMatch) {\n nodes.push(childPos)\n }\n }\n\n // If we already found a node and we only want the first item, we can stop here and skip the recursion\n if (firstItemOnly && nodes.length > 0) {\n return\n }\n\n nodes = nodes.concat(childPos.querySelectorAll(selector, attributes, firstItemOnly))\n })\n\n return nodes\n }\n\n setAttribute(attributes: { [key: string]: any }) {\n const { tr } = this.editor.state\n\n tr.setNodeMarkup(this.from, undefined, {\n ...this.node.attrs,\n ...attributes,\n })\n\n this.editor.view.dispatch(tr)\n }\n}\n","export const style = `.ProseMirror {\n position: relative;\n}\n\n.ProseMirror {\n word-wrap: break-word;\n white-space: pre-wrap;\n white-space: break-spaces;\n -webkit-font-variant-ligatures: none;\n font-variant-ligatures: none;\n font-feature-settings: \"liga\" 0; /* the above doesn't seem to work in Edge */\n}\n\n.ProseMirror [contenteditable=\"false\"] {\n white-space: normal;\n}\n\n.ProseMirror [contenteditable=\"false\"] [contenteditable=\"true\"] {\n white-space: pre-wrap;\n}\n\n.ProseMirror pre {\n white-space: pre-wrap;\n}\n\nimg.ProseMirror-separator {\n display: inline !important;\n border: none !important;\n margin: 0 !important;\n width: 0 !important;\n height: 0 !important;\n}\n\n.ProseMirror-gapcursor {\n display: none;\n pointer-events: none;\n position: absolute;\n margin: 0;\n}\n\n.ProseMirror-gapcursor:after {\n content: \"\";\n display: block;\n position: absolute;\n top: -2px;\n width: 20px;\n border-top: 1px solid black;\n animation: ProseMirror-cursor-blink 1.1s steps(2, start) infinite;\n}\n\n@keyframes ProseMirror-cursor-blink {\n to {\n visibility: hidden;\n }\n}\n\n.ProseMirror-hideselection *::selection {\n background: transparent;\n}\n\n.ProseMirror-hideselection *::-moz-selection {\n background: transparent;\n}\n\n.ProseMirror-hideselection * {\n caret-color: transparent;\n}\n\n.ProseMirror-focused .ProseMirror-gapcursor {\n display: block;\n}`\n","export function createStyleTag(style: string, nonce?: string, suffix?: string): HTMLStyleElement {\n const tiptapStyleTag = <HTMLStyleElement>(\n document.querySelector(`style[data-tiptap-style${suffix ? `-${suffix}` : ''}]`)\n )\n\n if (tiptapStyleTag !== null) {\n return tiptapStyleTag\n }\n\n const styleNode = document.createElement('style')\n\n if (nonce) {\n styleNode.setAttribute('nonce', nonce)\n }\n\n styleNode.setAttribute(`data-tiptap-style${suffix ? `-${suffix}` : ''}`, '')\n styleNode.innerHTML = style\n document.getElementsByTagName('head')[0].appendChild(styleNode)\n\n return styleNode\n}\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarksBetween } from '../helpers/getMarksBetween.js'\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule that adds a mark when the\n * matched text is typed into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function markInputRule(config: {\n find: InputRuleFinder\n type: MarkType\n undoable?: boolean\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match)\n\n if (attributes === false || attributes === null) {\n return null\n }\n\n const { tr } = state\n const captureGroup = match[match.length - 1]\n const fullMatch = match[0]\n\n if (captureGroup) {\n const startSpaces = fullMatch.search(/\\S/)\n const textStart = range.from + fullMatch.indexOf(captureGroup)\n const textEnd = textStart + captureGroup.length\n\n const excludedMarks = getMarksBetween(range.from, range.to, state.doc)\n .filter(item => {\n // @ts-ignore\n const excluded = item.mark.type.excluded as MarkType[]\n\n return excluded.find(type => type === config.type && type !== item.mark.type)\n })\n .filter(item => item.to > textStart)\n\n if (excludedMarks.length) {\n return null\n }\n\n if (textEnd < range.to) {\n tr.delete(textEnd, range.to)\n }\n\n if (textStart > range.from) {\n tr.delete(range.from + startSpaces, textStart)\n }\n\n const markEnd = range.from + startSpaces + captureGroup.length\n\n tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}))\n\n tr.removeStoredMark(config.type)\n }\n },\n undoable: config.undoable,\n })\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule that adds a node when the\n * matched text is typed into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function nodeInputRule(config: {\n /**\n * The regex to match.\n */\n find: InputRuleFinder\n\n /**\n * The node type to add.\n */\n type: NodeType\n\n /**\n * Whether the input rule should be undoable\n * when the user presses backspace.\n */\n undoable?: boolean\n\n /**\n * A function that returns the attributes for the node\n * can also be an object of attributes\n */\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match) || {}\n const { tr } = state\n const start = range.from\n let end = range.to\n\n const newNode = config.type.create(attributes)\n\n if (match[1]) {\n const offset = match[0].lastIndexOf(match[1])\n let matchStart = start + offset\n\n if (matchStart > end) {\n matchStart = end\n } else {\n end = matchStart + match[1].length\n }\n\n // insert last typed character\n const lastChar = match[0][match[0].length - 1]\n\n tr.insertText(lastChar, start + match[0].length - 1)\n\n // insert node from input rule\n tr.replaceWith(matchStart, end, newNode)\n } else if (match[0]) {\n const insertionStart = config.type.isInline ? start : start - 1\n\n tr.insert(insertionStart, config.type.create(attributes)).delete(tr.mapping.map(start), tr.mapping.map(end))\n }\n\n tr.scrollIntoView()\n },\n undoable: config.undoable,\n })\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule that changes the type of a textblock when the\n * matched text is typed into it. When using a regular expresion you’ll\n * probably want the regexp to start with `^`, so that the pattern can\n * only occur at the start of a textblock.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function textblockTypeInputRule(config: {\n find: InputRuleFinder\n type: NodeType\n undoable?: boolean\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n const $start = state.doc.resolve(range.from)\n const attributes = callOrReturn(config.getAttributes, undefined, match) || {}\n\n if (!$start.node(-1).canReplaceWith($start.index(-1), $start.indexAfter(-1), config.type)) {\n return null\n }\n\n state.tr.delete(range.from, range.to).setBlockType(range.from, range.from, config.type, attributes)\n },\n undoable: config.undoable,\n })\n}\n","import type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\n\n/**\n * Build an input rule that replaces text when the\n * matched text is typed into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function textInputRule(config: { find: InputRuleFinder; replace: string; undoable?: boolean }) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n let insert = config.replace\n let start = range.from\n const end = range.to\n\n if (match[1]) {\n const offset = match[0].lastIndexOf(match[1])\n\n insert += match[0].slice(offset + match[1].length)\n start += offset\n\n const cutOff = start - end\n\n if (cutOff > 0) {\n insert = match[0].slice(offset - cutOff, offset) + insert\n start = end\n }\n }\n\n state.tr.insertText(insert, start, end)\n },\n undoable: config.undoable,\n })\n}\n","import type { Node as ProseMirrorNode, NodeType } from '@tiptap/pm/model'\nimport { canJoin, findWrapping } from '@tiptap/pm/transform'\n\nimport type { Editor } from '../Editor.js'\nimport type { InputRuleFinder } from '../InputRule.js'\nimport { InputRule } from '../InputRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an input rule for automatically wrapping a textblock when a\n * given string is typed. When using a regular expresion you’ll\n * probably want the regexp to start with `^`, so that the pattern can\n * only occur at the start of a textblock.\n *\n * `type` is the type of node to wrap in.\n *\n * By default, if there’s a node with the same type above the newly\n * wrapped node, the rule will try to join those\n * two nodes. You can pass a join predicate, which takes a regular\n * expression match and the node before the wrapped node, and can\n * return a boolean to indicate whether a join should happen.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#input-rules\n */\nexport function wrappingInputRule(config: {\n find: InputRuleFinder\n type: NodeType\n keepMarks?: boolean\n keepAttributes?: boolean\n editor?: Editor\n undoable?: boolean\n getAttributes?: Record<string, any> | ((match: ExtendedRegExpMatchArray) => Record<string, any>) | false | null\n joinPredicate?: (match: ExtendedRegExpMatchArray, node: ProseMirrorNode) => boolean\n}) {\n return new InputRule({\n find: config.find,\n handler: ({ state, range, match, chain }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match) || {}\n const tr = state.tr.delete(range.from, range.to)\n const $start = tr.doc.resolve(range.from)\n const blockRange = $start.blockRange()\n const wrapping = blockRange && findWrapping(blockRange, config.type, attributes)\n\n if (!wrapping) {\n return null\n }\n\n tr.wrap(blockRange, wrapping)\n\n if (config.keepMarks && config.editor) {\n const { selection, storedMarks } = state\n const { splittableMarks } = config.editor.extensionManager\n const marks = storedMarks || (selection.$to.parentOffset && selection.$from.marks())\n\n if (marks) {\n const filteredMarks = marks.filter(mark => splittableMarks.includes(mark.type.name))\n\n tr.ensureMarks(filteredMarks)\n }\n }\n if (config.keepAttributes) {\n /** If the nodeType is `bulletList` or `orderedList` set the `nodeType` as `listItem` */\n const nodeType =\n config.type.name === 'bulletList' || config.type.name === 'orderedList' ? 'listItem' : 'taskList'\n\n chain().updateAttributes(nodeType, attributes).run()\n }\n\n const before = tr.doc.resolve(range.from - 1).nodeBefore\n\n if (\n before &&\n before.type === config.type &&\n canJoin(tr.doc, range.from - 1) &&\n (!config.joinPredicate || config.joinPredicate(match, before))\n ) {\n tr.join(range.from - 1)\n }\n },\n undoable: config.undoable,\n })\n}\n","export type Attributes = Record<string, any>\n\nexport type DOMOutputSpecElement = 0 | Attributes | DOMOutputSpecArray\n/**\n * Better describes the output of a `renderHTML` function in prosemirror\n * @see https://prosemirror.net/docs/ref/#model.DOMOutputSpec\n */\nexport type DOMOutputSpecArray =\n | [string]\n | [string, Attributes]\n | [string, 0]\n | [string, Attributes, 0]\n | [string, Attributes, DOMOutputSpecArray | 0]\n | [string, DOMOutputSpecArray]\n\n// JSX types for Tiptap's JSX runtime\n// These types only apply when using @jsxImportSource @tiptap/core\n// eslint-disable-next-line @typescript-eslint/no-namespace\nexport namespace JSX {\n export type Element = DOMOutputSpecArray\n export interface IntrinsicElements {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [key: string]: any\n }\n export interface ElementChildrenAttribute {\n children: unknown\n }\n}\n\nexport type JSXRenderer = (\n tag: 'slot' | string | ((props?: Attributes) => DOMOutputSpecArray | DOMOutputSpecElement),\n props?: Attributes,\n ...children: JSXRenderer[]\n) => DOMOutputSpecArray | DOMOutputSpecElement\n\nexport function Fragment(props: { children: JSXRenderer[] }) {\n return props.children\n}\n\nexport const h: JSXRenderer = (tag, attributes) => {\n // Treat the slot tag as the Prosemirror hole to render content into\n if (tag === 'slot') {\n return 0\n }\n\n // If the tag is a function, call it with the props\n if (tag instanceof Function) {\n return tag(attributes)\n }\n\n const { children, ...rest } = attributes ?? {}\n\n if (tag === 'svg') {\n throw new Error('SVG elements are not supported in the JSX syntax, use the array syntax instead')\n }\n\n // Otherwise, return the tag, attributes, and children\n return [tag, rest, children]\n}\n\n// See\n// https://esbuild.github.io/api/#jsx-import-source\n// https://www.typescriptlang.org/tsconfig/#jsxImportSource\n\nexport { h as createElement, h as jsx, h as jsxDEV, h as jsxs }\n","import type { Node as PMNode } from '@tiptap/pm/model'\nimport type { Decoration, DecorationSource, NodeView } from '@tiptap/pm/view'\n\nimport type { Editor } from '../Editor.js'\n\nconst isTouchEvent = (e: MouseEvent | TouchEvent): e is TouchEvent => {\n return 'touches' in e\n}\n\n/**\n * Directions where resize handles can be placed\n *\n * @example\n * - `'top'` - Top edge handle\n * - `'bottom-right'` - Bottom-right corner handle\n */\nexport type ResizableNodeViewDirection =\n | 'top'\n | 'right'\n | 'bottom'\n | 'left'\n | 'top-right'\n | 'top-left'\n | 'bottom-right'\n | 'bottom-left'\n\n/**\n * Dimensions for the resizable node in pixels\n */\nexport type ResizableNodeDimensions = {\n /** Width in pixels */\n width: number\n /** Height in pixels */\n height: number\n}\n\n/**\n * Configuration options for creating a ResizableNodeView\n *\n * @example\n * ```ts\n * new ResizableNodeView({\n * element: imgElement,\n * node,\n * getPos,\n * onResize: (width, height) => {\n * imgElement.style.width = `${width}px`\n * imgElement.style.height = `${height}px`\n * },\n * onCommit: (width, height) => {\n * editor.commands.updateAttributes('image', { width, height })\n * },\n * onUpdate: (node) => true,\n * options: {\n * directions: ['bottom-right', 'bottom-left'],\n * min: { width: 100, height: 100 },\n * preserveAspectRatio: true\n * }\n * })\n * ```\n */\nexport type ResizableNodeViewOptions = {\n /**\n * The DOM element to make resizable (e.g., an img, video, or iframe element)\n */\n element: HTMLElement\n\n /**\n * The DOM element that will hold the editable content element\n */\n contentElement?: HTMLElement\n\n /**\n * The ProseMirror node instance\n */\n node: PMNode\n\n /**\n * The Tiptap editor instance\n */\n editor: Editor\n\n /**\n * Function that returns the current position of the node in the document\n */\n getPos: () => number | undefined\n\n /**\n * Callback fired continuously during resize with current dimensions.\n * Use this to update the element's visual size in real-time.\n *\n * @param width - Current width in pixels\n * @param height - Current height in pixels\n *\n * @example\n * ```ts\n * onResize: (width, height) => {\n * element.style.width = `${width}px`\n * element.style.height = `${height}px`\n * }\n * ```\n */\n onResize?: (width: number, height: number) => void\n\n /**\n * Callback fired once when resize completes with final dimensions.\n * Use this to persist the new size to the node's attributes.\n *\n * @param width - Final width in pixels\n * @param height - Final height in pixels\n *\n * @example\n * ```ts\n * onCommit: (width, height) => {\n * const pos = getPos()\n * if (pos !== undefined) {\n * editor.commands.updateAttributes('image', { width, height })\n * }\n * }\n * ```\n */\n onCommit: (width: number, height: number) => void\n\n /**\n * Callback for handling node updates.\n * Return `true` to accept the update, `false` to reject it.\n *\n * @example\n * ```ts\n * onUpdate: (node, decorations, innerDecorations) => {\n * if (node.type !== this.node.type) return false\n * return true\n * }\n * ```\n */\n onUpdate: NodeView['update']\n\n /**\n * Optional configuration for resize behavior and styling\n */\n options?: {\n /**\n * Which resize handles to display.\n * @default ['bottom-left', 'bottom-right', 'top-left', 'top-right']\n *\n * @example\n * ```ts\n * // Only show corner handles\n * directions: ['top-left', 'top-right', 'bottom-left', 'bottom-right']\n *\n * // Only show right edge handle\n * directions: ['right']\n * ```\n */\n directions?: ResizableNodeViewDirection[]\n\n /**\n * Minimum dimensions in pixels\n * @default { width: 8, height: 8 }\n *\n * @example\n * ```ts\n * min: { width: 100, height: 50 }\n * ```\n */\n min?: Partial<ResizableNodeDimensions>\n\n /**\n * Maximum dimensions in pixels\n * @default undefined (no maximum)\n *\n * @example\n * ```ts\n * max: { width: 1000, height: 800 }\n * ```\n */\n max?: Partial<ResizableNodeDimensions>\n\n /**\n * Always preserve aspect ratio when resizing.\n * When `false`, aspect ratio is preserved only when Shift key is pressed.\n * @default false\n *\n * @example\n * ```ts\n * preserveAspectRatio: true // Always lock aspect ratio\n * ```\n */\n preserveAspectRatio?: boolean\n\n /**\n * Custom CSS class names for styling\n *\n * @example\n * ```ts\n * className: {\n * container: 'resize-container',\n * wrapper: 'resize-wrapper',\n * handle: 'resize-handle',\n * resizing: 'is-resizing'\n * }\n * ```\n */\n className?: {\n /** Class for the outer container element */\n container?: string\n /** Class for the wrapper element that contains the resizable element */\n wrapper?: string\n /** Class applied to all resize handles */\n handle?: string\n /** Class added to container while actively resizing */\n resizing?: string\n }\n\n /**\n * Optional callback for creating custom resize handle elements.\n *\n * This function allows developers to define their own handle element\n * (e.g., custom icons, classes, or styles) for a given resize direction.\n * It is called internally for each handle direction.\n *\n * @param direction - The direction of the handle being created (e.g., 'top', 'bottom-right').\n * @returns The custom handle HTMLElement.\n *\n * @example\n * ```ts\n * createCustomHandle: (direction) => {\n * const handle = document.createElement('div')\n * handle.dataset.resizeHandle = direction\n * handle.style.position = 'absolute'\n * handle.className = 'tiptap-custom-handle'\n *\n * const isTop = direction.includes('top')\n * const isBottom = direction.includes('bottom')\n * const isLeft = direction.includes('left')\n * const isRight = direction.includes('right')\n *\n * if (isTop) handle.style.top = '0'\n * if (isBottom) handle.style.bottom = '0'\n * if (isLeft) handle.style.left = '0'\n * if (isRight) handle.style.right = '0'\n *\n * // Edge handles span the full width or height\n * if (direction === 'top' || direction === 'bottom') {\n * handle.style.left = '0'\n * handle.style.right = '0'\n * }\n *\n * if (direction === 'left' || direction === 'right') {\n * handle.style.top = '0'\n * handle.style.bottom = '0'\n * }\n *\n * return handle\n * }\n * ```\n */\n createCustomHandle?: (direction: ResizableNodeViewDirection) => HTMLElement\n }\n}\n\n/**\n * A NodeView implementation that adds resize handles to any DOM element.\n *\n * This class creates a resizable node view for Tiptap/ProseMirror editors.\n * It wraps your element with resize handles and manages the resize interaction,\n * including aspect ratio preservation, min/max constraints, and keyboard modifiers.\n *\n * @example\n * ```ts\n * // Basic usage in a Tiptap extension\n * addNodeView() {\n * return ({ node, getPos }) => {\n * const img = document.createElement('img')\n * img.src = node.attrs.src\n *\n * return new ResizableNodeView({\n * element: img,\n * node,\n * getPos,\n * onResize: (width, height) => {\n * img.style.width = `${width}px`\n * img.style.height = `${height}px`\n * },\n * onCommit: (width, height) => {\n * this.editor.commands.updateAttributes('image', { width, height })\n * },\n * onUpdate: () => true,\n * options: {\n * min: { width: 100, height: 100 },\n * preserveAspectRatio: true\n * }\n * })\n * }\n * }\n * ```\n */\nexport class ResizableNodeView {\n /** The ProseMirror node instance */\n node: PMNode\n\n /** The Tiptap editor instance */\n editor: Editor\n\n /** The DOM element being made resizable */\n element: HTMLElement\n\n /** The editable DOM element inside the DOM */\n contentElement?: HTMLElement\n\n /** The outer container element (returned as NodeView.dom) */\n container: HTMLElement\n\n /** The wrapper element that contains the element and handles */\n wrapper: HTMLElement\n\n /** Function to get the current node position */\n getPos: () => number | undefined\n\n /** Callback fired during resize */\n onResize?: (width: number, height: number) => void\n\n /** Callback fired when resize completes */\n onCommit: (width: number, height: number) => void\n\n /** Callback for node updates */\n onUpdate?: NodeView['update']\n\n /** Active resize handle directions */\n directions: ResizableNodeViewDirection[] = ['bottom-left', 'bottom-right', 'top-left', 'top-right']\n\n /** Minimum allowed dimensions */\n minSize: ResizableNodeDimensions = {\n height: 8,\n width: 8,\n }\n\n /** Maximum allowed dimensions (optional) */\n maxSize?: Partial<ResizableNodeDimensions>\n\n /** Whether to always preserve aspect ratio */\n preserveAspectRatio: boolean = false\n\n /** CSS class names for elements */\n classNames = {\n container: '',\n wrapper: '',\n handle: '',\n resizing: '',\n }\n\n /** Optional callback for creating custom resize handles */\n createCustomHandle?: (direction: ResizableNodeViewDirection) => HTMLElement\n\n /** Initial width of the element (for aspect ratio calculation) */\n private initialWidth: number = 0\n\n /** Initial height of the element (for aspect ratio calculation) */\n private initialHeight: number = 0\n\n /** Calculated aspect ratio (width / height) */\n private aspectRatio: number = 1\n\n /** Whether a resize operation is currently active */\n private isResizing: boolean = false\n\n /** The handle currently being dragged */\n private activeHandle: ResizableNodeViewDirection | null = null\n\n /** Starting mouse X position when resize began */\n private startX: number = 0\n\n /** Starting mouse Y position when resize began */\n private startY: number = 0\n\n /** Element width when resize began */\n private startWidth: number = 0\n\n /** Element height when resize began */\n private startHeight: number = 0\n\n /** Whether Shift key is currently pressed (for temporary aspect ratio lock) */\n private isShiftKeyPressed: boolean = false\n\n /** Last known editable state of the editor */\n private lastEditableState: boolean | undefined = undefined\n\n /** Map of handle elements by direction */\n private handleMap = new Map<ResizableNodeViewDirection, HTMLElement>()\n\n /**\n * Creates a new ResizableNodeView instance.\n *\n * The constructor sets up the resize handles, applies initial sizing from\n * node attributes, and configures all resize behavior options.\n *\n * @param options - Configuration options for the resizable node view\n */\n constructor(options: ResizableNodeViewOptions) {\n this.node = options.node\n this.editor = options.editor\n this.element = options.element\n this.contentElement = options.contentElement\n\n this.getPos = options.getPos\n\n this.onResize = options.onResize\n this.onCommit = options.onCommit\n this.onUpdate = options.onUpdate\n\n if (options.options?.min) {\n this.minSize = {\n ...this.minSize,\n ...options.options.min,\n }\n }\n\n if (options.options?.max) {\n this.maxSize = options.options.max\n }\n\n if (options?.options?.directions) {\n this.directions = options.options.directions\n }\n\n if (options.options?.preserveAspectRatio) {\n this.preserveAspectRatio = options.options.preserveAspectRatio\n }\n\n if (options.options?.className) {\n this.classNames = {\n container: options.options.className.container || '',\n wrapper: options.options.className.wrapper || '',\n handle: options.options.className.handle || '',\n resizing: options.options.className.resizing || '',\n }\n }\n\n if (options.options?.createCustomHandle) {\n this.createCustomHandle = options.options.createCustomHandle\n }\n\n this.wrapper = this.createWrapper()\n this.container = this.createContainer()\n\n this.applyInitialSize()\n this.attachHandles()\n\n this.editor.on('update', this.handleEditorUpdate.bind(this))\n }\n\n /**\n * Returns the top-level DOM node that should be placed in the editor.\n *\n * This is required by the ProseMirror NodeView interface. The container\n * includes the wrapper, handles, and the actual content element.\n *\n * @returns The container element to be inserted into the editor\n */\n get dom() {\n return this.container\n }\n\n get contentDOM() {\n return this.contentElement\n }\n\n private handleEditorUpdate() {\n const isEditable = this.editor.isEditable\n\n // Only if state actually changed\n if (isEditable === this.lastEditableState) {\n return\n }\n\n this.lastEditableState = isEditable\n\n if (!isEditable) {\n this.removeHandles()\n } else if (isEditable && this.handleMap.size === 0) {\n this.attachHandles()\n }\n }\n\n /**\n * Called when the node's content or attributes change.\n *\n * Updates the internal node reference. If a custom `onUpdate` callback\n * was provided, it will be called to handle additional update logic.\n *\n * @param node - The new/updated node\n * @param decorations - Node decorations\n * @param innerDecorations - Inner decorations\n * @returns `false` if the node type has changed (requires full rebuild), otherwise the result of `onUpdate` or `true`\n */\n update(node: PMNode, decorations: readonly Decoration[], innerDecorations: DecorationSource): boolean {\n if (node.type !== this.node.type) {\n return false\n }\n\n this.node = node\n\n if (this.onUpdate) {\n return this.onUpdate(node, decorations, innerDecorations)\n }\n\n return true\n }\n\n /**\n * Cleanup method called when the node view is being removed.\n *\n * Removes all event listeners to prevent memory leaks. This is required\n * by the ProseMirror NodeView interface. If a resize is active when\n * destroy is called, it will be properly cancelled.\n */\n destroy() {\n if (this.isResizing) {\n this.container.dataset.resizeState = 'false'\n\n if (this.classNames.resizing) {\n this.container.classList.remove(this.classNames.resizing)\n }\n\n document.removeEventListener('mousemove', this.handleMouseMove)\n document.removeEventListener('mouseup', this.handleMouseUp)\n document.removeEventListener('keydown', this.handleKeyDown)\n document.removeEventListener('keyup', this.handleKeyUp)\n this.isResizing = false\n this.activeHandle = null\n }\n\n this.editor.off('update', this.handleEditorUpdate.bind(this))\n\n this.container.remove()\n }\n\n /**\n * Creates the outer container element.\n *\n * The container is the top-level element returned by the NodeView and\n * wraps the entire resizable node. It's set up with flexbox to handle\n * alignment and includes data attributes for styling and identification.\n *\n * @returns The container element\n */\n createContainer() {\n const element = document.createElement('div')\n element.dataset.resizeContainer = ''\n element.dataset.node = this.node.type.name\n element.style.display = 'flex'\n\n if (this.classNames.container) {\n element.className = this.classNames.container\n }\n\n element.appendChild(this.wrapper)\n\n return element\n }\n\n /**\n * Creates the wrapper element that contains the content and handles.\n *\n * The wrapper uses relative positioning so that resize handles can be\n * positioned absolutely within it. This is the direct parent of the\n * content element being made resizable.\n *\n * @returns The wrapper element\n */\n createWrapper() {\n const element = document.createElement('div')\n element.style.position = 'relative'\n element.style.display = 'block'\n element.dataset.resizeWrapper = ''\n\n if (this.classNames.wrapper) {\n element.className = this.classNames.wrapper\n }\n\n element.appendChild(this.element)\n\n return element\n }\n\n /**\n * Creates a resize handle element for a specific direction.\n *\n * Each handle is absolutely positioned and includes a data attribute\n * identifying its direction for styling purposes.\n *\n * @param direction - The resize direction for this handle\n * @returns The handle element\n */\n private createHandle(direction: ResizableNodeViewDirection): HTMLElement {\n const handle = document.createElement('div')\n handle.dataset.resizeHandle = direction\n handle.style.position = 'absolute'\n\n if (this.classNames.handle) {\n handle.className = this.classNames.handle\n }\n\n return handle\n }\n\n /**\n * Positions a handle element according to its direction.\n *\n * Corner handles (e.g., 'top-left') are positioned at the intersection\n * of two edges. Edge handles (e.g., 'top') span the full width or height.\n *\n * @param handle - The handle element to position\n * @param direction - The direction determining the position\n */\n private positionHandle(handle: HTMLElement, direction: ResizableNodeViewDirection): void {\n const isTop = direction.includes('top')\n const isBottom = direction.includes('bottom')\n const isLeft = direction.includes('left')\n const isRight = direction.includes('right')\n\n if (isTop) {\n handle.style.top = '0'\n }\n\n if (isBottom) {\n handle.style.bottom = '0'\n }\n\n if (isLeft) {\n handle.style.left = '0'\n }\n\n if (isRight) {\n handle.style.right = '0'\n }\n\n // Edge handles span the full width or height\n if (direction === 'top' || direction === 'bottom') {\n handle.style.left = '0'\n handle.style.right = '0'\n }\n\n if (direction === 'left' || direction === 'right') {\n handle.style.top = '0'\n handle.style.bottom = '0'\n }\n }\n\n /**\n * Creates and attaches all resize handles to the wrapper.\n *\n * Iterates through the configured directions, creates a handle for each,\n * positions it, attaches the mousedown listener, and appends it to the DOM.\n */\n private attachHandles(): void {\n this.directions.forEach(direction => {\n let handle: HTMLElement\n\n if (this.createCustomHandle) {\n handle = this.createCustomHandle(direction)\n } else {\n handle = this.createHandle(direction)\n }\n\n if (!(handle instanceof HTMLElement)) {\n console.warn(\n `[ResizableNodeView] createCustomHandle(\"${direction}\") did not return an HTMLElement. Falling back to default handle.`,\n )\n handle = this.createHandle(direction)\n }\n\n if (!this.createCustomHandle) {\n this.positionHandle(handle, direction)\n }\n\n handle.addEventListener('mousedown', event => this.handleResizeStart(event, direction))\n handle.addEventListener('touchstart', event => this.handleResizeStart(event as unknown as MouseEvent, direction))\n\n this.handleMap.set(direction, handle)\n\n this.wrapper.appendChild(handle)\n })\n }\n\n /**\n * Removes all resize handles from the wrapper.\n *\n * Cleans up the handle map and removes each handle element from the DOM.\n */\n private removeHandles(): void {\n this.handleMap.forEach(el => el.remove())\n this.handleMap.clear()\n }\n\n /**\n * Applies initial sizing from node attributes to the element.\n *\n * If width/height attributes exist on the node, they're applied to the element.\n * Otherwise, the element's natural/current dimensions are measured. The aspect\n * ratio is calculated for later use in aspect-ratio-preserving resizes.\n */\n private applyInitialSize(): void {\n const width = this.node.attrs.width as number | undefined\n const height = this.node.attrs.height as number | undefined\n\n if (width) {\n this.element.style.width = `${width}px`\n this.initialWidth = width\n } else {\n this.initialWidth = this.element.offsetWidth\n }\n\n if (height) {\n this.element.style.height = `${height}px`\n this.initialHeight = height\n } else {\n this.initialHeight = this.element.offsetHeight\n }\n\n // Calculate aspect ratio for use during resizing\n if (this.initialWidth > 0 && this.initialHeight > 0) {\n this.aspectRatio = this.initialWidth / this.initialHeight\n }\n }\n\n /**\n * Initiates a resize operation when a handle is clicked.\n *\n * Captures the starting mouse position and element dimensions, sets up\n * the resize state, adds the resizing class and state attribute, and\n * attaches document-level listeners for mouse movement and keyboard input.\n *\n * @param event - The mouse down event\n * @param direction - The direction of the handle being dragged\n */\n private handleResizeStart(event: MouseEvent | TouchEvent, direction: ResizableNodeViewDirection): void {\n event.preventDefault()\n event.stopPropagation()\n\n // Capture initial state\n this.isResizing = true\n this.activeHandle = direction\n\n if (isTouchEvent(event)) {\n this.startX = event.touches[0].clientX\n this.startY = event.touches[0].clientY\n } else {\n this.startX = event.clientX\n this.startY = event.clientY\n }\n\n this.startWidth = this.element.offsetWidth\n this.startHeight = this.element.offsetHeight\n\n // Recalculate aspect ratio at resize start for accuracy\n if (this.startWidth > 0 && this.startHeight > 0) {\n this.aspectRatio = this.startWidth / this.startHeight\n }\n\n const pos = this.getPos()\n if (pos !== undefined) {\n // TODO: Select the node in the editor\n }\n\n // Update UI state\n this.container.dataset.resizeState = 'true'\n\n if (this.classNames.resizing) {\n this.container.classList.add(this.classNames.resizing)\n }\n\n // Attach document-level listeners for resize\n document.addEventListener('mousemove', this.handleMouseMove)\n document.addEventListener('touchmove', this.handleTouchMove)\n document.addEventListener('mouseup', this.handleMouseUp)\n document.addEventListener('keydown', this.handleKeyDown)\n document.addEventListener('keyup', this.handleKeyUp)\n }\n\n /**\n * Handles mouse movement during an active resize.\n *\n * Calculates the delta from the starting position, computes new dimensions\n * based on the active handle direction, applies constraints and aspect ratio,\n * then updates the element's style and calls the onResize callback.\n *\n * @param event - The mouse move event\n */\n private handleMouseMove = (event: MouseEvent): void => {\n if (!this.isResizing || !this.activeHandle) {\n return\n }\n\n const deltaX = event.clientX - this.startX\n const deltaY = event.clientY - this.startY\n\n this.handleResize(deltaX, deltaY)\n }\n\n private handleTouchMove = (event: TouchEvent): void => {\n if (!this.isResizing || !this.activeHandle) {\n return\n }\n\n const touch = event.touches[0]\n if (!touch) {\n return\n }\n\n const deltaX = touch.clientX - this.startX\n const deltaY = touch.clientY - this.startY\n\n this.handleResize(deltaX, deltaY)\n }\n\n private handleResize(deltaX: number, deltaY: number) {\n if (!this.activeHandle) {\n return\n }\n\n const shouldPreserveAspectRatio = this.preserveAspectRatio || this.isShiftKeyPressed\n const { width, height } = this.calculateNewDimensions(this.activeHandle, deltaX, deltaY)\n const constrained = this.applyConstraints(width, height, shouldPreserveAspectRatio)\n\n this.element.style.width = `${constrained.width}px`\n this.element.style.height = `${constrained.height}px`\n\n if (this.onResize) {\n this.onResize(constrained.width, constrained.height)\n }\n }\n\n /**\n * Completes the resize operation when the mouse button is released.\n *\n * Captures final dimensions, calls the onCommit callback to persist changes,\n * removes the resizing state and class, and cleans up document-level listeners.\n */\n private handleMouseUp = (): void => {\n if (!this.isResizing) {\n return\n }\n\n const finalWidth = this.element.offsetWidth\n const finalHeight = this.element.offsetHeight\n\n this.onCommit(finalWidth, finalHeight)\n\n this.isResizing = false\n this.activeHandle = null\n\n // Remove UI state\n this.container.dataset.resizeState = 'false'\n\n if (this.classNames.resizing) {\n this.container.classList.remove(this.classNames.resizing)\n }\n\n // Clean up document-level listeners\n document.removeEventListener('mousemove', this.handleMouseMove)\n document.removeEventListener('mouseup', this.handleMouseUp)\n document.removeEventListener('keydown', this.handleKeyDown)\n document.removeEventListener('keyup', this.handleKeyUp)\n }\n\n /**\n * Tracks Shift key state to enable temporary aspect ratio locking.\n *\n * When Shift is pressed during resize, aspect ratio is preserved even if\n * preserveAspectRatio is false.\n *\n * @param event - The keyboard event\n */\n private handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Shift') {\n this.isShiftKeyPressed = true\n }\n }\n\n /**\n * Tracks Shift key release to disable temporary aspect ratio locking.\n *\n * @param event - The keyboard event\n */\n private handleKeyUp = (event: KeyboardEvent): void => {\n if (event.key === 'Shift') {\n this.isShiftKeyPressed = false\n }\n }\n\n /**\n * Calculates new dimensions based on mouse delta and resize direction.\n *\n * Takes the starting dimensions and applies the mouse movement delta\n * according to the handle direction. For corner handles, both dimensions\n * are affected. For edge handles, only one dimension changes. If aspect\n * ratio should be preserved, delegates to applyAspectRatio.\n *\n * @param direction - The active resize handle direction\n * @param deltaX - Horizontal mouse movement since resize start\n * @param deltaY - Vertical mouse movement since resize start\n * @returns The calculated width and height\n */\n private calculateNewDimensions(\n direction: ResizableNodeViewDirection,\n deltaX: number,\n deltaY: number,\n ): ResizableNodeDimensions {\n let newWidth = this.startWidth\n let newHeight = this.startHeight\n\n const isRight = direction.includes('right')\n const isLeft = direction.includes('left')\n const isBottom = direction.includes('bottom')\n const isTop = direction.includes('top')\n\n // Apply horizontal delta\n if (isRight) {\n newWidth = this.startWidth + deltaX\n } else if (isLeft) {\n newWidth = this.startWidth - deltaX\n }\n\n // Apply vertical delta\n if (isBottom) {\n newHeight = this.startHeight + deltaY\n } else if (isTop) {\n newHeight = this.startHeight - deltaY\n }\n\n // For pure horizontal/vertical handles, only one dimension changes\n if (direction === 'right' || direction === 'left') {\n newWidth = this.startWidth + (isRight ? deltaX : -deltaX)\n }\n\n if (direction === 'top' || direction === 'bottom') {\n newHeight = this.startHeight + (isBottom ? deltaY : -deltaY)\n }\n\n const shouldPreserveAspectRatio = this.preserveAspectRatio || this.isShiftKeyPressed\n\n if (shouldPreserveAspectRatio) {\n return this.applyAspectRatio(newWidth, newHeight, direction)\n }\n\n return { width: newWidth, height: newHeight }\n }\n\n /**\n * Applies min/max constraints to dimensions.\n *\n * When aspect ratio is NOT preserved, constraints are applied independently\n * to width and height. When aspect ratio IS preserved, constraints are\n * applied while maintaining the aspect ratio—if one dimension hits a limit,\n * the other is recalculated proportionally.\n *\n * This ensures that aspect ratio is never broken when constrained.\n *\n * @param width - The unconstrained width\n * @param height - The unconstrained height\n * @param preserveAspectRatio - Whether to maintain aspect ratio while constraining\n * @returns The constrained dimensions\n */\n private applyConstraints(width: number, height: number, preserveAspectRatio: boolean): ResizableNodeDimensions {\n if (!preserveAspectRatio) {\n // Independent constraints for each dimension\n let constrainedWidth = Math.max(this.minSize.width, width)\n let constrainedHeight = Math.max(this.minSize.height, height)\n\n if (this.maxSize?.width) {\n constrainedWidth = Math.min(this.maxSize.width, constrainedWidth)\n }\n\n if (this.maxSize?.height) {\n constrainedHeight = Math.min(this.maxSize.height, constrainedHeight)\n }\n\n return { width: constrainedWidth, height: constrainedHeight }\n }\n\n // Aspect-ratio-aware constraints: adjust both dimensions proportionally\n let constrainedWidth = width\n let constrainedHeight = height\n\n // Check minimum constraints\n if (constrainedWidth < this.minSize.width) {\n constrainedWidth = this.minSize.width\n constrainedHeight = constrainedWidth / this.aspectRatio\n }\n\n if (constrainedHeight < this.minSize.height) {\n constrainedHeight = this.minSize.height\n constrainedWidth = constrainedHeight * this.aspectRatio\n }\n\n // Check maximum constraints\n if (this.maxSize?.width && constrainedWidth > this.maxSize.width) {\n constrainedWidth = this.maxSize.width\n constrainedHeight = constrainedWidth / this.aspectRatio\n }\n\n if (this.maxSize?.height && constrainedHeight > this.maxSize.height) {\n constrainedHeight = this.maxSize.height\n constrainedWidth = constrainedHeight * this.aspectRatio\n }\n\n return { width: constrainedWidth, height: constrainedHeight }\n }\n\n /**\n * Adjusts dimensions to maintain the original aspect ratio.\n *\n * For horizontal handles (left/right), uses width as the primary dimension\n * and calculates height from it. For vertical handles (top/bottom), uses\n * height as primary and calculates width. For corner handles, uses width\n * as the primary dimension.\n *\n * @param width - The new width\n * @param height - The new height\n * @param direction - The active resize direction\n * @returns Dimensions adjusted to preserve aspect ratio\n */\n private applyAspectRatio(\n width: number,\n height: number,\n direction: ResizableNodeViewDirection,\n ): ResizableNodeDimensions {\n const isHorizontal = direction === 'left' || direction === 'right'\n const isVertical = direction === 'top' || direction === 'bottom'\n\n if (isHorizontal) {\n // For horizontal resize, width is primary\n return {\n width,\n height: width / this.aspectRatio,\n }\n }\n\n if (isVertical) {\n // For vertical resize, height is primary\n return {\n width: height * this.aspectRatio,\n height,\n }\n }\n\n // For corner resize, width is primary\n return {\n width,\n height: width / this.aspectRatio,\n }\n }\n}\n\n/**\n * Alias for ResizableNodeView to maintain consistent naming.\n * @deprecated Use ResizableNodeView instead - will be removed in future versions.\n */\nexport const ResizableNodeview = ResizableNodeView\n","import type { NodeType } from '@tiptap/pm/model'\nimport { type EditorState, NodeSelection } from '@tiptap/pm/state'\n\nexport function canInsertNode(state: EditorState, nodeType: NodeType): boolean {\n const { selection } = state\n const { $from } = selection\n\n // Special handling for NodeSelection\n if (selection instanceof NodeSelection) {\n const index = $from.index()\n const parent = $from.parent\n\n // Can we replace the selected node with the horizontal rule?\n return parent.canReplaceWith(index, index + 1, nodeType)\n }\n\n // Default: check if we can insert at the current position\n let depth = $from.depth\n\n while (depth >= 0) {\n const index = $from.index(depth)\n const parent = $from.node(depth)\n const match = parent.contentMatchAt(index)\n if (match.matchType(nodeType)) {\n return true\n }\n depth -= 1\n }\n return false\n}\n","// source: https://stackoverflow.com/a/6969486\nexport function escapeForRegEx(string: string): string {\n return string.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&')\n}\n","export function isString(value: any): value is string {\n return typeof value === 'string'\n}\n","/**\n * @fileoverview Markdown utilities for creating standardized markdown specs.\n *\n * This module provides utilities for creating complete markdown specifications\n * for different types of nodes using unified syntax patterns.\n */\n\nexport * from './attributeUtils.js'\nexport * from './createAtomBlockMarkdownSpec.js'\nexport * from './createBlockMarkdownSpec.js'\nexport * from './createInlineMarkdownSpec.js'\nexport * from './parseIndentedBlocks.js'\nexport * from './renderNestedMarkdownContent.js'\n","/**\n * @fileoverview Utility functions for parsing and serializing markdown attributes.\n *\n * These utilities handle the common patterns for parsing attribute strings\n * in various markdown syntaxes like Pandoc attributes.\n */\n\n/**\n * Parses a Pandoc-style attribute string into an object.\n *\n * Supports the following patterns:\n * - Classes: `.className` → `{ class: 'className' }`\n * - IDs: `#myId` → `{ id: 'myId' }`\n * - Key-value pairs: `key=\"value\"` → `{ key: 'value' }`\n * - Boolean attributes: `disabled` → `{ disabled: true }`\n *\n * @param attrString - The attribute string to parse\n * @returns Parsed attributes object\n *\n * @example\n * ```ts\n * parseAttributes('.btn #submit disabled type=\"button\"')\n * // → { class: 'btn', id: 'submit', disabled: true, type: 'button' }\n * ```\n */\nexport function parseAttributes(attrString: string): Record<string, any> {\n if (!attrString?.trim()) {\n return {}\n }\n\n const attributes: Record<string, any> = {}\n\n // First, extract and remove quoted strings to avoid parsing content inside them\n const quotedStrings: string[] = []\n const tempString = attrString.replace(/[\"']([^\"']*)[\"']/g, match => {\n quotedStrings.push(match)\n return `__QUOTED_${quotedStrings.length - 1}__`\n })\n\n // Parse classes (.className) - only outside of quoted strings\n const classMatches = tempString.match(/(?:^|\\s)\\.([a-zA-Z][\\w-]*)/g)\n if (classMatches) {\n const classes = classMatches.map(match => match.trim().slice(1)) // Remove the dot\n attributes.class = classes.join(' ')\n }\n\n // Parse IDs (#myId) - only outside of quoted strings\n const idMatch = tempString.match(/(?:^|\\s)#([a-zA-Z][\\w-]*)/)\n if (idMatch) {\n attributes.id = idMatch[1]\n }\n\n // Parse key-value pairs (key=\"value\" or key='value') - restore quoted strings\n const kvRegex = /([a-zA-Z][\\w-]*)\\s*=\\s*(__QUOTED_\\d+__)/g\n const kvMatches = Array.from(tempString.matchAll(kvRegex))\n kvMatches.forEach(([, key, quotedRef]) => {\n const quotedIndex = parseInt(quotedRef.match(/__QUOTED_(\\d+)__/)?.[1] || '0', 10)\n const quotedValue = quotedStrings[quotedIndex]\n if (quotedValue) {\n // Remove the outer quotes\n attributes[key] = quotedValue.slice(1, -1)\n }\n })\n\n // Parse boolean attributes (standalone words that aren't classes/IDs)\n const cleanString = tempString\n .replace(/(?:^|\\s)\\.([a-zA-Z][\\w-]*)/g, '') // Remove classes\n .replace(/(?:^|\\s)#([a-zA-Z][\\w-]*)/g, '') // Remove IDs\n .replace(/([a-zA-Z][\\w-]*)\\s*=\\s*__QUOTED_\\d+__/g, '') // Remove key-value pairs\n .trim()\n\n if (cleanString) {\n const booleanAttrs = cleanString.split(/\\s+/).filter(Boolean)\n booleanAttrs.forEach(attr => {\n if (attr.match(/^[a-zA-Z][\\w-]*$/)) {\n attributes[attr] = true\n }\n })\n }\n\n return attributes\n}\n\n/**\n * Serializes an attributes object back to a Pandoc-style attribute string.\n *\n * @param attributes - The attributes object to serialize\n * @returns Serialized attribute string\n *\n * @example\n * ```ts\n * serializeAttributes({ class: 'btn primary', id: 'submit', disabled: true, type: 'button' })\n * // → '.btn.primary #submit disabled type=\"button\"'\n * ```\n */\nexport function serializeAttributes(attributes: Record<string, any>): string {\n if (!attributes || Object.keys(attributes).length === 0) {\n return ''\n }\n\n const parts: string[] = []\n\n // Handle classes\n if (attributes.class) {\n const classes = String(attributes.class).split(/\\s+/).filter(Boolean)\n classes.forEach(cls => parts.push(`.${cls}`))\n }\n\n // Handle ID\n if (attributes.id) {\n parts.push(`#${attributes.id}`)\n }\n\n // Handle other attributes\n Object.entries(attributes).forEach(([key, value]) => {\n if (key === 'class' || key === 'id') {\n return // Already handled\n }\n\n if (value === true) {\n // Boolean attribute\n parts.push(key)\n } else if (value !== false && value != null) {\n // Key-value attribute\n parts.push(`${key}=\"${String(value)}\"`)\n }\n })\n\n return parts.join(' ')\n}\n","import type {\n JSONContent,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownToken,\n MarkdownTokenizer,\n} from '../../types.js'\nimport {\n parseAttributes as defaultParseAttributes,\n serializeAttributes as defaultSerializeAttributes,\n} from './attributeUtils.js'\n\nexport interface AtomBlockMarkdownSpecOptions {\n /** The Tiptap node name this spec is for */\n nodeName: string\n /** The markdown syntax name (defaults to nodeName if not provided) */\n name?: string\n /** Function to parse attributes from token attribute string */\n parseAttributes?: (attrString: string) => Record<string, any>\n /** Function to serialize attributes back to string for rendering */\n serializeAttributes?: (attrs: Record<string, any>) => string\n /** Default attributes to apply when parsing */\n defaultAttributes?: Record<string, any>\n /** Required attributes that must be present for successful parsing */\n requiredAttributes?: string[]\n /** Attributes that are allowed to be rendered back to markdown (whitelist) */\n allowedAttributes?: string[]\n}\n\n/**\n * Creates a complete markdown spec for atomic block nodes using Pandoc syntax.\n *\n * The generated spec handles:\n * - Parsing self-closing blocks with `:::blockName {attributes}`\n * - Extracting and parsing attributes\n * - Validating required attributes\n * - Rendering blocks back to markdown\n *\n * @param options - Configuration for the atomic block markdown spec\n * @returns Complete markdown specification object\n *\n * @example\n * ```ts\n * const youtubeSpec = createAtomBlockMarkdownSpec({\n * nodeName: 'youtube',\n * requiredAttributes: ['src'],\n * defaultAttributes: { start: 0 },\n * allowedAttributes: ['src', 'start', 'width', 'height'] // Only these get rendered to markdown\n * })\n *\n * // Usage in extension:\n * export const Youtube = Node.create({\n * // ... other config\n * markdown: youtubeSpec\n * })\n * ```\n */\nexport function createAtomBlockMarkdownSpec(options: AtomBlockMarkdownSpecOptions): {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => MarkdownParseResult\n markdownTokenizer: MarkdownTokenizer\n renderMarkdown: (node: JSONContent) => string\n} {\n const {\n nodeName,\n name: markdownName,\n parseAttributes = defaultParseAttributes,\n serializeAttributes = defaultSerializeAttributes,\n defaultAttributes = {},\n requiredAttributes = [],\n allowedAttributes,\n } = options\n\n // Use markdownName for syntax, fallback to nodeName\n const blockName = markdownName || nodeName\n\n // Helper function to filter attributes based on allowlist\n const filterAttributes = (attrs: Record<string, any>) => {\n if (!allowedAttributes) {\n return attrs\n }\n\n const filtered: Record<string, any> = {}\n allowedAttributes.forEach(key => {\n if (key in attrs) {\n filtered[key] = attrs[key]\n }\n })\n return filtered\n }\n\n return {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => {\n const attrs = { ...defaultAttributes, ...token.attributes }\n return h.createNode(nodeName, attrs, [])\n },\n\n markdownTokenizer: {\n name: nodeName,\n level: 'block' as const,\n start(src: string) {\n const regex = new RegExp(`^:::${blockName}(?:\\\\s|$)`, 'm')\n const index = src.match(regex)?.index\n return index !== undefined ? index : -1\n },\n tokenize(src, _tokens, _lexer) {\n // Use non-global regex to match from the start of the string\n // Include optional newline to ensure we consume the entire line\n const regex = new RegExp(`^:::${blockName}(?:\\\\s+\\\\{([^}]*)\\\\})?\\\\s*:::(?:\\\\n|$)`)\n const match = src.match(regex)\n\n if (!match) {\n return undefined\n }\n\n // Parse attributes if present\n const attrString = match[1] || ''\n const attributes = parseAttributes(attrString)\n\n // Validate required attributes\n const missingRequired = requiredAttributes.find(required => !(required in attributes))\n if (missingRequired) {\n return undefined\n }\n\n return {\n type: nodeName,\n raw: match[0],\n attributes,\n }\n },\n },\n\n renderMarkdown: node => {\n const filteredAttrs = filterAttributes(node.attrs || {})\n const attrs = serializeAttributes(filteredAttrs)\n const attrString = attrs ? ` {${attrs}}` : ''\n\n return `:::${blockName}${attrString} :::`\n },\n }\n}\n","import type {\n JSONContent,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownRendererHelpers,\n MarkdownToken,\n MarkdownTokenizer,\n} from '../../types.js'\nimport {\n parseAttributes as defaultParseAttributes,\n serializeAttributes as defaultSerializeAttributes,\n} from './attributeUtils.js'\n\nexport interface BlockMarkdownSpecOptions {\n /** The Tiptap node name this spec is for */\n nodeName: string\n /** The markdown syntax name (defaults to nodeName if not provided) */\n name?: string\n /** Function to extract content from the node for serialization */\n getContent?: (token: MarkdownToken) => string\n /** Function to parse attributes from the attribute string */\n parseAttributes?: (attrString: string) => Record<string, any>\n /** Function to serialize attributes to string */\n serializeAttributes?: (attrs: Record<string, any>) => string\n /** Default attributes to apply when parsing */\n defaultAttributes?: Record<string, any>\n /** Content type: 'block' allows paragraphs/lists/etc, 'inline' only allows bold/italic/links/etc */\n content?: 'block' | 'inline'\n /** Allowlist of attributes to include in markdown (if not provided, all attributes are included) */\n allowedAttributes?: string[]\n}\n\n/**\n * Creates a complete markdown spec for block-level nodes using Pandoc syntax.\n *\n * The generated spec handles:\n * - Parsing blocks with `:::blockName {attributes}` syntax\n * - Extracting and parsing attributes\n * - Rendering blocks back to markdown with proper formatting\n * - Nested content support\n *\n * @param options - Configuration for the block markdown spec\n * @returns Complete markdown specification object\n *\n * @example\n * ```ts\n * const calloutSpec = createBlockMarkdownSpec({\n * nodeName: 'callout',\n * defaultAttributes: { type: 'info' },\n * allowedAttributes: ['type', 'title'] // Only these get rendered to markdown\n * })\n *\n * // Usage in extension:\n * export const Callout = Node.create({\n * // ... other config\n * markdown: calloutSpec\n * })\n * ```\n */\nexport function createBlockMarkdownSpec(options: BlockMarkdownSpecOptions): {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => MarkdownParseResult\n markdownTokenizer: MarkdownTokenizer\n renderMarkdown: (node: JSONContent, h: MarkdownRendererHelpers) => string\n} {\n const {\n nodeName,\n name: markdownName,\n getContent,\n parseAttributes = defaultParseAttributes,\n serializeAttributes = defaultSerializeAttributes,\n defaultAttributes = {},\n content = 'block',\n allowedAttributes,\n } = options\n\n // Use markdownName for syntax, fallback to nodeName\n const blockName = markdownName || nodeName\n\n // Helper function to filter attributes based on allowlist\n const filterAttributes = (attrs: Record<string, any>) => {\n if (!allowedAttributes) {\n return attrs\n }\n\n const filtered: Record<string, any> = {}\n allowedAttributes.forEach(key => {\n if (key in attrs) {\n filtered[key] = attrs[key]\n }\n })\n return filtered\n }\n\n return {\n parseMarkdown: (token, h) => {\n let nodeContent: JSONContent[]\n\n if (getContent) {\n const contentResult = getContent(token)\n // If getContent returns a string, wrap it in a text node\n nodeContent = typeof contentResult === 'string' ? [{ type: 'text', text: contentResult }] : contentResult\n } else if (content === 'block') {\n nodeContent = h.parseChildren(token.tokens || [])\n } else {\n nodeContent = h.parseInline(token.tokens || [])\n }\n\n const attrs = { ...defaultAttributes, ...token.attributes }\n\n return h.createNode(nodeName, attrs, nodeContent)\n },\n\n markdownTokenizer: {\n name: nodeName,\n level: 'block' as const,\n start(src) {\n const regex = new RegExp(`^:::${blockName}`, 'm')\n const index = src.match(regex)?.index\n return index !== undefined ? index : -1\n },\n tokenize(src, _tokens, lexer) {\n // Match the opening tag with optional attributes\n const openingRegex = new RegExp(`^:::${blockName}(?:\\\\s+\\\\{([^}]*)\\\\})?\\\\s*\\\\n`)\n const openingMatch = src.match(openingRegex)\n\n if (!openingMatch) {\n return undefined\n }\n\n const [openingTag, attrString = ''] = openingMatch\n const attributes = parseAttributes(attrString)\n\n // Find the matching closing tag by tracking nesting level\n let level = 1\n const position = openingTag.length\n let matchedContent = ''\n\n // Pattern to match any block opening (:::word) or closing (:::)\n const blockPattern = /^:::([\\w-]*)(\\s.*)?/gm\n const remaining = src.slice(position)\n\n blockPattern.lastIndex = 0\n\n // run until no more matches are found\n for (;;) {\n const match = blockPattern.exec(remaining)\n if (match === null) {\n break\n }\n const matchPos = match.index\n const blockType = match[1] // Empty string for closing tag, block name for opening\n\n if (match[2]?.endsWith(':::')) {\n // this is an atom ::: node, we skip it\n continue\n }\n\n if (blockType) {\n // Opening tag found - increase level\n level += 1\n } else {\n // Closing tag found - decrease level\n level -= 1\n\n if (level === 0) {\n // Found our matching closing tag\n // Don't trim yet - keep newlines for tokenizer regex matching\n const rawContent = remaining.slice(0, matchPos)\n matchedContent = rawContent.trim()\n const fullMatch = src.slice(0, position + matchPos + match[0].length)\n\n // Tokenize the content\n let contentTokens: MarkdownToken[] = []\n if (matchedContent) {\n if (content === 'block') {\n // Use rawContent for tokenization to preserve line boundaries for regex matching\n contentTokens = lexer.blockTokens(rawContent)\n\n // Parse inline tokens for any token that has text content but no tokens\n contentTokens.forEach(token => {\n if (token.text && (!token.tokens || token.tokens.length === 0)) {\n token.tokens = lexer.inlineTokens(token.text)\n }\n })\n\n // Clean up empty trailing paragraphs\n while (contentTokens.length > 0) {\n const lastToken = contentTokens[contentTokens.length - 1]\n if (lastToken.type === 'paragraph' && (!lastToken.text || lastToken.text.trim() === '')) {\n contentTokens.pop()\n } else {\n break\n }\n }\n } else {\n contentTokens = lexer.inlineTokens(matchedContent)\n }\n }\n\n return {\n type: nodeName,\n raw: fullMatch,\n attributes,\n content: matchedContent,\n tokens: contentTokens,\n }\n }\n }\n }\n\n // No matching closing tag found\n return undefined\n },\n },\n\n renderMarkdown: (node, h) => {\n const filteredAttrs = filterAttributes(node.attrs || {})\n const attrs = serializeAttributes(filteredAttrs)\n const attrString = attrs ? ` {${attrs}}` : ''\n const renderedContent = h.renderChildren(node.content || [], '\\n\\n')\n\n return `:::${blockName}${attrString}\\n\\n${renderedContent}\\n\\n:::`\n },\n }\n}\n","import type {\n JSONContent,\n MarkdownParseHelpers,\n MarkdownParseResult,\n MarkdownToken,\n MarkdownTokenizer,\n} from '../../types.js'\n\n/**\n * Parse shortcode attributes like 'id=\"madonna\" handle=\"john\" name=\"John Doe\"'\n * Requires all values to be quoted with either single or double quotes\n */\nfunction parseShortcodeAttributes(attrString: string): Record<string, any> {\n if (!attrString.trim()) {\n return {}\n }\n\n const attributes: Record<string, any> = {}\n // Match key=value pairs, only accepting quoted values\n const regex = /(\\w+)=(?:\"([^\"]*)\"|'([^']*)')/g\n let match = regex.exec(attrString)\n\n while (match !== null) {\n const [, key, doubleQuoted, singleQuoted] = match\n attributes[key] = doubleQuoted || singleQuoted\n match = regex.exec(attrString)\n }\n\n return attributes\n}\n\n/**\n * Serialize attributes back to shortcode format\n * Always quotes all values with double quotes\n */\nfunction serializeShortcodeAttributes(attrs: Record<string, any>): string {\n return Object.entries(attrs)\n .filter(([, value]) => value !== undefined && value !== null)\n .map(([key, value]) => `${key}=\"${value}\"`)\n .join(' ')\n}\n\n/**\n * Configuration for an allowed attribute in markdown serialization.\n * Can be a simple string (attribute name) or an object with additional options.\n */\nexport type AllowedAttribute =\n | string\n | {\n /** The attribute name */\n name: string\n /**\n * If provided, the attribute will be skipped during serialization when its value\n * equals this default value. This keeps markdown output clean by omitting\n * attributes that have their default values.\n */\n skipIfDefault?: any\n }\n\nexport interface InlineMarkdownSpecOptions {\n /** The Tiptap node name this spec is for */\n nodeName: string\n /** The shortcode name (defaults to nodeName if not provided) */\n name?: string\n /** Function to extract content from the node for serialization */\n getContent?: (node: any) => string\n /** Function to parse attributes from the attribute string */\n parseAttributes?: (attrString: string) => Record<string, any>\n /** Function to serialize attributes to string */\n serializeAttributes?: (attrs: Record<string, any>) => string\n /** Default attributes to apply when parsing */\n defaultAttributes?: Record<string, any>\n /** Whether this is a self-closing shortcode (no content, like [emoji name=party]) */\n selfClosing?: boolean\n /**\n * Allowlist of attributes to include in markdown serialization.\n * If not provided, all attributes are included.\n *\n * Each item can be either:\n * - A string: the attribute name (always included if present)\n * - An object: `{ name: string, skipIfDefault?: any }` for conditional inclusion\n *\n * @example\n * // Simple string attributes (backward compatible)\n * allowedAttributes: ['id', 'label']\n *\n * // Mixed with conditional attributes\n * allowedAttributes: [\n * 'id',\n * 'label',\n * { name: 'mentionSuggestionChar', skipIfDefault: '@' }\n * ]\n */\n allowedAttributes?: AllowedAttribute[]\n}\n\n/**\n * Creates a complete markdown spec for inline nodes using attribute syntax.\n *\n * The generated spec handles:\n * - Parsing shortcode syntax with `[nodeName attributes]content[/nodeName]` format\n * - Self-closing shortcodes like `[emoji name=party_popper]`\n * - Extracting and parsing attributes from the opening tag\n * - Rendering inline elements back to shortcode markdown\n * - Supporting both content-based and self-closing inline elements\n *\n * @param options - Configuration for the inline markdown spec\n * @returns Complete markdown specification object\n *\n * @example\n * ```ts\n * // Self-closing mention: [mention id=\"madonna\" label=\"Madonna\"]\n * const mentionSpec = createInlineMarkdownSpec({\n * nodeName: 'mention',\n * selfClosing: true,\n * defaultAttributes: { type: 'user' },\n * allowedAttributes: ['id', 'label'] // Only these get rendered to markdown\n * })\n *\n * // Self-closing emoji: [emoji name=\"party_popper\"]\n * const emojiSpec = createInlineMarkdownSpec({\n * nodeName: 'emoji',\n * selfClosing: true,\n * allowedAttributes: ['name']\n * })\n *\n * // With content: [highlight color=\"yellow\"]text[/highlight]\n * const highlightSpec = createInlineMarkdownSpec({\n * nodeName: 'highlight',\n * selfClosing: false,\n * allowedAttributes: ['color', 'style']\n * })\n *\n * // Usage in extension:\n * export const Mention = Node.create({\n * name: 'mention', // Must match nodeName\n * // ... other config\n * markdown: mentionSpec\n * })\n * ```\n */\nexport function createInlineMarkdownSpec(options: InlineMarkdownSpecOptions): {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => MarkdownParseResult\n markdownTokenizer: MarkdownTokenizer\n renderMarkdown: (node: JSONContent) => string\n} {\n const {\n nodeName,\n name: shortcodeName,\n getContent,\n parseAttributes = parseShortcodeAttributes,\n serializeAttributes = serializeShortcodeAttributes,\n defaultAttributes = {},\n selfClosing = false,\n allowedAttributes,\n } = options\n\n // Use shortcodeName for markdown syntax, fallback to nodeName\n const shortcode = shortcodeName || nodeName\n\n // Helper function to filter attributes based on allowlist\n const filterAttributes = (attrs: Record<string, any>) => {\n if (!allowedAttributes) {\n return attrs\n }\n\n const filtered: Record<string, any> = {}\n allowedAttributes.forEach(attr => {\n // Handle both string and object formats for backward compatibility\n const attrName = typeof attr === 'string' ? attr : attr.name\n const skipIfDefault = typeof attr === 'string' ? undefined : attr.skipIfDefault\n\n if (attrName in attrs) {\n const value = attrs[attrName]\n\n // Skip if value equals the default (when skipIfDefault is specified)\n if (skipIfDefault !== undefined && value === skipIfDefault) {\n return\n }\n\n filtered[attrName] = value\n }\n })\n return filtered\n }\n\n // Escape special regex characters in shortcode name\n const escapedShortcode = shortcode.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')\n\n return {\n parseMarkdown: (token: MarkdownToken, h: MarkdownParseHelpers) => {\n const attrs = { ...defaultAttributes, ...token.attributes }\n\n if (selfClosing) {\n // Self-closing nodes like mentions are atomic - no content\n return h.createNode(nodeName, attrs)\n }\n\n // Nodes with content\n const content = getContent ? getContent(token) : token.content || ''\n if (content) {\n // For inline content, create text nodes using the proper helper\n return h.createNode(nodeName, attrs, [h.createTextNode(content)])\n }\n return h.createNode(nodeName, attrs, [])\n },\n\n markdownTokenizer: {\n name: nodeName,\n level: 'inline' as const,\n start(src: string) {\n // Create a non-global version for finding the start position\n const startPattern = selfClosing\n ? new RegExp(`\\\\[${escapedShortcode}\\\\s*[^\\\\]]*\\\\]`)\n : new RegExp(`\\\\[${escapedShortcode}\\\\s*[^\\\\]]*\\\\][\\\\s\\\\S]*?\\\\[\\\\/${escapedShortcode}\\\\]`)\n\n const match = src.match(startPattern)\n const index = match?.index\n return index !== undefined ? index : -1\n },\n tokenize(src, _tokens, _lexer) {\n // Use non-global regex to match from the start of the string\n const tokenPattern = selfClosing\n ? new RegExp(`^\\\\[${escapedShortcode}\\\\s*([^\\\\]]*)\\\\]`)\n : new RegExp(`^\\\\[${escapedShortcode}\\\\s*([^\\\\]]*)\\\\]([\\\\s\\\\S]*?)\\\\[\\\\/${escapedShortcode}\\\\]`)\n\n const match = src.match(tokenPattern)\n\n if (!match) {\n return undefined\n }\n\n let content = ''\n let attrString = ''\n\n if (selfClosing) {\n // Self-closing: [shortcode attr=\"value\"]\n const [, attrs] = match\n attrString = attrs\n } else {\n // With content: [shortcode attr=\"value\"]content[/shortcode]\n const [, attrs, contentMatch] = match\n attrString = attrs\n content = contentMatch || ''\n }\n\n // Parse attributes from the attribute string\n const attributes = parseAttributes(attrString.trim())\n\n return {\n type: nodeName,\n raw: match[0],\n content: content.trim(),\n attributes,\n }\n },\n },\n\n renderMarkdown: (node: JSONContent) => {\n let content = ''\n if (getContent) {\n content = getContent(node)\n } else if (node.content && node.content.length > 0) {\n // Extract text from content array for inline nodes\n content = node.content\n .filter((child: any) => child.type === 'text')\n .map((child: any) => child.text)\n .join('')\n }\n\n const filteredAttrs = filterAttributes(node.attrs || {})\n const attrs = serializeAttributes(filteredAttrs)\n const attrString = attrs ? ` ${attrs}` : ''\n\n if (selfClosing) {\n return `[${shortcode}${attrString}]`\n }\n\n return `[${shortcode}${attrString}]${content}[/${shortcode}]`\n },\n }\n}\n","/**\n * @fileoverview Utility for parsing indented markdown blocks with hierarchical nesting.\n *\n * This utility handles the complex logic of parsing markdown blocks that can contain\n * nested content based on indentation levels, maintaining proper hierarchical structure\n * for lists, task lists, and other indented block types.\n */\n\nexport interface ParsedBlock {\n type: string\n raw: string\n mainContent: string\n indentLevel: number\n nestedContent?: string\n nestedTokens?: any[]\n [key: string]: any\n}\n\nexport interface BlockParserConfig {\n /** Regex pattern to match block items */\n itemPattern: RegExp\n /** Function to extract data from regex match */\n extractItemData: (match: RegExpMatchArray) => {\n mainContent: string\n indentLevel: number\n [key: string]: any\n }\n /** Function to create the final token */\n createToken: (data: any, nestedTokens?: any[]) => ParsedBlock\n /** Base indentation to remove from nested content (default: 2 spaces) */\n baseIndentSize?: number\n /**\n * Custom parser for nested content. If provided, this will be called instead\n * of the default lexer.blockTokens() for parsing nested content.\n * This allows recursive parsing of the same block type.\n */\n customNestedParser?: (dedentedContent: string) => any[] | undefined\n}\n\n/**\n * Parses markdown text into hierarchical indented blocks with proper nesting.\n *\n * This utility handles:\n * - Line-by-line parsing with pattern matching\n * - Hierarchical nesting based on indentation levels\n * - Nested content collection and parsing\n * - Empty line handling\n * - Content dedenting for nested blocks\n *\n * The key difference from flat parsing is that this maintains the hierarchical\n * structure where nested items become `nestedTokens` of their parent items,\n * rather than being flattened into a single array.\n *\n * @param src - The markdown source text to parse\n * @param config - Configuration object defining how to parse and create tokens\n * @param lexer - Markdown lexer for parsing nested content\n * @returns Parsed result with hierarchical items, or undefined if no matches\n *\n * @example\n * ```ts\n * const result = parseIndentedBlocks(src, {\n * itemPattern: /^(\\s*)([-+*])\\s+\\[([ xX])\\]\\s+(.*)$/,\n * extractItemData: (match) => ({\n * indentLevel: match[1].length,\n * mainContent: match[4],\n * checked: match[3].toLowerCase() === 'x'\n * }),\n * createToken: (data, nestedTokens) => ({\n * type: 'taskItem',\n * checked: data.checked,\n * text: data.mainContent,\n * nestedTokens\n * })\n * }, lexer)\n * ```\n */\nexport function parseIndentedBlocks(\n src: string,\n config: BlockParserConfig,\n lexer: {\n inlineTokens: (src: string) => any[]\n blockTokens: (src: string) => any[]\n },\n):\n | {\n items: ParsedBlock[]\n raw: string\n }\n | undefined {\n const lines = src.split('\\n')\n const items: ParsedBlock[] = []\n let totalRaw = ''\n let i = 0\n const baseIndentSize = config.baseIndentSize || 2\n\n while (i < lines.length) {\n const currentLine = lines[i]\n const itemMatch = currentLine.match(config.itemPattern)\n\n if (!itemMatch) {\n // Not a matching item - stop if we have items, otherwise this isn't our block type\n if (items.length > 0) {\n break\n } else if (currentLine.trim() === '') {\n i += 1\n totalRaw = `${totalRaw}${currentLine}\\n`\n continue\n } else {\n return undefined\n }\n }\n\n const itemData = config.extractItemData(itemMatch)\n const { indentLevel, mainContent } = itemData\n totalRaw = `${totalRaw}${currentLine}\\n`\n\n // Collect content for this item (including nested items)\n const itemContent = [mainContent] // Start with the main text\n i += 1\n\n // Look ahead for nested content (indented more than current item)\n while (i < lines.length) {\n const nextLine = lines[i]\n\n if (nextLine.trim() === '') {\n // Empty line - might be end of nested content\n const nextNonEmptyIndex = lines.slice(i + 1).findIndex(l => l.trim() !== '')\n if (nextNonEmptyIndex === -1) {\n // No more content\n break\n }\n\n const nextNonEmpty = lines[i + 1 + nextNonEmptyIndex]\n const nextIndent = nextNonEmpty.match(/^(\\s*)/)?.[1]?.length || 0\n\n if (nextIndent > indentLevel) {\n // Nested content continues after empty line\n itemContent.push(nextLine)\n totalRaw = `${totalRaw}${nextLine}\\n`\n i += 1\n continue\n } else {\n // End of nested content\n break\n }\n }\n\n const nextIndent = nextLine.match(/^(\\s*)/)?.[1]?.length || 0\n\n if (nextIndent > indentLevel) {\n // This is nested content for the current item\n itemContent.push(nextLine)\n totalRaw = `${totalRaw}${nextLine}\\n`\n i += 1\n } else {\n // Same or less indentation - this belongs to parent level\n break\n }\n }\n\n // Parse nested content if present\n let nestedTokens: any[] | undefined\n const nestedContent = itemContent.slice(1)\n\n if (nestedContent.length > 0) {\n // Remove the base indentation from nested content\n const dedentedNested = nestedContent\n .map(nestedLine => nestedLine.slice(indentLevel + baseIndentSize)) // Remove base indent + 2 spaces\n .join('\\n')\n\n if (dedentedNested.trim()) {\n // Use custom nested parser if provided, otherwise fall back to default\n if (config.customNestedParser) {\n nestedTokens = config.customNestedParser(dedentedNested)\n } else {\n nestedTokens = lexer.blockTokens(dedentedNested)\n }\n }\n }\n\n // Create the token using the provided factory function\n const token = config.createToken(itemData, nestedTokens)\n items.push(token)\n }\n\n if (items.length === 0) {\n return undefined\n }\n\n return {\n items,\n raw: totalRaw,\n }\n}\n","import type { JSONContent } from '@tiptap/core'\n\n/**\n * @fileoverview Utility functions for rendering nested content in markdown.\n *\n * This module provides reusable utilities for extensions that need to render\n * content with a prefix on the main line and properly indented nested content.\n */\n\n/**\n * Utility function for rendering content with a main line prefix and nested indented content.\n *\n * This function handles the common pattern of rendering content with:\n * 1. A main line with a prefix (like \"- \" for lists, \"> \" for blockquotes, etc.)\n * 2. Nested content that gets indented properly\n *\n * @param node - The ProseMirror node representing the content\n * @param h - The markdown renderer helper\n * @param prefixOrGenerator - Either a string prefix or a function that generates the prefix from context\n * @param ctx - Optional context object (used when prefixOrGenerator is a function)\n * @returns The rendered markdown string\n *\n * @example\n * ```ts\n * // For a bullet list item with static prefix\n * return renderNestedMarkdownContent(node, h, '- ')\n *\n * // For a task item with static prefix\n * const prefix = `- [${node.attrs?.checked ? 'x' : ' '}] `\n * return renderNestedMarkdownContent(node, h, prefix)\n *\n * // For a blockquote with static prefix\n * return renderNestedMarkdownContent(node, h, '> ')\n *\n * // For content with dynamic prefix based on context\n * return renderNestedMarkdownContent(node, h, ctx => {\n * if (ctx.parentType === 'orderedList') {\n * return `${ctx.index + 1}. `\n * }\n * return '- '\n * }, ctx)\n *\n * // Custom extension example\n * const CustomContainer = Node.create({\n * name: 'customContainer',\n * // ... other config\n * markdown: {\n * render: (node, h) => {\n * const type = node.attrs?.type || 'info'\n * return renderNestedMarkdownContent(node, h, `[${type}] `)\n * }\n * }\n * })\n * ```\n */\nexport function renderNestedMarkdownContent(\n node: JSONContent,\n h: {\n renderChildren: (nodes: JSONContent[]) => string\n indent: (text: string) => string\n },\n prefixOrGenerator: string | ((ctx: any) => string),\n ctx?: any,\n): string {\n if (!node || !Array.isArray(node.content)) {\n return ''\n }\n\n // Determine the prefix based on the input\n const prefix = typeof prefixOrGenerator === 'function' ? prefixOrGenerator(ctx) : prefixOrGenerator\n\n const [content, ...children] = node.content\n\n // Render the main content (typically a paragraph)\n const mainContent = h.renderChildren([content])\n const output = [`${prefix}${mainContent}`]\n\n // Handle nested children with proper indentation\n if (children && children.length > 0) {\n children.forEach(child => {\n const childContent = h.renderChildren([child])\n if (childContent) {\n // Split the child content by lines and indent each line\n const indentedChild = childContent\n .split('\\n')\n .map(line => (line ? h.indent(line) : ''))\n .join('\\n')\n output.push(indentedChild)\n }\n })\n }\n\n return output.join('\\n')\n}\n","import type { Mark } from '@tiptap/pm/model'\nimport type { ViewMutationRecord } from '@tiptap/pm/view'\n\nimport type { Editor } from './Editor.js'\nimport type { MarkViewProps, MarkViewRendererOptions } from './types.js'\nimport { isAndroid, isiOS } from './utilities/index.js'\n\nexport function updateMarkViewAttributes(checkMark: Mark, editor: Editor, attrs: Record<string, any> = {}): void {\n const { state } = editor\n const { doc, tr } = state\n const thisMark = checkMark\n\n doc.descendants((node, pos) => {\n const from = tr.mapping.map(pos)\n const to = tr.mapping.map(pos) + node.nodeSize\n let foundMark: Mark | null = null\n\n // find the mark on the current node\n node.marks.forEach(mark => {\n if (mark !== thisMark) {\n return false\n }\n\n foundMark = mark\n })\n\n if (!foundMark) {\n return\n }\n\n // check if we need to update given the attributes\n let needsUpdate = false\n Object.keys(attrs).forEach(k => {\n if (attrs[k] !== foundMark!.attrs[k]) {\n needsUpdate = true\n }\n })\n\n if (needsUpdate) {\n const updatedMark = checkMark.type.create({\n ...checkMark.attrs,\n ...attrs,\n })\n\n tr.removeMark(from, to, checkMark.type)\n tr.addMark(from, to, updatedMark)\n }\n })\n\n if (tr.docChanged) {\n editor.view.dispatch(tr)\n }\n}\n\nexport class MarkView<Component, Options extends MarkViewRendererOptions = MarkViewRendererOptions> {\n component: Component\n editor: Editor\n options: Options\n mark: MarkViewProps['mark']\n HTMLAttributes: MarkViewProps['HTMLAttributes']\n\n constructor(component: Component, props: MarkViewProps, options?: Partial<Options>) {\n this.component = component\n this.editor = props.editor\n this.options = { ...options } as Options\n this.mark = props.mark\n this.HTMLAttributes = props.HTMLAttributes\n }\n\n get dom(): HTMLElement {\n return this.editor.view.dom\n }\n\n get contentDOM(): HTMLElement | null {\n return null\n }\n\n /**\n * Update the attributes of the mark in the document.\n * @param attrs The attributes to update.\n */\n updateAttributes(attrs: Record<string, any>, checkMark?: Mark): void {\n updateMarkViewAttributes(checkMark || this.mark, this.editor, attrs)\n }\n\n ignoreMutation(mutation: ViewMutationRecord): boolean {\n if (!this.dom || !this.contentDOM) {\n return true\n }\n\n if (typeof this.options.ignoreMutation === 'function') {\n return this.options.ignoreMutation({ mutation })\n }\n\n if (mutation.type === 'selection') {\n return false\n }\n\n if (\n this.dom.contains(mutation.target) &&\n mutation.type === 'childList' &&\n (isiOS() || isAndroid()) &&\n this.editor.isFocused\n ) {\n const changedNodes = [...Array.from(mutation.addedNodes), ...Array.from(mutation.removedNodes)] as HTMLElement[]\n\n if (changedNodes.every(node => node.isContentEditable)) {\n return false\n }\n }\n\n if (this.contentDOM === mutation.target && mutation.type === 'attributes') {\n return true\n }\n\n if (this.contentDOM.contains(mutation.target)) {\n return false\n }\n\n return true\n }\n}\n","import type { DOMOutputSpec, Node as ProseMirrorNode, NodeSpec, NodeType } from '@tiptap/pm/model'\n\nimport type { Editor } from './Editor.js'\nimport type { ExtendableConfig } from './Extendable.js'\nimport { Extendable } from './Extendable.js'\nimport type { Attributes, NodeViewRenderer, ParentConfig } from './types.js'\n\nexport interface NodeConfig<Options = any, Storage = any>\n extends ExtendableConfig<Options, Storage, NodeConfig<Options, Storage>, NodeType> {\n /**\n * Node View\n */\n addNodeView?:\n | ((this: {\n name: string\n options: Options\n storage: Storage\n editor: Editor\n type: NodeType\n parent: ParentConfig<NodeConfig<Options, Storage>>['addNodeView']\n }) => NodeViewRenderer | null)\n | null\n\n /**\n * Defines if this node should be a top level node (doc)\n * @default false\n * @example true\n */\n topNode?: boolean\n\n /**\n * The content expression for this node, as described in the [schema\n * guide](/docs/guide/#schema.content_expressions). When not given,\n * the node does not allow any content.\n *\n * You can read more about it on the Prosemirror documentation here\n * @see https://prosemirror.net/docs/guide/#schema.content_expressions\n * @default undefined\n * @example content: 'block+'\n * @example content: 'headline paragraph block*'\n */\n content?:\n | NodeSpec['content']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['content']\n editor?: Editor\n }) => NodeSpec['content'])\n\n /**\n * The marks that are allowed inside of this node. May be a\n * space-separated string referring to mark names or groups, `\"_\"`\n * to explicitly allow all marks, or `\"\"` to disallow marks. When\n * not given, nodes with inline content default to allowing all\n * marks, other nodes default to not allowing marks.\n *\n * @example marks: 'strong em'\n */\n marks?:\n | NodeSpec['marks']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['marks']\n editor?: Editor\n }) => NodeSpec['marks'])\n\n /**\n * The group or space-separated groups to which this node belongs,\n * which can be referred to in the content expressions for the\n * schema.\n *\n * By default Tiptap uses the groups 'block' and 'inline' for nodes. You\n * can also use custom groups if you want to group specific nodes together\n * and handle them in your schema.\n * @example group: 'block'\n * @example group: 'inline'\n * @example group: 'customBlock' // this uses a custom group\n */\n group?:\n | NodeSpec['group']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['group']\n editor?: Editor\n }) => NodeSpec['group'])\n\n /**\n * Should be set to true for inline nodes. (Implied for text nodes.)\n */\n inline?:\n | NodeSpec['inline']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['inline']\n editor?: Editor\n }) => NodeSpec['inline'])\n\n /**\n * Can be set to true to indicate that, though this isn't a [leaf\n * node](https://prosemirror.net/docs/ref/#model.NodeType.isLeaf), it doesn't have directly editable\n * content and should be treated as a single unit in the view.\n *\n * @example atom: true\n */\n atom?:\n | NodeSpec['atom']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['atom']\n editor?: Editor\n }) => NodeSpec['atom'])\n\n /**\n * Controls whether nodes of this type can be selected as a [node\n * selection](https://prosemirror.net/docs/ref/#state.NodeSelection). Defaults to true for non-text\n * nodes.\n *\n * @default true\n * @example selectable: false\n */\n selectable?:\n | NodeSpec['selectable']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['selectable']\n editor?: Editor\n }) => NodeSpec['selectable'])\n\n /**\n * Determines whether nodes of this type can be dragged without\n * being selected. Defaults to false.\n *\n * @default: false\n * @example: draggable: true\n */\n draggable?:\n | NodeSpec['draggable']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['draggable']\n editor?: Editor\n }) => NodeSpec['draggable'])\n\n /**\n * Can be used to indicate that this node contains code, which\n * causes some commands to behave differently.\n */\n code?:\n | NodeSpec['code']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['code']\n editor?: Editor\n }) => NodeSpec['code'])\n\n /**\n * Controls way whitespace in this a node is parsed. The default is\n * `\"normal\"`, which causes the [DOM parser](https://prosemirror.net/docs/ref/#model.DOMParser) to\n * collapse whitespace in normal mode, and normalize it (replacing\n * newlines and such with spaces) otherwise. `\"pre\"` causes the\n * parser to preserve spaces inside the node. When this option isn't\n * given, but [`code`](https://prosemirror.net/docs/ref/#model.NodeSpec.code) is true, `whitespace`\n * will default to `\"pre\"`. Note that this option doesn't influence\n * the way the node is rendered—that should be handled by `toDOM`\n * and/or styling.\n */\n whitespace?:\n | NodeSpec['whitespace']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['whitespace']\n editor?: Editor\n }) => NodeSpec['whitespace'])\n\n /**\n * Allows a **single** node to be set as linebreak equivalent (e.g. hardBreak).\n * When converting between block types that have whitespace set to \"pre\"\n * and don't support the linebreak node (e.g. codeBlock) and other block types\n * that do support the linebreak node (e.g. paragraphs) - this node will be used\n * as the linebreak instead of stripping the newline.\n *\n * See [linebreakReplacement](https://prosemirror.net/docs/ref/#model.NodeSpec.linebreakReplacement).\n */\n linebreakReplacement?:\n | NodeSpec['linebreakReplacement']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['linebreakReplacement']\n editor?: Editor\n }) => NodeSpec['linebreakReplacement'])\n\n /**\n * When enabled, enables both\n * [`definingAsContext`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingAsContext) and\n * [`definingForContent`](https://prosemirror.net/docs/ref/#model.NodeSpec.definingForContent).\n *\n * @default false\n * @example isolating: true\n */\n defining?:\n | NodeSpec['defining']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['defining']\n editor?: Editor\n }) => NodeSpec['defining'])\n\n /**\n * When enabled (default is false), the sides of nodes of this type\n * count as boundaries that regular editing operations, like\n * backspacing or lifting, won't cross. An example of a node that\n * should probably have this enabled is a table cell.\n */\n isolating?:\n | NodeSpec['isolating']\n | ((this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['isolating']\n editor?: Editor\n }) => NodeSpec['isolating'])\n\n /**\n * Associates DOM parser information with this node, which can be\n * used by [`DOMParser.fromSchema`](https://prosemirror.net/docs/ref/#model.DOMParser^fromSchema) to\n * automatically derive a parser. The `node` field in the rules is\n * implied (the name of this node will be filled in automatically).\n * If you supply your own parser, you do not need to also specify\n * parsing rules in your schema.\n *\n * @example parseHTML: [{ tag: 'div', attrs: { 'data-id': 'my-block' } }]\n */\n parseHTML?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['parseHTML']\n editor?: Editor\n }) => NodeSpec['parseDOM']\n\n /**\n * A description of a DOM structure. Can be either a string, which is\n * interpreted as a text node, a DOM node, which is interpreted as\n * itself, a `{dom, contentDOM}` object, or an array.\n *\n * An array describes a DOM element. The first value in the array\n * should be a string—the name of the DOM element, optionally prefixed\n * by a namespace URL and a space. If the second element is plain\n * object, it is interpreted as a set of attributes for the element.\n * Any elements after that (including the 2nd if it's not an attribute\n * object) are interpreted as children of the DOM elements, and must\n * either be valid `DOMOutputSpec` values, or the number zero.\n *\n * The number zero (pronounced “hole”) is used to indicate the place\n * where a node's child nodes should be inserted. If it occurs in an\n * output spec, it should be the only child element in its parent\n * node.\n *\n * @example toDOM: ['div[data-id=\"my-block\"]', { class: 'my-block' }, 0]\n */\n renderHTML?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['renderHTML']\n editor?: Editor\n },\n props: {\n node: ProseMirrorNode\n HTMLAttributes: Record<string, any>\n },\n ) => DOMOutputSpec)\n | null\n\n /**\n * renders the node as text\n * @example renderText: () => 'foo\n */\n renderText?:\n | ((\n this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['renderText']\n editor?: Editor\n },\n props: {\n node: ProseMirrorNode\n pos: number\n parent: ProseMirrorNode\n index: number\n },\n ) => string)\n | null\n\n /**\n * Add attributes to the node\n * @example addAttributes: () => ({ class: 'foo' })\n */\n addAttributes?: (this: {\n name: string\n options: Options\n storage: Storage\n parent: ParentConfig<NodeConfig<Options, Storage>>['addAttributes']\n editor?: Editor\n // eslint-disable-next-line @typescript-eslint/no-empty-object-type\n }) => Attributes | {}\n}\n\n/**\n * The Node class is used to create custom node extensions.\n * @see https://tiptap.dev/api/extensions#create-a-new-extension\n */\nexport class Node<Options = any, Storage = any> extends Extendable<Options, Storage, NodeConfig<Options, Storage>> {\n type = 'node'\n\n /**\n * Create a new Node instance\n * @param config - Node configuration object or a function that returns a configuration object\n */\n static create<O = any, S = any>(config: Partial<NodeConfig<O, S>> | (() => Partial<NodeConfig<O, S>>) = {}) {\n // If the config is a function, execute it to get the configuration object\n const resolvedConfig = typeof config === 'function' ? config() : config\n return new Node<O, S>(resolvedConfig)\n }\n\n configure(options?: Partial<Options>) {\n return super.configure(options) as Node<Options, Storage>\n }\n\n extend<\n ExtendedOptions = Options,\n ExtendedStorage = Storage,\n ExtendedConfig extends NodeConfig<ExtendedOptions, ExtendedStorage> = NodeConfig<ExtendedOptions, ExtendedStorage>,\n >(\n extendedConfig?:\n | (() => Partial<ExtendedConfig>)\n | (Partial<ExtendedConfig> &\n ThisType<{\n name: string\n options: ExtendedOptions\n storage: ExtendedStorage\n editor: Editor\n type: NodeType\n }>),\n ): Node<ExtendedOptions, ExtendedStorage> {\n // If the extended config is a function, execute it to get the configuration object\n const resolvedConfig = typeof extendedConfig === 'function' ? extendedConfig() : extendedConfig\n return super.extend(resolvedConfig) as Node<ExtendedOptions, ExtendedStorage>\n }\n}\n","import { NodeSelection } from '@tiptap/pm/state'\nimport type { NodeView as ProseMirrorNodeView, ViewMutationRecord } from '@tiptap/pm/view'\n\nimport type { Editor as CoreEditor } from './Editor.js'\nimport type { DecorationWithType, NodeViewRendererOptions, NodeViewRendererProps } from './types.js'\nimport { isAndroid } from './utilities/isAndroid.js'\nimport { isiOS } from './utilities/isiOS.js'\n\n/**\n * Node views are used to customize the rendered DOM structure of a node.\n * @see https://tiptap.dev/guide/node-views\n */\nexport class NodeView<\n Component,\n NodeEditor extends CoreEditor = CoreEditor,\n Options extends NodeViewRendererOptions = NodeViewRendererOptions,\n> implements ProseMirrorNodeView\n{\n component: Component\n\n editor: NodeEditor\n\n options: Options\n\n extension: NodeViewRendererProps['extension']\n\n node: NodeViewRendererProps['node']\n\n decorations: NodeViewRendererProps['decorations']\n\n innerDecorations: NodeViewRendererProps['innerDecorations']\n\n view: NodeViewRendererProps['view']\n\n getPos: NodeViewRendererProps['getPos']\n\n HTMLAttributes: NodeViewRendererProps['HTMLAttributes']\n\n isDragging = false\n\n constructor(component: Component, props: NodeViewRendererProps, options?: Partial<Options>) {\n this.component = component\n this.editor = props.editor as NodeEditor\n this.options = {\n stopEvent: null,\n ignoreMutation: null,\n ...options,\n } as Options\n this.extension = props.extension\n this.node = props.node\n this.decorations = props.decorations as DecorationWithType[]\n this.innerDecorations = props.innerDecorations\n this.view = props.view\n this.HTMLAttributes = props.HTMLAttributes\n this.getPos = props.getPos\n this.mount()\n }\n\n mount() {\n // eslint-disable-next-line\n return\n }\n\n get dom(): HTMLElement {\n return this.editor.view.dom as HTMLElement\n }\n\n get contentDOM(): HTMLElement | null {\n return null\n }\n\n onDragStart(event: DragEvent) {\n const { view } = this.editor\n const target = event.target as HTMLElement\n\n // get the drag handle element\n // `closest` is not available for text nodes so we may have to use its parent\n const dragHandle =\n target.nodeType === 3 ? target.parentElement?.closest('[data-drag-handle]') : target.closest('[data-drag-handle]')\n\n if (!this.dom || this.contentDOM?.contains(target) || !dragHandle) {\n return\n }\n\n let x = 0\n let y = 0\n\n // calculate offset for drag element if we use a different drag handle element\n if (this.dom !== dragHandle) {\n const domBox = this.dom.getBoundingClientRect()\n const handleBox = dragHandle.getBoundingClientRect()\n\n // In React, we have to go through nativeEvent to reach offsetX/offsetY.\n const offsetX = event.offsetX ?? (event as any).nativeEvent?.offsetX\n const offsetY = event.offsetY ?? (event as any).nativeEvent?.offsetY\n\n x = handleBox.x - domBox.x + offsetX\n y = handleBox.y - domBox.y + offsetY\n }\n\n const clonedNode = this.dom.cloneNode(true) as HTMLElement\n\n // Preserve the visual size of the original when using the clone as\n // the drag image.\n try {\n const domBox = this.dom.getBoundingClientRect()\n clonedNode.style.width = `${Math.round(domBox.width)}px`\n clonedNode.style.height = `${Math.round(domBox.height)}px`\n clonedNode.style.boxSizing = 'border-box'\n // Ensure the clone doesn't capture pointer events while offscreen\n clonedNode.style.pointerEvents = 'none'\n } catch {\n // ignore measurement errors (e.g. if element not in DOM)\n }\n\n // Some browsers (notably Safari) require the element passed to\n // setDragImage to be present in the DOM. Using a detached node can\n // cause the drag to immediately end.\n let dragImageWrapper: HTMLElement | null = null\n\n try {\n dragImageWrapper = document.createElement('div')\n dragImageWrapper.style.position = 'absolute'\n dragImageWrapper.style.top = '-9999px'\n dragImageWrapper.style.left = '-9999px'\n dragImageWrapper.style.pointerEvents = 'none'\n dragImageWrapper.appendChild(clonedNode)\n document.body.appendChild(dragImageWrapper)\n\n event.dataTransfer?.setDragImage(clonedNode, x, y)\n } finally {\n // Remove the wrapper on the next tick so the browser can use the\n // element as the drag image. A 0ms timeout is enough in practice.\n if (dragImageWrapper) {\n setTimeout(() => {\n try {\n dragImageWrapper?.remove()\n } catch {\n // ignore removal errors\n }\n }, 0)\n }\n }\n\n const pos = this.getPos()\n\n if (typeof pos !== 'number') {\n return\n }\n // we need to tell ProseMirror that we want to move the whole node\n // so we create a NodeSelection\n const selection = NodeSelection.create(view.state.doc, pos)\n const transaction = view.state.tr.setSelection(selection)\n\n view.dispatch(transaction)\n }\n\n stopEvent(event: Event) {\n if (!this.dom) {\n return false\n }\n\n if (typeof this.options.stopEvent === 'function') {\n return this.options.stopEvent({ event })\n }\n\n const target = event.target as HTMLElement\n const isInElement = this.dom.contains(target) && !this.contentDOM?.contains(target)\n\n // any event from child nodes should be handled by ProseMirror\n if (!isInElement) {\n return false\n }\n\n const isDragEvent = event.type.startsWith('drag')\n const isDropEvent = event.type === 'drop'\n const isInput = ['INPUT', 'BUTTON', 'SELECT', 'TEXTAREA'].includes(target.tagName) || target.isContentEditable\n\n // any input event within node views should be ignored by ProseMirror\n if (isInput && !isDropEvent && !isDragEvent) {\n return true\n }\n\n const { isEditable } = this.editor\n const { isDragging } = this\n const isDraggable = !!this.node.type.spec.draggable\n const isSelectable = NodeSelection.isSelectable(this.node)\n const isCopyEvent = event.type === 'copy'\n const isPasteEvent = event.type === 'paste'\n const isCutEvent = event.type === 'cut'\n const isClickEvent = event.type === 'mousedown'\n\n // ProseMirror tries to drag selectable nodes\n // even if `draggable` is set to `false`\n // this fix prevents that\n if (!isDraggable && isSelectable && isDragEvent && event.target === this.dom) {\n event.preventDefault()\n }\n\n if (isDraggable && isDragEvent && !isDragging && event.target === this.dom) {\n event.preventDefault()\n return false\n }\n\n // we have to store that dragging started\n if (isDraggable && isEditable && !isDragging && isClickEvent) {\n const dragHandle = target.closest('[data-drag-handle]')\n const isValidDragHandle = dragHandle && (this.dom === dragHandle || this.dom.contains(dragHandle))\n\n if (isValidDragHandle) {\n this.isDragging = true\n\n document.addEventListener(\n 'dragend',\n () => {\n this.isDragging = false\n },\n { once: true },\n )\n\n document.addEventListener(\n 'drop',\n () => {\n this.isDragging = false\n },\n { once: true },\n )\n\n document.addEventListener(\n 'mouseup',\n () => {\n this.isDragging = false\n },\n { once: true },\n )\n }\n }\n\n // these events are handled by prosemirror\n if (isDragging || isDropEvent || isCopyEvent || isPasteEvent || isCutEvent || (isClickEvent && isSelectable)) {\n return false\n }\n\n return true\n }\n\n /**\n * Called when a DOM [mutation](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) or a selection change happens within the view.\n * @return `false` if the editor should re-read the selection or re-parse the range around the mutation\n * @return `true` if it can safely be ignored.\n */\n ignoreMutation(mutation: ViewMutationRecord) {\n if (!this.dom || !this.contentDOM) {\n return true\n }\n\n if (typeof this.options.ignoreMutation === 'function') {\n return this.options.ignoreMutation({ mutation })\n }\n\n // a leaf/atom node is like a black box for ProseMirror\n // and should be fully handled by the node view\n if (this.node.isLeaf || this.node.isAtom) {\n return true\n }\n\n // ProseMirror should handle any selections\n if (mutation.type === 'selection') {\n return false\n }\n\n // try to prevent a bug on iOS and Android that will break node views on enter\n // this is because ProseMirror can’t preventDispatch on enter\n // this will lead to a re-render of the node view on enter\n // see: https://github.com/ueberdosis/tiptap/issues/1214\n // see: https://github.com/ueberdosis/tiptap/issues/2534\n if (\n this.dom.contains(mutation.target) &&\n mutation.type === 'childList' &&\n (isiOS() || isAndroid()) &&\n this.editor.isFocused\n ) {\n const changedNodes = [...Array.from(mutation.addedNodes), ...Array.from(mutation.removedNodes)] as HTMLElement[]\n\n // we’ll check if every changed node is contentEditable\n // to make sure it’s probably mutated by ProseMirror\n if (changedNodes.every(node => node.isContentEditable)) {\n return false\n }\n }\n\n // we will allow mutation contentDOM with attributes\n // so we can for example adding classes within our node view\n if (this.contentDOM === mutation.target && mutation.type === 'attributes') {\n return true\n }\n\n // ProseMirror should handle any changes within contentDOM\n if (this.contentDOM.contains(mutation.target)) {\n return false\n }\n\n return true\n }\n\n /**\n * Update the attributes of the prosemirror node.\n */\n updateAttributes(attributes: Record<string, any>): void {\n this.editor.commands.command(({ tr }) => {\n const pos = this.getPos()\n\n if (typeof pos !== 'number') {\n return false\n }\n\n tr.setNodeMarkup(pos, undefined, {\n ...this.node.attrs,\n ...attributes,\n })\n\n return true\n })\n }\n\n /**\n * Delete the node.\n */\n deleteNode(): void {\n const from = this.getPos()\n\n if (typeof from !== 'number') {\n return\n }\n const to = from + this.node.nodeSize\n\n this.editor.commands.deleteRange({ from, to })\n }\n}\n","import type { MarkType } from '@tiptap/pm/model'\n\nimport { getMarksBetween } from '../helpers/getMarksBetween.js'\nimport type { PasteRuleFinder } from '../PasteRule.js'\nimport { PasteRule } from '../PasteRule.js'\nimport type { ExtendedRegExpMatchArray } from '../types.js'\nimport { callOrReturn } from '../utilities/callOrReturn.js'\n\n/**\n * Build an paste rule that adds a mark when the\n * matched text is pasted into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules\n */\nexport function markPasteRule(config: {\n find: PasteRuleFinder\n type: MarkType\n getAttributes?:\n | Record<string, any>\n | ((match: ExtendedRegExpMatchArray, event: ClipboardEvent) => Record<string, any>)\n | false\n | null\n}) {\n return new PasteRule({\n find: config.find,\n handler: ({ state, range, match, pasteEvent }) => {\n const attributes = callOrReturn(config.getAttributes, undefined, match, pasteEvent)\n\n if (attributes === false || attributes === null) {\n return null\n }\n\n const { tr } = state\n const captureGroup = match[match.length - 1]\n const fullMatch = match[0]\n let markEnd = range.to\n\n if (captureGroup) {\n const startSpaces = fullMatch.search(/\\S/)\n const textStart = range.from + fullMatch.indexOf(captureGroup)\n const textEnd = textStart + captureGroup.length\n\n const excludedMarks = getMarksBetween(range.from, range.to, state.doc)\n .filter(item => {\n // @ts-ignore\n const excluded = item.mark.type.excluded as MarkType[]\n\n return excluded.find(type => type === config.type && type !== item.mark.type)\n })\n .filter(item => item.to > textStart)\n\n if (excludedMarks.length) {\n return null\n }\n\n if (textEnd < range.to) {\n tr.delete(textEnd, range.to)\n }\n\n if (textStart > range.from) {\n tr.delete(range.from + startSpaces, textStart)\n }\n\n markEnd = range.from + startSpaces + captureGroup.length\n\n tr.addMark(range.from + startSpaces, markEnd, config.type.create(attributes || {}))\n\n tr.removeStoredMark(config.type)\n }\n },\n })\n}\n","import type { NodeType } from '@tiptap/pm/model'\n\nimport type { PasteRuleFinder } from '../PasteRule.js'\nimport { PasteRule } from '../PasteRule.js'\nimport type { ExtendedRegExpMatchArray, JSONContent } from '../types.js'\nimport { callOrReturn } from '../utilities/index.js'\n\n/**\n * Build an paste rule that adds a node when the\n * matched text is pasted into it.\n * @see https://tiptap.dev/docs/editor/api/paste-rules\n */\nexport function nodePasteRule(config: {\n find: PasteRuleFinder\n type: NodeType\n getAttributes?:\n | Record<string, any>\n | ((match: ExtendedRegExpMatchArray, event: ClipboardEvent) => Record<string, any>)\n | false\n | null\n getContent?: JSONContent[] | ((attrs: Record<string, any>) => JSONContent[]) | false | null\n}) {\n return new PasteRule({\n find: config.find,\n handler({ match, chain, range, pasteEvent }) {\n const attributes = callOrReturn(config.getAttributes, undefined, match, pasteEvent)\n const content = callOrReturn(config.getContent, undefined, attributes)\n\n if (attributes === false || attributes === null) {\n return null\n }\n\n const node = { type: config.type.name, attrs: attributes } as JSONContent\n\n if (content) {\n node.content = content\n }\n\n if (match.input) {\n chain().deleteRange(range).insertContentAt(range.from, node)\n }\n },\n })\n}\n","import type { PasteRuleFinder } from '../PasteRule.js'\nimport { PasteRule } from '../PasteRule.js'\n\n/**\n * Build an paste rule that replaces text when the\n * matched text is pasted into it.\n * @see https://tiptap.dev/docs/editor/extensions/custom-extensions/extend-existing#paste-rules\n */\nexport function textPasteRule(config: { find: PasteRuleFinder; replace: string }) {\n return new PasteRule({\n find: config.find,\n handler: ({ state, range, match }) => {\n let insert = config.replace\n let start = range.from\n const end = range.to\n\n if (match[1]) {\n const offset = match[0].lastIndexOf(match[1])\n\n insert += match[0].slice(offset + match[1].length)\n start += offset\n\n const cutOff = start - end\n\n if (cutOff > 0) {\n insert = match[0].slice(offset - cutOff, offset) + insert\n start = end\n }\n }\n\n state.tr.insertText(insert, start, end)\n },\n })\n}\n","import type { Transaction } from '@tiptap/pm/state'\n\nexport interface TrackerResult {\n position: number\n deleted: boolean\n}\n\nexport class Tracker {\n transaction: Transaction\n\n currentStep: number\n\n constructor(transaction: Transaction) {\n this.transaction = transaction\n this.currentStep = this.transaction.steps.length\n }\n\n map(position: number): TrackerResult {\n let deleted = false\n\n const mappedPosition = this.transaction.steps.slice(this.currentStep).reduce((newPosition, step) => {\n const mapResult = step.getMap().mapResult(newPosition)\n\n if (mapResult.deleted) {\n deleted = true\n }\n\n return mapResult.pos\n }, position)\n\n return {\n position: mappedPosition,\n deleted,\n }\n }\n}\n","import { Mark, mergeAttributes } from '@tiptap/core'\nimport { Plugin, PluginKey } from '@tiptap/pm/state'\nimport type { Editor } from '@tiptap/core'\nimport type { CommentWithUser } from './comments'\n\nexport interface CommentMarkOptions {\n onCommentClick?: (commentId: string) => void\n}\n\ndeclare module '@tiptap/core' {\n interface Commands<ReturnType> {\n comment: {\n setComment: (commentId: string) => ReturnType\n unsetComment: (commentId: string) => ReturnType\n }\n }\n}\n\nexport const CommentMark = Mark.create<CommentMarkOptions>({\n name: 'comment',\n\n addOptions() {\n return {\n onCommentClick: undefined,\n }\n },\n\n addAttributes() {\n return {\n commentId: {\n default: null,\n parseHTML: (element) => element.getAttribute('data-comment-id'),\n renderHTML: (attributes) => ({\n 'data-comment-id': attributes.commentId,\n }),\n },\n }\n },\n\n parseHTML() {\n return [{ tag: 'mark[data-comment-id]' }]\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n 'mark',\n mergeAttributes(HTMLAttributes, {\n class:\n 'bg-yellow-200/50 dark:bg-yellow-500/30 dark:text-foreground cursor-pointer hover:bg-yellow-300/60 dark:hover:bg-yellow-500/40 transition-colors rounded-sm',\n }),\n 0,\n ]\n },\n\n addCommands() {\n return {\n setComment:\n (commentId: string) =>\n ({ commands }) => {\n return commands.setMark(this.name, { commentId })\n },\n unsetComment:\n (commentId: string) =>\n ({ tr, state }) => {\n const { doc } = state\n let found = false\n\n doc.descendants((node, pos) => {\n node.marks.forEach((mark) => {\n if (mark.type.name === this.name && mark.attrs.commentId === commentId) {\n tr.removeMark(pos, pos + node.nodeSize, mark.type)\n found = true\n }\n })\n })\n\n return found\n },\n }\n },\n\n addProseMirrorPlugins() {\n const { onCommentClick } = this.options\n\n return [\n new Plugin({\n key: new PluginKey('commentClick'),\n props: {\n handleClick(view, pos) {\n if (!onCommentClick) return false\n\n const { state } = view\n const $pos = state.doc.resolve(pos)\n const marks = $pos.marks()\n\n const commentMark = marks.find((mark) => mark.type.name === 'comment')\n if (commentMark && commentMark.attrs.commentId) {\n // Blur the editor to prevent keyboard popup on mobile\n ;(view.dom as HTMLElement).blur()\n onCommentClick(commentMark.attrs.commentId)\n return true\n }\n\n return false\n },\n },\n }),\n ]\n },\n})\n\n/**\n * Apply comment mark to the current selection\n */\nexport function addCommentMark(\n editor: Editor,\n commentId: string,\n from: number,\n to: number\n): void {\n // Guard against editor not being ready\n if (!editor.view || editor.isDestroyed) {\n console.warn('Cannot add comment mark: editor not ready')\n return\n }\n \n editor\n .chain()\n .setTextSelection({ from, to })\n .setComment(commentId)\n .run()\n}\n\n/**\n * Remove comment mark from the document\n */\nexport function removeCommentMark(editor: Editor, commentId: string): void {\n if (!editor.view || editor.isDestroyed) return\n editor.chain().unsetComment(commentId).run()\n}\n\n/**\n * Re-apply comment marks based on quoted text matching.\n * Called when loading a post with existing comments.\n */\nexport function applyCommentMarks(\n editor: Editor,\n comments: CommentWithUser[]\n): void {\n if (!editor.view || editor.isDestroyed) return\n \n const { doc } = editor.state\n const textContent = doc.textContent\n\n comments.forEach((comment) => {\n if (!comment.quotedText || comment.parentId) return // Skip replies\n\n const index = textContent.indexOf(comment.quotedText)\n if (index === -1) return // Text not found\n\n // Find the actual position in the document\n let currentPos = 0\n let startPos: number | null = null\n let endPos: number | null = null\n\n doc.descendants((node, pos) => {\n if (startPos !== null && endPos !== null) return false\n\n if (node.isText && node.text) {\n const nodeStart = currentPos\n const nodeEnd = currentPos + node.text.length\n\n if (startPos === null && nodeEnd > index) {\n // Start is in this node\n const offsetInNode = index - nodeStart\n startPos = pos + offsetInNode\n }\n\n if (startPos !== null && endPos === null) {\n const targetEnd = index + comment.quotedText.length\n if (nodeEnd >= targetEnd) {\n // End is in this node\n const offsetInNode = targetEnd - nodeStart\n endPos = pos + offsetInNode\n }\n }\n\n currentPos = nodeEnd\n }\n\n return true\n })\n\n if (startPos !== null && endPos !== null) {\n editor\n .chain()\n .setTextSelection({ from: startPos, to: endPos })\n .setComment(comment.id)\n .setTextSelection(endPos) // Deselect\n .run()\n }\n })\n}\n\n/**\n * Scroll to a comment mark in the editor\n */\nexport function scrollToComment(editor: Editor, commentId: string): void {\n if (!editor.view || editor.isDestroyed) return\n \n const { doc } = editor.state\n\n doc.descendants((node, pos) => {\n const commentMark = node.marks.find(\n (mark) => mark.type.name === 'comment' && mark.attrs.commentId === commentId\n )\n\n if (commentMark) {\n editor.chain().setTextSelection(pos).run()\n\n // Scroll to the selection\n const view = editor.view\n const coords = view.coordsAtPos(pos)\n const editorRect = view.dom.getBoundingClientRect()\n\n if (coords.top < editorRect.top || coords.bottom > editorRect.bottom) {\n view.dom.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }\n\n return false // Stop iteration\n }\n\n return true\n })\n}\n","'use client'\n\nimport { useState, useRef, useEffect, useCallback } from 'react'\nimport { createPortal } from 'react-dom'\nimport { X, MessageSquare, ArrowUp, Loader2, ChevronDown, ChevronRight } from 'lucide-react'\nimport { cn } from '../../lib/cn'\nimport { CommentWithUser } from '../../lib/comments'\nimport { CommentThread } from './CommentThread'\n\ninterface CommentsPanelProps {\n comments: CommentWithUser[]\n currentUserEmail: string\n isAdmin: boolean\n selectedText: string | null\n onCreateComment: (content: string) => Promise<void>\n onReply: (parentId: string, content: string) => Promise<void>\n onEdit: (commentId: string, content: string) => Promise<void>\n onDelete: (commentId: string) => Promise<void>\n onResolve: (commentId: string) => Promise<void>\n onCommentClick: (commentId: string) => void\n activeCommentId: string | null\n isOpen: boolean\n onClose: () => void\n onClearSelection: () => void\n}\n\nexport function CommentsPanel({\n comments,\n currentUserEmail,\n isAdmin,\n selectedText,\n onCreateComment,\n onReply,\n onEdit,\n onDelete,\n onResolve,\n onCommentClick,\n activeCommentId,\n isOpen,\n onClose,\n onClearSelection,\n}: CommentsPanelProps) {\n const [newComment, setNewComment] = useState('')\n const [isAnimating, setIsAnimating] = useState(false)\n const [isVisible, setIsVisible] = useState(false)\n const [mounted, setMounted] = useState(false)\n const [creating, setCreating] = useState(false)\n const [showResolved, setShowResolved] = useState(false)\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const commentsEndRef = useRef<HTMLDivElement>(null)\n\n // Separate open and resolved comments\n const openComments = comments.filter((c) => !c.resolved)\n const resolvedComments = comments.filter((c) => c.resolved)\n\n // Client-side only for portal\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Handle open/close animation and body scroll lock\n useEffect(() => {\n if (isOpen) {\n setIsVisible(true)\n document.body.style.overflow = 'hidden'\n document.body.style.position = 'fixed'\n document.body.style.width = '100%'\n document.body.style.top = `-${window.scrollY}px`\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setIsAnimating(true)\n })\n })\n } else {\n setIsAnimating(false)\n const scrollY = document.body.style.top\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.width = ''\n document.body.style.top = ''\n window.scrollTo(0, parseInt(scrollY || '0') * -1)\n const timer = setTimeout(() => {\n setIsVisible(false)\n }, 200)\n return () => clearTimeout(timer)\n }\n }, [isOpen])\n\n // Focus textarea when selected text changes\n useEffect(() => {\n if (selectedText && isOpen) {\n textareaRef.current?.focus()\n }\n }, [selectedText, isOpen])\n\n // Auto-resize textarea as content grows\n useEffect(() => {\n const textarea = textareaRef.current\n if (textarea) {\n textarea.style.height = 'auto'\n textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`\n }\n }, [newComment])\n\n const handleCreateComment = useCallback(async () => {\n if (!newComment.trim() || !selectedText) return\n setCreating(true)\n try {\n await onCreateComment(newComment.trim())\n setNewComment('')\n onClearSelection()\n } finally {\n setCreating(false)\n }\n }, [newComment, selectedText, onCreateComment, onClearSelection])\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.stopPropagation()\n handleCreateComment()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n if (selectedText) {\n onClearSelection()\n setNewComment('')\n } else {\n onClose()\n }\n }\n }\n\n if (!isVisible || !mounted) return null\n\n return createPortal(\n <>\n {/* Backdrop */}\n <div\n className={cn(\n 'fixed inset-0 h-[100dvh] bg-black/20 z-[60] transition-opacity duration-200',\n isAnimating ? 'opacity-100' : 'opacity-0'\n )}\n onClick={onClose}\n />\n\n {/* Panel */}\n <div\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Comments\"\n className={cn(\n 'fixed z-[70] flex flex-col bg-white dark:bg-gray-900 shadow-xl transition-transform duration-200 ease-out overflow-hidden',\n 'inset-x-0 top-0 h-[100dvh]',\n 'md:left-auto md:w-full md:max-w-[380px] md:border-l md:border-gray-200 md:dark:border-gray-700',\n isAnimating ? 'translate-x-0' : 'translate-x-full'\n )}\n >\n {/* Header */}\n <div className=\"flex-shrink-0 border-b border-gray-200 dark:border-gray-700 px-4 py-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-4 h-4 text-gray-500\" />\n <h2 className=\"font-medium\">Comments</h2>\n {openComments.length > 0 && (\n <span className=\"text-xs text-gray-500\">\n ({openComments.length})\n </span>\n )}\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"w-8 h-8 rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center justify-center text-gray-500\"\n aria-label=\"Close comments\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n {/* New comment section - shown when text is selected */}\n {selectedText && (\n <div className=\"flex-shrink-0 border-b border-gray-200 dark:border-gray-700 px-4 py-5 bg-gray-50/50 dark:bg-gray-800/30\">\n <div className=\"flex items-center justify-between mb-3\">\n <div className=\"flex-1 px-2 py-1 bg-yellow-100/50 dark:bg-yellow-900/30 rounded text-sm italic text-gray-600 dark:text-gray-400 line-clamp-2\">\n \"{selectedText}\"\n </div>\n <button\n type=\"button\"\n onClick={() => {\n onClearSelection()\n setNewComment('')\n }}\n disabled={creating}\n className=\"ml-2 text-xs text-gray-500 hover:text-gray-900 dark:hover:text-white disabled:opacity-50\"\n >\n Cancel\n </button>\n </div>\n <div className=\"flex items-end gap-2\">\n <textarea\n ref={textareaRef}\n value={newComment}\n onChange={(e) => setNewComment(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder=\"Add a comment...\"\n className=\"flex-1 min-h-[60px] max-h-[120px] px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 resize-none text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\"\n rows={2}\n enterKeyHint=\"send\"\n />\n <button\n type=\"button\"\n onClick={handleCreateComment}\n disabled={creating || !newComment.trim()}\n className=\"w-10 h-10 flex-shrink-0 rounded-full bg-gray-100 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 flex items-center justify-center disabled:opacity-50 hover:bg-gray-200 dark:hover:bg-gray-700\"\n >\n {creating ? (\n <Loader2 className=\"h-5 w-5 animate-spin\" />\n ) : (\n <ArrowUp className=\"h-5 w-5\" />\n )}\n </button>\n </div>\n </div>\n )}\n\n {/* Comments list - hidden when composing new comment */}\n <div className={cn(\"flex-1 overflow-y-auto\", selectedText && \"hidden\")}>\n {comments.length === 0 ? (\n <div className=\"h-full flex items-center justify-center\">\n <div className=\"text-center max-w-xs px-6\">\n <MessageSquare className=\"w-8 h-8 mx-auto mb-2 text-gray-400\" />\n <p className=\"text-gray-500 text-sm\">\n No comments yet. Select text and click the comment button to add one.\n </p>\n </div>\n </div>\n ) : (\n <div className=\"p-4 space-y-3\">\n {/* Open comments */}\n {openComments.map((comment) => (\n <CommentThread\n key={comment.id}\n comment={comment}\n currentUserEmail={currentUserEmail}\n isAdmin={isAdmin}\n isActive={activeCommentId === comment.id}\n onReply={(content) => onReply(comment.id, content)}\n onEdit={(content) => onEdit(comment.id, content)}\n onDelete={() => onDelete(comment.id)}\n onResolve={() => onResolve(comment.id)}\n onClick={() => onCommentClick(comment.id)}\n />\n ))}\n\n {/* Resolved comments section */}\n {resolvedComments.length > 0 && (\n <div className=\"pt-2\">\n <button\n type=\"button\"\n onClick={() => setShowResolved(!showResolved)}\n className=\"w-full text-left text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors py-2\"\n >\n <span className=\"inline-flex items-center gap-1\">\n {showResolved ? <ChevronDown className=\"w-4 h-4\" /> : <ChevronRight className=\"w-4 h-4\" />}\n Resolved ({resolvedComments.length})\n </span>\n </button>\n {showResolved && (\n <div className=\"space-y-3 mt-2\">\n {resolvedComments.map((comment) => (\n <CommentThread\n key={comment.id}\n comment={comment}\n currentUserEmail={currentUserEmail}\n isAdmin={isAdmin}\n isActive={activeCommentId === comment.id}\n onReply={(content) => onReply(comment.id, content)}\n onEdit={(content) => onEdit(comment.id, content)}\n onDelete={() => onDelete(comment.id)}\n onResolve={() => onResolve(comment.id)}\n onClick={() => onCommentClick(comment.id)}\n />\n ))}\n </div>\n )}\n </div>\n )}\n\n <div ref={commentsEndRef} />\n </div>\n )}\n </div>\n </div>\n </>,\n document.body\n )\n}\n","'use client'\n\nimport { useState } from 'react'\nimport { MoreHorizontal, Reply, Check, Trash2, Pencil } from 'lucide-react'\nimport { cn } from '../../lib/cn'\nimport { CommentWithUser, canEditComment, canDeleteComment } from '../../lib/comments'\nimport { Dropdown, DropdownItem, DropdownDivider } from './Dropdown'\n\n// Simple relative time formatter\nfunction formatRelativeTime(dateStr: string): string {\n const date = new Date(dateStr)\n const now = new Date()\n const diff = now.getTime() - date.getTime()\n const minutes = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n if (hours < 24) return `${hours}h ago`\n if (days < 7) return `${days}d ago`\n return date.toLocaleDateString()\n}\n\ninterface CommentThreadProps {\n comment: CommentWithUser\n currentUserEmail: string\n isAdmin: boolean\n isActive: boolean\n onReply: (content: string) => Promise<void>\n onEdit: (content: string) => Promise<void>\n onDelete: () => Promise<void>\n onResolve: () => Promise<void>\n onClick: () => void\n}\n\nexport function CommentThread({\n comment,\n currentUserEmail,\n isAdmin,\n isActive,\n onReply,\n onEdit,\n onDelete,\n onResolve,\n onClick,\n}: CommentThreadProps) {\n const [isReplying, setIsReplying] = useState(false)\n const [isEditing, setIsEditing] = useState(false)\n const [replyContent, setReplyContent] = useState('')\n const [editContent, setEditContent] = useState(comment.content)\n const [loading, setLoading] = useState(false)\n\n const canEdit = canEditComment(comment, currentUserEmail)\n const canDelete = canDeleteComment(comment, currentUserEmail, isAdmin)\n const isOwn = comment.user.email === currentUserEmail\n\n const handleReply = async () => {\n if (!replyContent.trim()) return\n setLoading(true)\n try {\n await onReply(replyContent.trim())\n setReplyContent('')\n setIsReplying(false)\n } finally {\n setLoading(false)\n }\n }\n\n const handleEdit = async () => {\n if (!editContent.trim()) return\n setLoading(true)\n try {\n await onEdit(editContent.trim())\n setIsEditing(false)\n } finally {\n setLoading(false)\n }\n }\n\n const handleDelete = async () => {\n if (!confirm('Delete this comment?')) return\n setLoading(true)\n try {\n await onDelete()\n } finally {\n setLoading(false)\n }\n }\n\n return (\n <div\n role=\"button\"\n tabIndex={0}\n onClick={onClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n onClick()\n }\n }}\n className={cn(\n 'rounded-lg border p-3 transition-colors cursor-pointer',\n isActive\n ? 'border-yellow-400 bg-yellow-50/50 dark:border-yellow-600 dark:bg-yellow-900/20'\n : 'border-gray-200 dark:border-gray-700 bg-white dark:bg-gray-900',\n comment.resolved && 'opacity-60'\n )}\n >\n {/* Header */}\n <div className=\"flex items-start justify-between gap-2 mb-2\">\n <div className=\"flex items-center gap-2 text-sm\">\n <span className=\"font-medium\">\n {isOwn ? 'You' : comment.user.name || comment.user.email.split('@')[0]}\n </span>\n <span className=\"text-gray-500 dark:text-gray-400\">\n {formatRelativeTime(comment.createdAt)}\n </span>\n {comment.resolved && (\n <span className=\"inline-flex items-center gap-1 text-xs text-green-600 dark:text-green-400\">\n <Check className=\"w-3 h-3\" />\n Resolved\n </span>\n )}\n </div>\n\n {/* Dropdown menu */}\n <div onClick={(e) => e.stopPropagation()}>\n <Dropdown\n trigger={\n <button\n type=\"button\"\n className=\"w-6 h-6 rounded hover:bg-gray-100 dark:hover:bg-gray-800 flex items-center justify-center text-gray-500\"\n >\n <MoreHorizontal className=\"w-4 h-4\" />\n </button>\n }\n align=\"right\"\n className=\"min-w-[140px]\"\n >\n <DropdownItem onClick={onResolve}>\n <span className=\"flex items-center gap-2\">\n <Check className=\"w-4 h-4\" />\n {comment.resolved ? 'Unresolve' : 'Resolve'}\n </span>\n </DropdownItem>\n {canEdit && (\n <DropdownItem onClick={() => setIsEditing(true)}>\n <span className=\"flex items-center gap-2\">\n <Pencil className=\"w-4 h-4\" />\n Edit\n </span>\n </DropdownItem>\n )}\n {canDelete && (\n <>\n <DropdownDivider />\n <DropdownItem onClick={handleDelete} destructive>\n <span className=\"flex items-center gap-2\">\n <Trash2 className=\"w-4 h-4\" />\n Delete\n </span>\n </DropdownItem>\n </>\n )}\n </Dropdown>\n </div>\n </div>\n\n {/* Quoted text */}\n {comment.quotedText && (\n <div className=\"mb-2 px-2 py-1 bg-yellow-100/50 dark:bg-yellow-900/30 rounded text-sm italic text-gray-600 dark:text-gray-400 line-clamp-2\">\n \"{comment.quotedText}\"\n </div>\n )}\n\n {/* Content */}\n {isEditing ? (\n <div className=\"space-y-2\">\n <textarea\n value={editContent}\n onChange={(e) => {\n setEditContent(e.target.value)\n e.target.style.height = 'auto'\n e.target.style.height = `${Math.min(e.target.scrollHeight, 120)}px`\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.stopPropagation()\n handleEdit()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n setIsEditing(false)\n setEditContent(comment.content)\n }\n }}\n onClick={(e) => e.stopPropagation()}\n className=\"w-full min-h-[60px] max-h-[120px] px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 resize-none text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\"\n />\n <div className=\"flex gap-2 justify-end\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setIsEditing(false)\n setEditContent(comment.content)\n }}\n disabled={loading}\n className=\"px-3 py-1.5 text-sm rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n handleEdit()\n }}\n disabled={loading || !editContent.trim()}\n className=\"px-3 py-1.5 text-sm rounded-md bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50\"\n >\n Save\n </button>\n </div>\n </div>\n ) : (\n <p className=\"text-sm whitespace-pre-wrap\">{comment.content}</p>\n )}\n\n {/* Replies */}\n {comment.replies && comment.replies.length > 0 && (\n <div className=\"mt-3 pl-3 border-l-2 border-gray-200 dark:border-gray-700 space-y-3\">\n {comment.replies.map((reply) => (\n <ReplyItem\n key={reply.id}\n reply={reply}\n currentUserEmail={currentUserEmail}\n />\n ))}\n </div>\n )}\n\n {/* Reply button / form */}\n {!isEditing && (\n <div className=\"mt-3\">\n {isReplying ? (\n <div className=\"space-y-2\">\n <textarea\n value={replyContent}\n onChange={(e) => {\n setReplyContent(e.target.value)\n e.target.style.height = 'auto'\n e.target.style.height = `${Math.min(e.target.scrollHeight, 120)}px`\n }}\n onKeyDown={(e) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n e.stopPropagation()\n handleReply()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n setIsReplying(false)\n setReplyContent('')\n }\n }}\n placeholder=\"Write a reply...\"\n className=\"w-full min-h-[60px] max-h-[120px] px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-900 resize-none text-sm focus:outline-none focus:ring-1 focus:ring-blue-500\"\n onClick={(e) => e.stopPropagation()}\n />\n <div className=\"flex gap-2 justify-end\">\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setIsReplying(false)\n setReplyContent('')\n }}\n disabled={loading}\n className=\"px-3 py-1.5 text-sm rounded-md hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n handleReply()\n }}\n disabled={loading || !replyContent.trim()}\n className=\"px-3 py-1.5 text-sm rounded-md bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50\"\n >\n Reply\n </button>\n </div>\n </div>\n ) : (\n <button\n type=\"button\"\n onClick={(e) => {\n e.stopPropagation()\n setIsReplying(true)\n }}\n className=\"inline-flex items-center gap-1 text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors\"\n >\n <Reply className=\"w-3.5 h-3.5\" />\n Reply\n </button>\n )}\n </div>\n )}\n </div>\n )\n}\n\n// Simple reply display (no nested replies allowed)\nfunction ReplyItem({\n reply,\n currentUserEmail,\n}: {\n reply: CommentWithUser\n currentUserEmail: string\n}) {\n const isOwn = reply.user.email === currentUserEmail\n\n return (\n <div className=\"text-sm\">\n <div className=\"flex items-center gap-2 mb-1\">\n <span className=\"font-medium\">\n {isOwn ? 'You' : reply.user.name || reply.user.email.split('@')[0]}\n </span>\n <span className=\"text-gray-500 dark:text-gray-400 text-xs\">\n {formatRelativeTime(reply.createdAt)}\n </span>\n </div>\n <p className=\"whitespace-pre-wrap text-gray-600 dark:text-gray-400\">{reply.content}</p>\n </div>\n )\n}\n","/**\n * Comment types and client-side API helpers for the editor commenting system.\n * Used for collaborative inline comments on posts.\n */\n\n// Types matching Prisma schema + includes\nexport interface CommentUser {\n id: string\n name: string | null\n email: string\n}\n\nexport interface CommentWithUser {\n id: string\n postId: string\n userId: string\n quotedText: string\n content: string\n parentId: string | null\n resolved: boolean\n createdAt: string\n updatedAt: string\n user: CommentUser\n replies?: CommentWithUser[]\n}\n\nexport interface CreateCommentData {\n quotedText: string\n content: string\n parentId?: string\n}\n\nexport interface SelectionState {\n text: string\n from: number\n to: number\n hasExistingComment?: boolean\n}\n\n// Permission helpers (compare by email since that's what we have in session)\nexport function canDeleteComment(\n comment: CommentWithUser,\n currentUserEmail: string,\n isAdmin: boolean\n): boolean {\n return comment.user.email === currentUserEmail || isAdmin\n}\n\nexport function canEditComment(\n comment: CommentWithUser,\n currentUserEmail: string\n): boolean {\n return comment.user.email === currentUserEmail\n}\n\n// API client factory - creates functions bound to a specific API base path\nexport function createCommentsClient(apiBasePath: string = '/api/cms') {\n return {\n async fetchComments(postId: string): Promise<CommentWithUser[]> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments`)\n if (!res.ok) throw new Error('Failed to fetch comments')\n const json = await res.json()\n return json.data || json\n },\n\n async createComment(\n postId: string,\n data: CreateCommentData\n ): Promise<CommentWithUser> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to create comment')\n }\n const json = await res.json()\n return json.data || json\n },\n\n async updateComment(\n postId: string,\n commentId: string,\n content: string\n ): Promise<CommentWithUser> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/${commentId}`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ content }),\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to update comment')\n }\n const json = await res.json()\n return json.data || json\n },\n\n async deleteComment(\n postId: string,\n commentId: string\n ): Promise<void> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/${commentId}`, {\n method: 'DELETE',\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to delete comment')\n }\n },\n\n async toggleResolve(\n postId: string,\n commentId: string\n ): Promise<CommentWithUser> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/${commentId}/resolve`, {\n method: 'POST',\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to toggle resolve')\n }\n const json = await res.json()\n return json.data || json\n },\n\n async resolveAllComments(\n postId: string\n ): Promise<{ resolved: number }> {\n const res = await fetch(`${apiBasePath}/posts/${postId}/comments/resolve-all`, {\n method: 'POST',\n })\n if (!res.ok) {\n const error = await res.json()\n throw new Error(error.error || 'Failed to resolve all comments')\n }\n const json = await res.json()\n return json.data || json\n },\n }\n}\n","'use client'\n\nimport { useState, useEffect, useRef } from 'react'\n\ninterface Tag {\n id: string\n name: string\n}\n\ninterface TagsSectionProps {\n tags: Tag[]\n onTagsChange: (tags: Tag[]) => void\n apiBasePath: string\n disabled?: boolean\n}\n\nexport function TagsSection({\n tags,\n onTagsChange,\n apiBasePath,\n disabled = false,\n}: TagsSectionProps) {\n const [isExpanded, setIsExpanded] = useState(false)\n const [availableTags, setAvailableTags] = useState<Tag[]>([])\n const [loading, setLoading] = useState(false)\n const [dropdownOpen, setDropdownOpen] = useState(false)\n const dropdownRef = useRef<HTMLDivElement>(null)\n\n // Fetch available tags when section expands\n useEffect(() => {\n if (isExpanded && availableTags.length === 0) {\n setLoading(true)\n fetch(`${apiBasePath}/tags`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(data => {\n setAvailableTags(data.data || [])\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }\n }, [isExpanded, availableTags.length, apiBasePath])\n\n // Close dropdown when clicking outside\n useEffect(() => {\n if (!dropdownOpen) return\n function handleClick(e: MouseEvent) {\n if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {\n setDropdownOpen(false)\n }\n }\n document.addEventListener('click', handleClick)\n return () => document.removeEventListener('click', handleClick)\n }, [dropdownOpen])\n\n const handleAddTag = (tagId: string) => {\n const tagToAdd = availableTags.find(t => t.id === tagId)\n if (tagToAdd && !tags.some(t => t.id === tagId)) {\n onTagsChange([...tags, tagToAdd])\n }\n setDropdownOpen(false)\n }\n\n const handleRemoveTag = (tagId: string) => {\n onTagsChange(tags.filter(t => t.id !== tagId))\n }\n\n // Tags not already selected\n const unselectedTags = availableTags.filter(\n at => !tags.some(t => t.id === at.id)\n )\n\n const tagSummary = tags.length === 0\n ? 'no tags'\n : tags.length === 1\n ? '1 tag'\n : `${tags.length} tags`\n\n return (\n <div className=\"border-t border-gray-200 dark:border-gray-800 pt-4\">\n <button\n type=\"button\"\n onClick={() => setIsExpanded(!isExpanded)}\n className=\"flex items-center gap-2 text-sm text-gray-500 hover:text-gray-900 dark:hover:text-white transition-colors w-full\"\n >\n <svg \n className={`h-4 w-4 transition-transform ${isExpanded ? 'rotate-90' : ''}`} \n fill=\"none\" \n viewBox=\"0 0 24 24\" \n stroke=\"currentColor\"\n >\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M9 5l7 7-7 7\" />\n </svg>\n <span>Tags</span>\n <span className=\"ml-auto text-xs text-gray-400\">{tagSummary}</span>\n </button>\n\n {isExpanded && (\n <div className=\"mt-4 space-y-3 pl-6\">\n {/* Selected tags */}\n {tags.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {tags.map(tag => (\n <span\n key={tag.id}\n className=\"inline-flex items-center gap-1 px-2 py-1 rounded-md bg-gray-100 dark:bg-gray-800 text-gray-700 dark:text-gray-300 text-sm\"\n >\n {tag.name}\n {!disabled && (\n <button\n type=\"button\"\n onClick={() => handleRemoveTag(tag.id)}\n className=\"hover:text-red-500 transition-colors\"\n >\n <svg className=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M6 18L18 6M6 6l12 12\" />\n </svg>\n </button>\n )}\n </span>\n ))}\n </div>\n )}\n\n {/* Add tag dropdown */}\n {!disabled && (\n <>\n {loading ? (\n <p className=\"text-sm text-gray-500\">Loading tags...</p>\n ) : unselectedTags.length > 0 ? (\n <div className=\"relative max-w-[200px]\" ref={dropdownRef}>\n <button\n type=\"button\"\n onClick={() => setDropdownOpen(!dropdownOpen)}\n className=\"w-full h-8 px-3 text-sm text-left border border-gray-200 dark:border-gray-700 rounded-md bg-transparent flex items-center justify-between\"\n >\n <span className=\"text-gray-500\">Add tag...</span>\n <svg className=\"h-4 w-4 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path strokeLinecap=\"round\" strokeLinejoin=\"round\" strokeWidth={2} d=\"M19 9l-7 7-7-7\" />\n </svg>\n </button>\n {dropdownOpen && (\n <div \n className=\"absolute z-50 top-full left-0 mt-1 w-full bg-popover border border-border rounded-md shadow-lg max-h-48 overflow-auto overscroll-contain p-1\"\n onWheel={(e) => e.stopPropagation()}\n >\n {unselectedTags.map(tag => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => handleAddTag(tag.id)}\n className=\"w-full px-3 py-2.5 md:px-2 md:py-1.5 min-h-[44px] md:min-h-0 text-left text-sm rounded-sm hover:bg-accent cursor-default\"\n >\n {tag.name}\n </button>\n ))}\n </div>\n )}\n </div>\n ) : availableTags.length === 0 ? (\n <p className=\"text-sm text-gray-500\">\n No tags available. Create tags in Settings.\n </p>\n ) : (\n <p className=\"text-sm text-gray-500\">All tags added</p>\n )}\n </>\n )}\n </div>\n )}\n </div>\n )\n}\n","'use client'\n\nimport { useState, useCallback, useEffect } from 'react'\nimport type { Editor } from '@tiptap/react'\nimport {\n CommentWithUser,\n SelectionState,\n createCommentsClient,\n} from '../../lib/comments'\nimport {\n addCommentMark,\n removeCommentMark,\n applyCommentMarks,\n scrollToComment,\n} from '../../lib/comment-mark'\n\nexport interface UseCommentsOptions {\n postId: string | null\n editor: Editor | null\n apiBasePath?: string\n onSave?: () => Promise<string | null> // Called to auto-save post, returns postId\n}\n\nexport interface CommentsState {\n list: CommentWithUser[]\n loading: boolean\n activeId: string | null\n setActiveId: (id: string | null) => void\n selectedText: SelectionState | null\n setSelectedText: (selection: SelectionState | null) => void\n postId: string | null\n create: (content: string) => Promise<void>\n reply: (parentId: string, content: string) => Promise<void>\n edit: (commentId: string, content: string) => Promise<void>\n remove: (commentId: string) => Promise<void>\n resolve: (commentId: string) => Promise<void>\n resolveAll: () => Promise<void>\n scrollTo: (commentId: string) => void\n openCount: number\n}\n\nexport function useComments({\n postId: initialPostId,\n editor,\n apiBasePath = '/api/cms',\n onSave,\n}: UseCommentsOptions): CommentsState {\n const [postId, setPostId] = useState<string | null>(initialPostId)\n const [comments, setComments] = useState<CommentWithUser[]>([])\n const [loading, setLoading] = useState(false)\n const [activeCommentId, setActiveCommentId] = useState<string | null>(null)\n const [selectedText, setSelectedText] = useState<SelectionState | null>(null)\n\n const client = createCommentsClient(apiBasePath)\n\n // Update postId when it changes\n useEffect(() => {\n setPostId(initialPostId)\n }, [initialPostId])\n\n // Fetch comments when postId changes\n useEffect(() => {\n if (!postId) {\n setComments([])\n return\n }\n\n setLoading(true)\n client.fetchComments(postId)\n .then((data) => {\n setComments(data)\n // Apply comment marks to editor\n if (editor) {\n applyCommentMarks(editor, data)\n }\n })\n .catch(console.error)\n .finally(() => setLoading(false))\n }, [postId, editor])\n\n // Create a new comment\n const createComment = useCallback(async (content: string) => {\n if (!selectedText) return\n\n // Auto-save the post first if it hasn't been saved yet\n let effectivePostId = postId\n if (!effectivePostId && onSave) {\n effectivePostId = await onSave()\n if (effectivePostId) {\n setPostId(effectivePostId)\n }\n }\n\n if (!effectivePostId) {\n console.error('Cannot create comment: no post ID')\n return\n }\n\n const comment = await client.createComment(effectivePostId, {\n quotedText: selectedText.text,\n content,\n })\n\n // Add highlight mark to editor\n if (editor) {\n addCommentMark(editor, comment.id, selectedText.from, selectedText.to)\n }\n\n // Add to list with empty replies array\n setComments((prev) => [{ ...comment, replies: [] }, ...prev])\n setSelectedText(null)\n }, [postId, selectedText, editor, onSave, client])\n\n // Reply to a comment\n const replyToComment = useCallback(async (parentId: string, content: string) => {\n if (!postId) return\n\n const reply = await client.createComment(postId, {\n quotedText: '',\n content,\n parentId,\n })\n\n // Add reply to parent's replies array\n setComments((prev) =>\n prev.map((c) =>\n c.id === parentId\n ? { ...c, replies: [...(c.replies || []), reply] }\n : c\n )\n )\n }, [postId, client])\n\n // Edit a comment\n const editComment = useCallback(async (commentId: string, content: string) => {\n if (!postId) return\n\n const updated = await client.updateComment(postId, commentId, content)\n\n // Update in list (could be top-level or reply)\n setComments((prev) =>\n prev.map((c) => {\n if (c.id === commentId) {\n return { ...c, ...updated }\n }\n // Check in replies\n if (c.replies) {\n return {\n ...c,\n replies: c.replies.map((r) =>\n r.id === commentId ? { ...r, ...updated } : r\n ),\n }\n }\n return c\n })\n )\n }, [postId, client])\n\n // Remove a comment\n const removeComment = useCallback(async (commentId: string) => {\n if (!postId) return\n\n await client.deleteComment(postId, commentId)\n\n // Remove highlight mark from editor\n if (editor) {\n removeCommentMark(editor, commentId)\n }\n\n // Remove from list (could be top-level or reply)\n setComments((prev) => {\n // Check if it's a top-level comment\n const isTopLevel = prev.some((c) => c.id === commentId)\n if (isTopLevel) {\n return prev.filter((c) => c.id !== commentId)\n }\n // It's a reply\n return prev.map((c) => ({\n ...c,\n replies: c.replies?.filter((r) => r.id !== commentId),\n }))\n })\n }, [postId, editor, client])\n\n // Resolve/unresolve a comment\n const resolveComment = useCallback(async (commentId: string) => {\n if (!postId) return\n\n const updated = await client.toggleResolve(postId, commentId)\n\n // Toggle highlight: remove when resolved, re-add when unresolved\n if (editor) {\n const comment = comments.find((c) => c.id === commentId)\n if (updated.resolved) {\n removeCommentMark(editor, commentId)\n } else if (comment && selectedText) {\n // Re-apply mark if unresolving\n addCommentMark(editor, commentId, selectedText.from, selectedText.to)\n }\n }\n\n // Update in list\n setComments((prev) =>\n prev.map((c) => (c.id === commentId ? { ...c, resolved: updated.resolved } : c))\n )\n }, [postId, editor, comments, selectedText, client])\n\n // Resolve all comments\n const resolveAllComments = useCallback(async () => {\n if (!postId) return\n\n await client.resolveAllComments(postId)\n\n // Remove all highlight marks\n if (editor) {\n comments.filter((c) => !c.resolved && !c.parentId).forEach((c) => {\n removeCommentMark(editor, c.id)\n })\n }\n\n // Update all comments to resolved\n setComments((prev) =>\n prev.map((c) => ({ ...c, resolved: true }))\n )\n }, [postId, editor, comments, client])\n\n // Scroll to a comment in the editor\n const scrollToCommentMark = useCallback((commentId: string) => {\n if (editor) {\n scrollToComment(editor, commentId)\n }\n setActiveCommentId(commentId)\n }, [editor])\n\n // Count open comments (non-resolved, non-replies)\n const openCount = comments.filter((c) => !c.resolved && !c.parentId).length\n\n return {\n list: comments,\n loading,\n activeId: activeCommentId,\n setActiveId: setActiveCommentId,\n selectedText,\n setSelectedText,\n postId,\n create: createComment,\n reply: replyToComment,\n edit: editComment,\n remove: removeComment,\n resolve: resolveComment,\n resolveAll: resolveAllComments,\n scrollTo: scrollToCommentMark,\n openCount,\n }\n}\n","'use client'\n\nimport { useState, useEffect } from 'react'\nimport { ChevronDown, ChevronLeft, ChevronRight, RotateCcw, Plus, Pencil, Trash2, Play, X, MoreVertical, MoreHorizontal, Loader2 } from 'lucide-react'\nimport { useDashboardContext } from '../context'\n\ninterface SettingsLink {\n path: string\n label: string\n description: string\n countKey?: string\n}\n\ninterface User {\n id: string\n email: string\n name: string | null\n role: string\n createdAt: string\n}\n\ninterface Tag {\n id: string\n name: string\n createdAt?: string\n _count?: { posts: number }\n}\n\ninterface Topic {\n id: string\n name: string\n isActive: boolean\n keywords: string\n rssFeeds: string\n frequency: string\n maxPerPeriod: number\n essayFocus: string | null\n useKeywordFilter: boolean\n lastRunAt: string | null\n _count?: { posts: number; newsItems: number }\n}\n\ninterface Revision {\n id: string\n postId: string\n title: string | null\n markdown: string\n createdAt: string\n post: { id: string; title: string; slug: string; markdown: string }\n}\n\n\nexport function SettingsPage({ subPath }: { subPath: string }) {\n const { navigate, sharedData, sharedDataLoading } = useDashboardContext()\n const counts = sharedData?.counts || {}\n const autoDraftEnabled = sharedData?.settings?.autoDraftEnabled ?? false\n const loading = sharedDataLoading\n\n const allSettingsLinks: SettingsLink[] = [\n { path: '/settings/users', label: 'Users', description: 'Manage who can access the CMS', countKey: 'users' },\n { path: '/settings/posts', label: 'All Posts', description: 'Manage all posts', countKey: 'posts' },\n { path: '/settings/tags', label: 'Tags', description: 'Organize posts with tags', countKey: 'tags' },\n { path: '/settings/ai', label: 'AI Settings', description: 'Configure AI models and rules' },\n { path: '/settings/revisions', label: 'Revisions', description: 'View revision history' },\n { path: '/settings/comments', label: 'Comments', description: 'Manage post comments' },\n { path: '/settings/topics', label: 'Topics', description: 'RSS subscriptions for auto-draft', countKey: 'topics' },\n ]\n\n // Filter out Topics if autoDraftEnabled is off\n const settingsLinks = autoDraftEnabled \n ? allSettingsLinks \n : allSettingsLinks.filter(link => link.path !== '/settings/topics')\n\n // Main settings overview\n if (!subPath || subPath === '/') {\n if (loading) {\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n <div className=\"h-7 w-24 bg-muted rounded animate-pulse mb-6\" />\n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4\">\n {[1, 2, 3, 4, 5, 6].map(i => (\n <div key={i} className=\"p-4 sm:p-6 border border-border rounded-lg\">\n <div className=\"h-4 w-16 bg-muted rounded animate-pulse\" />\n <div className=\"h-8 w-12 bg-muted rounded animate-pulse mt-2\" />\n </div>\n ))}\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n <h2 className=\"text-lg font-semibold pb-4 mb-6 border-b border-border\">Settings</h2>\n \n <div className=\"grid grid-cols-2 sm:grid-cols-3 gap-3 sm:gap-4\">\n {settingsLinks.map((item) => (\n <button\n key={item.path}\n onClick={() => navigate(item.path)}\n className=\"p-4 sm:p-6 border border-border rounded-lg text-left hover:bg-accent transition-colors\"\n >\n <p className=\"text-sm text-muted-foreground\">{item.label}</p>\n {item.countKey ? (\n <p className=\"text-2xl font-bold mt-1\">{counts[item.countKey] ?? 0}</p>\n ) : (\n <p className=\"text-sm text-muted-foreground mt-1\">Configure →</p>\n )}\n </button>\n ))}\n </div>\n </div>\n )\n }\n\n // Sub-pages - each component renders its own header\n const pageName = subPath.slice(1) // Remove leading /\n \n // Handle revision detail page: /revisions/:id\n const revisionDetailMatch = pageName.match(/^revisions\\/(.+)$/)\n \n return (\n <div className=\"max-w-5xl mx-auto px-6 py-8\">\n {pageName === 'users' && <UsersSettingsContent />}\n {pageName === 'ai' && <AISettingsContent />}\n {pageName === 'tags' && <TagsSettingsContent />}\n {pageName === 'topics' && <TopicsSettingsContent />}\n {pageName === 'posts' && <PostsSettingsContent />}\n {pageName === 'revisions' && <RevisionsSettingsContent />}\n {revisionDetailMatch && <RevisionDetailContent revisionId={revisionDetailMatch[1]} />}\n {pageName === 'comments' && <CommentsSettingsContent />}\n </div>\n )\n}\n\nfunction UsersSettingsContent() {\n const { apiBasePath } = useDashboardContext()\n const [users, setUsers] = useState<User[]>([])\n const [loading, setLoading] = useState(true)\n const [showForm, setShowForm] = useState(false)\n const [editingUser, setEditingUser] = useState<User | null>(null)\n const [formEmail, setFormEmail] = useState('')\n const [formName, setFormName] = useState('')\n const [formRole, setFormRole] = useState('writer')\n const [formError, setFormError] = useState('')\n const [saving, setSaving] = useState(false)\n const [menuOpen, setMenuOpen] = useState<string | null>(null)\n\n useEffect(() => {\n fetch(`${apiBasePath}/users`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => { setUsers(res.data || []); setLoading(false) })\n .catch(() => setLoading(false))\n }, [apiBasePath])\n\n function resetForm() {\n setFormEmail('')\n setFormName('')\n setFormRole('writer')\n setFormError('')\n setEditingUser(null)\n }\n\n function openNewForm() {\n resetForm()\n setShowForm(true)\n }\n\n function openEditForm(user: User) {\n setEditingUser(user)\n setFormEmail(user.email)\n setFormName(user.name || '')\n setFormRole(user.role)\n setFormError('')\n setShowForm(true)\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!formEmail) return\n setSaving(true)\n setFormError('')\n\n const url = editingUser ? `${apiBasePath}/users/${editingUser.id}` : `${apiBasePath}/users`\n const method = editingUser ? 'PATCH' : 'POST'\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: formEmail, name: formName || null, role: formRole }),\n })\n\n if (res.ok) {\n const json = await res.json()\n if (editingUser) {\n setUsers(users.map(u => u.id === editingUser.id ? json.data : u))\n } else {\n setUsers([json.data, ...users])\n }\n setShowForm(false)\n resetForm()\n } else {\n const result = await res.json().catch(() => ({}))\n setFormError(result.error || `Failed to ${editingUser ? 'update' : 'create'} user`)\n }\n setSaving(false)\n }\n\n async function handleDeleteUser(id: string, email: string) {\n if (!confirm(`Delete user \"${email}\"?`)) return\n const res = await fetch(`${apiBasePath}/users/${id}`, { method: 'DELETE' })\n if (res.ok) setUsers(users.filter(u => u.id !== id))\n setMenuOpen(null)\n }\n\n function getRoleBadgeClasses(role: string) {\n if (role === 'admin') return 'bg-primary text-primary-foreground'\n if (role === 'drafter') return 'border border-border text-foreground'\n return 'bg-secondary text-secondary-foreground'\n }\n\n if (loading) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div className=\"space-y-6\">\n {/* Add/Edit Form - shown as separate view like old interface */}\n {showForm ? (\n <div className=\"max-w-md\">\n <div className=\"flex items-center justify-between mb-6\">\n <h2 className=\"text-lg font-semibold\">{editingUser ? 'Edit User' : 'Add User'}</h2>\n </div>\n\n {formError && (\n <div className=\"bg-destructive/10 text-destructive p-3 rounded-lg text-sm mb-4\">\n {formError}\n </div>\n )}\n\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"email\" className=\"block text-sm font-medium\">Email *</label>\n <input\n type=\"email\"\n id=\"email\"\n value={formEmail}\n onChange={e => setFormEmail(e.target.value)}\n required\n placeholder={editingUser ? undefined : 'user@example.com'}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label htmlFor=\"name\" className=\"block text-sm font-medium\">Name</label>\n <input\n type=\"text\"\n id=\"name\"\n value={formName}\n onChange={e => setFormName(e.target.value)}\n placeholder={editingUser ? undefined : 'John Doe'}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n />\n </div>\n\n <div className=\"space-y-2\">\n <label htmlFor=\"role\" className=\"block text-sm font-medium\">Role</label>\n <select\n id=\"role\"\n value={formRole}\n onChange={e => setFormRole(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n >\n <option value=\"drafter\">Drafter</option>\n <option value=\"writer\">Writer</option>\n <option value=\"admin\">Admin</option>\n </select>\n </div>\n\n <div className=\"flex gap-4 pt-2\">\n <button type=\"submit\" disabled={saving} className=\"px-4 py-2 bg-primary text-primary-foreground rounded-md disabled:opacity-50\">\n {saving ? (editingUser ? 'Saving...' : 'Creating...') : (editingUser ? 'Save Changes' : 'Create User')}\n </button>\n <button type=\"button\" onClick={() => { setShowForm(false); resetForm() }} className=\"px-4 py-2 text-muted-foreground hover:text-foreground\">\n Cancel\n </button>\n </div>\n </form>\n </div>\n ) : (\n <>\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">Users</h2>\n <button onClick={openNewForm} className=\"px-4 py-2 bg-primary text-primary-foreground rounded-md text-sm\">\n Add User\n </button>\n </div>\n\n {/* Desktop Table */}\n <div className=\"hidden md:block rounded-md border border-border\">\n <table className=\"w-full\">\n <thead className=\"bg-muted/50\">\n <tr>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Email</th>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Name</th>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Role</th>\n <th className=\"px-4 py-3 text-left text-sm font-medium text-muted-foreground\">Created</th>\n <th className=\"px-4 py-3 text-right text-sm font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"divide-y divide-border\">\n {users.map(user => (\n <tr key={user.id}>\n <td className=\"px-4 py-3 text-sm max-w-[250px] truncate\">{user.email}</td>\n <td className=\"px-4 py-3 text-sm text-muted-foreground max-w-[150px] truncate\">{user.name || '—'}</td>\n <td className=\"px-4 py-3 text-sm\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getRoleBadgeClasses(user.role)}`}>\n {user.role}\n </span>\n </td>\n <td className=\"px-4 py-3 text-sm text-muted-foreground\">{new Date(user.createdAt).toLocaleDateString()}</td>\n <td className=\"px-4 py-3 text-right\">\n <div className=\"relative inline-block\">\n <button\n onClick={() => setMenuOpen(menuOpen === user.id ? null : user.id)}\n className=\"p-1.5 text-muted-foreground hover:text-foreground rounded hover:bg-accent\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === user.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[100px] py-1\">\n <button\n onClick={() => { openEditForm(user); setMenuOpen(null) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"h-px bg-border my-1\" />\n <button\n onClick={() => handleDeleteUser(user.id, user.email)}\n className=\"w-full px-3 py-2 text-left text-sm text-red-500 hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n {users.length === 0 && (\n <p className=\"p-8 text-center text-muted-foreground\">No users yet. Add one to get started.</p>\n )}\n </div>\n\n {/* Mobile List */}\n <div className=\"md:hidden divide-y divide-border rounded-md border border-border bg-background\">\n {users.map(user => (\n <div key={user.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">{user.email}</span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getRoleBadgeClasses(user.role)}`}>\n {user.role}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {user.name || 'No name'} · Created {new Date(user.createdAt).toLocaleDateString()}\n </p>\n </div>\n <div className=\"relative\">\n <button\n onClick={() => setMenuOpen(menuOpen === user.id ? null : user.id)}\n className=\"p-1.5 text-muted-foreground hover:text-foreground rounded hover:bg-accent\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === user.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[100px] py-1\">\n <button\n onClick={() => { openEditForm(user); setMenuOpen(null) }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"h-px bg-border my-1\" />\n <button\n onClick={() => handleDeleteUser(user.id, user.email)}\n className=\"w-full px-3 py-2 text-left text-sm text-red-500 hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n {users.length === 0 && (\n <p className=\"p-8 text-center text-muted-foreground\">No users yet. Add one to get started.</p>\n )}\n </div>\n </>\n )}\n </div>\n )\n}\n\nfunction CollapsibleTemplate({ \n label, \n value, \n defaultValue, \n onChange, \n onReset, \n placeholders, \n disabled \n}: { \n label: string\n value: string | null\n defaultValue: string\n onChange: (v: string) => void\n onReset: () => void\n placeholders: string\n disabled?: boolean\n}) {\n const [open, setOpen] = useState(false)\n const isCustom = value !== null\n const displayValue = value ?? defaultValue\n\n return (\n <div className=\"mt-2\">\n <button\n type=\"button\"\n onClick={() => setOpen(!open)}\n className=\"flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground\"\n >\n <ChevronDown className={`h-4 w-4 transition-transform ${open ? '' : '-rotate-90'}`} />\n {isCustom ? `Edit prompt template (customized)` : `Edit prompt template`}\n </button>\n {open && (\n <div className=\"mt-2 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <p className=\"text-xs text-muted-foreground\">Placeholders: {placeholders}</p>\n {isCustom && (\n <button type=\"button\" onClick={onReset} className=\"flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground\">\n <RotateCcw className=\"h-3 w-3\" /> Reset to default\n </button>\n )}\n </div>\n <textarea\n value={displayValue}\n onChange={e => onChange(e.target.value)}\n rows={10}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent resize-none font-mono text-xs\"\n disabled={disabled}\n />\n </div>\n )}\n </div>\n )\n}\n\nfunction AISettingsContent() {\n const { apiBasePath, navigate } = useDashboardContext()\n const [rules, setRules] = useState('')\n const [chatRules, setChatRules] = useState('')\n const [rewriteRules, setRewriteRules] = useState('')\n const [autoDraftRules, setAutoDraftRules] = useState('')\n const [planRules, setPlanRules] = useState('')\n const [autoDraftWordCount, setAutoDraftWordCount] = useState(800)\n const [autoDraftEnabled, setAutoDraftEnabled] = useState(false)\n const [defaultModel, setDefaultModel] = useState('claude-sonnet')\n const [models, setModels] = useState<{ id: string; name: string; description: string }[]>([])\n const [generateTemplate, setGenerateTemplate] = useState<string | null>(null)\n const [chatTemplate, setChatTemplate] = useState<string | null>(null)\n const [rewriteTemplate, setRewriteTemplate] = useState<string | null>(null)\n const [autoDraftTemplate, setAutoDraftTemplate] = useState<string | null>(null)\n const [planTemplate, setPlanTemplate] = useState<string | null>(null)\n const [expandPlanTemplate, setExpandPlanTemplate] = useState<string | null>(null)\n const [defaultGenerateTemplate, setDefaultGenerateTemplate] = useState('')\n const [defaultChatTemplate, setDefaultChatTemplate] = useState('')\n const [defaultRewriteTemplate, setDefaultRewriteTemplate] = useState('')\n const [defaultAutoDraftTemplate, setDefaultAutoDraftTemplate] = useState('')\n const [defaultPlanRules, setDefaultPlanRules] = useState('')\n const [defaultPlanTemplate, setDefaultPlanTemplate] = useState('')\n const [defaultExpandPlanTemplate, setDefaultExpandPlanTemplate] = useState('')\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [saved, setSaved] = useState(false)\n\n useEffect(() => {\n Promise.all([\n fetch(`${apiBasePath}/ai/settings`).then(res => res.ok ? res.json() : Promise.reject()),\n fetch(`${apiBasePath}/settings`).then(res => res.ok ? res.json() : Promise.reject()),\n ])\n .then(([aiRes, settingsRes]) => {\n // Handle both wrapped { data: {...} } and unwrapped {...} response formats\n const data = aiRes.data || aiRes || {}\n setRules(data.rules || '')\n setChatRules(data.chatRules || '')\n setRewriteRules(data.rewriteRules || '')\n setAutoDraftRules(data.autoDraftRules || '')\n setPlanRules(data.planRules || '')\n setAutoDraftWordCount(data.autoDraftWordCount ?? 800)\n setDefaultModel(data.defaultModel || 'claude-sonnet')\n setModels(data.availableModels || [\n { id: 'claude-sonnet', name: 'Sonnet 4.5', description: 'Fast, capable, best value' },\n { id: 'claude-opus', name: 'Opus 4.5', description: 'Highest quality, slower' },\n { id: 'gpt-5.2', name: 'GPT-5.2', description: 'Latest OpenAI flagship' },\n { id: 'gpt-5-mini', name: 'GPT-5 Mini', description: 'Fast and cost-efficient' },\n ])\n setGenerateTemplate(data.generateTemplate ?? null)\n setChatTemplate(data.chatTemplate ?? null)\n setRewriteTemplate(data.rewriteTemplate ?? null)\n setAutoDraftTemplate(data.autoDraftTemplate ?? null)\n setPlanTemplate(data.planTemplate ?? null)\n setExpandPlanTemplate(data.expandPlanTemplate ?? null)\n setDefaultGenerateTemplate(data.defaultGenerateTemplate || '')\n setDefaultChatTemplate(data.defaultChatTemplate || '')\n setDefaultRewriteTemplate(data.defaultRewriteTemplate || '')\n setDefaultAutoDraftTemplate(data.defaultAutoDraftTemplate || '')\n setDefaultPlanRules(data.defaultPlanRules || '')\n setDefaultPlanTemplate(data.defaultPlanTemplate || '')\n setDefaultExpandPlanTemplate(data.defaultExpandPlanTemplate || '')\n // Settings\n setAutoDraftEnabled(settingsRes.data?.autoDraftEnabled ?? false)\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath])\n\n\n async function handleSave() {\n setSaving(true)\n setSaved(false)\n await Promise.all([\n fetch(`${apiBasePath}/ai/settings`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n rules, chatRules, rewriteRules, autoDraftRules, planRules, autoDraftWordCount, defaultModel,\n generateTemplate, chatTemplate, rewriteTemplate, autoDraftTemplate, planTemplate, expandPlanTemplate\n }),\n }),\n fetch(`${apiBasePath}/settings`, {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ autoDraftEnabled }),\n }),\n ])\n setSaving(false)\n setSaved(true)\n setTimeout(() => setSaved(false), 2000)\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center py-12\">\n <Loader2 className=\"h-6 w-6 animate-spin text-muted-foreground\" />\n </div>\n )\n }\n\n return (\n <div className=\"space-y-6\">\n {/* Header - matches other settings pages */}\n <div>\n <h2 className=\"text-lg font-semibold\">AI Settings</h2>\n <p className=\"text-sm text-muted-foreground\">Configure your AI writing assistant.</p>\n </div>\n\n {/* Card container */}\n <div className=\"rounded-lg border bg-card text-card-foreground shadow-sm\">\n <div className=\"p-6 space-y-6\">\n <div className=\"flex items-center gap-4\">\n <label className=\"text-sm font-medium leading-none shrink-0\">Default Model</label>\n <div className=\"relative max-w-sm flex-1\">\n <select\n value={defaultModel}\n onChange={e => setDefaultModel(e.target.value)}\n className=\"h-10 w-full appearance-none rounded-md border border-input bg-background pl-3 pr-10 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 cursor-pointer\"\n >\n {models.map(model => (\n <option key={model.id} value={model.id}>{model.name} — {model.description}</option>\n ))}\n </select>\n <ChevronDown className=\"absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 opacity-50 pointer-events-none\" />\n </div>\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Essay Writing Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Style and format rules for generated essays. Applied when generating or rewriting content.\n </p>\n <textarea\n value={rules}\n onChange={e => setRules(e.target.value)}\n placeholder={`- Never use \"utilize\" — always say \"use\"\n- Avoid passive voice\n- Start with concrete scenes, not abstractions\n- Short paragraphs (3-4 sentences max)\n- Use em-dashes sparingly\n- End with forward motion, not tidy conclusions`}\n className=\"flex min-h-[180px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <CollapsibleTemplate\n label=\"Generate\"\n value={generateTemplate}\n defaultValue={defaultGenerateTemplate}\n onChange={setGenerateTemplate}\n onReset={() => setGenerateTemplate(null)}\n placeholders=\"{{RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Chat Behavior Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n How the assistant should behave during brainstorming conversations. Controls personality and interaction style.\n </p>\n <textarea\n value={chatRules}\n onChange={e => setChatRules(e.target.value)}\n placeholder={`- Be direct and concise\n- Push back on vague ideas\n- Ask clarifying questions before drafting\n- Challenge my assumptions`}\n className=\"flex min-h-[140px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <CollapsibleTemplate\n label=\"Chat\"\n value={chatTemplate}\n defaultValue={defaultChatTemplate}\n onChange={setChatTemplate}\n onReset={() => setChatTemplate(null)}\n placeholders=\"{{RULES}}, {{CHAT_RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Rewrite Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Rules for cleaning up selected text with the rewrite tool.\n </p>\n <textarea\n value={rewriteRules}\n onChange={e => setRewriteRules(e.target.value)}\n placeholder={`- Keep the same meaning, improve clarity\n- Maintain sentence length variety\n- Remove filler words\n- Don't add new ideas`}\n className=\"flex min-h-[140px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <CollapsibleTemplate\n label=\"Rewrite\"\n value={rewriteTemplate}\n defaultValue={defaultRewriteTemplate}\n onChange={setRewriteTemplate}\n onReset={() => setRewriteTemplate(null)}\n placeholders=\"{{RULES}}, {{REWRITE_RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Auto-Draft Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Rules for generating essays from news articles via RSS feeds. Controls how topics are transformed into original essays.\n </p>\n <textarea\n value={autoDraftRules}\n onChange={e => setAutoDraftRules(e.target.value)}\n placeholder={`- Write original perspectives, don't summarize\n- Take a contrarian angle when appropriate\n- Include personal insights and experiences\n- Focus on implications, not just facts`}\n className=\"flex min-h-[140px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n <div className=\"flex items-center gap-4 pt-2\">\n <div className=\"flex items-center gap-2\">\n <label className=\"text-sm whitespace-nowrap\">Target word count:</label>\n <input\n type=\"number\"\n min={200}\n max={3000}\n value={autoDraftWordCount}\n onChange={e => setAutoDraftWordCount(parseInt(e.target.value) || 800)}\n className=\"flex h-10 w-24 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n disabled={saving}\n />\n </div>\n </div>\n <CollapsibleTemplate\n label=\"Auto-Draft\"\n value={autoDraftTemplate}\n defaultValue={defaultAutoDraftTemplate}\n onChange={setAutoDraftTemplate}\n onReset={() => setAutoDraftTemplate(null)}\n placeholders=\"{{AUTO_DRAFT_RULES}}, {{AUTO_DRAFT_WORD_COUNT}}, {{RULES}}, {{STYLE_EXAMPLES}}, {{TOPIC_NAME}}, {{ARTICLE_TITLE}}, {{ARTICLE_SUMMARY}}, {{ARTICLE_URL}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Plan Format Rules</label>\n <p className=\"text-sm text-muted-foreground\">\n Rules for essay plan structure and format. Controls how outlines are organized in Plan mode.\n </p>\n <div className=\"flex items-center justify-end\">\n {planRules && (\n <button\n type=\"button\"\n onClick={() => setPlanRules('')}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-7 px-2 hover:bg-accent hover:text-accent-foreground\"\n >\n <RotateCcw className=\"h-3 w-3 mr-1\" />\n Reset to default\n </button>\n )}\n </div>\n <textarea\n value={planRules || defaultPlanRules}\n onChange={e => setPlanRules(e.target.value)}\n className=\"flex min-h-[250px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 font-mono resize-none\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Plan Mode Template</label>\n <p className=\"text-sm text-muted-foreground\">\n Prompt template for Plan mode in chat. Controls the full system prompt.\n </p>\n <CollapsibleTemplate\n label=\"Plan\"\n value={planTemplate}\n defaultValue={defaultPlanTemplate}\n onChange={setPlanTemplate}\n onReset={() => setPlanTemplate(null)}\n placeholders=\"{{PLAN_RULES}}, {{STYLE_EXAMPLES}}\"\n disabled={saving}\n />\n </div>\n\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">Expand Plan Template</label>\n <p className=\"text-sm text-muted-foreground\">\n Prompt template for expanding a plan outline into a full essay draft.\n </p>\n <CollapsibleTemplate\n label=\"Expand Plan\"\n value={expandPlanTemplate}\n defaultValue={defaultExpandPlanTemplate}\n onChange={setExpandPlanTemplate}\n onReset={() => setExpandPlanTemplate(null)}\n placeholders=\"{{RULES}}, {{STYLE_EXAMPLES}}, {{PLAN}}\"\n disabled={saving}\n />\n </div>\n\n {/* Auto-Draft Feature Toggle */}\n <div className=\"flex items-center justify-between py-4 border-t border-border\">\n <div className=\"space-y-0.5\">\n <label className=\"text-sm font-medium leading-none\">Auto-Draft Feature</label>\n <p className=\"text-sm text-muted-foreground\">\n Enable RSS topic subscriptions and automatic draft generation.\n </p>\n </div>\n <button\n type=\"button\"\n role=\"switch\"\n aria-checked={autoDraftEnabled}\n onClick={() => setAutoDraftEnabled(!autoDraftEnabled)}\n className={`relative inline-flex h-6 w-11 shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background ${autoDraftEnabled ? 'bg-primary' : 'bg-input'}`}\n >\n <span\n className={`pointer-events-none block h-5 w-5 rounded-full bg-background shadow-lg ring-0 transition-transform ${autoDraftEnabled ? 'translate-x-5' : 'translate-x-0'}`}\n />\n </button>\n </div>\n\n <div className=\"flex items-center gap-3\">\n <button\n onClick={handleSave}\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50\"\n >\n {saving && <Loader2 className=\"mr-2 h-4 w-4 animate-spin\" />}\n {saving ? 'Saving...' : 'Save'}\n </button>\n {saved && (\n <span className=\"text-sm text-green-600 dark:text-green-400\">\n Saved!\n </span>\n )}\n </div>\n </div>\n </div>\n </div>\n )\n}\n\nfunction TagsSettingsContent() {\n const { apiBasePath } = useDashboardContext()\n const [tags, setTags] = useState<Tag[]>([])\n const [loading, setLoading] = useState(true)\n const [dialogOpen, setDialogOpen] = useState(false)\n const [editingTag, setEditingTag] = useState<Tag | null>(null)\n const [tagName, setTagName] = useState('')\n const [saving, setSaving] = useState(false)\n const [error, setError] = useState('')\n const [menuOpen, setMenuOpen] = useState<string | null>(null)\n\n useEffect(() => {\n fetchTags()\n }, [apiBasePath])\n\n async function fetchTags() {\n const res = await fetch(`${apiBasePath}/tags`)\n if (res.ok) {\n const json = await res.json()\n setTags(json.data || [])\n }\n setLoading(false)\n }\n\n function openCreateDialog() {\n setEditingTag(null)\n setTagName('')\n setError('')\n setDialogOpen(true)\n }\n\n function openEditDialog(tag: Tag) {\n setEditingTag(tag)\n setTagName(tag.name)\n setError('')\n setDialogOpen(true)\n setMenuOpen(null)\n }\n\n async function handleDelete(tag: Tag) {\n if (!confirm(`Delete tag \"${tag.name}\"? This will remove it from all posts.`)) return\n const res = await fetch(`${apiBasePath}/tags/${tag.id}`, { method: 'DELETE' })\n if (res.ok) {\n fetchTags()\n }\n setMenuOpen(null)\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n if (!tagName.trim()) {\n setError('Tag name is required')\n return\n }\n\n setSaving(true)\n setError('')\n\n const url = editingTag\n ? `${apiBasePath}/tags/${editingTag.id}`\n : `${apiBasePath}/tags`\n const method = editingTag ? 'PATCH' : 'POST'\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ name: tagName.trim() }),\n })\n\n if (res.ok) {\n setDialogOpen(false)\n fetchTags()\n } else {\n const data = await res.json().catch(() => ({}))\n setError(data.error || 'Failed to save tag')\n }\n\n setSaving(false)\n }\n\n if (loading) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header */}\n <div className=\"flex items-center justify-between mb-6 md:mb-8\">\n <h2 className=\"text-lg font-semibold\">Tags</h2>\n <button\n onClick={openCreateDialog}\n className=\"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n Add Tag\n </button>\n </div>\n\n {tags.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No tags yet. Create one to get started.</div>\n ) : (\n <>\n {/* Desktop Table */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Name</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Posts</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Created</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {tags.map(tag => (\n <tr key={tag.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[250px]\">{tag.name}</span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">{tag._count?.posts ?? 0}</td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {tag.createdAt ? new Date(tag.createdAt).toLocaleDateString() : '—'}\n </td>\n <td className=\"p-4 align-middle text-right\">\n <div className=\"relative inline-block\">\n <button\n onClick={() => setMenuOpen(menuOpen === tag.id ? null : tag.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === tag.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => openEditDialog(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {tags.map(tag => (\n <div key={tag.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <span className=\"font-medium truncate block\">{tag.name}</span>\n <p className=\"text-sm text-muted-foreground truncate\">\n {tag._count?.posts ?? 0} posts · Created {tag.createdAt ? new Date(tag.createdAt).toLocaleDateString() : '—'}\n </p>\n </div>\n <div className=\"relative\">\n <button\n onClick={() => setMenuOpen(menuOpen === tag.id ? null : tag.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === tag.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => openEditDialog(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(tag)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </>\n )}\n\n {/* Dialog for Create/Edit */}\n {dialogOpen && (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <div \n className=\"fixed inset-0 bg-black/80\" \n onClick={() => setDialogOpen(false)}\n />\n {/* Dialog */}\n <div className=\"relative z-50 w-full max-w-sm bg-background border border-border rounded-lg shadow-lg\">\n <div className=\"flex flex-col space-y-1.5 p-6\">\n <h3 className=\"text-lg font-semibold leading-none tracking-tight\">\n {editingTag ? 'Edit Tag' : 'Create Tag'}\n </h3>\n </div>\n <form onSubmit={handleSubmit}>\n <div className=\"px-6 pb-4\">\n <div className=\"space-y-2\">\n <label htmlFor=\"tagName\" className=\"text-sm font-medium leading-none\">Name</label>\n <input\n id=\"tagName\"\n type=\"text\"\n value={tagName}\n onChange={(e) => setTagName(e.target.value)}\n placeholder=\"e.g. technology\"\n autoFocus\n className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2\"\n />\n {error && (\n <p className=\"text-sm text-destructive\">{error}</p>\n )}\n </div>\n </div>\n <div className=\"flex items-center justify-end gap-2 p-6 pt-0\">\n <button\n type=\"button\"\n onClick={() => setDialogOpen(false)}\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground disabled:opacity-50\"\n >\n Cancel\n </button>\n <button\n type=\"submit\"\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {saving ? 'Saving...' : editingTag ? 'Save' : 'Create'}\n </button>\n </div>\n </form>\n </div>\n </div>\n )}\n </div>\n )\n}\n\nfunction TopicsSettingsContent() {\n const { apiBasePath } = useDashboardContext()\n const [topics, setTopics] = useState<Topic[]>([])\n const [loading, setLoading] = useState(true)\n const [showForm, setShowForm] = useState(false)\n const [editingTopic, setEditingTopic] = useState<Topic | null>(null)\n const [generating, setGenerating] = useState<string | null>(null)\n\n // Form state\n const [formName, setFormName] = useState('')\n const [formKeywords, setFormKeywords] = useState('')\n const [formFeeds, setFormFeeds] = useState('')\n const [formFrequency, setFormFrequency] = useState('daily')\n const [formMaxPerPeriod, setFormMaxPerPeriod] = useState(3)\n const [formEssayFocus, setFormEssayFocus] = useState('')\n const [formIsActive, setFormIsActive] = useState(true)\n\n useEffect(() => {\n fetchTopics()\n }, [apiBasePath])\n\n async function fetchTopics() {\n const res = await fetch(`${apiBasePath}/topics`)\n if (res.ok) {\n const json = await res.json()\n setTopics(json.data || [])\n }\n setLoading(false)\n }\n\n function resetForm() {\n setFormName('')\n setFormKeywords('')\n setFormFeeds('')\n setFormFrequency('daily')\n setFormMaxPerPeriod(3)\n setFormEssayFocus('')\n setFormIsActive(true)\n }\n\n function openEditForm(topic: Topic) {\n setEditingTopic(topic)\n setFormName(topic.name)\n setFormKeywords(JSON.parse(topic.keywords).join(', '))\n setFormFeeds(JSON.parse(topic.rssFeeds).join('\\n'))\n setFormFrequency(topic.frequency)\n setFormMaxPerPeriod(topic.maxPerPeriod)\n setFormEssayFocus(topic.essayFocus || '')\n setFormIsActive(topic.isActive)\n setShowForm(true)\n }\n\n function openNewForm() {\n setEditingTopic(null)\n resetForm()\n setShowForm(true)\n }\n\n async function handleSubmit(e: React.FormEvent) {\n e.preventDefault()\n const data = {\n name: formName,\n keywords: formKeywords.split(',').map(k => k.trim()).filter(Boolean),\n rssFeeds: formFeeds.split('\\n').map(f => f.trim()).filter(Boolean),\n frequency: formFrequency,\n maxPerPeriod: formMaxPerPeriod,\n essayFocus: formEssayFocus || null,\n isActive: formIsActive,\n }\n\n const url = editingTopic ? `${apiBasePath}/topics/${editingTopic.id}` : `${apiBasePath}/topics`\n const method = editingTopic ? 'PATCH' : 'POST'\n\n const res = await fetch(url, {\n method,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(data),\n })\n\n if (res.ok) {\n setShowForm(false)\n resetForm()\n setEditingTopic(null)\n fetchTopics()\n }\n }\n\n async function handleDelete(id: string) {\n if (!confirm('Delete this topic? This will also delete associated news items.')) return\n const res = await fetch(`${apiBasePath}/topics/${id}`, { method: 'DELETE' })\n if (res.ok) setTopics(topics.filter(t => t.id !== id))\n }\n\n async function handleGenerate(topicId: string) {\n setGenerating(topicId)\n try {\n const res = await fetch(`${apiBasePath}/topics/${topicId}/generate`, { method: 'POST' })\n if (res.ok) {\n fetchTopics()\n }\n } finally {\n setGenerating(null)\n }\n }\n\n if (loading) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div className=\"space-y-4\">\n <div className=\"flex items-center justify-between\">\n <h2 className=\"text-lg font-semibold\">Topics</h2>\n <button onClick={openNewForm} className=\"flex items-center gap-1 px-3 py-1.5 bg-primary text-primary-foreground rounded-md text-sm\">\n <Plus className=\"h-4 w-4\" /> New Topic\n </button>\n </div>\n <p className=\"text-muted-foreground text-sm -mt-2\">RSS topic subscriptions for auto-generating draft posts.</p>\n\n {showForm && (\n <div className=\"border border-border rounded-lg p-4 space-y-4 bg-muted/30\">\n <div className=\"flex items-center justify-between\">\n <h3 className=\"font-medium\">{editingTopic ? 'Edit Topic' : 'New Topic'}</h3>\n <button onClick={() => { setShowForm(false); resetForm(); setEditingTopic(null) }} className=\"text-muted-foreground hover:text-foreground\">\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <form onSubmit={handleSubmit} className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">Name</label>\n <input\n type=\"text\"\n value={formName}\n onChange={e => setFormName(e.target.value)}\n required\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n placeholder=\"e.g. School Lunch Policy\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">Keywords (comma-separated)</label>\n <input\n type=\"text\"\n value={formKeywords}\n onChange={e => setFormKeywords(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n placeholder=\"school lunch, USDA, nutrition\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">RSS Feed URLs (one per line)</label>\n <textarea\n value={formFeeds}\n onChange={e => setFormFeeds(e.target.value)}\n rows={3}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent resize-none font-mono text-sm\"\n placeholder=\"https://example.com/feed.xml\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium mb-1\">Frequency</label>\n <select\n value={formFrequency}\n onChange={e => setFormFrequency(e.target.value)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n >\n <option value=\"daily\">Daily</option>\n <option value=\"weekly\">Weekly</option>\n <option value=\"hourly\">Hourly</option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">Max per period</label>\n <input\n type=\"number\"\n min={1}\n max={20}\n value={formMaxPerPeriod}\n onChange={e => setFormMaxPerPeriod(parseInt(e.target.value) || 3)}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent\"\n />\n </div>\n </div>\n <div>\n <label className=\"block text-sm font-medium mb-1\">Essay Focus (optional)</label>\n <textarea\n value={formEssayFocus}\n onChange={e => setFormEssayFocus(e.target.value)}\n rows={2}\n className=\"w-full px-3 py-2 border border-input rounded-md bg-transparent resize-none\"\n placeholder=\"Specific angle or perspective for essays on this topic\"\n />\n </div>\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n id=\"isActive\"\n checked={formIsActive}\n onChange={e => setFormIsActive(e.target.checked)}\n />\n <label htmlFor=\"isActive\" className=\"text-sm\">Active</label>\n </div>\n <div className=\"flex gap-2\">\n <button type=\"submit\" className=\"px-4 py-2 bg-primary text-primary-foreground rounded-md\">\n {editingTopic ? 'Update' : 'Create'}\n </button>\n <button type=\"button\" onClick={() => { setShowForm(false); resetForm(); setEditingTopic(null) }} className=\"px-4 py-2 border border-input rounded-md\">\n Cancel\n </button>\n </div>\n </form>\n </div>\n )}\n \n <div className=\"border border-border rounded-lg divide-y divide-border\">\n {topics.map(topic => {\n const keywords = JSON.parse(topic.keywords) as string[]\n const feeds = JSON.parse(topic.rssFeeds) as string[]\n return (\n <div key={topic.id} className=\"p-4\">\n <div className=\"flex items-start justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <p className=\"font-medium\">{topic.name}</p>\n {!topic.isActive && <span className=\"text-xs bg-muted px-1.5 py-0.5 rounded\">Paused</span>}\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {keywords.slice(0, 3).join(', ')}{keywords.length > 3 && ` +${keywords.length - 3}`}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {feeds.length} feed{feeds.length !== 1 ? 's' : ''} · {topic.frequency} · {topic._count?.posts ?? 0} posts\n {topic.lastRunAt && ` · Last run: ${new Date(topic.lastRunAt).toLocaleDateString()}`}\n </p>\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n onClick={() => handleGenerate(topic.id)}\n disabled={generating !== null}\n className=\"p-1.5 text-muted-foreground hover:text-foreground disabled:opacity-50\"\n title=\"Generate now\"\n >\n <Play className={`h-4 w-4 ${generating === topic.id ? 'animate-pulse' : ''}`} />\n </button>\n <button onClick={() => openEditForm(topic)} className=\"p-1.5 text-muted-foreground hover:text-foreground\">\n <Pencil className=\"h-4 w-4\" />\n </button>\n <button onClick={() => handleDelete(topic.id)} className=\"p-1.5 text-red-500 hover:text-red-600\">\n <Trash2 className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n </div>\n )\n })}\n {topics.length === 0 && (\n <p className=\"p-4 text-muted-foreground text-center\">No topics configured</p>\n )}\n </div>\n </div>\n )\n}\n\ninterface PostWithRevisions {\n id: string\n title: string\n slug: string\n status: string\n updatedAt: string\n _count?: { revisions: number }\n}\n\nconst POSTS_PER_PAGE = 25\n\nfunction PostsSettingsContent() {\n const { apiBasePath, navigate } = useDashboardContext()\n const [posts, setPosts] = useState<PostWithRevisions[]>([])\n const [totalCount, setTotalCount] = useState(0)\n const [loading, setLoading] = useState(true)\n const [currentPage, setCurrentPage] = useState(1)\n const [totalPages, setTotalPages] = useState(1)\n const [menuOpen, setMenuOpen] = useState<string | null>(null)\n\n useEffect(() => {\n setLoading(true)\n fetch(`${apiBasePath}/posts?all=1&page=${currentPage}&limit=${POSTS_PER_PAGE}&includeRevisionCount=1`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setPosts(res.data || [])\n setTotalCount(res.total ?? res.data?.length ?? 0)\n setTotalPages(Math.ceil((res.total ?? res.data?.length ?? 0) / POSTS_PER_PAGE))\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, currentPage])\n\n async function handleDelete(post: PostWithRevisions) {\n if (!confirm(`Delete \"${post.title || 'Untitled'}\"? This will also delete all revisions.`)) return\n const res = await fetch(`${apiBasePath}/posts/${post.id}`, { method: 'DELETE' })\n if (res.ok) {\n setPosts(posts.filter(p => p.id !== post.id))\n setTotalCount(c => c - 1)\n }\n setMenuOpen(null)\n }\n\n function getStatusBadgeClasses(status: string) {\n if (status === 'published') return 'bg-primary text-primary-foreground'\n if (status === 'deleted') return 'bg-destructive text-destructive-foreground'\n return 'bg-secondary text-secondary-foreground'\n }\n\n // Pagination component matching blog's shadcn style\n const PaginationControls = ({ position }: { position: 'top' | 'bottom' }) => {\n if (totalPages <= 1) return null\n \n const getPageNumbers = () => {\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = []\n if (totalPages <= 7) {\n for (let i = 1; i <= totalPages; i++) pages.push(i)\n } else {\n pages.push(1)\n if (currentPage > 3) pages.push('ellipsis-start')\n const start = Math.max(2, currentPage - 1)\n const end = Math.min(totalPages - 1, currentPage + 1)\n for (let i = start; i <= end; i++) {\n if (!pages.includes(i)) pages.push(i)\n }\n if (currentPage < totalPages - 2) pages.push('ellipsis-end')\n if (!pages.includes(totalPages)) pages.push(totalPages)\n }\n return pages\n }\n\n const spacingClass = position === 'bottom' ? 'mt-4' : ''\n \n return (\n <nav role=\"navigation\" aria-label=\"pagination\" className={`mx-auto flex w-full justify-end ${spacingClass}`}>\n <ul className=\"flex flex-row items-center gap-1\">\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.max(1, p - 1))}\n disabled={currentPage <= 1}\n aria-label=\"Go to previous page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:block\">Previous</span>\n </button>\n </li>\n {getPageNumbers().map((page) => \n typeof page === 'string' ? (\n <li key={page}>\n <span aria-hidden className=\"flex h-9 w-9 items-center justify-center\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </span>\n </li>\n ) : (\n <li key={page}>\n <button\n onClick={() => setCurrentPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n className={`inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-9 w-9 ${\n page === currentPage \n ? 'border border-input bg-background hover:bg-accent hover:text-accent-foreground' \n : 'hover:bg-accent hover:text-accent-foreground'\n }`}\n >\n {page}\n </button>\n </li>\n )\n )}\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}\n disabled={currentPage >= totalPages}\n aria-label=\"Go to next page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <span className=\"hidden sm:block\">Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </li>\n </ul>\n </nav>\n )\n }\n\n if (loading && posts.length === 0) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header - matches AdminPageHeader pattern */}\n <div className=\"flex items-end justify-between gap-4 mb-6 md:mb-8\">\n <div className=\"shrink-0\">\n <h1 className=\"text-lg font-bold\">Posts</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{totalCount} total post{totalCount !== 1 ? 's' : ''}</p>\n </div>\n <div className=\"flex items-center gap-4\">\n <PaginationControls position=\"top\" />\n <button\n onClick={() => navigate('/editor')}\n className=\"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-10 px-4 py-2 bg-primary text-primary-foreground hover:bg-primary/90\"\n >\n New Post\n </button>\n </div>\n </div>\n\n {posts.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No posts yet.</div>\n ) : (\n <>\n {/* Desktop Table - matches AdminTable */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Title</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Slug</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Status</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Revisions</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Updated</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {posts.map(post => (\n <tr key={post.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[200px]\">{post.title || 'Untitled'}</span>\n </td>\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[250px] text-muted-foreground font-mono\">{post.slug}</span>\n </td>\n <td className=\"p-4 align-middle\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getStatusBadgeClasses(post.status)}`}>\n {post.status}\n </span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">{post._count?.revisions ?? 0}</td>\n <td className=\"p-4 align-middle text-muted-foreground\">{new Date(post.updatedAt).toLocaleDateString()}</td>\n <td className=\"p-4 align-middle text-right\">\n <div className=\"relative inline-block\">\n <button\n onClick={() => setMenuOpen(menuOpen === post.id ? null : post.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === post.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => { navigate(`/editor/${post.slug}`); setMenuOpen(null) }}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n {post.status === 'published' && (\n <a\n href={`/e/${post.slug}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n onClick={() => setMenuOpen(null)}\n >\n View\n </a>\n )}\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(post)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List - matches AdminTable mobile view */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {posts.map(post => (\n <div key={post.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">{post.title || 'Untitled'}</span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold shrink-0 ${getStatusBadgeClasses(post.status)}`}>\n {post.status}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {post.slug} · {post._count?.revisions ?? 0} rev · {new Date(post.updatedAt).toLocaleDateString()}\n </p>\n </div>\n <div className=\"relative\">\n <button\n onClick={() => setMenuOpen(menuOpen === post.id ? null : post.id)}\n className=\"inline-flex items-center justify-center rounded-md h-8 w-8 hover:bg-accent text-muted-foreground\"\n >\n <MoreVertical className=\"h-4 w-4\" />\n </button>\n {menuOpen === post.id && (\n <div className=\"absolute right-0 top-full mt-1 bg-popover border border-border rounded-md shadow-md z-50 min-w-[8rem] py-1\">\n <button\n onClick={() => { navigate(`/editor/${post.slug}`); setMenuOpen(null) }}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n >\n Edit\n </button>\n {post.status === 'published' && (\n <a\n href={`/e/${post.slug}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left hover:bg-accent\"\n onClick={() => setMenuOpen(null)}\n >\n View\n </a>\n )}\n <div className=\"-mx-1 my-1 h-px bg-muted\" />\n <button\n onClick={() => handleDelete(post)}\n className=\"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none w-full text-left text-destructive hover:bg-accent\"\n >\n Delete\n </button>\n </div>\n )}\n </div>\n </div>\n ))}\n </div>\n </>\n )}\n\n {/* Bottom Pagination */}\n <PaginationControls position=\"bottom\" />\n </div>\n )\n}\n\nconst REVISIONS_PER_PAGE = 25\n\nfunction RevisionsSettingsContent() {\n const { apiBasePath, navigate } = useDashboardContext()\n const [revisions, setRevisions] = useState<Revision[]>([])\n const [totalCount, setTotalCount] = useState(0)\n const [loading, setLoading] = useState(true)\n const [currentPage, setCurrentPage] = useState(1)\n const [totalPages, setTotalPages] = useState(1)\n\n useEffect(() => {\n setLoading(true)\n fetch(`${apiBasePath}/revisions?page=${currentPage}&limit=${REVISIONS_PER_PAGE}`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setRevisions(res.data || [])\n setTotalCount(res.total ?? res.data?.length ?? 0)\n setTotalPages(res.totalPages || Math.ceil((res.total ?? res.data?.length ?? 0) / REVISIONS_PER_PAGE))\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, currentPage])\n\n function getStatusBadgeClasses(isCurrent: boolean) {\n if (isCurrent) return 'bg-primary text-primary-foreground'\n return 'bg-secondary text-secondary-foreground'\n }\n\n // Pagination component matching blog's shadcn style\n const PaginationControls = ({ position }: { position: 'top' | 'bottom' }) => {\n if (totalPages <= 1) return null\n \n const getPageNumbers = () => {\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = []\n if (totalPages <= 7) {\n for (let i = 1; i <= totalPages; i++) pages.push(i)\n } else {\n pages.push(1)\n if (currentPage > 3) pages.push('ellipsis-start')\n const start = Math.max(2, currentPage - 1)\n const end = Math.min(totalPages - 1, currentPage + 1)\n for (let i = start; i <= end; i++) {\n if (!pages.includes(i)) pages.push(i)\n }\n if (currentPage < totalPages - 2) pages.push('ellipsis-end')\n if (!pages.includes(totalPages)) pages.push(totalPages)\n }\n return pages\n }\n\n const spacingClass = position === 'bottom' ? 'mt-4' : ''\n \n return (\n <nav role=\"navigation\" aria-label=\"pagination\" className={`mx-auto flex w-full justify-end ${spacingClass}`}>\n <ul className=\"flex flex-row items-center gap-1\">\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.max(1, p - 1))}\n disabled={currentPage <= 1}\n aria-label=\"Go to previous page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:block\">Previous</span>\n </button>\n </li>\n {getPageNumbers().map((page) => \n typeof page === 'string' ? (\n <li key={page}>\n <span aria-hidden className=\"flex h-9 w-9 items-center justify-center\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </span>\n </li>\n ) : (\n <li key={page}>\n <button\n onClick={() => setCurrentPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n className={`inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-9 w-9 ${\n page === currentPage \n ? 'border border-input bg-background hover:bg-accent hover:text-accent-foreground' \n : 'hover:bg-accent hover:text-accent-foreground'\n }`}\n >\n {page}\n </button>\n </li>\n )\n )}\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}\n disabled={currentPage >= totalPages}\n aria-label=\"Go to next page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <span className=\"hidden sm:block\">Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </li>\n </ul>\n </nav>\n )\n }\n\n if (loading && revisions.length === 0) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header - matches AdminPageHeader pattern */}\n <div className=\"flex items-end justify-between gap-4 mb-6 md:mb-8\">\n <div className=\"shrink-0\">\n <h1 className=\"text-lg font-bold\">Revisions</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{totalCount} total revision{totalCount !== 1 ? 's' : ''}</p>\n </div>\n <PaginationControls position=\"top\" />\n </div>\n\n {revisions.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No revisions yet.</div>\n ) : (\n <>\n {/* Desktop Table - matches AdminTable */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Post</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Content Preview</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Created</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Status</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {revisions.map(revision => {\n const isCurrent = revision.post.markdown === revision.markdown\n return (\n <tr key={revision.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <button\n onClick={() => navigate(`/editor/${revision.post.slug}`)}\n className=\"block truncate max-w-[200px] hover:underline text-left\"\n >\n {revision.post.title || 'Untitled'}\n </button>\n </td>\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[300px] text-muted-foreground\">\n {revision.markdown.slice(0, 80)}{revision.markdown.length > 80 ? '...' : ''}\n </span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {new Date(revision.createdAt).toLocaleString()}\n </td>\n <td className=\"p-4 align-middle\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${getStatusBadgeClasses(isCurrent)}`}>\n {isCurrent ? 'current' : 'past'}\n </span>\n </td>\n <td className=\"p-4 align-middle text-right\">\n <button\n onClick={() => navigate(`/settings/revisions/${revision.id}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground\"\n >\n View\n </button>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List - matches AdminTable mobile view */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {revisions.map(revision => {\n const isCurrent = revision.post.markdown === revision.markdown\n return (\n <div key={revision.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">{revision.post.title || 'Untitled'}</span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold shrink-0 ${getStatusBadgeClasses(isCurrent)}`}>\n {isCurrent ? 'current' : 'past'}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {revision.markdown.slice(0, 40)}{revision.markdown.length > 40 ? '...' : ''} · {new Date(revision.createdAt).toLocaleDateString()}\n </p>\n </div>\n <button\n onClick={() => navigate(`/settings/revisions/${revision.id}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground shrink-0\"\n >\n View\n </button>\n </div>\n )\n })}\n </div>\n </>\n )}\n\n {/* Bottom Pagination */}\n <PaginationControls position=\"bottom\" />\n </div>\n )\n}\n\ninterface RevisionDetail {\n id: string\n postId: string\n title: string | null\n subtitle: string | null\n markdown: string\n createdAt: string\n post: { id: string; title: string; slug: string; markdown: string }\n}\n\nfunction RevisionDetailContent({ revisionId }: { revisionId: string }) {\n const { apiBasePath, navigate } = useDashboardContext()\n const [revision, setRevision] = useState<RevisionDetail | null>(null)\n const [loading, setLoading] = useState(true)\n const [restoring, setRestoring] = useState(false)\n\n useEffect(() => {\n fetch(`${apiBasePath}/revisions/${revisionId}`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setRevision(res.data)\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, revisionId])\n\n async function handleRestore() {\n if (!revision) return\n if (!confirm('Restore this revision? This will replace the current post content.')) return\n \n setRestoring(true)\n const res = await fetch(`${apiBasePath}/revisions/${revisionId}/restore`, { method: 'POST' })\n if (res.ok) {\n navigate(`/editor/${revision.post.slug}`)\n }\n setRestoring(false)\n }\n\n if (loading) {\n return (\n <div className=\"space-y-6\">\n <div className=\"h-6 w-32 bg-muted rounded animate-pulse\" />\n <div className=\"h-64 bg-muted rounded animate-pulse\" />\n </div>\n )\n }\n\n if (!revision) {\n return (\n <div className=\"space-y-6\">\n <button\n onClick={() => navigate('/settings/revisions')}\n className=\"text-sm text-muted-foreground hover:text-foreground flex items-center gap-1\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Back to Revisions\n </button>\n <p className=\"text-muted-foreground\">Revision not found.</p>\n </div>\n )\n }\n\n const isCurrent = revision.post.markdown === revision.markdown\n\n return (\n <div className=\"space-y-6\">\n {/* Header */}\n <div className=\"flex items-center justify-between\">\n <div className=\"space-y-1\">\n <button\n onClick={() => navigate('/settings/revisions')}\n className=\"text-sm text-muted-foreground hover:text-foreground flex items-center gap-1\"\n >\n <ChevronLeft className=\"h-4 w-4\" /> Back to Revisions\n </button>\n <h2 className=\"text-lg font-semibold\">Revision Detail</h2>\n </div>\n <div className=\"flex items-center gap-2\">\n {isCurrent ? (\n <span className=\"inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold bg-primary text-primary-foreground\">\n current\n </span>\n ) : (\n <button\n onClick={handleRestore}\n disabled={restoring}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-9 px-4 bg-primary text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n <RotateCcw className=\"h-4 w-4 mr-2\" />\n {restoring ? 'Restoring...' : 'Restore This Revision'}\n </button>\n )}\n </div>\n </div>\n\n {/* Metadata */}\n <div className=\"rounded-lg border bg-card p-4 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Post</span>\n <button\n onClick={() => navigate(`/editor/${revision.post.slug}`)}\n className=\"text-sm hover:underline\"\n >\n {revision.post.title || 'Untitled'}\n </button>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Title at revision</span>\n <span className=\"text-sm\">{revision.title || '—'}</span>\n </div>\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-muted-foreground\">Created</span>\n <span className=\"text-sm\">{new Date(revision.createdAt).toLocaleString()}</span>\n </div>\n </div>\n\n {/* Content Preview */}\n <div className=\"space-y-2\">\n <h3 className=\"text-sm font-medium\">Content</h3>\n <div className=\"rounded-lg border bg-muted/30 p-4 max-h-96 overflow-auto\">\n <pre className=\"text-sm whitespace-pre-wrap font-mono\">{revision.markdown}</pre>\n </div>\n </div>\n </div>\n )\n}\n\ninterface CommentWithDetails {\n id: string\n content: string\n createdAt: string\n resolved: boolean\n deletedAt: string | null\n parentId: string | null\n post: { id: string; title: string; slug: string }\n user: { name: string | null; email: string }\n}\n\nconst COMMENTS_PER_PAGE = 25\n\nfunction CommentsSettingsContent() {\n const { apiBasePath, navigate, basePath } = useDashboardContext()\n const [comments, setComments] = useState<CommentWithDetails[]>([])\n const [totalCount, setTotalCount] = useState(0)\n const [loading, setLoading] = useState(true)\n const [currentPage, setCurrentPage] = useState(1)\n const [totalPages, setTotalPages] = useState(1)\n\n useEffect(() => {\n setLoading(true)\n fetch(`${apiBasePath}/comments?page=${currentPage}&limit=${COMMENTS_PER_PAGE}`)\n .then(res => res.ok ? res.json() : Promise.reject())\n .then(res => {\n setComments(res.data || [])\n setTotalCount(res.total ?? res.data?.length ?? 0)\n setTotalPages(res.totalPages || Math.ceil((res.total ?? res.data?.length ?? 0) / COMMENTS_PER_PAGE))\n setLoading(false)\n })\n .catch(() => setLoading(false))\n }, [apiBasePath, currentPage])\n\n function getStatusBadge(comment: CommentWithDetails) {\n const isDeleted = comment.deletedAt !== null\n const isResolved = comment.resolved\n \n if (isDeleted) {\n return { label: 'deleted', classes: 'bg-destructive text-destructive-foreground' }\n }\n if (isResolved) {\n return { label: 'resolved', classes: 'bg-secondary text-secondary-foreground' }\n }\n return { label: 'active', classes: 'bg-primary text-primary-foreground' }\n }\n\n // Pagination component matching other settings pages\n const PaginationControls = ({ position }: { position: 'top' | 'bottom' }) => {\n if (totalPages <= 1) return null\n \n const getPageNumbers = () => {\n const pages: (number | 'ellipsis-start' | 'ellipsis-end')[] = []\n if (totalPages <= 7) {\n for (let i = 1; i <= totalPages; i++) pages.push(i)\n } else {\n pages.push(1)\n if (currentPage > 3) pages.push('ellipsis-start')\n const start = Math.max(2, currentPage - 1)\n const end = Math.min(totalPages - 1, currentPage + 1)\n for (let i = start; i <= end; i++) {\n if (!pages.includes(i)) pages.push(i)\n }\n if (currentPage < totalPages - 2) pages.push('ellipsis-end')\n if (!pages.includes(totalPages)) pages.push(totalPages)\n }\n return pages\n }\n\n const spacingClass = position === 'bottom' ? 'mt-4' : ''\n \n return (\n <nav role=\"navigation\" aria-label=\"pagination\" className={`mx-auto flex w-full justify-end ${spacingClass}`}>\n <ul className=\"flex flex-row items-center gap-1\">\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.max(1, p - 1))}\n disabled={currentPage <= 1}\n aria-label=\"Go to previous page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:block\">Previous</span>\n </button>\n </li>\n {getPageNumbers().map((page) => \n typeof page === 'string' ? (\n <li key={page}>\n <span aria-hidden className=\"flex h-9 w-9 items-center justify-center\">\n <MoreHorizontal className=\"h-4 w-4\" />\n </span>\n </li>\n ) : (\n <li key={page}>\n <button\n onClick={() => setCurrentPage(page)}\n aria-current={page === currentPage ? 'page' : undefined}\n className={`inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium h-9 w-9 ${\n page === currentPage \n ? 'border border-input bg-background hover:bg-accent hover:text-accent-foreground' \n : 'hover:bg-accent hover:text-accent-foreground'\n }`}\n >\n {page}\n </button>\n </li>\n )\n )}\n <li>\n <button\n onClick={() => setCurrentPage(p => Math.min(totalPages, p + 1))}\n disabled={currentPage >= totalPages}\n aria-label=\"Go to next page\"\n className=\"inline-flex items-center justify-center gap-1 whitespace-nowrap rounded-md text-sm font-medium h-9 px-2.5 hover:bg-accent hover:text-accent-foreground disabled:pointer-events-none disabled:opacity-50\"\n >\n <span className=\"hidden sm:block\">Next</span>\n <ChevronRight className=\"h-4 w-4\" />\n </button>\n </li>\n </ul>\n </nav>\n )\n }\n\n if (loading && comments.length === 0) return <div className=\"animate-pulse h-32 bg-muted rounded\" />\n\n return (\n <div>\n {/* Header - matches AdminPageHeader pattern */}\n <div className=\"flex items-end justify-between gap-4 mb-6 md:mb-8\">\n <div className=\"shrink-0\">\n <h1 className=\"text-lg font-bold\">Comments</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">{totalCount} total comment{totalCount !== 1 ? 's' : ''}</p>\n </div>\n <PaginationControls position=\"top\" />\n </div>\n\n {comments.length === 0 ? (\n <div className=\"py-8 text-center text-muted-foreground\">No comments yet.</div>\n ) : (\n <>\n {/* Desktop Table - matches AdminTable */}\n <div className=\"hidden md:block rounded-md border\">\n <table className=\"w-full caption-bottom text-sm\">\n <thead className=\"[&_tr]:border-b\">\n <tr className=\"border-b\">\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground max-w-[200px]\">Post</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Author</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground max-w-[300px]\">Comment</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Created</th>\n <th className=\"h-12 px-4 text-left align-middle font-medium text-muted-foreground\">Status</th>\n <th className=\"h-12 px-4 text-right align-middle font-medium text-muted-foreground\">Actions</th>\n </tr>\n </thead>\n <tbody className=\"[&_tr:last-child]:border-0\">\n {comments.map(comment => {\n const status = getStatusBadge(comment)\n const isReply = comment.parentId !== null\n const commentIdToOpen = comment.parentId || comment.id\n \n return (\n <tr key={comment.id} className=\"border-b\">\n <td className=\"p-4 align-middle\">\n <button\n onClick={() => navigate(`/editor/${comment.post.slug}`)}\n className=\"block truncate max-w-[200px] hover:underline text-left\"\n >\n {comment.post.title || 'Untitled'}\n </button>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {comment.user.name || comment.user.email}\n </td>\n <td className=\"p-4 align-middle\">\n <span className=\"block truncate max-w-[300px] text-muted-foreground\">\n {isReply && <span className=\"text-xs mr-1\">↳</span>}\n {comment.content.slice(0, 60)}{comment.content.length > 60 ? '...' : ''}\n </span>\n </td>\n <td className=\"p-4 align-middle text-muted-foreground\">\n {new Date(comment.createdAt).toLocaleString()}\n </td>\n <td className=\"p-4 align-middle\">\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold ${status.classes}`}>\n {status.label}\n </span>\n </td>\n <td className=\"p-4 align-middle text-right\">\n <button\n onClick={() => navigate(`/editor/${comment.post.slug}?comment=${commentIdToOpen}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground\"\n >\n View\n </button>\n </td>\n </tr>\n )\n })}\n </tbody>\n </table>\n </div>\n\n {/* Mobile List - matches AdminTable mobile view */}\n <div className=\"md:hidden divide-y rounded-md border bg-background\">\n {comments.map(comment => {\n const status = getStatusBadge(comment)\n const isReply = comment.parentId !== null\n const commentIdToOpen = comment.parentId || comment.id\n \n return (\n <div key={comment.id} className=\"flex items-center justify-between gap-4 px-4 py-5\">\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"flex items-center gap-2\">\n <span className=\"font-medium truncate\">\n {isReply && <span className=\"text-xs mr-1\">↳</span>}\n {comment.content.slice(0, 40)}{comment.content.length > 40 ? '...' : ''}\n </span>\n <span className={`inline-flex items-center rounded-md px-2.5 py-0.5 text-xs font-semibold shrink-0 ${status.classes}`}>\n {status.label}\n </span>\n </div>\n <p className=\"text-sm text-muted-foreground truncate\">\n {comment.user.name || comment.user.email} · {new Date(comment.createdAt).toLocaleDateString()}\n </p>\n </div>\n <button\n onClick={() => navigate(`/editor/${comment.post.slug}?comment=${commentIdToOpen}`)}\n className=\"inline-flex items-center justify-center rounded-md text-sm font-medium h-8 px-3 hover:bg-accent text-muted-foreground hover:text-foreground shrink-0\"\n >\n View\n </button>\n </div>\n )\n })}\n </div>\n </>\n )}\n\n {/* Bottom Pagination */}\n <PaginationControls position=\"bottom\" />\n </div>\n )\n}\n","'use client'\n\nimport { ChevronLeft, Moon, Sun } from 'lucide-react'\nimport { useDashboardContext } from '../context'\nimport { Dropdown, DropdownItem, DropdownDivider } from './Dropdown'\n\nexport interface NavbarProps {\n // Callbacks\n onSignOut?: () => void\n onThemeToggle?: () => void\n // Current theme for icon display\n theme?: 'light' | 'dark'\n // Extra slot for host-app specific buttons (e.g., chat toggle)\n rightSlot?: React.ReactNode\n}\n\nexport function Navbar({\n onSignOut,\n onThemeToggle,\n theme,\n rightSlot,\n}: NavbarProps) {\n const { session, currentPath, navigate, goBack, basePath } = useDashboardContext()\n\n // Detect current route state\n const isRoot = currentPath === '/' || currentPath === ''\n const isSettings = currentPath.startsWith('/settings')\n\n const handleBack = (e: React.MouseEvent<HTMLButtonElement>) => {\n // Blur to clear focus/active state (prevents sticky highlight on mobile)\n e.currentTarget.blur()\n goBack()\n }\n\n const avatarTrigger = (\n <button\n type=\"button\"\n className=\"relative w-9 h-9 rounded-full bg-secondary flex items-center justify-center text-sm font-medium text-secondary-foreground hover:ring-2 hover:ring-ring transition-shadow\"\n >\n {session?.user?.email?.charAt(0).toUpperCase() || '?'}\n <span className=\"absolute bottom-0 right-0 w-2.5 h-2.5 bg-green-500 rounded-full border-2 border-background\" />\n </button>\n )\n\n return (\n <header className=\"sticky top-0 z-50 border-b border-border bg-background\">\n <div className=\"max-w-5xl mx-auto px-6 py-4 flex items-center justify-between\">\n {/* Left side: Logo or back button */}\n {isRoot ? (\n <a href={basePath} className=\"font-medium flex items-center gap-1.5\">\n Writer\n <span className=\"text-xs px-1.5 py-0.5 bg-primary text-primary-foreground rounded\">AI</span>\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={handleBack}\n className=\"h-9 px-3 -ml-3 gap-1.5 inline-flex items-center justify-center text-sm font-medium rounded-md hover:bg-accent hover:text-accent-foreground touch-manipulation\"\n >\n <ChevronLeft className=\"h-4 w-4\" />\n <span className=\"hidden sm:inline\">Back</span>\n </button>\n )}\n\n {/* Right side: icons and user menu */}\n <div className=\"flex items-center gap-2\">\n {/* Extra slot from host app */}\n {rightSlot}\n\n {/* Theme toggle */}\n {onThemeToggle && (\n <button\n type=\"button\"\n onClick={onThemeToggle}\n className=\"w-9 h-9 rounded-md border border-border hover:bg-accent text-muted-foreground flex items-center justify-center\"\n aria-label=\"Toggle theme\"\n >\n {theme === 'dark' ? <Sun className=\"h-4 w-4\" /> : <Moon className=\"h-4 w-4\" />}\n </button>\n )}\n\n {/* User menu */}\n {session && (\n <Dropdown trigger={avatarTrigger} align=\"right\" className=\"min-w-[180px]\">\n {session.user?.role === 'admin' && (\n <>\n {/* Settings toggle */}\n {!isSettings ? (\n <DropdownItem onClick={() => navigate('/settings')}>\n Go to settings\n </DropdownItem>\n ) : (\n <DropdownItem onClick={() => navigate('/')}>\n Back to writer\n </DropdownItem>\n )}\n <DropdownDivider />\n </>\n )}\n\n {/* Back to site */}\n <DropdownItem onClick={() => { window.location.href = '/' }}>\n Back to site\n </DropdownItem>\n\n {/* Logout */}\n {onSignOut && (\n <DropdownItem onClick={onSignOut}>\n Logout\n </DropdownItem>\n )}\n </Dropdown>\n )}\n </div>\n </div>\n </header>\n )\n}\n","import { useEffect, useRef } from 'react'\n\ninterface KeyboardShortcut {\n key: string\n metaKey?: boolean\n ctrlKey?: boolean\n shiftKey?: boolean\n action: () => void\n description?: string\n}\n\nexport function useKeyboard(shortcuts: KeyboardShortcut[], enabled: boolean = true) {\n // Use ref to avoid effect re-running when shortcuts array reference changes\n const shortcutsRef = useRef(shortcuts)\n shortcutsRef.current = shortcuts\n\n useEffect(() => {\n if (!enabled) return\n\n function handleKeyDown(event: KeyboardEvent) {\n // Don't trigger shortcuts when typing in inputs\n const target = event.target as HTMLElement\n const isTyping = \n target.tagName === 'INPUT' ||\n target.tagName === 'TEXTAREA' ||\n target.isContentEditable\n \n if (isTyping) {\n // Allow Escape always, and Cmd+/ or Cmd+' for navigation\n const hasMeta = event.metaKey || event.ctrlKey\n const isEscape = event.key === 'Escape'\n const isNavShortcut = hasMeta && (event.key === '/' || event.key === \"'\")\n if (!isEscape && !isNavShortcut) {\n return\n }\n }\n\n for (const shortcut of shortcutsRef.current) {\n const metaMatch = shortcut.metaKey ? event.metaKey || event.ctrlKey : true\n const ctrlMatch = shortcut.ctrlKey ? event.ctrlKey : true\n const shiftMatch = shortcut.shiftKey ? event.shiftKey : !event.shiftKey\n const keyMatch = event.key.toLowerCase() === shortcut.key.toLowerCase()\n\n if (keyMatch && metaMatch && ctrlMatch && shiftMatch) {\n event.preventDefault()\n shortcut.action()\n break\n }\n }\n }\n\n window.addEventListener('keydown', handleKeyDown)\n return () => window.removeEventListener('keydown', handleKeyDown)\n }, [enabled])\n}\n\n// Dashboard-specific shortcuts\nexport function useDashboardKeyboard(options: {\n basePath: string\n onToggleView?: () => void\n onToggleSettings?: () => void\n onNewPost?: () => void\n onEscape?: () => void\n}) {\n const shortcuts: KeyboardShortcut[] = [\n {\n key: '/',\n metaKey: true,\n action: () => options.onToggleView?.(),\n description: 'Toggle view',\n },\n {\n key: \"'\",\n metaKey: true,\n action: () => options.onToggleSettings?.(),\n description: 'Toggle settings',\n },\n {\n key: 'n',\n action: () => options.onNewPost?.(),\n description: 'New post',\n },\n {\n key: 'Escape',\n action: () => options.onEscape?.(),\n description: 'Go back',\n },\n ]\n\n useKeyboard(shortcuts, true)\n}\n","'use client'\n\nimport { createContext, useContext, useState, useCallback, useRef, useEffect, useMemo, type ReactNode } from 'react'\n\nexport interface EssaySnapshot {\n title: string\n subtitle: string\n markdown: string\n}\n\nexport interface Message {\n role: 'user' | 'assistant'\n content: string\n mode?: ChatMode\n appliedEdits?: boolean\n previousState?: EssaySnapshot\n}\n\nexport interface EssayContext {\n title: string\n subtitle?: string\n markdown: string\n}\n\nexport type ChatMode = 'ask' | 'agent' | 'plan' | 'search'\n\nexport interface EssayEdit {\n type: 'replace_all' | 'replace_section' | 'insert' | 'delete'\n title?: string\n subtitle?: string\n markdown?: string\n find?: string\n replace?: string\n position?: 'before' | 'after' | 'start' | 'end'\n}\n\nexport type EditHandler = (edit: EssayEdit) => boolean\nexport type ExpandPlanHandler = (plan: string, wordCount: number) => void\n\ninterface ChatContextValue {\n messages: Message[]\n essayContext: EssayContext | null\n isStreaming: boolean\n isOpen: boolean\n mode: ChatMode\n webSearchEnabled: boolean\n thinkingEnabled: boolean\n selectedModel: string\n \n setEssayContext: (context: EssayContext | null) => void\n sendMessage: (content: string) => Promise<void>\n stopStreaming: () => void\n addMessage: (role: 'user' | 'assistant', content: string) => void\n clearMessages: () => void\n setIsOpen: (open: boolean) => void\n setMode: (mode: ChatMode) => void\n setWebSearchEnabled: (enabled: boolean) => void\n setThinkingEnabled: (enabled: boolean) => void\n setSelectedModel: (modelId: string) => void\n registerEditHandler: (handler: EditHandler | null) => void\n undoEdit: (messageIndex: number) => void\n registerExpandPlanHandler: (handler: ExpandPlanHandler | null) => void\n expandPlan: (wordCount?: number) => void\n}\n\nconst ChatContext = createContext<ChatContextValue | null>(null)\n\n// Parse edit blocks from agent mode responses\nfunction parseEditBlocks(content: string): { edits: EssayEdit[], cleanContent: string } {\n const editRegex = /:::edit\\s*([\\s\\S]*?)\\s*:::/g\n const edits: EssayEdit[] = []\n let cleanContent = content\n \n let match\n while ((match = editRegex.exec(content)) !== null) {\n try {\n const edit = JSON.parse(match[1]) as EssayEdit\n edits.push(edit)\n cleanContent = cleanContent.replace(match[0], '')\n } catch {\n console.warn('Failed to parse edit block:', match[1])\n }\n }\n \n cleanContent = cleanContent.replace(/\\n{3,}/g, '\\n\\n').trim()\n \n return { edits, cleanContent }\n}\n\n/**\n * Clean plan mode output by extracting content from <plan> tags and stripping trailing text.\n */\nfunction cleanPlanOutput(content: string): string {\n let cleaned = content\n \n // Extract content from <plan> tags if present\n const planMatch = cleaned.match(/<plan>([\\s\\S]*?)<\\/plan>/i)\n if (planMatch) {\n cleaned = planMatch[1]\n } else {\n const openTagMatch = cleaned.match(/<plan>([\\s\\S]*)/i)\n if (openTagMatch) {\n cleaned = openTagMatch[1]\n }\n }\n \n // Strip any text after the last bullet point\n const lines = cleaned.split('\\n')\n let lastBulletIndex = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n if (lines[i].trim().startsWith('- ')) {\n lastBulletIndex = i\n break\n }\n }\n \n if (lastBulletIndex === -1) {\n return cleaned.trim()\n }\n \n return lines.slice(0, lastBulletIndex + 1).join('\\n').trim()\n}\n\ninterface ChatProviderProps {\n children: ReactNode\n apiBasePath?: string\n chatApiPath?: string // Custom path for chat API (defaults to apiBasePath + '/ai/chat' or '/api/ai/chat')\n historyApiPath?: string // Custom path for history API (defaults to '/api/chat/history')\n}\n\nexport function ChatProvider({ \n children, \n apiBasePath = '/api/cms',\n chatApiPath,\n historyApiPath = '/api/chat/history',\n}: ChatProviderProps) {\n const [messages, setMessages] = useState<Message[]>([])\n const [essayContext, setEssayContext] = useState<EssayContext | null>(null)\n const [isStreaming, setIsStreaming] = useState(false)\n const [isOpen, setIsOpen] = useState(false)\n const [mode, setMode] = useState<ChatMode>('ask')\n const [webSearchEnabled, setWebSearchEnabled] = useState(false)\n const [thinkingEnabled, setThinkingEnabled] = useState(false)\n const [selectedModel, setSelectedModel] = useState('claude-sonnet')\n \n const editHandlerRef = useRef<EditHandler | null>(null)\n const expandPlanHandlerRef = useRef<ExpandPlanHandler | null>(null)\n const historyLoadedRef = useRef(false)\n const abortControllerRef = useRef<AbortController | null>(null)\n\n // Resolve chat API path\n const resolvedChatApiPath = chatApiPath || '/api/ai/chat'\n \n const registerEditHandler = useCallback((handler: EditHandler | null) => {\n editHandlerRef.current = handler\n }, [])\n\n const registerExpandPlanHandler = useCallback((handler: ExpandPlanHandler | null) => {\n expandPlanHandlerRef.current = handler\n }, [])\n\n const stopStreaming = useCallback(() => {\n if (abortControllerRef.current) {\n abortControllerRef.current.abort()\n abortControllerRef.current = null\n }\n setIsStreaming(false)\n }, [])\n\n // Preload history on mount\n useEffect(() => {\n if (historyLoadedRef.current) return\n historyLoadedRef.current = true\n \n fetch(historyApiPath)\n .then(res => res.ok ? res.json() : [])\n .then((data: { role: 'user' | 'assistant'; content: string }[]) => {\n if (data.length > 0) {\n setMessages(data.map(m => ({ role: m.role, content: m.content })))\n }\n })\n .catch(() => {})\n }, [historyApiPath])\n\n const saveMessage = useCallback((role: 'user' | 'assistant', content: string) => {\n fetch(historyApiPath, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ role, content }),\n }).catch(() => {})\n }, [historyApiPath])\n\n const sendMessage = useCallback(async (content: string) => {\n if (!content.trim() || isStreaming) return\n\n abortControllerRef.current = new AbortController()\n const signal = abortControllerRef.current.signal\n\n const userMessage: Message = { role: 'user', content: content.trim() }\n const newMessages = [...messages, userMessage]\n setMessages(newMessages)\n setIsStreaming(true)\n\n saveMessage('user', content.trim())\n\n const assistantMessage: Message = { role: 'assistant', content: '', mode }\n setMessages([...newMessages, assistantMessage])\n\n try {\n const response = await fetch(resolvedChatApiPath, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ \n messages: newMessages,\n essayContext: essayContext,\n mode: mode,\n modelId: selectedModel,\n useWebSearch: webSearchEnabled,\n useThinking: thinkingEnabled,\n }),\n signal,\n })\n\n if (!response.ok) {\n if (response.status === 503) {\n throw new Error('AI service temporarily unavailable. Please try again.')\n }\n if (response.status === 429) {\n throw new Error('Rate limit exceeded. Please wait before trying again.')\n }\n const errorText = await response.text()\n try {\n const error = JSON.parse(errorText)\n throw new Error(error.error || 'Failed to send message')\n } catch {\n throw new Error(`Server error (${response.status}). Please try again.`)\n }\n }\n\n const reader = response.body?.getReader()\n if (!reader) throw new Error('No response body')\n\n const decoder = new TextDecoder()\n let assistantContent = ''\n let appliedEdits = false\n\n while (true) {\n const { done, value } = await reader.read()\n if (done) break\n\n const chunk = decoder.decode(value, { stream: true })\n assistantContent += chunk\n\n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { role: 'assistant', content: assistantContent, mode }\n return updated\n })\n }\n \n // Process agent mode edits\n if (mode === 'agent' && editHandlerRef.current && essayContext) {\n const { edits, cleanContent } = parseEditBlocks(assistantContent)\n \n const previousState: EssaySnapshot = {\n title: essayContext.title,\n subtitle: essayContext.subtitle || '',\n markdown: essayContext.markdown,\n }\n \n for (const edit of edits) {\n const success = editHandlerRef.current(edit)\n if (success) appliedEdits = true\n }\n \n if (edits.length > 0) {\n const finalContent = cleanContent || 'Edit applied.'\n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { \n role: 'assistant', \n content: finalContent,\n mode,\n appliedEdits,\n previousState: appliedEdits ? previousState : undefined,\n }\n return updated\n })\n saveMessage('assistant', finalContent)\n } else {\n saveMessage('assistant', assistantContent)\n }\n } else if (mode === 'plan') {\n const cleanedContent = cleanPlanOutput(assistantContent)\n \n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { \n role: 'assistant', \n content: cleanedContent,\n mode,\n }\n return updated\n })\n \n saveMessage('assistant', cleanedContent)\n } else {\n saveMessage('assistant', assistantContent)\n }\n } catch (error) {\n if (error instanceof Error && error.name === 'AbortError') {\n return\n }\n console.error('Chat error:', error)\n const errorMessage = error instanceof Error ? error.message : 'Something went wrong'\n setMessages(prev => {\n const updated = [...prev]\n updated[updated.length - 1] = { role: 'assistant', content: `Error: ${errorMessage}` }\n return updated\n })\n } finally {\n abortControllerRef.current = null\n setIsStreaming(false)\n }\n }, [messages, isStreaming, essayContext, mode, webSearchEnabled, thinkingEnabled, selectedModel, saveMessage, resolvedChatApiPath])\n\n const clearMessages = useCallback(() => {\n setMessages([])\n }, [])\n\n const addMessage = useCallback((role: 'user' | 'assistant', content: string) => {\n const message: Message = { role, content }\n setMessages(prev => [...prev, message])\n saveMessage(role, content)\n }, [saveMessage])\n\n const undoEdit = useCallback((messageIndex: number) => {\n const message = messages[messageIndex]\n if (!message?.previousState || !editHandlerRef.current) return\n \n const success = editHandlerRef.current({\n type: 'replace_all',\n title: message.previousState.title,\n subtitle: message.previousState.subtitle,\n markdown: message.previousState.markdown,\n })\n \n if (success) {\n setMessages(prev => {\n const updated = [...prev]\n updated[messageIndex] = {\n ...updated[messageIndex],\n appliedEdits: false,\n previousState: undefined,\n }\n return updated\n })\n }\n }, [messages])\n\n const expandPlan = useCallback((wordCount: number = 800) => {\n if (!expandPlanHandlerRef.current) return\n \n const lastAssistantMessage = [...messages].reverse().find(m => m.role === 'assistant')\n if (!lastAssistantMessage?.content) return\n \n expandPlanHandlerRef.current(lastAssistantMessage.content, wordCount)\n \n setIsOpen(false)\n }, [messages])\n\n const value = useMemo<ChatContextValue>(() => ({\n messages,\n essayContext,\n isStreaming,\n isOpen,\n mode,\n webSearchEnabled,\n thinkingEnabled,\n selectedModel,\n setEssayContext,\n sendMessage,\n stopStreaming,\n addMessage,\n clearMessages,\n setIsOpen,\n setMode,\n setWebSearchEnabled,\n setThinkingEnabled,\n setSelectedModel,\n registerEditHandler,\n undoEdit,\n registerExpandPlanHandler,\n expandPlan,\n }), [\n messages,\n essayContext,\n isStreaming,\n isOpen,\n mode,\n webSearchEnabled,\n thinkingEnabled,\n selectedModel,\n sendMessage,\n stopStreaming,\n addMessage,\n clearMessages,\n registerEditHandler,\n undoEdit,\n registerExpandPlanHandler,\n expandPlan,\n ])\n\n return (\n <ChatContext.Provider value={value}>\n {children}\n </ChatContext.Provider>\n )\n}\n\nexport function useChatContext() {\n const context = useContext(ChatContext)\n if (!context) {\n throw new Error('useChatContext must be used within a ChatProvider')\n }\n return context\n}\n\nexport { ChatContext }\n","'use client'\n\nimport { useState, useRef, useEffect, useCallback } from 'react'\nimport { createPortal } from 'react-dom'\nimport { Loader2, X, Copy, Check, ArrowUp, Pencil, Undo2, ChevronDown, MessageSquare, Globe, Brain, Square, List } from 'lucide-react'\nimport { useChatContext, type ChatMode } from '../hooks/useChat'\nimport { useAIModels } from '../hooks/useAIModels'\nimport { ControlButton } from './ControlButton'\nimport { ModelSelector } from './ModelSelector'\nimport { markdownToHtml } from '../../lib/markdown'\n\n/** Default prose classes for chat messages */\nconst DEFAULT_PROSE_CLASSES = 'prose prose-gray dark:prose-invert max-w-none prose-p:leading-relaxed prose-a:underline'\n\n/** Strip <plan> tags for display during streaming */\nfunction stripPlanTags(content: string): string {\n return content\n .replace(/<plan>/gi, '')\n .replace(/<\\/plan>/gi, '')\n}\n\ninterface ChatPanelProps {\n /** Optional prose classes for message rendering */\n proseClasses?: string\n /** Optional callback when navigating (e.g., for expandPlan navigation) */\n onNavigate?: (path: string) => void\n /** Whether currently on an editor page (controls Draft Essay behavior) */\n isOnEditor?: boolean\n /** API path for fetching models (defaults to /api/cms/ai/settings) */\n modelsApiPath?: string\n}\n\nexport function ChatPanel({ \n proseClasses = DEFAULT_PROSE_CLASSES,\n onNavigate,\n isOnEditor: isOnEditorProp,\n}: ChatPanelProps) {\n const { \n messages, \n isStreaming, \n isOpen: open, \n setIsOpen,\n sendMessage: contextSendMessage,\n stopStreaming,\n essayContext,\n mode,\n setMode,\n undoEdit,\n webSearchEnabled,\n setWebSearchEnabled,\n thinkingEnabled,\n setThinkingEnabled,\n selectedModel,\n setSelectedModel,\n expandPlan,\n } = useChatContext()\n \n // Use essayContext presence to detect if we're editing (works with autoblogger)\n const isOnEditor = isOnEditorProp ?? !!essayContext\n \n const [input, setInput] = useState('')\n const [isAnimating, setIsAnimating] = useState(false)\n const [isVisible, setIsVisible] = useState(false)\n const [mounted, setMounted] = useState(false)\n const [copiedIndex, setCopiedIndex] = useState<number | null>(null)\n const [modeMenuOpen, setModeMenuOpen] = useState(false)\n const modeMenuRef = useRef<HTMLDivElement>(null)\n const messagesEndRef = useRef<HTMLDivElement>(null)\n const messagesContainerRef = useRef<HTMLDivElement>(null)\n const textareaRef = useRef<HTMLTextAreaElement>(null)\n const prevMessageCountRef = useRef(0)\n const savedScrollPositionRef = useRef<number | null>(null)\n const lastUserMessageRef = useRef<HTMLDivElement>(null)\n \n // Fetch models, use context for selection state\n const { models, currentModel } = useAIModels({\n externalSelectedModel: selectedModel,\n externalSetSelectedModel: setSelectedModel,\n })\n \n const onClose = useCallback(() => setIsOpen(false), [setIsOpen])\n \n const copyToClipboard = useCallback(async (text: string, index: number) => {\n await navigator.clipboard.writeText(text)\n setCopiedIndex(index)\n setTimeout(() => setCopiedIndex(null), 2000)\n }, [])\n \n // Handle Draft Essay button - either expand in current editor or navigate to new editor\n const handleDraftEssay = useCallback(() => {\n // Find the last assistant message (the plan)\n const lastAssistantMessage = [...messages].reverse().find(m => m.role === 'assistant')\n if (!lastAssistantMessage?.content) return\n \n if (isOnEditor) {\n // Already on editor page - use the registered handler and switch to agent mode\n expandPlan()\n setMode('agent')\n } else if (onNavigate) {\n // Not on editor - store plan in sessionStorage and navigate\n sessionStorage.setItem('pendingPlan', lastAssistantMessage.content)\n setIsOpen(false)\n onNavigate('/editor?fromPlan=1')\n }\n }, [messages, isOnEditor, expandPlan, setIsOpen, setMode, onNavigate])\n \n // Close mode menu on outside click\n useEffect(() => {\n function handleClick(e: MouseEvent) {\n if (modeMenuRef.current && !modeMenuRef.current.contains(e.target as Node)) {\n setModeMenuOpen(false)\n }\n }\n document.addEventListener('mousedown', handleClick)\n return () => document.removeEventListener('mousedown', handleClick)\n }, [])\n \n // Client-side only for portal\n useEffect(() => {\n setMounted(true)\n }, [])\n\n // Handle open/close - just set visibility\n useEffect(() => {\n if (open) {\n setIsVisible(true)\n // Lock body scroll on iOS\n document.body.style.overflow = 'hidden'\n document.body.style.position = 'fixed'\n document.body.style.width = '100%'\n document.body.style.top = `-${window.scrollY}px`\n } else {\n setIsAnimating(false)\n // Restore body scroll\n const scrollY = document.body.style.top\n document.body.style.overflow = ''\n document.body.style.position = ''\n document.body.style.width = ''\n document.body.style.top = ''\n window.scrollTo(0, parseInt(scrollY || '0') * -1)\n // Wait for animation to complete before hiding\n const timer = setTimeout(() => {\n setIsVisible(false)\n }, 200)\n return () => clearTimeout(timer)\n }\n }, [open])\n\n // Start animation AFTER visibility is set (separate render cycle)\n useEffect(() => {\n if (isVisible && open && !isAnimating) {\n // Element is now in DOM with translate-x-full, trigger transition\n requestAnimationFrame(() => {\n setIsAnimating(true)\n })\n }\n }, [isVisible, open, isAnimating])\n\n // Save scroll position when panel closes\n useEffect(() => {\n if (!open && messagesContainerRef.current) {\n savedScrollPositionRef.current = messagesContainerRef.current.scrollTop\n }\n }, [open])\n\n // Restore scroll position or scroll to bottom for new messages\n useEffect(() => {\n if (!open || !isVisible) return\n \n const container = messagesContainerRef.current\n if (!container) return\n \n const prevCount = prevMessageCountRef.current\n const currentCount = messages.length\n \n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n if (currentCount > prevCount) {\n // Scroll when new messages added (user sends message)\n const behavior = prevCount === 0 ? 'instant' : 'smooth'\n messagesEndRef.current?.scrollIntoView({ behavior })\n } else if (savedScrollPositionRef.current !== null) {\n // Re-opening panel - restore scroll position\n container.scrollTop = savedScrollPositionRef.current\n }\n \n prevMessageCountRef.current = currentCount\n })\n })\n }, [messages.length, open, isVisible])\n\n // During streaming, scroll until user's message reaches top of container\n useEffect(() => {\n if (!isStreaming) return\n \n const container = messagesContainerRef.current\n const userMessage = lastUserMessageRef.current\n if (!container || !userMessage) return\n \n // Get positions\n const containerRect = container.getBoundingClientRect()\n const messageRect = userMessage.getBoundingClientRect()\n \n // If user message is still below the top of container, scroll down\n const distanceFromTop = messageRect.top - containerRect.top\n if (distanceFromTop > 10) {\n // Scroll by a portion of the distance, creates smooth catching up\n container.scrollTop += Math.min(distanceFromTop * 0.3, 30)\n }\n }, [messages, isStreaming])\n\n // Auto-resize textarea\n useEffect(() => {\n const textarea = textareaRef.current\n if (textarea) {\n textarea.style.height = 'auto'\n textarea.style.height = `${Math.min(textarea.scrollHeight, 200)}px`\n }\n }, [input])\n\n\n const sendMessage = useCallback(async () => {\n if (!input.trim() || isStreaming) return\n const content = input.trim()\n setInput('')\n await contextSendMessage(content)\n }, [input, isStreaming, contextSendMessage])\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault()\n sendMessage()\n }\n if (e.key === 'Escape') {\n e.stopPropagation()\n onClose()\n }\n }\n \n // Keyboard shortcut for agent mode (Cmd/Ctrl + Shift + A)\n useEffect(() => {\n const handleGlobalKeyDown = (e: KeyboardEvent) => {\n if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === 'a') {\n e.preventDefault()\n if (!open) {\n setIsOpen(true)\n }\n // Only switch to agent mode if there's an essay context\n if (essayContext) {\n setMode('agent')\n }\n }\n }\n \n document.addEventListener('keydown', handleGlobalKeyDown)\n return () => document.removeEventListener('keydown', handleGlobalKeyDown)\n }, [open, setIsOpen, setMode, essayContext])\n\n if (!isVisible || !mounted) return null\n \n const getModeStyles = (m: ChatMode) => {\n if (m === 'ask') return 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400'\n if (m === 'agent') return 'bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400'\n return 'bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400'\n }\n\n return createPortal(\n <>\n {/* Backdrop */}\n <div \n className={`fixed inset-0 h-[100dvh] bg-black/20 z-[60] transition-opacity duration-200 ${isAnimating ? 'opacity-100' : 'opacity-0'}`}\n onClick={onClose}\n />\n \n {/* Panel */}\n <div \n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Chat\"\n className={`fixed z-[70] flex flex-col bg-background shadow-xl transition-transform duration-200 ease-out overflow-hidden inset-x-0 top-0 h-[100dvh] md:left-auto md:w-full md:max-w-[380px] md:border-l md:border-border ${isAnimating ? 'translate-x-0' : 'translate-x-full'}`}\n >\n {/* Header */}\n <div className=\"flex-shrink-0 border-b border-border px-4 py-3 flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <h2 className=\"font-medium\">Chat</h2>\n {essayContext && (\n <span className=\"inline-flex items-center px-2 py-0.5 text-xs font-medium rounded-full bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400 truncate max-w-[120px]\">\n {essayContext.title || 'Untitled'}\n </span>\n )}\n </div>\n <button\n onClick={onClose}\n className=\"w-8 h-8 rounded-md hover:bg-accent flex items-center justify-center text-muted-foreground\"\n aria-label=\"Close chat\"\n >\n <X className=\"w-4 h-4\" />\n </button>\n </div>\n\n {/* Messages */}\n <div ref={messagesContainerRef} className=\"flex-1 overflow-y-auto\">\n {messages.length === 0 ? (\n <div className=\"h-full flex items-center justify-center\">\n <div className=\"text-center max-w-xs px-6\">\n <p className=\"text-muted-foreground text-sm\">\n {mode === 'plan'\n ? \"Describe your essay idea and I'll create a structured outline with section headers and key points.\"\n : essayContext \n ? \"Chat about your essay — ask for feedback, discuss ideas, or get help with specific sections.\"\n : \"Chat with AI to brainstorm ideas, get feedback, or explore topics.\"\n }\n </p>\n </div>\n </div>\n ) : (\n <div className=\"px-4 py-4 space-y-4\">\n {messages.map((message, index) => {\n // Track last user message for scroll behavior\n const isLastUserMessage = message.role === 'user' && \n !messages.slice(index + 1).some(m => m.role === 'user')\n \n return (\n <div\n key={index}\n ref={isLastUserMessage ? lastUserMessageRef : undefined}\n className={`flex gap-3 group ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}\n >\n <div\n className={`max-w-[85%] rounded-2xl px-3 py-2 text-sm relative ${message.role === 'user' ? 'bg-primary text-primary-foreground' : 'bg-muted'}`}\n >\n {message.role === 'assistant' ? (\n <div \n className={`${proseClasses} [&>*:first-child]:mt-0 [&>*:last-child]:mb-0`}\n dangerouslySetInnerHTML={{ __html: markdownToHtml(stripPlanTags(message.content)) }}\n />\n ) : (\n <div className=\"whitespace-pre-wrap break-words\">\n {message.content}\n </div>\n )}\n {isStreaming && index === messages.length - 1 && message.role === 'assistant' && (\n <span className=\"inline-block w-1.5 h-3 bg-current ml-0.5 animate-pulse\" />\n )}\n {/* Applied edit indicator with undo button */}\n {message.appliedEdits && message.previousState && (\n <div className=\"flex items-center gap-2 mt-1.5\">\n <div className=\"flex items-center gap-1 text-xs text-green-600 dark:text-green-400\">\n <Pencil className=\"w-3 h-3\" />\n <span>Edit applied</span>\n </div>\n <button\n onClick={() => undoEdit(index)}\n className=\"flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors\"\n aria-label=\"Undo edit\"\n >\n <Undo2 className=\"w-3 h-3\" />\n <span>Undo</span>\n </button>\n </div>\n )}\n {/* Action buttons for assistant messages */}\n {message.role === 'assistant' && !isStreaming && (\n <div className=\"absolute -bottom-6 left-0 flex items-center gap-2 opacity-0 group-hover:opacity-100 transition-opacity\">\n <button\n onClick={() => copyToClipboard(message.content, index)}\n className=\"text-muted-foreground hover:text-foreground p-1 rounded\"\n aria-label=\"Copy message\"\n >\n {copiedIndex === index ? (\n <Check className=\"w-3.5 h-3.5 text-green-500\" />\n ) : (\n <Copy className=\"w-3.5 h-3.5\" />\n )}\n </button>\n {/* Draft Essay button for plan mode */}\n {message.mode === 'plan' && index === messages.length - 1 && message.content && (\n <button\n onClick={handleDraftEssay}\n className=\"text-xs text-muted-foreground hover:text-foreground px-1 rounded\"\n >\n Draft Essay\n </button>\n )}\n </div>\n )}\n </div>\n </div>\n )})}\n <div ref={messagesEndRef} />\n </div>\n )}\n </div>\n\n {/* Input */}\n <form \n onSubmit={(e) => { e.preventDefault(); sendMessage(); }}\n className=\"flex-shrink-0 border-t border-border bg-background p-3 pb-[calc(1rem+env(safe-area-inset-bottom,0px))]\"\n >\n {/* Controls Row: Mode Dropdown, Globe Toggle, Model Dropdown */}\n <div className=\"mb-2 flex items-center gap-3\">\n {/* Mode Dropdown */}\n <div ref={modeMenuRef} className=\"relative\">\n <button\n type=\"button\"\n onClick={() => setModeMenuOpen(!modeMenuOpen)}\n className={`inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium rounded-full transition-colors hover:opacity-80 ${getModeStyles(mode)}`}\n title=\"Switch mode (⌘⇧A)\"\n >\n {mode === 'ask' && <MessageSquare className=\"w-3 h-3\" />}\n {mode === 'agent' && <Pencil className=\"w-3 h-3\" />}\n {mode === 'plan' && <List className=\"w-3 h-3\" />}\n {mode === 'ask' ? 'Ask' : mode === 'agent' ? 'Agent' : 'Plan'}\n <ChevronDown className=\"w-3 h-3 opacity-60\" />\n </button>\n {modeMenuOpen && (\n <div className=\"absolute top-full left-0 mt-1 min-w-[140px] bg-popover border border-border rounded-md shadow-md z-[80]\">\n <button\n type=\"button\"\n onClick={() => { setMode('agent'); setModeMenuOpen(false); textareaRef.current?.focus() }}\n disabled={!essayContext}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <span className=\"flex items-center gap-2\">\n <Pencil className=\"w-4 h-4\" />\n Agent\n </span>\n <span className=\"flex items-center gap-2\">\n <span className=\"text-xs text-muted-foreground\">⌘⇧A</span>\n {mode === 'agent' && <Check className=\"w-4 h-4\" />}\n </span>\n </button>\n <button\n type=\"button\"\n onClick={() => { setMode('plan'); setModeMenuOpen(false); textareaRef.current?.focus() }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between\"\n >\n <span className=\"flex items-center gap-2\">\n <List className=\"w-4 h-4\" />\n Plan\n </span>\n {mode === 'plan' && <Check className=\"w-4 h-4\" />}\n </button>\n <button\n type=\"button\"\n onClick={() => { setMode('ask'); setModeMenuOpen(false); textareaRef.current?.focus() }}\n className=\"w-full px-3 py-2 text-left text-sm hover:bg-accent flex items-center justify-between\"\n >\n <span className=\"flex items-center gap-2\">\n <MessageSquare className=\"w-4 h-4\" />\n Ask\n </span>\n {mode === 'ask' && <Check className=\"w-4 h-4\" />}\n </button>\n </div>\n )}\n </div>\n \n {/* Web Search Toggle */}\n <ControlButton\n onClick={() => { setWebSearchEnabled(!webSearchEnabled); textareaRef.current?.focus() }}\n active={webSearchEnabled}\n title={webSearchEnabled ? \"Web search enabled\" : \"Enable web search\"}\n tabIndex={-1}\n >\n <Globe className=\"w-4 h-4\" />\n </ControlButton>\n \n {/* Thinking Mode Toggle */}\n <ControlButton\n onClick={() => { setThinkingEnabled(!thinkingEnabled); textareaRef.current?.focus() }}\n active={thinkingEnabled}\n title={thinkingEnabled ? \"Thinking mode enabled\" : \"Enable thinking mode\"}\n tabIndex={-1}\n >\n <Brain className=\"w-4 h-4\" />\n </ControlButton>\n \n {/* Model Dropdown */}\n <ModelSelector\n models={models}\n selectedModel={selectedModel}\n onModelChange={(id) => { setSelectedModel(id); textareaRef.current?.focus() }}\n currentModel={currentModel}\n />\n </div>\n <div className=\"flex items-end gap-2\">\n <textarea\n ref={textareaRef}\n value={input}\n onChange={(e) => setInput(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={\n mode === 'plan'\n ? \"Describe your essay idea...\"\n : mode === 'agent' && essayContext\n ? \"Ask me to edit your essay...\"\n : essayContext \n ? \"Ask about your essay...\" \n : \"Ask anything...\"\n }\n className=\"flex-1 min-h-[40px] max-h-[120px] resize-none px-3 py-2 border border-input rounded-md bg-transparent text-sm focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2\"\n rows={1}\n autoFocus\n />\n <button\n type={isStreaming ? 'button' : 'submit'}\n onClick={isStreaming ? stopStreaming : undefined}\n disabled={!isStreaming && !input.trim()}\n className=\"rounded-full w-10 h-10 flex-shrink-0 border border-input bg-secondary text-secondary-foreground hover:bg-secondary/80 disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center touch-manipulation\"\n >\n {isStreaming ? (\n <Square className=\"h-4 w-4 fill-current\" />\n ) : (\n <ArrowUp className=\"h-5 w-5\" />\n )}\n </button>\n </div>\n </form>\n </div>\n </>,\n document.body\n )\n}\n","'use client'\n\nimport { useState, useEffect } from 'react'\nimport type { AIModelOption } from '../../lib/models'\n\ninterface UseAIModelsOptions {\n /** External selected model state (for context-managed selection) */\n externalSelectedModel?: string\n /** External setter (for context-managed selection) */\n externalSetSelectedModel?: (id: string) => void\n /** Custom API path for settings (defaults to /api/cms/ai/settings) */\n apiPath?: string\n}\n\ninterface UseAIModelsResult {\n models: AIModelOption[]\n selectedModel: string\n setSelectedModel: (id: string) => void\n currentModel: AIModelOption | undefined\n isLoading: boolean\n}\n\n/**\n * Hook to fetch available AI models and manage selection.\n * Fetches models from AI settings endpoint and sets default model on mount.\n * \n * Can use internal state (default) or external state (for context-managed selection).\n */\nexport function useAIModels(options?: UseAIModelsOptions): UseAIModelsResult {\n const [models, setModels] = useState<AIModelOption[]>([])\n const [internalSelectedModel, setInternalSelectedModel] = useState('')\n const [isLoading, setIsLoading] = useState(true)\n\n // Use external state if provided, otherwise internal\n const selectedModel = options?.externalSelectedModel ?? internalSelectedModel\n const setSelectedModel = options?.externalSetSelectedModel ?? setInternalSelectedModel\n \n const apiPath = options?.apiPath ?? '/api/cms/ai/settings'\n\n useEffect(() => {\n fetch(apiPath)\n .then(res => {\n if (!res.ok) throw new Error(`HTTP ${res.status}`)\n return res.json()\n })\n .then(data => {\n // Handle both wrapped { data: {...} } and unwrapped response formats\n const settings = data.data || data\n setModels(settings.availableModels || [])\n if (settings.defaultModel && !selectedModel) {\n setSelectedModel(settings.defaultModel)\n }\n })\n .catch(console.error)\n .finally(() => setIsLoading(false))\n }, [apiPath]) // eslint-disable-line react-hooks/exhaustive-deps\n // Intentionally omit selectedModel/setSelectedModel - we only want to set default on mount\n\n const currentModel = models.find(m => m.id === selectedModel)\n\n return {\n models,\n selectedModel,\n setSelectedModel,\n currentModel,\n isLoading,\n }\n}\n"],"mappings":";;;;AAEA,SAAS,YAAAA,kBAAgC;AACzC,SAAS,MAAM,WAAAC,gBAAe;;;ACD9B,SAAS,eAAe,YAAY,UAAU,WAAW,aAAa,eAA+B;AAmQjG;AAjLJ,IAAM,mBAAmB,cAA4C,IAAI;AAElE,SAAS,sBAAsB;AACpC,QAAM,MAAM,WAAW,gBAAgB;AACvC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,2DAA2D;AACrF,SAAO;AACT;AAEA,IAAM,iBAAyC;AAAA,EAC7C,WAAW;AAAA,EACX,OAAO;AAAA,EACP,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,OAAO;AACT;AAEA,SAAS,YAAY,UAAkB,UAA0B;AAC/D,QAAM,aAAa,SAAS,QAAQ,OAAO,EAAE;AAC7C,QAAM,OAAO,SAAS,QAAQ,OAAO,EAAE;AACvC,MAAI,eAAe,KAAM,QAAO;AAChC,MAAI,WAAW,WAAW,OAAO,GAAG,EAAG,QAAO,WAAW,MAAM,KAAK,MAAM,KAAK;AAC/E,SAAO;AACT;AAaO,SAAS,kBAAkB;AAAA,EAChC,WAAW;AAAA,EACX,cAAc;AAAA,EACd;AAAA,EACA,SAAS,CAAC;AAAA,EACV,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,QAAM,CAAC,aAAa,cAAc,IAAI,SAAS,GAAG;AAClD,QAAM,CAAC,YAAY,aAAa,IAAI,SAA4B,IAAI;AACpE,QAAM,CAAC,mBAAmB,oBAAoB,IAAI,SAAS,IAAI;AAE/D,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,CAAC;AAElD,YAAU,MAAM;AACd,QAAI,OAAO,WAAW,aAAa;AACjC,qBAAe,YAAY,OAAO,SAAS,UAAU,QAAQ,CAAC;AAC9D,YAAM,iBAAiB,MAAM;AAC3B,uBAAe,YAAY,OAAO,SAAS,UAAU,QAAQ,CAAC;AAE9D,wBAAgB,OAAK,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;AAAA,MACzC;AACA,aAAO,iBAAiB,YAAY,cAAc;AAClD,aAAO,MAAM,OAAO,oBAAoB,YAAY,cAAc;AAAA,IACpE;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,WAAW,YAAY,CAAC,SAAiB;AAC7C,UAAM,WAAW,KAAK,WAAW,GAAG,IAAI,WAAW,OAAO,WAAW,MAAM;AAC3E,WAAO,QAAQ,UAAU,CAAC,GAAG,IAAI,QAAQ;AACzC,mBAAe,KAAK,WAAW,GAAG,IAAI,OAAO,MAAM,IAAI;AAEvD,oBAAgB,OAAK,IAAI,CAAC;AAAA,EAC5B,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,SAAS,YAAY,MAAM;AAC/B,QAAI,eAAe,GAAG;AAEpB,aAAO,QAAQ,KAAK;AAAA,IACtB,OAAO;AAEL,eAAS,GAAG;AAAA,IACd;AAAA,EACF,GAAG,CAAC,cAAc,QAAQ,CAAC;AAE3B,QAAM,YAAY,eAAe;AAGjC,QAAM,kBAAkB,YAAY,YAAY;AAC9C,yBAAqB,IAAI;AACzB,QAAI;AACF,YAAM,CAAC,WAAW,aAAa,UAAU,aAAa,IAAI,MAAM,QAAQ,IAAI;AAAA,QAC1E,MAAM,GAAG,WAAW,eAAe,EAAE,KAAK,OAAK,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI;AAAA,QACrE,MAAM,GAAG,WAAW,WAAW,EAAE,KAAK,OAAK,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI;AAAA,QACjE,MAAM,GAAG,WAAW,QAAQ,EAAE,KAAK,OAAK,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI;AAAA,QAC9D,MAAM,GAAG,WAAW,cAAc,EAAE,KAAK,OAAK,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI;AAAA,MACtE,CAAC;AAED,YAAM,mBAAmB,aAAa,MAAM,oBAAoB;AAGhE,UAAI,iBAA4B,CAAC;AACjC,UAAI,kBAAkB;AACpB,cAAM,eAAe,MAAM,MAAM,GAAG,WAAW,yBAAyB,EAAE,KAAK,OAAK,EAAE,KAAK,EAAE,KAAK,IAAI,IAAI;AAC1G,yBAAiB,cAAc,QAAQ,CAAC;AAAA,MAC1C;AAEA,oBAAc;AAAA,QACZ,QAAQ,WAAW,QAAQ,CAAC;AAAA,QAC5B,UAAU,EAAE,iBAAiB;AAAA,QAC7B,OAAO,UAAU,QAAQ,CAAC;AAAA,QAC1B;AAAA,QACA,YAAY;AAAA,UACV,cAAc,eAAe,MAAM,gBAAgB;AAAA,UACnD,iBAAiB,eAAe,MAAM,mBAAmB,CAAC;AAAA,QAC5D;AAAA,MACF,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,cAAQ,MAAM,gCAAgC,GAAG;AACjD,oBAAc;AAAA,QACZ,QAAQ,CAAC;AAAA,QACT,UAAU,EAAE,kBAAkB,MAAM;AAAA,QACpC,OAAO,CAAC;AAAA,QACR,gBAAgB,CAAC;AAAA,QACjB,YAAY,EAAE,cAAc,iBAAiB,iBAAiB,CAAC,EAAE;AAAA,MACnE,CAAC;AAAA,IACH,UAAE;AACA,2BAAqB,KAAK;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,WAAW,CAAC;AAEhB,YAAU,MAAM;AACd,oBAAgB;AAAA,EAClB,GAAG,CAAC,eAAe,CAAC;AAEpB,QAAM,eAAe;AAAA,IACnB,OAAO,EAAE,GAAG,gBAAgB,GAAG,OAAO;AAAA,IACtC,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,SAAS;AAAA,IACb,OAAO,EAAE,QAAQ,QAAQ,aAAa;AAAA,IACtC,CAAC,QAAQ,YAAY;AAAA,EACvB;AAEA,QAAM,eAAe,QAA+B,OAAO;AAAA,IACzD;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB;AAAA,IACA;AAAA,EACF,IAAI;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE,oBAAC,iBAAiB,UAAjB,EAA0B,OAAO,cAC/B,UACH;AAEJ;;;ACvQA,SAAS,YAAAC,WAAU,aAAAC,YAAW,WAAAC,UAAS,UAAAC,eAAc;AACrD,SAAS,OAAO,OAAO,SAAS,eAAAC,cAAa,SAAAC,QAAO,GAAG,MAAM,QAAQ,cAAc,oBAAoB;;;ACDvG,SAAS,kBAAkB;AAgBrB,gBAAAC,YAAA;AAVC,IAAM,gBAAgB;AAAA,EAC3B,CAAC,EAAE,YAAY,IAAI,QAAQ,UAAU,UAAU,OAAO,UAAU,GAAG,MAAM,GAAG,QAAQ;AAClF,UAAM,cAAc;AACpB,UAAM,eAAe,WACjB,gDACA,SACE,qCACA;AAEN,WACE,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW,GAAG,WAAW,IAAI,YAAY,IAAI,SAAS;AAAA,QACrD,GAAG;AAAA,QAEH;AAAA;AAAA,IACH;AAAA,EAEJ;AACF;AAEA,cAAc,cAAc;;;AC7B5B,SAAS,YAAAC,WAAU,QAAQ,aAAAC,kBAAiB;AAC5C,SAAS,aAAa,aAAa;AAiC7B,SAEE,OAAAC,MAFF;AAtBC,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAS,KAAK;AACtC,QAAM,MAAM,OAAuB,IAAI;AACvC,QAAM,eAAe,gBAAgB,OAAO,KAAK,OAAK,EAAE,OAAO,aAAa;AAE5E,EAAAC,WAAU,MAAM;AACd,aAAS,YAAY,GAAe;AAClC,UAAI,IAAI,WAAW,CAAC,IAAI,QAAQ,SAAS,EAAE,MAAc,GAAG;AAC1D,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AACA,aAAS,iBAAiB,aAAa,WAAW;AAClD,WAAO,MAAM,SAAS,oBAAoB,aAAa,WAAW;AAAA,EACpE,GAAG,CAAC,CAAC;AAEL,SACE,qBAAC,SAAI,KAAU,WAAU,YACvB;AAAA,yBAAC,iBAAc,SAAS,MAAM,QAAQ,CAAC,IAAI,GACxC;AAAA,oBAAc,QAAQ;AAAA,MACvB,gBAAAF,KAAC,eAAY,WAAU,eAAc;AAAA,OACvC;AAAA,IACC,QACC,gBAAAA,KAAC,SAAI,WAAU,yGACZ,iBAAO,IAAI,WACV;AAAA,MAAC;AAAA;AAAA,QAEC,SAAS,MAAM;AAAE,wBAAc,MAAM,EAAE;AAAG,kBAAQ,KAAK;AAAA,QAAE;AAAA,QACzD,WAAU;AAAA,QAEV;AAAA,0BAAAA,KAAC,UAAM,gBAAM,MAAK;AAAA,UACjB,kBAAkB,MAAM,MAAM,gBAAAA,KAAC,SAAM,WAAU,WAAU;AAAA;AAAA;AAAA,MALrD,MAAM;AAAA,IAMb,CACD,GACH;AAAA,KAEJ;AAEJ;;;ACvDO,IAAM,iBAAiB,CAAC,KAAK,KAAK,KAAK,GAAI;AAY3C,IAAM,iBAAkC;AAAA,EAC7C,EAAE,IAAI,iBAAiB,MAAM,cAAc,aAAa,iBAAiB,iBAAiB,MAAM;AAAA,EAChG,EAAE,IAAI,eAAe,MAAM,YAAY,aAAa,mBAAmB,iBAAiB,MAAM;AAAA,EAC9F,EAAE,IAAI,WAAW,MAAM,WAAW,aAAa,iBAAiB,iBAAiB,KAAK;AAAA,EACtF,EAAE,IAAI,cAAc,MAAM,cAAc,aAAa,sBAAsB,iBAAiB,KAAK;AACnG;;;AClBA,IAAM,SAAS;AAAf,IAAuB,OAAO;AAA9B,IAAyC,MAAM;AAKxC,SAAS,mBAAmB,SAAyB;AAC1D,QAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,QAAM,SAAS,KAAK,IAAI,IAAI,KAAK,QAAQ;AACzC,QAAM,OAAO,KAAK,MAAM,SAAS,MAAM;AACvC,QAAM,QAAQ,KAAK,MAAM,SAAS,IAAI;AACtC,QAAM,OAAO,KAAK,MAAM,SAAS,GAAG;AAEpC,MAAI,OAAO,EAAG,QAAO;AACrB,MAAI,OAAO,GAAI,QAAO,GAAG,IAAI;AAC7B,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAC/B,MAAI,SAAS,EAAG,QAAO;AACvB,MAAI,OAAO,EAAG,QAAO,GAAG,IAAI;AAC5B,SAAO,KAAK,mBAAmB;AACjC;AAoBO,SAAS,gBAAgB,MAAoB;AAClD,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,SAAS,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC5C,QAAM,WAAW,KAAK,MAAM,SAAS,GAAI;AACzC,QAAM,WAAW,KAAK,MAAM,SAAS,MAAM;AAG3C,MAAI,WAAW,GAAI,QAAO;AAC1B,MAAI,WAAW,GAAI,QAAO,GAAG,QAAQ;AACrC,MAAI,WAAW,GAAI,QAAO,GAAG,QAAQ;AAGrC,QAAM,UAAU,KAAK,aAAa,MAAM,IAAI,aAAa;AACzD,MAAI,SAAS;AACX,WAAO,KAAK,mBAAmB,CAAC,GAAG,EAAE,MAAM,WAAW,QAAQ,UAAU,CAAC;AAAA,EAC3E;AAEA,SAAO,KAAK,mBAAmB,SAAS;AAAA,IACtC,OAAO;AAAA,IACP,KAAK;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,EACV,CAAC;AACH;AAKO,SAAS,WAAW,MAA8B;AACvD,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,MAAM,KAAK,EAAE,OAAO,OAAO,EAAE;AAC3C;;;AJuFQ,SACE,OAAAG,MADF,QAAAC,aAAA;AA5HD,SAAS,kBAAkB;AAChC,QAAM,EAAE,aAAa,UAAU,YAAY,kBAAkB,IAAI,oBAAoB;AACrF,QAAM,CAAC,OAAO,QAAQ,IAAIC,UAAiB,CAAC,CAAC;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,IAAI;AAC3C,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,EAAE;AACjD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAClD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAkB,KAAK;AACzD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,UAA0B,CAAC,CAAC;AACxE,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,UAAS,KAAK;AAC9D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAS,KAAK;AACxD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AAEtE,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAA0B,cAAc;AACpE,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,eAAe;AACtD,QAAM,CAAC,QAAQ,SAAS,IAAIA,UAAiB,GAAG;AAChD,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAClD,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,UAAS,KAAK;AAC5D,QAAM,CAAC,YAAY,aAAa,IAAIA,UAAS,KAAK;AAGlD,EAAAC,WAAU,MAAM;AACd,QAAI,YAAY;AACd,eAAS,WAAW,KAAe;AACnC,wBAAkB,WAAW,cAAiC;AAC9D,0BAAoB,WAAW,SAAS,gBAAgB;AACxD,UAAI,WAAW,WAAW,gBAAgB,SAAS,GAAG;AACpD,kBAAU,WAAW,WAAW,eAAkC;AAAA,MACpE;AACA,UAAI,WAAW,WAAW,cAAc;AACtC,mBAAW,WAAW,WAAW,YAAY;AAAA,MAC/C;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAGf,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,mBAAmB;AACtB,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,iBAAiB,CAAC;AAEtB,QAAM,eAAe,OAAO,KAAK,OAAK,EAAE,OAAO,OAAO;AACtD,QAAM,aAAaC,SAAQ,MAAM,MAAM,OAAO,OAAK,EAAE,WAAW,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC;AACxF,QAAM,iBAAiBA,SAAQ,MAAM,MAAM,OAAO,OAAK,EAAE,WAAW,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC;AAEhG,QAAM,gBAAgBA,SAAQ,MAAM;AAClC,QAAI,SAAS,MAAM,OAAO,OAAK,EAAE,WAAW,eAAe,EAAE,WAAW,SAAS;AACjF,QAAI,aAAa;AACf,eAAS,OAAO,OAAO,OAAK,EAAE,MAAM,YAAY,EAAE,SAAS,YAAY,YAAY,CAAC,CAAC;AAAA,IACvF;AACA,QAAI,cAAc,SAAU,UAAS,OAAO,OAAO,OAAK,EAAE,WAAW,OAAO;AAAA,aACnE,cAAc,YAAa,UAAS,OAAO,OAAO,OAAK,EAAE,WAAW,WAAW;AACxF,WAAO,OAAO,KAAK,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC;AAAA,EAChG,GAAG,CAAC,OAAO,aAAa,SAAS,CAAC;AAElC,iBAAe,aAAa,IAAY;AACtC,QAAI,CAAC,QAAQ,mBAAmB,EAAG;AACnC,UAAM,MAAM,GAAG,WAAW,UAAU,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAC9D,aAAS,MAAM,OAAO,OAAK,EAAE,OAAO,EAAE,CAAC;AAAA,EACzC;AAEA,iBAAe,cAAc,IAAY;AACvC,UAAM,MAAM,GAAG,WAAW,UAAU,EAAE,IAAI;AAAA,MACxC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,YAAY,CAAC;AAAA,IAC9C,CAAC;AACD,aAAS,MAAM,IAAI,OAAK,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,QAAQ,YAAqB,IAAI,CAAC,CAAC;AAAA,EACnF;AAEA,iBAAe,gBAAgB,IAAY;AACzC,UAAM,MAAM,GAAG,WAAW,UAAU,EAAE,IAAI;AAAA,MACxC,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,QAAQ,CAAC;AAAA,IAC1C,CAAC;AACD,aAAS,MAAM,IAAI,OAAK,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,QAAQ,QAAiB,IAAI,CAAC,CAAC;AAAA,EAC/E;AAEA,iBAAe,sBAAsB,MAAqB;AACxD,qBAAiB,KAAK,EAAE;AACxB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,KAAK,EAAE,IAAI;AAAA,QACzD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,QAAQ,CAAC;AAAA,MAC1C,CAAC;AACD,UAAI,IAAI,GAAI,UAAS,WAAW,KAAK,IAAI,EAAE;AAAA,IAC7C,UAAE;AACA,uBAAiB,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,iBAAe,sBAAsB,MAAqB;AACxD,QAAI,CAAC,QAAQ,WAAW,KAAK,KAAK,IAAI,EAAG;AACzC,qBAAiB,KAAK,EAAE;AACxB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,KAAK,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAC/E,UAAI,IAAI,GAAI,mBAAkB,eAAe,OAAO,OAAK,EAAE,OAAO,KAAK,EAAE,CAAC;AAAA,IAC5E,UAAE;AACA,uBAAiB,IAAI;AAAA,IACvB;AAAA,EACF;AAEA,WAAS,iBAAiB,GAAqC;AAC7D,MAAE,eAAe;AACjB,UAAM,WAAW,IAAI,SAAS,EAAE,aAAa;AAC7C,UAAM,OAAQ,SAAS,IAAI,MAAM,GAAc,KAAK;AACpD,QAAI,MAAM;AACR,YAAM,SAAS,IAAI,gBAAgB;AAAA,QACjC;AAAA,QACA,OAAO;AAAA,QACP,QAAQ,OAAO,MAAM;AAAA,QACrB,GAAI,cAAc,EAAE,KAAK,IAAI;AAAA,QAC7B,GAAI,mBAAmB,EAAE,UAAU,IAAI;AAAA,MACzC,CAAC;AACD,eAAS,WAAW,MAAM,EAAE;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,SAAS;AACX,WACE,gBAAAH,MAAC,SAAI,WAAU,+BACb;AAAA,sBAAAA,MAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,KAAC,SAAI,WAAU,gDAA+C;AAAA,QAC9D,gBAAAA,KAAC,SAAI,WAAU,8CAA6C;AAAA,SAC9D;AAAA,MACA,gBAAAC,MAAC,SAAI,WAAU,0CACb;AAAA,wBAAAD,KAAC,SAAI,WAAU,4CAA2C;AAAA,QAC1D,gBAAAA,KAAC,SAAI,WAAU,4CAA2C;AAAA,QAC1D,gBAAAA,KAAC,SAAI,WAAU,4CAA2C;AAAA,SAC5D;AAAA,MACC,CAAC,GAAE,GAAE,GAAE,GAAE,CAAC,EAAE,IAAI,OACf,gBAAAA,KAAC,SAAY,WAAU,8CAAb,CAAwD,CACnE;AAAA,OACH;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAC,SAAI,WAAU,qCACb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,SAAS;AAAA,QACjC,WAAU;AAAA,QAEV,0BAAAA,KAAC,QAAK,WAAU,WAAU;AAAA;AAAA,IAC5B;AAAA,IAEA,gBAAAC,MAAC,SAAI,WAAU,aACb;AAAA,sBAAAD,KAAC,QAAG,WAAU,8BAA6B,kCAAoB;AAAA,MAC/D,gBAAAC,MAAC,UAAK,UAAU,kBACd;AAAA,wBAAAA,MAAC,SAAI,WAAU,YACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,aAAY;AAAA,cACZ,MAAM;AAAA,cACN,WAAU;AAAA,cACV,WAAW,CAAC,MAAM;AAChB,oBAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,oBAAE,eAAe;AACjB,oBAAE,cAAc,MAAM,cAAc;AAAA,gBACtC;AAAA,cACF;AAAA;AAAA,UACF;AAAA,UACA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,WAAU;AAAA,cAEV,0BAAAA,KAAC,WAAQ,WAAU,WAAU;AAAA;AAAA,UAC/B;AAAA,WACF;AAAA,QAEA,gBAAAC,MAAC,SAAI,WAAU,2BACb;AAAA,0BAAAA,MAAC,SAAI,WAAU,YACb;AAAA,4BAAAA,MAAC,iBAAc,SAAS,MAAM,cAAc,CAAC,UAAU,GACpD;AAAA;AAAA,cAAO;AAAA,cACR,gBAAAD,KAACK,cAAA,EAAY,WAAU,eAAc;AAAA,eACvC;AAAA,YACC,cACC,gBAAAL,KAAC,SAAI,WAAU,2FACZ,yBAAe,IAAI,SAClB,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBAEC,SAAS,MAAM;AAAE,4BAAU,GAAG;AAAG,gCAAc,KAAK;AAAA,gBAAE;AAAA,gBACtD,WAAU;AAAA,gBAET;AAAA;AAAA,kBAAI;AAAA,kBACJ,WAAW,OAAO,gBAAAD,KAACM,QAAA,EAAM,WAAU,WAAU;AAAA;AAAA;AAAA,cALzC;AAAA,YAMP,CACD,GACH;AAAA,aAEJ;AAAA,UAEA,gBAAAN,KAAC,iBAAc,SAAS,MAAM,cAAc,CAAC,UAAU,GAAG,QAAQ,YAAY,OAAM,kBAClF,0BAAAA,KAAC,SAAM,WAAU,WAAU,GAC7B;AAAA,UAEA,gBAAAA,KAAC,iBAAc,SAAS,MAAM,mBAAmB,CAAC,eAAe,GAAG,QAAQ,iBAAiB,OAAM,wBACjG,0BAAAA,KAAC,SAAM,WAAU,WAAU,GAC7B;AAAA,UAEA,gBAAAA,KAAC,iBAAc,QAAgB,eAAe,SAAS,eAAe,YAAY,cAA4B;AAAA,WAChH;AAAA,SACF;AAAA,OACF;AAAA,IAEC,oBACC,gBAAAC,MAAC,aAAQ,WAAU,QACjB;AAAA,sBAAAA,MAAC,YAAO,SAAS,MAAM,iBAAiB,CAAC,aAAa,GAAG,WAAU,sDACjE;AAAA,wBAAAD,KAACK,cAAA,EAAY,WAAW,sDAAsD,gBAAgB,KAAK,YAAY,IAAI;AAAA,QAAE;AAAA,QAEpH,eAAe,SAAS,KACvB,gBAAAL,KAAC,UAAK,WAAU,qGACb,yBAAe,QAClB;AAAA,SAEJ;AAAA,MACC,kBACC,eAAe,SAAS,IACtB,gBAAAA,KAAC,SAAI,WAAU,aACZ,yBAAe,IAAI,UAClB,gBAAAC,MAAC,SAAkB,WAAU,uEAC3B;AAAA,wBAAAA,MAAC,SAAI,WAAU,kBACb;AAAA,0BAAAD,KAAC,OAAE,WAAU,wBAAwB,eAAK,OAAM;AAAA,UAChD,gBAAAC,MAAC,SAAI,WAAU,yDACZ;AAAA,iBAAK,SAAS,gBAAAD,KAAC,UAAK,WAAU,wCAAwC,eAAK,MAAM,MAAK;AAAA,YACvF,gBAAAA,KAAC,UAAM,cAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB,GAAE;AAAA,YACpD,KAAK,aACJ,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,KAAK;AAAA,gBACX,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,WAAU;AAAA,gBACV,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,gBAElC;AAAA,kCAAAD,KAAC,gBAAa,WAAU,WAAU;AAAA,kBAAE;AAAA;AAAA;AAAA,YAEtC;AAAA,aAEJ;AAAA,WACF;AAAA,QACA,gBAAAC,MAAC,SAAI,WAAU,yCACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,sBAAsB,IAAI;AAAA,cACzC,UAAU,kBAAkB,KAAK;AAAA,cACjC,WAAU;AAAA,cAEV,0BAAAA,KAAC,KAAE,WAAU,WAAU;AAAA;AAAA,UACzB;AAAA,UACA,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,sBAAsB,IAAI;AAAA,cACzC,UAAU,kBAAkB,KAAK;AAAA,cACjC,WAAU;AAAA,cAEV;AAAA,gCAAAD,KAACM,QAAA,EAAM,WAAU,WAAU;AAAA,gBAAE;AAAA;AAAA;AAAA,UAE/B;AAAA,WACF;AAAA,WApCQ,KAAK,EAqCf,CACD,GACH,IAEA,gBAAAN,KAAC,OAAE,WAAU,iCAAgC,4EAE7C;AAAA,OAGN;AAAA,IAGF,gBAAAC,MAAC,SAAI,WAAU,0EACb;AAAA,sBAAAD,KAAC,SAAI,WAAW,QAAQ,aAAa,yBAAyB,EAAE,IAC5D,WAAC,OAAO,UAAU,WAAW,EAAgB,IAAI,SACjD,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,MAAM,aAAa,GAAG;AAAA,UAC/B,WAAW,qEACT,cAAc,MAAM,sCAAsC,gEAC5D;AAAA,UAEC;AAAA,gBAAI,OAAO,CAAC,EAAE,YAAY,IAAI,IAAI,MAAM,CAAC;AAAA,YAC1C,gBAAAA,MAAC,UAAK,WAAU,8BAA6B;AAAA;AAAA,cACzC,QAAQ,QAAQ,MAAM,OAAO,OAAK,EAAE,WAAW,eAAe,EAAE,WAAW,SAAS,EAAE,SAAS,QAAQ,WAAW,aAAa;AAAA,cAAe;AAAA,eAClJ;AAAA;AAAA;AAAA,QATK;AAAA,MAUP,CACD,GACH;AAAA,MAEA,gBAAAA,MAAC,SAAI,WAAW,gCAAgC,aAAa,mCAAmC,EAAE,IAChG;AAAA,wBAAAD,KAAC,YAAO,SAAS,MAAM,SAAS,SAAS,GAAG,WAAU,6FACpD,0BAAAA,KAAC,QAAK,WAAU,WAAU,GAC5B;AAAA,QACC,aACC,gBAAAC,MAAC,SAAI,WAAU,kDACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,aAAY;AAAA,cACZ,OAAO;AAAA,cACP,UAAU,OAAK,eAAe,EAAE,OAAO,KAAK;AAAA,cAC5C,WAAS;AAAA,cACT,WAAU;AAAA;AAAA,UACZ;AAAA,UACA,gBAAAA,KAAC,YAAO,SAAS,MAAM;AAAE,0BAAc,KAAK;AAAG,2BAAe,EAAE;AAAA,UAAE,GAAG,WAAU,6DAC7E,0BAAAA,KAAC,KAAE,WAAU,WAAU,GACzB;AAAA,WACF,IAEA,gBAAAA,KAAC,YAAO,SAAS,MAAM,cAAc,IAAI,GAAG,WAAU,8EACpD,0BAAAA,KAAC,UAAO,WAAU,WAAU,GAC9B;AAAA,SAEJ;AAAA,OACF;AAAA,IAEA,gBAAAA,KAAC,aACE,wBAAc,SAAS,IACtB,gBAAAA,KAAC,SAAI,WAAU,aACZ,wBAAc,IAAI,UACjB,gBAAAA;AAAA,MAAC;AAAA;AAAA,QAEC;AAAA,QACA,YAAY,MAAM,SAAS,WAAW,KAAK,IAAI,EAAE;AAAA,QACjD,UAAU,MAAM,aAAa,KAAK,EAAE;AAAA,QACpC,WAAW,MAAM,cAAc,KAAK,EAAE;AAAA,QACtC,aAAa,MAAM,gBAAgB,KAAK,EAAE;AAAA,QAC1C,YAAY,cAAc;AAAA;AAAA,MANrB,KAAK;AAAA,IAOZ,CACD,GACH,IAEA,gBAAAA,KAAC,OAAE,WAAU,0CACV,wBAAc,yBAAyB,mBAC1C,GAEJ;AAAA,KACF;AAEJ;AAYA,SAAS,SAAS,EAAE,MAAM,YAAY,UAAU,WAAW,aAAa,WAAW,GAAkB;AACnG,QAAM,CAAC,UAAU,WAAW,IAAIE,UAAS,KAAK;AAC9C,QAAM,UAAUK,QAAuB,IAAI;AAG3C,EAAAJ,WAAU,MAAM;AACd,QAAI,CAAC,SAAU;AACf,aAAS,YAAY,GAAe;AAClC,UAAI,QAAQ,WAAW,CAAC,QAAQ,QAAQ,SAAS,EAAE,MAAc,GAAG;AAClE,oBAAY,KAAK;AAAA,MACnB;AAAA,IACF;AACA,aAAS,iBAAiB,SAAS,WAAW;AAC9C,WAAO,MAAM,SAAS,oBAAoB,SAAS,WAAW;AAAA,EAChE,GAAG,CAAC,QAAQ,CAAC;AAEb,SACE,gBAAAF,MAAC,SAAI,WAAU,uEACb;AAAA,oBAAAD,KAAC,SAAI,WAAU,kBACb,0BAAAC,MAAC,YAAO,SAAS,YAAY,WAAU,0BACrC;AAAA,sBAAAD,KAAC,QAAG,WAAU,0DACX,eAAK,SAAS,YACjB;AAAA,MACA,gBAAAC,MAAC,OAAE,WAAU,8DACV;AAAA,sBACC,gBAAAD,KAAC,UAAK,WAAW,uDACf,KAAK,WAAW,UACZ,uDACA,oDACN,IACG,eAAK,QACR;AAAA,QAEF,gBAAAC,MAAC,UAAM;AAAA,6BAAmB,KAAK,SAAS;AAAA,UAAG,KAAK,YAAY,SAAM,KAAK,SAAS,WAAW;AAAA,WAAG;AAAA,SAChG;AAAA,OACF,GACF;AAAA,IAEA,gBAAAA,MAAC,SAAI,WAAU,YAAW,KAAK,SAC7B;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,YAAY,CAAC,QAAQ;AAAA,UACpC,WAAU;AAAA,UAEV,0BAAAA,KAAC,gBAAa,WAAU,WAAU;AAAA;AAAA,MACpC;AAAA,MACC,YACC,gBAAAC,MAAC,SAAI,WAAU,8HACb;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM;AAAE,yBAAW;AAAG,0BAAY,KAAK;AAAA,YAAE;AAAA,YAClD,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,QACC,KAAK,WAAW,WACf,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM;AAAE,wBAAU;AAAG,0BAAY,KAAK;AAAA,YAAE;AAAA,YACjD,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,QAED,KAAK,WAAW,eACf,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM;AAAE,0BAAY;AAAG,0BAAY,KAAK;AAAA,YAAE;AAAA,YACnD,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,QAEF,gBAAAA,KAAC,SAAI,WAAU,uBAAsB;AAAA,QACrC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM;AAAE,uBAAS;AAAG,0BAAY,KAAK;AAAA,YAAE;AAAA,YAChD,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,OAEJ;AAAA,KACF;AAEJ;;;AK9cA,SAAS,YAAAQ,YAAU,aAAAC,aAAW,eAAAC,cAAa,UAAAC,eAAc;;;ACEzD,SAAS,mBAAmB,qBAAqB;;;ACJjD,SAAS,YAA6B;AACtC,SAAS,eAAe;AAEjB,SAAS,MAAM,QAAsB;AAC1C,SAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B;;;ACOS,gBAAAC,YAAA;AADF,SAAS,SAAS,EAAE,UAAU,GAAkB;AACrD,SAAO,gBAAAA,KAAC,SAAI,WAAW,GAAG,kCAAkC,SAAS,GAAG;AAC1E;;;ACEI,gBAAAC,YAAA;AAFG,SAAS,cAAc,EAAE,SAAS,QAAQ,UAAU,UAAU,MAAM,GAAuB;AAChG,SACE,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU;AAAA,QACV,CAAC,UAAU;AAAA,MACb;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAEO,SAAS,UAAU;AACxB,SAAO,gBAAAA,KAAC,SAAI,WAAU,8CAA6C;AACrE;AAGO,SAAS,iBAAiB;AAC/B,SAAO,gBAAAA,KAAC,YAAS,WAAU,iDAAgD;AAC7E;;;ACvCA,SAAS,YAAAC,WAAU,aAAAC,YAAW,eAAAC,oBAAmB;AAMjD,SAAS,SAAS,aAAa;;;ACCxB,SAAS,eACd,UACA,QACA,OACA,UACA,kBACA;AACA,QAAM,QAAQ,SAAS;AACvB,QAAM,MAAM,SAAS;AACrB,QAAM,WAAW,SAAS,UAAU,OAAO,GAAG;AAC9C,QAAM,UAAU,SAAS,UAAU,GAAG,KAAK,IAAI,SAAS,WAAW,QAAQ,SAAS,UAAU,GAAG;AACjG,mBAAiB,OAAO;AAGxB,wBAAsB,MAAM;AAC1B,aAAS,MAAM;AACf,UAAM,eAAe,QAAQ,OAAO,SAAS,SAAS;AACtD,aAAS,kBAAkB,cAAc,YAAY;AAAA,EACvD,CAAC;AACH;AAMO,SAAS,oBACd,UACA,QACA,UACA,kBACA;AACA,QAAM,QAAQ,SAAS;AACvB,QAAM,MAAM,SAAS;AAGrB,MAAI,YAAY;AAChB,SAAO,YAAY,KAAK,SAAS,YAAY,CAAC,MAAM,MAAM;AACxD;AAAA,EACF;AAGA,MAAI,UAAU;AACd,SAAO,UAAU,SAAS,UAAU,SAAS,OAAO,MAAM,MAAM;AAC9D;AAAA,EACF;AACA,QAAM,cAAc,SAAS,UAAU,WAAW,OAAO;AAGzD,MAAI,YAAY,WAAW,MAAM,GAAG;AAClC,UAAM,UAAU,SAAS,UAAU,GAAG,SAAS,IAAI,YAAY,UAAU,OAAO,MAAM,IAAI,SAAS,UAAU,OAAO;AACpH,qBAAiB,OAAO;AAAA,EAC1B,OAAO;AACL,UAAM,UAAU,SAAS,UAAU,GAAG,SAAS,IAAI,SAAS,cAAc,SAAS,UAAU,OAAO;AACpG,qBAAiB,OAAO;AAAA,EAC1B;AAEA,wBAAsB,MAAM;AAC1B,aAAS,MAAM;AAAA,EACjB,CAAC;AACH;AAMO,SAAS,mBACd,UACA,OACA,UACA,kBACA;AACA,QAAM,QAAQ,SAAS;AACvB,QAAM,MAAM,SAAS;AAGrB,MAAI,YAAY;AAChB,SAAO,YAAY,KAAK,SAAS,YAAY,CAAC,MAAM,MAAM;AACxD;AAAA,EACF;AAGA,MAAI,UAAU;AACd,SAAO,UAAU,SAAS,UAAU,SAAS,OAAO,MAAM,MAAM;AAC9D;AAAA,EACF;AACA,QAAM,cAAc,SAAS,UAAU,WAAW,OAAO;AAGzD,QAAM,iBAAiB,YAAY,QAAQ,cAAc,EAAE;AAC3D,QAAM,YAAY,IAAI,OAAO,KAAK,IAAI;AAGtC,QAAM,sBAAsB,YAAY,MAAM,aAAa;AAC3D,MAAI,uBAAuB,oBAAoB,CAAC,EAAE,WAAW,OAAO;AAElE,UAAM,UAAU,SAAS,UAAU,GAAG,SAAS,IAAI,iBAAiB,SAAS,UAAU,OAAO;AAC9F,qBAAiB,OAAO;AAAA,EAC1B,OAAO;AAEL,UAAM,UAAU,SAAS,UAAU,GAAG,SAAS,IAAI,YAAY,iBAAiB,SAAS,UAAU,OAAO;AAC1G,qBAAiB,OAAO;AAAA,EAC1B;AAEA,wBAAsB,MAAM;AAC1B,aAAS,MAAM;AAAA,EACjB,CAAC;AACH;AAMO,SAAS,wBACd,UACA,UACA,kBACA;AACA,QAAM,QAAQ,SAAS;AACvB,QAAM,MAAM,SAAS;AAErB,MAAI,UAAU,IAAK;AAEnB,QAAM,WAAW,SAAS,UAAU,OAAO,GAAG;AAC9C,QAAM,UAAU,SACb,QAAQ,kBAAkB,IAAI,EAC9B,QAAQ,cAAc,IAAI,EAC1B,QAAQ,cAAc,IAAI,EAC1B,QAAQ,YAAY,IAAI,EACxB,QAAQ,qBAAqB,IAAI;AAEpC,QAAM,UAAU,SAAS,UAAU,GAAG,KAAK,IAAI,UAAU,SAAS,UAAU,GAAG;AAC/E,mBAAiB,OAAO;AAExB,wBAAsB,MAAM;AAC1B,aAAS,MAAM;AACf,aAAS,kBAAkB,OAAO,QAAQ,QAAQ,MAAM;AAAA,EAC1D,CAAC;AACH;;;AD7CM,mBAEE,OAAAC,MAFF,QAAAC,aAAA;AA/EC,SAAS,cAAc,EAAE,QAAQ,YAAY,aAAa,UAAU,kBAAkB,cAAc,SAAS,cAAc,WAAW,GAAuB;AAElK,QAAM,SAAS;AACf,QAAM,iBAAiB,CAAC,UAAU,eAAe,aAAa,UAAa;AAC3E,QAAM,CAAC,cAAc,eAAe,IAAIC,UAAS,KAAK;AACtD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AAGpD,EAAAC,WAAU,MAAM;AACd,QAAI,CAAC,QAAQ;AACX,sBAAgB,KAAK;AACrB;AAAA,IACF;AAEA,UAAM,kBAAkB,MAAM;AAC5B,YAAM,EAAE,MAAM,GAAG,IAAI,OAAO,MAAM;AAClC,sBAAgB,SAAS,EAAE;AAAA,IAC7B;AAEA,WAAO,GAAG,mBAAmB,eAAe;AAC5C,WAAO,GAAG,eAAe,eAAe;AAExC,WAAO,MAAM;AACX,aAAO,IAAI,mBAAmB,eAAe;AAC7C,aAAO,IAAI,eAAe,eAAe;AAAA,IAC3C;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,QAAM,gBAAgBC,aAAY,YAAY;AAC5C,QAAI,CAAC,UAAU,YAAa;AAE5B,UAAM,EAAE,MAAM,GAAG,IAAI,OAAO,MAAM;AAClC,QAAI,SAAS,GAAI;AAEjB,UAAM,eAAe,OAAO,MAAM,IAAI,YAAY,MAAM,IAAI,GAAG;AAC/D,QAAI,CAAC,aAAa,KAAK,EAAG;AAE1B,mBAAe,IAAI;AACnB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,eAAe;AAAA,QACnD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,aAAa,CAAC;AAAA,MAC7C,CAAC;AAED,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,cAAM,IAAI,MAAM,MAAM,SAAS,gBAAgB;AAAA,MACjD;AAEA,YAAM,SAAS,MAAM,IAAI,KAAK;AAG9B,aAAO,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,cAAc,OAAO,IAAI,EAAE,IAAI;AAAA,IAC1E,SAAS,KAAK;AACZ,cAAQ,MAAM,kBAAkB,GAAG;AACnC,YAAM,eAAe,QAAQ,IAAI,UAAU,wBAAwB;AAAA,IACrE,UAAE;AACA,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,QAAQ,aAAa,WAAW,CAAC;AAErC,QAAM,aAAa,CAAC,UAAkB;AACpC,QAAI,aAAa,WAAW,aAAa,UAAa,kBAAkB;AACtE,yBAAmB,YAAY,SAAS,OAAO,UAAU,gBAAgB;AAAA,IAC3E;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,QAAgB,UAAkB;AACvD,QAAI,OAAQ;AACZ,QAAI,aAAa,WAAW,aAAa,UAAa,kBAAkB;AACtE,qBAAe,YAAY,SAAS,QAAQ,OAAO,UAAU,gBAAgB;AAAA,IAC/E;AAAA,EACF;AAGA,MAAI,SAAS;AACX,WACE,gBAAAH,MAAA,YAEE;AAAA,sBAAAD,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,WAAQ;AAAA,MAET,gBAAAA,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,MAEhB,gBAAAA,KAAC,WAAQ;AAAA,MACT,gBAAAA,KAAC,kBAAe;AAAA,OAClB;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAA,YAEE;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,IAAI,WAAW,CAAC;AAAA,QAC/F,QAAQ,QAAQ,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC;AAAA,QAChD,UAAU;AAAA,QACV,OAAM;AAAA,QACP;AAAA;AAAA,IAED;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,IAAI,WAAW,CAAC;AAAA,QAC/F,QAAQ,QAAQ,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC;AAAA,QAChD,UAAU;AAAA,QACV,OAAM;AAAA,QACP;AAAA;AAAA,IAED;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,IAAI,WAAW,CAAC;AAAA,QAC/F,QAAQ,QAAQ,SAAS,WAAW,EAAE,OAAO,EAAE,CAAC;AAAA,QAChD,UAAU;AAAA,QACV,OAAM;AAAA,QACP;AAAA;AAAA,IAED;AAAA,IAEA,gBAAAA,KAAC,WAAQ;AAAA,IAGT,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,IAAI,cAAc,MAAM,IAAI;AAAA,QAC5F,QAAQ,QAAQ,SAAS,MAAM;AAAA,QAC/B,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,UAAK,WAAU,aAAY,eAAC;AAAA;AAAA,IAC/B;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,IAAI,cAAc,KAAK,GAAG;AAAA,QAC5F,QAAQ,QAAQ,SAAS,QAAQ;AAAA,QACjC,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,UAAK,WAAU,UAAS,eAAC;AAAA;AAAA,IAC5B;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,IAAI,cAAc,MAAM,IAAI;AAAA,QAC9F,QAAQ,QAAQ,SAAS,QAAQ;AAAA,QACjC,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,UAAK,WAAU,gBAAe,eAAC;AAAA;AAAA,IAClC;AAAA,IAGC,CAAC,kBACA,gBAAAC,MAAA,YACE;AAAA,sBAAAD,KAAC,WAAQ;AAAA,MACT,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,UAAU,CAAC,UAAU,CAAC,gBAAgB,eAAe;AAAA,UACrD,OACE,CAAC,SACG,sBACA,eACE,8BACA;AAAA,UAGP,wBACC,gBAAAA,KAAC,WAAQ,WAAU,wBAAuB,IAE1C,gBAAAA,KAAC,SAAM,WAAU,WAAU;AAAA;AAAA,MAE/B;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AEnMA,SAAS,MAAM,aAAa,OAAO,OAAO,aAAa;AAmBjD,qBAAAK,WAEE,OAAAC,MAFF,QAAAC,aAAA;AANC,SAAS,aAAa,EAAE,QAAQ,YAAY,aAAa,UAAU,kBAAkB,cAAc,QAAQ,GAAsB;AAEtI,QAAM,SAAS;AAEf,MAAI,SAAS;AACX,WACE,gBAAAA,MAAAF,WAAA,EAEE;AAAA,sBAAAC,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,MAEhB,gBAAAA,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,OAClB;AAAA,EAEJ;AACA,QAAM,iBAAiB,CAAC,UAAU,eAAe,aAAa,UAAa;AAE3E,QAAM,cAAc,CAAC,WAAmB;AACtC,QAAI,aAAa,WAAW,aAAa,UAAa,kBAAkB;AACtE,0BAAoB,YAAY,SAAS,QAAQ,UAAU,gBAAgB;AAAA,IAC7E;AAAA,EACF;AAEA,QAAM,gBAAgB,CAAC,QAAgB,UAAkB;AACvD,QAAI,OAAQ;AACZ,QAAI,aAAa,WAAW,aAAa,UAAa,kBAAkB;AACtE,qBAAe,YAAY,SAAS,QAAQ,OAAO,UAAU,gBAAgB;AAAA,IAC/E;AAAA,EACF;AAEA,QAAM,uBAAuB,MAAM;AACjC,QAAI,QAAQ;AACV,aAAO,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI;AAAA,IACjD,WAAW,kBAAkB,aAAa,SAAS;AACjD,YAAM,WAAW,YAAY;AAC7B,YAAM,QAAQ,SAAS;AACvB,YAAM,UAAU,SAAU,UAAU,GAAG,KAAK,IAAI,YAAY,SAAU,UAAU,KAAK;AACrF,uBAAkB,OAAO;AACzB,4BAAsB,MAAM,SAAS,MAAM,CAAC;AAAA,IAC9C;AAAA,EACF;AAEA,SACE,gBAAAC,MAAAF,WAAA,EAEE;AAAA,oBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,IAAI,YAAY,IAAI;AAAA,QAC1F,QAAQ,QAAQ,SAAS,YAAY;AAAA,QACrC,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,QAAK,WAAU,WAAU;AAAA;AAAA,IAC5B;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,IAAI,YAAY,KAAK;AAAA,QAC5F,QAAQ,QAAQ,SAAS,aAAa;AAAA,QACtC,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,eAAY,WAAU,WAAU;AAAA;AAAA,IACnC;AAAA,IAGA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,IAAI,IAAI,YAAY,IAAI;AAAA,QAC1F,QAAQ,QAAQ,SAAS,YAAY;AAAA,QACrC,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,SAAM,WAAU,WAAU;AAAA;AAAA,IAC7B;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,MAAM,SAAS,OAAO,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,IAAI,IAAI,cAAc,SAAS,OAAO;AAAA,QACvG,QAAQ,QAAQ,SAAS,WAAW;AAAA,QACpC,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,SAAM,WAAU,WAAU;AAAA;AAAA,IAC7B;AAAA,IACA,gBAAAA,KAAC,iBAAc,SAAS,sBAAsB,UAAU,cAAc,OAAM,mBAC1E,0BAAAA,KAAC,SAAM,WAAU,WAAU,GAC7B;AAAA,KACF;AAEJ;;;ACrGA,SAAS,UAAAE,SAAQ,eAAAC,oBAAmB;AAGpC,SAAS,OAAO,OAAO,wBAAwB;AAkHzC,qBAAAC,WAEE,OAAAC,MAFF,QAAAC,aAAA;AApGC,SAAS,aAAa,EAAE,QAAQ,YAAY,aAAa,UAAU,kBAAkB,cAAc,SAAS,cAAc,WAAW,GAAsB;AAEhK,QAAM,SAAS;AACf,QAAM,eAAeC,QAAyB,IAAI;AAClD,QAAM,iBAAiB,CAAC,UAAU,eAAe,aAAa,UAAa;AAE3E,QAAM,kBAAkBC,aAAY,MAAM;AACxC,QAAI,QAAQ;AACV,UAAI,OAAO,SAAS,MAAM,GAAG;AAC3B,eAAO,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI;AACvC;AAAA,MACF;AACA,YAAM,MAAM,OAAO,OAAO,YAAY;AACtC,UAAI,KAAK;AACP,eAAO,MAAM,EAAE,MAAM,EAAE,gBAAgB,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAE,IAAI;AAAA,MAC5E;AAAA,IACF,WAAW,gBAAgB;AACzB,YAAM,MAAM,OAAO,OAAO,YAAY;AACtC,UAAI,OAAO,aAAa,SAAS;AAC/B,cAAM,WAAW,YAAY;AAC7B,cAAM,QAAQ,SAAS;AACvB,cAAM,MAAM,SAAS;AACrB,cAAM,WAAW,SAAU,UAAU,OAAO,GAAG,KAAK;AACpD,cAAM,UAAU,SAAU,UAAU,GAAG,KAAK,IAAI,IAAI,QAAQ,KAAK,GAAG,MAAM,SAAU,UAAU,GAAG;AACjG,yBAAkB,OAAO;AACzB,8BAAsB,MAAM,SAAS,MAAM,CAAC;AAAA,MAC9C;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,gBAAgB,aAAa,UAAU,gBAAgB,CAAC;AAEpE,QAAM,oBAAoBA,aAAY,OAAO,MAA2C;AACtF,UAAM,OAAO,EAAE,OAAO,QAAQ,CAAC;AAC/B,QAAI,CAAC,KAAM;AAGX,UAAM,UAAU,IAAI,OAAO;AAC3B,QAAI,KAAK,OAAO,SAAS;AACvB,YAAM,qBAAqB,KAAK,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC,2BAA2B;AACzF,QAAE,OAAO,QAAQ;AACjB;AAAA,IACF;AAEA,UAAM,aAAa,CAAC,cAAc,aAAa,aAAa,YAAY;AACxE,QAAI,CAAC,WAAW,SAAS,KAAK,IAAI,GAAG;AACnC,YAAM,sBAAsB,KAAK,IAAI,0CAA0C;AAC/E,QAAE,OAAO,QAAQ;AACjB;AAAA,IACF;AAEA,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,OAAO,SAAS,IAAI;AAE7B,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,WAAW;AAAA,QAC/C,QAAQ;AAAA,QACR,MAAM;AAAA,MACR,CAAC;AAED,YAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,KAAK,SAAS,wBAAwB;AAC5C,UAAE,OAAO,QAAQ;AACjB;AAAA,MACF;AAGA,YAAM,MAAM,KAAK,OAAO,KAAK,MAAM;AACnC,UAAI,KAAK;AACP,YAAI,QAAQ;AACV,iBAAO,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,IAAI,CAAC,EAAE,IAAI;AAAA,QACpD,WAAW,kBAAkB,aAAa,SAAS;AACjD,gBAAM,WAAW,YAAY;AAC7B,gBAAM,QAAQ,SAAS;AACvB,gBAAM,UAAU,SAAU,UAAU,GAAG,KAAK,IAAI,YAAY,GAAG,MAAM,SAAU,UAAU,KAAK;AAC9F,2BAAkB,OAAO;AACzB,gCAAsB,MAAM,SAAS,MAAM,CAAC;AAAA,QAC9C;AAAA,MACF,OAAO;AACL,cAAM,sCAAsC;AAAA,MAC9C;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,2CAA2C;AACjD,cAAQ,MAAM,uBAAuB,GAAG;AAAA,IAC1C;AAEA,MAAE,OAAO,QAAQ;AAAA,EACnB,GAAG,CAAC,QAAQ,gBAAgB,aAAa,UAAU,kBAAkB,WAAW,CAAC;AAEjF,QAAM,wBAAwBA,aAAY,MAAM;AAC9C,QAAI,QAAQ;AACV,aAAO,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,WAAW,EAAE,IAAI;AAAA,IAC1D,WAAW,kBAAkB,aAAa,WAAW,YAAY,kBAAkB;AACjF,8BAAwB,YAAY,SAAS,UAAU,gBAAgB;AAAA,IACzE;AAAA,EACF,GAAG,CAAC,QAAQ,gBAAgB,aAAa,UAAU,gBAAgB,CAAC;AAGpE,MAAI,SAAS;AACX,WACE,gBAAAF,MAAAF,WAAA,EAEE;AAAA,sBAAAC,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,MAChB,gBAAAA,KAAC,kBAAe;AAAA,OAClB;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAAF,WAAA,EACE;AAAA,oBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,QAAQ,QAAQ,SAAS,MAAM;AAAA,QAC/B,UAAU;AAAA,QACV,OAAM;AAAA,QAEN,0BAAAA,KAAC,SAAM,WAAU,WAAU;AAAA;AAAA,IAC7B;AAAA,IACA,gBAAAA,KAAC,iBAAc,SAAS,MAAM,aAAa,SAAS,MAAM,GAAG,UAAU,cAAc,OAAM,gBACzF,0BAAAA,KAAC,SAAM,WAAU,WAAU,GAC7B;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,MAAK;AAAA,QACL,QAAO;AAAA,QACP,UAAU;AAAA,QACV,WAAU;AAAA;AAAA,IACZ;AAAA,IACA,gBAAAA,KAAC,iBAAc,SAAS,uBAAuB,UAAU,cAAc,OAAM,oBAC3E,0BAAAA,KAAC,oBAAiB,WAAU,WAAU,GACxC;AAAA,KACF;AAEJ;;;ACvJA,SAAS,eAAAI,oBAAmB;AAG5B,SAAS,OAAO,aAAa;;;ACH7B,SAAS,YAAAC,iBAAgB;AACzB,SAAS,WAAAC,UAAS,eAAe;;;ACDjC,SAAS,YAAAC,WAAU,UAAAC,SAAQ,aAAAC,YAAW,eAAAC,cAAa,iBAAAC,gBAAe,cAAAC,mBAAkB;AACpF,SAAS,oBAAoB;AAuIvB,SAuBF,YAAAC,WAvBE,OAAAC,OAuBF,QAAAC,aAvBE;AAhIN,IAAM,kBAAkBC,eAA4C,IAAI;AAuBjE,SAAS,SAAS;AAAA,EACvB;AAAA,EACA;AAAA,EACA,QAAQ;AAAA,EACR;AAAA,EACA,MAAM;AAAA,EACN;AAAA,EACA;AACF,GAAkB;AAChB,QAAM,CAAC,cAAc,eAAe,IAAIC,UAAS,KAAK;AACtD,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,EAAE,CAAC;AACtE,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAE5C,QAAM,aAAaC,QAAuB,IAAI;AAC9C,QAAM,UAAUA,QAAuB,IAAI;AAG3C,QAAM,eAAe,mBAAmB;AACxC,QAAM,SAAS,eAAe,iBAAiB;AAE/C,QAAM,UAAUC,aAAY,CAAC,UAAmB;AAC9C,QAAI,CAAC,cAAc;AACjB,sBAAgB,KAAK;AAAA,IACvB;AACA,mBAAe,KAAK;AAAA,EACtB,GAAG,CAAC,cAAc,YAAY,CAAC;AAG/B,EAAAC,WAAU,MAAM;AACd,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,CAAC;AAGL,QAAM,iBAAiBD,aAAY,MAAM;AACvC,QAAI,WAAW,SAAS;AACtB,YAAM,OAAO,WAAW,QAAQ,sBAAsB;AACtD,kBAAY;AAAA,QACV,KAAK,KAAK,SAAS;AAAA;AAAA,QACnB,MAAM,KAAK;AAAA,QACX,OAAO,OAAO,aAAa,KAAK;AAAA,MAClC,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,EAAAC,WAAU,MAAM;AACd,QAAI,QAAQ;AACV,qBAAe;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,CAAC;AAG3B,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAEb,UAAM,cAAc,CAAC,MAAkB;AACrC,YAAM,SAAS,EAAE;AACjB,UACE,QAAQ,WAAW,CAAC,QAAQ,QAAQ,SAAS,MAAM,KACnD,WAAW,WAAW,CAAC,WAAW,QAAQ,SAAS,MAAM,GACzD;AACA,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAEA,aAAS,iBAAiB,aAAa,WAAW;AAClD,WAAO,MAAM,SAAS,oBAAoB,aAAa,WAAW;AAAA,EACpE,GAAG,CAAC,QAAQ,OAAO,CAAC;AAGpB,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAEb,UAAM,gBAAgB,CAAC,MAAqB;AAC1C,UAAI,EAAE,QAAQ,UAAU;AACtB,gBAAQ,KAAK;AAAA,MACf;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,aAAa;AAClD,WAAO,MAAM,SAAS,oBAAoB,WAAW,aAAa;AAAA,EACpE,GAAG,CAAC,QAAQ,OAAO,CAAC;AAGpB,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,OAAQ;AAEb,UAAM,eAAe,MAAM,eAAe;AAC1C,WAAO,iBAAiB,UAAU,cAAc,IAAI;AACpD,WAAO,iBAAiB,UAAU,YAAY;AAC9C,WAAO,MAAM;AACX,aAAO,oBAAoB,UAAU,cAAc,IAAI;AACvD,aAAO,oBAAoB,UAAU,YAAY;AAAA,IACnD;AAAA,EACF,GAAG,CAAC,QAAQ,cAAc,CAAC;AAE3B,QAAM,qBAAqB,MAAM;AAC/B,QAAI,SAAU;AACd,YAAQ,CAAC,MAAM;AAAA,EACjB;AAEA,QAAM,QAAQD,aAAY,MAAM,QAAQ,KAAK,GAAG,CAAC,OAAO,CAAC;AAEzD,QAAM,OAAO,UAAU,UAAU;AAAA,IAC/B,gBAAAL,MAAC,gBAAgB,UAAhB,EAAyB,OAAO,EAAE,MAAM,GACvC,0BAAAA;AAAA,MAAC;AAAA;AAAA,QACC,KAAK;AAAA,QACL,OAAO;AAAA,UACL,UAAU;AAAA,UACV,KAAK,SAAS;AAAA,UACd,GAAI,UAAU,UACV,EAAE,OAAO,SAAS,MAAM,IACxB,EAAE,MAAM,SAAS,KAAK;AAAA,QAE5B;AAAA,QACA,WAAW;AAAA,UACT;AAAA,UACA;AAAA,QACF;AAAA,QACA,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,QAEjC;AAAA;AAAA,IACH,GACF;AAAA,IACA,SAAS;AAAA,EACX,IAAI;AAEJ,SACE,gBAAAC,MAAAF,WAAA,EACE;AAAA,oBAAAC,MAAC,SAAI,KAAK,YAAY,SAAS,oBAC5B,mBACH;AAAA,IACC;AAAA,KACH;AAEJ;AAcO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsB;AACpB,QAAM,UAAUO,YAAW,eAAe;AAE1C,QAAM,cAAc,MAAM;AACxB,QAAI,CAAC,UAAU;AACb,gBAAU;AACV,eAAS,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,SACE,gBAAAP;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,QACA,eAAe;AAAA,QACf,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,EACH;AAEJ;AAMO,SAAS,kBAAkB;AAChC,SAAO,gBAAAA,MAAC,SAAI,WAAU,uBAAsB;AAC9C;AAMO,SAAS,cAAc,EAAE,SAAS,GAAkC;AACzE,SACE,gBAAAA,MAAC,SAAI,WAAU,yEACZ,UACH;AAEJ;;;AD/LQ,gBAAAQ,OA8BI,QAAAC,aA9BJ;AA1BD,SAAS,wBAAwB;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAU;AACR,QAAM,CAAC,MAAM,OAAO,IAAIC,UAAS,KAAK;AAEtC,QAAM,mBAAmB,CAAC,WAAoB;AAC5C,QAAI,UAAU,CAAC,MAAM;AACnB,aAAO;AAAA,IACT;AACA,YAAQ,MAAM;AAAA,EAChB;AAEA,QAAM,UACJ,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU,YAAY,iBAAiB;AAAA,MACvC,OAAO,WAAW,gCAAgC;AAAA,MAClD,WAAU;AAAA,MAET,2BACC,gBAAAA,MAACG,UAAA,EAAQ,WAAU,wBAAuB,IAE1C,gBAAAH,MAAC,WAAQ,WAAU,WAAU;AAAA;AAAA,EAEjC;AAGF,SACE,gBAAAC;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd,UAAU,YAAY,iBAAiB;AAAA,MACvC,OAAM;AAAA,MACN,WAAU;AAAA,MAEV;AAAA,wBAAAD,MAAC,iBAAc,8BAAgB;AAAA,QAC/B,gBAAAA,MAAC,SAAI,WAAU,0BAAyB;AAAA,QAEvC,UACC,gBAAAA,MAAC,SAAI,WAAU,yCACb,0BAAAA,MAACG,UAAA,EAAQ,WAAU,sCAAqC,GAC1D,IACE,UAAU,WAAW,IACvB,gBAAAH,MAAC,SAAI,WAAU,0CAAyC,8BAExD,IAEA,UAAU,IAAI,CAAC,QACb,gBAAAA,MAAC,gBAA0B,SAAS,MAAM,SAAS,IAAI,EAAE,GACvD,0BAAAC,MAAC,SAAI,WAAU,iBACb;AAAA,0BAAAD,MAAC,UAAK,WAAU,oBAAoB,cAAI,SAAS,YAAW;AAAA,UAC5D,gBAAAA,MAAC,UAAK,WAAU,yBACb,6BAAmB,IAAI,SAAS,GACnC;AAAA,WACF,KANiB,IAAI,EAOvB,CACD;AAAA;AAAA;AAAA,EAEL;AAEJ;;;AD9BM,qBAAAI,WAEE,OAAAC,OAFF,QAAAC,aAAA;AAlCC,SAAS,eAAe;AAAA,EAC7B,QAAQ;AAAA,EACR;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AAEtB,QAAM,SAAS;AAEf,QAAM,aAAaC,aAAY,MAAM;AACnC,QAAI,QAAQ;AACV,aAAO,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI;AAAA,IACpC,WAAW,aAAa,SAAS;AAC/B,kBAAY,QAAQ,MAAM;AAC1B,eAAS,YAAY,MAAM;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,QAAQ,WAAW,CAAC;AAExB,QAAM,aAAaA,aAAY,MAAM;AACnC,QAAI,QAAQ;AACV,aAAO,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI;AAAA,IACpC,WAAW,aAAa,SAAS;AAC/B,kBAAY,QAAQ,MAAM;AAC1B,eAAS,YAAY,MAAM;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,QAAQ,WAAW,CAAC;AAGxB,MAAI,SAAS;AACX,WACE,gBAAAD,MAAAF,WAAA,EAEE;AAAA,sBAAAC,MAAC,kBAAe;AAAA,MAChB,gBAAAA,MAAC,kBAAe;AAAA,MAEhB,gBAAAA,MAAC,WAAQ;AAAA,MACT,gBAAAA,MAAC,kBAAe;AAAA,MAEhB,gBAAAA,MAAC,WAAQ;AAAA,MACT,gBAAAA,MAAC,kBAAe;AAAA,OAClB;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAAF,WAAA,EAEE;AAAA,oBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,UAAU,iBAAiB,SAAS,CAAC,OAAO,IAAI,EAAE,KAAK,IAAI;AAAA,QAC3D,OAAM;AAAA,QAEN,0BAAAA,MAAC,SAAM,WAAU,WAAU;AAAA;AAAA,IAC7B;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,UAAU,iBAAiB,SAAS,CAAC,OAAO,IAAI,EAAE,KAAK,IAAI;AAAA,QAC3D,OAAM;AAAA,QAEN,0BAAAA,MAAC,SAAM,WAAU,WAAU;AAAA;AAAA,IAC7B;AAAA,IAGC,mBACC,gBAAAC,MAAAF,WAAA,EACE;AAAA,sBAAAC,MAAC,WAAQ;AAAA,MACT,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,gBAAgB,CAAC,YAAY;AAAA,UAC5C,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,OAAO,eAAe,+BAA+B;AAAA,UAErD,0BAAAA,MAAC,UAAK,WAAU,qBAAoB,gBAAE;AAAA;AAAA,MACxC;AAAA,OACF;AAAA,IAID,aACC,gBAAAC,MAAAF,WAAA,EACE;AAAA,sBAAAC,MAAC,WAAQ;AAAA,MACT,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,UAAU;AAAA,UACrB,SAAS,UAAU;AAAA,UACnB,gBAAgB,UAAU;AAAA,UAC1B,UAAU,gBAAgB,CAAC;AAAA,UAC3B,eAAe,CAAC,CAAC,UAAU;AAAA,UAC3B,QAAQ,UAAU;AAAA,UAClB,UAAU,UAAU;AAAA;AAAA,MACtB;AAAA,OACF;AAAA,KAEJ;AAEJ;;;AR3DM,SACE,OAAAG,OADF,QAAAC,aAAA;AAtBC,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,cAAc;AAChB,GAAuB;AAGrB,MAAI,SAAS;AACX,WACE,gBAAAA,MAAC,SAAI,WAAU,mLACb;AAAA,sBAAAD,MAAC,iBAAc,SAAS,MAAM;AAAA,MAC9B,gBAAAA,MAAC,WAAQ;AAAA,MACT,gBAAAA,MAAC,gBAAa,SAAS,MAAM;AAAA,MAC7B,gBAAAA,MAAC,WAAQ;AAAA,MACT,gBAAAA,MAAC,gBAAa,SAAS,MAAM;AAAA,MAC7B,gBAAAA,MAAC,WAAQ;AAAA,MACT,gBAAAA,MAAC,kBAAe,SAAS,MAAM;AAAA,MAE/B,gBAAAA,MAAC,WAAQ;AAAA,MACT,gBAAAA,MAAC,kBAAe;AAAA,MAChB,gBAAAA,MAAC,kBAAe;AAAA,OAClB;AAAA,EAEJ;AAEA,SACE,gBAAAC,MAAC,SAAI,WAAU,mLACb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAEA,gBAAAA,MAAC,WAAQ;AAAA,IAET,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAEA,gBAAAA,MAAC,WAAQ;AAAA,IAET,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAEA,gBAAAA,MAAC,WAAQ;AAAA,IAET,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA;AAAA,IACF;AAAA,IAGA,gBAAAA,MAAC,WAAQ;AAAA,IACT,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,iBAAiB,MAAM;AAAA,QAAC;AAAA,QACjC,UAAU,gBAAgB,CAAC,gBAAgB,CAAC;AAAA,QAC5C,OACE,eACI,gCACA,sBACE,+BACA;AAAA,QAGR,0BAAAA,MAAC,qBAAkB,WAAU,WAAU;AAAA;AAAA,IACzC;AAAA,IACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS,mBAAmB,MAAM;AAAA,QAAC;AAAA,QACnC,UAAU,gBAAgB,CAAC;AAAA,QAC3B,OAAM;AAAA,QAEN,0BAAAC,MAAC,UAAK,WAAU,2BACd;AAAA,0BAAAD,MAAC,iBAAc,WAAU,WAAU;AAAA,UAClC,kBAAkB,UAAa,gBAAgB,KAC9C,gBAAAA,MAAC,UAAK,WAAU,wBAAwB,yBAAc;AAAA,WAE1D;AAAA;AAAA,IACF;AAAA,KACF;AAEJ;;;AWnJA,SAAS,aAAAE,YAAW,WAAAC,gBAA4B;AAChD,SAAS,WAAW,qBAA6B;AACjD,OAAO,gBAAgB;AACvB,OAAO,iBAAiB;AACxB,OAAO,UAAU;AACjB,OAAOC,YAAW;;;ACPlB,SAAS,cAAc;AACvB,OAAO,qBAAqB;AAG5B,OAAO,WAAW;AAAA,EAChB,KAAK;AAAA,EACL,QAAQ;AACV,CAAC;AAKM,SAAS,eAAe,UAA0B;AACvD,SAAO,OAAO,MAAM,QAAQ;AAC9B;AAMO,SAAS,eAAe,UAA0B;AACvD,SAAO,OAAO,MAAM,UAAU,EAAE,KAAK,MAAM,QAAQ,KAAK,CAAC;AAC3D;AAUA,IAAM,kBAAkB,IAAI,gBAAgB;AAAA,EAC1C,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,kBAAkB;AACpB,CAAC;AAKM,SAAS,eAAe,MAAsB;AACnD,SAAO,gBAAgB,SAAS,IAAI;AACtC;;;AM3CA,SAAS,kBAAkB;AEA3B,SAAS,uBAAuB,mCAAmC;ACAnE,SAAS,qBAAqB;AKA9B,SAAS,mBAAmB,+BAA+B;AEA3D,SAAS,YAAY,wBAAwB;ACC7C,SAAS,iBAAAC,sBAAqB;AMD9B,SAAS,iBAAAA,sBAAqB;ACC9B,SAAS,WAAW,iBAAAA,sBAAqB;AQAzC,SAAS,YAAAC,kBAAgB;ACAzB,SAAS,WAAW,YAAAA,WAAU,QAAQ,iBAAiB,cAAc;AEArE,SAAS,aAAAC,kBAAiB;AAC1B,SAAS,mBAAmB,mBAAmB;ACF/C;EACE,gBAAgB;EAChB,YAAY;EACZ,eAAe;EACf,UAAU;OACL;ACLP,SAAS,iBAAiB;ACA1B,SAAS,aAAAC,kBAAiB;ACA1B,SAAS,yBAAyB,uBAAuB;ACAzD,SAAS,wBAAwBC,wBAAuB;AGAxD,SAAS,QAAQ,oBAAoB;AEArC,SAAS,kBAAkB,8BAA8B;ACCzD,SAAS,gBAAgB,4BAA4B;ACDrD,SAAS,iBAAiB,6BAA6B;AKAvD,SAAS,oBAAoB;ACA7B,SAAS,sBAAsB,kCAAkC;ACAjE,SAAS,qBAAqB,iCAAiC;ACA/D,SAAS,oBAAoB,gCAAgC;ACE7D,SAAS,sBAAsB,kCAAkC;ACAjE,SAAS,wBAAwB,oCAAoC;AIArE,SAAS,iBAAiB;AQF1B,SAAS,YAAY;ACCrB,SAAS,qBAAqB;ACA9B,SAAS,UAAAC,eAAc;AcDvB,SAAS,aAAAC,kBAAiB;ACA1B,SAAS,QAAAC,aAAY;AqBArB,SAAS,qBAAqB;AMA9B,SAAS,oBAAoB;ACA7B,SAAS,iBAAAC,sBAAqB;AEA9B,SAAS,iBAAAR,sBAAqB;ACC9B,SAAS,gBAAgB,4BAA4B;ACArD,SAAS,iBAAAQ,gBAAe,iBAAAR,sBAAqB;AAC7C,SAAS,gBAAgB;ACDzB,SAAS,YAAAC,YAAU,aAAa;AAChC,SAAS,iBAAAD,sBAAqB;AAC9B,SAAS,YAAAS,iBAAgB;ACDzB,SAAS,eAAe;ASFxB,SAAS,UAAU,sBAAsB;ACCzC,SAAS,cAAc,0BAA0B;ACEjD,SAAS,mBAAmB;AAC5B,SAAS,kBAAkB;AEJ3B,SAAS,cAAc;ACCvB,SAAS,YAAAR,kBAAgB;AAEzB,SAAS,cAAc;AKFvB,SAAS,YAAAA,kBAAgB;AAEzB,SAAS,UAAAS,eAAc;AGHvB,SAAS,UAAAA,SAAQ,iBAAiB;AGAlC,SAAS,sBAAsB;ACA/B,SAAS,UAAAA,SAAQ,aAAAC,kBAAiB;ACAlC,SAAS,UAAAD,SAAQ,aAAAC,kBAAiB;ACAlC,SAAS,UAAAD,SAAQ,aAAAC,kBAAiB;ACAlC,SAAS,UAAAD,SAAQ,aAAAC,YAAW,aAAAT,kBAAiB;ACA7C,SAAS,UAAAQ,SAAQ,aAAAC,kBAAiB;ACAlC,SAAS,UAAAD,SAAQ,aAAAC,kBAAiB;ACAlC,SAAS,UAAAD,UAAQ,aAAAC,kBAAiB;AQClC,SAAS,WAAAC,UAAS,oBAAoB;AGAtC,SAA2B,iBAAAJ,sBAAqB;AYDhD,SAAS,iBAAAA,sBAAqB;;;;;;A9KOvB,SAAS,qBAAqB,QAAuE;AAC1G,QAAM,EAAE,OAAO,YAAY,IAAI;AAC/B,MAAI,EAAE,UAAU,IAAI;AACpB,MAAI,EAAE,IAAI,IAAI;AACd,MAAI,EAAE,YAAY,IAAI;AAEtB,SAAO;IACL,GAAG;IACH,OAAO,MAAM,MAAM,KAAK,KAAK;IAC7B,kBAAkB,MAAM,iBAAiB,KAAK,KAAK;IACnD,SAAS,MAAM;IACf,QAAQ,MAAM;IACd,aAAa,MAAM,YAAY,KAAK,KAAK;IACzC,QAAQ,MAAM,OAAO,KAAK,KAAK;IAC/B,IAAI,cAAc;AAChB,aAAO;IACT;IACA,IAAI,YAAY;AACd,aAAO;IACT;IACA,IAAI,MAAM;AACR,aAAO;IACT;IACA,IAAI,KAAK;AACP,kBAAY,YAAY;AACxB,YAAM,YAAY;AAClB,oBAAc,YAAY;AAE1B,aAAO;IACT;EACF;AACF;AChCO,IAAM,iBAAN,MAAqB;EAO1B,YAAY,OAAgD;AAC1D,SAAK,SAAS,MAAM;AACpB,SAAK,cAAc,KAAK,OAAO,iBAAiB;AAChD,SAAK,cAAc,MAAM;EAC3B;EAEA,IAAI,iBAA0B;AAC5B,WAAO,CAAC,CAAC,KAAK;EAChB;EAEA,IAAI,QAAqB;AACvB,WAAO,KAAK,eAAe,KAAK,OAAO;EACzC;EAEA,IAAI,WAA2B;AAC7B,UAAM,EAAE,aAAa,QAAQ,MAAM,IAAI;AACvC,UAAM,EAAE,KAAK,IAAI;AACjB,UAAM,EAAE,GAAG,IAAI;AACf,UAAM,QAAQ,KAAK,WAAW,EAAE;AAEhC,WAAO,OAAO;MACZ,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,CAAC,MAAMK,QAAO,MAAM;AACnD,cAAM,SAAS,IAAI,SAAgB;AACjC,gBAAM,WAAWA,SAAQ,GAAG,IAAI,EAAE,KAAK;AAEvC,cAAI,CAAC,GAAG,QAAQ,iBAAiB,KAAK,CAAC,KAAK,gBAAgB;AAC1D,iBAAK,SAAS,EAAE;UAClB;AAEA,iBAAO;QACT;AAEA,eAAO,CAAC,MAAM,MAAM;MACtB,CAAC;IACH;EACF;EAEA,IAAI,QAA+B;AACjC,WAAO,MAAM,KAAK,YAAY;EAChC;EAEA,IAAI,MAAyB;AAC3B,WAAO,MAAM,KAAK,UAAU;EAC9B;EAEO,YAAY,SAAuB,iBAAiB,MAAuB;AAChF,UAAM,EAAE,aAAa,QAAQ,MAAM,IAAI;AACvC,UAAM,EAAE,KAAK,IAAI;AACjB,UAAM,YAAuB,CAAC;AAC9B,UAAM,sBAAsB,CAAC,CAAC;AAC9B,UAAM,KAAK,WAAW,MAAM;AAE5B,UAAMC,OAAM,MAAM;AAChB,UAAI,CAAC,uBAAuB,kBAAkB,CAAC,GAAG,QAAQ,iBAAiB,KAAK,CAAC,KAAK,gBAAgB;AACpG,aAAK,SAAS,EAAE;MAClB;AAEA,aAAO,UAAU,MAAM,CAAA,aAAY,aAAa,IAAI;IACtD;AAEA,UAAM,QAAQ;MACZ,GAAG,OAAO;QACR,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,CAAC,MAAMD,QAAO,MAAM;AACnD,gBAAM,iBAAiB,IAAI,SAAkB;AAC3C,kBAAM,QAAQ,KAAK,WAAW,IAAI,cAAc;AAChD,kBAAM,WAAWA,SAAQ,GAAG,IAAI,EAAE,KAAK;AAEvC,sBAAU,KAAK,QAAQ;AAEvB,mBAAO;UACT;AAEA,iBAAO,CAAC,MAAM,cAAc;QAC9B,CAAC;MACH;MACA,KAAAC;IACF;AAEA,WAAO;EACT;EAEO,UAAU,SAAoC;AACnD,UAAM,EAAE,aAAa,MAAM,IAAI;AAC/B,UAAM,WAAW;AACjB,UAAM,KAAK,WAAW,MAAM;AAC5B,UAAM,QAAQ,KAAK,WAAW,IAAI,QAAQ;AAC1C,UAAM,oBAAoB,OAAO;MAC/B,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,CAAC,MAAMD,QAAO,MAAM;AACnD,eAAO,CAAC,MAAM,IAAI,SAAkBA,SAAQ,GAAG,IAAI,EAAE,EAAE,GAAG,OAAO,UAAU,OAAU,CAAC,CAAC;MACzF,CAAC;IACH;AAEA,WAAO;MACL,GAAG;MACH,OAAO,MAAM,KAAK,YAAY,IAAI,QAAQ;IAC5C;EACF;EAEO,WAAW,IAAiB,iBAAiB,MAAoB;AACtE,UAAM,EAAE,aAAa,QAAQ,MAAM,IAAI;AACvC,UAAM,EAAE,KAAK,IAAI;AAEjB,UAAM,QAAsB;MAC1B;MACA;MACA;MACA,OAAO,qBAAqB;QAC1B;QACA,aAAa;MACf,CAAC;MACD,UAAU,iBAAiB,MAAM,SAAY;MAC7C,OAAO,MAAM,KAAK,YAAY,IAAI,cAAc;MAChD,KAAK,MAAM,KAAK,UAAU,EAAE;MAC5B,IAAI,WAAW;AACb,eAAO,OAAO;UACZ,OAAO,QAAQ,WAAW,EAAE,IAAI,CAAC,CAAC,MAAMA,QAAO,MAAM;AACnD,mBAAO,CAAC,MAAM,IAAI,SAAkBA,SAAQ,GAAG,IAAI,EAAE,KAAK,CAAC;UAC7D,CAAC;QACH;MACF;IACF;AAEA,WAAO;EACT;AACF;ACzIA,IAAA,mBAAA,CAAA;AAAA,SAAA,kBAAA;EAAA,MAAA,MAAA;EAAA,cAAA,MAAA;EAAA,YAAA,MAAA;EAAA,SAAA,MAAA;EAAA,qBAAA,MAAA;EAAA,KAAA,MAAA;EAAA,mBAAA,MAAA;EAAA,YAAA,MAAA;EAAA,aAAA,MAAA;EAAA,iBAAA,MAAA;EAAA,OAAA,MAAA;EAAA,UAAA,MAAA;EAAA,iBAAA,MAAA;EAAA,OAAA,MAAA;EAAA,OAAA,MAAA;EAAA,SAAA,MAAA;EAAA,eAAA,MAAA;EAAA,iBAAA,MAAA;EAAA,cAAA,MAAA;EAAA,UAAA,MAAA;EAAA,aAAA,MAAA;EAAA,kBAAA,MAAA;EAAA,iBAAA,MAAA;EAAA,uBAAA,MAAA;EAAA,sBAAA,MAAA;EAAA,QAAA,MAAA;EAAA,kBAAA,MAAA;EAAA,MAAA,MAAA;EAAA,gBAAA,MAAA;EAAA,cAAA,MAAA;EAAA,eAAA,MAAA;EAAA,iBAAA,MAAA;EAAA,gBAAA,MAAA;EAAA,WAAA,MAAA;EAAA,oBAAA,MAAA;EAAA,mBAAA,MAAA;EAAA,kBAAA,MAAA;EAAA,oBAAA,MAAA;EAAA,sBAAA,MAAA;EAAA,YAAA,MAAA;EAAA,SAAA,MAAA;EAAA,SAAA,MAAA;EAAA,SAAA,MAAA;EAAA,kBAAA,MAAA;EAAA,kBAAA,MAAA;EAAA,kBAAA,MAAA;EAAA,cAAA,MAAA;EAAA,YAAA,MAAA;EAAA,eAAA,MAAA;EAAA,YAAA,MAAA;EAAA,YAAA,MAAA;EAAA,YAAA,MAAA;EAAA,YAAA,MAAA;EAAA,eAAA,MAAA;EAAA,eAAA,MAAA;EAAA,WAAA,MAAA;EAAA,oBAAA,MAAA;EAAA,kBAAA,MAAA;EAAA,QAAA,MAAA;EAAA,YAAA,MAAA;AAAA,CAAA;ACcO,IAAM,OACX,MACA,CAAC,EAAE,QAAQ,KAAK,MAAM;AACpB,wBAAsB,MAAM;AAjBhC,QAAA;AAkBM,QAAI,CAAC,OAAO,aAAa;AACvB;AAAE,WAAK,IAAoB,KAAK;AAIhC,OAAA,KAAA,UAAA,OAAA,SAAA,OAAQ,aAAA,MAAR,OAAA,SAAA,GAAwB,gBAAA;IAC1B;EACF,CAAC;AAED,SAAO;AACT;ACRK,IAAM,eACX,CAAC,aAAa,SACd,CAAC,EAAE,SAAS,MAAM;AAChB,SAAO,SAAS,WAAW,IAAI,EAAE,WAAW,CAAC;AAC/C;ACRK,IAAM,aACX,MACA,CAAC,EAAE,OAAO,IAAI,SAAS,MAAM;AAC3B,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,EAAE,OAAO,IAAI;AAEnB,MAAI,CAAC,UAAU;AACb,WAAO;EACT;AAEA,SAAO,QAAQ,CAAC,EAAE,OAAO,IAAI,MAAM;AACjC,UAAM,IAAI,aAAa,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,QAAQ;AACxD,UAAI,KAAK,KAAK,QAAQ;AACpB;MACF;AAEA,YAAM,EAAE,KAAK,QAAQ,IAAI;AACzB,YAAM,cAAc,IAAI,QAAQ,QAAQ,IAAI,GAAG,CAAC;AAChD,YAAM,YAAY,IAAI,QAAQ,QAAQ,IAAI,MAAM,KAAK,QAAQ,CAAC;AAC9D,YAAM,YAAY,YAAY,WAAW,SAAS;AAElD,UAAI,CAAC,WAAW;AACd;MACF;AAEA,YAAM,kBAAkB,WAAW,SAAS;AAE5C,UAAI,KAAK,KAAK,aAAa;AACzB,cAAM,EAAE,YAAY,IAAI,YAAY,OAAO,eAAe,YAAY,MAAM,CAAC;AAE7E,WAAG,cAAc,UAAU,OAAO,WAAW;MAC/C;AAEA,UAAI,mBAAmB,oBAAoB,GAAG;AAC5C,WAAG,KAAK,WAAW,eAAe;MACpC;IACF,CAAC;EACH,CAAC;AAED,SAAO;AACT;ACrCK,IAAM,UAAkC,CAAA,OAAM,CAAA,UAAS;AAC5D,SAAO,GAAG,KAAK;AACjB;ACLO,IAAM,sBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,4BAA4B,OAAO,QAAQ;AACpD;ACAK,IAAM,MACX,CAAC,aAAa,cACd,CAAC,EAAE,QAAQ,GAAG,MAAM;AAClB,QAAM,EAAE,MAAM,IAAI;AAElB,QAAM,eAAe,MAAM,IAAI,MAAM,YAAY,MAAM,YAAY,EAAE;AAErE,KAAG,YAAY,YAAY,MAAM,YAAY,EAAE;AAC/C,QAAM,SAAS,GAAG,QAAQ,IAAI,SAAS;AAEvC,KAAG,OAAO,QAAQ,aAAa,OAAO;AAEtC,KAAG,aAAa,IAAI,cAAc,GAAG,IAAI,QAAQ,KAAK,IAAI,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC;AAE1E,SAAO;AACT;ACrBK,IAAM,oBACX,MACA,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,cAAc,UAAU,QAAQ,KAAK;AAG3C,MAAI,YAAY,QAAQ,OAAO,GAAG;AAChC,WAAO;EACT;AAEA,QAAM,OAAO,GAAG,UAAU;AAE1B,WAAS,QAAQ,KAAK,OAAO,QAAQ,GAAG,SAAS,GAAG;AAClD,UAAM,OAAO,KAAK,KAAK,KAAK;AAE5B,QAAI,KAAK,SAAS,YAAY,MAAM;AAClC,UAAI,UAAU;AACZ,cAAM,OAAO,KAAK,OAAO,KAAK;AAC9B,cAAM,KAAK,KAAK,MAAM,KAAK;AAE3B,WAAG,OAAO,MAAM,EAAE,EAAE,eAAe;MACrC;AAEA,aAAO;IACT;EACF;AAEA,SAAO;AACT;ACzCK,SAAS,YAAY,YAA+B,QAA0B;AACnF,MAAI,OAAO,eAAe,UAAU;AAClC,QAAI,CAAC,OAAO,MAAM,UAAU,GAAG;AAC7B,YAAM,MAAM,gCAAgC,UAAU,2CAA2C;IACnG;AAEA,WAAO,OAAO,MAAM,UAAU;EAChC;AAEA,SAAO;AACT;ACMO,IAAM,aACX,CAAA,eACA,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAC3B,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAM,OAAO,GAAG,UAAU;AAE1B,WAAS,QAAQ,KAAK,OAAO,QAAQ,GAAG,SAAS,GAAG;AAClD,UAAM,OAAO,KAAK,KAAK,KAAK;AAE5B,QAAI,KAAK,SAAS,MAAM;AACtB,UAAI,UAAU;AACZ,cAAM,OAAO,KAAK,OAAO,KAAK;AAC9B,cAAM,KAAK,KAAK,MAAM,KAAK;AAE3B,WAAG,OAAO,MAAM,EAAE,EAAE,eAAe;MACrC;AAEA,aAAO;IACT;EACF;AAEA,SAAO;AACT;ACzBK,IAAM,cACX,CAAA,UACA,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,QAAM,EAAE,MAAM,GAAG,IAAI;AAErB,MAAI,UAAU;AACZ,OAAG,OAAO,MAAM,EAAE;EACpB;AAEA,SAAO;AACT;ACTK,IAAM,kBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,wBAAwB,OAAO,QAAQ;AAChD;ACNK,IAAM,QACX,MACA,CAAC,EAAE,SAAS,MAAM;AAChB,SAAO,SAAS,iBAAiB,OAAO;AAC1C;ACFK,IAAM,WACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,iBAAiB,OAAO,QAAQ;AACzC;AEpBK,SAAS,SAAS,OAA6B;AACpD,SAAO,OAAO,UAAU,SAAS,KAAK,KAAK,MAAM;AACnD;ACKO,SAAS,eACd,SACA,SACA,UAA+B,EAAE,QAAQ,KAAK,GACrC;AACT,QAAM,OAAO,OAAO,KAAK,OAAO;AAEhC,MAAI,CAAC,KAAK,QAAQ;AAChB,WAAO;EACT;AAEA,SAAO,KAAK,MAAM,CAAA,QAAO;AACvB,QAAI,QAAQ,QAAQ;AAClB,aAAO,QAAQ,GAAG,MAAM,QAAQ,GAAG;IACrC;AAEA,QAAI,SAAS,QAAQ,GAAG,CAAC,GAAG;AAC1B,aAAO,QAAQ,GAAG,EAAE,KAAK,QAAQ,GAAG,CAAC;IACvC;AAEA,WAAO,QAAQ,GAAG,MAAM,QAAQ,GAAG;EACrC,CAAC;AACH;ACxBA,SAAS,cACP,OACA,MACA,aAAkC,CAAC,GACN;AAC7B,SAAO,MAAM,KAAK,CAAA,SAAQ;AACxB,WACE,KAAK,SAAS,QACd;;MAEE,OAAO,YAAY,OAAO,KAAK,UAAU,EAAE,IAAI,CAAA,MAAK,CAAC,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC;MACvE;IACF;EAEJ,CAAC;AACH;AAEA,SAAS,YAAY,OAA0B,MAAgB,aAAkC,CAAC,GAAY;AAC5G,SAAO,CAAC,CAAC,cAAc,OAAO,MAAM,UAAU;AAChD;AAKO,SAAS,aAId,MAIA,MAKA,YACc;AA3ChB,MAAA;AA4CE,MAAI,CAAC,QAAQ,CAAC,MAAM;AAClB;EACF;AACA,MAAI,QAAQ,KAAK,OAAO,WAAW,KAAK,YAAY;AAGpD,MAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,KAAK,MAAM,KAAK,CAAAE,UAAQA,MAAK,SAAS,IAAI,GAAG;AACrE,YAAQ,KAAK,OAAO,YAAY,KAAK,YAAY;EACnD;AAGA,MAAI,CAAC,MAAM,QAAQ,CAAC,MAAM,KAAK,MAAM,KAAK,CAAAA,UAAQA,MAAK,SAAS,IAAI,GAAG;AACrE;EACF;AAGA,eAAa,gBAAc,KAAA,MAAM,KAAK,MAAM,CAAC,MAAlB,OAAA,SAAA,GAAqB;AAIhD,QAAM,OAAO,cAAc,CAAC,GAAG,MAAM,KAAK,KAAK,GAAG,MAAM,UAAU;AAElE,MAAI,CAAC,MAAM;AACT;EACF;AAEA,MAAI,aAAa,MAAM;AACvB,MAAI,WAAW,KAAK,MAAM,IAAI,MAAM;AACpC,MAAI,WAAW,aAAa;AAC5B,MAAI,SAAS,WAAW,MAAM,KAAK;AAEnC,SAAO,aAAa,KAAK,YAAY,CAAC,GAAG,KAAK,OAAO,MAAM,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,UAAU,GAAG;AACpG,kBAAc;AACd,gBAAY,KAAK,OAAO,MAAM,UAAU,EAAE;EAC5C;AAEA,SAAO,WAAW,KAAK,OAAO,cAAc,YAAY,CAAC,GAAG,KAAK,OAAO,MAAM,QAAQ,EAAE,KAAK,GAAG,MAAM,UAAU,GAAG;AACjH,cAAU,KAAK,OAAO,MAAM,QAAQ,EAAE;AACtC,gBAAY;EACd;AAEA,SAAO;IACL,MAAM;IACN,IAAI;EACN;AACF;ACvFO,SAAS,YAAY,YAA+B,QAA0B;AACnF,MAAI,OAAO,eAAe,UAAU;AAClC,QAAI,CAAC,OAAO,MAAM,UAAU,GAAG;AAC7B,YAAM,MAAM,gCAAgC,UAAU,2CAA2C;IACnG;AAEA,WAAO,OAAO,MAAM,UAAU;EAChC;AAEA,SAAO;AACT;AJoBO,IAAM,kBACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAC3B,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAM,EAAE,KAAK,UAAU,IAAI;AAC3B,QAAM,EAAE,OAAO,MAAM,GAAG,IAAI;AAE5B,MAAI,UAAU;AACZ,UAAM,QAAQ,aAAa,OAAO,MAAM,UAAU;AAElD,QAAI,SAAS,MAAM,QAAQ,QAAQ,MAAM,MAAM,IAAI;AACjD,YAAM,eAAef,eAAc,OAAO,KAAK,MAAM,MAAM,MAAM,EAAE;AAEnE,SAAG,aAAa,YAAY;IAC9B;EACF;AAEA,SAAO;AACT;AKnCK,IAAM,QAA8B,CAAA,aAAY,CAAA,UAAS;AAC9D,QAAM,QAAQ,OAAO,aAAa,aAAa,SAAS,KAAK,IAAI;AAEjE,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACxC,QAAI,MAAM,CAAC,EAAE,KAAK,GAAG;AACnB,aAAO;IACT;EACF;AAEA,SAAO;AACT;ACvBO,SAAS,gBAAgB,OAAwC;AACtE,SAAO,iBAAiBA;AAC1B;AEJO,SAAS,OAAO,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAW;AAC1D,SAAO,KAAK,IAAI,KAAK,IAAI,OAAO,GAAG,GAAG,GAAG;AAC3C;ADIO,SAAS,qBAAqB,KAAsB,WAA0B,MAAwB;AAC3G,MAAI,CAAC,UAAU;AACb,WAAO;EACT;AAEA,QAAM,mBAAmB,UAAU,QAAQ,GAAG;AAC9C,QAAM,iBAAiB,UAAU,MAAM,GAAG;AAE1C,MAAI,aAAa,WAAW,aAAa,MAAM;AAC7C,WAAO;EACT;AAEA,MAAI,aAAa,OAAO;AACtB,WAAO;EACT;AAEA,QAAM,SAAS,iBAAiB;AAChC,QAAM,SAAS,eAAe;AAE9B,MAAI,aAAa,OAAO;AACtB,WAAOA,eAAc,OAAO,KAAK,OAAO,GAAG,QAAQ,MAAM,GAAG,OAAO,IAAI,QAAQ,MAAM,QAAQ,MAAM,CAAC;EACtG;AAEA,SAAOA,eAAc,OAAO,KAAK,OAAO,UAAU,QAAQ,MAAM,GAAG,OAAO,UAAU,QAAQ,MAAM,CAAC;AACrG;AE9BO,SAAS,YAAqB;AACnC,SAAO,UAAU,aAAa,aAAa,WAAW,KAAK,UAAU,SAAS;AAChF;ACFO,SAAS,QAAiB;AAC/B,SACE,CAAC,kBAAkB,oBAAoB,kBAAkB,QAAQ,UAAU,MAAM,EAAE,SAAS,UAAU,QAAQ;EAE7G,UAAU,UAAU,SAAS,KAAK,KAAK,gBAAgB;AAE5D;ACEO,SAAS,WAAoB;AAClC,SAAO,OAAO,cAAc,cAAc,iCAAiC,KAAK,UAAU,SAAS,IAAI;AACzG;ACyBO,IAAM,QACX,CAAC,WAAW,MAAM,UAAU,CAAC,MAC7B,CAAC,EAAE,QAAQ,MAAM,IAAI,SAAS,MAAM;AAClC,YAAU;IACR,gBAAgB;IAChB,GAAG;EACL;AAEA,QAAM,eAAe,MAAM;AAGzB,QAAI,MAAM,KAAK,UAAU,GAAG;AAC1B;AAAE,WAAK,IAAoB,MAAM;IACnC;AAMA,QAAI,SAAS,KAAK,CAAC,MAAM,KAAK,CAAC,UAAU,GAAG;AAC1C;AAAE,WAAK,IAAoB,MAAM,EAAE,eAAe,KAAK,CAAC;IAC1D;AAIA,0BAAsB,MAAM;AAC1B,UAAI,CAAC,OAAO,aAAa;AACvB,aAAK,MAAM;AAEX,YAAI,WAAA,OAAA,SAAA,QAAS,gBAAgB;AAC3B,iBAAO,SAAS,eAAe;QACjC;MACF;IACF,CAAC;EACH;AAEA,MAAK,KAAK,SAAS,KAAK,aAAa,QAAS,aAAa,OAAO;AAChE,WAAO;EACT;AAGA,MAAI,YAAY,aAAa,QAAQ,CAAC,gBAAgB,OAAO,MAAM,SAAS,GAAG;AAC7E,iBAAa;AACb,WAAO;EACT;AAIA,QAAM,YAAY,qBAAqB,GAAG,KAAK,QAAQ,KAAK,OAAO,MAAM;AACzE,QAAM,kBAAkB,OAAO,MAAM,UAAU,GAAG,SAAS;AAE3D,MAAI,UAAU;AACZ,QAAI,CAAC,iBAAiB;AACpB,SAAG,aAAa,SAAS;IAC3B;AAIA,QAAI,mBAAmB,GAAG,aAAa;AACrC,SAAG,eAAe,GAAG,WAAW;IAClC;AAEA,iBAAa;EACf;AAEA,SAAO;AACT;AChFK,IAAM,UAAkC,CAAC,OAAO,OAAO,CAAA,UAAS;AACrE,SAAO,MAAM,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,EAAE,GAAG,OAAO,MAAM,CAAC,CAAC;AACnE;ACkBO,IAAM,gBACX,CAAC,OAAO,YACR,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,SAAO,SAAS,gBAAgB,EAAE,MAAM,GAAG,UAAU,MAAM,IAAI,GAAG,UAAU,GAAG,GAAG,OAAO,OAAO;AAClG;AG7CF,IAAM,oBAAoB,CAAC,SAAsB;AAC/C,QAAM,WAAW,KAAK;AAEtB,WAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AAChD,UAAM,QAAQ,SAAS,CAAC;AAExB,QAAI,MAAM,aAAa,KAAK,MAAM,aAAa,gBAAgB,KAAK,MAAM,SAAS,GAAG;AACpF,WAAK,YAAY,KAAK;IACxB,WAAW,MAAM,aAAa,GAAG;AAC/B,wBAAkB,KAAoB;IACxC;EACF;AAEA,SAAO;AACT;AAEO,SAAS,kBAAkB,OAA4B;AAC5D,MAAI,OAAO,WAAW,aAAa;AACjC,UAAM,IAAI,MAAM,sFAAsF;EACxG;AAEA,QAAM,eAAe,SAAS,KAAK;AAEnC,QAAM,OAAO,IAAI,OAAO,UAAU,EAAE,gBAAgB,cAAc,WAAW,EAAE;AAE/E,SAAO,kBAAkB,IAAI;AAC/B;ADPO,SAAS,sBACd,SACA,QACA,SAC4B;AAC5B,MAAI,mBAAmB,mBAAmB,mBAAmBC,WAAU;AACrE,WAAO;EACT;AACA,YAAU;IACR,OAAO;IACP,cAAc,CAAC;IACf,GAAG;EACL;AAEA,QAAM,gBAAgB,OAAO,YAAY,YAAY,YAAY;AACjE,QAAM,gBAAgB,OAAO,YAAY;AAEzC,MAAI,eAAe;AACjB,QAAI;AACF,YAAM,iBAAiB,MAAM,QAAQ,OAAO,KAAK,QAAQ,SAAS;AAGlE,UAAI,gBAAgB;AAClB,eAAOA,UAAS,UAAU,QAAQ,IAAI,CAAA,SAAQ,OAAO,aAAa,IAAI,CAAC,CAAC;MAC1E;AAEA,YAAM,OAAO,OAAO,aAAa,OAAO;AAExC,UAAI,QAAQ,uBAAuB;AACjC,aAAK,MAAM;MACb;AAEA,aAAO;IACT,SAAS,OAAO;AACd,UAAI,QAAQ,uBAAuB;AACjC,cAAM,IAAI,MAAM,wCAAwC,EAAE,OAAO,MAAe,CAAC;MACnF;AAEA,cAAQ,KAAK,mCAAmC,iBAAiB,SAAS,UAAU,KAAK;AAEzF,aAAO,sBAAsB,IAAI,QAAQ,OAAO;IAClD;EACF;AAEA,MAAI,eAAe;AAEjB,QAAI,QAAQ,uBAAuB;AACjC,UAAI,oBAAoB;AACxB,UAAI,iBAAiB;AAGrB,YAAM,qBAAqB,IAAI,OAAO;QACpC,SAAS,OAAO,KAAK;QACrB,OAAO,OAAO,KAAK;;;QAGnB,OAAO,OAAO,KAAK,MAAM,OAAO;UAC9B,8CAA8C;YAC5C,SAAS;YACT,OAAO;YACP,UAAU;cACR;gBACE,KAAK;gBACL,UAAU,CAAA,MAAK;AAEb,sCAAoB;AAEpB,mCAAiB,OAAO,MAAM,WAAW,IAAI,EAAE;AAC/C,yBAAO;gBACT;cACF;YACF;UACF;QACF,CAAC;MACH,CAAC;AAED,UAAI,QAAQ,OAAO;AACjB,kBAAU,WAAW,kBAAkB,EAAE,WAAW,kBAAkB,OAAO,GAAG,QAAQ,YAAY;MACtG,OAAO;AACL,kBAAU,WAAW,kBAAkB,EAAE,MAAM,kBAAkB,OAAO,GAAG,QAAQ,YAAY;MACjG;AAEA,UAAI,QAAQ,yBAAyB,mBAAmB;AACtD,cAAM,IAAI,MAAM,wCAAwC;UACtD,OAAO,IAAI,MAAM,0BAA0B,cAAc,EAAE;QAC7D,CAAC;MACH;IACF;AAEA,UAAM,SAAS,UAAU,WAAW,MAAM;AAE1C,QAAI,QAAQ,OAAO;AACjB,aAAO,OAAO,WAAW,kBAAkB,OAAO,GAAG,QAAQ,YAAY,EAAE;IAC7E;AAEA,WAAO,OAAO,MAAM,kBAAkB,OAAO,GAAG,QAAQ,YAAY;EACtE;AAEA,SAAO,sBAAsB,IAAI,QAAQ,OAAO;AAClD;AEjHO,SAAS,wBAAwB,IAAiB,UAAkB,MAAc;AACvF,QAAM,OAAO,GAAG,MAAM,SAAS;AAE/B,MAAI,OAAO,UAAU;AACnB;EACF;AAEA,QAAM,OAAO,GAAG,MAAM,IAAI;AAE1B,MAAI,EAAE,gBAAgB,eAAe,gBAAgB,oBAAoB;AACvE;EACF;AAEA,QAAM,MAAM,GAAG,QAAQ,KAAK,IAAI;AAChC,MAAI,MAAM;AAEV,MAAI,QAAQ,CAAC,OAAO,KAAK,UAAU,UAAU;AAC3C,QAAI,QAAQ,GAAG;AACb,YAAM;IACR;EACF,CAAC;AAED,KAAG,aAAaC,WAAU,KAAK,GAAG,IAAI,QAAQ,GAAG,GAAG,IAAI,CAAC;AAC3D;AHiCA,IAAM,aAAa,CAAC,mBAA2E;AAC7F,SAAO,EAAE,UAAU;AACrB;AAEO,IAAM,kBACX,CAAC,UAAU,OAAO,YAClB,CAAC,EAAE,IAAI,UAAU,OAAO,MAAM;AAnEhC,MAAA;AAoEI,MAAI,UAAU;AACZ,cAAU;MACR,cAAc,OAAO,QAAQ;MAC7B,iBAAiB;MACjB,iBAAiB;MACjB,iBAAiB;MACjB,GAAG;IACL;AAEA,QAAI;AAEJ,UAAM,mBAAmB,CAAC,UAAiB;AACzC,aAAO,KAAK,gBAAgB;QAC1B;QACA;QACA,sBAAsB,MAAM;AAC1B,cACE,mBAAmB,OAAO,WAC1B,OAAO,OAAO,QAAQ,kBAAkB,YACxC,OAAO,QAAQ,eACf;AACA;AAAE,mBAAO,QAAQ,cAAsB,aAAa;UACtD;QACF;MACF,CAAC;IACH;AAEA,UAAM,eAA6B;MACjC,oBAAoB;MACpB,GAAG,QAAQ;IACb;AAIA,QAAI,CAAC,QAAQ,yBAAyB,CAAC,OAAO,QAAQ,sBAAsB,OAAO,QAAQ,kBAAkB;AAC3G,UAAI;AACF,8BAAsB,OAAO,OAAO,QAAQ;UAC1C;UACA,uBAAuB;QACzB,CAAC;MACH,SAAS,GAAG;AACV,yBAAiB,CAAU;MAC7B;IACF;AAEA,QAAI;AACF,gBAAU,sBAAsB,OAAO,OAAO,QAAQ;QACpD;QACA,wBAAuB,KAAA,QAAQ,0BAAR,OAAA,KAAiC,OAAO,QAAQ;MACzE,CAAC;IACH,SAAS,GAAG;AACV,uBAAiB,CAAU;AAC3B,aAAO;IACT;AAEA,QAAI,EAAE,MAAM,GAAG,IACb,OAAO,aAAa,WAAW,EAAE,MAAM,UAAU,IAAI,SAAS,IAAI,EAAE,MAAM,SAAS,MAAM,IAAI,SAAS,GAAG;AAE3G,QAAI,oBAAoB;AACxB,QAAI,qBAAqB;AACzB,UAAM,QAAQ,WAAW,OAAO,IAAI,UAAU,CAAC,OAAO;AAEtD,UAAM,QAAQ,CAAA,SAAQ;AAEpB,WAAK,MAAM;AAEX,0BAAoB,oBAAoB,KAAK,UAAU,KAAK,MAAM,WAAW,IAAI;AAEjF,2BAAqB,qBAAqB,KAAK,UAAU;IAC3D,CAAC;AAOD,QAAI,SAAS,MAAM,oBAAoB;AACrC,YAAM,EAAE,OAAO,IAAI,GAAG,IAAI,QAAQ,IAAI;AACtC,YAAM,mBAAmB,OAAO,eAAe,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,OAAO;AAEjF,UAAI,kBAAkB;AACpB,gBAAQ;AACR,cAAM;MACR;IACF;AAEA,QAAI;AAIJ,QAAI,mBAAmB;AAGrB,UAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,qBAAa,MAAM,IAAI,CAAA,MAAK,EAAE,QAAQ,EAAE,EAAE,KAAK,EAAE;MACnD,WAAW,iBAAiBD,YAAU;AACpC,YAAI,OAAO;AAEX,cAAM,QAAQ,CAAA,SAAQ;AACpB,cAAI,KAAK,MAAM;AACb,oBAAQ,KAAK;UACf;QACF,CAAC;AAED,qBAAa;MACf,WAAW,OAAO,UAAU,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC,MAAM,MAAM;AAC/D,qBAAa,MAAM;MACrB,OAAO;AACL,qBAAa;MACf;AAEA,SAAG,WAAW,YAAY,MAAM,EAAE;IACpC,OAAO;AACL,mBAAa;AAEb,YAAM,QAAQ,GAAG,IAAI,QAAQ,IAAI;AACjC,YAAM,YAAY,MAAM,KAAK;AAC7B,YAAM,uBAAuB,MAAM,iBAAiB;AACpD,YAAMe,mBAAkB,UAAU,UAAU,UAAU;AACtD,YAAM,aAAa,UAAU,QAAQ,OAAO;AAE5C,UAAI,wBAAwBA,oBAAmB,YAAY;AACzD,eAAO,KAAK,IAAI,GAAG,OAAO,CAAC;MAC7B;AAEA,SAAG,YAAY,MAAM,IAAI,UAAU;IACrC;AAGA,QAAI,QAAQ,iBAAiB;AAC3B,8BAAwB,IAAI,GAAG,MAAM,SAAS,GAAG,EAAE;IACrD;AAEA,QAAI,QAAQ,iBAAiB;AAC3B,SAAG,QAAQ,mBAAmB,EAAE,MAAM,MAAM,WAAW,CAAC;IAC1D;AAEA,QAAI,QAAQ,iBAAiB;AAC3B,SAAG,QAAQ,mBAAmB,EAAE,MAAM,MAAM,WAAW,CAAC;IAC1D;EACF;AAEA,SAAO;AACT;AIrKK,IAAM,SACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,eAAe,OAAO,QAAQ;AACvC;AAEK,IAAM,WACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,iBAAiB,OAAO,QAAQ;AACzC;AAEK,IAAM,eACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,qBAAqB,OAAO,QAAQ;AAC7C;AAEK,IAAM,cACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,oBAAoB,OAAO,QAAQ;AAC5C;ACpDK,IAAM,mBACX,MACA,CAAC,EAAE,OAAO,UAAU,GAAG,MAAM;AAC3B,MAAI;AACF,UAAM,QAAQ,UAAU,MAAM,KAAK,MAAM,UAAU,MAAM,KAAK,EAAE;AAEhE,QAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,aAAO;IACT;AAEA,OAAG,KAAK,OAAO,CAAC;AAEhB,QAAI,UAAU;AACZ,eAAS,EAAE;IACb;AAEA,WAAO;EACT,QAAQ;AACN,WAAO;EACT;AACF;ACpBK,IAAM,kBACX,MACA,CAAC,EAAE,OAAO,UAAU,GAAG,MAAM;AAC3B,MAAI;AACF,UAAM,QAAQb,WAAU,MAAM,KAAK,MAAM,UAAU,MAAM,KAAK,CAAE;AAEhE,QAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,aAAO;IACT;AAEA,OAAG,KAAK,OAAO,CAAC;AAEhB,QAAI,UAAU;AACZ,eAAS,EAAE;IACb;AAEA,WAAO;EACT,QAAQ;AACN,WAAO;EACT;AACF;ACrBK,IAAM,wBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,gBAAgB,OAAO,QAAQ;AACxC;ACJK,IAAM,uBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAOC,iBAAgB,OAAO,QAAQ;AACxC;ACnBK,SAAS,UAAmB;AACjC,SAAO,OAAO,cAAc,cAAc,MAAM,KAAK,UAAU,QAAQ,IAAI;AAC7E;ACEA,SAAS,iBAAiB,MAAc;AACtC,QAAM,QAAQ,KAAK,MAAM,QAAQ;AACjC,MAAI,SAAS,MAAM,MAAM,SAAS,CAAC;AAEnC,MAAI,WAAW,SAAS;AACtB,aAAS;EACX;AAEA,MAAI;AACJ,MAAI;AACJ,MAAI;AACJ,MAAI;AAEJ,WAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG;AAC5C,UAAM,MAAM,MAAM,CAAC;AAEnB,QAAI,kBAAkB,KAAK,GAAG,GAAG;AAC/B,aAAO;IACT,WAAW,YAAY,KAAK,GAAG,GAAG;AAChC,YAAM;IACR,WAAW,sBAAsB,KAAK,GAAG,GAAG;AAC1C,aAAO;IACT,WAAW,cAAc,KAAK,GAAG,GAAG;AAClC,cAAQ;IACV,WAAW,SAAS,KAAK,GAAG,GAAG;AAC7B,UAAI,MAAM,KAAK,QAAQ,GAAG;AACxB,eAAO;MACT,OAAO;AACL,eAAO;MACT;IACF,OAAO;AACL,YAAM,IAAI,MAAM,+BAA+B,GAAG,EAAE;IACtD;EACF;AAEA,MAAI,KAAK;AACP,aAAS,OAAO,MAAM;EACxB;AAEA,MAAI,MAAM;AACR,aAAS,QAAQ,MAAM;EACzB;AAEA,MAAI,MAAM;AACR,aAAS,QAAQ,MAAM;EACzB;AAEA,MAAI,OAAO;AACT,aAAS,SAAS,MAAM;EAC1B;AAEA,SAAO;AACT;AAeO,IAAM,mBACX,CAAA,SACA,CAAC,EAAE,QAAQ,MAAM,IAAI,SAAS,MAAM;AAClC,QAAM,OAAO,iBAAiB,IAAI,EAAE,MAAM,QAAQ;AAClD,QAAM,MAAM,KAAK,KAAK,CAAA,SAAQ,CAAC,CAAC,OAAO,QAAQ,QAAQ,OAAO,EAAE,SAAS,IAAI,CAAC;AAC9E,QAAM,QAAQ,IAAI,cAAc,WAAW;IACzC,KAAK,QAAQ,UAAU,MAAM;IAC7B,QAAQ,KAAK,SAAS,KAAK;IAC3B,SAAS,KAAK,SAAS,MAAM;IAC7B,SAAS,KAAK,SAAS,MAAM;IAC7B,UAAU,KAAK,SAAS,OAAO;IAC/B,SAAS;IACT,YAAY;EACd,CAAC;AAED,QAAM,sBAAsB,OAAO,mBAAmB,MAAM;AAC1D,SAAK,SAAS,iBAAiB,CAAA,MAAK,EAAE,MAAM,KAAK,CAAC;EACpD,CAAC;AAED,yBAAA,OAAA,SAAA,oBAAqB,MAAM,QAAQ,CAAA,SAAQ;AACzC,UAAM,UAAU,KAAK,IAAI,GAAG,OAAO;AAEnC,QAAI,WAAW,UAAU;AACvB,SAAG,UAAU,OAAO;IACtB;EACF,CAAA;AAEA,SAAO;AACT;AE5FK,SAAS,aACd,OACA,YACA,aAAkC,CAAC,GAC1B;AACT,QAAM,EAAE,MAAM,IAAI,MAAM,IAAI,MAAM;AAClC,QAAM,OAAO,aAAa,YAAY,YAAY,MAAM,MAAM,IAAI;AAElE,QAAM,aAA0B,CAAC;AAEjC,QAAM,IAAI,aAAa,MAAM,IAAI,CAAC,MAAM,QAAQ;AAC9C,QAAI,KAAK,QAAQ;AACf;IACF;AAEA,UAAM,eAAe,KAAK,IAAI,MAAM,GAAG;AACvC,UAAM,aAAa,KAAK,IAAI,IAAI,MAAM,KAAK,QAAQ;AAEnD,eAAW,KAAK;MACd;MACA,MAAM;MACN,IAAI;IACN,CAAC;EACH,CAAC;AAED,QAAM,iBAAiB,KAAK;AAC5B,QAAM,oBAAoB,WACvB,OAAO,CAAA,cAAa;AACnB,QAAI,CAAC,MAAM;AACT,aAAO;IACT;AAEA,WAAO,KAAK,SAAS,UAAU,KAAK,KAAK;EAC3C,CAAC,EACA,OAAO,CAAA,cAAa,eAAe,UAAU,KAAK,OAAO,YAAY,EAAE,QAAQ,MAAM,CAAC,CAAC;AAE1F,MAAI,OAAO;AACT,WAAO,CAAC,CAAC,kBAAkB;EAC7B;AAEA,QAAM,QAAQ,kBAAkB,OAAO,CAAC,KAAK,cAAc,MAAM,UAAU,KAAK,UAAU,MAAM,CAAC;AAEjG,SAAO,SAAS;AAClB;AD5BO,IAAM,OACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAMa,YAAW,aAAa,OAAO,MAAM,UAAU;AAErD,MAAI,CAACA,WAAU;AACb,WAAO;EACT;AAEA,SAAO,aAAa,OAAO,QAAQ;AACrC;AEjBK,IAAM,iBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,uBAAuB,OAAO,QAAQ;AAC/C;ACDK,IAAM,eACX,CAAA,eACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AAEjD,SAAO,qBAAqB,IAAI,EAAE,OAAO,QAAQ;AACnD;ACTK,IAAM,gBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,sBAAsB,OAAO,QAAQ;AAC9C;ACZK,SAAS,wBAAwB,MAAc,QAAwC;AAC5F,MAAI,OAAO,MAAM,IAAI,GAAG;AACtB,WAAO;EACT;AAEA,MAAI,OAAO,MAAM,IAAI,GAAG;AACtB,WAAO;EACT;AAEA,SAAO;AACT;ACbO,SAAS,YAAY,KAA0B,aAAqD;AACzG,QAAM,QAAQ,OAAO,gBAAgB,WAAW,CAAC,WAAW,IAAI;AAEhE,SAAO,OAAO,KAAK,GAAG,EAAE,OAAO,CAAC,QAA6B,SAAS;AACpE,QAAI,CAAC,MAAM,SAAS,IAAI,GAAG;AACzB,aAAO,IAAI,IAAI,IAAI,IAAI;IACzB;AAEA,WAAO;EACT,GAAG,CAAC,CAAC;AACP;ACOO,IAAM,kBACX,CAAC,YAAY,eACb,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAC3B,MAAI,WAA4B;AAChC,MAAI,WAA4B;AAEhC,QAAM,aAAa;IACjB,OAAO,eAAe,WAAW,aAAa,WAAW;IACzD,MAAM;EACR;AAEA,MAAI,CAAC,YAAY;AACf,WAAO;EACT;AAEA,MAAI,eAAe,QAAQ;AACzB,eAAW,YAAY,YAAwB,MAAM,MAAM;EAC7D;AAEA,MAAI,eAAe,QAAQ;AACzB,eAAW,YAAY,YAAwB,MAAM,MAAM;EAC7D;AAEA,MAAI,WAAW;AAEf,KAAG,UAAU,OAAO,QAAQ,CAAA,UAAS;AACnC,UAAM,IAAI,aAAa,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK,CAAC,MAAM,QAAQ;AACpE,UAAI,YAAY,aAAa,KAAK,MAAM;AACtC,mBAAW;AAEX,YAAI,UAAU;AACZ,aAAG,cAAc,KAAK,QAAW,YAAY,KAAK,OAAO,UAAU,CAAC;QACtE;MACF;AAEA,UAAI,YAAY,KAAK,MAAM,QAAQ;AACjC,aAAK,MAAM,QAAQ,CAAA,SAAQ;AACzB,cAAI,aAAa,KAAK,MAAM;AAC1B,uBAAW;AAEX,gBAAI,UAAU;AACZ,iBAAG,QAAQ,KAAK,MAAM,KAAK,UAAU,SAAS,OAAO,YAAY,KAAK,OAAO,UAAU,CAAC,CAAC;YAC3F;UACF;QACF,CAAC;MACH;IACF,CAAC;EACH,CAAC;AAED,SAAO;AACT;AC1DK,IAAM,iBACX,MACA,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,MAAI,UAAU;AACZ,OAAG,eAAe;EACpB;AAEA,SAAO;AACT;ACNK,IAAM,YACX,MACA,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,MAAI,UAAU;AACZ,UAAM,YAAY,IAAI,aAAa,GAAG,GAAG;AAEzC,OAAG,aAAa,SAAS;EAC3B;AAEA,SAAO;AACT;ACVK,IAAM,qBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,2BAA2B,OAAO,QAAQ;AACnD;ACJK,IAAM,oBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,0BAA0B,OAAO,QAAQ;AAClD;ACJK,IAAM,mBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,yBAAyB,OAAO,QAAQ;AACjD;ACFK,IAAM,qBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,2BAA2B,OAAO,QAAQ;AACnD;ACJK,IAAM,uBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,SAAO,6BAA6B,OAAO,QAAQ;AACrD;ACVK,SAAS,eACd,SACA,QACA,eAA6B,CAAC,GAC9B,UAA+C,CAAC,GAC/B;AACjB,SAAO,sBAAsB,SAAS,QAAQ;IAC5C,OAAO;IACP;IACA,uBAAuB,QAAQ;EACjC,CAAC;AACH;AC0BO,IAAM,aACX,CAAC,SAAS,EAAE,uBAAuB,aAAa,MAAM,eAAe,CAAC,EAAE,IAAI,CAAC,MAC7E,CAAC,EAAE,QAAQ,IAAI,UAAU,SAAS,MAAM;AACtC,QAAM,EAAE,IAAI,IAAI;AAIhB,MAAI,aAAa,uBAAuB,QAAQ;AAC9C,UAAMC,YAAW,eAAe,SAAS,OAAO,QAAQ,cAAc;MACpE,uBAAuB,yBAAA,OAAA,wBAAyB,OAAO,QAAQ;IACjE,CAAC;AAED,QAAI,UAAU;AACZ,SAAG,YAAY,GAAG,IAAI,QAAQ,MAAMA,SAAQ,EAAE,QAAQ,iBAAiB,CAAC,UAAU;IACpF;AACA,WAAO;EACT;AAEA,MAAI,UAAU;AACZ,OAAG,QAAQ,iBAAiB,CAAC,UAAU;EACzC;AAEA,SAAO,SAAS,gBAAgB,EAAE,MAAM,GAAG,IAAI,IAAI,QAAQ,KAAK,GAAG,SAAS;IAC1E;IACA,uBAAuB,yBAAA,OAAA,wBAAyB,OAAO,QAAQ;EACjE,CAAC;AACH;ACtEK,SAAS,kBAAkB,OAAoB,YAAoD;AACxG,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAM,EAAE,MAAM,IAAI,MAAM,IAAI,MAAM;AAClC,QAAM,QAAgB,CAAC;AAEvB,MAAI,OAAO;AACT,QAAI,MAAM,aAAa;AACrB,YAAM,KAAK,GAAG,MAAM,WAAW;IACjC;AAEA,UAAM,KAAK,GAAG,MAAM,UAAU,MAAM,MAAM,CAAC;EAC7C,OAAO;AACL,UAAM,IAAI,aAAa,MAAM,IAAI,CAAA,SAAQ;AACvC,YAAM,KAAK,GAAG,KAAK,KAAK;IAC1B,CAAC;EACH;AAEA,QAAM,OAAO,MAAM,KAAK,CAAA,aAAY,SAAS,KAAK,SAAS,KAAK,IAAI;AAEpE,MAAI,CAAC,MAAM;AACT,WAAO,CAAC;EACV;AAEA,SAAO,EAAE,GAAG,KAAK,MAAM;AACzB;ACnBO,SAAS,wBAAwB,QAAyB,cAAwC;AACvG,QAAM,YAAY,IAAI,UAAU,MAAM;AAEtC,eAAa,QAAQ,CAAA,gBAAe;AAClC,gBAAY,MAAM,QAAQ,CAAA,SAAQ;AAChC,gBAAU,KAAK,IAAI;IACrB,CAAC;EACH,CAAC;AAED,SAAO;AACT;ACbO,SAAS,eAAe,OAAsC;AACnE,WAAS,IAAI,GAAG,IAAI,MAAM,WAAW,KAAK,GAAG;AAC3C,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,CAAC;AAE7B,QAAI,KAAK,eAAe,CAAC,KAAK,iBAAiB,GAAG;AAChD,aAAO;IACT;EACF;AAEA,SAAO;AACT;AGJO,SAAS,2BACd,MACA,WAQY;AACZ,WAAS,IAAI,KAAK,OAAO,IAAI,GAAG,KAAK,GAAG;AACtC,UAAM,OAAO,KAAK,KAAK,CAAC;AAExB,QAAI,UAAU,IAAI,GAAG;AACnB,aAAO;QACL,KAAK,IAAI,IAAI,KAAK,OAAO,CAAC,IAAI;QAC9B,OAAO,KAAK,MAAM,CAAC;QACnB,OAAO;QACP;MACF;IACF;EACF;AACF;ACvBO,SAAS,eACd,WACyE;AACzE,SAAO,CAAC,cAAyB,2BAA2B,UAAU,OAAO,SAAS;AACxF;ACLO,SAAS,kBACd,WACA,OACA,SACe;AACf,MAAI,UAAU,OAAO,KAAsC,MAAM,UAAa,UAAU,QAAQ;AAC9F,WAAO,kBAAkB,UAAU,QAAQ,OAAO,OAAO;EAC3D;AAEA,MAAI,OAAO,UAAU,OAAO,KAAsC,MAAM,YAAY;AAClF,UAAM,QAAS,UAAU,OAAO,KAAsC,EAAU,KAAK;MACnF,GAAG;MACH,QAAQ,UAAU,SAAS,kBAAkB,UAAU,QAAQ,OAAO,OAAO,IAAI;IACnF,CAAC;AAED,WAAO;EACT;AAEA,SAAO,UAAU,OAAO,KAAsC;AAChE;ACvBO,SAAS,kBAAkB,YAAoC;AACpE,SACE,WACG,IAAI,CAAA,cAAa;AAChB,UAAM,UAAU;MACd,MAAM,UAAU;MAChB,SAAS,UAAU;MACnB,SAAS,UAAU;IACrB;AAEA,UAAM,gBAAgB,kBAA8C,WAAW,iBAAiB,OAAO;AAEvG,QAAI,eAAe;AACjB,aAAO,CAAC,WAAW,GAAG,kBAAkB,cAAc,CAAC,CAAC;IAC1D;AAEA,WAAO;EACT,CAAC,EAEA,KAAK,EAAE;AAEd;AE1BO,SAAS,oBAAoB,UAAoB,QAAwB;AAC9E,QAAM,mBAAmB,cAAc,WAAW,MAAM,EAAE,kBAAkB,QAAQ;AAEpF,QAAM,oBAAoB,SAAS,eAAe,mBAAmB;AACrE,QAAM,YAAY,kBAAkB,cAAc,KAAK;AAEvD,YAAU,YAAY,gBAAgB;AAEtC,SAAO,UAAU;AACnB;AEXO,SAAS,WAAW,OAA+B;AACxD,SAAO,OAAO,UAAU;AAC1B;ACOO,SAAS,aAAgB,OAAU,UAAe,WAAc,OAAkC;AACvG,MAAI,WAAW,KAAK,GAAG;AACrB,QAAI,SAAS;AACX,aAAO,MAAM,KAAK,OAAO,EAAE,GAAG,KAAK;IACrC;AAEA,WAAO,MAAM,GAAG,KAAK;EACvB;AAEA,SAAO;AACT;ACpBO,SAAS,cAAc,QAAQ,CAAC,GAAY;AACjD,SAAO,OAAO,KAAK,KAAK,EAAE,WAAW,KAAK,MAAM,gBAAgB;AAClE;ACGO,SAAS,gBAAgB,YAAwB;AACtD,QAAM,iBAAiB,WAAW,OAAO,CAAA,cAAa,UAAU,SAAS,WAAW;AACpF,QAAM,iBAAiB,WAAW,OAAO,CAAA,cAAa,UAAU,SAAS,MAAM;AAC/E,QAAM,iBAAiB,WAAW,OAAO,CAAA,cAAa,UAAU,SAAS,MAAM;AAE/E,SAAO;IACL;IACA;IACA;EACF;AACF;ACNO,SAAS,4BAA4B,YAA8C;AACxF,QAAM,sBAA4C,CAAC;AACnD,QAAM,EAAE,gBAAgB,eAAe,IAAI,gBAAgB,UAAU;AACrE,QAAM,wBAAwB,CAAC,GAAG,gBAAgB,GAAG,cAAc;AACnE,QAAM,mBAAwF;IAC5F,SAAS;IACT,UAAU;IACV,UAAU;IACV,YAAY;IACZ,WAAW;IACX,aAAa;IACb,YAAY;EACd;AAEA,aAAW,QAAQ,CAAA,cAAa;AAC9B,UAAM,UAAU;MACd,MAAM,UAAU;MAChB,SAAS,UAAU;MACnB,SAAS,UAAU;MACnB,YAAY;IACd;AAEA,UAAM,sBAAsB;MAC1B;MACA;MACA;IACF;AAEA,QAAI,CAAC,qBAAqB;AACxB;IACF;AAEA,UAAM,mBAAmB,oBAAoB;AAE7C,qBAAiB,QAAQ,CAAA,oBAAmB;AAC1C,sBAAgB,MAAM,QAAQ,CAAA,SAAQ;AACpC,eAAO,QAAQ,gBAAgB,UAAU,EAAE,QAAQ,CAAC,CAAC,MAAM,SAAS,MAAM;AACxE,8BAAoB,KAAK;YACvB;YACA;YACA,WAAW;cACT,GAAG;cACH,GAAG;YACL;UACF,CAAC;QACH,CAAC;MACH,CAAC;IACH,CAAC;EACH,CAAC;AAED,wBAAsB,QAAQ,CAAA,cAAa;AACzC,UAAM,UAAU;MACd,MAAM,UAAU;MAChB,SAAS,UAAU;MACnB,SAAS,UAAU;IACrB;AAEA,UAAM,gBAAgB;MACpB;MACA;MACA;IACF;AAEA,QAAI,CAAC,eAAe;AAClB;IACF;AAGA,UAAM,aAAa,cAAc;AAEjC,WAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,MAAM,SAAS,MAAM;AACxD,YAAM,aAAa;QACjB,GAAG;QACH,GAAG;MACL;AAEA,UAAI,QAAO,cAAA,OAAA,SAAA,WAAY,aAAY,YAAY;AAC7C,mBAAW,UAAU,WAAW,QAAQ;MAC1C;AAEA,WAAI,cAAA,OAAA,SAAA,WAAY,gBAAc,cAAA,OAAA,SAAA,WAAY,aAAY,QAAW;AAC/D,eAAO,WAAW;MACpB;AAEA,0BAAoB,KAAK;QACvB,MAAM,UAAU;QAChB;QACA,WAAW;MACb,CAAC;IACH,CAAC;EACH,CAAC;AAED,SAAO;AACT;ACtGO,SAAS,mBAAmB,SAAqD;AACtF,SAAO,QACJ,OAAO,CAAA,SAAQ,CAAC,CAAC,IAAI,EACrB,OAAO,CAAC,OAAO,SAAS;AACvB,UAAM,mBAAmB,EAAE,GAAG,MAAM;AAEpC,WAAO,QAAQ,IAAI,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AAC7C,YAAM,SAAS,iBAAiB,GAAG;AAEnC,UAAI,CAAC,QAAQ;AACX,yBAAiB,GAAG,IAAI;AAExB;MACF;AAEA,UAAI,QAAQ,SAAS;AACnB,cAAM,eAAyB,QAAQ,OAAO,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;AACnE,cAAM,kBAA4B,iBAAiB,GAAG,IAAI,iBAAiB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;AAE9F,cAAM,gBAAgB,aAAa,OAAO,CAAA,eAAc,CAAC,gBAAgB,SAAS,UAAU,CAAC;AAE7F,yBAAiB,GAAG,IAAI,CAAC,GAAG,iBAAiB,GAAG,aAAa,EAAE,KAAK,GAAG;MACzE,WAAW,QAAQ,SAAS;AAC1B,cAAM,YAAsB,QACxB,MACG,MAAM,GAAG,EACT,IAAI,CAACC,WAAkBA,OAAM,KAAK,CAAC,EACnC,OAAO,OAAO,IACjB,CAAC;AACL,cAAM,iBAA2B,iBAAiB,GAAG,IACjD,iBAAiB,GAAG,EACjB,MAAM,GAAG,EACT,IAAI,CAACA,WAAkBA,OAAM,KAAK,CAAC,EACnC,OAAO,OAAO,IACjB,CAAC;AAEL,cAAM,WAAW,oBAAI,IAAoB;AAEzC,uBAAe,QAAQ,CAAAA,WAAS;AAC9B,gBAAM,CAAC,UAAU,GAAG,IAAIA,OAAM,MAAM,GAAG,EAAE,IAAI,CAAA,SAAQ,KAAK,KAAK,CAAC;AAEhE,mBAAS,IAAI,UAAU,GAAG;QAC5B,CAAC;AAED,kBAAU,QAAQ,CAAAA,WAAS;AACzB,gBAAM,CAAC,UAAU,GAAG,IAAIA,OAAM,MAAM,GAAG,EAAE,IAAI,CAAA,SAAQ,KAAK,KAAK,CAAC;AAEhE,mBAAS,IAAI,UAAU,GAAG;QAC5B,CAAC;AAED,yBAAiB,GAAG,IAAI,MAAM,KAAK,SAAS,QAAQ,CAAC,EAClD,IAAI,CAAC,CAAC,UAAU,GAAG,MAAM,GAAG,QAAQ,KAAK,GAAG,EAAE,EAC9C,KAAK,IAAI;MACd,OAAO;AACL,yBAAiB,GAAG,IAAI;MAC1B;IACF,CAAC;AAED,WAAO;EACT,GAAG,CAAC,CAAC;AACT;ACvDO,SAAS,sBACd,YACA,qBACqB;AACrB,SAAO,oBACJ,OAAO,CAAA,cAAa,UAAU,SAAS,WAAW,KAAK,IAAI,EAC3D,OAAO,CAAA,SAAQ,KAAK,UAAU,QAAQ,EACtC,IAAI,CAAA,SAAQ;AACX,QAAI,CAAC,KAAK,UAAU,YAAY;AAC9B,aAAO;QACL,CAAC,KAAK,IAAI,GAAG,WAAW,MAAM,KAAK,IAAI;MACzC;IACF;AAEA,WAAO,KAAK,UAAU,WAAW,WAAW,KAAK,KAAK,CAAC;EACzD,CAAC,EACA,OAAO,CAAC,YAAY,cAAc,gBAAgB,YAAY,SAAS,GAAG,CAAC,CAAC;AACjF;ACtBO,SAAS,WAAW,OAAiB;AAC1C,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;EACT;AAEA,MAAI,MAAM,MAAM,sBAAsB,GAAG;AACvC,WAAO,OAAO,KAAK;EACrB;AAEA,MAAI,UAAU,QAAQ;AACpB,WAAO;EACT;AAEA,MAAI,UAAU,SAAS;AACrB,WAAO;EACT;AAEA,SAAO;AACT;ACPO,SAAS,qCACd,WACA,qBACW;AACX,MAAI,WAAW,WAAW;AACxB,WAAO;EACT;AAEA,SAAO;IACL,GAAG;IACH,UAAU,CAAC,SAAsB;AAC/B,YAAM,gBAAgB,UAAU,WAAW,UAAU,SAAS,IAAI,IAAI,UAAU;AAEhF,UAAI,kBAAkB,OAAO;AAC3B,eAAO;MACT;AAEA,YAAM,gBAAgB,oBAAoB,OAAO,CAAC,OAAO,SAAS;AAChE,cAAM,QAAQ,KAAK,UAAU,YACzB,KAAK,UAAU,UAAU,IAAI,IAC7B,WAAW,KAAK,aAAa,KAAK,IAAI,CAAC;AAE3C,YAAI,UAAU,QAAQ,UAAU,QAAW;AACzC,iBAAO;QACT;AAEA,eAAO;UACL,GAAG;UACH,CAAC,KAAK,IAAI,GAAG;QACf;MACF,GAAG,CAAC,CAAC;AAEL,aAAO,EAAE,GAAG,eAAe,GAAG,cAAc;IAC9C;EACF;AACF;ATjCA,SAAS,kBAAqB,MAAS;AACrC,SAAO,OAAO;;IAEZ,OAAO,QAAQ,IAAI,EAAE,OAAO,CAAC,CAAC,KAAK,KAAK,MAAM;AAC5C,UAAI,QAAQ,WAAW,cAAc,KAA2B,GAAG;AACjE,eAAO;MACT;AAEA,aAAO,UAAU,QAAQ,UAAU;IACrC,CAAC;EACH;AACF;AAOA,SAAS,mBACP,oBAC+B;AAjCjC,MAAA,IAAA;AAkCE,QAAM,OAA4B,CAAC;AAGnC,MAAI,GAAC,KAAA,sBAAA,OAAA,SAAA,mBAAoB,cAApB,OAAA,SAAA,GAA+B,eAAc,eAAc,sBAAA,OAAA,SAAA,mBAAoB,cAAa,CAAC,IAAI;AACpG,SAAK,UAAU,mBAAmB,UAAU;EAC9C;AAGA,QAAI,KAAA,sBAAA,OAAA,SAAA,mBAAoB,cAApB,OAAA,SAAA,GAA+B,cAAa,QAAW;AACzD,SAAK,WAAW,mBAAmB,UAAU;EAC/C;AAEA,SAAO,CAAC,mBAAmB,MAAM,IAAI;AACvC;AAQO,SAAS,8BAA8B,YAAwB,QAAyB;AAvD/F,MAAA;AAwDE,QAAM,gBAAgB,4BAA4B,UAAU;AAC5D,QAAM,EAAE,gBAAgB,eAAe,IAAI,gBAAgB,UAAU;AACrE,QAAM,WAAU,KAAA,eAAe,KAAK,CAAA,cAAa,kBAAkB,WAAW,SAAS,CAAC,MAAxE,OAAA,SAAA,GAA2E;AAE3F,QAAM,QAAQ,OAAO;IACnB,eAAe,IAAI,CAAA,cAAa;AAC9B,YAAM,sBAAsB,cAAc,OAAO,CAAA,cAAa,UAAU,SAAS,UAAU,IAAI;AAC/F,YAAM,UAAU;QACd,MAAM,UAAU;QAChB,SAAS,UAAU;QACnB,SAAS,UAAU;QACnB;MACF;AAEA,YAAM,kBAAkB,WAAW,OAAO,CAAC,QAAQ,MAAM;AACvD,cAAM,mBAAmB,kBAAiD,GAAG,oBAAoB,OAAO;AAExG,eAAO;UACL,GAAG;UACH,GAAI,mBAAmB,iBAAiB,SAAS,IAAI,CAAC;QACxD;MACF,GAAG,CAAC,CAAC;AAEL,YAAM,SAAmB,kBAAkB;QACzC,GAAG;QACH,SAAS,aAAa,kBAAyC,WAAW,WAAW,OAAO,CAAC;QAC7F,OAAO,aAAa,kBAAuC,WAAW,SAAS,OAAO,CAAC;QACvF,OAAO,aAAa,kBAAuC,WAAW,SAAS,OAAO,CAAC;QACvF,QAAQ,aAAa,kBAAwC,WAAW,UAAU,OAAO,CAAC;QAC1F,MAAM,aAAa,kBAAsC,WAAW,QAAQ,OAAO,CAAC;QACpF,YAAY,aAAa,kBAA4C,WAAW,cAAc,OAAO,CAAC;QACtG,WAAW,aAAa,kBAA2C,WAAW,aAAa,OAAO,CAAC;QACnG,MAAM,aAAa,kBAAsC,WAAW,QAAQ,OAAO,CAAC;QACpF,YAAY,aAAa,kBAA4C,WAAW,cAAc,OAAO,CAAC;QACtG,sBAAsB;UACpB,kBAAsD,WAAW,wBAAwB,OAAO;QAClG;QACA,UAAU,aAAa,kBAA0C,WAAW,YAAY,OAAO,CAAC;QAChG,WAAW,aAAa,kBAA2C,WAAW,aAAa,OAAO,CAAC;QACnG,OAAO,OAAO,YAAY,oBAAoB,IAAI,kBAAkB,CAAC;MACvE,CAAC;AAED,YAAM,YAAY,aAAa,kBAA2C,WAAW,aAAa,OAAO,CAAC;AAE1G,UAAI,WAAW;AACb,eAAO,WAAW,UAAU;UAAI,CAAA,cAC9B,qCAAqC,WAAW,mBAAmB;QACrE;MACF;AAEA,YAAM,aAAa,kBAA4C,WAAW,cAAc,OAAO;AAE/F,UAAI,YAAY;AACd,eAAO,QAAQ,CAAA,SACb,WAAW;UACT;UACA,gBAAgB,sBAAsB,MAAM,mBAAmB;QACjE,CAAC;MACL;AAEA,YAAM,aAAa,kBAA4C,WAAW,cAAc,OAAO;AAE/F,UAAI,YAAY;AACd,eAAO,SAAS;MAClB;AAEA,aAAO,CAAC,UAAU,MAAM,MAAM;IAChC,CAAC;EACH;AAEA,QAAM,QAAQ,OAAO;IACnB,eAAe,IAAI,CAAA,cAAa;AAC9B,YAAM,sBAAsB,cAAc,OAAO,CAAA,cAAa,UAAU,SAAS,UAAU,IAAI;AAC/F,YAAM,UAAU;QACd,MAAM,UAAU;QAChB,SAAS,UAAU;QACnB,SAAS,UAAU;QACnB;MACF;AAEA,YAAM,kBAAkB,WAAW,OAAO,CAAC,QAAQ,MAAM;AACvD,cAAM,mBAAmB,kBAAiD,GAAG,oBAAoB,OAAO;AAExG,eAAO;UACL,GAAG;UACH,GAAI,mBAAmB,iBAAiB,SAAgB,IAAI,CAAC;QAC/D;MACF,GAAG,CAAC,CAAC;AAEL,YAAM,SAAmB,kBAAkB;QACzC,GAAG;QACH,WAAW,aAAa,kBAA2C,WAAW,aAAa,OAAO,CAAC;QACnG,UAAU,aAAa,kBAA0C,WAAW,YAAY,OAAO,CAAC;QAChG,OAAO,aAAa,kBAAuC,WAAW,SAAS,OAAO,CAAC;QACvF,UAAU,aAAa,kBAA0C,WAAW,YAAY,OAAO,CAAC;QAChG,MAAM,aAAa,kBAAsC,WAAW,QAAQ,OAAO,CAAC;QACpF,OAAO,OAAO,YAAY,oBAAoB,IAAI,kBAAkB,CAAC;MACvE,CAAC;AAED,YAAM,YAAY,aAAa,kBAA2C,WAAW,aAAa,OAAO,CAAC;AAE1G,UAAI,WAAW;AACb,eAAO,WAAW,UAAU;UAAI,CAAA,cAC9B,qCAAqC,WAAW,mBAAmB;QACrE;MACF;AAEA,YAAM,aAAa,kBAA4C,WAAW,cAAc,OAAO;AAE/F,UAAI,YAAY;AACd,eAAO,QAAQ,CAAA,SACb,WAAW;UACT;UACA,gBAAgB,sBAAsB,MAAM,mBAAmB;QACjE,CAAC;MACL;AAEA,aAAO,CAAC,UAAU,MAAM,MAAM;IAChC,CAAC;EACH;AAEA,SAAO,IAAIC,QAAO;IAChB;IACA;IACA;EACF,CAAC;AACH;AUnLO,SAAS,eAAkB,OAAiB;AACjD,QAAM,WAAW,MAAM,OAAO,CAAC,IAAI,UAAU,MAAM,QAAQ,EAAE,MAAM,KAAK;AAExE,SAAO,MAAM,KAAK,IAAI,IAAI,QAAQ,CAAC;AACrC;ACCO,SAAS,eAAe,YAAoC;AACjE,QAAM,kBAAkB;AAExB,SAAO,WAAW,KAAK,CAAC,GAAG,MAAM;AAC/B,UAAM,YAAY,kBAAyC,GAAG,UAAU,KAAK;AAC7E,UAAM,YAAY,kBAAyC,GAAG,UAAU,KAAK;AAE7E,QAAI,YAAY,WAAW;AACzB,aAAO;IACT;AAEA,QAAI,YAAY,WAAW;AACzB,aAAO;IACT;AAEA,WAAO;EACT,CAAC;AACH;ACdO,SAAS,kBAAkB,YAAoC;AACpE,QAAM,qBAAqB,eAAe,kBAAkB,UAAU,CAAC;AACvE,QAAM,kBAAkB,eAAe,mBAAmB,IAAI,CAAA,cAAa,UAAU,IAAI,CAAC;AAE1F,MAAI,gBAAgB,QAAQ;AAC1B,YAAQ;MACN,oDAAoD,gBACjD,IAAI,CAAA,SAAQ,IAAI,IAAI,GAAG,EACvB,KAAK,IAAI,CAAC;IACf;EACF;AAEA,SAAO;AACT;AIZO,SAAS,eACd,WACA,OACA,SAIQ;AACR,QAAM,EAAE,MAAM,GAAG,IAAI;AACrB,QAAM,EAAE,iBAAiB,QAAQ,kBAAkB,CAAC,EAAE,IAAI,WAAW,CAAC;AACtE,MAAI,OAAO;AAEX,YAAU,aAAa,MAAM,IAAI,CAAC,MAAM,KAAK,QAAQ,UAAU;AAxBjE,QAAA;AAyBI,QAAI,KAAK,WAAW,MAAM,MAAM;AAC9B,cAAQ;IACV;AAEA,UAAM,iBAAiB,mBAAA,OAAA,SAAA,gBAAkB,KAAK,KAAK,IAAA;AAEnD,QAAI,gBAAgB;AAClB,UAAI,QAAQ;AACV,gBAAQ,eAAe;UACrB;UACA;UACA;UACA;UACA;QACF,CAAC;MACH;AAEA,aAAO;IACT;AAEA,QAAI,KAAK,QAAQ;AACf,eAAQ,KAAA,QAAA,OAAA,SAAA,KAAM,SAAN,OAAA,SAAA,GAAY,MAAM,KAAK,IAAI,MAAM,GAAG,IAAI,KAAK,KAAK,GAAA;IAC5D;EACF,CAAC;AAED,SAAO;AACT;AE1CO,SAAS,6BAA6B,QAAgD;AAC3F,SAAO,OAAO;IACZ,OAAO,QAAQ,OAAO,KAAK,EACxB,OAAO,CAAC,CAAC,EAAE,IAAI,MAAM,KAAK,KAAK,MAAM,EACrC,IAAI,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK,MAAM,CAAC;EACnD;AACF;AGXO,SAAS,iBAAoB,OAAY,KAAK,KAAK,WAAgB;AACxE,QAAM,OAAyB,CAAC;AAEhC,SAAO,MAAM,OAAO,CAAA,SAAQ;AAC1B,UAAM,MAAM,GAAG,IAAI;AAEnB,WAAO,OAAO,UAAU,eAAe,KAAK,MAAM,GAAG,IAAI,QAAS,KAAK,GAAG,IAAI;EAChF,CAAC;AACH;ACEA,SAAS,sBAAsB,SAAyC;AACtE,QAAM,gBAAgB,iBAAiB,OAAO;AAE9C,SAAO,cAAc,WAAW,IAC5B,gBACA,cAAc,OAAO,CAAC,QAAQ,UAAU;AACtC,UAAM,OAAO,cAAc,OAAO,CAAC,GAAG,MAAM,MAAM,KAAK;AAEvD,WAAO,CAAC,KAAK,KAAK,CAAA,gBAAe;AAC/B,aACE,OAAO,SAAS,QAAQ,YAAY,SAAS,QAC7C,OAAO,SAAS,MAAM,YAAY,SAAS,MAC3C,OAAO,SAAS,QAAQ,YAAY,SAAS,QAC7C,OAAO,SAAS,MAAM,YAAY,SAAS;IAE/C,CAAC;EACH,CAAC;AACP;AAMO,SAAS,iBAAiB,WAAsC;AACrE,QAAM,EAAE,SAAS,MAAM,IAAI;AAC3B,QAAM,UAA0B,CAAC;AAEjC,UAAQ,KAAK,QAAQ,CAAC,SAAS,UAAU;AACvC,UAAM,SAAkB,CAAC;AAKzB,QAAI,CAAC,QAAQ,OAAO,QAAQ;AAC1B,YAAM,EAAE,MAAM,GAAG,IAAI,MAAM,KAAK;AAKhC,UAAI,SAAS,UAAa,OAAO,QAAW;AAC1C;MACF;AAEA,aAAO,KAAK,EAAE,MAAM,GAAG,CAAC;IAC1B,OAAO;AACL,cAAQ,QAAQ,CAAC,MAAM,OAAO;AAC5B,eAAO,KAAK,EAAE,MAAM,GAAG,CAAC;MAC1B,CAAC;IACH;AAEA,WAAO,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM;AAC/B,YAAM,WAAW,QAAQ,MAAM,KAAK,EAAE,IAAI,MAAM,EAAE;AAClD,YAAM,SAAS,QAAQ,MAAM,KAAK,EAAE,IAAI,EAAE;AAC1C,YAAM,WAAW,QAAQ,OAAO,EAAE,IAAI,UAAU,EAAE;AAClD,YAAM,SAAS,QAAQ,OAAO,EAAE,IAAI,MAAM;AAE1C,cAAQ,KAAK;QACX,UAAU;UACR,MAAM;UACN,IAAI;QACN;QACA,UAAU;UACR,MAAM;UACN,IAAI;QACN;MACF,CAAC;IACH,CAAC;EACH,CAAC;AAED,SAAO,sBAAsB,OAAO;AACtC;AI5EO,SAAS,oBAAoB,MAAc,QAA4C;AAC5F,SAAO,OAAO,MAAM,IAAI,KAAK,OAAO,MAAM,IAAI,KAAK;AACrD;ACDO,SAAS,sBACd,qBACA,UACA,YACqB;AACrB,SAAO,OAAO;IACZ,OAAO,QAAQ,UAAU,EAAE,OAAO,CAAC,CAAC,IAAI,MAAM;AAC5C,YAAM,qBAAqB,oBAAoB,KAAK,CAAA,SAAQ;AAC1D,eAAO,KAAK,SAAS,YAAY,KAAK,SAAS;MACjD,CAAC;AAED,UAAI,CAAC,oBAAoB;AACvB,eAAO;MACT;AAEA,aAAO,mBAAmB,UAAU;IACtC,CAAC;EACH;AACF;ACnBO,IAAM,0BAA0B,CAAC,OAAoB,WAAW,QAAQ;AAC7E,MAAI,aAAa;AAEjB,QAAM,cAAc,MAAM;AAE1B,QAAM,OAAO,aAAa,KAAK,IAAI,GAAG,cAAc,QAAQ,GAAG,aAAa,CAAC,MAAM,KAAK,QAAQ,UAAU;AAb5G,QAAA,IAAA;AAcI,UAAM,UACJ,MAAA,KAAA,KAAK,KAAK,MAAK,WAAf,OAAA,SAAA,GAAA,KAAA,IAAwB;MACtB;MACA;MACA;MACA;IACF,CAAA,MACA,KAAK,eACL;AAEF,kBAAc,KAAK,UAAU,CAAC,KAAK,SAAS,QAAQ,MAAM,MAAM,GAAG,KAAK,IAAI,GAAG,cAAc,GAAG,CAAC;EACnG,CAAC;AAED,SAAO;AACT;ACrBO,SAAS,aACd,OACA,YACA,aAAkC,CAAC,GAC1B;AACT,QAAM,EAAE,OAAO,OAAO,IAAI,MAAM;AAChC,QAAM,OAAO,aAAa,YAAY,YAAY,MAAM,MAAM,IAAI;AAElE,MAAI,OAAO;AACT,WAAO,CAAC,EAAE,MAAM,eAAe,MAAM,UAAU,MAAM,MAAM,GACxD,OAAO,CAAA,SAAQ;AACd,UAAI,CAAC,MAAM;AACT,eAAO;MACT;AAEA,aAAO,KAAK,SAAS,KAAK,KAAK;IACjC,CAAC,EACA,KAAK,CAAA,SAAQ,eAAe,KAAK,OAAO,YAAY,EAAE,QAAQ,MAAM,CAAC,CAAC;EAC3E;AAEA,MAAI,iBAAiB;AACrB,QAAM,aAA0B,CAAC;AAEjC,SAAO,QAAQ,CAAC,EAAE,OAAO,IAAI,MAAM;AACjC,UAAM,OAAO,MAAM;AACnB,UAAM,KAAK,IAAI;AAEf,UAAM,IAAI,aAAa,MAAM,IAAI,CAAC,MAAM,QAAQ;AAC9C,UAAI,CAAC,KAAK,UAAU,CAAC,KAAK,MAAM,QAAQ;AACtC;MACF;AAEA,YAAM,eAAe,KAAK,IAAI,MAAM,GAAG;AACvC,YAAM,aAAa,KAAK,IAAI,IAAI,MAAM,KAAK,QAAQ;AACnD,YAAMC,SAAQ,aAAa;AAE3B,wBAAkBA;AAElB,iBAAW;QACT,GAAG,KAAK,MAAM,IAAI,CAAA,UAAS;UACzB;UACA,MAAM;UACN,IAAI;QACN,EAAE;MACJ;IACF,CAAC;EACH,CAAC;AAED,MAAI,mBAAmB,GAAG;AACxB,WAAO;EACT;AAGA,QAAM,eAAe,WAClB,OAAO,CAAA,cAAa;AACnB,QAAI,CAAC,MAAM;AACT,aAAO;IACT;AAEA,WAAO,KAAK,SAAS,UAAU,KAAK,KAAK;EAC3C,CAAC,EACA,OAAO,CAAA,cAAa,eAAe,UAAU,KAAK,OAAO,YAAY,EAAE,QAAQ,MAAM,CAAC,CAAC,EACvF,OAAO,CAAC,KAAK,cAAc,MAAM,UAAU,KAAK,UAAU,MAAM,CAAC;AAIpE,QAAM,gBAAgB,WACnB,OAAO,CAAA,cAAa;AACnB,QAAI,CAAC,MAAM;AACT,aAAO;IACT;AAEA,WAAO,UAAU,KAAK,SAAS,QAAQ,UAAU,KAAK,KAAK,SAAS,IAAI;EAC1E,CAAC,EACA,OAAO,CAAC,KAAK,cAAc,MAAM,UAAU,KAAK,UAAU,MAAM,CAAC;AAIpE,QAAM,QAAQ,eAAe,IAAI,eAAe,gBAAgB;AAEhE,SAAO,SAAS;AAClB;AItFO,SAAS,wBAAwB,WAAyB,SAA+B;AAC9F,MAAI,MAAM,QAAQ,OAAO,GAAG;AAC1B,WAAO,QAAQ,KAAK,CAAA,qBAAoB;AACtC,YAAM,OAAO,OAAO,qBAAqB,WAAW,mBAAmB,iBAAiB;AAExF,aAAO,SAAS,UAAU;IAC5B,CAAC;EACH;AAEA,SAAO;AACT;ACNO,SAAS,OAAO,MAAc,YAAiC;AACpE,QAAM,EAAE,eAAe,IAAI,gBAAgB,UAAU;AACrD,QAAM,YAAY,eAAe,KAAK,CAAA,SAAQ,KAAK,SAAS,IAAI;AAEhE,MAAI,CAAC,WAAW;AACd,WAAO;EACT;AAEA,QAAM,UAAU;IACd,MAAM,UAAU;IAChB,SAAS,UAAU;IACnB,SAAS,UAAU;EACrB;AACA,QAAM,QAAQ,aAAa,kBAAuC,WAAW,SAAS,OAAO,CAAC;AAE9F,MAAI,OAAO,UAAU,UAAU;AAC7B,WAAO;EACT;AAEA,SAAO,MAAM,MAAM,GAAG,EAAE,SAAS,MAAM;AACzC;ACrBO,SAAS,YACd,MACA;EACE,gBAAgB;EAChB,mBAAmB;AACrB,IASI,CAAC,GACI;AApBX,MAAA;AAqBE,MAAI,kBAAkB;AACpB,QAAI,KAAK,KAAK,SAAS,aAAa;AAElC,aAAO;IACT;AACA,QAAI,KAAK,QAAQ;AACf,aAAO,SAAS,MAAK,KAAA,KAAK,SAAL,OAAA,KAAa,EAAE;IACtC;EACF;AAEA,MAAI,KAAK,QAAQ;AACf,WAAO,CAAC,KAAK;EACf;AAEA,MAAI,KAAK,UAAU,KAAK,QAAQ;AAC9B,WAAO;EACT;AAEA,MAAI,KAAK,QAAQ,eAAe,GAAG;AACjC,WAAO;EACT;AAEA,MAAI,eAAe;AACjB,QAAI,iBAAiB;AAErB,SAAK,QAAQ,QAAQ,CAAA,cAAa;AAChC,UAAI,mBAAmB,OAAO;AAE5B;MACF;AAEA,UAAI,CAAC,YAAY,WAAW,EAAE,kBAAkB,cAAc,CAAC,GAAG;AAChE,yBAAiB;MACnB;IACF,CAAC;AAED,WAAO;EACT;AAEA,SAAO;AACT;AKxCA,SAAS,WAAW,OAAoB,IAAiB,aAAuB;AArBhF,MAAA;AAsBE,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI,SAA6B;AAEjC,MAAI,gBAAgB,SAAS,GAAG;AAC9B,aAAS,UAAU;EACrB;AAEA,MAAI,QAAQ;AACV,UAAM,gBAAe,KAAA,MAAM,gBAAN,OAAA,KAAqB,OAAO,MAAM;AACvD,UAAM,uBAAuB,OAAO,OAAO,KAAK,eAAe,WAAW;AAG1E,WACE,yBACC,CAAC,CAAC,YAAY,QAAQ,YAAY,KAAK,CAAC,aAAa,KAAK,CAAA,SAAQ,KAAK,KAAK,SAAS,WAAW,CAAC;EAEtG;AAEA,QAAM,EAAE,OAAO,IAAI;AAEnB,SAAO,OAAO,KAAK,CAAC,EAAE,OAAO,IAAI,MAAM;AACrC,QAAI,uBACF,MAAM,UAAU,IAAI,MAAM,IAAI,iBAAiB,MAAM,IAAI,KAAK,eAAe,WAAW,IAAI;AAE9F,UAAM,IAAI,aAAa,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,MAAM,WAAW;AAEjE,UAAI,sBAAsB;AACxB,eAAO;MACT;AAEA,UAAI,KAAK,UAAU;AACjB,cAAM,uBAAuB,CAAC,UAAU,OAAO,KAAK,eAAe,WAAW;AAC9E,cAAM,4BACJ,CAAC,CAAC,YAAY,QAAQ,KAAK,KAAK,KAAK,CAAC,KAAK,MAAM,KAAK,CAAA,cAAa,UAAU,KAAK,SAAS,WAAW,CAAC;AAEzG,+BAAuB,wBAAwB;MACjD;AACA,aAAO,CAAC;IACV,CAAC;AAED,WAAO;EACT,CAAC;AACH;AACO,IAAM,UACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAC3B,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AAEjD,MAAI,UAAU;AACZ,QAAI,OAAO;AACT,YAAM,gBAAgB,kBAAkB,OAAO,IAAI;AAEnD,SAAG;QACD,KAAK,OAAO;UACV,GAAG;UACH,GAAG;QACL,CAAC;MACH;IACF,OAAO;AACL,aAAO,QAAQ,CAAA,UAAS;AACtB,cAAM,OAAO,MAAM,MAAM;AACzB,cAAM,KAAK,MAAM,IAAI;AAErB,cAAM,IAAI,aAAa,MAAM,IAAI,CAAC,MAAM,QAAQ;AAC9C,gBAAM,cAAc,KAAK,IAAI,KAAK,IAAI;AACtC,gBAAM,YAAY,KAAK,IAAI,MAAM,KAAK,UAAU,EAAE;AAClD,gBAAM,cAAc,KAAK,MAAM,KAAK,CAAA,SAAQ,KAAK,SAAS,IAAI;AAK9D,cAAI,aAAa;AACf,iBAAK,MAAM,QAAQ,CAAA,SAAQ;AACzB,kBAAI,SAAS,KAAK,MAAM;AACtB,mBAAG;kBACD;kBACA;kBACA,KAAK,OAAO;oBACV,GAAG,KAAK;oBACR,GAAG;kBACL,CAAC;gBACH;cACF;YACF,CAAC;UACH,OAAO;AACL,eAAG,QAAQ,aAAa,WAAW,KAAK,OAAO,UAAU,CAAC;UAC5D;QACF,CAAC;MACH,CAAC;IACH;EACF;AAEA,SAAO,WAAW,OAAO,IAAI,IAAI;AACnC;ACnGK,IAAM,UACX,CAAC,KAAK,UACN,CAAC,EAAE,GAAG,MAAM;AACV,KAAG,QAAQ,KAAK,KAAK;AAErB,SAAO;AACT;ACJK,IAAM,UACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,OAAO,UAAU,MAAM,MAAM;AAC9B,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AAEjD,MAAI;AAEJ,MAAI,MAAM,UAAU,QAAQ,WAAW,MAAM,UAAU,KAAK,GAAG;AAE7D,uBAAmB,MAAM,UAAU,QAAQ,OAAO;EACpD;AAGA,MAAI,CAAC,KAAK,aAAa;AACrB,YAAQ,KAAK,sEAAsE;AAEnF,WAAO;EACT;AAEA,SACE,MAAM,EAEH,QAAQ,CAAC,EAAE,SAAS,MAAM;AACzB,UAAM,cAAc,aAAa,MAAM,EAAE,GAAG,kBAAkB,GAAG,WAAW,CAAC,EAAE,KAAK;AAEpF,QAAI,aAAa;AACf,aAAO;IACT;AAEA,WAAO,SAAS,WAAW;EAC7B,CAAC,EACA,QAAQ,CAAC,EAAE,OAAO,aAAa,MAAM;AACpC,WAAO,aAAa,MAAM,EAAE,GAAG,kBAAkB,GAAG,WAAW,CAAC,EAAE,cAAc,QAAQ;EAC1F,CAAC,EACA,IAAI;AAEX;ACtCK,IAAM,mBACX,CAAA,aACA,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,MAAI,UAAU;AACZ,UAAM,EAAE,IAAI,IAAI;AAChB,UAAM,OAAO,OAAO,UAAU,GAAG,IAAI,QAAQ,IAAI;AACjD,UAAM,YAAYC,eAAc,OAAO,KAAK,IAAI;AAEhD,OAAG,aAAa,SAAS;EAC3B;AAEA,SAAO;AACT;ACZK,IAAM,mBACX,CAAC,WAAW,aACZ,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAC3B,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI;AACJ,MAAI;AAEJ,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO;AACP,SAAK;EACP,WAAW,YAAY,UAAU,YAAY,QAAQ,UAAU;AAC7D,WAAO,SAAS;AAChB,SAAK,SAAS;EAChB,OAAO;AACL,WAAO,UAAU;AACjB,SAAK,UAAU;EACjB;AAEA,MAAI,UAAU;AACZ,OAAG,IAAI,aAAa,MAAM,IAAI,CAAC,MAAM,QAAQ;AAC3C,UAAI,KAAK,QAAQ;AACf;MACF;AAEA,SAAG,cAAc,KAAK,QAAW;QAC/B,GAAG,KAAK;QACR,KAAK;MACP,CAAC;IACH,CAAC;EACH;AAEA,SAAO;AACT;AChCK,IAAM,mBACX,CAAA,aACA,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,MAAI,UAAU;AACZ,UAAM,EAAE,IAAI,IAAI;AAChB,UAAM,EAAE,MAAM,GAAG,IAAI,OAAO,aAAa,WAAW,EAAE,MAAM,UAAU,IAAI,SAAS,IAAI;AACvF,UAAM,SAASC,eAAc,QAAQ,GAAG,EAAE;AAC1C,UAAM,SAASA,eAAc,MAAM,GAAG,EAAE;AACxC,UAAM,eAAe,OAAO,MAAM,QAAQ,MAAM;AAChD,UAAM,cAAc,OAAO,IAAI,QAAQ,MAAM;AAC7C,UAAM,YAAYA,eAAc,OAAO,KAAK,cAAc,WAAW;AAErE,OAAG,aAAa,SAAS;EAC3B;AAEA,SAAO;AACT;ACfK,IAAM,eACX,CAAA,eACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AAEjD,SAAO,qBAAqB,IAAI,EAAE,OAAO,QAAQ;AACnD;ACjBF,SAAS,YAAY,OAAoB,iBAA4B;AACnE,QAAM,QAAQ,MAAM,eAAgB,MAAM,UAAU,IAAI,gBAAgB,MAAM,UAAU,MAAM,MAAM;AAEpG,MAAI,OAAO;AACT,UAAM,gBAAgB,MAAM,OAAO,CAAA,SAAQ,mBAAA,OAAA,SAAA,gBAAiB,SAAS,KAAK,KAAK,IAAA,CAAK;AAEpF,UAAM,GAAG,YAAY,aAAa;EACpC;AACF;AAgBO,IAAM,aACX,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC,MACzB,CAAC,EAAE,IAAI,OAAO,UAAU,OAAO,MAAM;AACnC,QAAM,EAAE,WAAW,IAAI,IAAI;AAC3B,QAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QAAM,sBAAsB,OAAO,iBAAiB;AACpD,QAAM,gBAAgB,sBAAsB,qBAAqB,MAAM,KAAK,EAAE,KAAK,MAAM,MAAM,KAAK,EAAE,KAAK;AAE3G,MAAI,qBAAqBD,kBAAiB,UAAU,KAAK,SAAS;AAChE,QAAI,CAAC,MAAM,gBAAgB,CAAC,SAAS,KAAK,MAAM,GAAG,GAAG;AACpD,aAAO;IACT;AAEA,QAAI,UAAU;AACZ,UAAI,WAAW;AACb,oBAAY,OAAO,OAAO,iBAAiB,eAAe;MAC5D;AAEA,SAAG,MAAM,MAAM,GAAG,EAAE,eAAe;IACrC;AAEA,WAAO;EACT;AAEA,MAAI,CAAC,MAAM,OAAO,SAAS;AACzB,WAAO;EACT;AAEA,QAAM,QAAQ,IAAI,iBAAiB,IAAI,OAAO,QAAQ;AAEtD,QAAM,QAAQ,MAAM,UAAU,IAAI,SAAY,eAAe,MAAM,KAAK,EAAE,EAAE,eAAe,MAAM,WAAW,EAAE,CAAC,CAAC;AAEhH,MAAI,QACF,SAAS,QACL;IACE;MACE,MAAM;MACN,OAAO;IACT;EACF,IACA;AAEN,MAAI,MAAM,SAAS,GAAG,KAAK,GAAG,QAAQ,IAAI,MAAM,GAAG,GAAG,GAAG,KAAK;AAE9D,MAAI,CAAC,SAAS,CAAC,OAAO,SAAS,GAAG,KAAK,GAAG,QAAQ,IAAI,MAAM,GAAG,GAAG,GAAG,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC,IAAI,MAAS,GAAG;AAC3G,UAAM;AACN,YAAQ,QACJ;MACE;QACE,MAAM;QACN,OAAO;MACT;IACF,IACA;EACN;AAEA,MAAI,UAAU;AACZ,QAAI,KAAK;AACP,UAAI,qBAAqBC,gBAAe;AACtC,WAAG,gBAAgB;MACrB;AAEA,SAAG,MAAM,GAAG,QAAQ,IAAI,MAAM,GAAG,GAAG,GAAG,KAAK;AAE5C,UAAI,SAAS,CAAC,SAAS,CAAC,MAAM,gBAAgB,MAAM,OAAO,SAAS,OAAO;AACzE,cAAMC,SAAQ,GAAG,QAAQ,IAAI,MAAM,OAAO,CAAC;AAC3C,cAAM,SAAS,GAAG,IAAI,QAAQA,MAAK;AAEnC,YAAI,MAAM,KAAK,EAAE,EAAE,eAAe,OAAO,MAAM,GAAG,OAAO,MAAM,IAAI,GAAG,KAAK,GAAG;AAC5E,aAAG,cAAc,GAAG,QAAQ,IAAI,MAAM,OAAO,CAAC,GAAG,KAAK;QACxD;MACF;IACF;AAEA,QAAI,WAAW;AACb,kBAAY,OAAO,OAAO,iBAAiB,eAAe;IAC5D;AAEA,OAAG,eAAe;EACpB;AAEA,SAAO;AACT;AC3FK,IAAM,gBACX,CAAC,YAAY,gBAAgB,CAAC,MAC9B,CAAC,EAAE,IAAI,OAAO,UAAU,OAAO,MAAM;AAzBvC,MAAA;AA0BI,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAM,EAAE,OAAO,IAAI,IAAI,MAAM;AAI7B,QAAM,OAAwB,MAAM,UAAU;AAE9C,MAAK,QAAQ,KAAK,WAAY,MAAM,QAAQ,KAAK,CAAC,MAAM,WAAW,GAAG,GAAG;AACvE,WAAO;EACT;AAEA,QAAM,cAAc,MAAM,KAAK,EAAE;AAEjC,MAAI,YAAY,SAAS,MAAM;AAC7B,WAAO;EACT;AAEA,QAAM,sBAAsB,OAAO,iBAAiB;AAEpD,MAAI,MAAM,OAAO,QAAQ,SAAS,KAAK,MAAM,KAAK,EAAE,EAAE,eAAe,MAAM,WAAW,EAAE,GAAG;AAIzF,QAAI,MAAM,UAAU,KAAK,MAAM,KAAK,EAAE,EAAE,SAAS,QAAQ,MAAM,MAAM,EAAE,MAAM,MAAM,KAAK,EAAE,EAAE,aAAa,GAAG;AAC1G,aAAO;IACT;AAEA,QAAI,UAAU;AACZ,UAAI,OAAOC,WAAS;AAEpB,YAAM,cAAc,MAAM,MAAM,EAAE,IAAI,IAAI,MAAM,MAAM,EAAE,IAAI,IAAI;AAIhE,eAAS,IAAI,MAAM,QAAQ,aAAa,KAAK,MAAM,QAAQ,GAAG,KAAK,GAAG;AACpE,eAAOA,WAAS,KAAK,MAAM,KAAK,CAAC,EAAE,KAAK,IAAI,CAAC;MAC/C;AAEA,YAAM;;QAEJ,MAAM,WAAW,EAAE,IAAI,MAAM,KAAK,EAAE,EAAE,aAClC,IACA,MAAM,WAAW,EAAE,IAAI,MAAM,KAAK,EAAE,EAAE,aACpC,IACA;;AAGR,YAAMC,yBAAwB;QAC5B,GAAG,sBAAsB,qBAAqB,MAAM,KAAK,EAAE,KAAK,MAAM,MAAM,KAAK,EAAE,KAAK;QACxF,GAAG;MACL;AACA,YAAMC,cAAW,KAAA,KAAK,aAAa,gBAAlB,OAAA,SAAA,GAA+B,cAAcD,sBAAAA,MAA0B;AAExF,aAAO,KAAK,OAAOD,WAAS,KAAK,KAAK,cAAc,MAAME,SAAQ,KAAK,MAAS,CAAC;AAEjF,YAAM,QAAQ,MAAM,OAAO,MAAM,SAAS,cAAc,EAAE;AAE1D,SAAG,QAAQ,OAAO,MAAM,MAAM,CAAC,UAAU,GAAG,IAAI,MAAM,MAAM,IAAI,aAAa,CAAC,CAAC;AAE/E,UAAI,MAAM;AAEV,SAAG,IAAI,aAAa,OAAO,GAAG,IAAI,QAAQ,MAAM,CAAC,GAAG,QAAQ;AAC1D,YAAI,MAAM,IAAI;AACZ,iBAAO;QACT;AAEA,YAAI,EAAE,eAAe,EAAE,QAAQ,SAAS,GAAG;AACzC,gBAAM,MAAM;QACd;MACF,CAAC;AAED,UAAI,MAAM,IAAI;AACZ,WAAG,aAAaJ,eAAc,KAAK,GAAG,IAAI,QAAQ,GAAG,CAAC,CAAC;MACzD;AAEA,SAAG,eAAe;IACpB;AAEA,WAAO;EACT;AAEA,QAAM,WAAW,IAAI,QAAQ,MAAM,IAAI,IAAI,YAAY,eAAe,CAAC,EAAE,cAAc;AAEvF,QAAM,oBAAoB;IACxB,GAAG,sBAAsB,qBAAqB,YAAY,KAAK,MAAM,YAAY,KAAK;IACtF,GAAG;EACL;AACA,QAAM,wBAAwB;IAC5B,GAAG,sBAAsB,qBAAqB,MAAM,KAAK,EAAE,KAAK,MAAM,MAAM,KAAK,EAAE,KAAK;IACxF,GAAG;EACL;AAEA,KAAG,OAAO,MAAM,KAAK,IAAI,GAAG;AAE5B,QAAM,QAAQ,WACV;IACE,EAAE,MAAM,OAAO,kBAAkB;IACjC,EAAE,MAAM,UAAU,OAAO,sBAAsB;EACjD,IACA,CAAC,EAAE,MAAM,OAAO,kBAAkB,CAAC;AAEvC,MAAI,CAACK,UAAS,GAAG,KAAK,MAAM,KAAK,CAAC,GAAG;AACnC,WAAO;EACT;AAEA,MAAI,UAAU;AACZ,UAAM,EAAE,WAAW,YAAY,IAAI;AACnC,UAAM,EAAE,gBAAgB,IAAI,OAAO;AACnC,UAAM,QAAQ,eAAgB,UAAU,IAAI,gBAAgB,UAAU,MAAM,MAAM;AAElF,OAAG,MAAM,MAAM,KAAK,GAAG,KAAK,EAAE,eAAe;AAE7C,QAAI,CAAC,SAAS,CAAC,UAAU;AACvB,aAAO;IACT;AAEA,UAAM,gBAAgB,MAAM,OAAO,CAAA,SAAQ,gBAAgB,SAAS,KAAK,KAAK,IAAI,CAAC;AAEnF,OAAG,YAAY,aAAa;EAC9B;AAEA,SAAO;AACT;AC3IF,IAAM,oBAAoB,CAAC,IAAiB,aAAgC;AAC1E,QAAM,OAAO,eAAe,CAAA,SAAQ,KAAK,SAAS,QAAQ,EAAE,GAAG,SAAS;AAExE,MAAI,CAAC,MAAM;AACT,WAAO;EACT;AAEA,QAAM,SAAS,GAAG,IAAI,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,CAAC,CAAC,EAAE,OAAO,KAAK,KAAK;AAE1E,MAAI,WAAW,QAAW;AACxB,WAAO;EACT;AAEA,QAAM,aAAa,GAAG,IAAI,OAAO,MAAM;AACvC,QAAM,mBAAmB,KAAK,KAAK,UAAS,cAAA,OAAA,SAAA,WAAY,SAAQ,QAAQ,GAAG,KAAK,KAAK,GAAG;AAExF,MAAI,CAAC,kBAAkB;AACrB,WAAO;EACT;AAEA,KAAG,KAAK,KAAK,GAAG;AAEhB,SAAO;AACT;AAEA,IAAM,mBAAmB,CAAC,IAAiB,aAAgC;AACzE,QAAM,OAAO,eAAe,CAAA,SAAQ,KAAK,SAAS,QAAQ,EAAE,GAAG,SAAS;AAExE,MAAI,CAAC,MAAM;AACT,WAAO;EACT;AAEA,QAAM,QAAQ,GAAG,IAAI,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK,KAAK;AAEzD,MAAI,UAAU,QAAW;AACvB,WAAO;EACT;AAEA,QAAM,YAAY,GAAG,IAAI,OAAO,KAAK;AACrC,QAAM,kBAAkB,KAAK,KAAK,UAAS,aAAA,OAAA,SAAA,UAAW,SAAQ,QAAQ,GAAG,KAAK,KAAK;AAEnF,MAAI,CAAC,iBAAiB;AACpB,WAAO;EACT;AAEA,KAAG,KAAK,KAAK;AAEb,SAAO;AACT;AAuBO,IAAM,aACX,CAAC,gBAAgB,gBAAgB,WAAW,aAAa,CAAC,MAC1D,CAAC,EAAE,QAAQ,IAAI,OAAO,UAAU,OAAO,UAAU,IAAI,MAAM;AACzD,QAAM,EAAE,YAAY,gBAAgB,IAAI,OAAO;AAC/C,QAAM,WAAW,YAAY,gBAAgB,MAAM,MAAM;AACzD,QAAM,WAAW,YAAY,gBAAgB,MAAM,MAAM;AACzD,QAAM,EAAE,WAAW,YAAY,IAAI;AACnC,QAAM,EAAE,OAAO,IAAI,IAAI;AACvB,QAAM,QAAQ,MAAM,WAAW,GAAG;AAElC,QAAM,QAAQ,eAAgB,UAAU,IAAI,gBAAgB,UAAU,MAAM,MAAM;AAElF,MAAI,CAAC,OAAO;AACV,WAAO;EACT;AAEA,QAAM,aAAa,eAAe,CAAA,SAAQ,OAAO,KAAK,KAAK,MAAM,UAAU,CAAC,EAAE,SAAS;AAEvF,MAAI,MAAM,SAAS,KAAK,cAAc,MAAM,QAAQ,WAAW,SAAS,GAAG;AAEzE,QAAI,WAAW,KAAK,SAAS,UAAU;AACrC,aAAO,SAAS,aAAa,QAAQ;IACvC;AAGA,QAAI,OAAO,WAAW,KAAK,KAAK,MAAM,UAAU,KAAK,SAAS,aAAa,WAAW,KAAK,OAAO,KAAK,UAAU;AAC/G,aAAO,MAAM,EACV,QAAQ,MAAM;AACb,WAAG,cAAc,WAAW,KAAK,QAAQ;AAEzC,eAAO;MACT,CAAC,EACA,QAAQ,MAAM,kBAAkB,IAAI,QAAQ,CAAC,EAC7C,QAAQ,MAAM,iBAAiB,IAAI,QAAQ,CAAC,EAC5C,IAAI;IACT;EACF;AACA,MAAI,CAAC,aAAa,CAAC,SAAS,CAAC,UAAU;AACrC,WACE,MAAM,EAEH,QAAQ,MAAM;AACb,YAAM,gBAAgB,IAAI,EAAE,WAAW,UAAU,UAAU;AAE3D,UAAI,eAAe;AACjB,eAAO;MACT;AAEA,aAAO,SAAS,WAAW;IAC7B,CAAC,EACA,WAAW,UAAU,UAAU,EAC/B,QAAQ,MAAM,kBAAkB,IAAI,QAAQ,CAAC,EAC7C,QAAQ,MAAM,iBAAiB,IAAI,QAAQ,CAAC,EAC5C,IAAI;EAEX;AAEA,SACE,MAAM,EAEH,QAAQ,MAAM;AACb,UAAM,gBAAgB,IAAI,EAAE,WAAW,UAAU,UAAU;AAE3D,UAAM,gBAAgB,MAAM,OAAO,CAAA,SAAQ,gBAAgB,SAAS,KAAK,KAAK,IAAI,CAAC;AAEnF,OAAG,YAAY,aAAa;AAE5B,QAAI,eAAe;AACjB,aAAO;IACT;AAEA,WAAO,SAAS,WAAW;EAC7B,CAAC,EACA,WAAW,UAAU,UAAU,EAC/B,QAAQ,MAAM,kBAAkB,IAAI,QAAQ,CAAC,EAC7C,QAAQ,MAAM,iBAAiB,IAAI,QAAQ,CAAC,EAC5C,IAAI;AAEX;ACxHK,IAAM,aACX,CAAC,YAAY,aAAa,CAAC,GAAG,UAAU,CAAC,MACzC,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,EAAE,uBAAuB,MAAM,IAAI;AACzC,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAMC,YAAW,aAAa,OAAO,MAAM,UAAU;AAErD,MAAIA,WAAU;AACZ,WAAO,SAAS,UAAU,MAAM,EAAE,qBAAqB,CAAC;EAC1D;AAEA,SAAO,SAAS,QAAQ,MAAM,UAAU;AAC1C;ACzBK,IAAM,aACX,CAAC,YAAY,kBAAkB,aAAa,CAAC,MAC7C,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAM,aAAa,YAAY,kBAAkB,MAAM,MAAM;AAC7D,QAAMA,YAAW,aAAa,OAAO,MAAM,UAAU;AAErD,MAAI;AAEJ,MAAI,MAAM,UAAU,QAAQ,WAAW,MAAM,UAAU,KAAK,GAAG;AAE7D,uBAAmB,MAAM,UAAU,QAAQ,OAAO;EACpD;AAEA,MAAIA,WAAU;AACZ,WAAO,SAAS,QAAQ,YAAY,gBAAgB;EACtD;AAIA,SAAO,SAAS,QAAQ,MAAM,EAAE,GAAG,kBAAkB,GAAG,WAAW,CAAC;AACtE;AC1BK,IAAM,aACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAMA,YAAW,aAAa,OAAO,MAAM,UAAU;AAErD,MAAIA,WAAU;AACZ,WAAO,SAAS,KAAK,IAAI;EAC3B;AAEA,SAAO,SAAS,OAAO,MAAM,UAAU;AACzC;ACjBK,IAAM,gBACX,MACA,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,UAAU,MAAM;AAEtB,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK,GAAG;AAC1C,UAAM,SAAS,QAAQ,CAAC;AACxB,QAAI;AAIJ,QAAI,OAAO,KAAK,iBAAiB,WAAW,OAAO,SAAS,KAAK,IAAI;AACnE,UAAI,UAAU;AACZ,cAAM,KAAK,MAAM;AACjB,cAAM,SAAS,SAAS;AAExB,iBAAS,IAAI,OAAO,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK,GAAG;AACpD,aAAG,KAAK,OAAO,MAAM,CAAC,EAAE,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC;QAChD;AAEA,YAAI,SAAS,MAAM;AACjB,gBAAM,QAAQ,GAAG,IAAI,QAAQ,SAAS,IAAI,EAAE,MAAM;AAElD,aAAG,YAAY,SAAS,MAAM,SAAS,IAAI,MAAM,OAAO,KAAK,SAAS,MAAM,KAAK,CAAC;QACpF,OAAO;AACL,aAAG,OAAO,SAAS,MAAM,SAAS,EAAE;QACtC;MACF;AAEA,aAAO;IACT;EACF;AAEA,SAAO;AACT;AClCK,IAAM,gBACX,MACA,CAAC,EAAE,IAAI,SAAS,MAAM;AACpB,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,EAAE,OAAO,OAAO,IAAI;AAE1B,MAAI,OAAO;AACT,WAAO;EACT;AAEA,MAAI,UAAU;AACZ,WAAO,QAAQ,CAAA,UAAS;AACtB,SAAG,WAAW,MAAM,MAAM,KAAK,MAAM,IAAI,GAAG;IAC9C,CAAC;EACH;AAEA,SAAO;AACT;ACCK,IAAM,YACX,CAAC,YAAY,UAAU,CAAC,MACxB,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAlC/B,MAAA;AAmCI,QAAM,EAAE,uBAAuB,MAAM,IAAI;AACzC,QAAM,EAAE,UAAU,IAAI;AACtB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AACjD,QAAM,EAAE,OAAO,OAAO,OAAO,IAAI;AAEjC,MAAI,CAAC,UAAU;AACb,WAAO;EACT;AAEA,MAAI,SAAS,sBAAsB;AACjC,QAAI,EAAE,MAAM,GAAG,IAAI;AACnB,UAAM,SAAQ,KAAA,MAAM,MAAM,EAAE,KAAK,CAAA,SAAQ,KAAK,SAAS,IAAI,MAA7C,OAAA,SAAA,GAAgD;AAC9D,UAAM,QAAQ,aAAa,OAAO,MAAM,KAAK;AAE7C,QAAI,OAAO;AACT,aAAO,MAAM;AACb,WAAK,MAAM;IACb;AAEA,OAAG,WAAW,MAAM,IAAI,IAAI;EAC9B,OAAO;AACL,WAAO,QAAQ,CAAA,UAAS;AACtB,SAAG,WAAW,MAAM,MAAM,KAAK,MAAM,IAAI,KAAK,IAAI;IACpD,CAAC;EACH;AAEA,KAAG,iBAAiB,IAAI;AAExB,SAAO;AACT;AC/CK,IAAM,qBACX,CAAA,aACA,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAC3B,QAAM,EAAE,UAAU,IAAI;AACtB,MAAI;AACJ,MAAI;AAEJ,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO;AACP,SAAK;EACP,WAAW,YAAY,UAAU,YAAY,QAAQ,UAAU;AAC7D,WAAO,SAAS;AAChB,SAAK,SAAS;EAChB,OAAO;AACL,WAAO,UAAU;AACjB,SAAK,UAAU;EACjB;AAEA,MAAI,UAAU;AACZ,OAAG,IAAI,aAAa,MAAM,IAAI,CAAC,MAAM,QAAQ;AAC3C,UAAI,KAAK,QAAQ;AACf;MACF;AAEA,YAAM,WAAW,EAAE,GAAG,KAAK,MAAM;AAEjC,aAAO,SAAS;AAEhB,SAAG,cAAc,KAAK,QAAW,QAAQ;IAC3C,CAAC;EACH;AAEA,SAAO;AACT;AClBK,IAAM,mBACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,IAAI,OAAO,SAAS,MAAM;AAC3B,MAAI,WAA4B;AAChC,MAAI,WAA4B;AAEhC,QAAM,aAAa;IACjB,OAAO,eAAe,WAAW,aAAa,WAAW;IACzD,MAAM;EACR;AAEA,MAAI,CAAC,YAAY;AACf,WAAO;EACT;AAEA,MAAI,eAAe,QAAQ;AACzB,eAAW,YAAY,YAAwB,MAAM,MAAM;EAC7D;AAEA,MAAI,eAAe,QAAQ;AACzB,eAAW,YAAY,YAAwB,MAAM,MAAM;EAC7D;AAEA,MAAI,YAAY;AAEhB,KAAG,UAAU,OAAO,QAAQ,CAAC,UAA0B;AACrD,UAAM,OAAO,MAAM,MAAM;AACzB,UAAM,KAAK,MAAM,IAAI;AAErB,QAAI;AACJ,QAAI;AACJ,QAAI;AACJ,QAAI;AAEJ,QAAI,GAAG,UAAU,OAAO;AACtB,YAAM,IAAI,aAAa,MAAM,IAAI,CAAC,MAAY,QAAgB;AAC5D,YAAI,YAAY,aAAa,KAAK,MAAM;AACtC,sBAAY;AACZ,wBAAc,KAAK,IAAI,KAAK,IAAI;AAChC,sBAAY,KAAK,IAAI,MAAM,KAAK,UAAU,EAAE;AAC5C,oBAAU;AACV,qBAAW;QACb;MACF,CAAC;IACH,OAAO;AACL,YAAM,IAAI,aAAa,MAAM,IAAI,CAAC,MAAY,QAAgB;AAC5D,YAAI,MAAM,QAAQ,YAAY,aAAa,KAAK,MAAM;AACpD,sBAAY;AACZ,wBAAc,KAAK,IAAI,KAAK,IAAI;AAChC,sBAAY,KAAK,IAAI,MAAM,KAAK,UAAU,EAAE;AAC5C,oBAAU;AACV,qBAAW;QACb;AAEA,YAAI,OAAO,QAAQ,OAAO,IAAI;AAC5B,cAAI,YAAY,aAAa,KAAK,MAAM;AACtC,wBAAY;AAEZ,gBAAI,UAAU;AACZ,iBAAG,cAAc,KAAK,QAAW;gBAC/B,GAAG,KAAK;gBACR,GAAG;cACL,CAAC;YACH;UACF;AAEA,cAAI,YAAY,KAAK,MAAM,QAAQ;AACjC,iBAAK,MAAM,QAAQ,CAAC,SAAe;AACjC,kBAAI,aAAa,KAAK,MAAM;AAC1B,4BAAY;AAEZ,oBAAI,UAAU;AACZ,wBAAM,eAAe,KAAK,IAAI,KAAK,IAAI;AACvC,wBAAM,aAAa,KAAK,IAAI,MAAM,KAAK,UAAU,EAAE;AAEnD,qBAAG;oBACD;oBACA;oBACA,SAAS,OAAO;sBACd,GAAG,KAAK;sBACR,GAAG;oBACL,CAAC;kBACH;gBACF;cACF;YACF,CAAC;UACH;QACF;MACF,CAAC;IACH;AAEA,QAAI,UAAU;AACZ,UAAI,YAAY,UAAa,UAAU;AACrC,WAAG,cAAc,SAAS,QAAW;UACnC,GAAG,SAAS;UACZ,GAAG;QACL,CAAC;MACH;AAEA,UAAI,YAAY,SAAS,MAAM,QAAQ;AACrC,iBAAS,MAAM,QAAQ,CAAC,SAAe;AACrC,cAAI,aAAa,KAAK,QAAQ,UAAU;AACtC,eAAG;cACD;cACA;cACA,SAAS,OAAO;gBACd,GAAG,KAAK;gBACR,GAAG;cACL,CAAC;YACH;UACF;QACF,CAAC;MACH;IACF;EACF,CAAC;AAED,SAAO;AACT;ACjIK,IAAM,SACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AAEjD,SAAO,eAAe,MAAM,UAAU,EAAE,OAAO,QAAQ;AACzD;ACNK,IAAM,aACX,CAAC,YAAY,aAAa,CAAC,MAC3B,CAAC,EAAE,OAAO,SAAS,MAAM;AACvB,QAAM,OAAO,YAAY,YAAY,MAAM,MAAM;AAEjD,SAAO,mBAAmB,MAAM,UAAU,EAAE,OAAO,QAAQ;AAC7D;AI6BF,IAAM,0BAA0B,CAAC,MAAc,SAA2D;AACxG,MAAI,SAAS,IAAI,GAAG;AAClB,WAAO,KAAK,KAAK,IAAI;EACvB;AAEA,QAAM,iBAAiB,KAAK,IAAI;AAEhC,MAAI,CAAC,gBAAgB;AACnB,WAAO;EACT;AAEA,QAAM,SAAmC,CAAC,eAAe,IAAI;AAE7D,SAAO,QAAQ,eAAe;AAC9B,SAAO,QAAQ;AACf,SAAO,OAAO,eAAe;AAE7B,MAAI,eAAe,aAAa;AAC9B,QAAI,CAAC,eAAe,KAAK,SAAS,eAAe,WAAW,GAAG;AAC7D,cAAQ,KAAK,oFAAoF;IACnG;AAEA,WAAO,KAAK,eAAe,WAAW;EACxC;AAEA,SAAO;AACT;AAEA,SAAS,IAAI,QAOD;AA1FZ,MAAA;AA2FE,QAAM,EAAE,QAAQ,MAAM,IAAI,MAAM,OAAO,OAAO,IAAI;AAClD,QAAM,EAAE,KAAK,IAAI;AAEjB,MAAI,KAAK,WAAW;AAClB,WAAO;EACT;AAEA,QAAM,QAAQ,KAAK,MAAM,IAAI,QAAQ,IAAI;AAEzC;;IAEE,MAAM,OAAO,KAAK,KAAK;IAEvB,CAAC,GAAE,KAAA,MAAM,cAAc,MAAM,cAA1B,OAAA,SAAA,GAAsC,MAAM,KAAK,CAAA,SAAQ,KAAK,KAAK,KAAK,IAAA;IAC3E;AACA,WAAO;EACT;AAEA,MAAI,UAAU;AAEd,QAAM,aAAa,wBAAwB,KAAK,IAAI;AAEpD,QAAM,QAAQ,CAAA,SAAQ;AACpB,QAAI,SAAS;AACX;IACF;AAEA,UAAM,QAAQ,wBAAwB,YAAY,KAAK,IAAI;AAE3D,QAAI,CAAC,OAAO;AACV;IACF;AAEA,UAAM,KAAK,KAAK,MAAM;AACtB,UAAM,QAAQ,qBAAqB;MACjC,OAAO,KAAK;MACZ,aAAa;IACf,CAAC;AACD,UAAM,QAAQ;MACZ,MAAM,QAAQ,MAAM,CAAC,EAAE,SAAS,KAAK;MACrC;IACF;AAEA,UAAM,EAAE,UAAU,OAAO,IAAI,IAAI,IAAI,eAAe;MAClD;MACA;IACF,CAAC;AAED,UAAM,UAAU,KAAK,QAAQ;MAC3B;MACA;MACA;MACA;MACA;MACA;IACF,CAAC;AAGD,QAAI,YAAY,QAAQ,CAAC,GAAG,MAAM,QAAQ;AACxC;IACF;AAIA,QAAI,KAAK,UAAU;AACjB,SAAG,QAAQ,QAAQ;QACjB,WAAW;QACX;QACA;QACA;MACF,CAAC;IACH;AAEA,SAAK,SAAS,EAAE;AAChB,cAAU;EACZ,CAAC;AAED,SAAO;AACT;AAOO,SAAS,iBAAiB,OAAuD;AACtF,QAAM,EAAE,QAAQ,MAAM,IAAI;AAC1B,QAAM,SAAS,IAAI,OAAO;IACxB,OAAO;MACL,OAAO;AACL,eAAO;MACT;MACA,MAAM,IAAI,MAAM,OAAO;AACrB,cAAM,SAAS,GAAG,QAAQ,MAAM;AAEhC,YAAI,QAAQ;AACV,iBAAO;QACT;AAGA,cAAM,qBAAqB,GAAG,QAAQ,iBAAiB;AAMvD,cAAM,mBAAmB,CAAC,CAAC;AAE3B,YAAI,kBAAkB;AACpB,qBAAW,MAAM;AACf,gBAAI,EAAE,KAAK,IAAI;AAEf,gBAAI,OAAO,SAAS,UAAU;AAC5B,qBAAO;YACT,OAAO;AACL,qBAAO,oBAAoBC,WAAS,KAAK,IAAI,GAAG,MAAM,MAAM;YAC9D;AAEA,kBAAM,EAAE,KAAK,IAAI;AACjB,kBAAM,KAAK,OAAO,KAAK;AAEvB,gBAAI;cACF;cACA;cACA;cACA;cACA;cACA;YACF,CAAC;UACH,CAAC;QACH;AAEA,eAAO,GAAG,gBAAgB,GAAG,aAAa,OAAO;MACnD;IACF;IAEA,OAAO;MACL,gBAAgB,MAAM,MAAM,IAAI,MAAM;AACpC,eAAO,IAAI;UACT;UACA;UACA;UACA;UACA;UACA;QACF,CAAC;MACH;MAEA,iBAAiB;QACf,gBAAgB,CAAA,SAAQ;AACtB,qBAAW,MAAM;AACf,kBAAM,EAAE,QAAQ,IAAI,KAAK,MAAM;AAE/B,gBAAI,SAAS;AACX,kBAAI;gBACF;gBACA,MAAM,QAAQ;gBACd,IAAI,QAAQ;gBACZ,MAAM;gBACN;gBACA;cACF,CAAC;YACH;UACF,CAAC;AAED,iBAAO;QACT;MACF;;;MAIA,cAAc,MAAM,OAAO;AACzB,YAAI,MAAM,QAAQ,SAAS;AACzB,iBAAO;QACT;AAEA,cAAM,EAAE,QAAQ,IAAI,KAAK,MAAM;AAE/B,YAAI,SAAS;AACX,iBAAO,IAAI;YACT;YACA,MAAM,QAAQ;YACd,IAAI,QAAQ;YACZ,MAAM;YACN;YACA;UACF,CAAC;QACH;AAEA,eAAO;MACT;IACF;;IAGA,cAAc;EAChB,CAAC;AAED,SAAO;AACT;AC/RA,SAAS,QAAQ,OAAoB;AACnC,SAAO,OAAO,UAAU,SAAS,KAAK,KAAK,EAAE,MAAM,GAAG,EAAE;AAC1D;AAEO,SAAS,cAAc,OAA0C;AACtE,MAAI,QAAQ,KAAK,MAAM,UAAU;AAC/B,WAAO;EACT;AAEA,SAAO,MAAM,gBAAgB,UAAU,OAAO,eAAe,KAAK,MAAM,OAAO;AACjF;ACVO,SAAS,UAAU,QAA6B,QAAkD;AACvG,QAAM,SAAS,EAAE,GAAG,OAAO;AAE3B,MAAI,cAAc,MAAM,KAAK,cAAc,MAAM,GAAG;AAClD,WAAO,KAAK,MAAM,EAAE,QAAQ,CAAA,QAAO;AACjC,UAAI,cAAc,OAAO,GAAG,CAAC,KAAK,cAAc,OAAO,GAAG,CAAC,GAAG;AAC5D,eAAO,GAAG,IAAI,UAAU,OAAO,GAAG,GAAG,OAAO,GAAG,CAAC;MAClD,OAAO;AACL,eAAO,GAAG,IAAI,OAAO,GAAG;MAC1B;IACF,CAAC;EACH;AAEA,SAAO;AACT;AC+cO,IAAM,aAAN,MAIL;EAYA,YAAY,SAA0B,CAAC,GAAG;AAX1C,SAAA,OAAO;AACP,SAAA,SAA4B;AAE5B,SAAA,QAA2B;AAE3B,SAAA,OAAO;AAEP,SAAA,SAAiB;MACf,MAAM,KAAK;IACb;AAGE,SAAK,SAAS;MACZ,GAAG,KAAK;MACR,GAAG;IACL;AAEA,SAAK,OAAQ,KAAK,OAAe;EACnC;EAEA,IAAI,UAAmB;AACrB,WAAO;MACL,GAAI;QACF,kBAA2C,MAAa,cAAc;UACpE,MAAM,KAAK;QACb,CAAC;MACH,KAAK,CAAC;IACR;EACF;EAEA,IAAI,UAA6B;AAC/B,WAAO;MACL,GAAI;QACF,kBAA2C,MAAa,cAAc;UACpE,MAAM,KAAK;UACX,SAAS,KAAK;QAChB,CAAC;MACH,KAAK,CAAC;IACR;EACF;EAEA,UAAU,UAA4B,CAAC,GAAG;AACxC,UAAM,YAAY,KAAK,OAAiC;MACtD,GAAG,KAAK;MACR,YAAY,MAAM;AAChB,eAAO,UAAU,KAAK,SAAgC,OAAO;MAC/D;IACF,CAAC;AAED,cAAU,OAAO,KAAK;AACtB,cAAU,SAAS,KAAK;AAExB,WAAO;EACT;EAEA,OAOE,iBAA0C,CAAC,GAAiD;AAC5F,UAAM,YAAY,IAAK,KAAK,YAAoB,EAAE,GAAG,KAAK,QAAQ,GAAG,eAAe,CAAC;AAErF,cAAU,SAAS;AACnB,SAAK,QAAQ;AACb,cAAU,OAAO,UAAU,iBAAiB,eAAe,OAAO,UAAU,OAAO;AAEnF,WAAO;EACT;AACF;AC1ZO,IAAM,OAAN,MAAM,cAA2C,WAA2D;EAA5G,cAAA;AAAA,UAAA,GAAA,SAAA;AACL,SAAA,OAAO;EAAA;;;;;EAMP,OAAO,OAAyB,SAAwE,CAAC,GAAG;AAE1G,UAAM,iBAAiB,OAAO,WAAW,aAAa,OAAO,IAAI;AACjE,WAAO,IAAI,MAAW,cAAc;EACtC;EAEA,OAAO,WAAW,EAAE,QAAQ,KAAK,GAAmC;AAClE,UAAM,EAAE,GAAG,IAAI,OAAO;AACtB,UAAM,aAAa,OAAO,MAAM,UAAU;AAC1C,UAAM,UAAU,WAAW,QAAQ,WAAW,IAAI;AAElD,QAAI,SAAS;AACX,YAAM,eAAe,WAAW,MAAM;AACtC,YAAM,WAAW,CAAC,CAAC,aAAa,KAAK,CAAA,OAAK,KAAA,OAAA,SAAA,EAAG,KAAK,UAAS,KAAK,IAAI;AAEpE,UAAI,CAAC,UAAU;AACb,eAAO;MACT;AAEA,YAAM,aAAa,aAAa,KAAK,CAAA,OAAK,KAAA,OAAA,SAAA,EAAG,KAAK,UAAS,KAAK,IAAI;AAEpE,UAAI,YAAY;AACd,WAAG,iBAAiB,UAAU;MAChC;AACA,SAAG,WAAW,KAAK,WAAW,GAAG;AAEjC,aAAO,KAAK,SAAS,EAAE;AAEvB,aAAO;IACT;AAEA,WAAO;EACT;EAEA,UAAU,SAA4B;AACpC,WAAO,MAAM,UAAU,OAAO;EAChC;EAEA,OAKE,gBAUwC;AAExC,UAAM,iBAAiB,OAAO,mBAAmB,aAAa,eAAe,IAAI;AACjF,WAAO,MAAM,OAAO,cAAc;EACpC;AACF;AElNO,SAAS,SAAS,OAA6B;AACpD,SAAO,OAAO,UAAU;AAC1B;AD2DA,IAAM,0BAA0B,CAC9B,MACA,MACA,UAC+B;AAC/B,MAAI,SAAS,IAAI,GAAG;AAClB,WAAO,CAAC,GAAG,KAAK,SAAS,IAAI,CAAC;EAChC;AAEA,QAAM,UAAU,KAAK,MAAM,KAAK;AAEhC,MAAI,CAAC,SAAS;AACZ,WAAO,CAAC;EACV;AAEA,SAAO,QAAQ,IAAI,CAAA,mBAAkB;AACnC,UAAM,SAAmC,CAAC,eAAe,IAAI;AAE7D,WAAO,QAAQ,eAAe;AAC9B,WAAO,QAAQ;AACf,WAAO,OAAO,eAAe;AAE7B,QAAI,eAAe,aAAa;AAC9B,UAAI,CAAC,eAAe,KAAK,SAAS,eAAe,WAAW,GAAG;AAC7D,gBAAQ,KAAK,oFAAoF;MACnG;AAEA,aAAO,KAAK,eAAe,WAAW;IACxC;AAEA,WAAO;EACT,CAAC;AACH;AAEA,SAASC,KAAI,QAQD;AACV,QAAM,EAAE,QAAQ,OAAO,MAAM,IAAI,MAAM,YAAY,UAAU,IAAI;AAEjE,QAAM,EAAE,UAAU,OAAO,IAAI,IAAI,IAAI,eAAe;IAClD;IACA;EACF,CAAC;AAED,QAAM,WAA4B,CAAC;AAEnC,QAAM,IAAI,aAAa,MAAM,IAAI,CAAC,MAAM,QAAQ;AAjHlD,QAAA,IAAA,IAAA,IAAA,IAAA;AAqHI,UAAI,MAAA,KAAA,KAAK,SAAL,OAAA,SAAA,GAAW,SAAX,OAAA,SAAA,GAAiB,SAAQ,EAAE,KAAK,UAAU,KAAK,eAAe,KAAK,WAAW;AAChF;IACF;AAKA,UAAM,eAAc,MAAA,MAAA,KAAA,KAAK,YAAL,OAAA,SAAA,GAAc,SAAd,OAAA,KAAsB,KAAK,aAA3B,OAAA,KAAuC;AAC3D,UAAM,eAAe,KAAK,IAAI,MAAM,GAAG;AACvC,UAAM,aAAa,KAAK,IAAI,IAAI,MAAM,WAAW;AAKjD,QAAI,gBAAgB,YAAY;AAC9B;IACF;AAEA,UAAM,cAAc,KAAK,SACrB,KAAK,QAAQ,KACb,KAAK,YAAY,eAAe,KAAK,aAAa,KAAK,QAAW,QAAQ;AAE9E,UAAM,UAAU,wBAAwB,aAAa,KAAK,MAAM,UAAU;AAE1E,YAAQ,QAAQ,CAAA,UAAS;AACvB,UAAI,MAAM,UAAU,QAAW;AAC7B;MACF;AAEA,YAAM,QAAQ,eAAe,MAAM,QAAQ;AAC3C,YAAM,MAAM,QAAQ,MAAM,CAAC,EAAE;AAC7B,YAAM,QAAQ;QACZ,MAAM,MAAM,GAAG,QAAQ,IAAI,KAAK;QAChC,IAAI,MAAM,GAAG,QAAQ,IAAI,GAAG;MAC9B;AAEA,YAAM,UAAU,KAAK,QAAQ;QAC3B;QACA;QACA;QACA;QACA;QACA;QACA;QACA;MACF,CAAC;AAED,eAAS,KAAK,OAAO;IACvB,CAAC;EACH,CAAC;AAED,QAAM,UAAU,SAAS,MAAM,CAAA,YAAW,YAAY,IAAI;AAE1D,SAAO;AACT;AAGA,IAAI,4BAA2C;AAE/C,IAAM,4BAA4B,CAAC,SAAiB;AAhLpD,MAAA;AAiLE,QAAM,QAAQ,IAAI,eAAe,SAAS;IACxC,eAAe,IAAI,aAAa;EAClC,CAAC;AAED,GAAA,KAAA,MAAM,kBAAN,OAAA,SAAA,GAAqB,QAAQ,aAAa,IAAA;AAE1C,SAAO;AACT;AAOO,SAAS,iBAAiB,OAAyD;AACxF,QAAM,EAAE,QAAQ,MAAM,IAAI;AAC1B,MAAI,oBAAoC;AACxC,MAAI,0BAA0B;AAC9B,MAAI,2BAA2B;AAC/B,MAAI,aAAa,OAAO,mBAAmB,cAAc,IAAI,eAAe,OAAO,IAAI;AACvF,MAAI;AAEJ,MAAI;AACF,gBAAY,OAAO,cAAc,cAAc,IAAI,UAAU,MAAM,IAAI;EACzE,QAAQ;AACN,gBAAY;EACd;AAEA,QAAM,eAAe,CAAC;IACpB;IACA;IACA;IACA;IACA;EACF,MAMM;AACJ,UAAM,KAAK,MAAM;AACjB,UAAM,iBAAiB,qBAAqB;MAC1C;MACA,aAAa;IACf,CAAC;AAED,UAAM,UAAUA,KAAI;MAClB;MACA,OAAO;MACP,MAAM,KAAK,IAAI,OAAO,GAAG,CAAC;MAC1B,IAAI,GAAG,IAAI;MACX;MACA,YAAY;MACZ;IACF,CAAC;AAED,QAAI,CAAC,WAAW,CAAC,GAAG,MAAM,QAAQ;AAChC;IACF;AAEA,QAAI;AACF,kBAAY,OAAO,cAAc,cAAc,IAAI,UAAU,MAAM,IAAI;IACzE,QAAQ;AACN,kBAAY;IACd;AACA,iBAAa,OAAO,mBAAmB,cAAc,IAAI,eAAe,OAAO,IAAI;AAEnF,WAAO;EACT;AAEA,QAAM,UAAU,MAAM,IAAI,CAAA,SAAQ;AAChC,WAAO,IAAIC,QAAO;;MAEhB,KAAK,MAAM;AACT,cAAM,kBAAkB,CAAC,UAAqB;AA5PtD,cAAA;AA6PU,gCAAoB,KAAA,KAAK,IAAI,kBAAT,OAAA,SAAA,GAAwB,SAAS,MAAM,MAAA,KAAqB,KAAK,IAAI,gBAAgB;AAEzG,cAAI,mBAAmB;AACrB,wCAA4B;UAC9B;QACF;AAEA,cAAM,gBAAgB,MAAM;AAC1B,cAAI,2BAA2B;AAC7B,wCAA4B;UAC9B;QACF;AAEA,eAAO,iBAAiB,aAAa,eAAe;AACpD,eAAO,iBAAiB,WAAW,aAAa;AAEhD,eAAO;UACL,UAAU;AACR,mBAAO,oBAAoB,aAAa,eAAe;AACvD,mBAAO,oBAAoB,WAAW,aAAa;UACrD;QACF;MACF;MAEA,OAAO;QACL,iBAAiB;UACf,MAAM,CAAC,MAAM,UAAiB;AAC5B,uCAA2B,sBAAsB,KAAK,IAAI;AAC1D,wBAAY;AAEZ,gBAAI,CAAC,0BAA0B;AAC7B,oBAAM,sBAAsB;AAE5B,kBAAI,uBAAA,OAAA,SAAA,oBAAqB,YAAY;AAEnC,2BAAW,MAAM;AACf,wBAAM,YAAY,oBAAoB,MAAM;AAE5C,sBAAI,WAAW;AACb,wCAAoB,SAAS,YAAY,EAAE,MAAM,UAAU,MAAM,IAAI,UAAU,GAAG,CAAC;kBACrF;gBACF,GAAG,EAAE;cACP;YACF;AACA,mBAAO;UACT;UAEA,OAAO,CAAC,OAAO,UAAiB;AA5S1C,gBAAA;AA6SY,kBAAM,QAAQ,KAAA,MAAyB,kBAAzB,OAAA,SAAA,GAAwC,QAAQ,WAAA;AAE9D,yBAAa;AAEb,sCAA0B,CAAC,EAAC,QAAA,OAAA,SAAA,KAAM,SAAS,eAAA;AAE3C,mBAAO;UACT;QACF;MACF;MAEA,mBAAmB,CAAC,cAAc,UAAU,UAAU;AACpD,cAAM,cAAc,aAAa,CAAC;AAClC,cAAM,UAAU,YAAY,QAAQ,SAAS,MAAM,WAAW,CAAC;AAC/D,cAAM,SAAS,YAAY,QAAQ,SAAS,MAAM,UAAU,CAAC;AAG7D,cAAM,qBAAqB,YAAY,QAAQ,iBAAiB;AAGhE,cAAM,mBAAmB,CAAC,CAAC;AAE3B,YAAI,CAAC,WAAW,CAAC,UAAU,CAAC,kBAAkB;AAC5C;QACF;AAGA,YAAI,kBAAkB;AACpB,cAAI,EAAE,KAAK,IAAI;AAEf,cAAI,OAAO,SAAS,UAAU;AAC5B,mBAAO;UACT,OAAO;AACL,mBAAO,oBAAoBC,WAAS,KAAK,IAAI,GAAG,MAAM,MAAM;UAC9D;AAEA,gBAAM,EAAE,MAAAC,MAAK,IAAI;AACjB,gBAAMC,MAAKD,QAAO,KAAK;AAEvB,gBAAM,WAAW,0BAA0B,IAAI;AAE/C,iBAAO,aAAa;YAClB;YACA;YACA,MAAAA;YACA,IAAI,EAAE,GAAGC,IAAG;YACZ;UACF,CAAC;QACH;AAGA,cAAM,OAAO,SAAS,IAAI,QAAQ,cAAc,MAAM,IAAI,OAAO;AACjE,cAAM,KAAK,SAAS,IAAI,QAAQ,YAAY,MAAM,IAAI,OAAO;AAG7D,YAAI,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,SAAS,GAAG,GAAG;AAC3C;QACF;AAEA,eAAO,aAAa;UAClB;UACA;UACA;UACA;UACA,UAAU;QACZ,CAAC;MACH;IACF,CAAC;EACH,CAAC;AAED,SAAO;AACT;AN1VO,IAAM,mBAAN,MAAuB;EAiB5B,YAAY,YAAwB,QAAgB;AAFpD,SAAA,kBAA4B,CAAC;AAG3B,SAAK,SAAS;AACd,SAAK,iBAAiB;AACtB,SAAK,aAAa,kBAAkB,UAAU;AAC9C,SAAK,SAAS,8BAA8B,KAAK,YAAY,MAAM;AACnE,SAAK,gBAAgB;EACvB;;;;;EAYA,IAAI,WAAwB;AAC1B,WAAO,KAAK,WAAW,OAAO,CAAC,UAAU,cAAc;AACrD,YAAM,UAAU;QACd,MAAM,UAAU;QAChB,SAAS,UAAU;QACnB,SAAS,KAAK,OAAO,iBAAiB,UAAU,IAAqB;QACrE,QAAQ,KAAK;QACb,MAAM,oBAAoB,UAAU,MAAM,KAAK,MAAM;MACvD;AAEA,YAAM,cAAc,kBAA4C,WAAW,eAAe,OAAO;AAEjG,UAAI,CAAC,aAAa;AAChB,eAAO;MACT;AAEA,aAAO;QACL,GAAG;QACH,GAAG,YAAY;MACjB;IACF,GAAG,CAAC,CAAgB;EACtB;;;;;EAMA,IAAI,UAAoB;AACtB,UAAM,EAAE,OAAO,IAAI;AAOnB,UAAM,aAAa,eAAe,CAAC,GAAG,KAAK,UAAU,EAAE,QAAQ,CAAC;AAEhE,UAAM,aAAa,WAAW,QAAQ,CAAA,cAAa;AACjD,YAAM,UAAU;QACd,MAAM,UAAU;QAChB,SAAS,UAAU;QACnB,SAAS,KAAK,OAAO,iBAAiB,UAAU,IAAqB;QACrE;QACA,MAAM,oBAAoB,UAAU,MAAM,KAAK,MAAM;MACvD;AAEA,YAAM,UAAoB,CAAC;AAE3B,YAAM,uBAAuB;QAC3B;QACA;QACA;MACF;AAEA,UAAI,kBAAiD,CAAC;AAGtD,UAAI,UAAU,SAAS,UAAU,kBAA0C,WAAW,YAAY,OAAO,GAAG;AAC1G,wBAAgB,aAAa,MAAM,KAAK,WAAW,EAAE,QAAQ,MAAM,UAAkB,CAAC;MACxF;AAEA,UAAI,sBAAsB;AACxB,cAAM,WAAW,OAAO;UACtB,OAAO,QAAQ,qBAAqB,CAAC,EAAE,IAAI,CAAC,CAAC,UAAU,MAAM,MAAM;AACjE,mBAAO,CAAC,UAAU,MAAM,OAAO,EAAE,OAAO,CAAC,CAAC;UAC5C,CAAC;QACH;AAEA,0BAAkB,EAAE,GAAG,iBAAiB,GAAG,SAAS;MACtD;AAEA,YAAM,eAAe,OAAO,eAAe;AAE3C,cAAQ,KAAK,YAAY;AAEzB,YAAM,gBAAgB,kBAA8C,WAAW,iBAAiB,OAAO;AAEvG,UAAI,wBAAwB,WAAW,OAAO,QAAQ,gBAAgB,KAAK,eAAe;AACxF,cAAM,QAAQ,cAAc;AAE5B,YAAI,SAAS,MAAM,QAAQ;AACzB,gBAAM,cAAc,iBAAiB;YACnC;YACA;UACF,CAAC;AAED,gBAAM,eAAe,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW;AAE5E,kBAAQ,KAAK,GAAG,YAAY;QAC9B;MACF;AAEA,YAAM,gBAAgB,kBAA8C,WAAW,iBAAiB,OAAO;AAEvG,UAAI,wBAAwB,WAAW,OAAO,QAAQ,gBAAgB,KAAK,eAAe;AACxF,cAAM,QAAQ,cAAc;AAE5B,YAAI,SAAS,MAAM,QAAQ;AACzB,gBAAM,aAAa,iBAAiB,EAAE,QAAQ,MAAM,CAAC;AAErD,kBAAQ,KAAK,GAAG,UAAU;QAC5B;MACF;AAEA,YAAM,wBAAwB;QAC5B;QACA;QACA;MACF;AAEA,UAAI,uBAAuB;AACzB,cAAM,qBAAqB,sBAAsB;AAEjD,gBAAQ,KAAK,GAAG,kBAAkB;MACpC;AAEA,aAAO;IACT,CAAC;AAED,WAAO;EACT;;;;;EAMA,IAAI,aAAa;AACf,WAAO,4BAA4B,KAAK,UAAU;EACpD;;;;;EAMA,IAAI,YAAiD;AACnD,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,EAAE,eAAe,IAAI,gBAAgB,KAAK,UAAU;AAE1D,WAAO,OAAO;MACZ,eACG,OAAO,CAAA,cAAa,CAAC,CAAC,kBAAkB,WAAW,aAAa,CAAC,EACjE,IAAI,CAAA,cAAa;AAChB,cAAM,sBAAsB,KAAK,WAAW,OAAO,CAAA,cAAa,UAAU,SAAS,UAAU,IAAI;AACjG,cAAM,UAAU;UACd,MAAM,UAAU;UAChB,SAAS,UAAU;UACnB,SAAS,KAAK,OAAO,iBAAiB,UAAU,IAAqB;UACrE;UACA,MAAM,YAAY,UAAU,MAAM,KAAK,MAAM;QAC/C;AACA,cAAM,cAAc,kBAA6C,WAAW,eAAe,OAAO;AAElG,YAAI,CAAC,aAAa;AAChB,iBAAO,CAAC;QACV;AAEA,cAAM,iBAAiB,YAAY;AAEnC,YAAI,CAAC,gBAAgB;AACnB,iBAAO,CAAC;QACV;AAEA,cAAM,WAAgC,CAAC,MAAM,MAAM,QAAQ,aAAa,qBAAqB;AAC3F,gBAAM,iBAAiB,sBAAsB,MAAM,mBAAmB;AAEtE,iBAAO,eAAe;;YAEpB;YACA;YACA;YACA;YACA;;YAEA;YACA;YACA;UACF,CAAC;QACH;AAEA,eAAO,CAAC,UAAU,MAAM,QAAQ;MAClC,CAAC;IACL;EACF;;;;;;EAOA,oBAAoB,cAAoE;AACtF,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,aAAa,eAAe,CAAC,GAAG,KAAK,UAAU,EAAE,QAAQ,CAAC;AAEhE,WAAO,WAAW,YAAY,CAAC,MAAM,cAAc;AACjD,YAAM,UAAU;QACd,MAAM,UAAU;QAChB,SAAS,UAAU;QACnB,SAAS,KAAK,OAAO,iBAAiB,UAAU,IAAqB;QACrE;QACA,MAAM,oBAAoB,UAAU,MAAM,KAAK,MAAM;MACvD;AAEA,YAAM,sBAAsB;QAC1B;QACA;QACA;MACF;AAEA,UAAI,CAAC,qBAAqB;AACxB,eAAO;MACT;AAEA,aAAO,CAAC,gBAA6B;AACnC,4BAAoB,KAAK,SAAS,EAAE,aAAa,KAAK,CAAC;MACzD;IACF,GAAG,YAAY;EACjB;EAEA,IAAI,YAAiD;AACnD,UAAM,EAAE,OAAO,IAAI;AACnB,UAAM,EAAE,eAAe,IAAI,gBAAgB,KAAK,UAAU;AAE1D,WAAO,OAAO;MACZ,eACG,OAAO,CAAA,cAAa,CAAC,CAAC,kBAAkB,WAAW,aAAa,CAAC,EACjE,IAAI,CAAA,cAAa;AAChB,cAAM,sBAAsB,KAAK,WAAW,OAAO,CAAA,cAAa,UAAU,SAAS,UAAU,IAAI;AACjG,cAAM,UAAU;UACd,MAAM,UAAU;UAChB,SAAS,UAAU;UACnB,SAAS,KAAK,OAAO,iBAAiB,UAAU,IAAqB;UACrE;UACA,MAAM,YAAY,UAAU,MAAM,KAAK,MAAM;QAC/C;AACA,cAAM,cAAc,kBAA6C,WAAW,eAAe,OAAO;AAElG,YAAI,CAAC,aAAa;AAChB,iBAAO,CAAC;QACV;AAEA,cAAM,WAAgC,CAAC,MAAM,MAAM,WAAW;AAC5D,gBAAM,iBAAiB,sBAAsB,MAAM,mBAAmB;AAEtE,iBAAO,YAAY,EAAE;;YAEnB;YACA;YACA;;YAEA;YACA;YACA;YACA,kBAAkB,CAAC,UAA+B;AAChD,uCAAyB,MAAM,QAAQ,KAAK;YAC9C;UACF,CAAC;QACH;AAEA,eAAO,CAAC,UAAU,MAAM,QAAQ;MAClC,CAAC;IACL;EACF;;;;;EAMQ,kBAAkB;AACxB,UAAM,aAAa,KAAK;AAExB,SAAK,OAAO,mBAAmB,OAAO;MACpC,WAAW,IAAI,CAAA,cAAa,CAAC,UAAU,MAAM,UAAU,OAAO,CAAC;IACjE;AAEA,eAAW,QAAQ,CAAA,cAAa;AA/UpC,UAAA;AAgVM,YAAM,UAAU;QACd,MAAM,UAAU;QAChB,SAAS,UAAU;QACnB,SAAS,KAAK,OAAO,iBAAiB,UAAU,IAAqB;QACrE,QAAQ,KAAK;QACb,MAAM,oBAAoB,UAAU,MAAM,KAAK,MAAM;MACvD;AAEA,UAAI,UAAU,SAAS,QAAQ;AAC7B,cAAM,eAAc,KAAA,aAAa,kBAAkB,WAAW,eAAe,OAAO,CAAC,MAAjE,OAAA,KAAsE;AAE1F,YAAI,aAAa;AACf,eAAK,gBAAgB,KAAK,UAAU,IAAI;QAC1C;MACF;AAEA,YAAM,iBAAiB,kBAA+C,WAAW,kBAAkB,OAAO;AAC1G,YAAM,WAAW,kBAAyC,WAAW,YAAY,OAAO;AACxF,YAAM,WAAW,kBAAyC,WAAW,YAAY,OAAO;AACxF,YAAM,oBAAoB;QACxB;QACA;QACA;MACF;AACA,YAAM,gBAAgB,kBAA8C,WAAW,iBAAiB,OAAO;AACvG,YAAM,UAAU,kBAAwC,WAAW,WAAW,OAAO;AACrF,YAAM,SAAS,kBAAuC,WAAW,UAAU,OAAO;AAClF,YAAM,YAAY,kBAA0C,WAAW,aAAa,OAAO;AAE3F,UAAI,gBAAgB;AAClB,aAAK,OAAO,GAAG,gBAAgB,cAAc;MAC/C;AAEA,UAAI,UAAU;AACZ,aAAK,OAAO,GAAG,UAAU,QAAQ;MACnC;AAEA,UAAI,UAAU;AACZ,aAAK,OAAO,GAAG,UAAU,QAAQ;MACnC;AAEA,UAAI,mBAAmB;AACrB,aAAK,OAAO,GAAG,mBAAmB,iBAAiB;MACrD;AAEA,UAAI,eAAe;AACjB,aAAK,OAAO,GAAG,eAAe,aAAa;MAC7C;AAEA,UAAI,SAAS;AACX,aAAK,OAAO,GAAG,SAAS,OAAO;MACjC;AAEA,UAAI,QAAQ;AACV,aAAK,OAAO,GAAG,QAAQ,MAAM;MAC/B;AAEA,UAAI,WAAW;AACb,aAAK,OAAO,GAAG,WAAW,SAAS;MACrC;IACF,CAAC;EACH;AACF;AApXa,iBAyBJ,UAAU;AAzBN,iBA2BJ,OAAO;AA3BH,iBA6BJ,UAAU;AQvDnB,IAAA,qBAAA,CAAA;AAAA,SAAA,oBAAA;EAAA,yBAAA,MAAA;EAAA,UAAA,MAAA;EAAA,QAAA,MAAA;EAAA,MAAA,MAAA;EAAA,UAAA,MAAA;EAAA,aAAA,MAAA;EAAA,QAAA,MAAA;EAAA,OAAA,MAAA;EAAA,UAAA,MAAA;EAAA,eAAA,MAAA;EAAA,sBAAA,MAAA;AAAA,CAAA;AEWO,IAAM,YAAN,MAAM,mBAAgD,WAI3D;EAJK,cAAA;AAAA,UAAA,GAAA,SAAA;AAKL,SAAA,OAAO;EAAA;;;;;EAMP,OAAO,OACL,SAAkF,CAAC,GACnF;AAEA,UAAM,iBAAiB,OAAO,WAAW,aAAa,OAAO,IAAI;AACjE,WAAO,IAAI,WAAgB,cAAc;EAC3C;EAEA,UAAU,SAA4B;AACpC,WAAO,MAAM,UAAU,OAAO;EAChC;EAEA,OAKE,gBAU6C;AAE7C,UAAM,iBAAiB,OAAO,mBAAmB,aAAa,eAAe,IAAI;AACjF,WAAO,MAAM,OAAO,cAAc;EACpC;AACF;AD5CO,IAAM,0BAA0B,UAAU,OAAuC;EACtF,MAAM;EAEN,aAAa;AACX,WAAO;MACL,gBAAgB;IAClB;EACF;EAEA,wBAAwB;AACtB,WAAO;MACL,IAAIH,QAAO;QACT,KAAK,IAAI,UAAU,yBAAyB;QAC5C,OAAO;UACL,yBAAyB,MAAM;AAC7B,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,EAAE,OAAO,OAAO,IAAI;AAC1B,kBAAM,EAAE,KAAK,UAAU,IAAI;AAC3B,kBAAM,EAAE,OAAO,IAAI;AACnB,kBAAM,OAAO,KAAK,IAAI,GAAG,OAAO,IAAI,CAAAI,WAASA,OAAM,MAAM,GAAG,CAAC;AAC7D,kBAAM,KAAK,KAAK,IAAI,GAAG,OAAO,IAAI,CAAAA,WAASA,OAAM,IAAI,GAAG,CAAC;AACzD,kBAAM,kBAAkB,6BAA6B,MAAM;AAC3D,kBAAM,QAAQ,EAAE,MAAM,GAAG;AAEzB,mBAAO,eAAe,KAAK,OAAO;cAChC,GAAI,KAAK,QAAQ,mBAAmB,SAAY,EAAE,gBAAgB,KAAK,QAAQ,eAAe,IAAI,CAAC;cACnG;YACF,CAAC;UACH;QACF;MACF,CAAC;IACH;EACF;AACF,CAAC;AEtCM,IAAM,WAAW,UAAU,OAAO;EACvC,MAAM;EAEN,cAAc;AACZ,WAAO;MACL,GAAG;IACL;EACF;AACF,CAAC;ACLM,IAAM,SAAS,UAAU,OAAO;EACrC,MAAM;EAEN,SAAS,EAAE,aAAa,qBAAqB,GAAG;AAXlD,QAAA,IAAA,IAAA;AAYI,UAAM,WAAW,MAAM;AAZ3B,UAAAC,KAAAC,KAAAC,KAAA;AAaM,WACE,MAAAA,OAAAD,OAAAD,MAAA,KAAK,OAAO,QAAQ,yBAApB,OAAA,SAAAA,IAA0C,WAA1C,OAAA,SAAAC,IAAkD,sBAAlD,OAAA,SAAAC,IAAA,KAAAD,KAAsE,WAAA,MAAtE,OAAA,KACA,YAAY,QAAQ,SAAS,GAC7B;AACA;MACF;AACA,YAAM,kBAAkB,wBAAwB,YAAY,QAAQ,CAAC,aAAa,GAAG,oBAAoB,CAAC;AAC1G,YAAM,UAAU,iBAAiB,eAAe;AAEhD,cAAQ,QAAQ,CAAA,WAAU;AACxB,YACE,gBAAgB,QAAQ,UAAU,OAAO,SAAS,IAAI,EAAE,gBACxD,gBAAgB,QAAQ,UAAU,OAAO,SAAS,EAAE,EAAE,eACtD;AACA,0BAAgB,OAAO,aAAa,OAAO,SAAS,MAAM,OAAO,SAAS,IAAI,CAAC,MAAM,SAAS;AAC5F,kBAAM,KAAK,OAAO,KAAK,WAAW;AAClC,kBAAM,qBAAqB,OAAO,SAAS,QAAQ,QAAQ,MAAM,OAAO,SAAS;AAEjF,iBAAK,OAAO,KAAK,UAAU;cACzB,MAAM;cACN;cACA;cACA;cACA,SAAS,gBAAgB,QAAQ,IAAI,IAAI;cACzC,OAAO,gBAAgB,QAAQ,IAAI,EAAE;cACrC,cAAc,OAAO;cACrB,UAAU,OAAO;cACjB,SAAS,CAAC;cACV,QAAQ,KAAK;cACb;cACA,mBAAmB;YACrB,CAAC;UACH,CAAC;QACH;MACF,CAAC;AAED,YAAM,UAAU,gBAAgB;AAChC,sBAAgB,MAAM,QAAQ,CAAC,MAAM,UAAU;AAlDrD,YAAAD,KAAAC;AAmDQ,YAAI,gBAAgB,gBAAgB;AAClC,gBAAM,WAAW,QAAQ,MAAM,KAAK,EAAE,IAAI,KAAK,MAAM,EAAE;AACvD,gBAAM,SAAS,QAAQ,MAAM,KAAK,EAAE,IAAI,KAAK,EAAE;AAC/C,gBAAM,WAAW,QAAQ,OAAO,EAAE,IAAI,UAAU,EAAE;AAClD,gBAAM,SAAS,QAAQ,OAAO,EAAE,IAAI,MAAM;AAE1C,gBAAM,mBAAkBD,MAAA,gBAAgB,IAAI,OAAO,WAAW,CAAC,MAAvC,OAAA,SAAAA,IAA0C,MAAM,KAAK,CAAA,SAAQ,KAAK,GAAG,KAAK,IAAI,CAAA;AACtG,gBAAM,kBAAiBC,MAAA,gBAAgB,IAAI,OAAO,MAAM,MAAjC,OAAA,SAAAA,IAAoC,MAAM,KAAK,CAAA,SAAQ,KAAK,GAAG,KAAK,IAAI,CAAA;AAE/F,eAAK,OAAO,KAAK,UAAU;YACzB,MAAM;YACN,MAAM,KAAK;YACX,MAAM,KAAK;YACX,IAAI,KAAK;YACT,cAAc;cACZ,MAAM;cACN,IAAI;YACN;YACA,UAAU;cACR,MAAM;cACN,IAAI;YACN;YACA,SAAS,QAAQ,kBAAkB,eAAe;YAClD,QAAQ,KAAK;YACb;YACA,mBAAmB;UACrB,CAAC;QACH;MACF,CAAC;IACH;AAEA,SAAI,MAAA,MAAA,KAAA,KAAK,OAAO,QAAQ,yBAApB,OAAA,SAAA,GAA0C,WAA1C,OAAA,SAAA,GAAkD,UAAlD,OAAA,KAA2D,MAAM;AACnE,iBAAW,UAAU,CAAC;IACxB,OAAO;AACL,eAAS;IACX;EACF;AACF,CAAC;ACpFM,IAAM,OAAO,UAAU,OAAO;EACnC,MAAM;EAEN,wBAAwB;AACtB,WAAO;MACL,IAAIN,QAAO;QACT,KAAK,IAAIQ,WAAU,YAAY;QAE/B,OAAO;UACL,YAAY,CAAC,GAAG,GAAG,OAAO,UAAU;AAClC,iBAAK,OAAO,KAAK,QAAQ;cACvB,QAAQ,KAAK;cACb,OAAO;cACP;cACA;YACF,CAAC;UACH;QACF;MACF,CAAC;IACH;EACF;AACF,CAAC;ACrBM,IAAM,WAAW,UAAU,OAAO;EACvC,MAAM;EAEN,wBAAwB;AACtB,WAAO;MACL,IAAIR,QAAO;QACT,KAAK,IAAIQ,WAAU,UAAU;QAC7B,OAAO;UACL,UAAU,MAAM,KAAK,OAAO,QAAQ;QACtC;MACF,CAAC;IACH;EACF;AACF,CAAC;ACbM,IAAM,uBAAuB,IAAIA,WAAU,aAAa;AAExD,IAAM,cAAc,UAAU,OAAO;EAC1C,MAAM;EAEN,wBAAwB;AACtB,UAAM,EAAE,OAAO,IAAI;AAEnB,WAAO;MACL,IAAIR,QAAO;QACT,KAAK;QACL,OAAO;UACL,iBAAiB;YACf,OAAO,CAAC,MAAM,UAAiB;AAC7B,qBAAO,YAAY;AAEnB,oBAAM,cAAc,OAAO,MAAM,GAAG,QAAQ,SAAS,EAAE,MAAM,CAAC,EAAE,QAAQ,gBAAgB,KAAK;AAE7F,mBAAK,SAAS,WAAW;AAEzB,qBAAO;YACT;YACA,MAAM,CAAC,MAAM,UAAiB;AAC5B,qBAAO,YAAY;AAEnB,oBAAM,cAAc,OAAO,MAAM,GAAG,QAAQ,QAAQ,EAAE,MAAM,CAAC,EAAE,QAAQ,gBAAgB,KAAK;AAE5F,mBAAK,SAAS,WAAW;AAEzB,qBAAO;YACT;UACF;QACF;MACF,CAAC;IACH;EACF;AACF,CAAC;AC/BM,IAAM,SAAS,UAAU,OAAO;EACrC,MAAM;EAEN,uBAAuB;AACrB,UAAM,kBAAkB,MACtB,KAAK,OAAO,SAAS,MAAM,CAAC,EAAE,SAAS,MAAM;MAC3C,MAAM,SAAS,cAAc;;MAG7B,MACE,SAAS,QAAQ,CAAC,EAAE,GAAG,MAAM;AAC3B,cAAM,EAAE,WAAW,IAAI,IAAI;AAC3B,cAAM,EAAE,OAAO,QAAQ,IAAI;AAC3B,cAAM,EAAE,KAAK,OAAO,IAAI;AACxB,cAAM,aAAa,QAAQ,OAAO,eAAe,MAAM,IAAI,GAAG,IAAI,QAAQ,MAAM,CAAC,IAAI;AACrF,cAAM,oBAAoB,WAAW,OAAO,KAAK,KAAK;AAEtD,cAAM,YAAY,QAAQ,MAAM,QAAQ;AAExC,cAAM,YACJ,qBAAqB,WAAW,OAAO,eAAe,IAClD,cAAc,QAAQ,MACtBS,WAAU,QAAQ,GAAG,EAAE,SAAS;AAEtC,YACE,CAAC,SACD,CAAC,OAAO,KAAK,eACb,OAAO,YAAY,UACnB,CAAC,aACA,aAAa,QAAQ,OAAO,KAAK,SAAS,aAC3C;AACA,iBAAO;QACT;AAEA,eAAO,SAAS,WAAW;MAC7B,CAAC;MAEH,MAAM,SAAS,gBAAgB;MAC/B,MAAM,SAAS,aAAa;MAC5B,MAAM,SAAS,mBAAmB;IACpC,CAAC;AAEH,UAAM,eAAe,MACnB,KAAK,OAAO,SAAS,MAAM,CAAC,EAAE,SAAS,MAAM;MAC3C,MAAM,SAAS,gBAAgB;MAC/B,MAAM,SAAS,kBAAkB;MACjC,MAAM,SAAS,YAAY;MAC3B,MAAM,SAAS,kBAAkB;IACnC,CAAC;AAEH,UAAM,cAAc,MAClB,KAAK,OAAO,SAAS,MAAM,CAAC,EAAE,SAAS,MAAM;MAC3C,MAAM,SAAS,cAAc;MAC7B,MAAM,SAAS,oBAAoB;MACnC,MAAM,SAAS,eAAe;MAC9B,MAAM,SAAS,WAAW;IAC5B,CAAC;AAEH,UAAM,aAAa;MACjB,OAAO;MACP,aAAa,MAAM,KAAK,OAAO,SAAS,SAAS;MACjD,WAAW;MACX,iBAAiB;MACjB,mBAAmB;MACnB,QAAQ;MACR,cAAc;MACd,SAAS,MAAM,KAAK,OAAO,SAAS,UAAU;IAChD;AAEA,UAAM,WAAW;MACf,GAAG;IACL;AAEA,UAAM,YAAY;MAChB,GAAG;MACH,UAAU;MACV,iBAAiB;MACjB,UAAU;MACV,sBAAsB;MACtB,cAAc;MACd,SAAS;MACT,UAAU,MAAM,KAAK,OAAO,SAAS,qBAAqB;MAC1D,UAAU,MAAM,KAAK,OAAO,SAAS,mBAAmB;IAC1D;AAEA,QAAI,MAAM,KAAK,QAAQ,GAAG;AACxB,aAAO;IACT;AAEA,WAAO;EACT;EAEA,wBAAwB;AACtB,WAAO;;;;;;MAML,IAAIT,QAAO;QACT,KAAK,IAAIQ,WAAU,eAAe;QAClC,mBAAmB,CAAC,cAAc,UAAU,aAAa;AACvD,cAAI,aAAa,KAAK,CAAAE,QAAMA,IAAG,QAAQ,aAAa,CAAC,GAAG;AACtD;UACF;AAEA,gBAAM,aAAa,aAAa,KAAK,CAAA,gBAAe,YAAY,UAAU,KAAK,CAAC,SAAS,IAAI,GAAG,SAAS,GAAG;AAE5G,gBAAM,WAAW,aAAa,KAAK,CAAA,gBAAe,YAAY,QAAQ,sBAAsB,CAAC;AAE7F,cAAI,CAAC,cAAc,UAAU;AAC3B;UACF;AAEA,gBAAM,EAAE,OAAO,MAAM,GAAG,IAAI,SAAS;AACrC,gBAAM,UAAUD,WAAU,QAAQ,SAAS,GAAG,EAAE;AAChD,gBAAM,SAASA,WAAU,MAAM,SAAS,GAAG,EAAE;AAC7C,gBAAM,iBAAiB,SAAS,WAAW,OAAO;AAElD,cAAI,SAAS,CAAC,gBAAgB;AAC5B;UACF;AAEA,gBAAM,UAAU,YAAY,SAAS,GAAG;AAExC,cAAI,CAAC,SAAS;AACZ;UACF;AAEA,gBAAM,KAAK,SAAS;AACpB,gBAAM,QAAQ,qBAAqB;YACjC,OAAO;YACP,aAAa;UACf,CAAC;AACD,gBAAM,EAAE,SAAS,IAAI,IAAI,eAAe;YACtC,QAAQ,KAAK;YACb;UACF,CAAC;AAED,mBAAS,WAAW;AAEpB,cAAI,CAAC,GAAG,MAAM,QAAQ;AACpB;UACF;AAEA,iBAAO;QACT;MACF,CAAC;IACH;EACF;AACF,CAAC;AC3JM,IAAM,QAAQ,UAAU,OAAO;EACpC,MAAM;EAEN,wBAAwB;AACtB,WAAO;MACL,IAAIT,QAAO;QACT,KAAK,IAAIQ,WAAU,aAAa;QAEhC,OAAO;UACL,aAAa,CAAC,OAAO,GAAG,UAAU;AAChC,iBAAK,OAAO,KAAK,SAAS;cACxB,QAAQ,KAAK;cACb,OAAO;cACP;YACF,CAAC;UACH;QACF;MACF,CAAC;IACH;EACF;AACF,CAAC;ACpBM,IAAM,WAAW,UAAU,OAAO;EACvC,MAAM;EAEN,wBAAwB;AACtB,WAAO;MACL,IAAIR,QAAO;QACT,KAAK,IAAIQ,WAAU,UAAU;QAC7B,OAAO;UACL,YAAY,MAAmC,KAAK,OAAO,aAAa,EAAE,UAAU,IAAI,IAAI,CAAC;QAC/F;MACF,CAAC;IACH;EACF;AACF,CAAC;ACAM,IAAM,gBAAgB,UAAU,OAA6B;EAClE,MAAM;EAEN,aAAa;AACX,WAAO;MACL,WAAW;IACb;EACF;EAEA,sBAAsB;AAGpB,QAAI,CAAC,KAAK,QAAQ,WAAW;AAC3B,aAAO,CAAC;IACV;AAEA,UAAM,EAAE,eAAe,IAAI,gBAAgB,KAAK,UAAU;AAE1D,WAAO;MACL;QACE,OAAO,eAAe,OAAO,CAAA,cAAa,UAAU,SAAS,MAAM,EAAE,IAAI,CAAA,cAAa,UAAU,IAAI;QACpG,YAAY;UACV,KAAK;YACH,SAAS,KAAK,QAAQ;YACtB,WAAW,CAAA,YAAW;AACpB,oBAAM,MAAM,QAAQ,aAAa,KAAK;AAEtC,kBAAI,QAAQ,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS;AAC7D,uBAAO;cACT;AAEA,qBAAO,KAAK,QAAQ;YACtB;YACA,YAAY,CAAA,eAAc;AACxB,kBAAI,CAAC,WAAW,KAAK;AACnB,uBAAO,CAAC;cACV;AAEA,qBAAO;gBACL,KAAK,WAAW;cAClB;YACF;UACF;QACF;MACF;IACF;EACF;EAEA,wBAAwB;AACtB,WAAO;MACL,IAAIR,SAAO;QACT,KAAK,IAAIQ,WAAU,eAAe;QAClC,OAAO;UACL,YAAY,MAAkC;AAC5C,kBAAM,YAAY,KAAK,QAAQ;AAE/B,gBAAI,CAAC,WAAW;AACd,qBAAO,CAAC;YACV;AAEA,mBAAO;cACL,KAAK;YACP;UACF;QACF;MACF,CAAC;IACH;EACF;AACF,CAAC;AcrFD,IAAA,mBAAA,CAAA;AAAA,SAAA,kBAAA;EAAA,6BAAA,MAAA;EAAA,yBAAA,MAAA;EAAA,0BAAA,MAAA;EAAA,iBAAA,MAAA;EAAA,qBAAA,MAAA;EAAA,6BAAA,MAAA;EAAA,qBAAA,MAAA;AAAA,CAAA;ACyBO,SAAS,gBAAgB,YAAyC;AACvE,MAAI,EAAC,cAAA,OAAA,SAAA,WAAY,KAAA,IAAQ;AACvB,WAAO,CAAC;EACV;AAEA,QAAM,aAAkC,CAAC;AAGzC,QAAM,gBAA0B,CAAC;AACjC,QAAM,aAAa,WAAW,QAAQ,qBAAqB,CAAA,UAAS;AAClE,kBAAc,KAAK,KAAK;AACxB,WAAO,YAAY,cAAc,SAAS,CAAC;EAC7C,CAAC;AAGD,QAAM,eAAe,WAAW,MAAM,6BAA6B;AACnE,MAAI,cAAc;AAChB,UAAM,UAAU,aAAa,IAAI,CAAA,UAAS,MAAM,KAAK,EAAE,MAAM,CAAC,CAAC;AAC/D,eAAW,QAAQ,QAAQ,KAAK,GAAG;EACrC;AAGA,QAAM,UAAU,WAAW,MAAM,2BAA2B;AAC5D,MAAI,SAAS;AACX,eAAW,KAAK,QAAQ,CAAC;EAC3B;AAGA,QAAM,UAAU;AAChB,QAAM,YAAY,MAAM,KAAK,WAAW,SAAS,OAAO,CAAC;AACzD,YAAU,QAAQ,CAAC,CAAC,EAAE,KAAK,SAAS,MAAM;AAvD5C,QAAA;AAwDI,UAAM,cAAc,WAAS,KAAA,UAAU,MAAM,kBAAkB,MAAlC,OAAA,SAAA,GAAsC,CAAA,MAAM,KAAK,EAAE;AAChF,UAAM,cAAc,cAAc,WAAW;AAC7C,QAAI,aAAa;AAEf,iBAAW,GAAG,IAAI,YAAY,MAAM,GAAG,EAAE;IAC3C;EACF,CAAC;AAGD,QAAM,cAAc,WACjB,QAAQ,+BAA+B,EAAE,EACzC,QAAQ,8BAA8B,EAAE,EACxC,QAAQ,0CAA0C,EAAE,EACpD,KAAK;AAER,MAAI,aAAa;AACf,UAAM,eAAe,YAAY,MAAM,KAAK,EAAE,OAAO,OAAO;AAC5D,iBAAa,QAAQ,CAAA,SAAQ;AAC3B,UAAI,KAAK,MAAM,kBAAkB,GAAG;AAClC,mBAAW,IAAI,IAAI;MACrB;IACF,CAAC;EACH;AAEA,SAAO;AACT;AAcO,SAAS,oBAAoB,YAAyC;AAC3E,MAAI,CAAC,cAAc,OAAO,KAAK,UAAU,EAAE,WAAW,GAAG;AACvD,WAAO;EACT;AAEA,QAAM,QAAkB,CAAC;AAGzB,MAAI,WAAW,OAAO;AACpB,UAAM,UAAU,OAAO,WAAW,KAAK,EAAE,MAAM,KAAK,EAAE,OAAO,OAAO;AACpE,YAAQ,QAAQ,CAAA,QAAO,MAAM,KAAK,IAAI,GAAG,EAAE,CAAC;EAC9C;AAGA,MAAI,WAAW,IAAI;AACjB,UAAM,KAAK,IAAI,WAAW,EAAE,EAAE;EAChC;AAGA,SAAO,QAAQ,UAAU,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACnD,QAAI,QAAQ,WAAW,QAAQ,MAAM;AACnC;IACF;AAEA,QAAI,UAAU,MAAM;AAElB,YAAM,KAAK,GAAG;IAChB,WAAW,UAAU,SAAS,SAAS,MAAM;AAE3C,YAAM,KAAK,GAAG,GAAG,KAAK,OAAO,KAAK,CAAC,GAAG;IACxC;EACF,CAAC;AAED,SAAO,MAAM,KAAK,GAAG;AACvB;ACxEO,SAAS,4BAA4B,SAI1C;AACA,QAAM;IACJ;IACA,MAAM;IACN,iBAAAG,mBAAkB;IAClB,qBAAAC,uBAAsB;IACtB,oBAAoB,CAAC;IACrB,qBAAqB,CAAC;IACtB;EACF,IAAI;AAGJ,QAAM,YAAY,gBAAgB;AAGlC,QAAM,mBAAmB,CAAC,UAA+B;AACvD,QAAI,CAAC,mBAAmB;AACtB,aAAO;IACT;AAEA,UAAM,WAAgC,CAAC;AACvC,sBAAkB,QAAQ,CAAA,QAAO;AAC/B,UAAI,OAAO,OAAO;AAChB,iBAAS,GAAG,IAAI,MAAM,GAAG;MAC3B;IACF,CAAC;AACD,WAAO;EACT;AAEA,SAAO;IACL,eAAe,CAAC,OAAsBC,OAA4B;AAChE,YAAM,QAAQ,EAAE,GAAG,mBAAmB,GAAG,MAAM,WAAW;AAC1D,aAAOA,GAAE,WAAW,UAAU,OAAO,CAAC,CAAC;IACzC;IAEA,mBAAmB;MACjB,MAAM;MACN,OAAO;MACP,MAAM,KAAa;AAnGzB,YAAA;AAoGQ,cAAM,QAAQ,IAAI,OAAO,OAAO,SAAS,aAAa,GAAG;AACzD,cAAM,SAAQ,KAAA,IAAI,MAAM,KAAK,MAAf,OAAA,SAAA,GAAkB;AAChC,eAAO,UAAU,SAAY,QAAQ;MACvC;MACA,SAAS,KAAK,SAAS,QAAQ;AAG7B,cAAM,QAAQ,IAAI,OAAO,OAAO,SAAS,wCAAwC;AACjF,cAAM,QAAQ,IAAI,MAAM,KAAK;AAE7B,YAAI,CAAC,OAAO;AACV,iBAAO;QACT;AAGA,cAAM,aAAa,MAAM,CAAC,KAAK;AAC/B,cAAM,aAAaF,iBAAgB,UAAU;AAG7C,cAAM,kBAAkB,mBAAmB,KAAK,CAAA,aAAY,EAAE,YAAY,WAAW;AACrF,YAAI,iBAAiB;AACnB,iBAAO;QACT;AAEA,eAAO;UACL,MAAM;UACN,KAAK,MAAM,CAAC;UACZ;QACF;MACF;IACF;IAEA,gBAAgB,CAAA,SAAQ;AACtB,YAAM,gBAAgB,iBAAiB,KAAK,SAAS,CAAC,CAAC;AACvD,YAAM,QAAQC,qBAAoB,aAAa;AAC/C,YAAM,aAAa,QAAQ,KAAK,KAAK,MAAM;AAE3C,aAAO,MAAM,SAAS,GAAG,UAAU;IACrC;EACF;AACF;ACjFO,SAAS,wBAAwB,SAItC;AACA,QAAM;IACJ;IACA,MAAM;IACN;IACA,iBAAAD,mBAAkB;IAClB,qBAAAC,uBAAsB;IACtB,oBAAoB,CAAC;IACrB,UAAU;IACV;EACF,IAAI;AAGJ,QAAM,YAAY,gBAAgB;AAGlC,QAAM,mBAAmB,CAAC,UAA+B;AACvD,QAAI,CAAC,mBAAmB;AACtB,aAAO;IACT;AAEA,UAAM,WAAgC,CAAC;AACvC,sBAAkB,QAAQ,CAAA,QAAO;AAC/B,UAAI,OAAO,OAAO;AAChB,iBAAS,GAAG,IAAI,MAAM,GAAG;MAC3B;IACF,CAAC;AACD,WAAO;EACT;AAEA,SAAO;IACL,eAAe,CAAC,OAAOC,OAAM;AAC3B,UAAI;AAEJ,UAAI,YAAY;AACd,cAAM,gBAAgB,WAAW,KAAK;AAEtC,sBAAc,OAAO,kBAAkB,WAAW,CAAC,EAAE,MAAM,QAAQ,MAAM,cAAc,CAAC,IAAI;MAC9F,WAAW,YAAY,SAAS;AAC9B,sBAAcA,GAAE,cAAc,MAAM,UAAU,CAAC,CAAC;MAClD,OAAO;AACL,sBAAcA,GAAE,YAAY,MAAM,UAAU,CAAC,CAAC;MAChD;AAEA,YAAM,QAAQ,EAAE,GAAG,mBAAmB,GAAG,MAAM,WAAW;AAE1D,aAAOA,GAAE,WAAW,UAAU,OAAO,WAAW;IAClD;IAEA,mBAAmB;MACjB,MAAM;MACN,OAAO;MACP,MAAM,KAAK;AAnHjB,YAAA;AAoHQ,cAAM,QAAQ,IAAI,OAAO,OAAO,SAAS,IAAI,GAAG;AAChD,cAAM,SAAQ,KAAA,IAAI,MAAM,KAAK,MAAf,OAAA,SAAA,GAAkB;AAChC,eAAO,UAAU,SAAY,QAAQ;MACvC;MACA,SAAS,KAAK,SAAS,OAAO;AAxHpC,YAAA;AA0HQ,cAAM,eAAe,IAAI,OAAO,OAAO,SAAS,+BAA+B;AAC/E,cAAM,eAAe,IAAI,MAAM,YAAY;AAE3C,YAAI,CAAC,cAAc;AACjB,iBAAO;QACT;AAEA,cAAM,CAAC,YAAY,aAAa,EAAE,IAAI;AACtC,cAAM,aAAaF,iBAAgB,UAAU;AAG7C,YAAI,QAAQ;AACZ,cAAM,WAAW,WAAW;AAC5B,YAAI,iBAAiB;AAGrB,cAAM,eAAe;AACrB,cAAM,YAAY,IAAI,MAAM,QAAQ;AAEpC,qBAAa,YAAY;AAGzB,mBAAS;AACP,gBAAM,QAAQ,aAAa,KAAK,SAAS;AACzC,cAAI,UAAU,MAAM;AAClB;UACF;AACA,gBAAM,WAAW,MAAM;AACvB,gBAAM,YAAY,MAAM,CAAC;AAEzB,eAAI,KAAA,MAAM,CAAC,MAAP,OAAA,SAAA,GAAU,SAAS,KAAA,GAAQ;AAE7B;UACF;AAEA,cAAI,WAAW;AAEb,qBAAS;UACX,OAAO;AAEL,qBAAS;AAET,gBAAI,UAAU,GAAG;AAGf,oBAAM,aAAa,UAAU,MAAM,GAAG,QAAQ;AAC9C,+BAAiB,WAAW,KAAK;AACjC,oBAAM,YAAY,IAAI,MAAM,GAAG,WAAW,WAAW,MAAM,CAAC,EAAE,MAAM;AAGpE,kBAAI,gBAAiC,CAAC;AACtC,kBAAI,gBAAgB;AAClB,oBAAI,YAAY,SAAS;AAEvB,kCAAgB,MAAM,YAAY,UAAU;AAG5C,gCAAc,QAAQ,CAAA,UAAS;AAC7B,wBAAI,MAAM,SAAS,CAAC,MAAM,UAAU,MAAM,OAAO,WAAW,IAAI;AAC9D,4BAAM,SAAS,MAAM,aAAa,MAAM,IAAI;oBAC9C;kBACF,CAAC;AAGD,yBAAO,cAAc,SAAS,GAAG;AAC/B,0BAAM,YAAY,cAAc,cAAc,SAAS,CAAC;AACxD,wBAAI,UAAU,SAAS,gBAAgB,CAAC,UAAU,QAAQ,UAAU,KAAK,KAAK,MAAM,KAAK;AACvF,oCAAc,IAAI;oBACpB,OAAO;AACL;oBACF;kBACF;gBACF,OAAO;AACL,kCAAgB,MAAM,aAAa,cAAc;gBACnD;cACF;AAEA,qBAAO;gBACL,MAAM;gBACN,KAAK;gBACL;gBACA,SAAS;gBACT,QAAQ;cACV;YACF;UACF;QACF;AAGA,eAAO;MACT;IACF;IAEA,gBAAgB,CAAC,MAAME,OAAM;AAC3B,YAAM,gBAAgB,iBAAiB,KAAK,SAAS,CAAC,CAAC;AACvD,YAAM,QAAQD,qBAAoB,aAAa;AAC/C,YAAM,aAAa,QAAQ,KAAK,KAAK,MAAM;AAC3C,YAAM,kBAAkBC,GAAE,eAAe,KAAK,WAAW,CAAC,GAAG,MAAM;AAEnE,aAAO,MAAM,SAAS,GAAG,UAAU;;EAAO,eAAe;;;IAC3D;EACF;AACF;ACpNA,SAAS,yBAAyB,YAAyC;AACzE,MAAI,CAAC,WAAW,KAAK,GAAG;AACtB,WAAO,CAAC;EACV;AAEA,QAAM,aAAkC,CAAC;AAEzC,QAAM,QAAQ;AACd,MAAI,QAAQ,MAAM,KAAK,UAAU;AAEjC,SAAO,UAAU,MAAM;AACrB,UAAM,CAAC,EAAE,KAAK,cAAc,YAAY,IAAI;AAC5C,eAAW,GAAG,IAAI,gBAAgB;AAClC,YAAQ,MAAM,KAAK,UAAU;EAC/B;AAEA,SAAO;AACT;AAMA,SAAS,6BAA6B,OAAoC;AACxE,SAAO,OAAO,QAAQ,KAAK,EACxB,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,UAAU,UAAa,UAAU,IAAI,EAC3D,IAAI,CAAC,CAAC,KAAK,KAAK,MAAM,GAAG,GAAG,KAAK,KAAK,GAAG,EACzC,KAAK,GAAG;AACb;AAqGO,SAAS,yBAAyB,SAIvC;AACA,QAAM;IACJ;IACA,MAAM;IACN;IACA,iBAAAF,mBAAkB;IAClB,qBAAAC,uBAAsB;IACtB,oBAAoB,CAAC;IACrB,cAAc;IACd;EACF,IAAI;AAGJ,QAAM,YAAY,iBAAiB;AAGnC,QAAM,mBAAmB,CAAC,UAA+B;AACvD,QAAI,CAAC,mBAAmB;AACtB,aAAO;IACT;AAEA,UAAM,WAAgC,CAAC;AACvC,sBAAkB,QAAQ,CAAA,SAAQ;AAEhC,YAAM,WAAW,OAAO,SAAS,WAAW,OAAO,KAAK;AACxD,YAAM,gBAAgB,OAAO,SAAS,WAAW,SAAY,KAAK;AAElE,UAAI,YAAY,OAAO;AACrB,cAAM,QAAQ,MAAM,QAAQ;AAG5B,YAAI,kBAAkB,UAAa,UAAU,eAAe;AAC1D;QACF;AAEA,iBAAS,QAAQ,IAAI;MACvB;IACF,CAAC;AACD,WAAO;EACT;AAGA,QAAM,mBAAmB,UAAU,QAAQ,uBAAuB,MAAM;AAExE,SAAO;IACL,eAAe,CAAC,OAAsBC,OAA4B;AAChE,YAAM,QAAQ,EAAE,GAAG,mBAAmB,GAAG,MAAM,WAAW;AAE1D,UAAI,aAAa;AAEf,eAAOA,GAAE,WAAW,UAAU,KAAK;MACrC;AAGA,YAAM,UAAU,aAAa,WAAW,KAAK,IAAI,MAAM,WAAW;AAClE,UAAI,SAAS;AAEX,eAAOA,GAAE,WAAW,UAAU,OAAO,CAACA,GAAE,eAAe,OAAO,CAAC,CAAC;MAClE;AACA,aAAOA,GAAE,WAAW,UAAU,OAAO,CAAC,CAAC;IACzC;IAEA,mBAAmB;MACjB,MAAM;MACN,OAAO;MACP,MAAM,KAAa;AAEjB,cAAM,eAAe,cACjB,IAAI,OAAO,MAAM,gBAAgB,gBAAgB,IACjD,IAAI,OAAO,MAAM,gBAAgB,iCAAiC,gBAAgB,KAAK;AAE3F,cAAM,QAAQ,IAAI,MAAM,YAAY;AACpC,cAAM,QAAQ,SAAA,OAAA,SAAA,MAAO;AACrB,eAAO,UAAU,SAAY,QAAQ;MACvC;MACA,SAAS,KAAK,SAAS,QAAQ;AAE7B,cAAM,eAAe,cACjB,IAAI,OAAO,OAAO,gBAAgB,kBAAkB,IACpD,IAAI,OAAO,OAAO,gBAAgB,qCAAqC,gBAAgB,KAAK;AAEhG,cAAM,QAAQ,IAAI,MAAM,YAAY;AAEpC,YAAI,CAAC,OAAO;AACV,iBAAO;QACT;AAEA,YAAI,UAAU;AACd,YAAI,aAAa;AAEjB,YAAI,aAAa;AAEf,gBAAM,CAAC,EAAE,KAAK,IAAI;AAClB,uBAAa;QACf,OAAO;AAEL,gBAAM,CAAC,EAAE,OAAO,YAAY,IAAI;AAChC,uBAAa;AACb,oBAAU,gBAAgB;QAC5B;AAGA,cAAM,aAAaF,iBAAgB,WAAW,KAAK,CAAC;AAEpD,eAAO;UACL,MAAM;UACN,KAAK,MAAM,CAAC;UACZ,SAAS,QAAQ,KAAK;UACtB;QACF;MACF;IACF;IAEA,gBAAgB,CAAC,SAAsB;AACrC,UAAI,UAAU;AACd,UAAI,YAAY;AACd,kBAAU,WAAW,IAAI;MAC3B,WAAW,KAAK,WAAW,KAAK,QAAQ,SAAS,GAAG;AAElD,kBAAU,KAAK,QACZ,OAAO,CAAC,UAAe,MAAM,SAAS,MAAM,EAC5C,IAAI,CAAC,UAAe,MAAM,IAAI,EAC9B,KAAK,EAAE;MACZ;AAEA,YAAM,gBAAgB,iBAAiB,KAAK,SAAS,CAAC,CAAC;AACvD,YAAM,QAAQC,qBAAoB,aAAa;AAC/C,YAAM,aAAa,QAAQ,IAAI,KAAK,KAAK;AAEzC,UAAI,aAAa;AACf,eAAO,IAAI,SAAS,GAAG,UAAU;MACnC;AAEA,aAAO,IAAI,SAAS,GAAG,UAAU,IAAI,OAAO,KAAK,SAAS;IAC5D;EACF;AACF;AC7MO,SAAS,oBACd,KACA,QACA,OASY;AAxFd,MAAA,IAAA,IAAA,IAAA;AAyFE,QAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,QAAM,QAAuB,CAAC;AAC9B,MAAI,WAAW;AACf,MAAI,IAAI;AACR,QAAM,iBAAiB,OAAO,kBAAkB;AAEhD,SAAO,IAAI,MAAM,QAAQ;AACvB,UAAM,cAAc,MAAM,CAAC;AAC3B,UAAM,YAAY,YAAY,MAAM,OAAO,WAAW;AAEtD,QAAI,CAAC,WAAW;AAEd,UAAI,MAAM,SAAS,GAAG;AACpB;MACF,WAAW,YAAY,KAAK,MAAM,IAAI;AACpC,aAAK;AACL,mBAAW,GAAG,QAAQ,GAAG,WAAW;;AACpC;MACF,OAAO;AACL,eAAO;MACT;IACF;AAEA,UAAM,WAAW,OAAO,gBAAgB,SAAS;AACjD,UAAM,EAAE,aAAa,YAAY,IAAI;AACrC,eAAW,GAAG,QAAQ,GAAG,WAAW;;AAGpC,UAAM,cAAc,CAAC,WAAW;AAChC,SAAK;AAGL,WAAO,IAAI,MAAM,QAAQ;AACvB,YAAM,WAAW,MAAM,CAAC;AAExB,UAAI,SAAS,KAAK,MAAM,IAAI;AAE1B,cAAM,oBAAoB,MAAM,MAAM,IAAI,CAAC,EAAE,UAAU,CAAA,MAAK,EAAE,KAAK,MAAM,EAAE;AAC3E,YAAI,sBAAsB,IAAI;AAE5B;QACF;AAEA,cAAM,eAAe,MAAM,IAAI,IAAI,iBAAiB;AACpD,cAAME,gBAAa,MAAA,KAAA,aAAa,MAAM,QAAQ,MAA3B,OAAA,SAAA,GAA+B,CAAA,MAA/B,OAAA,SAAA,GAAmC,WAAU;AAEhE,YAAIA,cAAa,aAAa;AAE5B,sBAAY,KAAK,QAAQ;AACzB,qBAAW,GAAG,QAAQ,GAAG,QAAQ;;AACjC,eAAK;AACL;QACF,OAAO;AAEL;QACF;MACF;AAEA,YAAM,eAAa,MAAA,KAAA,SAAS,MAAM,QAAQ,MAAvB,OAAA,SAAA,GAA2B,CAAA,MAA3B,OAAA,SAAA,GAA+B,WAAU;AAE5D,UAAI,aAAa,aAAa;AAE5B,oBAAY,KAAK,QAAQ;AACzB,mBAAW,GAAG,QAAQ,GAAG,QAAQ;;AACjC,aAAK;MACP,OAAO;AAEL;MACF;IACF;AAGA,QAAI;AACJ,UAAM,gBAAgB,YAAY,MAAM,CAAC;AAEzC,QAAI,cAAc,SAAS,GAAG;AAE5B,YAAM,iBAAiB,cACpB,IAAI,CAAA,eAAc,WAAW,MAAM,cAAc,cAAc,CAAC,EAChE,KAAK,IAAI;AAEZ,UAAI,eAAe,KAAK,GAAG;AAEzB,YAAI,OAAO,oBAAoB;AAC7B,yBAAe,OAAO,mBAAmB,cAAc;QACzD,OAAO;AACL,yBAAe,MAAM,YAAY,cAAc;QACjD;MACF;IACF;AAGA,UAAM,QAAQ,OAAO,YAAY,UAAU,YAAY;AACvD,UAAM,KAAK,KAAK;EAClB;AAEA,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO;EACT;AAEA,SAAO;IACL;IACA,KAAK;EACP;AACF;AC1IO,SAAS,4BACd,MACAD,IAIA,mBACA,KACQ;AACR,MAAI,CAAC,QAAQ,CAAC,MAAM,QAAQ,KAAK,OAAO,GAAG;AACzC,WAAO;EACT;AAGA,QAAM,SAAS,OAAO,sBAAsB,aAAa,kBAAkB,GAAG,IAAI;AAElF,QAAM,CAAC,SAAS,GAAG,QAAQ,IAAI,KAAK;AAGpC,QAAM,cAAcA,GAAE,eAAe,CAAC,OAAO,CAAC;AAC9C,QAAM,SAAS,CAAC,GAAG,MAAM,GAAG,WAAW,EAAE;AAGzC,MAAI,YAAY,SAAS,SAAS,GAAG;AACnC,aAAS,QAAQ,CAAA,UAAS;AACxB,YAAM,eAAeA,GAAE,eAAe,CAAC,KAAK,CAAC;AAC7C,UAAI,cAAc;AAEhB,cAAM,gBAAgB,aACnB,MAAM,IAAI,EACV,IAAI,CAAA,SAAS,OAAOA,GAAE,OAAO,IAAI,IAAI,EAAG,EACxC,KAAK,IAAI;AACZ,eAAO,KAAK,aAAa;MAC3B;IACF,CAAC;EACH;AAEA,SAAO,OAAO,KAAK,IAAI;AACzB;ACtFO,SAAS,yBAAyB,WAAiB,QAAgB,QAA6B,CAAC,GAAS;AAC/G,QAAM,EAAE,MAAM,IAAI;AAClB,QAAM,EAAE,KAAK,GAAG,IAAI;AACpB,QAAM,WAAW;AAEjB,MAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,UAAM,OAAO,GAAG,QAAQ,IAAI,GAAG;AAC/B,UAAM,KAAK,GAAG,QAAQ,IAAI,GAAG,IAAI,KAAK;AACtC,QAAI,YAAyB;AAG7B,SAAK,MAAM,QAAQ,CAAA,SAAQ;AACzB,UAAI,SAAS,UAAU;AACrB,eAAO;MACT;AAEA,kBAAY;IACd,CAAC;AAED,QAAI,CAAC,WAAW;AACd;IACF;AAGA,QAAI,cAAc;AAClB,WAAO,KAAK,KAAK,EAAE,QAAQ,CAAA,MAAK;AAC9B,UAAI,MAAM,CAAC,MAAM,UAAW,MAAM,CAAC,GAAG;AACpC,sBAAc;MAChB;IACF,CAAC;AAED,QAAI,aAAa;AACf,YAAM,cAAc,UAAU,KAAK,OAAO;QACxC,GAAG,UAAU;QACb,GAAG;MACL,CAAC;AAED,SAAG,WAAW,MAAM,IAAI,UAAU,IAAI;AACtC,SAAG,QAAQ,MAAM,IAAI,WAAW;IAClC;EACF,CAAC;AAED,MAAI,GAAG,YAAY;AACjB,WAAO,KAAK,SAAS,EAAE;EACzB;AACF;;;AOnDA,SAAS,UAAAE,UAAQ,aAAAC,kBAAiB;AAiB3B,IAAM,cAAc,KAAK,OAA2B;AAAA,EACzD,MAAM;AAAA,EAEN,aAAa;AACX,WAAO;AAAA,MACL,gBAAgB;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,gBAAgB;AACd,WAAO;AAAA,MACL,WAAW;AAAA,QACT,SAAS;AAAA,QACT,WAAW,CAAC,YAAY,QAAQ,aAAa,iBAAiB;AAAA,QAC9D,YAAY,CAAC,gBAAgB;AAAA,UAC3B,mBAAmB,WAAW;AAAA,QAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEA,YAAY;AACV,WAAO,CAAC,EAAE,KAAK,wBAAwB,CAAC;AAAA,EAC1C;AAAA,EAEA,WAAW,EAAE,eAAe,GAAG;AAC7B,WAAO;AAAA,MACL;AAAA,MACA,gBAAgB,gBAAgB;AAAA,QAC9B,OACE;AAAA,MACJ,CAAC;AAAA,MACD;AAAA,IACF;AAAA,EACF;AAAA,EAEA,cAAc;AACZ,WAAO;AAAA,MACL,YACE,CAAC,cACD,CAAC,EAAE,SAAS,MAAM;AAChB,eAAO,SAAS,QAAQ,KAAK,MAAM,EAAE,UAAU,CAAC;AAAA,MAClD;AAAA,MACF,cACE,CAAC,cACD,CAAC,EAAE,IAAI,MAAM,MAAM;AACjB,cAAM,EAAE,IAAI,IAAI;AAChB,YAAI,QAAQ;AAEZ,YAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,eAAK,MAAM,QAAQ,CAAC,SAAS;AAC3B,gBAAI,KAAK,KAAK,SAAS,KAAK,QAAQ,KAAK,MAAM,cAAc,WAAW;AACtE,iBAAG,WAAW,KAAK,MAAM,KAAK,UAAU,KAAK,IAAI;AACjD,sBAAQ;AAAA,YACV;AAAA,UACF,CAAC;AAAA,QACH,CAAC;AAED,eAAO;AAAA,MACT;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,wBAAwB;AACtB,UAAM,EAAE,eAAe,IAAI,KAAK;AAEhC,WAAO;AAAA,MACL,IAAID,SAAO;AAAA,QACT,KAAK,IAAIC,WAAU,cAAc;AAAA,QACjC,OAAO;AAAA,UACL,YAAY,MAAM,KAAK;AACrB,gBAAI,CAAC,eAAgB,QAAO;AAE5B,kBAAM,EAAE,MAAM,IAAI;AAClB,kBAAM,OAAO,MAAM,IAAI,QAAQ,GAAG;AAClC,kBAAM,QAAQ,KAAK,MAAM;AAEzB,kBAAM,cAAc,MAAM,KAAK,CAAC,SAAS,KAAK,KAAK,SAAS,SAAS;AACrE,gBAAI,eAAe,YAAY,MAAM,WAAW;AAE9C;AAAC,cAAC,KAAK,IAAoB,KAAK;AAChC,6BAAe,YAAY,MAAM,SAAS;AAC1C,qBAAO;AAAA,YACT;AAEA,mBAAO;AAAA,UACT;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF,CAAC;AAKM,SAAS,eACd,QACA,WACA,MACA,IACM;AAEN,MAAI,CAAC,OAAO,QAAQ,OAAO,aAAa;AACtC,YAAQ,KAAK,2CAA2C;AACxD;AAAA,EACF;AAEA,SACG,MAAM,EACN,iBAAiB,EAAE,MAAM,GAAG,CAAC,EAC7B,WAAW,SAAS,EACpB,IAAI;AACT;AAKO,SAAS,kBAAkB,QAAgB,WAAyB;AACzE,MAAI,CAAC,OAAO,QAAQ,OAAO,YAAa;AACxC,SAAO,MAAM,EAAE,aAAa,SAAS,EAAE,IAAI;AAC7C;AAMO,SAAS,kBACd,QACA,UACM;AACN,MAAI,CAAC,OAAO,QAAQ,OAAO,YAAa;AAExC,QAAM,EAAE,IAAI,IAAI,OAAO;AACvB,QAAM,cAAc,IAAI;AAExB,WAAS,QAAQ,CAAC,YAAY;AAC5B,QAAI,CAAC,QAAQ,cAAc,QAAQ,SAAU;AAE7C,UAAM,QAAQ,YAAY,QAAQ,QAAQ,UAAU;AACpD,QAAI,UAAU,GAAI;AAGlB,QAAI,aAAa;AACjB,QAAI,WAA0B;AAC9B,QAAI,SAAwB;AAE5B,QAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,UAAI,aAAa,QAAQ,WAAW,KAAM,QAAO;AAEjD,UAAI,KAAK,UAAU,KAAK,MAAM;AAC5B,cAAM,YAAY;AAClB,cAAM,UAAU,aAAa,KAAK,KAAK;AAEvC,YAAI,aAAa,QAAQ,UAAU,OAAO;AAExC,gBAAM,eAAe,QAAQ;AAC7B,qBAAW,MAAM;AAAA,QACnB;AAEA,YAAI,aAAa,QAAQ,WAAW,MAAM;AACxC,gBAAM,YAAY,QAAQ,QAAQ,WAAW;AAC7C,cAAI,WAAW,WAAW;AAExB,kBAAM,eAAe,YAAY;AACjC,qBAAS,MAAM;AAAA,UACjB;AAAA,QACF;AAEA,qBAAa;AAAA,MACf;AAEA,aAAO;AAAA,IACT,CAAC;AAED,QAAI,aAAa,QAAQ,WAAW,MAAM;AACxC,aACG,MAAM,EACN,iBAAiB,EAAE,MAAM,UAAU,IAAI,OAAO,CAAC,EAC/C,WAAW,QAAQ,EAAE,EACrB,iBAAiB,MAAM,EACvB,IAAI;AAAA,IACT;AAAA,EACF,CAAC;AACH;AAKO,SAAS,gBAAgB,QAAgB,WAAyB;AACvE,MAAI,CAAC,OAAO,QAAQ,OAAO,YAAa;AAExC,QAAM,EAAE,IAAI,IAAI,OAAO;AAEvB,MAAI,YAAY,CAAC,MAAM,QAAQ;AAC7B,UAAM,cAAc,KAAK,MAAM;AAAA,MAC7B,CAAC,SAAS,KAAK,KAAK,SAAS,aAAa,KAAK,MAAM,cAAc;AAAA,IACrE;AAEA,QAAI,aAAa;AACf,aAAO,MAAM,EAAE,iBAAiB,GAAG,EAAE,IAAI;AAGzC,YAAM,OAAO,OAAO;AACpB,YAAM,SAAS,KAAK,YAAY,GAAG;AACnC,YAAM,aAAa,KAAK,IAAI,sBAAsB;AAElD,UAAI,OAAO,MAAM,WAAW,OAAO,OAAO,SAAS,WAAW,QAAQ;AACpE,aAAK,IAAI,eAAe,EAAE,UAAU,UAAU,OAAO,SAAS,CAAC;AAAA,MACjE;AAEA,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT,CAAC;AACH;;;ArL/FS,gBAAAC,aAAA;AA7GT,IAAM,wBAAwB;AAEvB,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA,eAAe;AACjB,GAAsB;AAEpB,QAAM,cAAcC,SAAQ,MAAM,UAAU,eAAe,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC;AAGnF,QAAM,aAAaA,SAAQ,MAAM;AAAA,IAC/B,WAAW,UAAU;AAAA,MACnB,SAAS;AAAA,QACP,QAAQ,CAAC,GAAG,GAAG,CAAC;AAAA,MAClB;AAAA,IACF,CAAC;AAAA,IACD,YAAY,UAAU;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,IACD,KAAK,UAAU;AAAA,MACb,aAAa;AAAA,MACb,gBAAgB;AAAA,QACd,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IACDC,OAAM,UAAU;AAAA,MACd,gBAAgB;AAAA,QACd,OAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,IACD,YAAY,UAAU;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,aAAa,cAAc,CAAC;AAEhC,QAAM,SAAS,UAAU;AAAA,IACvB,mBAAmB;AAAA;AAAA,IACnB;AAAA,IACA,SAAS;AAAA,IACT,UAAU,CAAC,EAAE,QAAAC,QAAO,MAAM;AACxB,YAAM,OAAOA,QAAO,QAAQ;AAC5B,YAAM,WAAW,eAAe,IAAI;AACpC,eAAS,QAAQ;AAAA,IACnB;AAAA,IACA,mBAAmB,CAAC,EAAE,QAAAA,QAAO,MAAM;AACjC,UAAI,CAAC,kBAAmB;AAExB,YAAM,EAAE,MAAM,IAAI,MAAM,IAAIA,QAAO,MAAM;AACzC,UAAI,OAAO;AACT,0BAAkB,IAAI;AAAA,MACxB,OAAO;AACL,cAAM,OAAOA,QAAO,MAAM,IAAI,YAAY,MAAM,IAAI,GAAG;AAGvD,YAAI,qBAAqB;AACzB,QAAAA,QAAO,MAAM,IAAI,aAAa,MAAM,IAAI,CAAC,SAAS;AAChD,cAAI,KAAK,MAAM,KAAK,UAAQ,KAAK,KAAK,SAAS,SAAS,GAAG;AACzD,iCAAqB;AACrB,mBAAO;AAAA,UACT;AAAA,QACF,CAAC;AAED,0BAAkB;AAAA,UAChB,cAAc;AAAA,UACd;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,IACA,aAAa;AAAA,MACX,YAAY;AAAA,QACV,OAAO,GAAG,YAAY;AAAA,MACxB;AAAA,IACF;AAAA,EACF,CAAC;AAGD,EAAAC,WAAU,MAAM;AACd,QAAI,QAAQ;AACV,UAAI,eAAe;AACjB,sBAAc,MAAM;AAAA,MACtB;AACA,UAAI,WAAW;AAEb,mBAAW,MAAM,OAAO,SAAS,MAAM,GAAG,CAAC;AAAA,MAC7C;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,eAAe,SAAS,CAAC;AAGrC,EAAAA,WAAU,MAAM;AACd,QAAI,UAAU,YAAY,QAAW;AACnC,YAAM,kBAAkB,eAAe,OAAO,QAAQ,CAAC;AAEvD,UAAI,oBAAoB,SAAS;AAC/B,cAAM,OAAO,UAAU,eAAe,OAAO,IAAI;AACjD,eAAO,SAAS,WAAW,MAAM,EAAE,YAAY,MAAM,CAAC;AAAA,MACxD;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,OAAO,CAAC;AAEpB,SAAO,gBAAAJ,MAAC,iBAAc,QAAgB;AACxC;;;AsL1IA,SAAS,YAAAK,WAAU,UAAAC,SAAQ,aAAAC,YAAW,eAAAC,oBAAmB;AACzD,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,KAAAC,IAAG,iBAAAC,gBAAe,WAAAC,UAAS,WAAAC,UAAS,eAAAC,cAAa,oBAAoB;;;ACF9E,SAAS,YAAAC,iBAAgB;AACzB,SAAS,gBAAgB,OAAO,SAAAC,QAAO,QAAQ,cAAc;;;ACqCtD,SAAS,iBACd,SACA,kBACA,SACS;AACT,SAAO,QAAQ,KAAK,UAAU,oBAAoB;AACpD;AAEO,SAAS,eACd,SACA,kBACS;AACT,SAAO,QAAQ,KAAK,UAAU;AAChC;AAGO,SAAS,qBAAqB,cAAsB,YAAY;AACrE,SAAO;AAAA,IACL,MAAM,cAAc,QAA4C;AAC9D,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,MAAM,WAAW;AACjE,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,0BAA0B;AACvD,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO,KAAK,QAAQ;AAAA,IACtB;AAAA,IAEA,MAAM,cACJ,QACA,MAC0B;AAC1B,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,MAAM,aAAa;AAAA,QACjE,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,MAC3B,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,cAAM,IAAI,MAAM,MAAM,SAAS,0BAA0B;AAAA,MAC3D;AACA,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO,KAAK,QAAQ;AAAA,IACtB;AAAA,IAEA,MAAM,cACJ,QACA,WACA,SAC0B;AAC1B,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,MAAM,aAAa,SAAS,IAAI;AAAA,QAC9E,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,CAAC;AAAA,MAClC,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,cAAM,IAAI,MAAM,MAAM,SAAS,0BAA0B;AAAA,MAC3D;AACA,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO,KAAK,QAAQ;AAAA,IACtB;AAAA,IAEA,MAAM,cACJ,QACA,WACe;AACf,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,MAAM,aAAa,SAAS,IAAI;AAAA,QAC9E,QAAQ;AAAA,MACV,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,cAAM,IAAI,MAAM,MAAM,SAAS,0BAA0B;AAAA,MAC3D;AAAA,IACF;AAAA,IAEA,MAAM,cACJ,QACA,WAC0B;AAC1B,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,MAAM,aAAa,SAAS,YAAY;AAAA,QACtF,QAAQ;AAAA,MACV,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,cAAM,IAAI,MAAM,MAAM,SAAS,0BAA0B;AAAA,MAC3D;AACA,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO,KAAK,QAAQ;AAAA,IACtB;AAAA,IAEA,MAAM,mBACJ,QAC+B;AAC/B,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,MAAM,yBAAyB;AAAA,QAC7E,QAAQ;AAAA,MACV,CAAC;AACD,UAAI,CAAC,IAAI,IAAI;AACX,cAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,cAAM,IAAI,MAAM,MAAM,SAAS,gCAAgC;AAAA,MACjE;AACA,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,aAAO,KAAK,QAAQ;AAAA,IACtB;AAAA,EACF;AACF;;;AD9BU,SA2CI,YAAAC,WA3CJ,OAAAC,OAOE,QAAAC,cAPF;AAvGV,SAASC,oBAAmB,SAAyB;AACnD,QAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,OAAO,IAAI,QAAQ,IAAI,KAAK,QAAQ;AAC1C,QAAM,UAAU,KAAK,MAAM,OAAO,GAAK;AACvC,QAAM,QAAQ,KAAK,MAAM,OAAO,IAAO;AACvC,QAAM,OAAO,KAAK,MAAM,OAAO,KAAQ;AAEvC,MAAI,UAAU,EAAG,QAAO;AACxB,MAAI,UAAU,GAAI,QAAO,GAAG,OAAO;AACnC,MAAI,QAAQ,GAAI,QAAO,GAAG,KAAK;AAC/B,MAAI,OAAO,EAAG,QAAO,GAAG,IAAI;AAC5B,SAAO,KAAK,mBAAmB;AACjC;AAcO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,CAAC,YAAY,aAAa,IAAIC,UAAS,KAAK;AAClD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,EAAE;AACnD,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,QAAQ,OAAO;AAC9D,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAE5C,QAAM,UAAU,eAAe,SAAS,gBAAgB;AACxD,QAAM,YAAY,iBAAiB,SAAS,kBAAkB,OAAO;AACrE,QAAM,QAAQ,QAAQ,KAAK,UAAU;AAErC,QAAM,cAAc,YAAY;AAC9B,QAAI,CAAC,aAAa,KAAK,EAAG;AAC1B,eAAW,IAAI;AACf,QAAI;AACF,YAAM,QAAQ,aAAa,KAAK,CAAC;AACjC,sBAAgB,EAAE;AAClB,oBAAc,KAAK;AAAA,IACrB,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,aAAa,YAAY;AAC7B,QAAI,CAAC,YAAY,KAAK,EAAG;AACzB,eAAW,IAAI;AACf,QAAI;AACF,YAAM,OAAO,YAAY,KAAK,CAAC;AAC/B,mBAAa,KAAK;AAAA,IACpB,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,eAAe,YAAY;AAC/B,QAAI,CAAC,QAAQ,sBAAsB,EAAG;AACtC,eAAW,IAAI;AACf,QAAI;AACF,YAAM,SAAS;AAAA,IACjB,UAAE;AACA,iBAAW,KAAK;AAAA,IAClB;AAAA,EACF;AAEA,SACE,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,UAAU;AAAA,MACV;AAAA,MACA,WAAW,CAAC,MAAM;AAChB,YAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,KAAK;AACtC,YAAE,eAAe;AACjB,kBAAQ;AAAA,QACV;AAAA,MACF;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,WACI,mFACA;AAAA,QACJ,QAAQ,YAAY;AAAA,MACtB;AAAA,MAGA;AAAA,wBAAAA,OAAC,SAAI,WAAU,+CACb;AAAA,0BAAAA,OAAC,SAAI,WAAU,mCACb;AAAA,4BAAAD,MAAC,UAAK,WAAU,eACb,kBAAQ,QAAQ,QAAQ,KAAK,QAAQ,QAAQ,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,GACvE;AAAA,YACA,gBAAAA,MAAC,UAAK,WAAU,oCACb,UAAAE,oBAAmB,QAAQ,SAAS,GACvC;AAAA,YACC,QAAQ,YACP,gBAAAD,OAAC,UAAK,WAAU,6EACd;AAAA,8BAAAD,MAACI,QAAA,EAAM,WAAU,WAAU;AAAA,cAAE;AAAA,eAE/B;AAAA,aAEJ;AAAA,UAGA,gBAAAJ,MAAC,SAAI,SAAS,CAAC,MAAM,EAAE,gBAAgB,GACrC,0BAAAC;AAAA,YAAC;AAAA;AAAA,cACC,SACE,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,WAAU;AAAA,kBAEV,0BAAAA,MAAC,kBAAe,WAAU,WAAU;AAAA;AAAA,cACtC;AAAA,cAEF,OAAM;AAAA,cACN,WAAU;AAAA,cAEV;AAAA,gCAAAA,MAAC,gBAAa,SAAS,WACrB,0BAAAC,OAAC,UAAK,WAAU,2BACd;AAAA,kCAAAD,MAACI,QAAA,EAAM,WAAU,WAAU;AAAA,kBAC1B,QAAQ,WAAW,cAAc;AAAA,mBACpC,GACF;AAAA,gBACC,WACC,gBAAAJ,MAAC,gBAAa,SAAS,MAAM,aAAa,IAAI,GAC5C,0BAAAC,OAAC,UAAK,WAAU,2BACd;AAAA,kCAAAD,MAAC,UAAO,WAAU,WAAU;AAAA,kBAAE;AAAA,mBAEhC,GACF;AAAA,gBAED,aACC,gBAAAC,OAAAF,WAAA,EACE;AAAA,kCAAAC,MAAC,mBAAgB;AAAA,kBACjB,gBAAAA,MAAC,gBAAa,SAAS,cAAc,aAAW,MAC9C,0BAAAC,OAAC,UAAK,WAAU,2BACd;AAAA,oCAAAD,MAAC,UAAO,WAAU,WAAU;AAAA,oBAAE;AAAA,qBAEhC,GACF;AAAA,mBACF;AAAA;AAAA;AAAA,UAEJ,GACF;AAAA,WACF;AAAA,QAGC,QAAQ,cACP,gBAAAC,OAAC,SAAI,WAAU,8HAA6H;AAAA;AAAA,UACxI,QAAQ;AAAA,UAAW;AAAA,WACvB;AAAA,QAID,YACC,gBAAAA,OAAC,SAAI,WAAU,aACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM;AACf,+BAAe,EAAE,OAAO,KAAK;AAC7B,kBAAE,OAAO,MAAM,SAAS;AACxB,kBAAE,OAAO,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE,OAAO,cAAc,GAAG,CAAC;AAAA,cACjE;AAAA,cACA,WAAW,CAAC,MAAM;AAChB,oBAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,oBAAE,eAAe;AACjB,oBAAE,gBAAgB;AAClB,6BAAW;AAAA,gBACb;AACA,oBAAI,EAAE,QAAQ,UAAU;AACtB,oBAAE,gBAAgB;AAClB,+BAAa,KAAK;AAClB,iCAAe,QAAQ,OAAO;AAAA,gBAChC;AAAA,cACF;AAAA,cACA,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,cAClC,WAAU;AAAA;AAAA,UACZ;AAAA,UACA,gBAAAC,OAAC,SAAI,WAAU,0BACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,+BAAa,KAAK;AAClB,iCAAe,QAAQ,OAAO;AAAA,gBAChC;AAAA,gBACA,UAAU;AAAA,gBACV,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,6BAAW;AAAA,gBACb;AAAA,gBACA,UAAU,WAAW,CAAC,YAAY,KAAK;AAAA,gBACvC,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,WACF,IAEA,gBAAAA,MAAC,OAAE,WAAU,+BAA+B,kBAAQ,SAAQ;AAAA,QAI7D,QAAQ,WAAW,QAAQ,QAAQ,SAAS,KAC3C,gBAAAA,MAAC,SAAI,WAAU,uEACZ,kBAAQ,QAAQ,IAAI,CAAC,UACpB,gBAAAA;AAAA,UAAC;AAAA;AAAA,YAEC;AAAA,YACA;AAAA;AAAA,UAFK,MAAM;AAAA,QAGb,CACD,GACH;AAAA,QAID,CAAC,aACA,gBAAAA,MAAC,SAAI,WAAU,QACZ,uBACC,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,CAAC,MAAM;AACf,gCAAgB,EAAE,OAAO,KAAK;AAC9B,kBAAE,OAAO,MAAM,SAAS;AACxB,kBAAE,OAAO,MAAM,SAAS,GAAG,KAAK,IAAI,EAAE,OAAO,cAAc,GAAG,CAAC;AAAA,cACjE;AAAA,cACA,WAAW,CAAC,MAAM;AAChB,oBAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,oBAAE,eAAe;AACjB,oBAAE,gBAAgB;AAClB,8BAAY;AAAA,gBACd;AACA,oBAAI,EAAE,QAAQ,UAAU;AACtB,oBAAE,gBAAgB;AAClB,gCAAc,KAAK;AACnB,kCAAgB,EAAE;AAAA,gBACpB;AAAA,cACF;AAAA,cACA,aAAY;AAAA,cACZ,WAAU;AAAA,cACV,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA;AAAA,UACpC;AAAA,UACA,gBAAAC,OAAC,SAAI,WAAU,0BACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,gCAAc,KAAK;AACnB,kCAAgB,EAAE;AAAA,gBACpB;AAAA,gBACA,UAAU;AAAA,gBACV,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,CAAC,MAAM;AACd,oBAAE,gBAAgB;AAClB,8BAAY;AAAA,gBACd;AAAA,gBACA,UAAU,WAAW,CAAC,aAAa,KAAK;AAAA,gBACxC,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,WACF,IAEA,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,CAAC,MAAM;AACd,gBAAE,gBAAgB;AAClB,4BAAc,IAAI;AAAA,YACpB;AAAA,YACA,WAAU;AAAA,YAEV;AAAA,8BAAAD,MAAC,SAAM,WAAU,eAAc;AAAA,cAAE;AAAA;AAAA;AAAA,QAEnC,GAEJ;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAGA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA;AACF,GAGG;AACD,QAAM,QAAQ,MAAM,KAAK,UAAU;AAEnC,SACE,gBAAAC,OAAC,SAAI,WAAU,WACb;AAAA,oBAAAA,OAAC,SAAI,WAAU,gCACb;AAAA,sBAAAD,MAAC,UAAK,WAAU,eACb,kBAAQ,QAAQ,MAAM,KAAK,QAAQ,MAAM,KAAK,MAAM,MAAM,GAAG,EAAE,CAAC,GACnE;AAAA,MACA,gBAAAA,MAAC,UAAK,WAAU,4CACb,UAAAE,oBAAmB,MAAM,SAAS,GACrC;AAAA,OACF;AAAA,IACA,gBAAAF,MAAC,OAAE,WAAU,wDAAwD,gBAAM,SAAQ;AAAA,KACrF;AAEJ;;;AD5MI,qBAAAK,WAEE,OAAAC,OA0BQ,QAAAC,cA5BV;AA9GG,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAuB;AACrB,QAAM,CAAC,YAAY,aAAa,IAAIC,UAAS,EAAE;AAC/C,QAAM,CAAC,aAAa,cAAc,IAAIA,UAAS,KAAK;AACpD,QAAM,CAAC,WAAW,YAAY,IAAIA,UAAS,KAAK;AAChD,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,UAAU,WAAW,IAAIA,UAAS,KAAK;AAC9C,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,KAAK;AACtD,QAAM,cAAcC,QAA4B,IAAI;AACpD,QAAM,iBAAiBA,QAAuB,IAAI;AAGlD,QAAM,eAAe,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,QAAQ;AACvD,QAAM,mBAAmB,SAAS,OAAO,CAAC,MAAM,EAAE,QAAQ;AAG1D,EAAAC,WAAU,MAAM;AACd,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,CAAC;AAGL,EAAAA,WAAU,MAAM;AACd,QAAI,QAAQ;AACV,mBAAa,IAAI;AACjB,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,QAAQ;AAC5B,eAAS,KAAK,MAAM,MAAM,IAAI,OAAO,OAAO;AAC5C,4BAAsB,MAAM;AAC1B,8BAAsB,MAAM;AAC1B,yBAAe,IAAI;AAAA,QACrB,CAAC;AAAA,MACH,CAAC;AAAA,IACH,OAAO;AACL,qBAAe,KAAK;AACpB,YAAM,UAAU,SAAS,KAAK,MAAM;AACpC,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,QAAQ;AAC5B,eAAS,KAAK,MAAM,MAAM;AAC1B,aAAO,SAAS,GAAG,SAAS,WAAW,GAAG,IAAI,EAAE;AAChD,YAAM,QAAQ,WAAW,MAAM;AAC7B,qBAAa,KAAK;AAAA,MACpB,GAAG,GAAG;AACN,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,MAAM,CAAC;AAGX,EAAAA,WAAU,MAAM;AACd,QAAI,gBAAgB,QAAQ;AAC1B,kBAAY,SAAS,MAAM;AAAA,IAC7B;AAAA,EACF,GAAG,CAAC,cAAc,MAAM,CAAC;AAGzB,EAAAA,WAAU,MAAM;AACd,UAAM,WAAW,YAAY;AAC7B,QAAI,UAAU;AACZ,eAAS,MAAM,SAAS;AACxB,eAAS,MAAM,SAAS,GAAG,KAAK,IAAI,SAAS,cAAc,GAAG,CAAC;AAAA,IACjE;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,sBAAsBC,aAAY,YAAY;AAClD,QAAI,CAAC,WAAW,KAAK,KAAK,CAAC,aAAc;AACzC,gBAAY,IAAI;AAChB,QAAI;AACF,YAAM,gBAAgB,WAAW,KAAK,CAAC;AACvC,oBAAc,EAAE;AAChB,uBAAiB;AAAA,IACnB,UAAE;AACA,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF,GAAG,CAAC,YAAY,cAAc,iBAAiB,gBAAgB,CAAC;AAEhE,QAAM,gBAAgB,CAAC,MAA2B;AAChD,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,QAAE,eAAe;AACjB,QAAE,gBAAgB;AAClB,0BAAoB;AAAA,IACtB;AACA,QAAI,EAAE,QAAQ,UAAU;AACtB,QAAE,gBAAgB;AAClB,UAAI,cAAc;AAChB,yBAAiB;AACjB,sBAAc,EAAE;AAAA,MAClB,OAAO;AACL,gBAAQ;AAAA,MACV;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,aAAa,CAAC,QAAS,QAAO;AAEnC,SAAOC;AAAA,IACL,gBAAAL,OAAAF,WAAA,EAEE;AAAA,sBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,WAAW;AAAA,YACT;AAAA,YACA,cAAc,gBAAgB;AAAA,UAChC;AAAA,UACA,SAAS;AAAA;AAAA,MACX;AAAA,MAGA,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAW;AAAA,UACX,WAAW;AAAA,YACT;AAAA,YACA;AAAA,YACA;AAAA,YACA,cAAc,kBAAkB;AAAA,UAClC;AAAA,UAGA;AAAA,4BAAAA,OAAC,SAAI,WAAU,2GACb;AAAA,8BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,gCAAAD,MAACO,gBAAA,EAAc,WAAU,yBAAwB;AAAA,gBACjD,gBAAAP,MAAC,QAAG,WAAU,eAAc,sBAAQ;AAAA,gBACnC,aAAa,SAAS,KACrB,gBAAAC,OAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,kBACpC,aAAa;AAAA,kBAAO;AAAA,mBACxB;AAAA,iBAEJ;AAAA,cACA,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAK;AAAA,kBACL,SAAS;AAAA,kBACT,WAAU;AAAA,kBACV,cAAW;AAAA,kBAEX,0BAAAA,MAACQ,IAAA,EAAE,WAAU,WAAU;AAAA;AAAA,cACzB;AAAA,eACF;AAAA,YAGC,gBACC,gBAAAP,OAAC,SAAI,WAAU,2GACb;AAAA,8BAAAA,OAAC,SAAI,WAAU,0CACb;AAAA,gCAAAA,OAAC,SAAI,WAAU,gIAA+H;AAAA;AAAA,kBAC1I;AAAA,kBAAa;AAAA,mBACjB;AAAA,gBACA,gBAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM;AACb,uCAAiB;AACjB,oCAAc,EAAE;AAAA,oBAClB;AAAA,oBACA,UAAU;AAAA,oBACV,WAAU;AAAA,oBACX;AAAA;AAAA,gBAED;AAAA,iBACF;AAAA,cACA,gBAAAC,OAAC,SAAI,WAAU,wBACb;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,KAAK;AAAA,oBACL,OAAO;AAAA,oBACP,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK;AAAA,oBAC7C,WAAW;AAAA,oBACX,aAAY;AAAA,oBACZ,WAAU;AAAA,oBACV,MAAM;AAAA,oBACN,cAAa;AAAA;AAAA,gBACf;AAAA,gBACA,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS;AAAA,oBACT,UAAU,YAAY,CAAC,WAAW,KAAK;AAAA,oBACvC,WAAU;AAAA,oBAET,qBACC,gBAAAA,MAACS,UAAA,EAAQ,WAAU,wBAAuB,IAE1C,gBAAAT,MAACU,UAAA,EAAQ,WAAU,WAAU;AAAA;AAAA,gBAEjC;AAAA,iBACF;AAAA,eACF;AAAA,YAIF,gBAAAV,MAAC,SAAI,WAAW,GAAG,0BAA0B,gBAAgB,QAAQ,GAClE,mBAAS,WAAW,IACnB,gBAAAA,MAAC,SAAI,WAAU,2CACb,0BAAAC,OAAC,SAAI,WAAU,6BACb;AAAA,8BAAAD,MAACO,gBAAA,EAAc,WAAU,sCAAqC;AAAA,cAC9D,gBAAAP,MAAC,OAAE,WAAU,yBAAwB,mFAErC;AAAA,eACF,GACF,IAEA,gBAAAC,OAAC,SAAI,WAAU,iBAEZ;AAAA,2BAAa,IAAI,CAAC,YACjB,gBAAAD;AAAA,gBAAC;AAAA;AAAA,kBAEC;AAAA,kBACA;AAAA,kBACA;AAAA,kBACA,UAAU,oBAAoB,QAAQ;AAAA,kBACtC,SAAS,CAAC,YAAY,QAAQ,QAAQ,IAAI,OAAO;AAAA,kBACjD,QAAQ,CAAC,YAAY,OAAO,QAAQ,IAAI,OAAO;AAAA,kBAC/C,UAAU,MAAM,SAAS,QAAQ,EAAE;AAAA,kBACnC,WAAW,MAAM,UAAU,QAAQ,EAAE;AAAA,kBACrC,SAAS,MAAM,eAAe,QAAQ,EAAE;AAAA;AAAA,gBATnC,QAAQ;AAAA,cAUf,CACD;AAAA,cAGA,iBAAiB,SAAS,KACzB,gBAAAC,OAAC,SAAI,WAAU,QACb;AAAA,gCAAAD;AAAA,kBAAC;AAAA;AAAA,oBACC,MAAK;AAAA,oBACL,SAAS,MAAM,gBAAgB,CAAC,YAAY;AAAA,oBAC5C,WAAU;AAAA,oBAEV,0BAAAC,OAAC,UAAK,WAAU,kCACb;AAAA,qCAAe,gBAAAD,MAACW,cAAA,EAAY,WAAU,WAAU,IAAK,gBAAAX,MAAC,gBAAa,WAAU,WAAU;AAAA,sBAAG;AAAA,sBAChF,iBAAiB;AAAA,sBAAO;AAAA,uBACrC;AAAA;AAAA,gBACF;AAAA,gBACC,gBACC,gBAAAA,MAAC,SAAI,WAAU,kBACZ,2BAAiB,IAAI,CAAC,YACrB,gBAAAA;AAAA,kBAAC;AAAA;AAAA,oBAEC;AAAA,oBACA;AAAA,oBACA;AAAA,oBACA,UAAU,oBAAoB,QAAQ;AAAA,oBACtC,SAAS,CAAC,YAAY,QAAQ,QAAQ,IAAI,OAAO;AAAA,oBACjD,QAAQ,CAAC,YAAY,OAAO,QAAQ,IAAI,OAAO;AAAA,oBAC/C,UAAU,MAAM,SAAS,QAAQ,EAAE;AAAA,oBACnC,WAAW,MAAM,UAAU,QAAQ,EAAE;AAAA,oBACrC,SAAS,MAAM,eAAe,QAAQ,EAAE;AAAA;AAAA,kBATnC,QAAQ;AAAA,gBAUf,CACD,GACH;AAAA,iBAEJ;AAAA,cAGF,gBAAAA,MAAC,SAAI,KAAK,gBAAgB;AAAA,eAC5B,GAEJ;AAAA;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;;;AGtSA,SAAS,YAAAY,WAAU,aAAAC,YAAW,UAAAC,eAAc;AA6EtC,SA8CM,YAAAC,WAnCF,OAAAC,OAXJ,QAAAC,cAAA;AA/DC,SAAS,YAAY;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA,WAAW;AACb,GAAqB;AACnB,QAAM,CAAC,YAAY,aAAa,IAAIL,UAAS,KAAK;AAClD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAgB,CAAC,CAAC;AAC5D,QAAM,CAAC,SAAS,UAAU,IAAIA,UAAS,KAAK;AAC5C,QAAM,CAAC,cAAc,eAAe,IAAIA,UAAS,KAAK;AACtD,QAAM,cAAcE,QAAuB,IAAI;AAG/C,EAAAD,WAAU,MAAM;AACd,QAAI,cAAc,cAAc,WAAW,GAAG;AAC5C,iBAAW,IAAI;AACf,YAAM,GAAG,WAAW,OAAO,EACxB,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC,EAClD,KAAK,UAAQ;AACZ,yBAAiB,KAAK,QAAQ,CAAC,CAAC;AAChC,mBAAW,KAAK;AAAA,MAClB,CAAC,EACA,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,IAClC;AAAA,EACF,GAAG,CAAC,YAAY,cAAc,QAAQ,WAAW,CAAC;AAGlD,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,aAAc;AACnB,aAAS,YAAY,GAAe;AAClC,UAAI,YAAY,WAAW,CAAC,YAAY,QAAQ,SAAS,EAAE,MAAc,GAAG;AAC1E,wBAAgB,KAAK;AAAA,MACvB;AAAA,IACF;AACA,aAAS,iBAAiB,SAAS,WAAW;AAC9C,WAAO,MAAM,SAAS,oBAAoB,SAAS,WAAW;AAAA,EAChE,GAAG,CAAC,YAAY,CAAC;AAEjB,QAAM,eAAe,CAAC,UAAkB;AACtC,UAAM,WAAW,cAAc,KAAK,OAAK,EAAE,OAAO,KAAK;AACvD,QAAI,YAAY,CAAC,KAAK,KAAK,OAAK,EAAE,OAAO,KAAK,GAAG;AAC/C,mBAAa,CAAC,GAAG,MAAM,QAAQ,CAAC;AAAA,IAClC;AACA,oBAAgB,KAAK;AAAA,EACvB;AAEA,QAAM,kBAAkB,CAAC,UAAkB;AACzC,iBAAa,KAAK,OAAO,OAAK,EAAE,OAAO,KAAK,CAAC;AAAA,EAC/C;AAGA,QAAM,iBAAiB,cAAc;AAAA,IACnC,QAAM,CAAC,KAAK,KAAK,OAAK,EAAE,OAAO,GAAG,EAAE;AAAA,EACtC;AAEA,QAAM,aAAa,KAAK,WAAW,IAC/B,YACA,KAAK,WAAW,IAChB,UACA,GAAG,KAAK,MAAM;AAElB,SACE,gBAAAI,OAAC,SAAI,WAAU,sDACb;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,cAAc,CAAC,UAAU;AAAA,QACxC,WAAU;AAAA,QAEV;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,WAAW,gCAAgC,aAAa,cAAc,EAAE;AAAA,cACxE,MAAK;AAAA,cACL,SAAQ;AAAA,cACR,QAAO;AAAA,cAEP,0BAAAA,MAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAE,gBAAe;AAAA;AAAA,UACtF;AAAA,UACA,gBAAAA,MAAC,UAAK,kBAAI;AAAA,UACV,gBAAAA,MAAC,UAAK,WAAU,iCAAiC,sBAAW;AAAA;AAAA;AAAA,IAC9D;AAAA,IAEC,cACC,gBAAAC,OAAC,SAAI,WAAU,uBAEZ;AAAA,WAAK,SAAS,KACb,gBAAAD,MAAC,SAAI,WAAU,wBACZ,eAAK,IAAI,SACR,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,WAAU;AAAA,UAET;AAAA,gBAAI;AAAA,YACJ,CAAC,YACA,gBAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,gBAAgB,IAAI,EAAE;AAAA,gBACrC,WAAU;AAAA,gBAEV,0BAAAA,MAAC,SAAI,WAAU,WAAU,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAC9D,0BAAAA,MAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAE,wBAAuB,GAC9F;AAAA;AAAA,YACF;AAAA;AAAA;AAAA,QAbG,IAAI;AAAA,MAeX,CACD,GACH;AAAA,MAID,CAAC,YACA,gBAAAA,MAAAD,WAAA,EACG,oBACC,gBAAAC,MAAC,OAAE,WAAU,yBAAwB,6BAAe,IAClD,eAAe,SAAS,IAC1B,gBAAAC,OAAC,SAAI,WAAU,0BAAyB,KAAK,aAC3C;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,gBAAgB,CAAC,YAAY;AAAA,YAC5C,WAAU;AAAA,YAEV;AAAA,8BAAAD,MAAC,UAAK,WAAU,iBAAgB,wBAAU;AAAA,cAC1C,gBAAAA,MAAC,SAAI,WAAU,yBAAwB,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAC5E,0BAAAA,MAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAE,kBAAiB,GACxF;AAAA;AAAA;AAAA,QACF;AAAA,QACC,gBACC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YACV,SAAS,CAAC,MAAM,EAAE,gBAAgB;AAAA,YAEjC,yBAAe,IAAI,SAClB,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBAEC,MAAK;AAAA,gBACL,SAAS,MAAM,aAAa,IAAI,EAAE;AAAA,gBAClC,WAAU;AAAA,gBAET,cAAI;AAAA;AAAA,cALA,IAAI;AAAA,YAMX,CACD;AAAA;AAAA,QACH;AAAA,SAEJ,IACE,cAAc,WAAW,IAC3B,gBAAAA,MAAC,OAAE,WAAU,yBAAwB,yDAErC,IAEA,gBAAAA,MAAC,OAAE,WAAU,yBAAwB,4BAAc,GAEvD;AAAA,OAEJ;AAAA,KAEJ;AAEJ;;;ACzKA,SAAS,YAAAE,YAAU,eAAAC,cAAa,aAAAC,kBAAiB;AAuC1C,SAAS,YAAY;AAAA,EAC1B,QAAQ;AAAA,EACR;AAAA,EACA,cAAc;AAAA,EACd;AACF,GAAsC;AACpC,QAAM,CAAC,QAAQ,SAAS,IAAIC,WAAwB,aAAa;AACjE,QAAM,CAAC,UAAU,WAAW,IAAIA,WAA4B,CAAC,CAAC;AAC9D,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,KAAK;AAC5C,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,WAAwB,IAAI;AAC1E,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAgC,IAAI;AAE5E,QAAM,SAAS,qBAAqB,WAAW;AAG/C,EAAAC,WAAU,MAAM;AACd,cAAU,aAAa;AAAA,EACzB,GAAG,CAAC,aAAa,CAAC;AAGlB,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,QAAQ;AACX,kBAAY,CAAC,CAAC;AACd;AAAA,IACF;AAEA,eAAW,IAAI;AACf,WAAO,cAAc,MAAM,EACxB,KAAK,CAAC,SAAS;AACd,kBAAY,IAAI;AAEhB,UAAI,QAAQ;AACV,0BAAkB,QAAQ,IAAI;AAAA,MAChC;AAAA,IACF,CAAC,EACA,MAAM,QAAQ,KAAK,EACnB,QAAQ,MAAM,WAAW,KAAK,CAAC;AAAA,EACpC,GAAG,CAAC,QAAQ,MAAM,CAAC;AAGnB,QAAM,gBAAgBC,aAAY,OAAO,YAAoB;AAC3D,QAAI,CAAC,aAAc;AAGnB,QAAI,kBAAkB;AACtB,QAAI,CAAC,mBAAmB,QAAQ;AAC9B,wBAAkB,MAAM,OAAO;AAC/B,UAAI,iBAAiB;AACnB,kBAAU,eAAe;AAAA,MAC3B;AAAA,IACF;AAEA,QAAI,CAAC,iBAAiB;AACpB,cAAQ,MAAM,mCAAmC;AACjD;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,OAAO,cAAc,iBAAiB;AAAA,MAC1D,YAAY,aAAa;AAAA,MACzB;AAAA,IACF,CAAC;AAGD,QAAI,QAAQ;AACV,qBAAe,QAAQ,QAAQ,IAAI,aAAa,MAAM,aAAa,EAAE;AAAA,IACvE;AAGA,gBAAY,CAAC,SAAS,CAAC,EAAE,GAAG,SAAS,SAAS,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;AAC5D,oBAAgB,IAAI;AAAA,EACtB,GAAG,CAAC,QAAQ,cAAc,QAAQ,QAAQ,MAAM,CAAC;AAGjD,QAAM,iBAAiBA,aAAY,OAAO,UAAkB,YAAoB;AAC9E,QAAI,CAAC,OAAQ;AAEb,UAAM,QAAQ,MAAM,OAAO,cAAc,QAAQ;AAAA,MAC/C,YAAY;AAAA,MACZ;AAAA,MACA;AAAA,IACF,CAAC;AAGD;AAAA,MAAY,CAAC,SACX,KAAK;AAAA,QAAI,CAAC,MACR,EAAE,OAAO,WACL,EAAE,GAAG,GAAG,SAAS,CAAC,GAAI,EAAE,WAAW,CAAC,GAAI,KAAK,EAAE,IAC/C;AAAA,MACN;AAAA,IACF;AAAA,EACF,GAAG,CAAC,QAAQ,MAAM,CAAC;AAGnB,QAAM,cAAcA,aAAY,OAAO,WAAmB,YAAoB;AAC5E,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,MAAM,OAAO,cAAc,QAAQ,WAAW,OAAO;AAGrE;AAAA,MAAY,CAAC,SACX,KAAK,IAAI,CAAC,MAAM;AACd,YAAI,EAAE,OAAO,WAAW;AACtB,iBAAO,EAAE,GAAG,GAAG,GAAG,QAAQ;AAAA,QAC5B;AAEA,YAAI,EAAE,SAAS;AACb,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,SAAS,EAAE,QAAQ;AAAA,cAAI,CAAC,MACtB,EAAE,OAAO,YAAY,EAAE,GAAG,GAAG,GAAG,QAAQ,IAAI;AAAA,YAC9C;AAAA,UACF;AAAA,QACF;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,QAAQ,MAAM,CAAC;AAGnB,QAAM,gBAAgBA,aAAY,OAAO,cAAsB;AAC7D,QAAI,CAAC,OAAQ;AAEb,UAAM,OAAO,cAAc,QAAQ,SAAS;AAG5C,QAAI,QAAQ;AACV,wBAAkB,QAAQ,SAAS;AAAA,IACrC;AAGA,gBAAY,CAAC,SAAS;AAEpB,YAAM,aAAa,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS;AACtD,UAAI,YAAY;AACd,eAAO,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS;AAAA,MAC9C;AAEA,aAAO,KAAK,IAAI,CAAC,OAAO;AAAA,QACtB,GAAG;AAAA,QACH,SAAS,EAAE,SAAS,OAAO,CAAC,MAAM,EAAE,OAAO,SAAS;AAAA,MACtD,EAAE;AAAA,IACJ,CAAC;AAAA,EACH,GAAG,CAAC,QAAQ,QAAQ,MAAM,CAAC;AAG3B,QAAM,iBAAiBA,aAAY,OAAO,cAAsB;AAC9D,QAAI,CAAC,OAAQ;AAEb,UAAM,UAAU,MAAM,OAAO,cAAc,QAAQ,SAAS;AAG5D,QAAI,QAAQ;AACV,YAAM,UAAU,SAAS,KAAK,CAAC,MAAM,EAAE,OAAO,SAAS;AACvD,UAAI,QAAQ,UAAU;AACpB,0BAAkB,QAAQ,SAAS;AAAA,MACrC,WAAW,WAAW,cAAc;AAElC,uBAAe,QAAQ,WAAW,aAAa,MAAM,aAAa,EAAE;AAAA,MACtE;AAAA,IACF;AAGA;AAAA,MAAY,CAAC,SACX,KAAK,IAAI,CAAC,MAAO,EAAE,OAAO,YAAY,EAAE,GAAG,GAAG,UAAU,QAAQ,SAAS,IAAI,CAAE;AAAA,IACjF;AAAA,EACF,GAAG,CAAC,QAAQ,QAAQ,UAAU,cAAc,MAAM,CAAC;AAGnD,QAAM,qBAAqBA,aAAY,YAAY;AACjD,QAAI,CAAC,OAAQ;AAEb,UAAM,OAAO,mBAAmB,MAAM;AAGtC,QAAI,QAAQ;AACV,eAAS,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,MAAM;AAChE,0BAAkB,QAAQ,EAAE,EAAE;AAAA,MAChC,CAAC;AAAA,IACH;AAGA;AAAA,MAAY,CAAC,SACX,KAAK,IAAI,CAAC,OAAO,EAAE,GAAG,GAAG,UAAU,KAAK,EAAE;AAAA,IAC5C;AAAA,EACF,GAAG,CAAC,QAAQ,QAAQ,UAAU,MAAM,CAAC;AAGrC,QAAM,sBAAsBA,aAAY,CAAC,cAAsB;AAC7D,QAAI,QAAQ;AACV,sBAAgB,QAAQ,SAAS;AAAA,IACnC;AACA,uBAAmB,SAAS;AAAA,EAC9B,GAAG,CAAC,MAAM,CAAC;AAGX,QAAM,YAAY,SAAS,OAAO,CAAC,MAAM,CAAC,EAAE,YAAY,CAAC,EAAE,QAAQ,EAAE;AAErE,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,UAAU;AAAA,IACV,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR,OAAO;AAAA,IACP,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,UAAU;AAAA,IACV;AAAA,EACF;AACF;;;AtMxNS,gBAAAC,OAOH,QAAAC,cAPG;AADT,SAASC,UAAS,EAAE,UAAU,GAA2B;AACvD,SAAO,gBAAAF,MAAC,SAAI,WAAW,sDAAsD,aAAa,EAAE,IAAI;AAClG;AAGA,SAAS,gBAAgB,EAAE,OAAO,GAAsC;AACtE,SACE,gBAAAC,OAAC,SAAI,WAAW,GAAG,OAAO,SAAS,wBACjC;AAAA,oBAAAA,OAAC,SAAI,WAAU,kBACb;AAAA,sBAAAD,MAACE,WAAA,EAAS,WAAU,aAAY;AAAA,MAChC,gBAAAF,MAACE,WAAA,EAAS,WAAU,aAAY;AAAA,MAChC,gBAAAF,MAAC,SAAI,WAAU,SACb,0BAAAA,MAACE,WAAA,EAAS,WAAU,YAAW,GACjC;AAAA,OACF;AAAA,IACA,gBAAAD,OAAC,SAAI,WAAU,aACb;AAAA,sBAAAD,MAACE,WAAA,EAAS,WAAU,cAAa;AAAA,MACjC,gBAAAF,MAACE,WAAA,EAAS,WAAU,cAAa;AAAA,MACjC,gBAAAF,MAACE,WAAA,EAAS,WAAU,aAAY;AAAA,MAChC,gBAAAF,MAACE,WAAA,EAAS,WAAU,cAAa;AAAA,MACjC,gBAAAF,MAACE,WAAA,EAAS,WAAU,aAAY;AAAA,OAClC;AAAA,KACF;AAEJ;AAGA,SAAS,mBAAmB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMG;AACD,QAAM,MAAMC,QAA4B,IAAI;AAE5C,EAAAC,YAAU,MAAM;AACd,QAAI,IAAI,SAAS;AACf,UAAI,QAAQ,MAAM,SAAS;AAC3B,UAAI,QAAQ,MAAM,SAAS,GAAG,IAAI,QAAQ,YAAY;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAEV,SACE,gBAAAJ;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,MACxC;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN,WAAW,CAAC,MAAM;AAAE,YAAI,EAAE,QAAQ,QAAS,GAAE,eAAe;AAAA,MAAE;AAAA,MAC9D,WAAW,+BAA+B,aAAa,EAAE;AAAA;AAAA,EAC3D;AAEJ;AAOO,SAAS,WAAW,EAAE,MAAM,qBAAqB,wBAAwB,GAAoB;AAClG,QAAM,EAAE,aAAa,QAAQ,QAAQ,UAAU,UAAU,sBAAsB,IAAI,oBAAoB;AAEvG,QAAM,sBAAsB;AAC5B,QAAM,CAAC,MAAM,OAAO,IAAIK,WAAe;AAAA,IACrC,OAAO;AAAA,IACP,UAAU;AAAA,IACV,MAAM;AAAA,IACN,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,MAAM,CAAC;AAAA,EACT,CAAC;AACD,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,CAAC,CAAC,IAAI;AAC7C,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAuC,IAAI;AAC3E,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAsB,IAAI;AAC5D,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,WAAS,KAAK;AAChE,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,KAAK;AAClD,QAAM,eAAeF,QAAe,EAAE;AACtC,QAAM,cAAcA,QAA4B,IAAI;AACpD,QAAM,yBAAyBA,QAAO,KAAK;AAG3C,QAAM,CAAC,cAAc,eAAe,IAAIE,WAAS,KAAK;AACtD,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAwB,IAAI;AAGxD,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAqB,CAAC,CAAC;AACzD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,WAAS,KAAK;AAC9D,QAAM,CAAC,oBAAoB,qBAAqB,IAAIA,WAA0B,IAAI;AAClF,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAsB,IAAI;AAGlE,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAS,KAAK;AAGtD,QAAM,EAAE,QAAQ,IAAI,oBAAoB;AACxC,QAAM,mBAAmB,SAAS,MAAM,SAAS;AACjD,QAAM,UAAU,SAAS,MAAM,SAAS;AAGxC,QAAM,WAAWC,aAAY,OAAO,SAAS,UAAU;AACrD,QAAI,CAAC,QAAQ;AACX,gBAAU,IAAI;AACd,kBAAY,OAAO;AAAA,IACrB;AACA,QAAI;AACF,YAAM,SAAS,KAAK,KAAK,UAAU;AACnC,YAAM,MAAM,KAAK,KAAK,GAAG,WAAW,UAAU,KAAK,EAAE,KAAK,GAAG,WAAW;AAExE,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC3B;AAAA,QACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACnB,OAAO,KAAK,SAAS;AAAA,UACrB,UAAU,KAAK,YAAY;AAAA,UAC3B,MAAM,KAAK,QAAQ;AAAA,UACnB,UAAU,KAAK;AAAA,UACf,QAAQ,KAAK;AAAA,UACb,QAAQ,KAAK,MAAM,IAAI,OAAK,EAAE,EAAE;AAAA,UAChC,GAAG,OAAO,YAAY,OAAO,IAAI,OAAK,CAAC,EAAE,MAAM,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AAAA,QAC/D,CAAC;AAAA,MACH,CAAC;AAED,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI,KAAK,MAAM;AACb,gBAAQ,WAAS,EAAE,GAAG,MAAM,GAAG,KAAK,KAAK,EAAE;AAC3C,qBAAa,UAAU,KAAK,UAAU,EAAE,OAAO,KAAK,KAAK,OAAO,UAAU,KAAK,KAAK,UAAU,UAAU,KAAK,KAAK,SAAS,CAAC;AAC5H,qBAAa,oBAAI,KAAK,CAAC;AACvB,6BAAqB,KAAK;AAC1B,YAAI,CAAC,KAAK,MAAM,KAAK,KAAK,MAAM;AAC9B,mBAAS,WAAW,KAAK,KAAK,IAAI,EAAE;AAAA,QACtC;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,cAAQ,MAAM,gBAAgB,GAAG;AACjC,YAAM;AAAA,IACR,UAAE;AACA,UAAI,CAAC,QAAQ;AACX,kBAAU,KAAK;AACf,oBAAY,IAAI;AAAA,MAClB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,KAAK,IAAI,KAAK,OAAO,KAAK,UAAU,KAAK,MAAM,KAAK,UAAU,KAAK,QAAQ,KAAK,MAAM,aAAa,QAAQ,QAAQ,CAAC;AAExH,QAAM,gBAAgBA,aAAY,YAAY;AAC5C,QAAI,CAAC,QAAQ,qBAAqB,EAAG;AACrC,cAAU,IAAI;AACd,gBAAY,WAAW;AACvB,QAAI;AACF,YAAM,SAAS,KAAK,KAAK,UAAU;AACnC,YAAM,MAAM,KAAK,KAAK,GAAG,WAAW,UAAU,KAAK,EAAE,KAAK,GAAG,WAAW;AAGxE,UAAI;AACJ,cAAQ,OAAK;AAAE,sBAAc;AAAG,eAAO;AAAA,MAAE,CAAC;AAE1C,YAAM,MAAM,MAAM,MAAM,KAAK;AAAA,QAC3B;AAAA,QACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACnB,GAAG;AAAA,UACH,OAAO,YAAa,SAAS;AAAA,UAC7B,QAAQ;AAAA,UACR,GAAG,OAAO,YAAY,OAAO,IAAI,OAAK,CAAC,EAAE,MAAM,YAAa,EAAE,IAAI,CAAC,CAAC,CAAC;AAAA,QACvE,CAAC;AAAA,MACH,CAAC;AAED,UAAI,IAAI,IAAI;AACV,iBAAS,GAAG;AAAA,MACd;AAAA,IACF,UAAE;AACA,gBAAU,KAAK;AACf,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,KAAK,IAAI,aAAa,QAAQ,QAAQ,CAAC;AAG3C,QAAM,WAAW,YAAY;AAAA,IAC3B,QAAQ,KAAK,MAAM;AAAA,IACnB;AAAA,IACA;AAAA,IACA,QAAQ,YAAY;AAElB,YAAM,SAAS,IAAI;AACnB,aAAO,KAAK,MAAM;AAAA,IACpB;AAAA,EACF,CAAC;AAGD,QAAM,CAAC,WAAW,YAAY,IAAID,WAO/B,CAAC,CAAC;AAEL,EAAAD,YAAU,MAAM;AACd,QAAI,OAAO,WAAW,aAAa;AACjC,YAAM,SAAS,IAAI,gBAAgB,OAAO,SAAS,MAAM;AACzD,mBAAa;AAAA,QACX,MAAM,OAAO,IAAI,MAAM,KAAK;AAAA,QAC5B,OAAO,OAAO,IAAI,OAAO,KAAK;AAAA,QAC9B,QAAQ,OAAO,IAAI,QAAQ,KAAK;AAAA,QAChC,KAAK,OAAO,IAAI,KAAK,KAAK;AAAA,QAC1B,UAAU,OAAO,IAAI,UAAU,KAAK;AAAA,QACpC,SAAS,OAAO,IAAI,SAAS,KAAK;AAAA,MACpC,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,CAAC;AAGL,EAAAA,YAAU,MAAM;AACd,QAAI,UAAU,WAAW,CAAC,SAAS,WAAW,SAAS,KAAK,SAAS,GAAG;AAEtE,sBAAgB,IAAI;AAEpB,eAAS,YAAY,UAAU,OAAO;AAEtC,iBAAW,MAAM;AACf,iBAAS,SAAS,UAAU,OAAQ;AAAA,MACtC,GAAG,GAAG;AAAA,IACR;AAAA,EACF,GAAG,CAAC,UAAU,SAAS,SAAS,SAAS,SAAS,KAAK,MAAM,CAAC;AAG9D,EAAAA,YAAU,MAAM;AACd,QAAI,MAAM;AACR,YAAM,GAAG,WAAW,QAAQ,EACzB,KAAK,OAAK,EAAE,KAAK,CAAC,EAClB,KAAK,OAAK;AACT,cAAM,QAAQ,EAAE,MAAM,KAAK,CAAC,MAAY,EAAE,SAAS,IAAI;AACvD,YAAI,OAAO;AACT,kBAAQ,KAAK;AACb,uBAAa,UAAU,KAAK,UAAU,EAAE,OAAO,MAAM,OAAO,UAAU,MAAM,UAAU,UAAU,MAAM,SAAS,CAAC;AAAA,QAClH;AACA,mBAAW,KAAK;AAAA,MAClB,CAAC,EACA,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,IAClC;AAAA,EACF,GAAG,CAAC,MAAM,WAAW,CAAC;AAGtB,EAAAA,YAAU,MAAM;AACd,UAAM,UAAU,KAAK,UAAU,EAAE,OAAO,KAAK,OAAO,UAAU,KAAK,UAAU,UAAU,KAAK,SAAS,CAAC;AAGtG,QAAI,aAAa,YAAY,IAAI;AAC/B,2BAAqB,CAAC,EAAE,KAAK,SAAS,KAAK,YAAY,KAAK,SAAS;AAAA,IACvE,OAAO;AACL,2BAAqB,YAAY,aAAa,OAAO;AAAA,IACvD;AAAA,EACF,GAAG,CAAC,KAAK,OAAO,KAAK,UAAU,KAAK,QAAQ,CAAC;AAI7C,QAAM,cAAcD,QAAO,QAAQ;AACnC,QAAM,mBAAmBA,QAAO,aAAa;AAC7C,QAAM,yBAAyBA,QAAO,mBAAmB;AACzD,EAAAC,YAAU,MAAM;AAAE,gBAAY,UAAU;AAAA,EAAS,GAAG,CAAC,QAAQ,CAAC;AAC9D,EAAAA,YAAU,MAAM;AAAE,qBAAiB,UAAU;AAAA,EAAc,GAAG,CAAC,aAAa,CAAC;AAC7E,EAAAA,YAAU,MAAM;AAAE,2BAAuB,UAAU;AAAA,EAAoB,GAAG,CAAC,mBAAmB,CAAC;AAE/F,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,uBAAuB,QAAS;AAErC,UAAM,eAAe,MAAM;AACzB,UAAI,mBAAmB;AACrB,eAAO,QAAQ,yCAAyC;AAAA,MAC1D;AACA,aAAO;AAAA,IACT;AAEA,2BAAuB,QAAQ;AAAA,MAC7B;AAAA,MACA,QAAQ,KAAK;AAAA,MACb;AAAA,MACA,QAAQ,CAAC,WAAW;AAClB,YAAI,WAAW,SAAS;AACtB,sBAAY,QAAQ;AAAA,QACtB,OAAO;AACL,2BAAiB,QAAQ;AAAA,QAC3B;AAAA,MACF;AAAA,MACA;AAAA;AAAA,MAEA,SAAS;AAAA,QACP,OAAO,KAAK;AAAA,QACZ,UAAU,KAAK;AAAA,QACf,UAAU,KAAK;AAAA,MACjB;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,6BAAuB,UAAU,IAAI;AAAA,IACvC;AAAA,EACF,GAAG,CAAC,mBAAmB,KAAK,QAAQ,UAAU,KAAK,OAAO,KAAK,UAAU,KAAK,QAAQ,CAAC;AAGvF,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,sBAAuB;AAE5B,UAAM,aAAa,CAAC,SAA+B;AACjD,UAAI,KAAK,SAAS,eAAe;AAC/B,gBAAQ,WAAS;AAAA,UACf,GAAG;AAAA,UACH,OAAO,KAAK,SAAS,KAAK;AAAA,UAC1B,UAAU,KAAK,YAAY,KAAK;AAAA,UAChC,UAAU,KAAK,YAAY,KAAK;AAAA,QAClC,EAAE;AACF,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,SAAS,qBAAqB,KAAK,QAAQ,KAAK,YAAY,QAAW;AAC9E,YAAI,KAAK,SAAS,SAAS,KAAK,IAAI,GAAG;AACrC,kBAAQ,WAAS;AAAA,YACf,GAAG;AAAA,YACH,UAAU,KAAK,SAAS,QAAQ,KAAK,MAAO,KAAK,OAAQ;AAAA,UAC3D,EAAE;AACF,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,SAAS,YAAY,KAAK,YAAY,QAAW;AACxD,YAAI,KAAK,aAAa,SAAS;AAC7B,kBAAQ,WAAS,EAAE,GAAG,MAAM,UAAU,KAAK,UAAU,KAAK,SAAS,EAAE;AACrE,iBAAO;AAAA,QACT;AACA,YAAI,KAAK,aAAa,OAAO;AAC3B,kBAAQ,WAAS,EAAE,GAAG,MAAM,UAAU,KAAK,WAAW,KAAK,QAAQ,EAAE;AACrE,iBAAO;AAAA,QACT;AACA,YAAI,KAAK,QAAQ,KAAK,SAAS,SAAS,KAAK,IAAI,GAAG;AAClD,gBAAM,MAAM,KAAK,SAAS,QAAQ,KAAK,IAAI;AAC3C,gBAAM,cAAc,KAAK,aAAa,WAAW,MAAM,MAAM,KAAK,KAAK;AACvE,kBAAQ,WAAS;AAAA,YACf,GAAG;AAAA,YACH,UAAU,KAAK,SAAS,MAAM,GAAG,WAAW,IAAI,KAAK,UAAU,KAAK,SAAS,MAAM,WAAW;AAAA,UAChG,EAAE;AACF,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAEA,UAAI,KAAK,SAAS,YAAY,KAAK,MAAM;AACvC,YAAI,KAAK,SAAS,SAAS,KAAK,IAAI,GAAG;AACrC,kBAAQ,WAAS;AAAA,YACf,GAAG;AAAA,YACH,UAAU,KAAK,SAAS,QAAQ,KAAK,MAAO,EAAE;AAAA,UAChD,EAAE;AACF,iBAAO;AAAA,QACT;AACA,eAAO;AAAA,MACT;AAEA,aAAO;AAAA,IACT;AAEA,0BAAsB,UAAU;AAEhC,WAAO,MAAM;AACX,4BAAsB,IAAI;AAAA,IAC5B;AAAA,EACF,GAAG,CAAC,KAAK,UAAU,qBAAqB,CAAC;AAIzC,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,KAAK,MAAM,KAAK,WAAW,eAAe,CAAC,qBAAqB,mBAAoB;AACzF,UAAM,UAAU,WAAW,MAAM,YAAY,QAAQ,IAAI,GAAG,GAAI;AAChE,WAAO,MAAM,aAAa,OAAO;AAAA,EACnC,GAAG,CAAC,KAAK,IAAI,KAAK,QAAQ,KAAK,OAAO,KAAK,UAAU,KAAK,UAAU,mBAAmB,kBAAkB,CAAC;AAG1G,EAAAA,YAAU,MAAM;AACd,UAAM,UAAU,CAAC,MAAyB;AACxC,UAAI,mBAAmB;AACrB,UAAE,eAAe;AACjB,UAAE,cAAc;AAAA,MAClB;AAAA,IACF;AACA,WAAO,iBAAiB,gBAAgB,OAAO;AAC/C,WAAO,MAAM,OAAO,oBAAoB,gBAAgB,OAAO;AAAA,EACjE,GAAG,CAAC,iBAAiB,CAAC;AAGtB,EAAAA,YAAU,MAAM;AACd,UAAM,UAAU,CAAC,MAAqB;AACpC,UAAI,EAAE,QAAQ,UAAU;AACtB,YAAI,oBAAoB;AACtB,gCAAsB;AACtB;AAAA,QACF;AACA,YAAI,WAAY;AAChB,YAAI,qBAAqB,CAAC,QAAQ,yCAAyC,EAAG;AAC9E,iBAAS,GAAG;AAAA,MACd;AAAA,IACF;AACA,WAAO,iBAAiB,WAAW,OAAO;AAC1C,WAAO,MAAM,OAAO,oBAAoB,WAAW,OAAO;AAAA,EAC5D,GAAG,CAAC,mBAAmB,YAAY,UAAU,kBAAkB,CAAC;AAGhE,EAAAA,YAAU,MAAM;AACd,QAAI,UAAU,QAAQ,CAAC,QAAQ,CAAC,WAAW,CAAC,uBAAuB,SAAS;AAC1E,6BAAuB,UAAU;AACjC,oBAAc,IAAI;AAElB,YAAM,YAAY,UAAU,SAAS,SAAS,UAAU,MAAM,IAAI;AAGlE,UAAI,OAAO,WAAW,aAAa;AACjC,eAAO,QAAQ,aAAa,CAAC,GAAG,IAAI,GAAG,QAAQ,SAAS;AAAA,MAC1D;AAEA,YAAM,cAAc,YAAY;AAC9B,YAAI;AACF,gBAAM,MAAM,MAAM,MAAM,GAAG,WAAW,gBAAgB;AAAA,YACpD,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,YAC9C,MAAM,KAAK,UAAU;AAAA,cACnB,QAAQ,UAAU;AAAA,cAClB;AAAA,cACA,OAAO,UAAU;AAAA,YACnB,CAAC;AAAA,UACH,CAAC;AAED,cAAI,CAAC,IAAI,IAAI;AACX,kBAAM,QAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,oBAAoB,EAAE;AAC3E,oBAAQ,MAAM,sBAAsB,KAAK;AACzC;AAAA,UACF;AAGA,gBAAM,SAAS,IAAI,MAAM,UAAU;AACnC,cAAI,CAAC,OAAQ;AAEb,gBAAM,UAAU,IAAI,YAAY;AAChC,cAAI,cAAc;AAClB,cAAI,iBAAiB;AACrB,cAAI,oBAAoB;AACxB,cAAI,iBAAiB;AAErB,iBAAO,MAAM;AACX,kBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,gBAAI,KAAM;AAEV,kBAAM,QAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AACpD,kBAAM,WAAW,MAAM,MAAM,IAAI;AAEjC,uBAAW,QAAQ,UAAU;AAC3B,kBAAI,KAAK,WAAW,QAAQ,GAAG;AAC7B,sBAAM,OAAO,KAAK,MAAM,CAAC;AACzB,oBAAI,SAAS,SAAU;AACvB,oBAAI;AACF,wBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,sBAAI,OAAO,MAAM;AACf,mCAAe,OAAO;AAGtB,wBAAI,CAAC,kBAAkB,YAAY,SAAS,IAAI,GAAG;AACjD,4BAAM,YAAY,YAAY,MAAM,IAAI,EAAE,CAAC;AAC3C,0BAAI,UAAU,WAAW,IAAI,GAAG;AAC9B,8BAAM,QAAQ,UAAU,MAAM,CAAC,EAAE,KAAK;AACtC,gCAAQ,WAAS,EAAE,GAAG,MAAM,MAAM,EAAE;AACpC,yCAAiB;AACjB,yCAAiB,UAAU,SAAS;AAAA,sBACtC;AAAA,oBACF;AAGA,wBAAI,kBAAkB,CAAC,mBAAmB;AACxC,4BAAM,aAAa,YAAY,MAAM,cAAc;AACnD,0BAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,8BAAM,aAAa,WAAW,MAAM,IAAI,EAAE,CAAC,EAAE,KAAK;AAClD,8BAAM,cAAc,WAAW,MAAM,YAAY,KAAK,WAAW,MAAM,UAAU;AACjF,4BAAI,aAAa;AACf,gCAAM,WAAW,YAAY,CAAC;AAC9B,kCAAQ,WAAS,EAAE,GAAG,MAAM,SAAS,EAAE;AACvC,8CAAoB;AACpB,4CAAkB,WAAW,SAAS;AAAA,wBACxC,WAAW,YAAY;AAErB,8CAAoB;AAAA,wBACtB;AAAA,sBACF;AAAA,oBACF;AAGA,0BAAM,cAAc,YAAY,MAAM,cAAc,EAAE,KAAK;AAC3D,4BAAQ,WAAS,EAAE,GAAG,MAAM,UAAU,YAAY,EAAE;AAAA,kBACtD;AAAA,gBACF,QAAQ;AAAA,gBAER;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,gBAAM,YAAY,YAAY,MAAM,cAAc,EAAE,KAAK;AACzD,kBAAQ,WAAS;AAAA,YACf,GAAG;AAAA,YACH,UAAU;AAAA,UACZ,EAAE;AAAA,QACJ,SAAS,KAAK;AACZ,kBAAQ,MAAM,qBAAqB,GAAG;AAAA,QACxC,UAAE;AACA,wBAAc,KAAK;AAAA,QACrB;AAAA,MACF;AAEA,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,WAAW,MAAM,SAAS,aAAa,QAAQ,CAAC;AAGpD,QAAM,iBAAiBE,aAAY,YAAY;AAC7C,QAAI,CAAC,KAAK,GAAI;AACd,wBAAoB,IAAI;AACxB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,qBAAqB,KAAK,EAAE,EAAE;AACpE,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,mBAAa,KAAK,QAAQ,CAAC,CAAC;AAAA,IAC9B,SAAS,KAAK;AACZ,cAAQ,MAAM,8BAA8B,GAAG;AAAA,IACjD,UAAE;AACA,0BAAoB,KAAK;AAAA,IAC3B;AAAA,EACF,GAAG,CAAC,KAAK,IAAI,WAAW,CAAC;AAGzB,QAAM,kBAAkBA,aAAY,OAAO,eAAuB;AAChE,UAAM,WAAW,UAAU,KAAK,OAAK,EAAE,OAAO,UAAU;AACxD,QAAI,CAAC,SAAU;AAGf,QAAI,CAAC,cAAc;AACjB,sBAAgB,EAAE,GAAG,KAAK,CAAC;AAAA,IAC7B;AAEA,0BAAsB,QAAQ;AAC9B,YAAQ,WAAS;AAAA,MACf,GAAG;AAAA,MACH,OAAO,SAAS,SAAS,KAAK;AAAA,MAC9B,UAAU,SAAS,YAAY,KAAK;AAAA,MACpC,UAAU,SAAS;AAAA,IACrB,EAAE;AAAA,EACJ,GAAG,CAAC,WAAW,MAAM,YAAY,CAAC;AAGlC,QAAM,wBAAwBA,aAAY,MAAM;AAC9C,QAAI,cAAc;AAChB,cAAQ,YAAY;AACpB,sBAAgB,IAAI;AAAA,IACtB;AACA,0BAAsB,IAAI;AAAA,EAC5B,GAAG,CAAC,YAAY,CAAC;AAGjB,QAAM,kBAAkBA,aAAY,YAAY;AAC9C,QAAI,CAAC,mBAAoB;AACzB,oBAAgB,IAAI;AACpB,0BAAsB,IAAI;AAE1B,UAAM,SAAS;AAAA,EACjB,GAAG,CAAC,oBAAoB,QAAQ,CAAC;AAEjC,QAAM,kBAAkB,YAAY;AAClC,QAAI,CAAC,QAAQ,uBAAuB,EAAG;AACvC,UAAM,MAAM,GAAG,WAAW,UAAU,KAAK,EAAE,IAAI;AAAA,MAC7C,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,QAAQ,QAAQ,CAAC;AAAA,IAC1C,CAAC;AACD,YAAQ,WAAS,EAAE,GAAG,MAAM,QAAQ,QAAQ,EAAE;AAAA,EAChD;AAEA,QAAM,QAAQ,WAAW,KAAK,QAAQ;AACtC,QAAM,cAAc,KAAK,WAAW;AAGpC,MAAI,SAAS;AACX,WACE,gBAAAL,OAAC,SAAI,WAAU,wBACb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,UAAS;AAAA,UACT,kBAAkB,MAAM;AAAA,UAAC;AAAA,UACzB,SAAS;AAAA;AAAA,MACX;AAAA,MACA,gBAAAA,MAAC,UAAK,WAAU,8BACd,0BAAAA,MAAC,mBAAgB,QAAgB,GACnC;AAAA,OACF;AAAA,EAEJ;AAEA,SACE,gBAAAC,OAAC,SAAI,WAAU,wBAEZ;AAAA,0BACC,gBAAAA,OAAC,SAAI,WAAU,iIACb;AAAA,sBAAAA,OAAC,UAAK,WAAU,8CAA6C;AAAA;AAAA,QACjC,IAAI,KAAK,mBAAmB,SAAS,EAAE,eAAe;AAAA,SAClF;AAAA,MACA,gBAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,wBAAAD;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,OACF;AAAA,IAID,CAAC,sBACA,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,QAAQ,eAAe,OAAO;AAAA,QAC9B,aAAa,eAAe,cAAc;AAAA,QAC1C,UAAU,KAAK;AAAA,QACf,kBAAkB,CAAC,OAAO,QAAQ,WAAS,EAAE,GAAG,MAAM,UAAU,GAAG,EAAE;AAAA,QACrE;AAAA,QACA;AAAA,QACA,cAAc;AAAA,QACd,UAAU;AAAA,QACV,WAAW,KAAK,KAAK;AAAA,UACnB,MAAM;AAAA,UACN,SAAS;AAAA,UACT,gBAAgB;AAAA,UAChB,YAAY;AAAA,UACZ,OAAO;AAAA,UACP,SAAS;AAAA,UACT,QAAQ;AAAA,UACR,SAAS;AAAA,QACX,IAAI;AAAA,QACJ;AAAA,QACA,cAAc,CAAC,CAAC,SAAS,gBAAgB,CAAC,SAAS,aAAa;AAAA,QAChE,qBAAqB,SAAS,cAAc;AAAA,QAC5C,cAAc,MAAM,gBAAgB,IAAI;AAAA,QACxC,eAAe,SAAS,KAAK,OAAO,OAAK,CAAC,EAAE,QAAQ,EAAE;AAAA,QACtD,gBAAgB,MAAM,gBAAgB,IAAI;AAAA;AAAA,IAC5C;AAAA,IAIF,gBAAAA,MAAC,UAAK,WAAU,iDACd,0BAAAC,OAAC,aAAQ,WAAW,GAAG,OAAO,SAAS,wBAErC;AAAA,sBAAAA,OAAC,YAAO,WAAU,kBACf;AAAA,sBAAc,CAAC,KAAK,QACnB,gBAAAD,MAACE,WAAA,EAAS,WAAU,aAAY,IAEhC,gBAAAF;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,KAAK;AAAA,YACZ,UAAU,CAAC,QAAQ,QAAQ,WAAS,EAAE,GAAG,MAAM,OAAO,IAAI,EAAE;AAAA,YAC5D,aAAY;AAAA,YACZ,UAAU,cAAc,CAAC,CAAC;AAAA,YAC1B,WAAW,GAAG,OAAO,KAAK,kGAAmG,cAAc,qBAAsB,kCAAkC,EAAE;AAAA;AAAA,QACvM;AAAA,QAED,cAAc,CAAC,KAAK,WACnB,gBAAAA,MAACE,WAAA,EAAS,WAAU,aAAY,IAEhC,gBAAAF;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,KAAK;AAAA,YACZ,UAAU,CAAC,QAAQ,QAAQ,WAAS,EAAE,GAAG,MAAM,UAAU,IAAI,EAAE;AAAA,YAC/D,aAAY;AAAA,YACZ,UAAU,cAAc,CAAC,CAAC;AAAA,YAC1B,WAAW,GAAG,OAAO,QAAQ,kGAAmG,cAAc,qBAAsB,kCAAkC,EAAE;AAAA;AAAA,QAC1M;AAAA,QAEF,gBAAAA,MAAC,SAAI,WAAU,SACb,0BAAAA,MAAC,UAAK,WAAW,GAAG,OAAO,MAAM,cAAc,aAAa,eAAe,EAAE,IAC1E,mBAAS,MAAM,QAAQ,SAAS,MAAM,SAAS,UAClD,GACF;AAAA,SACF;AAAA,MAGA,gBAAAA,MAAC,SAAI,WAAU,QACZ,wBAAc,CAAC,KAAK,WACnB,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAACE,WAAA,EAAS,WAAU,cAAa;AAAA,QACjC,gBAAAF,MAACE,WAAA,EAAS,WAAU,cAAa;AAAA,QACjC,gBAAAF,MAACE,WAAA,EAAS,WAAU,aAAY;AAAA,QAChC,gBAAAF,MAACE,WAAA,EAAS,WAAU,cAAa;AAAA,QACjC,gBAAAF,MAACE,WAAA,EAAS,WAAU,aAAY;AAAA,SAClC,IACE,eACF,gBAAAF;AAAA,QAAC;AAAA;AAAA,UACC,KAAK;AAAA,UACL,OAAO,KAAK;AAAA,UACZ,UAAU,OAAK,QAAQ,WAAS,EAAE,GAAG,MAAM,UAAU,EAAE,OAAO,MAAM,EAAE;AAAA,UACtE,aAAY;AAAA,UACZ,UAAU,cAAc,CAAC,CAAC;AAAA,UAC1B,WAAW,GAAG,OAAO,KAAK,oIAAqI,cAAc,qBAAsB,kCAAkC,EAAE;AAAA;AAAA,MACzO,IAEA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,KAAK;AAAA,UACd,UAAU,CAAC,OAAO,QAAQ,WAAS,EAAE,GAAG,MAAM,UAAU,GAAG,EAAE;AAAA,UAC7D,eAAe;AAAA,UACf,WAAW,CAAC;AAAA,UACZ,cAAc,OAAO;AAAA,UACrB,mBAAmB,CAAC,QAA+B;AACjD,gBAAI,KAAK,cAAc;AACrB,uBAAS,gBAAgB;AAAA,gBACvB,MAAM,IAAI;AAAA,gBACV,MAAM,IAAI;AAAA,gBACV,IAAI,IAAI;AAAA,gBACR,oBAAoB,IAAI;AAAA,cAC1B,CAAC;AAAA,YACH,OAAO;AACL,uBAAS,gBAAgB,IAAI;AAAA,YAC/B;AAAA,UACF;AAAA,UACA,gBAAgB,CAAC,cAAsB;AACrC,qBAAS,YAAY,SAAS;AAC9B,4BAAgB,IAAI;AAAA,UACtB;AAAA;AAAA,MACF,GAEJ;AAAA,MAGC,CAAC,sBACA,gBAAAC,OAAC,SAAI,WAAU,sEAEb;AAAA,wBAAAD,MAAC,SAAI,WAAU,6CACb,0BAAAC,OAAC,SAAI,WAAU,2BACb;AAAA,0BAAAD,MAAC,UAAK,WAAU,sBAAqB,iBAAG;AAAA,UACxC,gBAAAA,MAAC,UAAK,WAAU,iBAAgB,iBAAG;AAAA,UAClC,cACC,gBAAAC,OAAC,UAAK,WAAU,8DACb;AAAA,iBAAK;AAAA,YACN,gBAAAD,MAAC,SAAI,WAAU,yBAAwB,MAAK,QAAO,SAAQ,aAAY,QAAO,gBAC5E,0BAAAA,MAAC,UAAK,eAAc,SAAQ,gBAAe,SAAQ,aAAa,GAAG,GAAE,wGAAuG,GAC9K;AAAA,aACF,IAEA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO,KAAK;AAAA,cACZ,UAAU,CAAC,MAAM,QAAQ,WAAS,EAAE,GAAG,MAAM,MAAM,EAAE,OAAO,MAAM,EAAE;AAAA,cACpE,aAAY;AAAA,cACZ,WAAU;AAAA;AAAA,UACZ;AAAA,WAEJ,GACF;AAAA,QAGC,OAAO,OAAO,OAAK,EAAE,aAAa,QAAQ,EAAE,IAAI,WAAS;AACxD,gBAAM,oBAAoB,CAAC,MAAc,UAAmB;AAC1D,oBAAQ,WAAS,EAAE,GAAG,MAAM,CAAC,IAAI,GAAG,MAAM,EAAE;AAAA,UAC9C;AAGA,cAAI,CAAC,MAAM,OAAO;AAChB,mBACE,gBAAAA;AAAA,cAAC,MAAM;AAAA,cAAN;AAAA,gBAEC,OAAO,KAAK,MAAM,IAAI;AAAA,gBACtB,UAAU,CAAC,QAAiB,QAAQ,WAAS,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,EAAE;AAAA,gBAC5E,eAAe;AAAA,gBACf;AAAA,gBACA,UAAU,UAAU;AAAA;AAAA,cALf,MAAM;AAAA,YAMb;AAAA,UAEJ;AAGA,iBACE,gBAAAA,MAAC,SAAqB,WAAU,mDAC9B,0BAAAC,OAAC,SAAI,WAAU,kCACb;AAAA,4BAAAD,MAAC,UAAK,WAAU,oCAAoC,gBAAM,OAAM;AAAA,YAChE,gBAAAA,MAAC,SAAI,WAAU,UACb,0BAAAA;AAAA,cAAC,MAAM;AAAA,cAAN;AAAA,gBACC,OAAO,KAAK,MAAM,IAAI;AAAA,gBACtB,UAAU,CAAC,QAAiB,QAAQ,WAAS,EAAE,GAAG,MAAM,CAAC,MAAM,IAAI,GAAG,IAAI,EAAE;AAAA,gBAC5E,eAAe;AAAA,gBACf;AAAA,gBACA,UAAU,UAAU;AAAA;AAAA,YACtB,GACF;AAAA,aACF,KAZQ,MAAM,IAahB;AAAA,QAEJ,CAAC;AAAA,QAGD,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,KAAK,QAAQ,CAAC;AAAA,YACpB,cAAc,CAAC,SAAS,QAAQ,WAAS,EAAE,GAAG,MAAM,KAAK,EAAE;AAAA,YAC3D;AAAA,YACA,UAAU,UAAU;AAAA;AAAA,QACtB;AAAA,QAGA,gBAAAC,OAAC,SAAI,WAAU,6CACb;AAAA,0BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,4BAAAD,MAAC,UAAK,WAAU,sBAAqB,oBAAM;AAAA,YAC3C,gBAAAA,MAAC,UAAK,WAAW,cAAc,uCAAuC,iBACnE,wBAAc,cAAc,SAC/B;AAAA,aACF;AAAA,UAGC,cACC,oBACE,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,UAAU,UAAU;AAAA,cACpB,WAAU;AAAA,cAET;AAAA,6BAAa,eACZ,gBAAAA,OAAC,SAAI,WAAU,wBAAuB,SAAQ,aAC5C;AAAA,kCAAAD,MAAC,YAAO,WAAU,cAAa,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK,QAAO,gBAAe,aAAY,KAAI,MAAK,QAAO;AAAA,kBACxG,gBAAAA,MAAC,UAAK,WAAU,cAAa,MAAK,gBAAe,GAAE,mHAAkH;AAAA,mBACvK;AAAA,gBACA;AAAA;AAAA;AAAA,UAEJ,IAEA,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,WAAU;AAAA,cACX;AAAA;AAAA,UAED,IAGF,gBAAAC;AAAA,YAAC;AAAA;AAAA,cACC,SAAS;AAAA,cACT,UAAU,UAAU;AAAA,cACpB,WAAU;AAAA,cAET;AAAA,6BAAa,eACZ,gBAAAA,OAAC,SAAI,WAAU,wBAAuB,SAAQ,aAC5C;AAAA,kCAAAD,MAAC,YAAO,WAAU,cAAa,IAAG,MAAK,IAAG,MAAK,GAAE,MAAK,QAAO,gBAAe,aAAY,KAAI,MAAK,QAAO;AAAA,kBACxG,gBAAAA,MAAC,UAAK,WAAU,cAAa,MAAK,gBAAe,GAAE,mHAAkH;AAAA,mBACvK;AAAA,gBACA;AAAA;AAAA;AAAA,UAEJ;AAAA,WAEJ;AAAA,QAGA,gBAAAC,OAAC,SAAI,WAAU,4EACZ;AAAA,gBAAM,eAAe;AAAA,UAAE;AAAA,UAAW,KAAK,KAAK,QAAQ,GAAG;AAAA,UAAE;AAAA,WAC5D;AAAA,SACF;AAAA,OAEJ,GACF;AAAA,IAGA,gBAAAD,MAAC,YAAO,WAAU,2HAChB,0BAAAA,MAAC,SAAI,WAAU,uDACZ,uBACC,gBAAAA,MAAC,YAAO,WAAU,+DAA8D,0CAEhF,IACE,qBACF,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,SAAS;AAAA,QACT,WAAU;AAAA,QACX;AAAA;AAAA,IAED,IACE,YACF,gBAAAC,OAAC,UAAK;AAAA;AAAA,MAAO,gBAAgB,SAAS;AAAA,OAAE,IAExC,gBAAAD,MAAC,UAAK,2BAAa,GAEvB,GACF;AAAA,IAGC,oBACC,gBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,UAAU,SAAS;AAAA,QACnB;AAAA,QACA;AAAA,QACA,cAAc,SAAS,cAAc,QAAQ;AAAA,QAC7C,iBAAiB,SAAS;AAAA,QAC1B,SAAS,SAAS;AAAA,QAClB,QAAQ,SAAS;AAAA,QACjB,UAAU,SAAS;AAAA,QACnB,WAAW,SAAS;AAAA,QACpB,gBAAgB,SAAS;AAAA,QACzB,iBAAiB,SAAS;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS,MAAM,gBAAgB,KAAK;AAAA,QACpC,kBAAkB,MAAM,SAAS,gBAAgB,IAAI;AAAA;AAAA,IACvD;AAAA,KAEJ;AAEJ;;;AuM57BA,SAAS,YAAAO,YAAU,aAAAC,mBAAiB;AACpC,SAAS,eAAAC,cAAa,aAAa,gBAAAC,eAAc,WAAW,QAAAC,OAAM,UAAAC,SAAQ,UAAAC,SAAQ,MAAM,KAAAC,IAAG,gBAAAC,eAAc,kBAAAC,iBAAgB,WAAAC,gBAAe;AA2E9H,SAmNF,YAAAC,YAnNE,OAAAC,OAGI,QAAAC,cAHJ;AA1BH,SAAS,aAAa,EAAE,QAAQ,GAAwB;AAC7D,QAAM,EAAE,UAAU,YAAY,kBAAkB,IAAI,oBAAoB;AACxE,QAAM,SAAS,YAAY,UAAU,CAAC;AACtC,QAAM,mBAAmB,YAAY,UAAU,oBAAoB;AACnE,QAAM,UAAU;AAEhB,QAAM,mBAAmC;AAAA,IACvC,EAAE,MAAM,mBAAmB,OAAO,SAAS,aAAa,iCAAiC,UAAU,QAAQ;AAAA,IAC3G,EAAE,MAAM,mBAAmB,OAAO,aAAa,aAAa,oBAAoB,UAAU,QAAQ;AAAA,IAClG,EAAE,MAAM,kBAAkB,OAAO,QAAQ,aAAa,4BAA4B,UAAU,OAAO;AAAA,IACnG,EAAE,MAAM,gBAAgB,OAAO,eAAe,aAAa,gCAAgC;AAAA,IAC3F,EAAE,MAAM,uBAAuB,OAAO,aAAa,aAAa,wBAAwB;AAAA,IACxF,EAAE,MAAM,sBAAsB,OAAO,YAAY,aAAa,uBAAuB;AAAA,IACrF,EAAE,MAAM,oBAAoB,OAAO,UAAU,aAAa,oCAAoC,UAAU,SAAS;AAAA,EACnH;AAGA,QAAM,gBAAgB,mBAClB,mBACA,iBAAiB,OAAO,UAAQ,KAAK,SAAS,kBAAkB;AAGpE,MAAI,CAAC,WAAW,YAAY,KAAK;AAC/B,QAAI,SAAS;AACX,aACE,gBAAAA,OAAC,SAAI,WAAU,+BACb;AAAA,wBAAAD,MAAC,SAAI,WAAU,gDAA+C;AAAA,QAC9D,gBAAAA,MAAC,SAAI,WAAU,kDACZ,WAAC,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,EAAE,IAAI,OACtB,gBAAAC,OAAC,SAAY,WAAU,8CACrB;AAAA,0BAAAD,MAAC,SAAI,WAAU,2CAA0C;AAAA,UACzD,gBAAAA,MAAC,SAAI,WAAU,gDAA+C;AAAA,aAFtD,CAGV,CACD,GACH;AAAA,SACF;AAAA,IAEJ;AAEA,WACE,gBAAAC,OAAC,SAAI,WAAU,+BACb;AAAA,sBAAAD,MAAC,QAAG,WAAU,0DAAyD,sBAAQ;AAAA,MAE/E,gBAAAA,MAAC,SAAI,WAAU,kDACZ,wBAAc,IAAI,CAAC,SAClB,gBAAAC;AAAA,QAAC;AAAA;AAAA,UAEC,SAAS,MAAM,SAAS,KAAK,IAAI;AAAA,UACjC,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,OAAE,WAAU,iCAAiC,eAAK,OAAM;AAAA,YACxD,KAAK,WACJ,gBAAAA,MAAC,OAAE,WAAU,2BAA2B,iBAAO,KAAK,QAAQ,KAAK,GAAE,IAEnE,gBAAAA,MAAC,OAAE,WAAU,sCAAqC,8BAAW;AAAA;AAAA;AAAA,QAR1D,KAAK;AAAA,MAUZ,CACD,GACH;AAAA,OACF;AAAA,EAEJ;AAGA,QAAM,WAAW,QAAQ,MAAM,CAAC;AAGhC,QAAM,sBAAsB,SAAS,MAAM,mBAAmB;AAE9D,SACE,gBAAAC,OAAC,SAAI,WAAU,+BACZ;AAAA,iBAAa,WAAW,gBAAAD,MAAC,wBAAqB;AAAA,IAC9C,aAAa,QAAQ,gBAAAA,MAAC,qBAAkB;AAAA,IACxC,aAAa,UAAU,gBAAAA,MAAC,uBAAoB;AAAA,IAC5C,aAAa,YAAY,gBAAAA,MAAC,yBAAsB;AAAA,IAChD,aAAa,WAAW,gBAAAA,MAAC,wBAAqB;AAAA,IAC9C,aAAa,eAAe,gBAAAA,MAAC,4BAAyB;AAAA,IACtD,uBAAuB,gBAAAA,MAAC,yBAAsB,YAAY,oBAAoB,CAAC,GAAG;AAAA,IAClF,aAAa,cAAc,gBAAAA,MAAC,2BAAwB;AAAA,KACvD;AAEJ;AAEA,SAAS,uBAAuB;AAC9B,QAAM,EAAE,YAAY,IAAI,oBAAoB;AAC5C,QAAM,CAAC,OAAO,QAAQ,IAAIE,WAAiB,CAAC,CAAC;AAC7C,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAC9C,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAsB,IAAI;AAChE,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,EAAE;AAC7C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,EAAE;AAC3C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,QAAQ;AACjD,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,EAAE;AAC7C,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAC1C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAwB,IAAI;AAE5D,EAAAC,YAAU,MAAM;AACd,UAAM,GAAG,WAAW,QAAQ,EACzB,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC,EAClD,KAAK,SAAO;AAAE,eAAS,IAAI,QAAQ,CAAC,CAAC;AAAG,iBAAW,KAAK;AAAA,IAAE,CAAC,EAC3D,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,EAClC,GAAG,CAAC,WAAW,CAAC;AAEhB,WAAS,YAAY;AACnB,iBAAa,EAAE;AACf,gBAAY,EAAE;AACd,gBAAY,QAAQ;AACpB,iBAAa,EAAE;AACf,mBAAe,IAAI;AAAA,EACrB;AAEA,WAAS,cAAc;AACrB,cAAU;AACV,gBAAY,IAAI;AAAA,EAClB;AAEA,WAAS,aAAa,MAAY;AAChC,mBAAe,IAAI;AACnB,iBAAa,KAAK,KAAK;AACvB,gBAAY,KAAK,QAAQ,EAAE;AAC3B,gBAAY,KAAK,IAAI;AACrB,iBAAa,EAAE;AACf,gBAAY,IAAI;AAAA,EAClB;AAEA,iBAAe,aAAa,GAAoB;AAC9C,MAAE,eAAe;AACjB,QAAI,CAAC,UAAW;AAChB,cAAU,IAAI;AACd,iBAAa,EAAE;AAEf,UAAM,MAAM,cAAc,GAAG,WAAW,UAAU,YAAY,EAAE,KAAK,GAAG,WAAW;AACnF,UAAM,SAAS,cAAc,UAAU;AAEvC,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,WAAW,MAAM,YAAY,MAAM,MAAM,SAAS,CAAC;AAAA,IACnF,CAAC;AAED,QAAI,IAAI,IAAI;AACV,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,UAAI,aAAa;AACf,iBAAS,MAAM,IAAI,OAAK,EAAE,OAAO,YAAY,KAAK,KAAK,OAAO,CAAC,CAAC;AAAA,MAClE,OAAO;AACL,iBAAS,CAAC,KAAK,MAAM,GAAG,KAAK,CAAC;AAAA,MAChC;AACA,kBAAY,KAAK;AACjB,gBAAU;AAAA,IACZ,OAAO;AACL,YAAM,SAAS,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAChD,mBAAa,OAAO,SAAS,aAAa,cAAc,WAAW,QAAQ,OAAO;AAAA,IACpF;AACA,cAAU,KAAK;AAAA,EACjB;AAEA,iBAAe,iBAAiB,IAAY,OAAe;AACzD,QAAI,CAAC,QAAQ,gBAAgB,KAAK,IAAI,EAAG;AACzC,UAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAC1E,QAAI,IAAI,GAAI,UAAS,MAAM,OAAO,OAAK,EAAE,OAAO,EAAE,CAAC;AACnD,gBAAY,IAAI;AAAA,EAClB;AAEA,WAAS,oBAAoB,MAAc;AACzC,QAAI,SAAS,QAAS,QAAO;AAC7B,QAAI,SAAS,UAAW,QAAO;AAC/B,WAAO;AAAA,EACT;AAEA,MAAI,QAAS,QAAO,gBAAAH,MAAC,SAAI,WAAU,uCAAsC;AAEzE,SACE,gBAAAA,MAAC,SAAI,WAAU,aAEZ,qBACC,gBAAAC,OAAC,SAAI,WAAU,YACb;AAAA,oBAAAD,MAAC,SAAI,WAAU,0CACb,0BAAAA,MAAC,QAAG,WAAU,yBAAyB,wBAAc,cAAc,YAAW,GAChF;AAAA,IAEC,aACC,gBAAAA,MAAC,SAAI,WAAU,kEACZ,qBACH;AAAA,IAGF,gBAAAC,OAAC,UAAK,UAAU,cAAc,WAAU,aACtC;AAAA,sBAAAA,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,SAAQ,SAAQ,WAAU,6BAA4B,qBAAO;AAAA,QACpE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,IAAG;AAAA,YACH,OAAO;AAAA,YACP,UAAU,OAAK,aAAa,EAAE,OAAO,KAAK;AAAA,YAC1C,UAAQ;AAAA,YACR,aAAa,cAAc,SAAY;AAAA,YACvC,WAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,SAAQ,QAAO,WAAU,6BAA4B,kBAAI;AAAA,QAChE,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,IAAG;AAAA,YACH,OAAO;AAAA,YACP,UAAU,OAAK,YAAY,EAAE,OAAO,KAAK;AAAA,YACzC,aAAa,cAAc,SAAY;AAAA,YACvC,WAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,SAAQ,QAAO,WAAU,6BAA4B,kBAAI;AAAA,QAChE,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,OAAO;AAAA,YACP,UAAU,OAAK,YAAY,EAAE,OAAO,KAAK;AAAA,YACzC,WAAU;AAAA,YAEV;AAAA,8BAAAD,MAAC,YAAO,OAAM,WAAU,qBAAO;AAAA,cAC/B,gBAAAA,MAAC,YAAO,OAAM,UAAS,oBAAM;AAAA,cAC7B,gBAAAA,MAAC,YAAO,OAAM,SAAQ,mBAAK;AAAA;AAAA;AAAA,QAC7B;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,mBACb;AAAA,wBAAAD,MAAC,YAAO,MAAK,UAAS,UAAU,QAAQ,WAAU,+EAC/C,mBAAU,cAAc,cAAc,gBAAkB,cAAc,iBAAiB,eAC1F;AAAA,QACA,gBAAAA,MAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,sBAAY,KAAK;AAAG,oBAAU;AAAA,QAAE,GAAG,WAAU,yDAAwD,oBAE5I;AAAA,SACF;AAAA,OACF;AAAA,KACF,IAEA,gBAAAC,OAAAF,YAAA,EAEE;AAAA,oBAAAE,OAAC,SAAI,WAAU,qCACb;AAAA,sBAAAD,MAAC,QAAG,WAAU,yBAAwB,mBAAK;AAAA,MAC3C,gBAAAA,MAAC,YAAO,SAAS,aAAa,WAAU,mEAAkE,sBAE1G;AAAA,OACF;AAAA,IAGA,gBAAAC,OAAC,SAAI,WAAU,mDACjB;AAAA,sBAAAA,OAAC,WAAM,WAAU,UACf;AAAA,wBAAAD,MAAC,WAAM,WAAU,eACf,0BAAAC,OAAC,QACC;AAAA,0BAAAD,MAAC,QAAG,WAAU,iEAAgE,mBAAK;AAAA,UACnF,gBAAAA,MAAC,QAAG,WAAU,iEAAgE,kBAAI;AAAA,UAClF,gBAAAA,MAAC,QAAG,WAAU,iEAAgE,kBAAI;AAAA,UAClF,gBAAAA,MAAC,QAAG,WAAU,iEAAgE,qBAAO;AAAA,UACrF,gBAAAA,MAAC,QAAG,WAAU,kEAAiE,qBAAO;AAAA,WACxF,GACF;AAAA,QACA,gBAAAA,MAAC,WAAM,WAAU,0BACd,gBAAM,IAAI,UACT,gBAAAC,OAAC,QACC;AAAA,0BAAAD,MAAC,QAAG,WAAU,4CAA4C,eAAK,OAAM;AAAA,UACrE,gBAAAA,MAAC,QAAG,WAAU,kEAAkE,eAAK,QAAQ,UAAI;AAAA,UACjG,gBAAAA,MAAC,QAAG,WAAU,qBACZ,0BAAAA,MAAC,UAAK,WAAW,2EAA2E,oBAAoB,KAAK,IAAI,CAAC,IACvH,eAAK,MACR,GACF;AAAA,UACA,gBAAAA,MAAC,QAAG,WAAU,2CAA2C,cAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB,GAAE;AAAA,UACvG,gBAAAA,MAAC,QAAG,WAAU,wBACZ,0BAAAC,OAAC,SAAI,WAAU,yBACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,YAAY,aAAa,KAAK,KAAK,OAAO,KAAK,EAAE;AAAA,gBAChE,WAAU;AAAA,gBAEV,0BAAAA,MAACI,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA,YACpC;AAAA,YACC,aAAa,KAAK,MACjB,gBAAAH,OAAC,SAAI,WAAU,+GACb;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,MAAM;AAAE,iCAAa,IAAI;AAAG,gCAAY,IAAI;AAAA,kBAAE;AAAA,kBACvD,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA,cACA,gBAAAA,MAAC,SAAI,WAAU,uBAAsB;AAAA,cACrC,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,MAAM,iBAAiB,KAAK,IAAI,KAAK,KAAK;AAAA,kBACnD,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA,eACF;AAAA,aAEJ,GACF;AAAA,aAnCO,KAAK,EAoCd,CACD,GACH;AAAA,SACF;AAAA,MACC,MAAM,WAAW,KAChB,gBAAAA,MAAC,OAAE,WAAU,yCAAwC,mDAAqC;AAAA,OAE9F;AAAA,IAGA,gBAAAC,OAAC,SAAI,WAAU,kFACZ;AAAA,YAAM,IAAI,UACT,gBAAAA,OAAC,SAAkB,WAAU,qDAC3B;AAAA,wBAAAA,OAAC,SAAI,WAAU,8BACb;AAAA,0BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,4BAAAD,MAAC,UAAK,WAAU,wBAAwB,eAAK,OAAM;AAAA,YACnD,gBAAAA,MAAC,UAAK,WAAW,2EAA2E,oBAAoB,KAAK,IAAI,CAAC,IACvH,eAAK,MACR;AAAA,aACF;AAAA,UACA,gBAAAC,OAAC,OAAE,WAAU,0CACV;AAAA,iBAAK,QAAQ;AAAA,YAAU;AAAA,YAAY,IAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB;AAAA,aAClF;AAAA,WACF;AAAA,QACA,gBAAAA,OAAC,SAAI,WAAU,YACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,YAAY,aAAa,KAAK,KAAK,OAAO,KAAK,EAAE;AAAA,cAChE,WAAU;AAAA,cAEV,0BAAAA,MAACI,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA,UACpC;AAAA,UACC,aAAa,KAAK,MACjB,gBAAAH,OAAC,SAAI,WAAU,+GACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM;AAAE,+BAAa,IAAI;AAAG,8BAAY,IAAI;AAAA,gBAAE;AAAA,gBACvD,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA,MAAC,SAAI,WAAU,uBAAsB;AAAA,YACrC,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,iBAAiB,KAAK,IAAI,KAAK,KAAK;AAAA,gBACnD,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,WAEJ;AAAA,WApCQ,KAAK,EAqCf,CACD;AAAA,MACE,MAAM,WAAW,KAChB,gBAAAA,MAAC,OAAE,WAAU,yCAAwC,mDAAqC;AAAA,OAE9F;AAAA,KACA,GAEJ;AAEJ;AAEA,SAAS,oBAAoB;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAQG;AACD,QAAM,CAAC,MAAM,OAAO,IAAIE,WAAS,KAAK;AACtC,QAAM,WAAW,UAAU;AAC3B,QAAM,eAAe,SAAS;AAE9B,SACE,gBAAAD,OAAC,SAAI,WAAU,QACb;AAAA,oBAAAA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,QAAQ,CAAC,IAAI;AAAA,QAC5B,WAAU;AAAA,QAEV;AAAA,0BAAAD,MAACK,cAAA,EAAY,WAAW,gCAAgC,OAAO,KAAK,YAAY,IAAI;AAAA,UACnF,WAAW,sCAAsC;AAAA;AAAA;AAAA,IACpD;AAAA,IACC,QACC,gBAAAJ,OAAC,SAAI,WAAU,kBACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,qCACb;AAAA,wBAAAA,OAAC,OAAE,WAAU,iCAAgC;AAAA;AAAA,UAAe;AAAA,WAAa;AAAA,QACxE,YACC,gBAAAA,OAAC,YAAO,MAAK,UAAS,SAAS,SAAS,WAAU,+EAChD;AAAA,0BAAAD,MAAC,aAAU,WAAU,WAAU;AAAA,UAAE;AAAA,WACnC;AAAA,SAEJ;AAAA,MACA,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,OAAO;AAAA,UACP,UAAU,OAAK,SAAS,EAAE,OAAO,KAAK;AAAA,UACtC,MAAM;AAAA,UACN,WAAU;AAAA,UACV;AAAA;AAAA,MACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,oBAAoB;AAC3B,QAAM,EAAE,aAAa,SAAS,IAAI,oBAAoB;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAIE,WAAS,EAAE;AACrC,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,EAAE;AAC7C,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAS,EAAE;AACnD,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,WAAS,EAAE;AACvD,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,EAAE;AAC7C,QAAM,CAAC,oBAAoB,qBAAqB,IAAIA,WAAS,GAAG;AAChE,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,WAAS,KAAK;AAC9D,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAS,eAAe;AAChE,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAA8D,CAAC,CAAC;AAC5F,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,WAAwB,IAAI;AAC5E,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAwB,IAAI;AACpE,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,WAAwB,IAAI;AAC1E,QAAM,CAAC,mBAAmB,oBAAoB,IAAIA,WAAwB,IAAI;AAC9E,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAwB,IAAI;AACpE,QAAM,CAAC,oBAAoB,qBAAqB,IAAIA,WAAwB,IAAI;AAChF,QAAM,CAAC,yBAAyB,0BAA0B,IAAIA,WAAS,EAAE;AACzE,QAAM,CAAC,qBAAqB,sBAAsB,IAAIA,WAAS,EAAE;AACjE,QAAM,CAAC,wBAAwB,yBAAyB,IAAIA,WAAS,EAAE;AACvE,QAAM,CAAC,0BAA0B,2BAA2B,IAAIA,WAAS,EAAE;AAC3E,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,WAAS,EAAE;AAC3D,QAAM,CAAC,qBAAqB,sBAAsB,IAAIA,WAAS,EAAE;AACjE,QAAM,CAAC,2BAA2B,4BAA4B,IAAIA,WAAS,EAAE;AAC7E,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAS,KAAK;AAExC,EAAAC,YAAU,MAAM;AACd,YAAQ,IAAI;AAAA,MACV,MAAM,GAAG,WAAW,cAAc,EAAE,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC;AAAA,MACtF,MAAM,GAAG,WAAW,WAAW,EAAE,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC;AAAA,IACrF,CAAC,EACE,KAAK,CAAC,CAAC,OAAO,WAAW,MAAM;AAE9B,YAAM,OAAO,MAAM,QAAQ,SAAS,CAAC;AACrC,eAAS,KAAK,SAAS,EAAE;AACzB,mBAAa,KAAK,aAAa,EAAE;AACjC,sBAAgB,KAAK,gBAAgB,EAAE;AACvC,wBAAkB,KAAK,kBAAkB,EAAE;AAC3C,mBAAa,KAAK,aAAa,EAAE;AACjC,4BAAsB,KAAK,sBAAsB,GAAG;AACpD,sBAAgB,KAAK,gBAAgB,eAAe;AACpD,gBAAU,KAAK,mBAAmB;AAAA,QAChC,EAAE,IAAI,iBAAiB,MAAM,cAAc,aAAa,4BAA4B;AAAA,QACpF,EAAE,IAAI,eAAe,MAAM,YAAY,aAAa,0BAA0B;AAAA,QAC9E,EAAE,IAAI,WAAW,MAAM,WAAW,aAAa,yBAAyB;AAAA,QACxE,EAAE,IAAI,cAAc,MAAM,cAAc,aAAa,0BAA0B;AAAA,MACjF,CAAC;AACD,0BAAoB,KAAK,oBAAoB,IAAI;AACjD,sBAAgB,KAAK,gBAAgB,IAAI;AACzC,yBAAmB,KAAK,mBAAmB,IAAI;AAC/C,2BAAqB,KAAK,qBAAqB,IAAI;AACnD,sBAAgB,KAAK,gBAAgB,IAAI;AACzC,4BAAsB,KAAK,sBAAsB,IAAI;AACrD,iCAA2B,KAAK,2BAA2B,EAAE;AAC7D,6BAAuB,KAAK,uBAAuB,EAAE;AACrD,gCAA0B,KAAK,0BAA0B,EAAE;AAC3D,kCAA4B,KAAK,4BAA4B,EAAE;AAC/D,0BAAoB,KAAK,oBAAoB,EAAE;AAC/C,6BAAuB,KAAK,uBAAuB,EAAE;AACrD,mCAA6B,KAAK,6BAA6B,EAAE;AAEjE,0BAAoB,YAAY,MAAM,oBAAoB,KAAK;AAC/D,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,EAClC,GAAG,CAAC,WAAW,CAAC;AAGhB,iBAAe,aAAa;AAC1B,cAAU,IAAI;AACd,aAAS,KAAK;AACd,UAAM,QAAQ,IAAI;AAAA,MAChB,MAAM,GAAG,WAAW,gBAAgB;AAAA,QAClC,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UAAO;AAAA,UAAW;AAAA,UAAc;AAAA,UAAgB;AAAA,UAAW;AAAA,UAAoB;AAAA,UAC/E;AAAA,UAAkB;AAAA,UAAc;AAAA,UAAiB;AAAA,UAAmB;AAAA,UAAc;AAAA,QACpF,CAAC;AAAA,MACH,CAAC;AAAA,MACD,MAAM,GAAG,WAAW,aAAa;AAAA,QAC/B,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,EAAE,iBAAiB,CAAC;AAAA,MAC3C,CAAC;AAAA,IACH,CAAC;AACD,cAAU,KAAK;AACf,aAAS,IAAI;AACb,eAAW,MAAM,SAAS,KAAK,GAAG,GAAI;AAAA,EACxC;AAEA,MAAI,SAAS;AACX,WACE,gBAAAH,MAAC,SAAI,WAAU,0CACb,0BAAAA,MAACM,UAAA,EAAQ,WAAU,8CAA6C,GAClE;AAAA,EAEJ;AAEA,SACE,gBAAAL,OAAC,SAAI,WAAU,aAEb;AAAA,oBAAAA,OAAC,SACC;AAAA,sBAAAD,MAAC,QAAG,WAAU,yBAAwB,yBAAW;AAAA,MACjD,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,kDAAoC;AAAA,OACnF;AAAA,IAGA,gBAAAA,MAAC,SAAI,WAAU,4DACb,0BAAAC,OAAC,SAAI,WAAU,iBACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,6CAA4C,2BAAa;AAAA,QAC1E,gBAAAC,OAAC,SAAI,WAAU,4BACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,OAAK,gBAAgB,EAAE,OAAO,KAAK;AAAA,cAC7C,WAAU;AAAA,cAET,iBAAO,IAAI,WACV,gBAAAC,OAAC,YAAsB,OAAO,MAAM,IAAK;AAAA,sBAAM;AAAA,gBAAK;AAAA,gBAAI,MAAM;AAAA,mBAAjD,MAAM,EAAuD,CAC3E;AAAA;AAAA,UACH;AAAA,UACA,gBAAAD,MAACK,cAAA,EAAY,WAAU,oFAAmF;AAAA,WAC5G;AAAA,SACF;AAAA,MAEA,gBAAAJ,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,oCAAmC,iCAAmB;AAAA,QACvE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,wGAE7C;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,OAAK,SAAS,EAAE,OAAO,KAAK;AAAA,YACtC,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,YAMb,WAAU;AAAA,YACV,UAAU;AAAA;AAAA,QACZ;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,cAAc;AAAA,YACd,UAAU;AAAA,YACV,SAAS,MAAM,oBAAoB,IAAI;AAAA,YACvC,cAAa;AAAA,YACb,UAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,oCAAmC,iCAAmB;AAAA,QACvE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,6HAE7C;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,OAAK,aAAa,EAAE,OAAO,KAAK;AAAA,YAC1C,aAAa;AAAA;AAAA;AAAA;AAAA,YAIb,WAAU;AAAA,YACV,UAAU;AAAA;AAAA,QACZ;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,cAAc;AAAA,YACd,UAAU;AAAA,YACV,SAAS,MAAM,gBAAgB,IAAI;AAAA,YACnC,cAAa;AAAA,YACb,UAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,oCAAmC,2BAAa;AAAA,QACjE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,wEAE7C;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,OAAK,gBAAgB,EAAE,OAAO,KAAK;AAAA,YAC7C,aAAa;AAAA;AAAA;AAAA;AAAA,YAIb,WAAU;AAAA,YACV,UAAU;AAAA;AAAA,QACZ;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,cAAc;AAAA,YACd,UAAU;AAAA,YACV,SAAS,MAAM,mBAAmB,IAAI;AAAA,YACtC,cAAa;AAAA,YACb,UAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,oCAAmC,8BAAgB;AAAA,QACpE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,qIAE7C;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO;AAAA,YACP,UAAU,OAAK,kBAAkB,EAAE,OAAO,KAAK;AAAA,YAC/C,aAAa;AAAA;AAAA;AAAA;AAAA,YAIb,WAAU;AAAA,YACV,UAAU;AAAA;AAAA,QACZ;AAAA,QACA,gBAAAA,MAAC,SAAI,WAAU,gCACb,0BAAAC,OAAC,SAAI,WAAU,2BACb;AAAA,0BAAAD,MAAC,WAAM,WAAU,6BAA4B,gCAAkB;AAAA,UAC/D,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,KAAK;AAAA,cACL,KAAK;AAAA,cACL,OAAO;AAAA,cACP,UAAU,OAAK,sBAAsB,SAAS,EAAE,OAAO,KAAK,KAAK,GAAG;AAAA,cACpE,WAAU;AAAA,cACV,UAAU;AAAA;AAAA,UACZ;AAAA,WACF,GACF;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,cAAc;AAAA,YACd,UAAU;AAAA,YACV,SAAS,MAAM,qBAAqB,IAAI;AAAA,YACxC,cAAa;AAAA,YACb,UAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,oCAAmC,+BAAiB;AAAA,QACrE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,0GAE7C;AAAA,QACA,gBAAAA,MAAC,SAAI,WAAU,iCACZ,uBACC,gBAAAC;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,SAAS,MAAM,aAAa,EAAE;AAAA,YAC9B,WAAU;AAAA,YAEV;AAAA,8BAAAD,MAAC,aAAU,WAAU,gBAAe;AAAA,cAAE;AAAA;AAAA;AAAA,QAExC,GAEJ;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAO,aAAa;AAAA,YACpB,UAAU,OAAK,aAAa,EAAE,OAAO,KAAK;AAAA,YAC1C,WAAU;AAAA,YACV,UAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,oCAAmC,gCAAkB;AAAA,QACtE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,qFAE7C;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,cAAc;AAAA,YACd,UAAU;AAAA,YACV,SAAS,MAAM,gBAAgB,IAAI;AAAA,YACnC,cAAa;AAAA,YACb,UAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAD,MAAC,WAAM,WAAU,oCAAmC,kCAAoB;AAAA,QACxE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,mFAE7C;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,OAAM;AAAA,YACN,OAAO;AAAA,YACP,cAAc;AAAA,YACd,UAAU;AAAA,YACV,SAAS,MAAM,sBAAsB,IAAI;AAAA,YACzC,cAAa;AAAA,YACb,UAAU;AAAA;AAAA,QACZ;AAAA,SACF;AAAA,MAGA,gBAAAC,OAAC,SAAI,WAAU,iEACb;AAAA,wBAAAA,OAAC,SAAI,WAAU,eACb;AAAA,0BAAAD,MAAC,WAAM,WAAU,oCAAmC,gCAAkB;AAAA,UACtE,gBAAAA,MAAC,OAAE,WAAU,iCAAgC,4EAE7C;AAAA,WACF;AAAA,QACA,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,MAAK;AAAA,YACL,gBAAc;AAAA,YACd,SAAS,MAAM,oBAAoB,CAAC,gBAAgB;AAAA,YACpD,WAAW,6PAA6P,mBAAmB,eAAe,UAAU;AAAA,YAEpT,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW,sGAAsG,mBAAmB,kBAAkB,eAAe;AAAA;AAAA,YACvK;AAAA;AAAA,QACF;AAAA,SACF;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,2BACb;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS;AAAA,YACT,UAAU;AAAA,YACV,WAAU;AAAA,YAET;AAAA,wBAAU,gBAAAD,MAACM,UAAA,EAAQ,WAAU,6BAA4B;AAAA,cACzD,SAAS,cAAc;AAAA;AAAA;AAAA,QAC1B;AAAA,QACC,SACC,gBAAAN,MAAC,UAAK,WAAU,8CAA6C,oBAE7D;AAAA,SAEJ;AAAA,OACF,GACF;AAAA,KACF;AAEJ;AAEA,SAAS,sBAAsB;AAC7B,QAAM,EAAE,YAAY,IAAI,oBAAoB;AAC5C,QAAM,CAAC,MAAM,OAAO,IAAIE,WAAgB,CAAC,CAAC;AAC1C,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,KAAK;AAClD,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAqB,IAAI;AAC7D,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,EAAE;AACzC,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAC1C,QAAM,CAAC,OAAO,QAAQ,IAAIA,WAAS,EAAE;AACrC,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAwB,IAAI;AAE5D,EAAAC,YAAU,MAAM;AACd,cAAU;AAAA,EACZ,GAAG,CAAC,WAAW,CAAC;AAEhB,iBAAe,YAAY;AACzB,UAAM,MAAM,MAAM,MAAM,GAAG,WAAW,OAAO;AAC7C,QAAI,IAAI,IAAI;AACV,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,cAAQ,KAAK,QAAQ,CAAC,CAAC;AAAA,IACzB;AACA,eAAW,KAAK;AAAA,EAClB;AAEA,WAAS,mBAAmB;AAC1B,kBAAc,IAAI;AAClB,eAAW,EAAE;AACb,aAAS,EAAE;AACX,kBAAc,IAAI;AAAA,EACpB;AAEA,WAAS,eAAe,KAAU;AAChC,kBAAc,GAAG;AACjB,eAAW,IAAI,IAAI;AACnB,aAAS,EAAE;AACX,kBAAc,IAAI;AAClB,gBAAY,IAAI;AAAA,EAClB;AAEA,iBAAe,aAAa,KAAU;AACpC,QAAI,CAAC,QAAQ,eAAe,IAAI,IAAI,wCAAwC,EAAG;AAC/E,UAAM,MAAM,MAAM,MAAM,GAAG,WAAW,SAAS,IAAI,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAC7E,QAAI,IAAI,IAAI;AACV,gBAAU;AAAA,IACZ;AACA,gBAAY,IAAI;AAAA,EAClB;AAEA,iBAAe,aAAa,GAAoB;AAC9C,MAAE,eAAe;AACjB,QAAI,CAAC,QAAQ,KAAK,GAAG;AACnB,eAAS,sBAAsB;AAC/B;AAAA,IACF;AAEA,cAAU,IAAI;AACd,aAAS,EAAE;AAEX,UAAM,MAAM,aACR,GAAG,WAAW,SAAS,WAAW,EAAE,KACpC,GAAG,WAAW;AAClB,UAAM,SAAS,aAAa,UAAU;AAEtC,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,QAAQ,KAAK,EAAE,CAAC;AAAA,IAC/C,CAAC;AAED,QAAI,IAAI,IAAI;AACV,oBAAc,KAAK;AACnB,gBAAU;AAAA,IACZ,OAAO;AACL,YAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,eAAS,KAAK,SAAS,oBAAoB;AAAA,IAC7C;AAEA,cAAU,KAAK;AAAA,EACjB;AAEA,MAAI,QAAS,QAAO,gBAAAH,MAAC,SAAI,WAAU,uCAAsC;AAEzE,SACE,gBAAAC,OAAC,SAEC;AAAA,oBAAAA,OAAC,SAAI,WAAU,kDACb;AAAA,sBAAAD,MAAC,QAAG,WAAU,yBAAwB,kBAAI;AAAA,MAC1C,gBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,WAAU;AAAA,UACX;AAAA;AAAA,MAED;AAAA,OACF;AAAA,IAEC,KAAK,WAAW,IACf,gBAAAA,MAAC,SAAI,WAAU,0CAAyC,qDAAuC,IAE/F,gBAAAC,OAAAF,YAAA,EAEE;AAAA,sBAAAC,MAAC,SAAI,WAAU,qCACb,0BAAAC,OAAC,WAAM,WAAU,iCACf;AAAA,wBAAAD,MAAC,WAAM,WAAU,mBACf,0BAAAC,OAAC,QAAG,WAAU,YACZ;AAAA,0BAAAD,MAAC,QAAG,WAAU,sEAAqE,kBAAI;AAAA,UACvF,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,mBAAK;AAAA,UACxF,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,qBAAO;AAAA,UAC1F,gBAAAA,MAAC,QAAG,WAAU,uEAAsE,qBAAO;AAAA,WAC7F,GACF;AAAA,QACA,gBAAAA,MAAC,WAAM,WAAU,8BACd,eAAK,IAAI,SACR,gBAAAC,OAAC,QAAgB,WAAU,YACzB;AAAA,0BAAAD,MAAC,QAAG,WAAU,oBACZ,0BAAAA,MAAC,UAAK,WAAU,gCAAgC,cAAI,MAAK,GAC3D;AAAA,UACA,gBAAAA,MAAC,QAAG,WAAU,0CAA0C,cAAI,QAAQ,SAAS,GAAE;AAAA,UAC/E,gBAAAA,MAAC,QAAG,WAAU,0CACX,cAAI,YAAY,IAAI,KAAK,IAAI,SAAS,EAAE,mBAAmB,IAAI,UAClE;AAAA,UACA,gBAAAA,MAAC,QAAG,WAAU,+BACZ,0BAAAC,OAAC,SAAI,WAAU,yBACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,YAAY,aAAa,IAAI,KAAK,OAAO,IAAI,EAAE;AAAA,gBAC9D,WAAU;AAAA,gBAEV,0BAAAA,MAACI,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA,YACpC;AAAA,YACC,aAAa,IAAI,MAChB,gBAAAH,OAAC,SAAI,WAAU,8GACb;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,MAAM,eAAe,GAAG;AAAA,kBACjC,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA,cACA,gBAAAA,MAAC,SAAI,WAAU,4BAA2B;AAAA,cAC1C,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,MAAM,aAAa,GAAG;AAAA,kBAC/B,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA,eACF;AAAA,aAEJ,GACF;AAAA,aAlCO,IAAI,EAmCb,CACD,GACH;AAAA,SACF,GACF;AAAA,MAGA,gBAAAA,MAAC,SAAI,WAAU,sDACZ,eAAK,IAAI,SACR,gBAAAC,OAAC,SAAiB,WAAU,qDAC1B;AAAA,wBAAAA,OAAC,SAAI,WAAU,8BACb;AAAA,0BAAAD,MAAC,UAAK,WAAU,8BAA8B,cAAI,MAAK;AAAA,UACvD,gBAAAC,OAAC,OAAE,WAAU,0CACV;AAAA,gBAAI,QAAQ,SAAS;AAAA,YAAE;AAAA,YAAkB,IAAI,YAAY,IAAI,KAAK,IAAI,SAAS,EAAE,mBAAmB,IAAI;AAAA,aAC3G;AAAA,WACF;AAAA,QACA,gBAAAA,OAAC,SAAI,WAAU,YACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,YAAY,aAAa,IAAI,KAAK,OAAO,IAAI,EAAE;AAAA,cAC9D,WAAU;AAAA,cAEV,0BAAAA,MAACI,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA,UACpC;AAAA,UACC,aAAa,IAAI,MAChB,gBAAAH,OAAC,SAAI,WAAU,8GACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,eAAe,GAAG;AAAA,gBACjC,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA,MAAC,SAAI,WAAU,4BAA2B;AAAA,YAC1C,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,aAAa,GAAG;AAAA,gBAC/B,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,WAEJ;AAAA,WA/BQ,IAAI,EAgCd,CACD,GACH;AAAA,OACF;AAAA,IAID,cACC,gBAAAC,OAAC,SAAI,WAAU,uDAEb;AAAA,sBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UACV,SAAS,MAAM,cAAc,KAAK;AAAA;AAAA,MACpC;AAAA,MAEA,gBAAAC,OAAC,SAAI,WAAU,yFACb;AAAA,wBAAAD,MAAC,SAAI,WAAU,iCACb,0BAAAA,MAAC,QAAG,WAAU,qDACX,uBAAa,aAAa,cAC7B,GACF;AAAA,QACA,gBAAAC,OAAC,UAAK,UAAU,cACd;AAAA,0BAAAD,MAAC,SAAI,WAAU,aACb,0BAAAC,OAAC,SAAI,WAAU,aACb;AAAA,4BAAAD,MAAC,WAAM,SAAQ,WAAU,WAAU,oCAAmC,kBAAI;AAAA,YAC1E,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,IAAG;AAAA,gBACH,MAAK;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU,CAAC,MAAM,WAAW,EAAE,OAAO,KAAK;AAAA,gBAC1C,aAAY;AAAA,gBACZ,WAAS;AAAA,gBACT,WAAU;AAAA;AAAA,YACZ;AAAA,YACC,SACC,gBAAAA,MAAC,OAAE,WAAU,4BAA4B,iBAAM;AAAA,aAEnD,GACF;AAAA,UACA,gBAAAC,OAAC,SAAI,WAAU,gDACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,SAAS,MAAM,cAAc,KAAK;AAAA,gBAClC,UAAU;AAAA,gBACV,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACA,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,UAAU;AAAA,gBACV,WAAU;AAAA,gBAET,mBAAS,cAAc,aAAa,SAAS;AAAA;AAAA,YAChD;AAAA,aACF;AAAA,WACF;AAAA,SACF;AAAA,OACF;AAAA,KAEJ;AAEJ;AAEA,SAAS,wBAAwB;AAC/B,QAAM,EAAE,YAAY,IAAI,oBAAoB;AAC5C,QAAM,CAAC,QAAQ,SAAS,IAAIE,WAAkB,CAAC,CAAC;AAChD,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,KAAK;AAC9C,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAuB,IAAI;AACnE,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAwB,IAAI;AAGhE,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAS,EAAE;AAC3C,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAS,EAAE;AACnD,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,EAAE;AAC7C,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAS,OAAO;AAC1D,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,WAAS,CAAC;AAC1D,QAAM,CAAC,gBAAgB,iBAAiB,IAAIA,WAAS,EAAE;AACvD,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAS,IAAI;AAErD,EAAAC,YAAU,MAAM;AACd,gBAAY;AAAA,EACd,GAAG,CAAC,WAAW,CAAC;AAEhB,iBAAe,cAAc;AAC3B,UAAM,MAAM,MAAM,MAAM,GAAG,WAAW,SAAS;AAC/C,QAAI,IAAI,IAAI;AACV,YAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,gBAAU,KAAK,QAAQ,CAAC,CAAC;AAAA,IAC3B;AACA,eAAW,KAAK;AAAA,EAClB;AAEA,WAAS,YAAY;AACnB,gBAAY,EAAE;AACd,oBAAgB,EAAE;AAClB,iBAAa,EAAE;AACf,qBAAiB,OAAO;AACxB,wBAAoB,CAAC;AACrB,sBAAkB,EAAE;AACpB,oBAAgB,IAAI;AAAA,EACtB;AAEA,WAAS,aAAa,OAAc;AAClC,oBAAgB,KAAK;AACrB,gBAAY,MAAM,IAAI;AACtB,oBAAgB,KAAK,MAAM,MAAM,QAAQ,EAAE,KAAK,IAAI,CAAC;AACrD,iBAAa,KAAK,MAAM,MAAM,QAAQ,EAAE,KAAK,IAAI,CAAC;AAClD,qBAAiB,MAAM,SAAS;AAChC,wBAAoB,MAAM,YAAY;AACtC,sBAAkB,MAAM,cAAc,EAAE;AACxC,oBAAgB,MAAM,QAAQ;AAC9B,gBAAY,IAAI;AAAA,EAClB;AAEA,WAAS,cAAc;AACrB,oBAAgB,IAAI;AACpB,cAAU;AACV,gBAAY,IAAI;AAAA,EAClB;AAEA,iBAAe,aAAa,GAAoB;AAC9C,MAAE,eAAe;AACjB,UAAM,OAAO;AAAA,MACX,MAAM;AAAA,MACN,UAAU,aAAa,MAAM,GAAG,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,MACnE,UAAU,UAAU,MAAM,IAAI,EAAE,IAAI,OAAK,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,MACjE,WAAW;AAAA,MACX,cAAc;AAAA,MACd,YAAY,kBAAkB;AAAA,MAC9B,UAAU;AAAA,IACZ;AAEA,UAAM,MAAM,eAAe,GAAG,WAAW,WAAW,aAAa,EAAE,KAAK,GAAG,WAAW;AACtF,UAAM,SAAS,eAAe,UAAU;AAExC,UAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC3B;AAAA,MACA,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,IAAI,IAAI;AACV,kBAAY,KAAK;AACjB,gBAAU;AACV,sBAAgB,IAAI;AACpB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,iBAAe,aAAa,IAAY;AACtC,QAAI,CAAC,QAAQ,iEAAiE,EAAG;AACjF,UAAM,MAAM,MAAM,MAAM,GAAG,WAAW,WAAW,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAC3E,QAAI,IAAI,GAAI,WAAU,OAAO,OAAO,OAAK,EAAE,OAAO,EAAE,CAAC;AAAA,EACvD;AAEA,iBAAe,eAAe,SAAiB;AAC7C,kBAAc,OAAO;AACrB,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,WAAW,WAAW,OAAO,aAAa,EAAE,QAAQ,OAAO,CAAC;AACvF,UAAI,IAAI,IAAI;AACV,oBAAY;AAAA,MACd;AAAA,IACF,UAAE;AACA,oBAAc,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,MAAI,QAAS,QAAO,gBAAAH,MAAC,SAAI,WAAU,uCAAsC;AAEzE,SACE,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,oBAAAA,OAAC,SAAI,WAAU,qCACb;AAAA,sBAAAD,MAAC,QAAG,WAAU,yBAAwB,oBAAM;AAAA,MAC5C,gBAAAC,OAAC,YAAO,SAAS,aAAa,WAAU,6FACtC;AAAA,wBAAAD,MAACO,OAAA,EAAK,WAAU,WAAU;AAAA,QAAE;AAAA,SAC9B;AAAA,OACF;AAAA,IACA,gBAAAP,MAAC,OAAE,WAAU,uCAAsC,sEAAwD;AAAA,IAE1G,YACC,gBAAAC,OAAC,SAAI,WAAU,6DACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,qCACb;AAAA,wBAAAD,MAAC,QAAG,WAAU,eAAe,yBAAe,eAAe,aAAY;AAAA,QACvE,gBAAAA,MAAC,YAAO,SAAS,MAAM;AAAE,sBAAY,KAAK;AAAG,oBAAU;AAAG,0BAAgB,IAAI;AAAA,QAAE,GAAG,WAAU,+CAC3F,0BAAAA,MAACQ,IAAA,EAAE,WAAU,WAAU,GACzB;AAAA,SACF;AAAA,MACA,gBAAAP,OAAC,UAAK,UAAU,cAAc,WAAU,aACtC;AAAA,wBAAAA,OAAC,SACC;AAAA,0BAAAD,MAAC,WAAM,WAAU,kCAAiC,kBAAI;AAAA,UACtD,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO;AAAA,cACP,UAAU,OAAK,YAAY,EAAE,OAAO,KAAK;AAAA,cACzC,UAAQ;AAAA,cACR,WAAU;AAAA,cACV,aAAY;AAAA;AAAA,UACd;AAAA,WACF;AAAA,QACA,gBAAAC,OAAC,SACC;AAAA,0BAAAD,MAAC,WAAM,WAAU,kCAAiC,wCAA0B;AAAA,UAC5E,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,OAAO;AAAA,cACP,UAAU,OAAK,gBAAgB,EAAE,OAAO,KAAK;AAAA,cAC7C,WAAU;AAAA,cACV,aAAY;AAAA;AAAA,UACd;AAAA,WACF;AAAA,QACA,gBAAAC,OAAC,SACC;AAAA,0BAAAD,MAAC,WAAM,WAAU,kCAAiC,0CAA4B;AAAA,UAC9E,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,OAAK,aAAa,EAAE,OAAO,KAAK;AAAA,cAC1C,MAAM;AAAA,cACN,WAAU;AAAA,cACV,aAAY;AAAA;AAAA,UACd;AAAA,WACF;AAAA,QACA,gBAAAC,OAAC,SAAI,WAAU,0BACb;AAAA,0BAAAA,OAAC,SACC;AAAA,4BAAAD,MAAC,WAAM,WAAU,kCAAiC,uBAAS;AAAA,YAC3D,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,OAAO;AAAA,gBACP,UAAU,OAAK,iBAAiB,EAAE,OAAO,KAAK;AAAA,gBAC9C,WAAU;AAAA,gBAEV;AAAA,kCAAAD,MAAC,YAAO,OAAM,SAAQ,mBAAK;AAAA,kBAC3B,gBAAAA,MAAC,YAAO,OAAM,UAAS,oBAAM;AAAA,kBAC7B,gBAAAA,MAAC,YAAO,OAAM,UAAS,oBAAM;AAAA;AAAA;AAAA,YAC/B;AAAA,aACF;AAAA,UACA,gBAAAC,OAAC,SACC;AAAA,4BAAAD,MAAC,WAAM,WAAU,kCAAiC,4BAAc;AAAA,YAChE,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAK;AAAA,gBACL,KAAK;AAAA,gBACL,KAAK;AAAA,gBACL,OAAO;AAAA,gBACP,UAAU,OAAK,oBAAoB,SAAS,EAAE,OAAO,KAAK,KAAK,CAAC;AAAA,gBAChE,WAAU;AAAA;AAAA,YACZ;AAAA,aACF;AAAA,WACF;AAAA,QACA,gBAAAC,OAAC,SACC;AAAA,0BAAAD,MAAC,WAAM,WAAU,kCAAiC,oCAAsB;AAAA,UACxE,gBAAAA;AAAA,YAAC;AAAA;AAAA,cACC,OAAO;AAAA,cACP,UAAU,OAAK,kBAAkB,EAAE,OAAO,KAAK;AAAA,cAC/C,MAAM;AAAA,cACN,WAAU;AAAA,cACV,aAAY;AAAA;AAAA,UACd;AAAA,WACF;AAAA,QACA,gBAAAC,OAAC,SAAI,WAAU,2BACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,IAAG;AAAA,cACH,SAAS;AAAA,cACT,UAAU,OAAK,gBAAgB,EAAE,OAAO,OAAO;AAAA;AAAA,UACjD;AAAA,UACA,gBAAAA,MAAC,WAAM,SAAQ,YAAW,WAAU,WAAU,oBAAM;AAAA,WACtD;AAAA,QACA,gBAAAC,OAAC,SAAI,WAAU,cACb;AAAA,0BAAAD,MAAC,YAAO,MAAK,UAAS,WAAU,2DAC7B,yBAAe,WAAW,UAC7B;AAAA,UACA,gBAAAA,MAAC,YAAO,MAAK,UAAS,SAAS,MAAM;AAAE,wBAAY,KAAK;AAAG,sBAAU;AAAG,4BAAgB,IAAI;AAAA,UAAE,GAAG,WAAU,4CAA2C,oBAEtJ;AAAA,WACF;AAAA,SACF;AAAA,OACF;AAAA,IAGF,gBAAAC,OAAC,SAAI,WAAU,0DACZ;AAAA,aAAO,IAAI,WAAS;AACnB,cAAM,WAAW,KAAK,MAAM,MAAM,QAAQ;AAC1C,cAAM,QAAQ,KAAK,MAAM,MAAM,QAAQ;AACvC,eACE,gBAAAD,MAAC,SAAmB,WAAU,OAC5B,0BAAAC,OAAC,SAAI,WAAU,oCACb;AAAA,0BAAAA,OAAC,SACC;AAAA,4BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,8BAAAD,MAAC,OAAE,WAAU,eAAe,gBAAM,MAAK;AAAA,cACtC,CAAC,MAAM,YAAY,gBAAAA,MAAC,UAAK,WAAU,0CAAyC,oBAAM;AAAA,eACrF;AAAA,YACA,gBAAAC,OAAC,OAAE,WAAU,sCACV;AAAA,uBAAS,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI;AAAA,cAAG,SAAS,SAAS,KAAK,KAAK,SAAS,SAAS,CAAC;AAAA,eACnF;AAAA,YACA,gBAAAA,OAAC,OAAE,WAAU,sCACV;AAAA,oBAAM;AAAA,cAAO;AAAA,cAAM,MAAM,WAAW,IAAI,MAAM;AAAA,cAAG;AAAA,cAAI,MAAM;AAAA,cAAU;AAAA,cAAI,MAAM,QAAQ,SAAS;AAAA,cAAE;AAAA,cAClG,MAAM,aAAa,mBAAgB,IAAI,KAAK,MAAM,SAAS,EAAE,mBAAmB,CAAC;AAAA,eACpF;AAAA,aACF;AAAA,UACA,gBAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,eAAe,MAAM,EAAE;AAAA,gBACtC,UAAU,eAAe;AAAA,gBACzB,WAAU;AAAA,gBACV,OAAM;AAAA,gBAEN,0BAAAA,MAAC,QAAK,WAAW,WAAW,eAAe,MAAM,KAAK,kBAAkB,EAAE,IAAI;AAAA;AAAA,YAChF;AAAA,YACA,gBAAAA,MAAC,YAAO,SAAS,MAAM,aAAa,KAAK,GAAG,WAAU,qDACpD,0BAAAA,MAACS,SAAA,EAAO,WAAU,WAAU,GAC9B;AAAA,YACA,gBAAAT,MAAC,YAAO,SAAS,MAAM,aAAa,MAAM,EAAE,GAAG,WAAU,yCACvD,0BAAAA,MAACU,SAAA,EAAO,WAAU,WAAU,GAC9B;AAAA,aACF;AAAA,WACF,KA/BQ,MAAM,EAgChB;AAAA,MAEJ,CAAC;AAAA,MACA,OAAO,WAAW,KACjB,gBAAAV,MAAC,OAAE,WAAU,yCAAwC,kCAAoB;AAAA,OAE7E;AAAA,KACF;AAEJ;AAWA,IAAM,iBAAiB;AAEvB,SAAS,uBAAuB;AAC9B,QAAM,EAAE,aAAa,SAAS,IAAI,oBAAoB;AACtD,QAAM,CAAC,OAAO,QAAQ,IAAIE,WAA8B,CAAC,CAAC;AAC1D,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,CAAC;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,CAAC;AAChD,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,CAAC;AAC9C,QAAM,CAAC,UAAU,WAAW,IAAIA,WAAwB,IAAI;AAE5D,EAAAC,YAAU,MAAM;AACd,eAAW,IAAI;AACf,UAAM,GAAG,WAAW,qBAAqB,WAAW,UAAU,cAAc,yBAAyB,EAClG,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC,EAClD,KAAK,SAAO;AACX,eAAS,IAAI,QAAQ,CAAC,CAAC;AACvB,oBAAc,IAAI,SAAS,IAAI,MAAM,UAAU,CAAC;AAChD,oBAAc,KAAK,MAAM,IAAI,SAAS,IAAI,MAAM,UAAU,KAAK,cAAc,CAAC;AAC9E,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,EAClC,GAAG,CAAC,aAAa,WAAW,CAAC;AAE7B,iBAAe,aAAa,MAAyB;AACnD,QAAI,CAAC,QAAQ,WAAW,KAAK,SAAS,UAAU,yCAAyC,EAAG;AAC5F,UAAM,MAAM,MAAM,MAAM,GAAG,WAAW,UAAU,KAAK,EAAE,IAAI,EAAE,QAAQ,SAAS,CAAC;AAC/E,QAAI,IAAI,IAAI;AACV,eAAS,MAAM,OAAO,OAAK,EAAE,OAAO,KAAK,EAAE,CAAC;AAC5C,oBAAc,OAAK,IAAI,CAAC;AAAA,IAC1B;AACA,gBAAY,IAAI;AAAA,EAClB;AAEA,WAAS,sBAAsB,QAAgB;AAC7C,QAAI,WAAW,YAAa,QAAO;AACnC,QAAI,WAAW,UAAW,QAAO;AACjC,WAAO;AAAA,EACT;AAGA,QAAM,qBAAqB,CAAC,EAAE,SAAS,MAAsC;AAC3E,QAAI,cAAc,EAAG,QAAO;AAE5B,UAAM,iBAAiB,MAAM;AAC3B,YAAM,QAAwD,CAAC;AAC/D,UAAI,cAAc,GAAG;AACnB,iBAAS,IAAI,GAAG,KAAK,YAAY,IAAK,OAAM,KAAK,CAAC;AAAA,MACpD,OAAO;AACL,cAAM,KAAK,CAAC;AACZ,YAAI,cAAc,EAAG,OAAM,KAAK,gBAAgB;AAChD,cAAM,QAAQ,KAAK,IAAI,GAAG,cAAc,CAAC;AACzC,cAAM,MAAM,KAAK,IAAI,aAAa,GAAG,cAAc,CAAC;AACpD,iBAAS,IAAI,OAAO,KAAK,KAAK,KAAK;AACjC,cAAI,CAAC,MAAM,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,QACtC;AACA,YAAI,cAAc,aAAa,EAAG,OAAM,KAAK,cAAc;AAC3D,YAAI,CAAC,MAAM,SAAS,UAAU,EAAG,OAAM,KAAK,UAAU;AAAA,MACxD;AACA,aAAO;AAAA,IACT;AAEA,UAAM,eAAe,aAAa,WAAW,SAAS;AAEtD,WACE,gBAAAH,MAAC,SAAI,MAAK,cAAa,cAAW,cAAa,WAAW,mCAAmC,YAAY,IACvG,0BAAAC,OAAC,QAAG,WAAU,oCACZ;AAAA,sBAAAD,MAAC,QACC,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,eAAe,OAAK,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;AAAA,UACrD,UAAU,eAAe;AAAA,UACzB,cAAW;AAAA,UACX,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,eAAY,WAAU,WAAU;AAAA,YACjC,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,sBAAQ;AAAA;AAAA;AAAA,MAC5C,GACF;AAAA,MACC,eAAe,EAAE;AAAA,QAAI,CAAC,SACrB,OAAO,SAAS,WACd,gBAAAA,MAAC,QACC,0BAAAA,MAAC,UAAK,eAAW,MAAC,WAAU,4CAC1B,0BAAAA,MAACW,iBAAA,EAAe,WAAU,WAAU,GACtC,KAHO,IAIT,IAEA,gBAAAX,MAAC,QACC,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,eAAe,IAAI;AAAA,YAClC,gBAAc,SAAS,cAAc,SAAS;AAAA,YAC9C,WAAW,oGACT,SAAS,cACL,mFACA,8CACN;AAAA,YAEC;AAAA;AAAA,QACH,KAXO,IAYT;AAAA,MAEJ;AAAA,MACA,gBAAAA,MAAC,QACC,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,eAAe,OAAK,KAAK,IAAI,YAAY,IAAI,CAAC,CAAC;AAAA,UAC9D,UAAU,eAAe;AAAA,UACzB,cAAW;AAAA,UACX,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,UAAK,WAAU,mBAAkB,kBAAI;AAAA,YACtC,gBAAAA,MAACY,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA;AAAA,MACpC,GACF;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,MAAI,WAAW,MAAM,WAAW,EAAG,QAAO,gBAAAZ,MAAC,SAAI,WAAU,uCAAsC;AAE/F,SACE,gBAAAC,OAAC,SAEC;AAAA,oBAAAA,OAAC,SAAI,WAAU,qDACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,YACb;AAAA,wBAAAD,MAAC,QAAG,WAAU,qBAAoB,mBAAK;AAAA,QACvC,gBAAAC,OAAC,OAAE,WAAU,sCAAsC;AAAA;AAAA,UAAW;AAAA,UAAY,eAAe,IAAI,MAAM;AAAA,WAAG;AAAA,SACxG;AAAA,MACA,gBAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,wBAAAD,MAAC,sBAAmB,UAAS,OAAM;AAAA,QACnC,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,SAAS,SAAS;AAAA,YACjC,WAAU;AAAA,YACX;AAAA;AAAA,QAED;AAAA,SACF;AAAA,OACF;AAAA,IAEC,MAAM,WAAW,IAChB,gBAAAA,MAAC,SAAI,WAAU,0CAAyC,2BAAa,IAErE,gBAAAC,OAAAF,YAAA,EAEE;AAAA,sBAAAC,MAAC,SAAI,WAAU,qCACb,0BAAAC,OAAC,WAAM,WAAU,iCACf;AAAA,wBAAAD,MAAC,WAAM,WAAU,mBACf,0BAAAC,OAAC,QAAG,WAAU,YACZ;AAAA,0BAAAD,MAAC,QAAG,WAAU,sEAAqE,mBAAK;AAAA,UACxF,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,kBAAI;AAAA,UACvF,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,oBAAM;AAAA,UACzF,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,uBAAS;AAAA,UAC5F,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,qBAAO;AAAA,UAC1F,gBAAAA,MAAC,QAAG,WAAU,uEAAsE,qBAAO;AAAA,WAC7F,GACF;AAAA,QACA,gBAAAA,MAAC,WAAM,WAAU,8BACd,gBAAM,IAAI,UACT,gBAAAC,OAAC,QAAiB,WAAU,YAC1B;AAAA,0BAAAD,MAAC,QAAG,WAAU,oBACZ,0BAAAA,MAAC,UAAK,WAAU,gCAAgC,eAAK,SAAS,YAAW,GAC3E;AAAA,UACA,gBAAAA,MAAC,QAAG,WAAU,oBACZ,0BAAAA,MAAC,UAAK,WAAU,gEAAgE,eAAK,MAAK,GAC5F;AAAA,UACA,gBAAAA,MAAC,QAAG,WAAU,oBACZ,0BAAAA,MAAC,UAAK,WAAW,2EAA2E,sBAAsB,KAAK,MAAM,CAAC,IAC3H,eAAK,QACR,GACF;AAAA,UACA,gBAAAA,MAAC,QAAG,WAAU,0CAA0C,eAAK,QAAQ,aAAa,GAAE;AAAA,UACpF,gBAAAA,MAAC,QAAG,WAAU,0CAA0C,cAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB,GAAE;AAAA,UACtG,gBAAAA,MAAC,QAAG,WAAU,+BACZ,0BAAAC,OAAC,SAAI,WAAU,yBACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,YAAY,aAAa,KAAK,KAAK,OAAO,KAAK,EAAE;AAAA,gBAChE,WAAU;AAAA,gBAEV,0BAAAA,MAACI,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA,YACpC;AAAA,YACC,aAAa,KAAK,MACjB,gBAAAH,OAAC,SAAI,WAAU,8GACb;AAAA,8BAAAD;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,MAAM;AAAE,6BAAS,WAAW,KAAK,IAAI,EAAE;AAAG,gCAAY,IAAI;AAAA,kBAAE;AAAA,kBACrE,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA,cACC,KAAK,WAAW,eACf,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,MAAM,MAAM,KAAK,IAAI;AAAA,kBACrB,QAAO;AAAA,kBACP,KAAI;AAAA,kBACJ,WAAU;AAAA,kBACV,SAAS,MAAM,YAAY,IAAI;AAAA,kBAChC;AAAA;AAAA,cAED;AAAA,cAEF,gBAAAA,MAAC,SAAI,WAAU,4BAA2B;AAAA,cAC1C,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS,MAAM,aAAa,IAAI;AAAA,kBAChC,WAAU;AAAA,kBACX;AAAA;AAAA,cAED;AAAA,eACF;AAAA,aAEJ,GACF;AAAA,aAnDO,KAAK,EAoDd,CACD,GACH;AAAA,SACF,GACF;AAAA,MAGA,gBAAAA,MAAC,SAAI,WAAU,sDACZ,gBAAM,IAAI,UACT,gBAAAC,OAAC,SAAkB,WAAU,qDAC3B;AAAA,wBAAAA,OAAC,SAAI,WAAU,8BACb;AAAA,0BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,4BAAAD,MAAC,UAAK,WAAU,wBAAwB,eAAK,SAAS,YAAW;AAAA,YACjE,gBAAAA,MAAC,UAAK,WAAW,oFAAoF,sBAAsB,KAAK,MAAM,CAAC,IACpI,eAAK,QACR;AAAA,aACF;AAAA,UACA,gBAAAC,OAAC,OAAE,WAAU,0CACV;AAAA,iBAAK;AAAA,YAAK;AAAA,YAAI,KAAK,QAAQ,aAAa;AAAA,YAAE;AAAA,YAAQ,IAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB;AAAA,aACjG;AAAA,WACF;AAAA,QACA,gBAAAA,OAAC,SAAI,WAAU,YACb;AAAA,0BAAAD;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,YAAY,aAAa,KAAK,KAAK,OAAO,KAAK,EAAE;AAAA,cAChE,WAAU;AAAA,cAEV,0BAAAA,MAACI,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA,UACpC;AAAA,UACC,aAAa,KAAK,MACjB,gBAAAH,OAAC,SAAI,WAAU,8GACb;AAAA,4BAAAD;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM;AAAE,2BAAS,WAAW,KAAK,IAAI,EAAE;AAAG,8BAAY,IAAI;AAAA,gBAAE;AAAA,gBACrE,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,YACC,KAAK,WAAW,eACf,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,MAAM,MAAM,KAAK,IAAI;AAAA,gBACrB,QAAO;AAAA,gBACP,KAAI;AAAA,gBACJ,WAAU;AAAA,gBACV,SAAS,MAAM,YAAY,IAAI;AAAA,gBAChC;AAAA;AAAA,YAED;AAAA,YAEF,gBAAAA,MAAC,SAAI,WAAU,4BAA2B;AAAA,YAC1C,gBAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,aAAa,IAAI;AAAA,gBAChC,WAAU;AAAA,gBACX;AAAA;AAAA,YAED;AAAA,aACF;AAAA,WAEJ;AAAA,WA/CQ,KAAK,EAgDf,CACD,GACH;AAAA,OACF;AAAA,IAIF,gBAAAA,MAAC,sBAAmB,UAAS,UAAS;AAAA,KACxC;AAEJ;AAEA,IAAM,qBAAqB;AAE3B,SAAS,2BAA2B;AAClC,QAAM,EAAE,aAAa,SAAS,IAAI,oBAAoB;AACtD,QAAM,CAAC,WAAW,YAAY,IAAIE,WAAqB,CAAC,CAAC;AACzD,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,CAAC;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,CAAC;AAChD,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,CAAC;AAE9C,EAAAC,YAAU,MAAM;AACd,eAAW,IAAI;AACf,UAAM,GAAG,WAAW,mBAAmB,WAAW,UAAU,kBAAkB,EAAE,EAC7E,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC,EAClD,KAAK,SAAO;AACX,mBAAa,IAAI,QAAQ,CAAC,CAAC;AAC3B,oBAAc,IAAI,SAAS,IAAI,MAAM,UAAU,CAAC;AAChD,oBAAc,IAAI,cAAc,KAAK,MAAM,IAAI,SAAS,IAAI,MAAM,UAAU,KAAK,kBAAkB,CAAC;AACpG,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,EAClC,GAAG,CAAC,aAAa,WAAW,CAAC;AAE7B,WAAS,sBAAsB,WAAoB;AACjD,QAAI,UAAW,QAAO;AACtB,WAAO;AAAA,EACT;AAGA,QAAM,qBAAqB,CAAC,EAAE,SAAS,MAAsC;AAC3E,QAAI,cAAc,EAAG,QAAO;AAE5B,UAAM,iBAAiB,MAAM;AAC3B,YAAM,QAAwD,CAAC;AAC/D,UAAI,cAAc,GAAG;AACnB,iBAAS,IAAI,GAAG,KAAK,YAAY,IAAK,OAAM,KAAK,CAAC;AAAA,MACpD,OAAO;AACL,cAAM,KAAK,CAAC;AACZ,YAAI,cAAc,EAAG,OAAM,KAAK,gBAAgB;AAChD,cAAM,QAAQ,KAAK,IAAI,GAAG,cAAc,CAAC;AACzC,cAAM,MAAM,KAAK,IAAI,aAAa,GAAG,cAAc,CAAC;AACpD,iBAAS,IAAI,OAAO,KAAK,KAAK,KAAK;AACjC,cAAI,CAAC,MAAM,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,QACtC;AACA,YAAI,cAAc,aAAa,EAAG,OAAM,KAAK,cAAc;AAC3D,YAAI,CAAC,MAAM,SAAS,UAAU,EAAG,OAAM,KAAK,UAAU;AAAA,MACxD;AACA,aAAO;AAAA,IACT;AAEA,UAAM,eAAe,aAAa,WAAW,SAAS;AAEtD,WACE,gBAAAH,MAAC,SAAI,MAAK,cAAa,cAAW,cAAa,WAAW,mCAAmC,YAAY,IACvG,0BAAAC,OAAC,QAAG,WAAU,oCACZ;AAAA,sBAAAD,MAAC,QACC,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,eAAe,OAAK,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;AAAA,UACrD,UAAU,eAAe;AAAA,UACzB,cAAW;AAAA,UACX,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,eAAY,WAAU,WAAU;AAAA,YACjC,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,sBAAQ;AAAA;AAAA;AAAA,MAC5C,GACF;AAAA,MACC,eAAe,EAAE;AAAA,QAAI,CAAC,SACrB,OAAO,SAAS,WACd,gBAAAA,MAAC,QACC,0BAAAA,MAAC,UAAK,eAAW,MAAC,WAAU,4CAC1B,0BAAAA,MAACW,iBAAA,EAAe,WAAU,WAAU,GACtC,KAHO,IAIT,IAEA,gBAAAX,MAAC,QACC,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,eAAe,IAAI;AAAA,YAClC,gBAAc,SAAS,cAAc,SAAS;AAAA,YAC9C,WAAW,oGACT,SAAS,cACL,mFACA,8CACN;AAAA,YAEC;AAAA;AAAA,QACH,KAXO,IAYT;AAAA,MAEJ;AAAA,MACA,gBAAAA,MAAC,QACC,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,eAAe,OAAK,KAAK,IAAI,YAAY,IAAI,CAAC,CAAC;AAAA,UAC9D,UAAU,eAAe;AAAA,UACzB,cAAW;AAAA,UACX,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,UAAK,WAAU,mBAAkB,kBAAI;AAAA,YACtC,gBAAAA,MAACY,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA;AAAA,MACpC,GACF;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,MAAI,WAAW,UAAU,WAAW,EAAG,QAAO,gBAAAZ,MAAC,SAAI,WAAU,uCAAsC;AAEnG,SACE,gBAAAC,OAAC,SAEC;AAAA,oBAAAA,OAAC,SAAI,WAAU,qDACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,YACb;AAAA,wBAAAD,MAAC,QAAG,WAAU,qBAAoB,uBAAS;AAAA,QAC3C,gBAAAC,OAAC,OAAE,WAAU,sCAAsC;AAAA;AAAA,UAAW;AAAA,UAAgB,eAAe,IAAI,MAAM;AAAA,WAAG;AAAA,SAC5G;AAAA,MACA,gBAAAD,MAAC,sBAAmB,UAAS,OAAM;AAAA,OACrC;AAAA,IAEC,UAAU,WAAW,IACpB,gBAAAA,MAAC,SAAI,WAAU,0CAAyC,+BAAiB,IAEzE,gBAAAC,OAAAF,YAAA,EAEE;AAAA,sBAAAC,MAAC,SAAI,WAAU,qCACb,0BAAAC,OAAC,WAAM,WAAU,iCACf;AAAA,wBAAAD,MAAC,WAAM,WAAU,mBACf,0BAAAC,OAAC,QAAG,WAAU,YACZ;AAAA,0BAAAD,MAAC,QAAG,WAAU,sEAAqE,kBAAI;AAAA,UACvF,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,6BAAe;AAAA,UAClG,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,qBAAO;AAAA,UAC1F,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,oBAAM;AAAA,UACzF,gBAAAA,MAAC,QAAG,WAAU,uEAAsE,qBAAO;AAAA,WAC7F,GACF;AAAA,QACA,gBAAAA,MAAC,WAAM,WAAU,8BACd,oBAAU,IAAI,cAAY;AACzB,gBAAM,YAAY,SAAS,KAAK,aAAa,SAAS;AACtD,iBACE,gBAAAC,OAAC,QAAqB,WAAU,YAC9B;AAAA,4BAAAD,MAAC,QAAG,WAAU,oBACZ,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,SAAS,WAAW,SAAS,KAAK,IAAI,EAAE;AAAA,gBACvD,WAAU;AAAA,gBAET,mBAAS,KAAK,SAAS;AAAA;AAAA,YAC1B,GACF;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,oBACZ,0BAAAC,OAAC,UAAK,WAAU,sDACb;AAAA,uBAAS,SAAS,MAAM,GAAG,EAAE;AAAA,cAAG,SAAS,SAAS,SAAS,KAAK,QAAQ;AAAA,eAC3E,GACF;AAAA,YACA,gBAAAD,MAAC,QAAG,WAAU,0CACX,cAAI,KAAK,SAAS,SAAS,EAAE,eAAe,GAC/C;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,oBACZ,0BAAAA,MAAC,UAAK,WAAW,2EAA2E,sBAAsB,SAAS,CAAC,IACzH,sBAAY,YAAY,QAC3B,GACF;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,+BACZ,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,SAAS,uBAAuB,SAAS,EAAE,EAAE;AAAA,gBAC5D,WAAU;AAAA,gBACX;AAAA;AAAA,YAED,GACF;AAAA,eA7BO,SAAS,EA8BlB;AAAA,QAEJ,CAAC,GACH;AAAA,SACF,GACF;AAAA,MAGA,gBAAAA,MAAC,SAAI,WAAU,sDACZ,oBAAU,IAAI,cAAY;AACzB,cAAM,YAAY,SAAS,KAAK,aAAa,SAAS;AACtD,eACE,gBAAAC,OAAC,SAAsB,WAAU,qDAC/B;AAAA,0BAAAA,OAAC,SAAI,WAAU,8BACb;AAAA,4BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,8BAAAD,MAAC,UAAK,WAAU,wBAAwB,mBAAS,KAAK,SAAS,YAAW;AAAA,cAC1E,gBAAAA,MAAC,UAAK,WAAW,oFAAoF,sBAAsB,SAAS,CAAC,IAClI,sBAAY,YAAY,QAC3B;AAAA,eACF;AAAA,YACA,gBAAAC,OAAC,OAAE,WAAU,0CACV;AAAA,uBAAS,SAAS,MAAM,GAAG,EAAE;AAAA,cAAG,SAAS,SAAS,SAAS,KAAK,QAAQ;AAAA,cAAG;AAAA,cAAI,IAAI,KAAK,SAAS,SAAS,EAAE,mBAAmB;AAAA,eAClI;AAAA,aACF;AAAA,UACA,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,SAAS,uBAAuB,SAAS,EAAE,EAAE;AAAA,cAC5D,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,aAjBQ,SAAS,EAkBnB;AAAA,MAEJ,CAAC,GACH;AAAA,OACF;AAAA,IAIF,gBAAAA,MAAC,sBAAmB,UAAS,UAAS;AAAA,KACxC;AAEJ;AAYA,SAAS,sBAAsB,EAAE,WAAW,GAA2B;AACrE,QAAM,EAAE,aAAa,SAAS,IAAI,oBAAoB;AACtD,QAAM,CAAC,UAAU,WAAW,IAAIE,WAAgC,IAAI;AACpE,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,KAAK;AAEhD,EAAAC,YAAU,MAAM;AACd,UAAM,GAAG,WAAW,cAAc,UAAU,EAAE,EAC3C,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC,EAClD,KAAK,SAAO;AACX,kBAAY,IAAI,IAAI;AACpB,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,EAClC,GAAG,CAAC,aAAa,UAAU,CAAC;AAE5B,iBAAe,gBAAgB;AAC7B,QAAI,CAAC,SAAU;AACf,QAAI,CAAC,QAAQ,oEAAoE,EAAG;AAEpF,iBAAa,IAAI;AACjB,UAAM,MAAM,MAAM,MAAM,GAAG,WAAW,cAAc,UAAU,YAAY,EAAE,QAAQ,OAAO,CAAC;AAC5F,QAAI,IAAI,IAAI;AACV,eAAS,WAAW,SAAS,KAAK,IAAI,EAAE;AAAA,IAC1C;AACA,iBAAa,KAAK;AAAA,EACpB;AAEA,MAAI,SAAS;AACX,WACE,gBAAAF,OAAC,SAAI,WAAU,aACb;AAAA,sBAAAD,MAAC,SAAI,WAAU,2CAA0C;AAAA,MACzD,gBAAAA,MAAC,SAAI,WAAU,uCAAsC;AAAA,OACvD;AAAA,EAEJ;AAEA,MAAI,CAAC,UAAU;AACb,WACE,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,sBAAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,SAAS,qBAAqB;AAAA,UAC7C,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,eAAY,WAAU,WAAU;AAAA,YAAE;AAAA;AAAA;AAAA,MACrC;AAAA,MACA,gBAAAA,MAAC,OAAE,WAAU,yBAAwB,iCAAmB;AAAA,OAC1D;AAAA,EAEJ;AAEA,QAAM,YAAY,SAAS,KAAK,aAAa,SAAS;AAEtD,SACE,gBAAAC,OAAC,SAAI,WAAU,aAEb;AAAA,oBAAAA,OAAC,SAAI,WAAU,qCACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,aACb;AAAA,wBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,SAAS,qBAAqB;AAAA,YAC7C,WAAU;AAAA,YAEV;AAAA,8BAAAD,MAAC,eAAY,WAAU,WAAU;AAAA,cAAE;AAAA;AAAA;AAAA,QACrC;AAAA,QACA,gBAAAA,MAAC,QAAG,WAAU,yBAAwB,6BAAe;AAAA,SACvD;AAAA,MACA,gBAAAA,MAAC,SAAI,WAAU,2BACZ,sBACC,gBAAAA,MAAC,UAAK,WAAU,8GAA6G,qBAE7H,IAEA,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS;AAAA,UACT,UAAU;AAAA,UACV,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,aAAU,WAAU,gBAAe;AAAA,YACnC,YAAY,iBAAiB;AAAA;AAAA;AAAA,MAChC,GAEJ;AAAA,OACF;AAAA,IAGA,gBAAAC,OAAC,SAAI,WAAU,2CACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,qCACb;AAAA,wBAAAD,MAAC,UAAK,WAAU,iCAAgC,kBAAI;AAAA,QACpD,gBAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,SAAS,WAAW,SAAS,KAAK,IAAI,EAAE;AAAA,YACvD,WAAU;AAAA,YAET,mBAAS,KAAK,SAAS;AAAA;AAAA,QAC1B;AAAA,SACF;AAAA,MACA,gBAAAC,OAAC,SAAI,WAAU,qCACb;AAAA,wBAAAD,MAAC,UAAK,WAAU,iCAAgC,+BAAiB;AAAA,QACjE,gBAAAA,MAAC,UAAK,WAAU,WAAW,mBAAS,SAAS,UAAI;AAAA,SACnD;AAAA,MACA,gBAAAC,OAAC,SAAI,WAAU,qCACb;AAAA,wBAAAD,MAAC,UAAK,WAAU,iCAAgC,qBAAO;AAAA,QACvD,gBAAAA,MAAC,UAAK,WAAU,WAAW,cAAI,KAAK,SAAS,SAAS,EAAE,eAAe,GAAE;AAAA,SAC3E;AAAA,OACF;AAAA,IAGA,gBAAAC,OAAC,SAAI,WAAU,aACb;AAAA,sBAAAD,MAAC,QAAG,WAAU,uBAAsB,qBAAO;AAAA,MAC3C,gBAAAA,MAAC,SAAI,WAAU,4DACb,0BAAAA,MAAC,SAAI,WAAU,yCAAyC,mBAAS,UAAS,GAC5E;AAAA,OACF;AAAA,KACF;AAEJ;AAaA,IAAM,oBAAoB;AAE1B,SAAS,0BAA0B;AACjC,QAAM,EAAE,aAAa,UAAU,SAAS,IAAI,oBAAoB;AAChE,QAAM,CAAC,UAAU,WAAW,IAAIE,WAA+B,CAAC,CAAC;AACjE,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,CAAC;AAC9C,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,IAAI;AAC3C,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,CAAC;AAChD,QAAM,CAAC,YAAY,aAAa,IAAIA,WAAS,CAAC;AAE9C,EAAAC,YAAU,MAAM;AACd,eAAW,IAAI;AACf,UAAM,GAAG,WAAW,kBAAkB,WAAW,UAAU,iBAAiB,EAAE,EAC3E,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,QAAQ,OAAO,CAAC,EAClD,KAAK,SAAO;AACX,kBAAY,IAAI,QAAQ,CAAC,CAAC;AAC1B,oBAAc,IAAI,SAAS,IAAI,MAAM,UAAU,CAAC;AAChD,oBAAc,IAAI,cAAc,KAAK,MAAM,IAAI,SAAS,IAAI,MAAM,UAAU,KAAK,iBAAiB,CAAC;AACnG,iBAAW,KAAK;AAAA,IAClB,CAAC,EACA,MAAM,MAAM,WAAW,KAAK,CAAC;AAAA,EAClC,GAAG,CAAC,aAAa,WAAW,CAAC;AAE7B,WAAS,eAAe,SAA6B;AACnD,UAAM,YAAY,QAAQ,cAAc;AACxC,UAAM,aAAa,QAAQ;AAE3B,QAAI,WAAW;AACb,aAAO,EAAE,OAAO,WAAW,SAAS,6CAA6C;AAAA,IACnF;AACA,QAAI,YAAY;AACd,aAAO,EAAE,OAAO,YAAY,SAAS,yCAAyC;AAAA,IAChF;AACA,WAAO,EAAE,OAAO,UAAU,SAAS,qCAAqC;AAAA,EAC1E;AAGA,QAAM,qBAAqB,CAAC,EAAE,SAAS,MAAsC;AAC3E,QAAI,cAAc,EAAG,QAAO;AAE5B,UAAM,iBAAiB,MAAM;AAC3B,YAAM,QAAwD,CAAC;AAC/D,UAAI,cAAc,GAAG;AACnB,iBAAS,IAAI,GAAG,KAAK,YAAY,IAAK,OAAM,KAAK,CAAC;AAAA,MACpD,OAAO;AACL,cAAM,KAAK,CAAC;AACZ,YAAI,cAAc,EAAG,OAAM,KAAK,gBAAgB;AAChD,cAAM,QAAQ,KAAK,IAAI,GAAG,cAAc,CAAC;AACzC,cAAM,MAAM,KAAK,IAAI,aAAa,GAAG,cAAc,CAAC;AACpD,iBAAS,IAAI,OAAO,KAAK,KAAK,KAAK;AACjC,cAAI,CAAC,MAAM,SAAS,CAAC,EAAG,OAAM,KAAK,CAAC;AAAA,QACtC;AACA,YAAI,cAAc,aAAa,EAAG,OAAM,KAAK,cAAc;AAC3D,YAAI,CAAC,MAAM,SAAS,UAAU,EAAG,OAAM,KAAK,UAAU;AAAA,MACxD;AACA,aAAO;AAAA,IACT;AAEA,UAAM,eAAe,aAAa,WAAW,SAAS;AAEtD,WACE,gBAAAH,MAAC,SAAI,MAAK,cAAa,cAAW,cAAa,WAAW,mCAAmC,YAAY,IACvG,0BAAAC,OAAC,QAAG,WAAU,oCACZ;AAAA,sBAAAD,MAAC,QACC,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,eAAe,OAAK,KAAK,IAAI,GAAG,IAAI,CAAC,CAAC;AAAA,UACrD,UAAU,eAAe;AAAA,UACzB,cAAW;AAAA,UACX,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,eAAY,WAAU,WAAU;AAAA,YACjC,gBAAAA,MAAC,UAAK,WAAU,mBAAkB,sBAAQ;AAAA;AAAA;AAAA,MAC5C,GACF;AAAA,MACC,eAAe,EAAE;AAAA,QAAI,CAAC,SACrB,OAAO,SAAS,WACd,gBAAAA,MAAC,QACC,0BAAAA,MAAC,UAAK,eAAW,MAAC,WAAU,4CAC1B,0BAAAA,MAACW,iBAAA,EAAe,WAAU,WAAU,GACtC,KAHO,IAIT,IAEA,gBAAAX,MAAC,QACC,0BAAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,MAAM,eAAe,IAAI;AAAA,YAClC,gBAAc,SAAS,cAAc,SAAS;AAAA,YAC9C,WAAW,oGACT,SAAS,cACL,mFACA,8CACN;AAAA,YAEC;AAAA;AAAA,QACH,KAXO,IAYT;AAAA,MAEJ;AAAA,MACA,gBAAAA,MAAC,QACC,0BAAAC;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,eAAe,OAAK,KAAK,IAAI,YAAY,IAAI,CAAC,CAAC;AAAA,UAC9D,UAAU,eAAe;AAAA,UACzB,cAAW;AAAA,UACX,WAAU;AAAA,UAEV;AAAA,4BAAAD,MAAC,UAAK,WAAU,mBAAkB,kBAAI;AAAA,YACtC,gBAAAA,MAACY,eAAA,EAAa,WAAU,WAAU;AAAA;AAAA;AAAA,MACpC,GACF;AAAA,OACF,GACF;AAAA,EAEJ;AAEA,MAAI,WAAW,SAAS,WAAW,EAAG,QAAO,gBAAAZ,MAAC,SAAI,WAAU,uCAAsC;AAElG,SACE,gBAAAC,OAAC,SAEC;AAAA,oBAAAA,OAAC,SAAI,WAAU,qDACb;AAAA,sBAAAA,OAAC,SAAI,WAAU,YACb;AAAA,wBAAAD,MAAC,QAAG,WAAU,qBAAoB,sBAAQ;AAAA,QAC1C,gBAAAC,OAAC,OAAE,WAAU,sCAAsC;AAAA;AAAA,UAAW;AAAA,UAAe,eAAe,IAAI,MAAM;AAAA,WAAG;AAAA,SAC3G;AAAA,MACA,gBAAAD,MAAC,sBAAmB,UAAS,OAAM;AAAA,OACrC;AAAA,IAEC,SAAS,WAAW,IACnB,gBAAAA,MAAC,SAAI,WAAU,0CAAyC,8BAAgB,IAExE,gBAAAC,OAAAF,YAAA,EAEE;AAAA,sBAAAC,MAAC,SAAI,WAAU,qCACb,0BAAAC,OAAC,WAAM,WAAU,iCACf;AAAA,wBAAAD,MAAC,WAAM,WAAU,mBACf,0BAAAC,OAAC,QAAG,WAAU,YACZ;AAAA,0BAAAD,MAAC,QAAG,WAAU,oFAAmF,kBAAI;AAAA,UACrG,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,oBAAM;AAAA,UACzF,gBAAAA,MAAC,QAAG,WAAU,oFAAmF,qBAAO;AAAA,UACxG,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,qBAAO;AAAA,UAC1F,gBAAAA,MAAC,QAAG,WAAU,sEAAqE,oBAAM;AAAA,UACzF,gBAAAA,MAAC,QAAG,WAAU,uEAAsE,qBAAO;AAAA,WAC7F,GACF;AAAA,QACA,gBAAAA,MAAC,WAAM,WAAU,8BACd,mBAAS,IAAI,aAAW;AACvB,gBAAM,SAAS,eAAe,OAAO;AACrC,gBAAM,UAAU,QAAQ,aAAa;AACrC,gBAAM,kBAAkB,QAAQ,YAAY,QAAQ;AAEpD,iBACE,gBAAAC,OAAC,QAAoB,WAAU,YAC7B;AAAA,4BAAAD,MAAC,QAAG,WAAU,oBACZ,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,SAAS,WAAW,QAAQ,KAAK,IAAI,EAAE;AAAA,gBACtD,WAAU;AAAA,gBAET,kBAAQ,KAAK,SAAS;AAAA;AAAA,YACzB,GACF;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,0CACX,kBAAQ,KAAK,QAAQ,QAAQ,KAAK,OACrC;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,oBACZ,0BAAAC,OAAC,UAAK,WAAU,sDACb;AAAA,yBAAW,gBAAAD,MAAC,UAAK,WAAU,gBAAe,oBAAC;AAAA,cAC3C,QAAQ,QAAQ,MAAM,GAAG,EAAE;AAAA,cAAG,QAAQ,QAAQ,SAAS,KAAK,QAAQ;AAAA,eACvE,GACF;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,0CACX,cAAI,KAAK,QAAQ,SAAS,EAAE,eAAe,GAC9C;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,oBACZ,0BAAAA,MAAC,UAAK,WAAW,2EAA2E,OAAO,OAAO,IACvG,iBAAO,OACV,GACF;AAAA,YACA,gBAAAA,MAAC,QAAG,WAAU,+BACZ,0BAAAA;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,MAAM,SAAS,WAAW,QAAQ,KAAK,IAAI,YAAY,eAAe,EAAE;AAAA,gBACjF,WAAU;AAAA,gBACX;AAAA;AAAA,YAED,GACF;AAAA,eAjCO,QAAQ,EAkCjB;AAAA,QAEJ,CAAC,GACH;AAAA,SACF,GACF;AAAA,MAGA,gBAAAA,MAAC,SAAI,WAAU,sDACZ,mBAAS,IAAI,aAAW;AACvB,cAAM,SAAS,eAAe,OAAO;AACrC,cAAM,UAAU,QAAQ,aAAa;AACrC,cAAM,kBAAkB,QAAQ,YAAY,QAAQ;AAEpD,eACE,gBAAAC,OAAC,SAAqB,WAAU,qDAC9B;AAAA,0BAAAA,OAAC,SAAI,WAAU,8BACb;AAAA,4BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,8BAAAA,OAAC,UAAK,WAAU,wBACb;AAAA,2BAAW,gBAAAD,MAAC,UAAK,WAAU,gBAAe,oBAAC;AAAA,gBAC3C,QAAQ,QAAQ,MAAM,GAAG,EAAE;AAAA,gBAAG,QAAQ,QAAQ,SAAS,KAAK,QAAQ;AAAA,iBACvE;AAAA,cACA,gBAAAA,MAAC,UAAK,WAAW,oFAAoF,OAAO,OAAO,IAChH,iBAAO,OACV;AAAA,eACF;AAAA,YACA,gBAAAC,OAAC,OAAE,WAAU,0CACV;AAAA,sBAAQ,KAAK,QAAQ,QAAQ,KAAK;AAAA,cAAM;AAAA,cAAI,IAAI,KAAK,QAAQ,SAAS,EAAE,mBAAmB;AAAA,eAC9F;AAAA,aACF;AAAA,UACA,gBAAAD;AAAA,YAAC;AAAA;AAAA,cACC,SAAS,MAAM,SAAS,WAAW,QAAQ,KAAK,IAAI,YAAY,eAAe,EAAE;AAAA,cACjF,WAAU;AAAA,cACX;AAAA;AAAA,UAED;AAAA,aApBQ,QAAQ,EAqBlB;AAAA,MAEJ,CAAC,GACH;AAAA,OACF;AAAA,IAIF,gBAAAA,MAAC,sBAAmB,UAAS,UAAS;AAAA,KACxC;AAEJ;;;AC3oEA,SAAS,eAAAa,cAAa,MAAM,WAAW;AAiCnC,SAkDY,YAAAC,YA7CV,OAAAC,OALF,QAAAC,cAAA;AAnBG,SAAS,OAAO;AAAA,EACrB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgB;AACd,QAAM,EAAE,SAAS,aAAa,UAAU,QAAQ,SAAS,IAAI,oBAAoB;AAGjF,QAAM,SAAS,gBAAgB,OAAO,gBAAgB;AACtD,QAAM,aAAa,YAAY,WAAW,WAAW;AAErD,QAAM,aAAa,CAAC,MAA2C;AAE7D,MAAE,cAAc,KAAK;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,gBACJ,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,WAAU;AAAA,MAET;AAAA,iBAAS,MAAM,OAAO,OAAO,CAAC,EAAE,YAAY,KAAK;AAAA,QAClD,gBAAAD,MAAC,UAAK,WAAU,8FAA6F;AAAA;AAAA;AAAA,EAC/G;AAGF,SACE,gBAAAA,MAAC,YAAO,WAAU,0DAChB,0BAAAC,OAAC,SAAI,WAAU,iEAEZ;AAAA,aACC,gBAAAA,OAAC,OAAE,MAAM,UAAU,WAAU,yCAAwC;AAAA;AAAA,MAEnE,gBAAAD,MAAC,UAAK,WAAU,oEAAmE,gBAAE;AAAA,OACvF,IAEA,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QAEV;AAAA,0BAAAD,MAACE,cAAA,EAAY,WAAU,WAAU;AAAA,UACjC,gBAAAF,MAAC,UAAK,WAAU,oBAAmB,kBAAI;AAAA;AAAA;AAAA,IACzC;AAAA,IAIF,gBAAAC,OAAC,SAAI,WAAU,2BAEZ;AAAA;AAAA,MAGA,iBACC,gBAAAD;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,SAAS;AAAA,UACT,WAAU;AAAA,UACV,cAAW;AAAA,UAEV,oBAAU,SAAS,gBAAAA,MAAC,OAAI,WAAU,WAAU,IAAK,gBAAAA,MAAC,QAAK,WAAU,WAAU;AAAA;AAAA,MAC9E;AAAA,MAID,WACC,gBAAAC,OAAC,YAAS,SAAS,eAAe,OAAM,SAAQ,WAAU,iBACvD;AAAA,gBAAQ,MAAM,SAAS,WACtB,gBAAAA,OAAAF,YAAA,EAEG;AAAA,WAAC,aACA,gBAAAC,MAAC,gBAAa,SAAS,MAAM,SAAS,WAAW,GAAG,4BAEpD,IAEA,gBAAAA,MAAC,gBAAa,SAAS,MAAM,SAAS,GAAG,GAAG,4BAE5C;AAAA,UAEF,gBAAAA,MAAC,mBAAgB;AAAA,WACnB;AAAA,QAIF,gBAAAA,MAAC,gBAAa,SAAS,MAAM;AAAE,iBAAO,SAAS,OAAO;AAAA,QAAI,GAAG,0BAE7D;AAAA,QAGC,aACC,gBAAAA,MAAC,gBAAa,SAAS,WAAW,oBAElC;AAAA,SAEJ;AAAA,OAEJ;AAAA,KACF,GACF;AAEJ;;;ACrHA,SAAS,aAAAG,aAAW,UAAAC,eAAc;AAW3B,SAAS,YAAY,WAA+B,UAAmB,MAAM;AAElF,QAAM,eAAeA,QAAO,SAAS;AACrC,eAAa,UAAU;AAEvB,EAAAD,YAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,aAAS,cAAc,OAAsB;AAE3C,YAAM,SAAS,MAAM;AACrB,YAAM,WACJ,OAAO,YAAY,WACnB,OAAO,YAAY,cACnB,OAAO;AAET,UAAI,UAAU;AAEZ,cAAM,UAAU,MAAM,WAAW,MAAM;AACvC,cAAM,WAAW,MAAM,QAAQ;AAC/B,cAAM,gBAAgB,YAAY,MAAM,QAAQ,OAAO,MAAM,QAAQ;AACrE,YAAI,CAAC,YAAY,CAAC,eAAe;AAC/B;AAAA,QACF;AAAA,MACF;AAEA,iBAAW,YAAY,aAAa,SAAS;AAC3C,cAAM,YAAY,SAAS,UAAU,MAAM,WAAW,MAAM,UAAU;AACtE,cAAM,YAAY,SAAS,UAAU,MAAM,UAAU;AACrD,cAAM,aAAa,SAAS,WAAW,MAAM,WAAW,CAAC,MAAM;AAC/D,cAAM,WAAW,MAAM,IAAI,YAAY,MAAM,SAAS,IAAI,YAAY;AAEtE,YAAI,YAAY,aAAa,aAAa,YAAY;AACpD,gBAAM,eAAe;AACrB,mBAAS,OAAO;AAChB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO,iBAAiB,WAAW,aAAa;AAChD,WAAO,MAAM,OAAO,oBAAoB,WAAW,aAAa;AAAA,EAClE,GAAG,CAAC,OAAO,CAAC;AACd;AAGO,SAAS,qBAAqB,SAMlC;AACD,QAAM,YAAgC;AAAA,IACpC;AAAA,MACE,KAAK;AAAA,MACL,SAAS;AAAA,MACT,QAAQ,MAAM,QAAQ,eAAe;AAAA,MACrC,aAAa;AAAA,IACf;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,SAAS;AAAA,MACT,QAAQ,MAAM,QAAQ,mBAAmB;AAAA,MACzC,aAAa;AAAA,IACf;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,QAAQ,MAAM,QAAQ,YAAY;AAAA,MAClC,aAAa;AAAA,IACf;AAAA,IACA;AAAA,MACE,KAAK;AAAA,MACL,QAAQ,MAAM,QAAQ,WAAW;AAAA,MACjC,aAAa;AAAA,IACf;AAAA,EACF;AAEA,cAAY,WAAW,IAAI;AAC7B;;;AhN9CM,SA2DF,YAAAE,YA3DE,OAAAC,OA2DF,QAAAC,cA3DE;AAhBC,SAAS,qBAAqB;AAAA,EACnC,WAAW;AAAA,EACX,cAAc;AAAA,EACd;AAAA,EACA,SAAS,CAAC;AAAA,EACV,UAAU;AAAA,EACV;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,SACE,gBAAAD,MAAC,qBAAkB,UAAoB,aAA0B,QAAgB,QAAgB,SAAkB,qBAA0C,uBAC3J,0BAAAA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACF,GACF;AAEJ;AAUA,SAAS,gBAAgB;AAAA,EACvB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,EAAE,UAAU,aAAa,UAAU,oBAAoB,IAAI,oBAAoB;AACrF,QAAM,CAAC,aAAa,cAAc,IAAIE,WAA6B,IAAI;AAGvE,QAAM,aAAa,YAAY,WAAW,UAAU,IAChD,YAAY,QAAQ,YAAY,EAAE,IAClC,gBAAgB,YAAY,SAAY;AAE5C,QAAM,eAAe,YAAY,WAAW,SAAS;AAGrD,QAAM,0BAA0B,CAAC,UAA8B;AAC7D,mBAAe,KAAK;AACpB,0BAAsB,KAAK;AAAA,EAC7B;AAEA,uBAAqB;AAAA,IACnB;AAAA,IACA,cAAc,eAAe,MAAM,aAAa,aAAa,UAAU,IAAI;AAAA,IAC3E,kBAAkB,MAAM;AACtB,UAAI,YAAY,WAAW,WAAW,EAAG,UAAS,GAAG;AAAA,UAChD,UAAS,WAAW;AAAA,IAC3B;AAAA,IACA,WAAW,MAAM;AACf,UAAI,gBAAgB,OAAO,gBAAgB,GAAI,UAAS,SAAS;AAAA,IACnE;AAAA,IACA,UAAU,MAAM;AACd,UAAI,gBAAgB,OAAO,gBAAgB,GAAI,UAAS,GAAG;AAAA,IAC7D;AAAA,EACF,CAAC;AAGD,QAAM,oBACJ,gBAAAD,OAAAF,YAAA,EAEG;AAAA,oBAAgB,eACf,gBAAAC;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,MAAM,YAAY,OAAO,OAAO;AAAA,QACzC,UAAU,CAAC,YAAY,qBAAqB,CAAC,CAAC,YAAY;AAAA,QAC1D,WAAU;AAAA,QACV,cAAW;AAAA,QACX,OAAO,YAAY,oBAAoB,2BAAsB;AAAA,QAE5D,sBAAY,WACX,gBAAAA,MAACG,UAAA,EAAQ,WAAU,wBAAuB,IAE1C,gBAAAH,MAAC,QAAK,WAAU,WAAU;AAAA;AAAA,IAE9B;AAAA,IAGD;AAAA,KACH;AAGF,SACE,gBAAAC,OAAC,SAAI,WAAU,4CACb;AAAA,oBAAAD;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA,WAAW;AAAA;AAAA,IACb;AAAA,IACA,gBAAAA,MAAC,UAAK,WAAU,UACd,0BAAAA,MAAC,mBAAgB,MAAM,aAAa,qBAAqB,yBAAyB,GACpF;AAAA,KACF;AAEJ;AAEA,SAAS,gBAAgB,EAAE,MAAM,oBAAoB,GAAgF;AAEnI,QAAM,mBAAmB,KAAK,MAAM,GAAG,EAAE,CAAC;AAE1C,MAAI,qBAAqB,OAAO,qBAAqB,GAAI,QAAO,gBAAAA,MAAC,mBAAgB;AACjF,MAAI,iBAAiB,WAAW,SAAS,GAAG;AAC1C,UAAM,OAAO,iBAAiB,QAAQ,YAAY,EAAE,EAAE,QAAQ,WAAW,EAAE;AAE3E,WAAO,gBAAAA,MAAC,cAAsB,MAAM,QAAQ,QAAW,uBAA/B,IAAyE;AAAA,EACnG;AACA,MAAI,iBAAiB,WAAW,WAAW,EAAG,QAAO,gBAAAA,MAAC,gBAAa,SAAS,iBAAiB,QAAQ,aAAa,EAAE,GAAG;AACvH,SAAO,gBAAAA,MAAC,SAAI,WAAU,+BAA8B,0BAAAC,OAAC,OAAE,WAAU,yBAAwB;AAAA;AAAA,IAAiB;AAAA,KAAK,GAAI;AACrH;;;AiNvJA,SAAS,iBAAAG,gBAAe,cAAAC,aAAY,YAAAC,YAAU,eAAAC,eAAa,UAAAC,SAAQ,aAAAC,aAAW,WAAAC,gBAA+B;AA4ZzG,gBAAAC,aAAA;AA7VJ,IAAM,cAAcP,eAAuC,IAAI;AAG/D,SAAS,gBAAgB,SAA+D;AACtF,QAAM,YAAY;AAClB,QAAM,QAAqB,CAAC;AAC5B,MAAI,eAAe;AAEnB,MAAI;AACJ,UAAQ,QAAQ,UAAU,KAAK,OAAO,OAAO,MAAM;AACjD,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,MAAM,CAAC,CAAC;AAChC,YAAM,KAAK,IAAI;AACf,qBAAe,aAAa,QAAQ,MAAM,CAAC,GAAG,EAAE;AAAA,IAClD,QAAQ;AACN,cAAQ,KAAK,+BAA+B,MAAM,CAAC,CAAC;AAAA,IACtD;AAAA,EACF;AAEA,iBAAe,aAAa,QAAQ,WAAW,MAAM,EAAE,KAAK;AAE5D,SAAO,EAAE,OAAO,aAAa;AAC/B;AAKA,SAAS,gBAAgB,SAAyB;AAChD,MAAI,UAAU;AAGd,QAAM,YAAY,QAAQ,MAAM,2BAA2B;AAC3D,MAAI,WAAW;AACb,cAAU,UAAU,CAAC;AAAA,EACvB,OAAO;AACL,UAAM,eAAe,QAAQ,MAAM,kBAAkB;AACrD,QAAI,cAAc;AAChB,gBAAU,aAAa,CAAC;AAAA,IAC1B;AAAA,EACF;AAGA,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,MAAI,kBAAkB;AACtB,WAAS,IAAI,MAAM,SAAS,GAAG,KAAK,GAAG,KAAK;AAC1C,QAAI,MAAM,CAAC,EAAE,KAAK,EAAE,WAAW,IAAI,GAAG;AACpC,wBAAkB;AAClB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,oBAAoB,IAAI;AAC1B,WAAO,QAAQ,KAAK;AAAA,EACtB;AAEA,SAAO,MAAM,MAAM,GAAG,kBAAkB,CAAC,EAAE,KAAK,IAAI,EAAE,KAAK;AAC7D;AASO,SAAS,aAAa;AAAA,EAC3B;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA,iBAAiB;AACnB,GAAsB;AACpB,QAAM,CAAC,UAAU,WAAW,IAAIE,WAAoB,CAAC,CAAC;AACtD,QAAM,CAAC,cAAc,eAAe,IAAIA,WAA8B,IAAI;AAC1E,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,KAAK;AACpD,QAAM,CAAC,QAAQ,SAAS,IAAIA,WAAS,KAAK;AAC1C,QAAM,CAAC,MAAM,OAAO,IAAIA,WAAmB,KAAK;AAChD,QAAM,CAAC,kBAAkB,mBAAmB,IAAIA,WAAS,KAAK;AAC9D,QAAM,CAAC,iBAAiB,kBAAkB,IAAIA,WAAS,KAAK;AAC5D,QAAM,CAAC,eAAe,gBAAgB,IAAIA,WAAS,eAAe;AAElE,QAAM,iBAAiBE,QAA2B,IAAI;AACtD,QAAM,uBAAuBA,QAAiC,IAAI;AAClE,QAAM,mBAAmBA,QAAO,KAAK;AACrC,QAAM,qBAAqBA,QAA+B,IAAI;AAG9D,QAAM,sBAAsB,eAAe;AAE3C,QAAM,sBAAsBD,cAAY,CAAC,YAAgC;AACvE,mBAAe,UAAU;AAAA,EAC3B,GAAG,CAAC,CAAC;AAEL,QAAM,4BAA4BA,cAAY,CAAC,YAAsC;AACnF,yBAAqB,UAAU;AAAA,EACjC,GAAG,CAAC,CAAC;AAEL,QAAM,gBAAgBA,cAAY,MAAM;AACtC,QAAI,mBAAmB,SAAS;AAC9B,yBAAmB,QAAQ,MAAM;AACjC,yBAAmB,UAAU;AAAA,IAC/B;AACA,mBAAe,KAAK;AAAA,EACtB,GAAG,CAAC,CAAC;AAGL,EAAAE,YAAU,MAAM;AACd,QAAI,iBAAiB,QAAS;AAC9B,qBAAiB,UAAU;AAE3B,UAAM,cAAc,EACjB,KAAK,SAAO,IAAI,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC,EACpC,KAAK,CAAC,SAA4D;AACjE,UAAI,KAAK,SAAS,GAAG;AACnB,oBAAY,KAAK,IAAI,QAAM,EAAE,MAAM,EAAE,MAAM,SAAS,EAAE,QAAQ,EAAE,CAAC;AAAA,MACnE;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,cAAcF,cAAY,CAAC,MAA4B,YAAoB;AAC/E,UAAM,gBAAgB;AAAA,MACpB,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,MAC9C,MAAM,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAAA,IACxC,CAAC,EAAE,MAAM,MAAM;AAAA,IAAC,CAAC;AAAA,EACnB,GAAG,CAAC,cAAc,CAAC;AAEnB,QAAM,cAAcA,cAAY,OAAO,YAAoB;AACzD,QAAI,CAAC,QAAQ,KAAK,KAAK,YAAa;AAEpC,uBAAmB,UAAU,IAAI,gBAAgB;AACjD,UAAM,SAAS,mBAAmB,QAAQ;AAE1C,UAAM,cAAuB,EAAE,MAAM,QAAQ,SAAS,QAAQ,KAAK,EAAE;AACrE,UAAM,cAAc,CAAC,GAAG,UAAU,WAAW;AAC7C,gBAAY,WAAW;AACvB,mBAAe,IAAI;AAEnB,gBAAY,QAAQ,QAAQ,KAAK,CAAC;AAElC,UAAM,mBAA4B,EAAE,MAAM,aAAa,SAAS,IAAI,KAAK;AACzE,gBAAY,CAAC,GAAG,aAAa,gBAAgB,CAAC;AAE9C,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,qBAAqB;AAAA,QAChD,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU;AAAA,UACnB,UAAU;AAAA,UACV;AAAA,UACA;AAAA,UACA,SAAS;AAAA,UACT,cAAc;AAAA,UACd,aAAa;AAAA,QACf,CAAC;AAAA,QACD;AAAA,MACF,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,YAAI,SAAS,WAAW,KAAK;AAC3B,gBAAM,IAAI,MAAM,uDAAuD;AAAA,QACzE;AACA,YAAI,SAAS,WAAW,KAAK;AAC3B,gBAAM,IAAI,MAAM,uDAAuD;AAAA,QACzE;AACA,cAAM,YAAY,MAAM,SAAS,KAAK;AACtC,YAAI;AACF,gBAAM,QAAQ,KAAK,MAAM,SAAS;AAClC,gBAAM,IAAI,MAAM,MAAM,SAAS,wBAAwB;AAAA,QACzD,QAAQ;AACN,gBAAM,IAAI,MAAM,iBAAiB,SAAS,MAAM,sBAAsB;AAAA,QACxE;AAAA,MACF;AAEA,YAAM,SAAS,SAAS,MAAM,UAAU;AACxC,UAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,kBAAkB;AAE/C,YAAM,UAAU,IAAI,YAAY;AAChC,UAAI,mBAAmB;AACvB,UAAI,eAAe;AAEnB,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,OAAAK,OAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI,KAAM;AAEV,cAAM,QAAQ,QAAQ,OAAOA,QAAO,EAAE,QAAQ,KAAK,CAAC;AACpD,4BAAoB;AAEpB,oBAAY,UAAQ;AAClB,gBAAM,UAAU,CAAC,GAAG,IAAI;AACxB,kBAAQ,QAAQ,SAAS,CAAC,IAAI,EAAE,MAAM,aAAa,SAAS,kBAAkB,KAAK;AACnF,iBAAO;AAAA,QACT,CAAC;AAAA,MACH;AAGA,UAAI,SAAS,WAAW,eAAe,WAAW,cAAc;AAC9D,cAAM,EAAE,OAAO,aAAa,IAAI,gBAAgB,gBAAgB;AAEhE,cAAM,gBAA+B;AAAA,UACnC,OAAO,aAAa;AAAA,UACpB,UAAU,aAAa,YAAY;AAAA,UACnC,UAAU,aAAa;AAAA,QACzB;AAEA,mBAAW,QAAQ,OAAO;AACxB,gBAAM,UAAU,eAAe,QAAQ,IAAI;AAC3C,cAAI,QAAS,gBAAe;AAAA,QAC9B;AAEA,YAAI,MAAM,SAAS,GAAG;AACpB,gBAAM,eAAe,gBAAgB;AACrC,sBAAY,UAAQ;AAClB,kBAAM,UAAU,CAAC,GAAG,IAAI;AACxB,oBAAQ,QAAQ,SAAS,CAAC,IAAI;AAAA,cAC5B,MAAM;AAAA,cACN,SAAS;AAAA,cACT;AAAA,cACA;AAAA,cACA,eAAe,eAAe,gBAAgB;AAAA,YAChD;AACA,mBAAO;AAAA,UACT,CAAC;AACD,sBAAY,aAAa,YAAY;AAAA,QACvC,OAAO;AACL,sBAAY,aAAa,gBAAgB;AAAA,QAC3C;AAAA,MACF,WAAW,SAAS,QAAQ;AAC1B,cAAM,iBAAiB,gBAAgB,gBAAgB;AAEvD,oBAAY,UAAQ;AAClB,gBAAM,UAAU,CAAC,GAAG,IAAI;AACxB,kBAAQ,QAAQ,SAAS,CAAC,IAAI;AAAA,YAC5B,MAAM;AAAA,YACN,SAAS;AAAA,YACT;AAAA,UACF;AACA,iBAAO;AAAA,QACT,CAAC;AAED,oBAAY,aAAa,cAAc;AAAA,MACzC,OAAO;AACL,oBAAY,aAAa,gBAAgB;AAAA,MAC3C;AAAA,IACF,SAAS,OAAO;AACd,UAAI,iBAAiB,SAAS,MAAM,SAAS,cAAc;AACzD;AAAA,MACF;AACA,cAAQ,MAAM,eAAe,KAAK;AAClC,YAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAY,UAAQ;AAClB,cAAM,UAAU,CAAC,GAAG,IAAI;AACxB,gBAAQ,QAAQ,SAAS,CAAC,IAAI,EAAE,MAAM,aAAa,SAAS,UAAU,YAAY,GAAG;AACrF,eAAO;AAAA,MACT,CAAC;AAAA,IACH,UAAE;AACA,yBAAmB,UAAU;AAC7B,qBAAe,KAAK;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,UAAU,aAAa,cAAc,MAAM,kBAAkB,iBAAiB,eAAe,aAAa,mBAAmB,CAAC;AAElI,QAAM,gBAAgBL,cAAY,MAAM;AACtC,gBAAY,CAAC,CAAC;AAAA,EAChB,GAAG,CAAC,CAAC;AAEL,QAAM,aAAaA,cAAY,CAAC,MAA4B,YAAoB;AAC9E,UAAM,UAAmB,EAAE,MAAM,QAAQ;AACzC,gBAAY,UAAQ,CAAC,GAAG,MAAM,OAAO,CAAC;AACtC,gBAAY,MAAM,OAAO;AAAA,EAC3B,GAAG,CAAC,WAAW,CAAC;AAEhB,QAAM,WAAWA,cAAY,CAAC,iBAAyB;AACrD,UAAM,UAAU,SAAS,YAAY;AACrC,QAAI,CAAC,SAAS,iBAAiB,CAAC,eAAe,QAAS;AAExD,UAAM,UAAU,eAAe,QAAQ;AAAA,MACrC,MAAM;AAAA,MACN,OAAO,QAAQ,cAAc;AAAA,MAC7B,UAAU,QAAQ,cAAc;AAAA,MAChC,UAAU,QAAQ,cAAc;AAAA,IAClC,CAAC;AAED,QAAI,SAAS;AACX,kBAAY,UAAQ;AAClB,cAAM,UAAU,CAAC,GAAG,IAAI;AACxB,gBAAQ,YAAY,IAAI;AAAA,UACtB,GAAG,QAAQ,YAAY;AAAA,UACvB,cAAc;AAAA,UACd,eAAe;AAAA,QACjB;AACA,eAAO;AAAA,MACT,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,aAAaA,cAAY,CAAC,YAAoB,QAAQ;AAC1D,QAAI,CAAC,qBAAqB,QAAS;AAEnC,UAAM,uBAAuB,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,KAAK,OAAK,EAAE,SAAS,WAAW;AACrF,QAAI,CAAC,sBAAsB,QAAS;AAEpC,yBAAqB,QAAQ,qBAAqB,SAAS,SAAS;AAEpE,cAAU,KAAK;AAAA,EACjB,GAAG,CAAC,QAAQ,CAAC;AAEb,QAAM,QAAQG,SAA0B,OAAO;AAAA,IAC7C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAAA,IACF;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SACE,gBAAAC,MAAC,YAAY,UAAZ,EAAqB,OACnB,UACH;AAEJ;AAEO,SAAS,iBAAiB;AAC/B,QAAM,UAAUN,YAAW,WAAW;AACtC,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AACA,SAAO;AACT;;;ACxaA,SAAS,YAAAQ,YAAU,UAAAC,UAAQ,aAAAC,aAAW,eAAAC,qBAAmB;AACzD,SAAS,gBAAAC,qBAAoB;AAC7B,SAAkB,KAAAC,IAAG,MAAM,SAAAC,QAAO,WAAAC,UAAS,UAAAC,SAAQ,SAAAC,QAAO,eAAAC,cAAa,iBAAAC,gBAAe,SAAAC,QAAO,SAAAC,QAAO,QAAQ,QAAAC,aAAY;;;ACFxH,SAAS,YAAAC,YAAU,aAAAC,mBAAiB;AA0B7B,SAAS,YAAY,SAAiD;AAC3E,QAAM,CAAC,QAAQ,SAAS,IAAID,WAA0B,CAAC,CAAC;AACxD,QAAM,CAAC,uBAAuB,wBAAwB,IAAIA,WAAS,EAAE;AACrE,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,IAAI;AAG/C,QAAM,gBAAgB,SAAS,yBAAyB;AACxD,QAAM,mBAAmB,SAAS,4BAA4B;AAE9D,QAAM,UAAU,SAAS,WAAW;AAEpC,EAAAC,YAAU,MAAM;AACd,UAAM,OAAO,EACV,KAAK,SAAO;AACX,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,MAAM,QAAQ,IAAI,MAAM,EAAE;AACjD,aAAO,IAAI,KAAK;AAAA,IAClB,CAAC,EACA,KAAK,UAAQ;AAEZ,YAAM,WAAW,KAAK,QAAQ;AAC9B,gBAAU,SAAS,mBAAmB,CAAC,CAAC;AACxC,UAAI,SAAS,gBAAgB,CAAC,eAAe;AAC3C,yBAAiB,SAAS,YAAY;AAAA,MACxC;AAAA,IACF,CAAC,EACA,MAAM,QAAQ,KAAK,EACnB,QAAQ,MAAM,aAAa,KAAK,CAAC;AAAA,EACtC,GAAG,CAAC,OAAO,CAAC;AAGZ,QAAM,eAAe,OAAO,KAAK,OAAK,EAAE,OAAO,aAAa;AAE5D,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ADwMI,qBAAAC,YAEE,OAAAC,OAcI,QAAAC,cAhBN;AA/PJ,IAAMC,yBAAwB;AAG9B,SAAS,cAAc,SAAyB;AAC9C,SAAO,QACJ,QAAQ,YAAY,EAAE,EACtB,QAAQ,cAAc,EAAE;AAC7B;AAaO,SAAS,UAAU;AAAA,EACxB,eAAeA;AAAA,EACf;AAAA,EACA,YAAY;AACd,GAAmB;AACjB,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,IACA,aAAa;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,eAAe;AAGnB,QAAM,aAAa,kBAAkB,CAAC,CAAC;AAEvC,QAAM,CAAC,OAAO,QAAQ,IAAIC,WAAS,EAAE;AACrC,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAS,KAAK;AACpD,QAAM,CAAC,WAAW,YAAY,IAAIA,WAAS,KAAK;AAChD,QAAM,CAAC,SAAS,UAAU,IAAIA,WAAS,KAAK;AAC5C,QAAM,CAAC,aAAa,cAAc,IAAIA,WAAwB,IAAI;AAClE,QAAM,CAAC,cAAc,eAAe,IAAIA,WAAS,KAAK;AACtD,QAAM,cAAcC,SAAuB,IAAI;AAC/C,QAAM,iBAAiBA,SAAuB,IAAI;AAClD,QAAM,uBAAuBA,SAAuB,IAAI;AACxD,QAAM,cAAcA,SAA4B,IAAI;AACpD,QAAM,sBAAsBA,SAAO,CAAC;AACpC,QAAM,yBAAyBA,SAAsB,IAAI;AACzD,QAAM,qBAAqBA,SAAuB,IAAI;AAGtD,QAAM,EAAE,QAAQ,aAAa,IAAI,YAAY;AAAA,IAC3C,uBAAuB;AAAA,IACvB,0BAA0B;AAAA,EAC5B,CAAC;AAED,QAAM,UAAUC,cAAY,MAAM,UAAU,KAAK,GAAG,CAAC,SAAS,CAAC;AAE/D,QAAM,kBAAkBA,cAAY,OAAO,MAAc,UAAkB;AACzE,UAAM,UAAU,UAAU,UAAU,IAAI;AACxC,mBAAe,KAAK;AACpB,eAAW,MAAM,eAAe,IAAI,GAAG,GAAI;AAAA,EAC7C,GAAG,CAAC,CAAC;AAGL,QAAM,mBAAmBA,cAAY,MAAM;AAEzC,UAAM,uBAAuB,CAAC,GAAG,QAAQ,EAAE,QAAQ,EAAE,KAAK,OAAK,EAAE,SAAS,WAAW;AACrF,QAAI,CAAC,sBAAsB,QAAS;AAEpC,QAAI,YAAY;AAEd,iBAAW;AACX,cAAQ,OAAO;AAAA,IACjB,WAAW,YAAY;AAErB,qBAAe,QAAQ,eAAe,qBAAqB,OAAO;AAClE,gBAAU,KAAK;AACf,iBAAW,oBAAoB;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,UAAU,YAAY,YAAY,WAAW,SAAS,UAAU,CAAC;AAGrE,EAAAC,YAAU,MAAM;AACd,aAAS,YAAY,GAAe;AAClC,UAAI,YAAY,WAAW,CAAC,YAAY,QAAQ,SAAS,EAAE,MAAc,GAAG;AAC1E,wBAAgB,KAAK;AAAA,MACvB;AAAA,IACF;AACA,aAAS,iBAAiB,aAAa,WAAW;AAClD,WAAO,MAAM,SAAS,oBAAoB,aAAa,WAAW;AAAA,EACpE,GAAG,CAAC,CAAC;AAGL,EAAAA,YAAU,MAAM;AACd,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,CAAC;AAGL,EAAAA,YAAU,MAAM;AACd,QAAI,MAAM;AACR,mBAAa,IAAI;AAEjB,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,QAAQ;AAC5B,eAAS,KAAK,MAAM,MAAM,IAAI,OAAO,OAAO;AAAA,IAC9C,OAAO;AACL,qBAAe,KAAK;AAEpB,YAAM,UAAU,SAAS,KAAK,MAAM;AACpC,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,WAAW;AAC/B,eAAS,KAAK,MAAM,QAAQ;AAC5B,eAAS,KAAK,MAAM,MAAM;AAC1B,aAAO,SAAS,GAAG,SAAS,WAAW,GAAG,IAAI,EAAE;AAEhD,YAAM,QAAQ,WAAW,MAAM;AAC7B,qBAAa,KAAK;AAAA,MACpB,GAAG,GAAG;AACN,aAAO,MAAM,aAAa,KAAK;AAAA,IACjC;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAGT,EAAAA,YAAU,MAAM;AACd,QAAI,aAAa,QAAQ,CAAC,aAAa;AAErC,4BAAsB,MAAM;AAC1B,uBAAe,IAAI;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF,GAAG,CAAC,WAAW,MAAM,WAAW,CAAC;AAGjC,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,qBAAqB,SAAS;AACzC,6BAAuB,UAAU,qBAAqB,QAAQ;AAAA,IAChE;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAGT,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,QAAQ,CAAC,UAAW;AAEzB,UAAM,YAAY,qBAAqB;AACvC,QAAI,CAAC,UAAW;AAEhB,UAAM,YAAY,oBAAoB;AACtC,UAAM,eAAe,SAAS;AAE9B,0BAAsB,MAAM;AAC1B,4BAAsB,MAAM;AAC1B,YAAI,eAAe,WAAW;AAE5B,gBAAM,WAAW,cAAc,IAAI,YAAY;AAC/C,yBAAe,SAAS,eAAe,EAAE,SAAS,CAAC;AAAA,QACrD,WAAW,uBAAuB,YAAY,MAAM;AAElD,oBAAU,YAAY,uBAAuB;AAAA,QAC/C;AAEA,4BAAoB,UAAU;AAAA,MAChC,CAAC;AAAA,IACH,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,QAAQ,MAAM,SAAS,CAAC;AAGrC,EAAAA,YAAU,MAAM;AACd,QAAI,CAAC,YAAa;AAElB,UAAM,YAAY,qBAAqB;AACvC,UAAM,cAAc,mBAAmB;AACvC,QAAI,CAAC,aAAa,CAAC,YAAa;AAGhC,UAAM,gBAAgB,UAAU,sBAAsB;AACtD,UAAM,cAAc,YAAY,sBAAsB;AAGtD,UAAM,kBAAkB,YAAY,MAAM,cAAc;AACxD,QAAI,kBAAkB,IAAI;AAExB,gBAAU,aAAa,KAAK,IAAI,kBAAkB,KAAK,EAAE;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,UAAU,WAAW,CAAC;AAG1B,EAAAA,YAAU,MAAM;AACd,UAAM,WAAW,YAAY;AAC7B,QAAI,UAAU;AACZ,eAAS,MAAM,SAAS;AACxB,eAAS,MAAM,SAAS,GAAG,KAAK,IAAI,SAAS,cAAc,GAAG,CAAC;AAAA,IACjE;AAAA,EACF,GAAG,CAAC,KAAK,CAAC;AAGV,QAAM,cAAcD,cAAY,YAAY;AAC1C,QAAI,CAAC,MAAM,KAAK,KAAK,YAAa;AAClC,UAAM,UAAU,MAAM,KAAK;AAC3B,aAAS,EAAE;AACX,UAAM,mBAAmB,OAAO;AAAA,EAClC,GAAG,CAAC,OAAO,aAAa,kBAAkB,CAAC;AAE3C,QAAM,gBAAgB,CAAC,MAA2B;AAChD,QAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;AACpC,QAAE,eAAe;AACjB,kBAAY;AAAA,IACd;AACA,QAAI,EAAE,QAAQ,UAAU;AACtB,QAAE,gBAAgB;AAClB,cAAQ;AAAA,IACV;AAAA,EACF;AAGA,EAAAC,YAAU,MAAM;AACd,UAAM,sBAAsB,CAAC,MAAqB;AAChD,WAAK,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAAE,IAAI,YAAY,MAAM,KAAK;AACzE,UAAE,eAAe;AACjB,YAAI,CAAC,MAAM;AACT,oBAAU,IAAI;AAAA,QAChB;AAEA,YAAI,cAAc;AAChB,kBAAQ,OAAO;AAAA,QACjB;AAAA,MACF;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,mBAAmB;AACxD,WAAO,MAAM,SAAS,oBAAoB,WAAW,mBAAmB;AAAA,EAC1E,GAAG,CAAC,MAAM,WAAW,SAAS,YAAY,CAAC;AAE3C,MAAI,CAAC,aAAa,CAAC,QAAS,QAAO;AAEnC,QAAM,gBAAgB,CAAC,MAAgB;AACrC,QAAI,MAAM,MAAO,QAAO;AACxB,QAAI,MAAM,QAAS,QAAO;AAC1B,WAAO;AAAA,EACT;AAEA,SAAOC;AAAA,IACL,gBAAAN,OAAAF,YAAA,EAEE;AAAA,sBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,WAAW,+EAA+E,cAAc,gBAAgB,WAAW;AAAA,UACnI,SAAS;AAAA;AAAA,MACX;AAAA,MAGA,gBAAAC;AAAA,QAAC;AAAA;AAAA,UACC,MAAK;AAAA,UACL,cAAW;AAAA,UACX,cAAW;AAAA,UACX,WAAW,iNAAiN,cAAc,kBAAkB,kBAAkB;AAAA,UAG9Q;AAAA,4BAAAA,OAAC,SAAI,WAAU,oFACb;AAAA,8BAAAA,OAAC,SAAI,WAAU,2BACb;AAAA,gCAAAD,MAAC,QAAG,WAAU,eAAc,kBAAI;AAAA,gBAC/B,gBACC,gBAAAA,MAAC,UAAK,WAAU,8JACb,uBAAa,SAAS,YACzB;AAAA,iBAEJ;AAAA,cACA,gBAAAA;AAAA,gBAAC;AAAA;AAAA,kBACC,SAAS;AAAA,kBACT,WAAU;AAAA,kBACV,cAAW;AAAA,kBAEX,0BAAAA,MAACQ,IAAA,EAAE,WAAU,WAAU;AAAA;AAAA,cACzB;AAAA,eACF;AAAA,YAGA,gBAAAR,MAAC,SAAI,KAAK,sBAAsB,WAAU,0BACvC,mBAAS,WAAW,IACnB,gBAAAA,MAAC,SAAI,WAAU,2CACb,0BAAAA,MAAC,SAAI,WAAU,6BACb,0BAAAA,MAAC,OAAE,WAAU,iCACV,mBAAS,SACN,uGACA,eACE,sGACA,sEAER,GACF,GACF,IAEA,gBAAAC,OAAC,SAAI,WAAU,uBACZ;AAAA,uBAAS,IAAI,CAAC,SAAS,UAAU;AAEhC,sBAAM,oBAAoB,QAAQ,SAAS,UACzC,CAAC,SAAS,MAAM,QAAQ,CAAC,EAAE,KAAK,OAAK,EAAE,SAAS,MAAM;AAExD,uBACA,gBAAAD;AAAA,kBAAC;AAAA;AAAA,oBAEC,KAAK,oBAAoB,qBAAqB;AAAA,oBAC9C,WAAW,oBAAoB,QAAQ,SAAS,SAAS,gBAAgB,eAAe;AAAA,oBAExF,0BAAAC;AAAA,sBAAC;AAAA;AAAA,wBACC,WAAW,sDAAsD,QAAQ,SAAS,SAAS,uCAAuC,UAAU;AAAA,wBAE3I;AAAA,kCAAQ,SAAS,cAChB,gBAAAD;AAAA,4BAAC;AAAA;AAAA,8BACC,WAAW,GAAG,YAAY;AAAA,8BAC1B,yBAAyB,EAAE,QAAQ,eAAe,cAAc,QAAQ,OAAO,CAAC,EAAE;AAAA;AAAA,0BACpF,IAEA,gBAAAA,MAAC,SAAI,WAAU,mCACZ,kBAAQ,SACX;AAAA,0BAED,eAAe,UAAU,SAAS,SAAS,KAAK,QAAQ,SAAS,eAChE,gBAAAA,MAAC,UAAK,WAAU,0DAAyD;AAAA,0BAG1E,QAAQ,gBAAgB,QAAQ,iBAC/B,gBAAAC,OAAC,SAAI,WAAU,kCACb;AAAA,4CAAAA,OAAC,SAAI,WAAU,sEACb;AAAA,8CAAAD,MAACS,SAAA,EAAO,WAAU,WAAU;AAAA,8BAC5B,gBAAAT,MAAC,UAAK,0BAAY;AAAA,+BACpB;AAAA,4BACA,gBAAAC;AAAA,8BAAC;AAAA;AAAA,gCACC,SAAS,MAAM,SAAS,KAAK;AAAA,gCAC7B,WAAU;AAAA,gCACV,cAAW;AAAA,gCAEX;AAAA,kDAAAD,MAACU,QAAA,EAAM,WAAU,WAAU;AAAA,kCAC3B,gBAAAV,MAAC,UAAK,kBAAI;AAAA;AAAA;AAAA,4BACZ;AAAA,6BACF;AAAA,0BAGD,QAAQ,SAAS,eAAe,CAAC,eAChC,gBAAAC,OAAC,SAAI,WAAU,0GACb;AAAA,4CAAAD;AAAA,8BAAC;AAAA;AAAA,gCACC,SAAS,MAAM,gBAAgB,QAAQ,SAAS,KAAK;AAAA,gCACrD,WAAU;AAAA,gCACV,cAAW;AAAA,gCAEV,0BAAgB,QACf,gBAAAA,MAACW,QAAA,EAAM,WAAU,8BAA6B,IAE9C,gBAAAX,MAAC,QAAK,WAAU,eAAc;AAAA;AAAA,4BAElC;AAAA,4BAEC,QAAQ,SAAS,UAAU,UAAU,SAAS,SAAS,KAAK,QAAQ,WACnE,gBAAAA;AAAA,8BAAC;AAAA;AAAA,gCACC,SAAS;AAAA,gCACT,WAAU;AAAA,gCACX;AAAA;AAAA,4BAED;AAAA,6BAEJ;AAAA;AAAA;AAAA,oBAEJ;AAAA;AAAA,kBA9DK;AAAA,gBA+DP;AAAA,cACD,CAAC;AAAA,cACF,gBAAAA,MAAC,SAAI,KAAK,gBAAgB;AAAA,eAC5B,GAEJ;AAAA,YAGA,gBAAAC;AAAA,cAAC;AAAA;AAAA,gBACC,UAAU,CAAC,MAAM;AAAE,oBAAE,eAAe;AAAG,8BAAY;AAAA,gBAAG;AAAA,gBACtD,WAAU;AAAA,gBAGV;AAAA,kCAAAA,OAAC,SAAI,WAAU,gCAEb;AAAA,oCAAAA,OAAC,SAAI,KAAK,aAAa,WAAU,YAC/B;AAAA,sCAAAA;AAAA,wBAAC;AAAA;AAAA,0BACC,MAAK;AAAA,0BACL,SAAS,MAAM,gBAAgB,CAAC,YAAY;AAAA,0BAC5C,WAAW,kHAAkH,cAAc,IAAI,CAAC;AAAA,0BAChJ,OAAM;AAAA,0BAEL;AAAA,qCAAS,SAAS,gBAAAD,MAACY,gBAAA,EAAc,WAAU,WAAU;AAAA,4BACrD,SAAS,WAAW,gBAAAZ,MAACS,SAAA,EAAO,WAAU,WAAU;AAAA,4BAChD,SAAS,UAAU,gBAAAT,MAACa,OAAA,EAAK,WAAU,WAAU;AAAA,4BAC7C,SAAS,QAAQ,QAAQ,SAAS,UAAU,UAAU;AAAA,4BACvD,gBAAAb,MAACc,cAAA,EAAY,WAAU,sBAAqB;AAAA;AAAA;AAAA,sBAC9C;AAAA,sBACC,gBACC,gBAAAb,OAAC,SAAI,WAAU,2GACb;AAAA,wCAAAA;AAAA,0BAAC;AAAA;AAAA,4BACC,MAAK;AAAA,4BACL,SAAS,MAAM;AAAE,sCAAQ,OAAO;AAAG,8CAAgB,KAAK;AAAG,0CAAY,SAAS,MAAM;AAAA,4BAAE;AAAA,4BACxF,UAAU,CAAC;AAAA,4BACX,WAAU;AAAA,4BAEV;AAAA,8CAAAA,OAAC,UAAK,WAAU,2BACd;AAAA,gDAAAD,MAACS,SAAA,EAAO,WAAU,WAAU;AAAA,gCAAE;AAAA,iCAEhC;AAAA,8BACA,gBAAAR,OAAC,UAAK,WAAU,2BACd;AAAA,gDAAAD,MAAC,UAAK,WAAU,iCAAgC,2BAAG;AAAA,gCAClD,SAAS,WAAW,gBAAAA,MAACW,QAAA,EAAM,WAAU,WAAU;AAAA,iCAClD;AAAA;AAAA;AAAA,wBACF;AAAA,wBACA,gBAAAV;AAAA,0BAAC;AAAA;AAAA,4BACC,MAAK;AAAA,4BACL,SAAS,MAAM;AAAE,sCAAQ,MAAM;AAAG,8CAAgB,KAAK;AAAG,0CAAY,SAAS,MAAM;AAAA,4BAAE;AAAA,4BACvF,WAAU;AAAA,4BAEV;AAAA,8CAAAA,OAAC,UAAK,WAAU,2BACd;AAAA,gDAAAD,MAACa,OAAA,EAAK,WAAU,WAAU;AAAA,gCAAE;AAAA,iCAE9B;AAAA,8BACC,SAAS,UAAU,gBAAAb,MAACW,QAAA,EAAM,WAAU,WAAU;AAAA;AAAA;AAAA,wBACjD;AAAA,wBACA,gBAAAV;AAAA,0BAAC;AAAA;AAAA,4BACC,MAAK;AAAA,4BACL,SAAS,MAAM;AAAE,sCAAQ,KAAK;AAAG,8CAAgB,KAAK;AAAG,0CAAY,SAAS,MAAM;AAAA,4BAAE;AAAA,4BACtF,WAAU;AAAA,4BAEV;AAAA,8CAAAA,OAAC,UAAK,WAAU,2BACd;AAAA,gDAAAD,MAACY,gBAAA,EAAc,WAAU,WAAU;AAAA,gCAAE;AAAA,iCAEvC;AAAA,8BACC,SAAS,SAAS,gBAAAZ,MAACW,QAAA,EAAM,WAAU,WAAU;AAAA;AAAA;AAAA,wBAChD;AAAA,yBACF;AAAA,uBAEJ;AAAA,oBAGA,gBAAAX;AAAA,sBAAC;AAAA;AAAA,wBACC,SAAS,MAAM;AAAE,8CAAoB,CAAC,gBAAgB;AAAG,sCAAY,SAAS,MAAM;AAAA,wBAAE;AAAA,wBACtF,QAAQ;AAAA,wBACR,OAAO,mBAAmB,uBAAuB;AAAA,wBACjD,UAAU;AAAA,wBAEV,0BAAAA,MAACe,QAAA,EAAM,WAAU,WAAU;AAAA;AAAA,oBAC7B;AAAA,oBAGA,gBAAAf;AAAA,sBAAC;AAAA;AAAA,wBACC,SAAS,MAAM;AAAE,6CAAmB,CAAC,eAAe;AAAG,sCAAY,SAAS,MAAM;AAAA,wBAAE;AAAA,wBACpF,QAAQ;AAAA,wBACR,OAAO,kBAAkB,0BAA0B;AAAA,wBACnD,UAAU;AAAA,wBAEV,0BAAAA,MAACgB,QAAA,EAAM,WAAU,WAAU;AAAA;AAAA,oBAC7B;AAAA,oBAGA,gBAAAhB;AAAA,sBAAC;AAAA;AAAA,wBACC;AAAA,wBACA;AAAA,wBACA,eAAe,CAAC,OAAO;AAAE,2CAAiB,EAAE;AAAG,sCAAY,SAAS,MAAM;AAAA,wBAAE;AAAA,wBAC5E;AAAA;AAAA,oBACF;AAAA,qBACF;AAAA,kBACA,gBAAAC,OAAC,SAAI,WAAU,wBACb;AAAA,oCAAAD;AAAA,sBAAC;AAAA;AAAA,wBACC,KAAK;AAAA,wBACL,OAAO;AAAA,wBACP,UAAU,CAAC,MAAM,SAAS,EAAE,OAAO,KAAK;AAAA,wBACxC,WAAW;AAAA,wBACX,aACE,SAAS,SACL,gCACA,SAAS,WAAW,eAClB,iCACA,eACE,4BACA;AAAA,wBAEV,WAAU;AAAA,wBACV,MAAM;AAAA,wBACN,WAAS;AAAA;AAAA,oBACX;AAAA,oBACA,gBAAAA;AAAA,sBAAC;AAAA;AAAA,wBACC,MAAM,cAAc,WAAW;AAAA,wBAC/B,SAAS,cAAc,gBAAgB;AAAA,wBACvC,UAAU,CAAC,eAAe,CAAC,MAAM,KAAK;AAAA,wBACtC,WAAU;AAAA,wBAET,wBACC,gBAAAA,MAAC,UAAO,WAAU,wBAAuB,IAEzC,gBAAAA,MAACiB,UAAA,EAAQ,WAAU,WAAU;AAAA;AAAA,oBAEjC;AAAA,qBACF;AAAA;AAAA;AAAA,YACF;AAAA;AAAA;AAAA,MACF;AAAA,OACF;AAAA,IACA,SAAS;AAAA,EACX;AACF;","names":["useState","Loader2","useState","useEffect","useMemo","useRef","ChevronDown","Check","jsx","useState","useEffect","jsx","useState","useEffect","jsx","jsxs","useState","useEffect","useMemo","ChevronDown","Check","useRef","useState","useEffect","useCallback","useRef","jsx","jsx","useState","useEffect","useCallback","jsx","jsxs","useState","useEffect","useCallback","Fragment","jsx","jsxs","useRef","useCallback","Fragment","jsx","jsxs","useRef","useCallback","useCallback","useState","Loader2","useState","useRef","useEffect","useCallback","createContext","useContext","Fragment","jsx","jsxs","createContext","useState","useRef","useCallback","useEffect","useContext","jsx","jsxs","useState","Loader2","Fragment","jsx","jsxs","useCallback","jsx","jsxs","useEffect","useMemo","Image","TextSelection","Fragment","Selection","joinPoint","originalCommand","Schema","DOMParser","Node","NodeSelection","canSplit","Plugin","PluginKey","canJoin","command","run","mark","isTextSelection","isActive","document","style","Schema","range","NodeSelection","TextSelection","first","Fragment","newNextTypeAttributes","nextType","canSplit","isActive","Fragment","run","Plugin","Fragment","from","to","range","_a","_b","_c","PluginKey","Selection","tr","parseAttributes","serializeAttributes","h","nextIndent","Plugin","PluginKey","jsx","useMemo","Image","editor","useEffect","useState","useRef","useEffect","useCallback","createPortal","X","MessageSquare","ArrowUp","Loader2","ChevronDown","useState","Check","Fragment","jsx","jsxs","formatRelativeTime","useState","Check","Fragment","jsx","jsxs","useState","useRef","useEffect","useCallback","createPortal","MessageSquare","X","Loader2","ArrowUp","ChevronDown","useState","useEffect","useRef","Fragment","jsx","jsxs","useState","useCallback","useEffect","useState","useEffect","useCallback","jsx","jsxs","Skeleton","useRef","useEffect","useState","useCallback","useState","useEffect","ChevronDown","ChevronRight","Plus","Pencil","Trash2","X","MoreVertical","MoreHorizontal","Loader2","Fragment","jsx","jsxs","useState","useEffect","MoreVertical","ChevronDown","Loader2","Plus","X","Pencil","Trash2","MoreHorizontal","ChevronRight","ChevronLeft","Fragment","jsx","jsxs","ChevronLeft","useEffect","useRef","Fragment","jsx","jsxs","useState","Loader2","createContext","useContext","useState","useCallback","useRef","useEffect","useMemo","jsx","value","useState","useRef","useEffect","useCallback","createPortal","X","Check","ArrowUp","Pencil","Undo2","ChevronDown","MessageSquare","Globe","Brain","List","useState","useEffect","Fragment","jsx","jsxs","DEFAULT_PROSE_CLASSES","useState","useRef","useCallback","useEffect","createPortal","X","Pencil","Undo2","Check","MessageSquare","List","ChevronDown","Globe","Brain","ArrowUp"]}
|