From 18364d1e6e235ab129a03e77279201436bcd954a Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Thu, 16 Apr 2026 11:11:06 +0100 Subject: [PATCH] WYSIWYG: Added inline code support to minimal editor Used for comments and descriptions. Also updated shortcut handling that we're not registering shortcuts for edits which can't use the related formatting types. For #6003 --- app/Util/HtmlDescriptionFilter.php | 1 + resources/js/wysiwyg/index.ts | 6 +- resources/js/wysiwyg/services/shortcuts.ts | 59 +++++++++++--------- resources/js/wysiwyg/ui/defaults/toolbars.ts | 1 + tests/Entity/BookTest.php | 4 +- 5 files changed, 40 insertions(+), 31 deletions(-) diff --git a/app/Util/HtmlDescriptionFilter.php b/app/Util/HtmlDescriptionFilter.php index 1baa11ffcfa..ba145460381 100644 --- a/app/Util/HtmlDescriptionFilter.php +++ b/app/Util/HtmlDescriptionFilter.php @@ -27,6 +27,7 @@ class HtmlDescriptionFilter 'span' => [], 'em' => [], 'br' => [], + 'code' => [], ]; public static function filterFromString(string $html): string diff --git a/resources/js/wysiwyg/index.ts b/resources/js/wysiwyg/index.ts index 01964b066c6..dc0ea211f59 100644 --- a/resources/js/wysiwyg/index.ts +++ b/resources/js/wysiwyg/index.ts @@ -59,7 +59,7 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st mergeRegister( registerRichText(editor), registerHistory(editor, createEmptyHistoryState(), 300), - registerShortcuts(context), + registerShortcuts(context, true), registerKeyboardHandling(context), registerMouseHandling(context), registerSelectionHandling(context), @@ -123,7 +123,7 @@ export function createBasicEditorInstance(container: HTMLElement, htmlContent: s const editorTeardown = mergeRegister( registerRichText(editor), registerHistory(editor, createEmptyHistoryState(), 300), - registerShortcuts(context), + registerShortcuts(context, false), registerAutoLinks(editor), ); @@ -157,7 +157,7 @@ export function createCommentEditorInstance(container: HTMLElement, htmlContent: const editorTeardown = mergeRegister( registerRichText(editor), registerHistory(editor, createEmptyHistoryState(), 300), - registerShortcuts(context), + registerShortcuts(context, false), registerAutoLinks(editor), registerMentions(context), ); diff --git a/resources/js/wysiwyg/services/shortcuts.ts b/resources/js/wysiwyg/services/shortcuts.ts index c4be0f3cf2f..00abe0c6d2f 100644 --- a/resources/js/wysiwyg/services/shortcuts.ts +++ b/resources/js/wysiwyg/services/shortcuts.ts @@ -38,29 +38,9 @@ type ShortcutAction = (editor: LexicalEditor, context: EditorUiContext) => boole * List of action functions by their shortcut combo. * We use "meta" as an abstraction for ctrl/cmd depending on platform. */ -const actionsByKeys: Record = { - 'meta+s': () => { - window.$events.emit('editor-save-draft'); - return true; - }, - 'meta+enter': () => { - window.$events.emit('editor-save-page'); - return true; - }, - 'meta+1': (editor, context) => headerHandler(context, 'h2'), - 'meta+2': (editor, context) => headerHandler(context, 'h3'), - 'meta+3': (editor, context) => headerHandler(context, 'h4'), - 'meta+4': (editor, context) => headerHandler(context, 'h5'), - 'meta+5': wrapFormatAction(toggleSelectionAsParagraph), - 'meta+d': wrapFormatAction(toggleSelectionAsParagraph), - 'meta+6': wrapFormatAction(toggleSelectionAsBlockquote), - 'meta+q': wrapFormatAction(toggleSelectionAsBlockquote), - 'meta+7': wrapFormatAction(formatCodeBlock), - 'meta+e': wrapFormatAction(formatCodeBlock), +const baseActionsByKeys: Record = { 'meta+8': toggleInlineCode, 'meta+shift+e': toggleInlineCode, - 'meta+9': wrapFormatAction(cycleSelectionCalloutFormats), - 'meta+o': wrapFormatAction((e) => toggleSelectionAsList(e, 'number')), 'meta+p': wrapFormatAction((e) => toggleSelectionAsList(e, 'bullet')), 'meta+k': (editor, context) => { @@ -87,12 +67,39 @@ const actionsByKeys: Record = { }, }; -function createKeyDownListener(context: EditorUiContext): (e: KeyboardEvent) => void { +/** + * An extended set of the above, used for fuller-featured editors with heavier block-level formatting. + */ +const extendedActionsByKeys: Record = { + ...baseActionsByKeys, + 'meta+s': () => { + window.$events.emit('editor-save-draft'); + return true; + }, + 'meta+enter': () => { + window.$events.emit('editor-save-page'); + return true; + }, + 'meta+1': (editor, context) => headerHandler(context, 'h2'), + 'meta+2': (editor, context) => headerHandler(context, 'h3'), + 'meta+3': (editor, context) => headerHandler(context, 'h4'), + 'meta+4': (editor, context) => headerHandler(context, 'h5'), + 'meta+5': wrapFormatAction(toggleSelectionAsParagraph), + 'meta+d': wrapFormatAction(toggleSelectionAsParagraph), + 'meta+6': wrapFormatAction(toggleSelectionAsBlockquote), + 'meta+7': wrapFormatAction(formatCodeBlock), + 'meta+e': wrapFormatAction(formatCodeBlock), + 'meta+q': wrapFormatAction(toggleSelectionAsBlockquote), + 'meta+9': wrapFormatAction(cycleSelectionCalloutFormats), +}; + +function createKeyDownListener(context: EditorUiContext, useExtended: boolean): (e: KeyboardEvent) => void { + const keySetToUse = useExtended ? extendedActionsByKeys : baseActionsByKeys; return (event: KeyboardEvent) => { const combo = keyboardEventToKeyComboString(event); // console.log(`pressed: ${combo}`); - if (actionsByKeys[combo]) { - const handled = actionsByKeys[combo](context.editor, context); + if (keySetToUse[combo]) { + const handled = keySetToUse[combo](context.editor, context); if (handled) { event.stopPropagation(); event.preventDefault(); @@ -127,8 +134,8 @@ function overrideDefaultCommands(editor: LexicalEditor) { }, COMMAND_PRIORITY_HIGH); } -export function registerShortcuts(context: EditorUiContext) { - const listener = createKeyDownListener(context); +export function registerShortcuts(context: EditorUiContext, useExtended: boolean) { + const listener = createKeyDownListener(context, useExtended); overrideDefaultCommands(context.editor); return context.editor.registerRootListener((rootElement: null | HTMLElement, prevRootElement: null | HTMLElement) => { diff --git a/resources/js/wysiwyg/ui/defaults/toolbars.ts b/resources/js/wysiwyg/ui/defaults/toolbars.ts index d6af996384b..a3ada5c89f6 100644 --- a/resources/js/wysiwyg/ui/defaults/toolbars.ts +++ b/resources/js/wysiwyg/ui/defaults/toolbars.ts @@ -227,6 +227,7 @@ export function getBasicEditorToolbar(context: EditorUiContext): EditorContainer new EditorButton(bold), new EditorButton(italic), new EditorButton(link), + new EditorButton(code), new EditorButton(bulletList), new EditorButton(numberList), ]) diff --git a/tests/Entity/BookTest.php b/tests/Entity/BookTest.php index 6082c59de61..c0d4fbc63e6 100644 --- a/tests/Entity/BookTest.php +++ b/tests/Entity/BookTest.php @@ -256,8 +256,8 @@ public function test_description_limited_to_specific_html() { $book = $this->entities->book(); - $input = '

Test

Contenta

Hello

'; - $expected = '

Contenta

'; + $input = '

Test

Contenta

Hello
code

'; + $expected = '

Contentacode

'; $this->asEditor()->put($book->getUrl(), [ 'name' => $book->name,