diff --git a/gui/src/components/mainInput/TipTapEditor/TipTapEditor.css b/gui/src/components/mainInput/TipTapEditor/TipTapEditor.css index 94c4969c682..3f2a2c7703f 100644 --- a/gui/src/components/mainInput/TipTapEditor/TipTapEditor.css +++ b/gui/src/components/mainInput/TipTapEditor/TipTapEditor.css @@ -39,3 +39,7 @@ .tiptap img.ProseMirror-selectednode { border: 1px solid var(--vscode-badge-background, #bfe2b6); } + +.tiptap img.selected-image { + outline: 3px solid var(--vscode-badge-background, #bfe2b6); +} diff --git a/gui/src/components/mainInput/TipTapEditor/utils/editorConfig.ts b/gui/src/components/mainInput/TipTapEditor/utils/editorConfig.ts index 2ecd4d41a0d..b05109c9bca 100644 --- a/gui/src/components/mainInput/TipTapEditor/utils/editorConfig.ts +++ b/gui/src/components/mainInput/TipTapEditor/utils/editorConfig.ts @@ -6,6 +6,7 @@ import Paragraph from "@tiptap/extension-paragraph"; import Placeholder from "@tiptap/extension-placeholder"; import Text from "@tiptap/extension-text"; import { Plugin } from "@tiptap/pm/state"; +import { Decoration, DecorationSet } from "@tiptap/pm/view"; import { useEditor } from "@tiptap/react"; import { InputModifiers } from "core"; import { modelSupportsImages } from "core/llm/autodetect"; @@ -144,7 +145,7 @@ export function createEditorConfig(options: { History, Image.extend({ addProseMirrorPlugins() { - const plugin = new Plugin({ + const pastePlugin = new Plugin({ props: { handleDOMEvents: { paste(view, event) { @@ -179,7 +180,34 @@ export function createEditorConfig(options: { }, }, }); - return [plugin]; + + const selectionPlugin = new Plugin({ + props: { + decorations(state) { + const { selection, doc } = state; + const decorations: Decoration[] = []; + + if (selection.empty) { + return DecorationSet.empty; + } + + // create custom highlighting for image when selected + doc.nodesBetween(selection.from, selection.to, (node, pos) => { + if (node.type.name === "image") { + decorations.push( + Decoration.node(pos, pos + node.nodeSize, { + class: "selected-image", + }), + ); + } + }); + + return DecorationSet.create(doc, decorations); + }, + }, + }); + + return [pastePlugin, selectionPlugin]; }, }).configure({ HTMLAttributes: { @@ -244,6 +272,15 @@ export function createEditorConfig(options: { () => commands.splitBlock(), ]), + "Mod-a": () => { + // override cmd/ctrl+a to include all text and images selection + this.editor.commands.setTextSelection({ + from: 0, + to: this.editor.state.doc.content.size, + }); + return true; + }, + ArrowUp: () => { if (this.editor.state.selection.anchor > 1) { return false;