QMS: Update inline comments extensions (#3814)

* update documents inline comments extensions

Signed-off-by: Anna No <anna.no@xored.com>

* update qms documents inline comments

Signed-off-by: Anna No <anna.no@xored.com>

* qms: update document inline comments extensions

Signed-off-by: Anna No <anna.no@xored.com>

* qms: move highlight to prose mirror decorations

Signed-off-by: Anna No <anna.no@xored.com>

* fix formatting issues

Signed-off-by: Anna No <anna.no@xored.com>

* fix formatting issues

Signed-off-by: Anna No <anna.no@xored.com>

* fix formatting issues

Signed-off-by: Anna No <anna.no@xored.com>

* fix formatting issues

Signed-off-by: Anna No <anna.no@xored.com>

---------

Signed-off-by: Anna No <anna.no@xored.com>
This commit is contained in:
Anna No 2023-10-10 00:26:09 +07:00 committed by GitHub
parent 8805a588bc
commit 4e08b75040
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 282 additions and 135 deletions

View File

@ -35,7 +35,7 @@
import { calculateDecorations } from './diff/decorations' import { calculateDecorations } from './diff/decorations'
import { defaultEditorAttributes } from './editor/editorProps' import { defaultEditorAttributes } from './editor/editorProps'
import { completionConfig, defaultExtensions } from './extensions' import { completionConfig, defaultExtensions } from './extensions'
import { InlineStyleToolbar } from './extension/inlineStyleToolbar' import { InlineStyleToolbarExtension } from './extension/inlineStyleToolbar'
import { NodeUuidExtension } from './extension/nodeUuid' import { NodeUuidExtension } from './extension/nodeUuid'
import StyleButton from './StyleButton.svelte' import StyleButton from './StyleButton.svelte'
import TextEditorStyleToolbar from './TextEditorStyleToolbar.svelte' import TextEditorStyleToolbar from './TextEditorStyleToolbar.svelte'
@ -47,7 +47,6 @@
export let token: string export let token: string
export let collaboratorURL: string export let collaboratorURL: string
export let isFormatting = true
export let buttonSize: IconSize = 'small' export let buttonSize: IconSize = 'small'
export let focusable: boolean = false export let focusable: boolean = false
export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder export let placeholder: IntlString = textEditorPlugin.string.EditorPlaceholder
@ -90,7 +89,6 @@
let editor: Editor let editor: Editor
let inlineToolbar: HTMLElement let inlineToolbar: HTMLElement
let showInlineToolbar = false
let placeHolderStr: string = '' let placeHolderStr: string = ''
@ -136,7 +134,6 @@
} }
const [$start, $end] = [doc.resolve(range.from), doc.resolve(range.to)] const [$start, $end] = [doc.resolve(range.from), doc.resolve(range.to)]
editor.view.dispatch(tr.setSelection(new TextSelection($start, $end))) editor.view.dispatch(tr.setSelection(new TextSelection($start, $end)))
needFocus = true needFocus = true
}) })
@ -146,6 +143,22 @@
provider.copyContent(documentId, snapshotId) provider.copyContent(documentId, snapshotId)
} }
export function unregisterPlugin (nameOrPluginKey: string | PluginKey) {
if (!editor) {
return
}
editor.unregisterPlugin(nameOrPluginKey)
}
export function registerPlugin (plugin: Plugin) {
if (!editor) {
return
}
editor.registerPlugin(plugin)
}
let needFocus = false let needFocus = false
let focused = false let focused = false
@ -201,6 +214,7 @@
}) })
$: updateEditor(editor, field, comparedVersion) $: updateEditor(editor, field, comparedVersion)
$: if (editor) dispatch('editor', editor)
onMount(() => { onMount(() => {
ph.then(() => { ph.then(() => {
@ -211,10 +225,10 @@
extensions: [ extensions: [
...defaultExtensions, ...defaultExtensions,
Placeholder.configure({ placeholder: placeHolderStr }), Placeholder.configure({ placeholder: placeHolderStr }),
InlineStyleToolbar.configure({ InlineStyleToolbarExtension.configure({
element: inlineToolbar, element: inlineToolbar,
getEditorElement: () => element, isSupported: () => !readonly,
isShown: () => !readonly && showInlineToolbar isSelectionOnly: () => false
}), }),
Collaboration.configure({ Collaboration.configure({
document: ydoc, document: ydoc,
@ -247,22 +261,14 @@
onFocus: () => { onFocus: () => {
focused = true focused = true
}, },
onUpdate: ({ editor, transaction }) => { onUpdate: ({ transaction }) => {
showInlineToolbar = false
// ignore non-document changes // ignore non-document changes
if (!transaction.docChanged) return if (!transaction.docChanged) return
// TODO this is heavy and should be replaced with more lightweight event
dispatch('content', editor.getHTML())
// ignore non-local changes // ignore non-local changes
if (isChangeOrigin(transaction)) return if (isChangeOrigin(transaction)) return
dispatch('update') dispatch('update')
},
onSelectionUpdate: () => {
showInlineToolbar = false
} }
}) })
@ -283,16 +289,11 @@
} }
}) })
function onEditorClick () {
if (!editor.isEmpty) {
showInlineToolbar = true
}
}
let showDiff = true let showDiff = true
</script> </script>
<slot /> <slot {editor} />
{#if visible} {#if visible}
{#if comparedVersion !== undefined || $$slots.tools} {#if comparedVersion !== undefined || $$slots.tools}
<div class="ref-container" class:autoOverflow> <div class="ref-container" class:autoOverflow>
@ -336,7 +337,7 @@
needFocus = true needFocus = true
}} }}
on:action={(event) => { on:action={(event) => {
dispatch('action', { action: event.detail, editor }) dispatch('action', event.detail)
needFocus = true needFocus = true
}} }}
/> />
@ -344,7 +345,7 @@
<div class="ref-container" class:autoOverflow> <div class="ref-container" class:autoOverflow>
<div class="textInput" class:focusable> <div class="textInput" class:focusable>
<div class="select-text" style="width: 100%;" on:mousedown={onEditorClick} bind:this={element} /> <div class="select-text" style="width: 100%;" bind:this={element} />
</div> </div>
</div> </div>
{/if} {/if}

View File

@ -28,7 +28,7 @@
import { themeStore } from '@hcengineering/ui' import { themeStore } from '@hcengineering/ui'
import TextEditorStyleToolbar from './TextEditorStyleToolbar.svelte' import TextEditorStyleToolbar from './TextEditorStyleToolbar.svelte'
import { TextFormatCategory } from '../types' import { TextFormatCategory } from '../types'
import { InlineStyleToolbar } from './extension/inlineStyleToolbar' import { InlineStyleToolbarExtension } from './extension/inlineStyleToolbar'
import { defaultEditorAttributes } from './editor/editorProps' import { defaultEditorAttributes } from './editor/editorProps'
export let content: string = '' export let content: string = ''
@ -77,7 +77,6 @@
let needFocus = false let needFocus = false
let focused = false let focused = false
let posFocus: FocusPosition | undefined = undefined let posFocus: FocusPosition | undefined = undefined
let showContextMenu = false
let textEditorToolbar: HTMLElement let textEditorToolbar: HTMLElement
export function focus (position?: FocusPosition): void { export function focus (position?: FocusPosition): void {
@ -137,10 +136,10 @@
...(supportSubmit ? [Handle] : []), // order important ...(supportSubmit ? [Handle] : []), // order important
Placeholder.configure({ placeholder: placeHolderStr }), Placeholder.configure({ placeholder: placeHolderStr }),
...extensions, ...extensions,
InlineStyleToolbar.configure({ InlineStyleToolbarExtension.configure({
element: textEditorToolbar, element: textEditorToolbar,
getEditorElement: () => element, isSupported: () => true,
isShown: () => showContextMenu isSelectionOnly: () => false
}) })
], ],
parseOptions: { parseOptions: {
@ -160,12 +159,8 @@
}, },
onUpdate: () => { onUpdate: () => {
content = editor.getHTML() content = editor.getHTML()
showContextMenu = false
dispatch('value', content) dispatch('value', content)
dispatch('update', content) dispatch('update', content)
},
onSelectionUpdate: () => {
showContextMenu = false
} }
}) })
}) })
@ -177,12 +172,6 @@
} }
}) })
function onEditorClick () {
if (!editor.isEmpty) {
showContextMenu = true
}
}
/** /**
* @public * @public
*/ */
@ -209,7 +198,7 @@
}} }}
/> />
</div> </div>
<div class="select-text" style="width: 100%;" on:mousedown={onEditorClick} bind:this={element} /> <div class="select-text" style="width: 100%;" bind:this={element} />
<style lang="scss"> <style lang="scss">
.formatPanel { .formatPanel {

View File

@ -330,7 +330,7 @@
disabled={textEditor.view.state.selection.empty} disabled={textEditor.view.state.selection.empty}
showTooltip={{ label: action.label }} showTooltip={{ label: action.label }}
on:click={() => { on:click={() => {
dispatch('action', action.id) dispatch('action', { action: action.id, editor: textEditor })
}} }}
/> />
{/each} {/each}

View File

@ -0,0 +1,16 @@
import { Extension } from '@tiptap/core'
import BubbleMenu, { BubbleMenuOptions } from '@tiptap/extension-bubble-menu'
export const InlinePopupExtension: Extension<BubbleMenuOptions> = BubbleMenu.extend({
addOptions () {
return {
...this.parent?.(),
pluginKey: 'inline-popup',
element: null,
tippyOptions: {
maxWidth: '38rem',
appendTo: () => document.body
}
}
}
})

View File

@ -1,48 +1,86 @@
import { Extension, isTextSelection } from '@tiptap/core' import { Editor, Extension, isTextSelection } from '@tiptap/core'
import BubbleMenu, { BubbleMenuOptions } from '@tiptap/extension-bubble-menu' import { BubbleMenuOptions } from '@tiptap/extension-bubble-menu'
import { Plugin, PluginKey } from 'prosemirror-state'
import { InlinePopupExtension } from './inlinePopup'
type InlineStyleToolbarOptions = BubbleMenuOptions & { export type InlineStyleToolbarOptions = BubbleMenuOptions & {
getEditorElement: () => HTMLElement | null | undefined isSupported: () => boolean
isShown?: () => boolean isSelectionOnly?: () => boolean
} }
export const InlineStyleToolbar = Extension.create<InlineStyleToolbarOptions>({ export interface InlineStyleToolbarStorage {
defaultOptions: { isShown: boolean
pluginKey: 'inline-style-toolbar', }
element: null,
tippyOptions: { const handleFocus = (editor: Editor, options: InlineStyleToolbarOptions, storage: InlineStyleToolbarStorage): void => {
maxWidth: '38rem', if (!options.isSupported()) {
appendTo: () => document.body return
}, }
getEditorElement: () => null
if (editor.isEmpty) {
return
}
if (options.isSelectionOnly?.() === true && editor.view.state.selection.empty) {
return
}
storage.isShown = true
}
export const InlineStyleToolbarExtension = Extension.create<InlineStyleToolbarOptions, InlineStyleToolbarStorage>({
pluginKey: new PluginKey('inline-style-toolbar'),
addProseMirrorPlugins () {
const options = this.options
const storage = this.storage
const editor = this.editor
const plugins = [
...(this.parent?.() ?? []),
new Plugin({
key: new PluginKey('inline-style-toolbar-click-plugin'),
props: {
handleClick () {
handleFocus(editor, options, storage)
}
}
})
]
return plugins
},
addStorage () {
return {
isShown: false
}
}, },
addExtensions () { addExtensions () {
const options: InlineStyleToolbarOptions = this.options const options: InlineStyleToolbarOptions = this.options
return [ return [
BubbleMenu.configure({ InlinePopupExtension.configure({
...options, ...options,
// to override shouldShow behaviour a little
// I need to copypaste original function and make a little change
// with showContextMenu falg
shouldShow: ({ editor, view, state, oldState, from, to }) => { shouldShow: ({ editor, view, state, oldState, from, to }) => {
const editorElement = options.getEditorElement() if (!this.options.isSupported()) {
return false
}
if (editor.isDestroyed || !editor.isEditable) {
return false
}
if (this.storage.isShown) {
return true
}
// For some reason shouldShow might be called after dismount and // For some reason shouldShow might be called after dismount and
// after destroing the editor. We should handle this just no to have // after destroing the editor. We should handle this just no to have
// any errors in runtime // any errors in runtime
const editorElement = editor.view.dom
if (editorElement === null || editorElement === undefined) { if (editorElement === null || editorElement === undefined) {
return false return false
} }
if (!editor.isEditable) {
return false
}
const isShown = options.isShown?.() ?? false
if (isShown) {
return true
}
// When clicking on a element inside the bubble menu the editor "blur" event // When clicking on a element inside the bubble menu the editor "blur" event
// is called and the bubble menu item is focussed. In this case we should // is called and the bubble menu item is focussed. In this case we should
// consider the menu as part of the editor and keep showing the menu // consider the menu as part of the editor and keep showing the menu
@ -67,5 +105,14 @@ export const InlineStyleToolbar = Extension.create<InlineStyleToolbarOptions>({
} }
}) })
] ]
},
onFocus () {
handleFocus(this.editor, this.options, this.storage)
},
onSelectionUpdate () {
this.storage.isShown = false
},
onUpdate () {
this.storage.isShown = false
} }
}) })

View File

@ -1,15 +1,17 @@
import { Extension, Range, getMarkRange, mergeAttributes } from '@tiptap/core' import { Extension, Range, getMarkRange, mergeAttributes } from '@tiptap/core'
import { NodeUuidExtension, NodeUuidOptions, NodeUuidStorage, findNodeUuidMark } from './nodeUuid'
import { Plugin, PluginKey, TextSelection } from 'prosemirror-state' import { Plugin, PluginKey, TextSelection } from 'prosemirror-state'
import { NodeUuidExtension, NodeUuidOptions } from './nodeUuid' import { Decoration, DecorationSet } from 'prosemirror-view'
export enum NodeHighlightType { export enum NodeHighlightType {
INFO = 'info', WARNING = 'warning',
ADD = 'add', ADD = 'add',
DELETE = 'delete' DELETE = 'delete'
} }
export interface NodeHighlightExtensionOptions extends NodeUuidOptions { export interface NodeHighlightExtensionOptions extends NodeUuidOptions {
getNodeHighlightType: (uuid: string) => NodeHighlightType | undefined | null getNodeHighlightType: (uuid: string) => NodeHighlightType | undefined | null
isHighlightModeOn: () => boolean isHighlightModeOn: () => boolean
isAutoSelect?: () => boolean
} }
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type // eslint-disable-next-line @typescript-eslint/no-invalid-void-type
@ -17,20 +19,47 @@ function isRange (range: Range | undefined | null | void): range is Range {
return range !== null && range !== undefined return range !== null && range !== undefined
} }
const generateAttributes = (uuid: string, options: NodeHighlightExtensionOptions): Record<string, any> | undefined => {
if (!options.isHighlightModeOn()) {
return undefined
}
const type = options.getNodeHighlightType(uuid)
if (type === null || type === undefined) {
return undefined
}
const classAttrs: { class?: string } = {}
if (type === NodeHighlightType.WARNING) {
classAttrs.class = 'text-editor-highlighted-node-warning'
} else if (type === NodeHighlightType.ADD) {
classAttrs.class = 'text-editor-highlighted-node-add'
} else if (type === NodeHighlightType.DELETE) {
classAttrs.class = 'text-editor-highlighted-node-delete'
}
return classAttrs
}
/** /**
* Extension allows to highlight nodes based on uuid * Extension allows to highlight nodes based on uuid
*/ */
export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions> = export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions, NodeUuidStorage> =
Extension.create<NodeHighlightExtensionOptions>({ Extension.create<NodeHighlightExtensionOptions>({
addStorage (): NodeUuidStorage {
return { activeNodeUuid: null }
},
addProseMirrorPlugins () { addProseMirrorPlugins () {
const options = this.options const options = this.options
const storage: NodeUuidStorage = this.storage
const plugins = [ const plugins = [
...(this.parent?.() ?? []), ...(this.parent?.() ?? []),
new Plugin({ new Plugin({
key: new PluginKey('handle-node-highlight-click-plugin'), key: new PluginKey('handle-node-highlight-click-plugin'),
props: { props: {
handleClick (view, pos) { handleClick (view, pos) {
if (!options.isHighlightModeOn()) { if (!options.isHighlightModeOn() || options.isAutoSelect?.() !== true) {
return return
} }
const { schema, doc, tr } = view.state const { schema, doc, tr } = view.state
@ -49,45 +78,65 @@ export const NodeHighlightExtension: Extension<NodeHighlightExtensionOptions> =
return true return true
} }
} }
}),
new Plugin({
key: new PluginKey('node-highlight-click-decorations-plugin'),
props: {
decorations (state) {
if (!options.isHighlightModeOn()) {
return undefined
}
const decorations: Decoration[] = []
const { doc, schema } = state
doc.descendants((node, pos) => {
const nodeUuidMark = findNodeUuidMark(node)
if (nodeUuidMark !== null && nodeUuidMark !== undefined) {
const nodeUuid = nodeUuidMark.attrs[NodeUuidExtension.name]
const attributes = generateAttributes(nodeUuid, options)
if (attributes === null || attributes === undefined) {
return
}
// the first pos does not contain the mark, so we need to add 1 (pos + 1) to get the correct range
const range = getMarkRange(doc.resolve(pos + 1), schema.marks[NodeUuidExtension.name])
if (!isRange(range)) {
return
}
decorations.push(
Decoration.inline(
range.from,
range.to,
mergeAttributes(
attributes,
nodeUuid === storage.activeNodeUuid ? { class: 'text-editor-highlighted-node-selected' } : {}
)
)
)
}
})
return DecorationSet.empty.add(doc, decorations)
}
}
}) })
] ]
return plugins return plugins
}, },
addExtensions () { addExtensions () {
const options = this.options const options: NodeHighlightExtensionOptions = this.options
const storage: NodeUuidStorage = this.storage
return [ return [
NodeUuidExtension.extend({ NodeUuidExtension.extend({
addOptions () { addOptions (): NodeUuidOptions {
return { return {
...this.parent?.(), ...this.parent?.(),
...options ...options,
} onNodeSelected: (uuid: string) => {
}, storage.activeNodeUuid = uuid
addAttributes () { options.onNodeSelected?.(uuid)
return {
[NodeUuidExtension.name]: {
renderHTML: (attrs) => {
// get uuid from parent mark (NodeUuidExtension) attributes
const uuid = attrs[NodeUuidExtension.name]
const classAttrs: { class?: string } = {}
if (options.isHighlightModeOn()) {
const type = options.getNodeHighlightType(uuid)
if (type === NodeHighlightType.INFO) {
classAttrs.class = 'text-editor-highlighted-node-info'
} else if (type === NodeHighlightType.ADD) {
classAttrs.class = 'text-editor-highlighted-node-add'
} else if (type === NodeHighlightType.DELETE) {
classAttrs.class = 'text-editor-highlighted-node-delete'
}
}
return mergeAttributes(attrs, classAttrs)
}
} }
} }
} }

View File

@ -1,5 +1,6 @@
import { Mark, getMarkAttributes, mergeAttributes } from '@tiptap/core' import { Command, CommandProps, Mark, getMarkAttributes, getMarkType, mergeAttributes } from '@tiptap/core'
import { Plugin, PluginKey } from 'prosemirror-state' import { Node, Mark as ProseMirrorMark } from 'prosemirror-model'
import { EditorState, Plugin, PluginKey } from 'prosemirror-state'
const NAME = 'node-uuid' const NAME = 'node-uuid'
@ -14,11 +15,11 @@ export interface NodeUuidCommands<ReturnType> {
/** /**
* Add uuid mark * Add uuid mark
*/ */
setUuid: (uuid: string) => ReturnType setNodeUuid: (uuid: string) => ReturnType
/** /**
* Unset uuid mark * Unset uuid mark
*/ */
unsetUuid: () => ReturnType unsetNodeUuid: () => ReturnType
} }
} }
@ -30,8 +31,32 @@ export interface NodeUuidStorage {
activeNodeUuid: string | null activeNodeUuid: string | null
} }
const findSelectionNodeUuidMark = (state: EditorState): ProseMirrorMark | undefined => {
if (state.selection === null || state.selection === undefined) {
return
}
let nodeUuidMark: ProseMirrorMark | undefined
state.doc.nodesBetween(state.selection.from, state.selection.to, (node) => {
if (nodeUuidMark !== null || nodeUuidMark !== undefined) {
return false
}
nodeUuidMark = findNodeUuidMark(node)
})
return nodeUuidMark
}
export const findNodeUuidMark = (node: Node): ProseMirrorMark | undefined => {
if (node === null || node === undefined) {
return
}
return node.marks.find((mark) => mark.type.name === NAME && mark.attrs[NAME])
}
/** /**
* This mark allows to add node uuid to the selected text * This extension allows to add node uuid to the selected text
* Creates span node with attribute node-uuid * Creates span node with attribute node-uuid
*/ */
export const NodeUuidExtension = Mark.create<NodeUuidOptions, NodeUuidStorage>({ export const NodeUuidExtension = Mark.create<NodeUuidOptions, NodeUuidStorage>({
@ -73,20 +98,23 @@ export const NodeUuidExtension = Mark.create<NodeUuidOptions, NodeUuidStorage>({
addProseMirrorPlugins () { addProseMirrorPlugins () {
const options = this.options const options = this.options
const storage: NodeUuidStorage = this.storage
const plugins = [ const plugins = [
...(this.parent?.() ?? []), ...(this.parent?.() ?? []),
new Plugin({ new Plugin({
key: new PluginKey('handle-node-uuid-click-plugin'), key: new PluginKey('handle-node-uuid-click-plugin'),
props: { props: {
handleClick (view) { handleClick (view) {
const { schema } = view.state const attrs = getMarkAttributes(view.state, view.state.schema.marks[NAME])
const attrs = getMarkAttributes(view.state, schema.marks[NAME])
const nodeUuid = attrs?.[NAME] const nodeUuid = attrs?.[NAME]
if (nodeUuid !== null || nodeUuid !== undefined) { if (nodeUuid !== null || nodeUuid !== undefined) {
options.onNodeClicked?.(nodeUuid) options.onNodeClicked?.(nodeUuid)
} }
if (storage.activeNodeUuid !== nodeUuid) {
storage.activeNodeUuid = nodeUuid
options.onNodeSelected?.(storage.activeNodeUuid)
}
} }
} }
}) })
@ -96,16 +124,27 @@ export const NodeUuidExtension = Mark.create<NodeUuidOptions, NodeUuidStorage>({
}, },
addCommands () { addCommands () {
return { const result: NodeUuidCommands<Command>[typeof NAME] = {
setUuid: setNodeUuid:
(uuid: string) => (uuid: string) =>
({ commands }) => ({ commands, state }: CommandProps) => {
commands.setMark(this.name, { [NAME]: uuid }), const { doc, selection } = state
unsetUuid: if (selection.empty) {
return false
}
if (doc.rangeHasMark(selection.from, selection.to, getMarkType(NAME, state.schema))) {
return false
}
return commands.setMark(this.name, { [NAME]: uuid })
},
unsetNodeUuid:
() => () =>
({ commands }) => ({ commands }: CommandProps) =>
commands.unsetMark(this.name) commands.unsetMark(this.name)
} }
return result
}, },
addStorage () { addStorage () {
@ -115,19 +154,13 @@ export const NodeUuidExtension = Mark.create<NodeUuidOptions, NodeUuidStorage>({
}, },
onSelectionUpdate () { onSelectionUpdate () {
const { $head } = this.editor.state.selection const activeNodeUuidMark = findSelectionNodeUuidMark(this.editor.state)
const activeNodeUuid =
activeNodeUuidMark !== null && activeNodeUuidMark !== undefined ? activeNodeUuidMark.attrs[NAME] : null
const marks = $head.marks() if (this.storage.activeNodeUuid !== activeNodeUuid) {
this.storage.activeNodeUuid = null this.storage.activeNodeUuid = activeNodeUuid
if (marks.length > 0) { this.options.onNodeSelected?.(this.storage.activeNodeUuid)
const nodeUuidMark = this.editor.schema.marks[NAME]
const activeNodeUuidMark = marks.find((mark) => mark.type === nodeUuidMark)
if (activeNodeUuidMark !== undefined && activeNodeUuidMark !== null) {
this.storage.activeNodeUuid = activeNodeUuidMark.attrs[NAME]
}
} }
this.options.onNodeSelected?.(this.storage.activeNodeUuid)
} }
}) })

View File

@ -27,6 +27,7 @@ export { default as StyledTextArea } from './components/StyledTextArea.svelte'
export { default as StyledTextBox } from './components/StyledTextBox.svelte' export { default as StyledTextBox } from './components/StyledTextBox.svelte'
export { default as StyledTextEditor } from './components/StyledTextEditor.svelte' export { default as StyledTextEditor } from './components/StyledTextEditor.svelte'
export { default as TextEditor } from './components/TextEditor.svelte' export { default as TextEditor } from './components/TextEditor.svelte'
export { default as TextEditorStyleToolbar } from './components/TextEditorStyleToolbar.svelte'
export { default } from './plugin' export { default } from './plugin'
export * from './types' export * from './types'
@ -41,5 +42,11 @@ export {
NodeHighlightType NodeHighlightType
} from './components/extension/nodeHighlight' } from './components/extension/nodeHighlight'
export { NodeUuidCommands, NodeUuidExtension, NodeUuidOptions, NodeUuidStorage } from './components/extension/nodeUuid' export { NodeUuidCommands, NodeUuidExtension, NodeUuidOptions, NodeUuidStorage } from './components/extension/nodeUuid'
export { InlinePopupExtension } from './components/extension/inlinePopup'
export {
InlineStyleToolbarExtension,
InlineStyleToolbarOptions,
InlineStyleToolbarStorage
} from './components/extension/inlineStyleToolbar'
export { textEditorId } export { textEditorId }

View File

@ -73,8 +73,9 @@
--highlight-red-hover: #ff967e; --highlight-red-hover: #ff967e;
--highlight-red-press: #f96f50bd; --highlight-red-press: #f96f50bd;
--text-editor-highlighted-node-info-background-color: #F2D7AE; --text-editor-highlighted-node-warning-active-background-color: #F2D7AE;
--text-editor-highlighted-node-info-border-color: #DE9B35; --text-editor-highlighted-node-warning-background-color: #F8EBD7;
--text-editor-highlighted-node-warning-border-color: #DE9B35;
--text-editor-highlighted-node-add-background-color: #DAEDDC; --text-editor-highlighted-node-add-background-color: #DAEDDC;
--text-editor-highlighted-node-add-font-color: #1C4220; --text-editor-highlighted-node-add-font-color: #1C4220;

View File

@ -108,9 +108,13 @@
object-fit: contain; object-fit: contain;
} }
.text-editor-highlighted-node-info { .text-editor-highlighted-node-warning {
background-color: var(--text-editor-highlighted-node-info-background-color); background-color: var(--text-editor-highlighted-node-warning-background-color);
border-bottom: 0.0625rem solid var(--text-editor-highlighted-node-info-border-color); border-bottom: 0.0625rem solid var(--text-editor-highlighted-node-warning-border-color);
&.text-editor-highlighted-node-selected, &:hover {
background-color: var(--text-editor-highlighted-node-warning-active-background-color);
}
} }
.text-editor-highlighted-node-delete { .text-editor-highlighted-node-delete {