From 9274ea81a10a8b7b82aa91a1590f244d487469bc Mon Sep 17 00:00:00 2001 From: urjitc <135136842+urjitc@users.noreply.github.com> Date: Sun, 26 Apr 2026 05:40:05 +0000 Subject: [PATCH 1/3] Fix TipTap toolbar state reactivity with useEditorState selectors Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- .../blockquote-button/use-blockquote.ts | 48 +++++++------ .../code-block-button/use-code-block.ts | 52 ++++++++------ .../use-color-highlight.ts | 51 +++++++++----- .../tiptap-ui/heading-button/use-heading.ts | 53 ++++++++------ .../use-heading-dropdown-menu.ts | 58 +++++++++------- .../tiptap-ui/list-button/use-list.ts | 47 ++++++++----- .../use-list-dropdown-menu.ts | 69 +++++++++++-------- .../tiptap-ui/mark-button/use-mark.ts | 49 +++++++------ .../text-align-button/use-text-align.ts | 49 +++++++------ src/hooks/use-tiptap-editor.ts | 27 +------- 10 files changed, 289 insertions(+), 214 deletions(-) diff --git a/src/components/tiptap-ui/blockquote-button/use-blockquote.ts b/src/components/tiptap-ui/blockquote-button/use-blockquote.ts index 1a892ef5..f78f1bc1 100644 --- a/src/components/tiptap-ui/blockquote-button/use-blockquote.ts +++ b/src/components/tiptap-ui/blockquote-button/use-blockquote.ts @@ -1,7 +1,8 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback } from "react" import type { Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" import { NodeSelection, TextSelection } from "@tiptap/pm/state" // --- Hooks --- @@ -231,25 +232,32 @@ export function useBlockquote(config?: UseBlockquoteConfig) { } = config || {} const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) - const canToggle = canToggleBlockquote(editor) - const isActive = editor?.isActive("blockquote") || false - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible(shouldShowButton({ editor, hideWhenUnavailable })) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) - - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, hideWhenUnavailable]) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + isActive: false, + canToggle: false, + } + } + + return { + isVisible: shouldShowButton({ + editor: currentEditor, + hideWhenUnavailable, + }), + isActive: currentEditor.isActive("blockquote"), + canToggle: canToggleBlockquote(currentEditor), + } + }, + }) ?? { + isVisible: true, + isActive: false, + canToggle: false, + } + const { isVisible, isActive, canToggle } = toolbarState const handleToggle = useCallback(() => { if (!editor) return false diff --git a/src/components/tiptap-ui/code-block-button/use-code-block.ts b/src/components/tiptap-ui/code-block-button/use-code-block.ts index 8cbf401a..91728785 100644 --- a/src/components/tiptap-ui/code-block-button/use-code-block.ts +++ b/src/components/tiptap-ui/code-block-button/use-code-block.ts @@ -1,7 +1,8 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback } from "react" import { type Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" import { NodeSelection, TextSelection } from "@tiptap/pm/state" // --- Hooks --- @@ -241,25 +242,36 @@ export function useCodeBlock(config?: UseCodeBlockConfig) { } = config || {} const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) - const canToggleState = canToggle(editor) - const isActive = editor?.isActive("codeBlock") || false - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible(shouldShowButton({ editor, hideWhenUnavailable })) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) - - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, hideWhenUnavailable]) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + isActive: false, + canToggle: false, + } + } + + return { + isVisible: shouldShowButton({ + editor: currentEditor, + hideWhenUnavailable, + }), + isActive: currentEditor.isActive("codeBlock"), + canToggle: canToggle(currentEditor), + } + }, + }) ?? { + isVisible: true, + isActive: false, + canToggle: false, + } + const { + isVisible, + isActive, + canToggle: canToggleState, + } = toolbarState const handleToggle = useCallback(() => { if (!editor) return false diff --git a/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts b/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts index e2cb5f0c..785013cf 100644 --- a/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts +++ b/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts @@ -1,7 +1,8 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback } from "react" import { type Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" import { useHotkeys } from "react-hotkeys-hook" // --- Hooks --- @@ -293,28 +294,40 @@ export function useColorHighlight(config: UseColorHighlightConfig) { const { editor } = useTiptapEditor(providedEditor) const isMobile = useIsBreakpoint() - const [isVisible, setIsVisible] = useState(true) - const canColorHighlightState = canColorHighlight(editor, mode) const actualColor = highlightColor ? getHighlightColorValue(highlightColor, useColorValue) : highlightColor - const isActive = isColorHighlightActive(editor, actualColor, mode) - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible(shouldShowButton({ editor, hideWhenUnavailable, mode })) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + isActive: false, + canColorHighlight: false, + } + } - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, hideWhenUnavailable, mode]) + return { + isVisible: shouldShowButton({ + editor: currentEditor, + hideWhenUnavailable, + mode, + }), + isActive: isColorHighlightActive(currentEditor, actualColor, mode), + canColorHighlight: canColorHighlight(currentEditor, mode), + } + }, + }) ?? { + isVisible: true, + isActive: false, + canColorHighlight: false, + } + const { + isVisible, + isActive, + canColorHighlight: canColorHighlightState, + } = toolbarState const handleColorHighlight = useCallback(() => { if (!editor || !canColorHighlightState || !actualColor || !label) diff --git a/src/components/tiptap-ui/heading-button/use-heading.ts b/src/components/tiptap-ui/heading-button/use-heading.ts index ba843f62..0fbb6049 100644 --- a/src/components/tiptap-ui/heading-button/use-heading.ts +++ b/src/components/tiptap-ui/heading-button/use-heading.ts @@ -1,7 +1,8 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback } from "react" import { type Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" import { NodeSelection, TextSelection } from "@tiptap/pm/state" // --- Hooks --- @@ -301,25 +302,37 @@ export function useHeading(config: UseHeadingConfig) { } = config const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) - const canToggleState = canToggle(editor, level) - const isActive = isHeadingActive(editor, level) - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible(shouldShowButton({ editor, level, hideWhenUnavailable })) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) - - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, level, hideWhenUnavailable]) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + isActive: false, + canToggle: false, + } + } + + return { + isVisible: shouldShowButton({ + editor: currentEditor, + level, + hideWhenUnavailable, + }), + isActive: isHeadingActive(currentEditor, level), + canToggle: canToggle(currentEditor, level), + } + }, + }) ?? { + isVisible: true, + isActive: false, + canToggle: false, + } + const { + isVisible, + isActive, + canToggle: canToggleState, + } = toolbarState const handleToggle = useCallback(() => { if (!editor) return false diff --git a/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts b/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts index 5bd2db3d..31f60444 100644 --- a/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts +++ b/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts @@ -1,7 +1,7 @@ "use client" -import { useEffect, useState } from "react" import type { Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" // --- Hooks --- import { useTiptapEditor } from "@/hooks/use-tiptap-editor" @@ -96,29 +96,41 @@ export function useHeadingDropdownMenu(config?: UseHeadingDropdownMenuConfig) { } = config || {} const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + activeLevel: undefined, + isActive: false, + canToggle: false, + } + } - const activeLevel = getActiveHeadingLevel(editor, levels) - const isActive = isHeadingActive(editor) - const canToggleState = canToggle(editor) - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible( - shouldShowButton({ editor, hideWhenUnavailable, level: levels }) - ) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) - - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, hideWhenUnavailable, levels]) + return { + isVisible: shouldShowButton({ + editor: currentEditor, + hideWhenUnavailable, + level: levels, + }), + activeLevel: getActiveHeadingLevel(currentEditor, levels), + isActive: isHeadingActive(currentEditor, levels), + canToggle: canToggle(currentEditor), + } + }, + }) ?? { + isVisible: true, + activeLevel: undefined, + isActive: false, + canToggle: false, + } + const { + isVisible, + activeLevel, + isActive, + canToggle: canToggleState, + } = toolbarState return { isVisible, diff --git a/src/components/tiptap-ui/list-button/use-list.ts b/src/components/tiptap-ui/list-button/use-list.ts index 9007f9cd..b9966d91 100644 --- a/src/components/tiptap-ui/list-button/use-list.ts +++ b/src/components/tiptap-ui/list-button/use-list.ts @@ -1,7 +1,8 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback } from "react" import { type Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" import { NodeSelection, TextSelection } from "@tiptap/pm/state" // --- Hooks --- @@ -309,25 +310,33 @@ export function useList(config: UseListConfig) { } = config const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) - const canToggle = canToggleList(editor, type) - const isActive = isListActive(editor, type) - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible(shouldShowButton({ editor, type, hideWhenUnavailable })) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + isActive: false, + canToggle: false, + } + } - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, type, hideWhenUnavailable]) + return { + isVisible: shouldShowButton({ + editor: currentEditor, + type, + hideWhenUnavailable, + }), + isActive: isListActive(currentEditor, type), + canToggle: canToggleList(currentEditor, type), + } + }, + }) ?? { + isVisible: true, + isActive: false, + canToggle: false, + } + const { isVisible, isActive, canToggle } = toolbarState const handleToggle = useCallback(() => { if (!editor) return false diff --git a/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts b/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts index d9b9d73e..8429e432 100644 --- a/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts +++ b/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts @@ -1,7 +1,8 @@ "use client" -import { useEffect, useMemo, useState } from "react" +import { useMemo } from "react" import type { Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" // --- Hooks --- import { useTiptapEditor } from "@/hooks/use-tiptap-editor" @@ -170,40 +171,50 @@ export function useListDropdownMenu(config?: UseListDropdownMenuConfig) { } = config || {} const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) - - const listInSchema = types.some((type) => isNodeInSchema(type, editor)) - const filteredLists = useMemo(() => getFilteredListOptions(types), [types]) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + canToggleAny: false, + isAnyActive: false, + activeType: undefined, + } + } + + const listInSchema = types.some((type) => + isNodeInSchema(type, currentEditor) + ) + const canToggleAny = canToggleAnyList(currentEditor, types) - const canToggleAny = canToggleAnyList(editor, types) - const isAnyActive = isAnyListActive(editor, types) - const activeType = getActiveListType(editor, types) - const activeList = filteredLists.find((option) => option.type === activeType) - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible( - shouldShowListDropdown({ - editor, + return { + isVisible: shouldShowListDropdown({ + editor: currentEditor, listTypes: types, hideWhenUnavailable, listInSchema, canToggleAny, - }) - ) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) - - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [canToggleAny, editor, hideWhenUnavailable, listInSchema, types]) + }), + canToggleAny, + isAnyActive: isAnyListActive(currentEditor, types), + activeType: getActiveListType(currentEditor, types), + } + }, + }) ?? { + isVisible: true, + canToggleAny: false, + isAnyActive: false, + activeType: undefined, + } + const { + isVisible, + canToggleAny, + isAnyActive, + activeType, + } = toolbarState + const activeList = filteredLists.find((option) => option.type === activeType) return { isVisible, diff --git a/src/components/tiptap-ui/mark-button/use-mark.ts b/src/components/tiptap-ui/mark-button/use-mark.ts index 64655e8d..8ee9e88c 100644 --- a/src/components/tiptap-ui/mark-button/use-mark.ts +++ b/src/components/tiptap-ui/mark-button/use-mark.ts @@ -1,7 +1,8 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback } from "react" import type { Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" // --- Hooks --- import { useTiptapEditor } from "@/hooks/use-tiptap-editor" @@ -173,25 +174,33 @@ export function useMark(config: UseMarkConfig) { } = config const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) - const canToggle = canToggleMark(editor, type) - const isActive = isMarkActive(editor, type) - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible(shouldShowButton({ editor, type, hideWhenUnavailable })) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) - - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, type, hideWhenUnavailable]) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + isActive: false, + canToggle: false, + } + } + + return { + isVisible: shouldShowButton({ + editor: currentEditor, + type, + hideWhenUnavailable, + }), + isActive: isMarkActive(currentEditor, type), + canToggle: canToggleMark(currentEditor, type), + } + }, + }) ?? { + isVisible: true, + isActive: false, + canToggle: false, + } + const { isVisible, isActive, canToggle } = toolbarState const handleMark = useCallback(() => { if (!editor) return false diff --git a/src/components/tiptap-ui/text-align-button/use-text-align.ts b/src/components/tiptap-ui/text-align-button/use-text-align.ts index 05fd4f93..93dd950d 100644 --- a/src/components/tiptap-ui/text-align-button/use-text-align.ts +++ b/src/components/tiptap-ui/text-align-button/use-text-align.ts @@ -1,8 +1,9 @@ "use client" -import { useCallback, useEffect, useState } from "react" +import { useCallback } from "react" import type { ChainedCommands } from "@tiptap/react" import { type Editor } from "@tiptap/react" +import { useEditorState } from "@tiptap/react" // --- Hooks --- import { useTiptapEditor } from "@/hooks/use-tiptap-editor" @@ -186,25 +187,33 @@ export function useTextAlign(config: UseTextAlignConfig) { } = config const { editor } = useTiptapEditor(providedEditor) - const [isVisible, setIsVisible] = useState(true) - const canAlign = canSetTextAlign(editor, align) - const isActive = isTextAlignActive(editor, align) - - useEffect(() => { - if (!editor) return - - const handleSelectionUpdate = () => { - setIsVisible(shouldShowButton({ editor, align, hideWhenUnavailable })) - } - - handleSelectionUpdate() - - editor.on("selectionUpdate", handleSelectionUpdate) - - return () => { - editor.off("selectionUpdate", handleSelectionUpdate) - } - }, [editor, hideWhenUnavailable, align]) + const toolbarState = useEditorState({ + editor, + selector: ({ editor: currentEditor }) => { + if (!currentEditor) { + return { + isVisible: true, + isActive: false, + canAlign: false, + } + } + + return { + isVisible: shouldShowButton({ + editor: currentEditor, + align, + hideWhenUnavailable, + }), + isActive: isTextAlignActive(currentEditor, align), + canAlign: canSetTextAlign(currentEditor, align), + } + }, + }) ?? { + isVisible: true, + isActive: false, + canAlign: false, + } + const { isVisible, isActive, canAlign } = toolbarState const handleTextAlign = useCallback(() => { if (!editor) return false diff --git a/src/hooks/use-tiptap-editor.ts b/src/hooks/use-tiptap-editor.ts index 6ec37d26..5636e6f4 100644 --- a/src/hooks/use-tiptap-editor.ts +++ b/src/hooks/use-tiptap-editor.ts @@ -1,7 +1,7 @@ "use client" import type { Editor } from "@tiptap/react" -import { useCurrentEditor, useEditorState } from "@tiptap/react" +import { useCurrentEditor } from "@tiptap/react" import { useMemo } from "react" /** @@ -17,33 +17,12 @@ import { useMemo } from "react" */ export function useTiptapEditor(providedEditor?: Editor | null): { editor: Editor | null - editorState?: Editor["state"] - canCommand?: Editor["can"] } { const { editor: coreEditor } = useCurrentEditor() - const mainEditor = useMemo( + const editor = useMemo( () => providedEditor || coreEditor, [providedEditor, coreEditor] ) - const editorState = useEditorState({ - editor: mainEditor, - selector(context) { - if (!context.editor) { - return { - editor: null, - editorState: undefined, - canCommand: undefined, - } - } - - return { - editor: context.editor, - editorState: context.editor.state, - canCommand: context.editor.can, - } - }, - }) - - return editorState || { editor: null } + return { editor: editor || null } } From 3364549ea506df3886a046363c2638c7772a6b7a Mon Sep 17 00:00:00 2001 From: urjitc <135136842+urjitc@users.noreply.github.com> Date: Sun, 26 Apr 2026 05:49:44 +0000 Subject: [PATCH 2/3] Clean up TipTap toolbar state fallbacks Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- .../tiptap-ui/blockquote-button/use-blockquote.ts | 6 +----- .../tiptap-ui/code-block-button/use-code-block.ts | 6 +----- .../color-highlight-button/use-color-highlight.ts | 6 +----- src/components/tiptap-ui/heading-button/use-heading.ts | 6 +----- .../heading-dropdown-menu/use-heading-dropdown-menu.ts | 7 +------ src/components/tiptap-ui/list-button/use-list.ts | 6 +----- .../tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts | 7 +------ src/components/tiptap-ui/mark-button/use-mark.ts | 6 +----- .../tiptap-ui/text-align-button/use-text-align.ts | 6 +----- 9 files changed, 9 insertions(+), 47 deletions(-) diff --git a/src/components/tiptap-ui/blockquote-button/use-blockquote.ts b/src/components/tiptap-ui/blockquote-button/use-blockquote.ts index f78f1bc1..8413a899 100644 --- a/src/components/tiptap-ui/blockquote-button/use-blockquote.ts +++ b/src/components/tiptap-ui/blockquote-button/use-blockquote.ts @@ -252,11 +252,7 @@ export function useBlockquote(config?: UseBlockquoteConfig) { canToggle: canToggleBlockquote(currentEditor), } }, - }) ?? { - isVisible: true, - isActive: false, - canToggle: false, - } + }) const { isVisible, isActive, canToggle } = toolbarState const handleToggle = useCallback(() => { diff --git a/src/components/tiptap-ui/code-block-button/use-code-block.ts b/src/components/tiptap-ui/code-block-button/use-code-block.ts index 91728785..50c5e96a 100644 --- a/src/components/tiptap-ui/code-block-button/use-code-block.ts +++ b/src/components/tiptap-ui/code-block-button/use-code-block.ts @@ -262,11 +262,7 @@ export function useCodeBlock(config?: UseCodeBlockConfig) { canToggle: canToggle(currentEditor), } }, - }) ?? { - isVisible: true, - isActive: false, - canToggle: false, - } + }) const { isVisible, isActive, diff --git a/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts b/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts index 785013cf..1f5f8ec4 100644 --- a/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts +++ b/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts @@ -318,11 +318,7 @@ export function useColorHighlight(config: UseColorHighlightConfig) { canColorHighlight: canColorHighlight(currentEditor, mode), } }, - }) ?? { - isVisible: true, - isActive: false, - canColorHighlight: false, - } + }) const { isVisible, isActive, diff --git a/src/components/tiptap-ui/heading-button/use-heading.ts b/src/components/tiptap-ui/heading-button/use-heading.ts index 0fbb6049..168f154a 100644 --- a/src/components/tiptap-ui/heading-button/use-heading.ts +++ b/src/components/tiptap-ui/heading-button/use-heading.ts @@ -323,11 +323,7 @@ export function useHeading(config: UseHeadingConfig) { canToggle: canToggle(currentEditor, level), } }, - }) ?? { - isVisible: true, - isActive: false, - canToggle: false, - } + }) const { isVisible, isActive, diff --git a/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts b/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts index 31f60444..5e6f8313 100644 --- a/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts +++ b/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts @@ -119,12 +119,7 @@ export function useHeadingDropdownMenu(config?: UseHeadingDropdownMenuConfig) { canToggle: canToggle(currentEditor), } }, - }) ?? { - isVisible: true, - activeLevel: undefined, - isActive: false, - canToggle: false, - } + }) const { isVisible, activeLevel, diff --git a/src/components/tiptap-ui/list-button/use-list.ts b/src/components/tiptap-ui/list-button/use-list.ts index b9966d91..23f0c52d 100644 --- a/src/components/tiptap-ui/list-button/use-list.ts +++ b/src/components/tiptap-ui/list-button/use-list.ts @@ -331,11 +331,7 @@ export function useList(config: UseListConfig) { canToggle: canToggleList(currentEditor, type), } }, - }) ?? { - isVisible: true, - isActive: false, - canToggle: false, - } + }) const { isVisible, isActive, canToggle } = toolbarState const handleToggle = useCallback(() => { diff --git a/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts b/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts index 8429e432..59805d97 100644 --- a/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts +++ b/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts @@ -202,12 +202,7 @@ export function useListDropdownMenu(config?: UseListDropdownMenuConfig) { activeType: getActiveListType(currentEditor, types), } }, - }) ?? { - isVisible: true, - canToggleAny: false, - isAnyActive: false, - activeType: undefined, - } + }) const { isVisible, canToggleAny, diff --git a/src/components/tiptap-ui/mark-button/use-mark.ts b/src/components/tiptap-ui/mark-button/use-mark.ts index 8ee9e88c..0a723b6d 100644 --- a/src/components/tiptap-ui/mark-button/use-mark.ts +++ b/src/components/tiptap-ui/mark-button/use-mark.ts @@ -195,11 +195,7 @@ export function useMark(config: UseMarkConfig) { canToggle: canToggleMark(currentEditor, type), } }, - }) ?? { - isVisible: true, - isActive: false, - canToggle: false, - } + }) const { isVisible, isActive, canToggle } = toolbarState const handleMark = useCallback(() => { diff --git a/src/components/tiptap-ui/text-align-button/use-text-align.ts b/src/components/tiptap-ui/text-align-button/use-text-align.ts index 93dd950d..aab6dbc4 100644 --- a/src/components/tiptap-ui/text-align-button/use-text-align.ts +++ b/src/components/tiptap-ui/text-align-button/use-text-align.ts @@ -208,11 +208,7 @@ export function useTextAlign(config: UseTextAlignConfig) { canAlign: canSetTextAlign(currentEditor, align), } }, - }) ?? { - isVisible: true, - isActive: false, - canAlign: false, - } + }) const { isVisible, isActive, canAlign } = toolbarState const handleTextAlign = useCallback(() => { From 8c897764d53595c8c5b6fd332268fa521ff6434a Mon Sep 17 00:00:00 2001 From: urjitc <135136842+urjitc@users.noreply.github.com> Date: Sun, 26 Apr 2026 05:55:52 +0000 Subject: [PATCH 3/3] Fix toolbar state cleanup typing Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --- src/components/tiptap-ui/blockquote-button/use-blockquote.ts | 2 +- src/components/tiptap-ui/code-block-button/use-code-block.ts | 2 +- .../tiptap-ui/color-highlight-button/use-color-highlight.ts | 2 +- src/components/tiptap-ui/heading-button/use-heading.ts | 2 +- .../heading-dropdown-menu/use-heading-dropdown-menu.ts | 2 +- src/components/tiptap-ui/list-button/use-list.ts | 2 +- .../tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts | 2 +- src/components/tiptap-ui/mark-button/use-mark.ts | 2 +- src/components/tiptap-ui/text-align-button/use-text-align.ts | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/components/tiptap-ui/blockquote-button/use-blockquote.ts b/src/components/tiptap-ui/blockquote-button/use-blockquote.ts index 8413a899..6e21708f 100644 --- a/src/components/tiptap-ui/blockquote-button/use-blockquote.ts +++ b/src/components/tiptap-ui/blockquote-button/use-blockquote.ts @@ -252,7 +252,7 @@ export function useBlockquote(config?: UseBlockquoteConfig) { canToggle: canToggleBlockquote(currentEditor), } }, - }) + })! const { isVisible, isActive, canToggle } = toolbarState const handleToggle = useCallback(() => { diff --git a/src/components/tiptap-ui/code-block-button/use-code-block.ts b/src/components/tiptap-ui/code-block-button/use-code-block.ts index 50c5e96a..a0d7fe25 100644 --- a/src/components/tiptap-ui/code-block-button/use-code-block.ts +++ b/src/components/tiptap-ui/code-block-button/use-code-block.ts @@ -262,7 +262,7 @@ export function useCodeBlock(config?: UseCodeBlockConfig) { canToggle: canToggle(currentEditor), } }, - }) + })! const { isVisible, isActive, diff --git a/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts b/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts index 1f5f8ec4..babf2fec 100644 --- a/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts +++ b/src/components/tiptap-ui/color-highlight-button/use-color-highlight.ts @@ -318,7 +318,7 @@ export function useColorHighlight(config: UseColorHighlightConfig) { canColorHighlight: canColorHighlight(currentEditor, mode), } }, - }) + })! const { isVisible, isActive, diff --git a/src/components/tiptap-ui/heading-button/use-heading.ts b/src/components/tiptap-ui/heading-button/use-heading.ts index 168f154a..1ada1db5 100644 --- a/src/components/tiptap-ui/heading-button/use-heading.ts +++ b/src/components/tiptap-ui/heading-button/use-heading.ts @@ -323,7 +323,7 @@ export function useHeading(config: UseHeadingConfig) { canToggle: canToggle(currentEditor, level), } }, - }) + })! const { isVisible, isActive, diff --git a/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts b/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts index 5e6f8313..a73e2866 100644 --- a/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts +++ b/src/components/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts @@ -119,7 +119,7 @@ export function useHeadingDropdownMenu(config?: UseHeadingDropdownMenuConfig) { canToggle: canToggle(currentEditor), } }, - }) + })! const { isVisible, activeLevel, diff --git a/src/components/tiptap-ui/list-button/use-list.ts b/src/components/tiptap-ui/list-button/use-list.ts index 23f0c52d..2c2a1936 100644 --- a/src/components/tiptap-ui/list-button/use-list.ts +++ b/src/components/tiptap-ui/list-button/use-list.ts @@ -331,7 +331,7 @@ export function useList(config: UseListConfig) { canToggle: canToggleList(currentEditor, type), } }, - }) + })! const { isVisible, isActive, canToggle } = toolbarState const handleToggle = useCallback(() => { diff --git a/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts b/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts index 59805d97..73d59730 100644 --- a/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts +++ b/src/components/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts @@ -202,7 +202,7 @@ export function useListDropdownMenu(config?: UseListDropdownMenuConfig) { activeType: getActiveListType(currentEditor, types), } }, - }) + })! const { isVisible, canToggleAny, diff --git a/src/components/tiptap-ui/mark-button/use-mark.ts b/src/components/tiptap-ui/mark-button/use-mark.ts index 0a723b6d..30f3e323 100644 --- a/src/components/tiptap-ui/mark-button/use-mark.ts +++ b/src/components/tiptap-ui/mark-button/use-mark.ts @@ -195,7 +195,7 @@ export function useMark(config: UseMarkConfig) { canToggle: canToggleMark(currentEditor, type), } }, - }) + })! const { isVisible, isActive, canToggle } = toolbarState const handleMark = useCallback(() => { diff --git a/src/components/tiptap-ui/text-align-button/use-text-align.ts b/src/components/tiptap-ui/text-align-button/use-text-align.ts index aab6dbc4..75de5310 100644 --- a/src/components/tiptap-ui/text-align-button/use-text-align.ts +++ b/src/components/tiptap-ui/text-align-button/use-text-align.ts @@ -208,7 +208,7 @@ export function useTextAlign(config: UseTextAlignConfig) { canAlign: canSetTextAlign(currentEditor, align), } }, - }) + })! const { isVisible, isActive, canAlign } = toolbarState const handleTextAlign = useCallback(() => {