Scroll code block into view after fence input rule

This commit is contained in:
Yuri Karamian
2026-08-07 21:50:34 +02:00
parent 23abd0a19a
commit 32ef51161d
2 changed files with 51 additions and 0 deletions
+2
View File
@@ -48,6 +48,7 @@
import { debounce } from '$lib/utils/debounce';
import { encryptSecretText, decryptSecretText, readSecretTitle } from '$lib/utils/secrets';
import { WrapSelectedText } from '$lib/editor/extensions/wrapSelectedText';
import { CodeBlockInputScroll } from '$lib/editor/extensions/codeBlockInputScroll';
import { calloutGroup, calloutIcon, calloutLabel, CALLOUT_MENU, transformCalloutBlockquotes, serializeCallout } from '$lib/editor/callouts';
import { wrapTextareaSelection } from '$lib/editor/source/selectionPairs';
import { convertListNode, type MixedListName } from '$lib/editor/mixedLists';
@@ -4396,6 +4397,7 @@
TextStyle,
Color,
CodeBlockLowlight.configure({ lowlight, enableTabIndentation: true, defaultLanguage: 'text' }),
CodeBlockInputScroll,
CodeBlockLanguageSelect,
CopyButtonExtension,
MermaidRenderer,
@@ -0,0 +1,49 @@
import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from '@tiptap/pm/state';
const CODE_FENCE_PATTERN = /^(?:```|~~~)(?:[a-z]+)?$/;
function isInputRulesPlugin(plugin: Plugin) {
return 'isInputRules' in plugin.spec && plugin.spec.isInputRules === true;
}
export function createCodeBlockInputScrollPlugin() {
return new Plugin({
key: new PluginKey('codeBlockInputScroll'),
appendTransaction(transactions, oldState, newState) {
const inputRulesPlugin = oldState.plugins.find(isInputRulesPlugin);
const wasKeyboardInputRule =
inputRulesPlugin !== undefined &&
transactions.some((transaction) => {
const metadata: unknown = transaction.getMeta(inputRulesPlugin);
return (
typeof metadata === 'object' &&
metadata !== null &&
'transform' in metadata &&
metadata.transform === transaction &&
'text' in metadata &&
metadata.text === '\n'
);
});
const { selection } = oldState;
const wasFenceParagraph =
selection.empty &&
selection.$from.parent.type.name === 'paragraph' &&
CODE_FENCE_PATTERN.test(selection.$from.parent.textContent);
const isCodeBlock = newState.selection.$from.parent.type.name === 'codeBlock';
return wasKeyboardInputRule && wasFenceParagraph && isCodeBlock
? newState.tr.scrollIntoView()
: null;
},
});
}
export const CodeBlockInputScroll = Extension.create({
name: 'codeBlockInputScroll',
addProseMirrorPlugins() {
return [createCodeBlockInputScrollPlugin()];
},
});