@betterstart/cli 0.1.2 → 0.1.4
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/README.md +133 -0
- package/dist/cli.d.ts +1 -9
- package/dist/cli.js +13484 -354
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +24 -260
- package/dist/index.js +4 -11373
- package/dist/index.js.map +1 -1
- package/package.json +29 -42
- package/templates/schema.json +959 -0
- package/templates/tiptap/hooks/use-composed-ref.ts +43 -0
- package/templates/tiptap/hooks/use-cursor-visibility.ts +68 -0
- package/templates/tiptap/hooks/use-element-rect.ts +166 -0
- package/templates/tiptap/hooks/use-is-breakpoint.ts +32 -0
- package/templates/tiptap/hooks/use-menu-navigation.ts +182 -0
- package/templates/tiptap/hooks/use-scrolling.ts +64 -0
- package/templates/tiptap/hooks/use-throttled-callback.ts +146 -0
- package/templates/tiptap/hooks/use-tiptap-editor.ts +46 -0
- package/templates/tiptap/hooks/use-unmount.ts +21 -0
- package/templates/tiptap/hooks/use-window-size.ts +87 -0
- package/templates/tiptap/lib/tiptap-utils.ts +587 -0
- package/templates/tiptap/styles/_keyframe-animations.scss +91 -0
- package/templates/tiptap/styles/_variables.scss +296 -0
- package/templates/tiptap/tiptap-extension/node-background-extension.ts +138 -0
- package/templates/tiptap/tiptap-icons/align-center-icon.tsx +38 -0
- package/templates/tiptap/tiptap-icons/align-justify-icon.tsx +38 -0
- package/templates/tiptap/tiptap-icons/align-left-icon.tsx +38 -0
- package/templates/tiptap/tiptap-icons/align-right-icon.tsx +38 -0
- package/templates/tiptap/tiptap-icons/arrow-left-icon.tsx +24 -0
- package/templates/tiptap/tiptap-icons/ban-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/blockquote-icon.tsx +44 -0
- package/templates/tiptap/tiptap-icons/bold-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/chevron-down-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/close-icon.tsx +24 -0
- package/templates/tiptap/tiptap-icons/code-block-icon.tsx +38 -0
- package/templates/tiptap/tiptap-icons/code2-icon.tsx +32 -0
- package/templates/tiptap/tiptap-icons/corner-down-left-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/external-link-icon.tsx +28 -0
- package/templates/tiptap/tiptap-icons/heading-five-icon.tsx +28 -0
- package/templates/tiptap/tiptap-icons/heading-four-icon.tsx +28 -0
- package/templates/tiptap/tiptap-icons/heading-icon.tsx +24 -0
- package/templates/tiptap/tiptap-icons/heading-one-icon.tsx +28 -0
- package/templates/tiptap/tiptap-icons/heading-six-icon.tsx +30 -0
- package/templates/tiptap/tiptap-icons/heading-three-icon.tsx +36 -0
- package/templates/tiptap/tiptap-icons/heading-two-icon.tsx +28 -0
- package/templates/tiptap/tiptap-icons/highlighter-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/image-plus-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/italic-icon.tsx +24 -0
- package/templates/tiptap/tiptap-icons/link-icon.tsx +28 -0
- package/templates/tiptap/tiptap-icons/list-icon.tsx +56 -0
- package/templates/tiptap/tiptap-icons/list-ordered-icon.tsx +56 -0
- package/templates/tiptap/tiptap-icons/list-todo-icon.tsx +50 -0
- package/templates/tiptap/tiptap-icons/moon-star-icon.tsx +30 -0
- package/templates/tiptap/tiptap-icons/redo2-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/strike-icon.tsx +28 -0
- package/templates/tiptap/tiptap-icons/subscript-icon.tsx +38 -0
- package/templates/tiptap/tiptap-icons/sun-icon.tsx +58 -0
- package/templates/tiptap/tiptap-icons/superscript-icon.tsx +38 -0
- package/templates/tiptap/tiptap-icons/trash-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/underline-icon.tsx +26 -0
- package/templates/tiptap/tiptap-icons/undo2-icon.tsx +26 -0
- package/templates/tiptap/tiptap-node/blockquote-node/blockquote-node.scss +37 -0
- package/templates/tiptap/tiptap-node/code-block-node/code-block-node.scss +54 -0
- package/templates/tiptap/tiptap-node/heading-node/heading-node.scss +45 -0
- package/templates/tiptap/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension.ts +10 -0
- package/templates/tiptap/tiptap-node/horizontal-rule-node/horizontal-rule-node.scss +25 -0
- package/templates/tiptap/tiptap-node/image-node/image-node.scss +35 -0
- package/templates/tiptap/tiptap-node/image-upload-node/image-upload-node-extension.ts +154 -0
- package/templates/tiptap/tiptap-node/image-upload-node/image-upload-node.scss +249 -0
- package/templates/tiptap/tiptap-node/image-upload-node/image-upload-node.tsx +522 -0
- package/templates/tiptap/tiptap-node/image-upload-node/index.tsx +1 -0
- package/templates/tiptap/tiptap-node/list-node/list-node.scss +208 -0
- package/templates/tiptap/tiptap-node/paragraph-node/paragraph-node.scss +273 -0
- package/templates/tiptap/tiptap-ui/blockquote-button/blockquote-button.tsx +104 -0
- package/templates/tiptap/tiptap-ui/blockquote-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/blockquote-button/use-blockquote.ts +252 -0
- package/templates/tiptap/tiptap-ui/code-block-button/code-block-button.tsx +106 -0
- package/templates/tiptap/tiptap-ui/code-block-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/code-block-button/use-code-block.ts +261 -0
- package/templates/tiptap/tiptap-ui/color-highlight-button/color-highlight-button.scss +49 -0
- package/templates/tiptap/tiptap-ui/color-highlight-button/color-highlight-button.tsx +153 -0
- package/templates/tiptap/tiptap-ui/color-highlight-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/color-highlight-button/use-color-highlight.ts +345 -0
- package/templates/tiptap/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx +207 -0
- package/templates/tiptap/tiptap-ui/color-highlight-popover/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui/heading-button/heading-button.tsx +107 -0
- package/templates/tiptap/tiptap-ui/heading-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/heading-button/use-heading.ts +314 -0
- package/templates/tiptap/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx +131 -0
- package/templates/tiptap/tiptap-ui/heading-dropdown-menu/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts +130 -0
- package/templates/tiptap/tiptap-ui/image-upload-button/image-upload-button.tsx +114 -0
- package/templates/tiptap/tiptap-ui/image-upload-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/image-upload-button/use-image-upload.ts +192 -0
- package/templates/tiptap/tiptap-ui/link-popover/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/link-popover/link-popover.tsx +285 -0
- package/templates/tiptap/tiptap-ui/link-popover/use-link-popover.ts +286 -0
- package/templates/tiptap/tiptap-ui/list-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/list-button/list-button.tsx +108 -0
- package/templates/tiptap/tiptap-ui/list-button/use-list.ts +329 -0
- package/templates/tiptap/tiptap-ui/list-dropdown-menu/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx +123 -0
- package/templates/tiptap/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts +203 -0
- package/templates/tiptap/tiptap-ui/mark-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/mark-button/mark-button.tsx +107 -0
- package/templates/tiptap/tiptap-ui/mark-button/use-mark.ts +206 -0
- package/templates/tiptap/tiptap-ui/text-align-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/text-align-button/text-align-button.tsx +118 -0
- package/templates/tiptap/tiptap-ui/text-align-button/use-text-align.ts +212 -0
- package/templates/tiptap/tiptap-ui/undo-redo-button/index.tsx +2 -0
- package/templates/tiptap/tiptap-ui/undo-redo-button/undo-redo-button.tsx +105 -0
- package/templates/tiptap/tiptap-ui/undo-redo-button/use-undo-redo.ts +173 -0
- package/templates/tiptap/tiptap-ui-primitive/badge/badge-colors.scss +395 -0
- package/templates/tiptap/tiptap-ui-primitive/badge/badge-group.scss +16 -0
- package/templates/tiptap/tiptap-ui-primitive/badge/badge.scss +99 -0
- package/templates/tiptap/tiptap-ui-primitive/badge/badge.tsx +46 -0
- package/templates/tiptap/tiptap-ui-primitive/badge/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/button/button-colors.scss +429 -0
- package/templates/tiptap/tiptap-ui-primitive/button/button-group.scss +22 -0
- package/templates/tiptap/tiptap-ui-primitive/button/button.scss +314 -0
- package/templates/tiptap/tiptap-ui-primitive/button/button.tsx +102 -0
- package/templates/tiptap/tiptap-ui-primitive/button/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/card/card.scss +77 -0
- package/templates/tiptap/tiptap-ui-primitive/card/card.tsx +59 -0
- package/templates/tiptap/tiptap-ui-primitive/card/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/dropdown-menu/dropdown-menu.scss +63 -0
- package/templates/tiptap/tiptap-ui-primitive/dropdown-menu/dropdown-menu.tsx +95 -0
- package/templates/tiptap/tiptap-ui-primitive/dropdown-menu/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/input/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/input/input.scss +45 -0
- package/templates/tiptap/tiptap-ui-primitive/input/input.tsx +18 -0
- package/templates/tiptap/tiptap-ui-primitive/popover/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/popover/popover.scss +63 -0
- package/templates/tiptap/tiptap-ui-primitive/popover/popover.tsx +33 -0
- package/templates/tiptap/tiptap-ui-primitive/separator/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/separator/separator.scss +23 -0
- package/templates/tiptap/tiptap-ui-primitive/separator/separator.tsx +33 -0
- package/templates/tiptap/tiptap-ui-primitive/spacer/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/spacer/spacer.tsx +21 -0
- package/templates/tiptap/tiptap-ui-primitive/toolbar/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/toolbar/toolbar.scss +98 -0
- package/templates/tiptap/tiptap-ui-primitive/toolbar/toolbar.tsx +113 -0
- package/templates/tiptap/tiptap-ui-primitive/tooltip/index.tsx +1 -0
- package/templates/tiptap/tiptap-ui-primitive/tooltip/tooltip.scss +43 -0
- package/templates/tiptap/tiptap-ui-primitive/tooltip/tooltip.tsx +223 -0
- package/templates/ui/accordion.tsx +52 -0
- package/templates/ui/alert-dialog.tsx +116 -0
- package/templates/ui/alert.tsx +48 -0
- package/templates/ui/aspect-ratio.tsx +7 -0
- package/templates/ui/avatar.tsx +46 -0
- package/templates/ui/badge.tsx +32 -0
- package/templates/ui/breadcrumb.tsx +98 -0
- package/templates/ui/button-group.tsx +77 -0
- package/templates/ui/button.tsx +48 -0
- package/templates/ui/calendar.tsx +176 -0
- package/templates/ui/card.tsx +54 -0
- package/templates/ui/carousel.tsx +234 -0
- package/templates/ui/chart.tsx +349 -0
- package/templates/ui/checkbox.tsx +27 -0
- package/templates/ui/collapsible.tsx +11 -0
- package/templates/ui/command.tsx +142 -0
- package/templates/ui/context-menu.tsx +188 -0
- package/templates/ui/curriculum-editor.tsx +601 -0
- package/templates/ui/date-picker.tsx +70 -0
- package/templates/ui/dialog.tsx +103 -0
- package/templates/ui/drawer.tsx +99 -0
- package/templates/ui/dropdown-menu.tsx +185 -0
- package/templates/ui/dynamic-list-field.tsx +95 -0
- package/templates/ui/empty.tsx +90 -0
- package/templates/ui/field.tsx +231 -0
- package/templates/ui/file-upload-example.tsx +113 -0
- package/templates/ui/form.tsx +172 -0
- package/templates/ui/hover-card.tsx +28 -0
- package/templates/ui/icon-picker.tsx +435 -0
- package/templates/ui/icons-data.ts +6 -0
- package/templates/ui/image-upload-field.tsx +360 -0
- package/templates/ui/input-group.tsx +160 -0
- package/templates/ui/input-otp.tsx +70 -0
- package/templates/ui/input.tsx +21 -0
- package/templates/ui/item.tsx +171 -0
- package/templates/ui/kbd.tsx +28 -0
- package/templates/ui/label.tsx +20 -0
- package/templates/ui/logo.tsx +113 -0
- package/templates/ui/markdown-editor.tsx +303 -0
- package/templates/ui/markdown-utils.ts +128 -0
- package/templates/ui/media-upload-field.tsx +255 -0
- package/templates/ui/menubar.tsx +230 -0
- package/templates/ui/navigation-menu.tsx +119 -0
- package/templates/ui/pagination.tsx +96 -0
- package/templates/ui/placeholder.tsx +25 -0
- package/templates/ui/popover.tsx +32 -0
- package/templates/ui/progress.tsx +24 -0
- package/templates/ui/radio-group.tsx +37 -0
- package/templates/ui/resizable.tsx +41 -0
- package/templates/ui/rich-text-editor.tsx +374 -0
- package/templates/ui/scroll-area.tsx +45 -0
- package/templates/ui/select.tsx +151 -0
- package/templates/ui/separator.tsx +25 -0
- package/templates/ui/sheet.tsx +120 -0
- package/templates/ui/sidebar.tsx +684 -0
- package/templates/ui/skeleton.tsx +7 -0
- package/templates/ui/slider.tsx +24 -0
- package/templates/ui/sonner.tsx +29 -0
- package/templates/ui/spinner.tsx +15 -0
- package/templates/ui/switch.tsx +28 -0
- package/templates/ui/table.tsx +93 -0
- package/templates/ui/tabs.tsx +54 -0
- package/templates/ui/textarea.tsx +20 -0
- package/templates/ui/toast.tsx +127 -0
- package/templates/ui/toggle-group.tsx +56 -0
- package/templates/ui/toggle.tsx +43 -0
- package/templates/ui/tooltip.tsx +31 -0
- package/templates/ui/use-mobile.tsx +19 -0
- package/templates/ui/video-upload-field.tsx +368 -0
- package/dist/chunk-G4KI4DVB.js +0 -179
- package/dist/chunk-G4KI4DVB.js.map +0 -1
- package/dist/chunk-NKRQYAS6.js +0 -260
- package/dist/chunk-NKRQYAS6.js.map +0 -1
- package/dist/chunk-QLVSHP7X.js +0 -235
- package/dist/chunk-QLVSHP7X.js.map +0 -1
- package/dist/chunk-WY6BC55D.js +0 -357
- package/dist/chunk-WY6BC55D.js.map +0 -1
- package/dist/config/index.d.ts +0 -93
- package/dist/config/index.js +0 -58
- package/dist/config/index.js.map +0 -1
- package/dist/core/index.d.ts +0 -415
- package/dist/core/index.js +0 -906
- package/dist/core/index.js.map +0 -1
- package/dist/import-resolver-BaZ-rzkH.d.ts +0 -123
- package/dist/logger-awLb347n.d.ts +0 -81
- package/dist/plugins/index.d.ts +0 -213
- package/dist/plugins/index.js +0 -365
- package/dist/plugins/index.js.map +0 -1
- package/dist/types-ByX_gl6y.d.ts +0 -232
- package/dist/types-eI549DEG.d.ts +0 -331
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { NodeSelection, TextSelection } from '@tiptap/pm/state'
|
|
4
|
+
import type { Editor } from '@tiptap/react'
|
|
5
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
6
|
+
|
|
7
|
+
// --- Hooks ---
|
|
8
|
+
import { useTiptapEditor } from '../../hooks/use-tiptap-editor'
|
|
9
|
+
// --- UI Utils ---
|
|
10
|
+
import {
|
|
11
|
+
findNodePosition,
|
|
12
|
+
getSelectedBlockNodes,
|
|
13
|
+
isNodeInSchema,
|
|
14
|
+
isNodeTypeSelected,
|
|
15
|
+
isValidPosition,
|
|
16
|
+
selectionWithinConvertibleTypes
|
|
17
|
+
} from '../../lib/tiptap-utils'
|
|
18
|
+
// --- Icons ---
|
|
19
|
+
import { BlockquoteIcon } from '../../tiptap-icons/blockquote-icon'
|
|
20
|
+
|
|
21
|
+
export const BLOCKQUOTE_SHORTCUT_KEY = 'mod+shift+b'
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Configuration for the blockquote functionality
|
|
25
|
+
*/
|
|
26
|
+
export interface UseBlockquoteConfig {
|
|
27
|
+
/**
|
|
28
|
+
* The Tiptap editor instance.
|
|
29
|
+
*/
|
|
30
|
+
editor?: Editor | null
|
|
31
|
+
/**
|
|
32
|
+
* Whether the button should hide when blockquote is not available.
|
|
33
|
+
* @default false
|
|
34
|
+
*/
|
|
35
|
+
hideWhenUnavailable?: boolean
|
|
36
|
+
/**
|
|
37
|
+
* Callback function called after a successful toggle.
|
|
38
|
+
*/
|
|
39
|
+
onToggled?: () => void
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Checks if blockquote can be toggled in the current editor state
|
|
44
|
+
*/
|
|
45
|
+
export function canToggleBlockquote(editor: Editor | null, turnInto: boolean = true): boolean {
|
|
46
|
+
if (!editor || !editor.isEditable) return false
|
|
47
|
+
if (!isNodeInSchema('blockquote', editor) || isNodeTypeSelected(editor, ['image'])) return false
|
|
48
|
+
|
|
49
|
+
if (!turnInto) {
|
|
50
|
+
return editor.can().toggleWrap('blockquote')
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Ensure selection is in nodes we're allowed to convert
|
|
54
|
+
if (
|
|
55
|
+
!selectionWithinConvertibleTypes(editor, [
|
|
56
|
+
'paragraph',
|
|
57
|
+
'heading',
|
|
58
|
+
'bulletList',
|
|
59
|
+
'orderedList',
|
|
60
|
+
'taskList',
|
|
61
|
+
'blockquote',
|
|
62
|
+
'codeBlock'
|
|
63
|
+
])
|
|
64
|
+
)
|
|
65
|
+
return false
|
|
66
|
+
|
|
67
|
+
// Either we can wrap in blockquote directly on the selection,
|
|
68
|
+
// or we can clear formatting/nodes to arrive at a blockquote.
|
|
69
|
+
return editor.can().toggleWrap('blockquote') || editor.can().clearNodes()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Toggles blockquote formatting for a specific node or the current selection
|
|
74
|
+
*/
|
|
75
|
+
export function toggleBlockquote(editor: Editor | null): boolean {
|
|
76
|
+
if (!editor || !editor.isEditable) return false
|
|
77
|
+
if (!canToggleBlockquote(editor)) return false
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const view = editor.view
|
|
81
|
+
let state = view.state
|
|
82
|
+
let tr = state.tr
|
|
83
|
+
|
|
84
|
+
const blocks = getSelectedBlockNodes(editor)
|
|
85
|
+
|
|
86
|
+
// In case a selection contains multiple blocks, we only allow
|
|
87
|
+
// toggling to nide if there's exactly one block selected
|
|
88
|
+
// we also dont block the canToggle since it will fall back to the bottom logic
|
|
89
|
+
const isPossibleToTurnInto =
|
|
90
|
+
selectionWithinConvertibleTypes(editor, [
|
|
91
|
+
'paragraph',
|
|
92
|
+
'heading',
|
|
93
|
+
'bulletList',
|
|
94
|
+
'orderedList',
|
|
95
|
+
'taskList',
|
|
96
|
+
'blockquote',
|
|
97
|
+
'codeBlock'
|
|
98
|
+
]) && blocks.length === 1
|
|
99
|
+
|
|
100
|
+
// No selection, find the the cursor position
|
|
101
|
+
if (
|
|
102
|
+
(state.selection.empty || state.selection instanceof TextSelection) &&
|
|
103
|
+
isPossibleToTurnInto
|
|
104
|
+
) {
|
|
105
|
+
const pos = findNodePosition({
|
|
106
|
+
editor,
|
|
107
|
+
node: state.selection.$anchor.node(1)
|
|
108
|
+
})?.pos
|
|
109
|
+
if (!isValidPosition(pos)) return false
|
|
110
|
+
|
|
111
|
+
tr = tr.setSelection(NodeSelection.create(state.doc, pos))
|
|
112
|
+
view.dispatch(tr)
|
|
113
|
+
state = view.state
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const selection = state.selection
|
|
117
|
+
|
|
118
|
+
let chain = editor.chain().focus()
|
|
119
|
+
|
|
120
|
+
// Handle NodeSelection
|
|
121
|
+
if (selection instanceof NodeSelection) {
|
|
122
|
+
const firstChild = selection.node.firstChild?.firstChild
|
|
123
|
+
const lastChild = selection.node.lastChild?.lastChild
|
|
124
|
+
|
|
125
|
+
const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1
|
|
126
|
+
|
|
127
|
+
const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1
|
|
128
|
+
|
|
129
|
+
const resolvedFrom = state.doc.resolve(from)
|
|
130
|
+
const resolvedTo = state.doc.resolve(to)
|
|
131
|
+
|
|
132
|
+
chain = chain.setTextSelection(TextSelection.between(resolvedFrom, resolvedTo)).clearNodes()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const toggle = editor.isActive('blockquote')
|
|
136
|
+
? chain.lift('blockquote')
|
|
137
|
+
: chain.wrapIn('blockquote')
|
|
138
|
+
|
|
139
|
+
toggle.run()
|
|
140
|
+
|
|
141
|
+
editor.chain().focus().selectTextblockEnd().run()
|
|
142
|
+
|
|
143
|
+
return true
|
|
144
|
+
} catch {
|
|
145
|
+
return false
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Determines if the blockquote button should be shown
|
|
151
|
+
*/
|
|
152
|
+
export function shouldShowButton(props: {
|
|
153
|
+
editor: Editor | null
|
|
154
|
+
hideWhenUnavailable: boolean
|
|
155
|
+
}): boolean {
|
|
156
|
+
const { editor, hideWhenUnavailable } = props
|
|
157
|
+
|
|
158
|
+
if (!editor || !editor.isEditable) return false
|
|
159
|
+
|
|
160
|
+
if (!hideWhenUnavailable) {
|
|
161
|
+
return true
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!isNodeInSchema('blockquote', editor)) return false
|
|
165
|
+
|
|
166
|
+
if (!editor.isActive('code')) {
|
|
167
|
+
return canToggleBlockquote(editor)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return true
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Custom hook that provides blockquote functionality for Tiptap editor
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```tsx
|
|
178
|
+
* // Simple usage - no params needed
|
|
179
|
+
* function MySimpleBlockquoteButton() {
|
|
180
|
+
* const { isVisible, handleToggle, isActive } = useBlockquote()
|
|
181
|
+
*
|
|
182
|
+
* if (!isVisible) return null
|
|
183
|
+
*
|
|
184
|
+
* return <button onClick={handleToggle}>Blockquote</button>
|
|
185
|
+
* }
|
|
186
|
+
*
|
|
187
|
+
* // Advanced usage with configuration
|
|
188
|
+
* function MyAdvancedBlockquoteButton() {
|
|
189
|
+
* const { isVisible, handleToggle, label, isActive } = useBlockquote({
|
|
190
|
+
* editor: myEditor,
|
|
191
|
+
* hideWhenUnavailable: true,
|
|
192
|
+
* onToggled: () => console.log('Blockquote toggled!')
|
|
193
|
+
* })
|
|
194
|
+
*
|
|
195
|
+
* if (!isVisible) return null
|
|
196
|
+
*
|
|
197
|
+
* return (
|
|
198
|
+
* <MyButton
|
|
199
|
+
* onClick={handleToggle}
|
|
200
|
+
* aria-label={label}
|
|
201
|
+
* aria-pressed={isActive}
|
|
202
|
+
* >
|
|
203
|
+
* Toggle Blockquote
|
|
204
|
+
* </MyButton>
|
|
205
|
+
* )
|
|
206
|
+
* }
|
|
207
|
+
* ```
|
|
208
|
+
*/
|
|
209
|
+
export function useBlockquote(config?: UseBlockquoteConfig) {
|
|
210
|
+
const { editor: providedEditor, hideWhenUnavailable = false, onToggled } = config || {}
|
|
211
|
+
|
|
212
|
+
const { editor } = useTiptapEditor(providedEditor)
|
|
213
|
+
const [isVisible, setIsVisible] = useState<boolean>(true)
|
|
214
|
+
const canToggle = canToggleBlockquote(editor)
|
|
215
|
+
const isActive = editor?.isActive('blockquote') || false
|
|
216
|
+
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
if (!editor) return
|
|
219
|
+
|
|
220
|
+
const handleSelectionUpdate = () => {
|
|
221
|
+
setIsVisible(shouldShowButton({ editor, hideWhenUnavailable }))
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
handleSelectionUpdate()
|
|
225
|
+
|
|
226
|
+
editor.on('selectionUpdate', handleSelectionUpdate)
|
|
227
|
+
|
|
228
|
+
return () => {
|
|
229
|
+
editor.off('selectionUpdate', handleSelectionUpdate)
|
|
230
|
+
}
|
|
231
|
+
}, [editor, hideWhenUnavailable])
|
|
232
|
+
|
|
233
|
+
const handleToggle = useCallback(() => {
|
|
234
|
+
if (!editor) return false
|
|
235
|
+
|
|
236
|
+
const success = toggleBlockquote(editor)
|
|
237
|
+
if (success) {
|
|
238
|
+
onToggled?.()
|
|
239
|
+
}
|
|
240
|
+
return success
|
|
241
|
+
}, [editor, onToggled])
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
isVisible,
|
|
245
|
+
isActive,
|
|
246
|
+
handleToggle,
|
|
247
|
+
canToggle,
|
|
248
|
+
label: 'Blockquote',
|
|
249
|
+
shortcutKeys: BLOCKQUOTE_SHORTCUT_KEY,
|
|
250
|
+
Icon: BlockquoteIcon
|
|
251
|
+
}
|
|
252
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { forwardRef, useCallback } from 'react'
|
|
4
|
+
|
|
5
|
+
// --- Hooks ---
|
|
6
|
+
import { useTiptapEditor } from '../../hooks/use-tiptap-editor'
|
|
7
|
+
|
|
8
|
+
// --- Lib ---
|
|
9
|
+
import { parseShortcutKeys } from '../../lib/tiptap-utils'
|
|
10
|
+
import { Badge } from '../../tiptap-ui-primitive/badge'
|
|
11
|
+
// --- UI Primitives ---
|
|
12
|
+
import type { ButtonProps } from '../../tiptap-ui-primitive/button'
|
|
13
|
+
import { Button } from '../../tiptap-ui-primitive/button'
|
|
14
|
+
// --- Tiptap UI ---
|
|
15
|
+
import type { UseCodeBlockConfig } from '.'
|
|
16
|
+
import { CODE_BLOCK_SHORTCUT_KEY, useCodeBlock } from '.'
|
|
17
|
+
|
|
18
|
+
export interface CodeBlockButtonProps extends Omit<ButtonProps, 'type'>, UseCodeBlockConfig {
|
|
19
|
+
/**
|
|
20
|
+
* Optional text to display alongside the icon.
|
|
21
|
+
*/
|
|
22
|
+
text?: string
|
|
23
|
+
/**
|
|
24
|
+
* Optional show shortcut keys in the button.
|
|
25
|
+
* @default false
|
|
26
|
+
*/
|
|
27
|
+
showShortcut?: boolean
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function CodeBlockShortcutBadge({
|
|
31
|
+
shortcutKeys = CODE_BLOCK_SHORTCUT_KEY
|
|
32
|
+
}: {
|
|
33
|
+
shortcutKeys?: string
|
|
34
|
+
}) {
|
|
35
|
+
return <Badge>{parseShortcutKeys({ shortcutKeys })}</Badge>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Button component for toggling code block in a Tiptap editor.
|
|
40
|
+
*
|
|
41
|
+
* For custom button implementations, use the `useCodeBlock` hook instead.
|
|
42
|
+
*/
|
|
43
|
+
export const CodeBlockButton = forwardRef<HTMLButtonElement, CodeBlockButtonProps>(
|
|
44
|
+
(
|
|
45
|
+
{
|
|
46
|
+
editor: providedEditor,
|
|
47
|
+
text,
|
|
48
|
+
hideWhenUnavailable = false,
|
|
49
|
+
onToggled,
|
|
50
|
+
showShortcut = false,
|
|
51
|
+
onClick,
|
|
52
|
+
children,
|
|
53
|
+
...buttonProps
|
|
54
|
+
},
|
|
55
|
+
ref
|
|
56
|
+
) => {
|
|
57
|
+
const { editor } = useTiptapEditor(providedEditor)
|
|
58
|
+
const { isVisible, canToggle, isActive, handleToggle, label, shortcutKeys, Icon } =
|
|
59
|
+
useCodeBlock({
|
|
60
|
+
editor,
|
|
61
|
+
hideWhenUnavailable,
|
|
62
|
+
onToggled
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const handleClick = useCallback(
|
|
66
|
+
(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
67
|
+
onClick?.(event)
|
|
68
|
+
if (event.defaultPrevented) return
|
|
69
|
+
handleToggle()
|
|
70
|
+
},
|
|
71
|
+
[handleToggle, onClick]
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
if (!isVisible) {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<Button
|
|
80
|
+
type="button"
|
|
81
|
+
variant="ghost"
|
|
82
|
+
data-active-state={isActive ? 'on' : 'off'}
|
|
83
|
+
role="button"
|
|
84
|
+
disabled={!canToggle}
|
|
85
|
+
data-disabled={!canToggle}
|
|
86
|
+
tabIndex={-1}
|
|
87
|
+
aria-label={label}
|
|
88
|
+
aria-pressed={isActive}
|
|
89
|
+
tooltip="Code Block"
|
|
90
|
+
onClick={handleClick}
|
|
91
|
+
{...buttonProps}
|
|
92
|
+
ref={ref}
|
|
93
|
+
>
|
|
94
|
+
{children ?? (
|
|
95
|
+
<>
|
|
96
|
+
<Icon className="tiptap-button-icon" />
|
|
97
|
+
{text && <span className="tiptap-button-text">{text}</span>}
|
|
98
|
+
{showShortcut && <CodeBlockShortcutBadge shortcutKeys={shortcutKeys} />}
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
</Button>
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
CodeBlockButton.displayName = 'CodeBlockButton'
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { NodeSelection, TextSelection } from '@tiptap/pm/state'
|
|
4
|
+
import type { Editor } from '@tiptap/react'
|
|
5
|
+
import { useCallback, useEffect, useState } from 'react'
|
|
6
|
+
|
|
7
|
+
// --- Hooks ---
|
|
8
|
+
import { useTiptapEditor } from '../../hooks/use-tiptap-editor'
|
|
9
|
+
|
|
10
|
+
// --- Lib ---
|
|
11
|
+
import {
|
|
12
|
+
findNodePosition,
|
|
13
|
+
getSelectedBlockNodes,
|
|
14
|
+
isNodeInSchema,
|
|
15
|
+
isNodeTypeSelected,
|
|
16
|
+
isValidPosition,
|
|
17
|
+
selectionWithinConvertibleTypes
|
|
18
|
+
} from '../../lib/tiptap-utils'
|
|
19
|
+
|
|
20
|
+
// --- Icons ---
|
|
21
|
+
import { CodeBlockIcon } from '../../tiptap-icons/code-block-icon'
|
|
22
|
+
|
|
23
|
+
export const CODE_BLOCK_SHORTCUT_KEY = 'mod+alt+c'
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configuration for the code block functionality
|
|
27
|
+
*/
|
|
28
|
+
export interface UseCodeBlockConfig {
|
|
29
|
+
/**
|
|
30
|
+
* The Tiptap editor instance.
|
|
31
|
+
*/
|
|
32
|
+
editor?: Editor | null
|
|
33
|
+
/**
|
|
34
|
+
* Whether the button should hide when code block is not available.
|
|
35
|
+
* @default false
|
|
36
|
+
*/
|
|
37
|
+
hideWhenUnavailable?: boolean
|
|
38
|
+
/**
|
|
39
|
+
* Callback function called after a successful code block toggle.
|
|
40
|
+
*/
|
|
41
|
+
onToggled?: () => void
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Checks if code block can be toggled in the current editor state
|
|
46
|
+
*/
|
|
47
|
+
export function canToggle(editor: Editor | null, turnInto: boolean = true): boolean {
|
|
48
|
+
if (!editor || !editor.isEditable) return false
|
|
49
|
+
if (!isNodeInSchema('codeBlock', editor) || isNodeTypeSelected(editor, ['image'])) return false
|
|
50
|
+
|
|
51
|
+
if (!turnInto) {
|
|
52
|
+
return editor.can().toggleNode('codeBlock', 'paragraph')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Ensure selection is in nodes we're allowed to convert
|
|
56
|
+
if (
|
|
57
|
+
!selectionWithinConvertibleTypes(editor, [
|
|
58
|
+
'paragraph',
|
|
59
|
+
'heading',
|
|
60
|
+
'bulletList',
|
|
61
|
+
'orderedList',
|
|
62
|
+
'taskList',
|
|
63
|
+
'blockquote',
|
|
64
|
+
'codeBlock'
|
|
65
|
+
])
|
|
66
|
+
)
|
|
67
|
+
return false
|
|
68
|
+
|
|
69
|
+
// Either we can toggle code block directly on the selection,
|
|
70
|
+
// or we can clear formatting/nodes to arrive at a code block.
|
|
71
|
+
return editor.can().toggleNode('codeBlock', 'paragraph') || editor.can().clearNodes()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Toggles code block in the editor
|
|
76
|
+
*/
|
|
77
|
+
export function toggleCodeBlock(editor: Editor | null): boolean {
|
|
78
|
+
if (!editor || !editor.isEditable) return false
|
|
79
|
+
if (!canToggle(editor)) return false
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
const view = editor.view
|
|
83
|
+
let state = view.state
|
|
84
|
+
let tr = state.tr
|
|
85
|
+
|
|
86
|
+
const blocks = getSelectedBlockNodes(editor)
|
|
87
|
+
|
|
88
|
+
// In case a selection contains multiple blocks, we only allow
|
|
89
|
+
// toggling to nide if there's exactly one block selected
|
|
90
|
+
// we also dont block the canToggle since it will fall back to the bottom logic
|
|
91
|
+
const isPossibleToTurnInto =
|
|
92
|
+
selectionWithinConvertibleTypes(editor, [
|
|
93
|
+
'paragraph',
|
|
94
|
+
'heading',
|
|
95
|
+
'bulletList',
|
|
96
|
+
'orderedList',
|
|
97
|
+
'taskList',
|
|
98
|
+
'blockquote',
|
|
99
|
+
'codeBlock'
|
|
100
|
+
]) && blocks.length === 1
|
|
101
|
+
|
|
102
|
+
// No selection, find the the cursor position
|
|
103
|
+
if (
|
|
104
|
+
(state.selection.empty || state.selection instanceof TextSelection) &&
|
|
105
|
+
isPossibleToTurnInto
|
|
106
|
+
) {
|
|
107
|
+
const pos = findNodePosition({
|
|
108
|
+
editor,
|
|
109
|
+
node: state.selection.$anchor.node(1)
|
|
110
|
+
})?.pos
|
|
111
|
+
if (!isValidPosition(pos)) return false
|
|
112
|
+
|
|
113
|
+
tr = tr.setSelection(NodeSelection.create(state.doc, pos))
|
|
114
|
+
view.dispatch(tr)
|
|
115
|
+
state = view.state
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const selection = state.selection
|
|
119
|
+
|
|
120
|
+
let chain = editor.chain().focus()
|
|
121
|
+
|
|
122
|
+
// Handle NodeSelection
|
|
123
|
+
if (selection instanceof NodeSelection) {
|
|
124
|
+
const firstChild = selection.node.firstChild?.firstChild
|
|
125
|
+
const lastChild = selection.node.lastChild?.lastChild
|
|
126
|
+
|
|
127
|
+
const from = firstChild ? selection.from + firstChild.nodeSize : selection.from + 1
|
|
128
|
+
|
|
129
|
+
const to = lastChild ? selection.to - lastChild.nodeSize : selection.to - 1
|
|
130
|
+
|
|
131
|
+
const resolvedFrom = state.doc.resolve(from)
|
|
132
|
+
const resolvedTo = state.doc.resolve(to)
|
|
133
|
+
|
|
134
|
+
chain = chain.setTextSelection(TextSelection.between(resolvedFrom, resolvedTo)).clearNodes()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const toggle = editor.isActive('codeBlock')
|
|
138
|
+
? chain.setNode('paragraph')
|
|
139
|
+
: chain.toggleNode('codeBlock', 'paragraph')
|
|
140
|
+
|
|
141
|
+
toggle.run()
|
|
142
|
+
|
|
143
|
+
editor.chain().focus().selectTextblockEnd().run()
|
|
144
|
+
|
|
145
|
+
return true
|
|
146
|
+
} catch {
|
|
147
|
+
return false
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Determines if the code block button should be shown
|
|
153
|
+
*/
|
|
154
|
+
export function shouldShowButton(props: {
|
|
155
|
+
editor: Editor | null
|
|
156
|
+
hideWhenUnavailable: boolean
|
|
157
|
+
}): boolean {
|
|
158
|
+
const { editor, hideWhenUnavailable } = props
|
|
159
|
+
|
|
160
|
+
if (!editor || !editor.isEditable) return false
|
|
161
|
+
|
|
162
|
+
if (!hideWhenUnavailable) {
|
|
163
|
+
return true
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!isNodeInSchema('codeBlock', editor)) return false
|
|
167
|
+
|
|
168
|
+
if (!editor.isActive('code')) {
|
|
169
|
+
return canToggle(editor)
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return true
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Custom hook that provides code block functionality for Tiptap editor
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* ```tsx
|
|
180
|
+
* // Simple usage - no params needed
|
|
181
|
+
* function MySimpleCodeBlockButton() {
|
|
182
|
+
* const { isVisible, isActive, handleToggle } = useCodeBlock()
|
|
183
|
+
*
|
|
184
|
+
* if (!isVisible) return null
|
|
185
|
+
*
|
|
186
|
+
* return (
|
|
187
|
+
* <button
|
|
188
|
+
* onClick={handleToggle}
|
|
189
|
+
* aria-pressed={isActive}
|
|
190
|
+
* >
|
|
191
|
+
* Code Block
|
|
192
|
+
* </button>
|
|
193
|
+
* )
|
|
194
|
+
* }
|
|
195
|
+
*
|
|
196
|
+
* // Advanced usage with configuration
|
|
197
|
+
* function MyAdvancedCodeBlockButton() {
|
|
198
|
+
* const { isVisible, isActive, handleToggle, label } = useCodeBlock({
|
|
199
|
+
* editor: myEditor,
|
|
200
|
+
* hideWhenUnavailable: true,
|
|
201
|
+
* onToggled: (isActive) => console.log('Code block toggled:', isActive)
|
|
202
|
+
* })
|
|
203
|
+
*
|
|
204
|
+
* if (!isVisible) return null
|
|
205
|
+
*
|
|
206
|
+
* return (
|
|
207
|
+
* <MyButton
|
|
208
|
+
* onClick={handleToggle}
|
|
209
|
+
* aria-label={label}
|
|
210
|
+
* aria-pressed={isActive}
|
|
211
|
+
* >
|
|
212
|
+
* Toggle Code Block
|
|
213
|
+
* </MyButton>
|
|
214
|
+
* )
|
|
215
|
+
* }
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
export function useCodeBlock(config?: UseCodeBlockConfig) {
|
|
219
|
+
const { editor: providedEditor, hideWhenUnavailable = false, onToggled } = config || {}
|
|
220
|
+
|
|
221
|
+
const { editor } = useTiptapEditor(providedEditor)
|
|
222
|
+
const [isVisible, setIsVisible] = useState<boolean>(true)
|
|
223
|
+
const canToggleState = canToggle(editor)
|
|
224
|
+
const isActive = editor?.isActive('codeBlock') || false
|
|
225
|
+
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (!editor) return
|
|
228
|
+
|
|
229
|
+
const handleSelectionUpdate = () => {
|
|
230
|
+
setIsVisible(shouldShowButton({ editor, hideWhenUnavailable }))
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
handleSelectionUpdate()
|
|
234
|
+
|
|
235
|
+
editor.on('selectionUpdate', handleSelectionUpdate)
|
|
236
|
+
|
|
237
|
+
return () => {
|
|
238
|
+
editor.off('selectionUpdate', handleSelectionUpdate)
|
|
239
|
+
}
|
|
240
|
+
}, [editor, hideWhenUnavailable])
|
|
241
|
+
|
|
242
|
+
const handleToggle = useCallback(() => {
|
|
243
|
+
if (!editor) return false
|
|
244
|
+
|
|
245
|
+
const success = toggleCodeBlock(editor)
|
|
246
|
+
if (success) {
|
|
247
|
+
onToggled?.()
|
|
248
|
+
}
|
|
249
|
+
return success
|
|
250
|
+
}, [editor, onToggled])
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
isVisible,
|
|
254
|
+
isActive,
|
|
255
|
+
handleToggle,
|
|
256
|
+
canToggle: canToggleState,
|
|
257
|
+
label: 'Code Block',
|
|
258
|
+
shortcutKeys: CODE_BLOCK_SHORTCUT_KEY,
|
|
259
|
+
Icon: CodeBlockIcon
|
|
260
|
+
}
|
|
261
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
.tiptap-button-highlight {
|
|
2
|
+
position: relative;
|
|
3
|
+
width: 1.25rem;
|
|
4
|
+
height: 1.25rem;
|
|
5
|
+
margin: 0 -0.175rem;
|
|
6
|
+
border-radius: var(--tt-radius-xl);
|
|
7
|
+
background-color: var(--highlight-color);
|
|
8
|
+
transition: transform 0.2s ease;
|
|
9
|
+
|
|
10
|
+
&::after {
|
|
11
|
+
content: "";
|
|
12
|
+
position: absolute;
|
|
13
|
+
width: 100%;
|
|
14
|
+
height: 100%;
|
|
15
|
+
left: 0;
|
|
16
|
+
top: 0;
|
|
17
|
+
border-radius: inherit;
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
border: 1px solid var(--highlight-color);
|
|
20
|
+
filter: brightness(95%);
|
|
21
|
+
mix-blend-mode: multiply;
|
|
22
|
+
|
|
23
|
+
.dark & {
|
|
24
|
+
filter: brightness(140%);
|
|
25
|
+
mix-blend-mode: lighten;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.tiptap-button {
|
|
31
|
+
&[data-active-state="on"] {
|
|
32
|
+
.tiptap-button-highlight {
|
|
33
|
+
&::after {
|
|
34
|
+
filter: brightness(80%);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.dark & {
|
|
40
|
+
&[data-active-state="on"] {
|
|
41
|
+
.tiptap-button-highlight {
|
|
42
|
+
&::after {
|
|
43
|
+
// Andere Eigenschaft für .dark Kontext
|
|
44
|
+
filter: brightness(180%);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|