import type { Editor } from '@tiptap/core'
import type { Slice } from '@tiptap/pm/model'
import type { EditorView } from '@tiptap/pm/view'
import type { EntityID } from '@wovin/core/applog'
import type { Accessor, Component, JSX, Setter } from 'solid-js'
import type { BlockVM } from '../data/VMs/BlockVM'
import type { RelationVM } from '../data/VMs/RelationVM'
import type { FakeBlock } from './BlockTree'
import { useSearchParams } from '@solidjs/router'
import Image from '@tiptap/extension-image'
import { untracked } from '@wovin/core/mobx'
import { Logger } from 'besonders-logger'
import classNames from 'classnames'
import { format } from 'date-fns/format'
import { parseISO } from 'date-fns/parseISO'
import { pick } from 'lodash-es'
import { stringify } from 'safe-stable-stringify'
import { createEffect, createMemo, createSignal, For, on, onCleanup, onMount, Show, splitProps } from 'solid-js'
import { createTiptapEditor, useEditorJSON } from 'solid-tiptap'
import { useAgent } from '../data/agent/AgentState'
import {	useBlockContext } from '../data/block-ui-helpers'
import { createTreeItemKeydownHandler } from '../data/block-ui-helpers-keydown'
import { copyToClipboard,	createTreeItemPasteHandler,	getPlainTextFromClipboard,	getValidURLFromClipboard } from '../data/block-ui-helpers-paste'
import { compareBlockContent } from '../data/block-utils-nowin'
import { REL_DEF } from '../data/data-types'
import { useBlockKeyhandlers } from '../data/keybindings'
import { plaintextStringToTiptap, tiptapToPlaintext } from '../data/note3-utils-nodeps'
import { doesContentMatchSearch, useSearchContext } from '../data/search'
import { useBlk } from '../data/VMs/BlockVM'
import { KnownFullAttrs } from '../data/VMs/utils-typemap'
import { MirrorBulletIcon } from '../icons/MirrorBulletIcon'
import { activePlugins } from '../plugins/init'
import { useBlockVM, useCurrentThread, useReadOnlyState, useRelationVM } from '../ui/reactive'
import { devMode, EventDebugger, focusViewOnBlock, onClickOrLongPress, useLocationNavigate } from '../ui/utils-ui'
import { BlockSettingsRow } from './BlockSettings'
import { BulletMenu } from './BulletMenu'
import { DynamicColored, Iconify, IconifyNames, useIdHover } from './mini-components'
import {
	baseExtensions,
	CreateTokenHidingExtension,
	SelectionDetectLinkHandler as CustomSelectionHandler,
	htmlToTiptap,
	TagHighlightExtension,
	WordInfoExtension,
} from './TipTapExtentions'
import { TiptapMenuWrapper } from './TiptapMenuWrapper'

const { WARN, LOG, DEBUG, VERBOSE, ERROR } = Logger.setup(Logger.INFO) // eslint-disable-line unused-imports/no-unused-vars
// scale: relVM?.isMirror ? 40 : blkVM.isReply ? 70 : (blkVM.isTodo ? 100 : 150) }

export const blockIconNameHooks = [
	function mirrorBlockIconName(blockOrFake: BlockVM | FakeBlock, relVM?: RelationVM, likelyKids = false) {
		return relVM?.isMirror
			? (likelyKids
					? { icon: <MirrorBulletIcon />, name: IconifyNames['diamond-duotone'], scale: 70 }
					: { name: IconifyNames['diamond-fill'], scale: 35 })
			: {}
	},
	function replyBlockIconName(blockOrFake: BlockVM | FakeBlock, _relVM?: RelationVM, likelyKids = false) {
		return blockOrFake?.isReply // HACK: BlockVM doesn't know about view context (but for fake block it's good to be able to set it)
			? (likelyKids
					? { name: IconifyNames['arrow-bend-double-up-left-bold'], scale: 70 }
					: { name: IconifyNames['arrow-bend-down-right-bold'], scale: 70 })
			: {}
	},
	function todoBlockIconName(blockOrFake: BlockVM | FakeBlock, _relVM?: RelationVM, _likelyKids = false) {
		return blockOrFake?.isTodo
			? (
					blockOrFake?.isTodoDone
						? { name: 'check-square-thin', scale: 100 }
						: { name: 'square-thin', scale: 100 }
				)
			: {}
	},
]

const UnknownAttributesPills: Component<{
	blockOrFake: BlockVM | FakeBlock
	hasContent?: boolean
}> = (props) => {
	const unknownAttrsWithValues = createMemo(() => {
		if (!props.blockOrFake || !('entityThread' in props.blockOrFake)) return []

		const blkVM = props.blockOrFake as BlockVM
		const applogs = blkVM.entityThread.applogs

		// Get all unique attribute names that are not in KnownFullAttrs
		const uniqueAttrs = new Set(
			applogs
				.filter(log => !KnownFullAttrs.Block.includes(log.at))
				.map(log => log.at),
		)

		// Get latest value for each unknown attribute
		return Array.from(uniqueAttrs).map((at) => {
			const latestLog = applogs.findLast(log => log.at === at)
			const value = latestLog?.vl
			// Truncate long values for display
			const displayValue = typeof value === 'string' && value.length > 50
				? `${value.substring(0, 47)}...`
				: value
			const fullValue = typeof value === 'string' ? value : JSON.stringify(value)
			return { name: at, displayValue, fullValue }
		})
	})

	const opacity = createMemo(() => props.hasContent ? 'opacity-50' : 'opacity-70')

	return (
		<Show when={unknownAttrsWithValues().length > 0}>
			<div class={classNames('flex flex-wrap items-center', props.hasContent ? 'mt-1' : '')} style={{ gap: '0.125rem' }}>
				<For each={unknownAttrsWithValues()}>
					{attr => (
						<sl-tag
							size='small'
							class={classNames(opacity(), 'rounded transition-opacity duration-200 hover:opacity-100')}
							style={{ 'font-size': '0.75rem' }}
							title={`${attr.name}: ${attr.fullValue}`}
						>
							{attr.name}: {attr.displayValue}
						</sl-tag>
					)}
				</For>
			</div>
		</Show>
	)
}

export const BlockItem: Component<{
	blockID: EntityID | null
	fake?: FakeBlock
	relationID: EntityID | null
	hasKids: boolean
	readOnly?: boolean // ? kind of redundant with thread context readonly
	isExpanded: Accessor<boolean>
	setExpanded: Setter<boolean> | ((newVal: boolean) => any)
}> = (props) => {
	const { blockID, fake } = props // assumed static
	const debugName = untracked(() => blockID ?? JSON.stringify(fake))
	if (!blockID && !fake) {
		throw ERROR(`<Block> with falsy blockID`, blockID)
	}
	VERBOSE(`[BlockItem#${debugName}] created`)

	const { locnav, location, navigate } = useLocationNavigate()
	const [searchParams, _setSearchParams] = useSearchParams()
	const previewFromUrl = createMemo(() => {
		DEBUG(`searchParams:`, { ...searchParams })
		return searchParams.preview
	})

	const { isExpanded, setExpanded } = props // accessor/setter, so keeps reactivity
	const thread = useCurrentThread()

	const blockContext = useBlockContext()
	const parentContext = blockContext?.parentContext
	// const relation = props.relationID && useRelation(props.relationID)
	const relVM = props.relationID && useRelationVM(props.relationID)
	const blkVM = fake ? undefined : useBlk(blockID)
	const parentVM = relVM ? useBlk(relVM.childOf) : null
	const blockOrFake = fake ?? blkVM
	const ag = useAgent().ag
	const isDeleted = createMemo(() => (blockOrFake as BlockVM).isDeleted)
	const readOnly = createMemo(() => props.readOnly || !!props.fake || thread.readOnly || isDeleted())

	const [search] = useSearchContext()
	const shouldHide = createMemo(() => !fake && search() && !doesContentMatchSearch(blkVM.contentPlaintext ?? '', search())) // HACK
	const [blockSettings, setBlockSettings] = createSignal(false)
	function toggleBlockSettings() {
		setBlockSettings(!blockSettings())
	}
	let blockRef: HTMLDivElement
	let editorRef: HTMLDivElement
	useBlockKeyhandlers(() => ({ blockRef, editorRef }))
	// TODO needs reactivity support:
	const pasteHandler = fake ? EventDebugger : createTreeItemPasteHandler(relVM, blkVM)
	// ? those too?
	const keyDownHandler = createTreeItemKeydownHandler(blockID, props.relationID, isExpanded, setExpanded, ag)
	const bulletIconProps = createMemo(() => {
		const likelyKids = props.hasKids || blockOrFake?.kidRelations?.length
		// HACK full object with name and scale (or even a jsx icon) could be returned from the hooks
		const iconProps = {
			name: likelyKids ? 'dot-duotone' : 'dot-bold',
			scale: 150,
			icon: undefined,
		}
		for (const eachFx of blockIconNameHooks) {
			const { name, scale, icon } = eachFx(blockOrFake, relVM, likelyKids)
			iconProps.name = name ?? iconProps.name
			iconProps.scale = scale ?? iconProps.scale
			iconProps.icon = icon ?? iconProps.icon
			if (icon) DEBUG('svgCustomIcon', { icon })
		}
		return iconProps
	})

	const [clickCount, setClickCount] = createSignal(0)
	let clickTimeout: ReturnType<typeof setTimeout> | null = null
	const DOUBLE_CLICK_DELAY = 250
	const AUTOEXPAND_DEPTH = 3

	const onExpandClick = (event: MouseEvent, long: boolean) => {
		event.preventDefault()
		VERBOSE.force({ event, onClickOrLongPress, clickCount: clickCount() })
		if (event.button === 1 || long) {
			focusViewOnBlock({ id: blockID }, locnav)
			return
		}

		// Clear existing timeout
		if (clickTimeout) clearTimeout(clickTimeout)

		// Increment click count
		const newClickCount = clickCount() + 1
		setClickCount(newClickCount)

		clickTimeout = setTimeout(() => {
			if (newClickCount > 1) {
				// Double-click: recursively expand/collapse all children
				const targetState = !isExpanded()
				setExpanded(targetState)

				// Notify children to recursively expand/collapse
				// This is done via a block context signal
				if (blockContext?.autoExpandDepth()) {
					blockContext?.setAutoExpandDepth(0)
				} else {
					blockContext?.setAutoExpandDepth(AUTOEXPAND_DEPTH)
				}
			} else {
				VERBOSE.force('// Single-click: toggle just this block')
				setExpanded(!isExpanded())
			}
			// Reset click count
			setClickCount(0)
		}, DOUBLE_CLICK_DELAY)
	}

	// Listen for recursive expand events from parent
	createEffect(() => {
		if (!blockRef) return
		const handleRecursiveExpand = (event: Event) => {
			VERBOSE.force('// Custom recursiveExpandChange ', { event })
			const customEvent = event as CustomEvent<{ shouldExpand: boolean }>
			setExpanded(customEvent.detail.shouldExpand)
		}
		blockRef.addEventListener('recursiveExpandChange', handleRecursiveExpand)
		onCleanup(() => blockRef?.removeEventListener('recursiveExpandChange', handleRecursiveExpand))
	})
	// wip prefix rendering placeholder
	const isPrefixNeeded = createMemo(() => false && blockContext?.prefix || parentContext?.prefix)
	const prefix = createMemo(() => {
		if (!isPrefixNeeded()) return ''

		if (parentVM) DEBUG('prefix', { parentVM, blkVM })
		const latestLog = blkVM.entityThread.latestLog
		const firstLog = blkVM.entityThread.firstLog
		if (!latestLog || !firstLog) {
			DEBUG('prefix: missing logs', { latestLog, firstLog })
			return ''
		}
		const latestTs = parseISO(latestLog.ts)
		const firstTs = parseISO(firstLog.ts)
		const creationPredicate = 'date:c(ddMMM<y@H\'h\')'
		const yearBeforeThis = format(firstTs, 'y') === format(new Date(), 'y') ? '2025 ' : format(firstTs, 'y ')
		const prefix = `${format(firstTs, `${yearBeforeThis}ddMMM@H'h'`)}`.toLocaleLowerCase()
		return prefix
	})

	// Check if block has content to display
	const hasContent = createMemo(() => {
		const content = blockOrFake?.content
		return content !== null && content !== undefined && (typeof content === 'string' || Object.keys(content || {}).length > 0)
	})
	// Check if block has content to display
	const isHeading = createMemo(() => {
		const content = blockOrFake?.content
		VERBOSE({ content })
		return content !== null && content !== undefined && (content.content?.[0]?.type === 'heading')
	})
	// Check if there are unknown attributes to render
	const hasUnknownAttributes = createMemo(() => {
		if (fake) return false
		const blkVM = blockOrFake as BlockVM
		return blkVM.entityThread.applogs.some(log => !KnownFullAttrs.Block.includes(log.at))
	})

	// Determine if we should show the content editor
	// Hide editor if: no contentApplog AND has unknown attributes (regardless of readOnly)
	// Otherwise show if: has content OR not readOnly
	const shouldShowContent = createMemo(() => {
		if (!fake && !(blockOrFake as BlockVM).contentApplog && hasUnknownAttributes()) return false
		if (hasContent()) return true
		if (!readOnly()) return true
		return false
	})

	return (
		<div
			class='block-content'
			data-info={fake ? `smartResult:${parentContext?.smartList?.query.source}` : `block:${blockID}`}
		>
			<div ref={blockRef} flex='~ items-start' data-block-id={blockID} class={shouldHide() ? 'opacity-60' : ''}>
				{/* <Show when={!shouldHide()}> */}
				{/* <Show when={blockOrFake.type === 'smartlist' || props.hasKids}> */}
				<div
					class='order-9 cursor-pointer color-gray-400 sm:order-0'
					flex='shrink-0'
					// onMouseDown={onExpandClick} // mousedown to not loose focus - https://stackoverflow.com/a/43627784
					use:onClickOrLongPress={onExpandClick}
				>
					<Iconify
						class={classNames([
							'color-gray-400 duration-300 transition-property-[transform]',
							isExpanded() ? ' rotate-90' : ' rotate-0',
						])}
						size='6 6'
						scale={60}
						name={props.hasKids ? 'caret-right-bold' : null}
					/>
				</div>
				{/* </Show> */}
				<BulletMenu
					class='mr-1 shrink-0'
					blockID={blockID}
					customIcon={bulletIconProps()?.icon}
					iconProps={bulletIconProps()}
					showBlockSettings={toggleBlockSettings}
					onlyShowOnHover={isHeading()}
				/>
				<Show when={isPrefixNeeded()}>
					{prefix()}
					<Iconify name='number-circle-one-fill' scale={120} />
				</Show>
				<Show when={blockOrFake.type === 'smartlist'}>
					<sl-tag
						size='small'
						variant='primary'
						onClick={() => setBlockSettings(!blockSettings())}
					>
						<Iconify name='list-magnifying-glass' scale={120} />
					</sl-tag>
					<div mx-1 h-6 opacity-40 flex='~ items-center'>
						{/* <span text-xs>{parentContext?.smartList?.query.source}</span> */}
						<Show when={parentContext?.smartList?.query.source}>
							<span text-xs title={parentContext?.smartList?.query.source} cursor-default>... AND</span>
						</Show>
					</div>
				</Show>
				<div flex='~ col grow-1 shrink-1'>
					<Show when={shouldShowContent()}>
						<EditableContent
							blockID={blockID}
							ref={editorRef}
							fake={fake ?? (isDeleted() && {
								content: htmlToTiptap('<em>deleted</em>'),
							})}
							relationID={props.relationID}
							data-hidden-tags={stringify(blockContext.hiddenTags?.() ?? null)}
							onPaste={pasteHandler}
							onKeyDown={keyDownHandler}
							readOnly={readOnly()}
							flex='grow-1 shrink-1'
							class={classNames(
								readOnly && 'cursor-pointer',
								isDeleted() && 'text-red opacity-80',
							)}
							onClick={readOnly() ? e => setExpanded(!isExpanded()) : undefined}
						/>
					</Show>
					<UnknownAttributesPills blockOrFake={blockOrFake} hasContent={hasContent()} />
				</div>
				{
					/* <Show when={blockOrFake.type === 'smartlist'}>
					<SmartListFilters kidCount={parentContext?.smartList()?.blocks.length} />
				</Show> */
				}
				<Show when={devMode()}>
					<span
						flex='inline col items-stretch'
						shrink-0
						ml-1
						font-size-2
						font-mono
						text-right
					>
						<Show when={blockID}>
							<div {...(useIdHover('block', blockID))} flex='~ items-stretch' transition='[opacity] duration-300'>
								B&thinsp;
								<DynamicColored
									text={blockID}
									class='cursor-pointer'
									title='click to copy'
									onClick={() => copyToClipboard(blockID)}
								/>
							</div>
						</Show>
						<Show when={props.relationID}>
							<div {...(useIdHover('block', props.relationID))} transition='[opacity] duration-300' flex='~ items-stretch'>
								R&thinsp;
								{' '}
								<DynamicColored
									text={props.relationID}
									class='cursor-pointer'
									title={`${JSON.stringify(pick(relVM, Object.values(REL_DEF._attrs)), undefined, 2)}\nClick to copy ID`}
									onClick={() => copyToClipboard(props.relationID)}
								/>
							</div>
							<div {...(useIdHover('block', relVM?.after))} transition='[opacity] duration-300' flex='~ items-stretch'>
								↑&thinsp;
								{' '}
								<DynamicColored
									text={relVM?.after ?? 'null'}
									class={relVM?.after && 'cursor-pointer'}
									title={relVM?.after && 'click to copy'}
									onClick={relVM?.after && (() => copyToClipboard(relVM.after))}
								/>
							</div>
						</Show>
					</span>
				</Show>
				{/* </Show> */}
			</div>

			<Show when={blockSettings()}>
				{/* HACK: prop drilling */}
				<BlockSettingsRow blockID={blockID} thread={thread} blockOrFake={blockOrFake} />
			</Show>
		</div>
	)
}

// HACK: I'm not sure how to get from dom node to Editor in another way
export const editorMap = new Map<HTMLDivElement, Editor>()

// const CustomShortcuts = Extension.create({
// 	name: 'CustomShortcuts',
// 	addKeyboardShortcuts() {
// 		return {
// 			// HACK: move default Enter behaviour to Shift-Enter (as our Enter creates a new block)
// 			'Shift-Enter': ({ editor }) =>
// 				// from: https://github.com/ueberdosis/tiptap/blob/39cf6979c49e953118bbbe4b894a1dc296128932/packages/core/src/extensions/keymap.ts#L49C25-L54C7
// 				editor.commands.first(({ commands }) => [
// 					() => commands.newlineInCode(),
// 					() => commands.createParagraphNear(),
// 					() => commands.liftEmptyBlock(),
// 					() => commands.splitBlock(),
// 				]),
// 		}
// 	},
// })

export const EditableContent: Component<
	{
		readOnly?: boolean
		blockID: EntityID | null
		fake?: FakeBlock
		relationID: EntityID | null
		// blockVM: BlockVM
		// block: typeof BLOCK
		// vintageContent: Accessor<string>
		onKeyDown: (evt: React.KeyboardEvent<HTMLDivElement>) => Promise<void>
		onPaste: (view: EditorView, event: ClipboardEvent, slice: Slice) => boolean | void
	} & JSX.HTMLAttributes<HTMLSpanElement>
> = (fullProps) => {
	const [props, otherProps] = splitProps(fullProps, ['readOnly', 'blockID', 'relationID', 'fake', 'onKeyDown', 'onPaste']) // (i) this function is still not properly reactive to those, though
	const readOnly = createMemo(() => props.readOnly || useReadOnlyState()())
	VERBOSE(`<EditableContent#${props.blockID}> created`, { props, otherProps, ro: readOnly(), ds: useCurrentThread() })
	const blockVM = props.fake ?? useBlockVM(props.blockID)
	const [isEditing, setEditing] = createSignal(null)
	const [selectionLink, setSelectionLink] = createSignal<{ href: string } | null>(null)

	let ref!: HTMLDivElement
	// let menuRef!: HTMLDivElement
	Image.configure({
		allowBase64: true,
	})
	const initialContent = untracked(() => blockVM.content)

	VERBOSE(`[Content#${props.blockID}] initial`, initialContent)

	const fakeContent = props.fake?.content && (typeof props.fake.content === 'string'
		? plaintextStringToTiptap(props.fake.content)
		: props.fake.content)
	// TODO decorate or somehow hide the outer (and maybe recursive) smartlist tokens
	// ... somehow based on parent block context isTokenHidden prop of smartlist
	const blockContext = useBlockContext()
	const parentContext = blockContext?.parentContext

	const extensionsMemo = createMemo(() => {
		const extensions = [
			...baseExtensions,
			Image,
			// CustomShortcuts,
			TagHighlightExtension,
			WordInfoExtension,
			CustomSelectionHandler(selectionLink, setSelectionLink),
			// BubbleMenu.configure({
			// 	element: menuRef!,
			// }),
			...activePlugins.flatMap(p => p.tiptapExtensions),
		]

		const hiddenTags = parentContext?.hiddenTags()
		if (hiddenTags) {
			VERBOSE('CreateTokenHidingExtension', { hiddenTags })
			extensions.push(CreateTokenHidingExtension(hiddenTags))
		}
		return extensions
	})

	const editor = createTiptapEditor(() => {
		const editorProps = {
			element: ref!,
			editable: !readOnly(),
			editorProps: {
				attributes: {
					//   class: 'prose prose-sm sm:prose lg:prose-lg xl:prose-2xl mx-auto focus:outline-none',
					class: 'focus:outline-none -my-3',
				},
				// handlePaste: CustomStackedPasteHandler([props.onPaste /* () => LOG('handler1'), () => LOG('handler2') */]),
				handlePaste: props.onPaste,
			},
			extensions: extensionsMemo(),
			content: fakeContent ?? initialContent,
		}
		DEBUG(`<EditableContent.tiptap> props`, editorProps)
		return editorProps
	})
	onMount(() => {
		editorMap.set(ref!, editor())
	})
	onCleanup(() => {
		editorMap.delete(ref!)
	})

	const contentJson = useEditorJSON(editor)
	if (!readOnly()) {
		createEffect(on(() => contentJson(), (newContent) => {
			DEBUG(`[EditableContent#${props.blockID}].htmlUpdate]`, newContent, { blockVM })

			if (!compareBlockContent(newContent, blockVM.content)) {
				DEBUG('[EditableContent#${props.blockID}].htmlUpdate] changed', { old: blockVM.content, new: newContent /* , parsed  */ })

				blockVM.content = newContent // this is a json object

				setEditing(setTimeout(() => setEditing(null), 1000)) // HACK: unify with BlockVM.isEditing
				// persistBlockContent(block.en, spanText)
			}
		}, { defer: true })) // (i) still called on first load (I think)
	}
	createEffect(on(() => blockVM.content, (newContent) => {
		DEBUG(`[EditableContent#${props.blockID}] newContent:`, newContent, { ref, isEditing: blockVM.isEditing })
		if (!isEditing()) {
			if (!compareBlockContent(contentJson(), newContent)) {
				editor().commands.setContent(newContent)
			}
		}
	}, { defer: true })) // don't call on first load

	const handleLinkButton = async (_evt: PointerEvent) => {
		if (editor().isActive('link')) {
			DEBUG('removing', editor().getAttributes('link').href)
			// (i) tries to extend to
			editor().chain().focus().extendMarkRange('link').unsetLink().run()
			setSelectionLink(null)
		} else {
			const maybeValidURL = await getValidURLFromClipboard()
			if (maybeValidURL) {
				VERBOSE('setting link in tiptap', maybeValidURL)
				editor().chain().focus().toggleLink({ href: maybeValidURL }).run()
				setSelectionLink({ href: maybeValidURL })
				// ? i guess focus is needed cuz the mini menu steals it on click ?
			} else {
				WARN('Link Button clicked but no link found', await getPlainTextFromClipboard())
			}
		}
	}
	if (props.fake) VERBOSE('fake:', props.fake)
	return (
		<>
			<div
				{...otherProps}
				onKeyDown={props.onKeyDown}
				ref={ref}
				id={props.fake
					? `fake:${props.fake.smartQuery?.text ?? tiptapToPlaintext(props.fake.content)
						/* ?? props.fake.content?.content?.[0]?.content?.[0]?.text */
					}`
					: `block-${blockVM.en}`}
				flex='grow-1'
				min-w-0
			/>
			<TiptapMenuWrapper
				editor={editor()}
				tippyOptions={{ duration: 300 }}
			>
				{/* shouldShow={({ editor, state, view, from, to }) => { }} */}
				{/* FIXME: tooltips not shown */}
				<sl-button-group
					label='Formatting'
					flex
					drop-shadow-lg
					style={{ 'background-color': 'hsl(240 5% 27.6%)', 'border': '1px solid hsl(240 5% 27.6%)', 'border-radius': '0.25rem' }}
				>
					<sl-tooltip content='Bold'>
						<sl-button size='small' onClick={() => editor().chain().focus().toggleBold().run()}>
							<Iconify slot='prefix' scale={140} name='text-b-bold' />
						</sl-button>
					</sl-tooltip>
					<sl-tooltip content='Italic'>
						<sl-button size='small' onClick={() => editor().chain().focus().toggleItalic().run()}>
							<Iconify slot='prefix' scale={140} name='text-italic-bold' />
						</sl-button>
					</sl-tooltip>
					<sl-tooltip content='Underline'>
						<sl-button size='small' onClick={() => editor().chain().focus().toggleUnderline().run()}>
							<Iconify slot='prefix' scale={140} name='text-underline-bold' />
						</sl-button>
					</sl-tooltip>
					<sl-tooltip content='Strikethrough'>
						<sl-button size='small' onClick={() => editor().chain().focus().toggleStrike().run()}>
							<Iconify slot='prefix' scale={140} name='text-strikethrough-bold' />
						</sl-button>
					</sl-tooltip>
					<sl-tooltip content='Regular style'>
						<sl-button size='small' onClick={() => editor().chain().focus().unsetAllMarks().run()}>
							<Iconify slot='prefix' scale={140} name='eraser-fill' />
						</sl-button>
					</sl-tooltip>
					<div h-full style={{ 'background-color': 'hsl(240 5% 27.6%)', 'width': '2px' }}>
						{/* divider */}
					</div>

					<Show when={!selectionLink()}>
						<sl-tooltip content='Link'>
							<sl-button size='small' onClick={handleLinkButton}>
								<Iconify slot='prefix' scale={140} name='link-bold' />
							</sl-button>
						</sl-tooltip>
					</Show>
					<Show when={selectionLink()}>
						<sl-tooltip content={`Remove link: ${selectionLink().href}`}>
							<sl-button size='small' onClick={handleLinkButton}>
								<Iconify slot='prefix' scale={140} name='link-break-bold' />
							</sl-button>
						</sl-tooltip>
						<a href={selectionLink().href} target='_blank'>
							{/* <sl-tooltip content={'Open: ' + selectionLink().href}> */}
							<sl-button size='small'>
								{/* onClick={() => window.open(selectionLink().href, '_blank')} */}
								<Iconify slot='prefix' scale={140} name='arrow-square-out' />
							</sl-button>
						</a>
						{/* </sl-tooltip> */}
					</Show>
				</sl-button-group>
			</TiptapMenuWrapper>
		</>
	)
	// return (
	// 	<span
	// 		{...otherProps}
	// 		focus-outline-none
	// 		role='textbox'
	// 		onBlur={onBlur}
	// 		onKeyUp={onType}
	// 		onKeyDown={local.onKeyDown}
	// 		onPaste={local.onPaste}
	// 		contenteditable
	// 		p-1
	// 		cursor-text
	// 		min-w-16
	// 		ref={contentElem}
	// 		id={'block-' + blockVM.enID}
	// 		break-all // TODO: should be only break-words, but doesn't work
	// 		hyphens-auto
	// 	>
	// 		{/* w-full w-min-content */}
	// 		{untracked(() => blockVM.content)}
	// 	</span>
	// )
}
