From ebe2ca7faff580ccb8064920a24fb2fad6412105 Mon Sep 17 00:00:00 2001
From: Dan Brown <ssddanbrown@googlemail.com>
Date: Tue, 17 Dec 2024 22:40:28 +0000
Subject: [PATCH] Lexical: Added about button/view

Re-used existing route and moved tinymce help to its own different
route. Added test to cover.
Added new external-content block to support in editor UI.
---
 lang/en/editor.php                            |   2 +
 .../icons/editor/{help.svg => about.svg}      |   0
 resources/js/wysiwyg-tinymce/plugins-about.js |   2 +-
 .../wysiwyg/ui/defaults/buttons/controls.ts   |  15 +-
 .../js/wysiwyg/ui/defaults/forms/controls.ts  |  17 +-
 resources/js/wysiwyg/ui/defaults/modals.ts    |   6 +-
 .../js/wysiwyg/ui/{ => defaults}/toolbars.ts  |  39 +--
 .../ui/framework/blocks/external-content.ts   |  29 ++
 resources/js/wysiwyg/ui/index.ts              |   2 +-
 resources/sass/_editor.scss                   |   7 +
 resources/views/help/tinymce.blade.php        | 146 +++++++++
 resources/views/help/wysiwyg.blade.php        | 280 +++++++++---------
 routes/web.php                                |   1 +
 tests/Meta/HelpTest.php                       |  12 +-
 14 files changed, 388 insertions(+), 170 deletions(-)
 rename resources/icons/editor/{help.svg => about.svg} (100%)
 rename resources/js/wysiwyg/ui/{ => defaults}/toolbars.ts (88%)
 create mode 100644 resources/js/wysiwyg/ui/framework/blocks/external-content.ts
 create mode 100644 resources/views/help/tinymce.blade.php

diff --git a/lang/en/editor.php b/lang/en/editor.php
index de9aa0ece..a61b46042 100644
--- a/lang/en/editor.php
+++ b/lang/en/editor.php
@@ -163,6 +163,8 @@ return [
     'about' => 'About the editor',
     'about_title' => 'About the WYSIWYG Editor',
     'editor_license' => 'Editor License & Copyright',
+    'editor_lexical_license' => 'This editor is built as a fork of :lexicalLink which is distributed under the MIT license.',
+    'editor_lexical_license_link' => 'Full license details can be found here.',
     'editor_tiny_license' => 'This editor is built using :tinyLink which is provided under the MIT license.',
     'editor_tiny_license_link' => 'The copyright and license details of TinyMCE can be found here.',
     'save_continue' => 'Save Page & Continue',
diff --git a/resources/icons/editor/help.svg b/resources/icons/editor/about.svg
similarity index 100%
rename from resources/icons/editor/help.svg
rename to resources/icons/editor/about.svg
diff --git a/resources/js/wysiwyg-tinymce/plugins-about.js b/resources/js/wysiwyg-tinymce/plugins-about.js
index 096b4f968..75cf476cf 100644
--- a/resources/js/wysiwyg-tinymce/plugins-about.js
+++ b/resources/js/wysiwyg-tinymce/plugins-about.js
@@ -4,7 +4,7 @@
 function register(editor) {
     const aboutDialog = {
         title: 'About the WYSIWYG Editor',
-        url: window.baseUrl('/help/wysiwyg'),
+        url: window.baseUrl('/help/tinymce'),
     };
 
     editor.ui.registry.addButton('about', {
diff --git a/resources/js/wysiwyg/ui/defaults/buttons/controls.ts b/resources/js/wysiwyg/ui/defaults/buttons/controls.ts
index 77223dac3..5e3200539 100644
--- a/resources/js/wysiwyg/ui/defaults/buttons/controls.ts
+++ b/resources/js/wysiwyg/ui/defaults/buttons/controls.ts
@@ -11,8 +11,9 @@ import {
 } from "lexical";
 import redoIcon from "@icons/editor/redo.svg";
 import sourceIcon from "@icons/editor/source-view.svg";
-import {getEditorContentAsHtml} from "../../../utils/actions";
 import fullscreenIcon from "@icons/editor/fullscreen.svg";
+import aboutIcon from "@icons/editor/about.svg";
+import {getEditorContentAsHtml} from "../../../utils/actions";
 
 export const undo: EditorButtonDefinition = {
     label: 'Undo',
@@ -80,4 +81,16 @@ export const fullscreen: EditorButtonDefinition = {
     isActive(selection, context: EditorUiContext) {
         return context.containerDOM.classList.contains('fullscreen');
     }
+};
+
+export const about: EditorButtonDefinition = {
+    label: 'About the editor',
+    icon: aboutIcon,
+    async action(context: EditorUiContext, button: EditorButton) {
+        const modal = context.manager.createModal('about');
+        modal.show({});
+    },
+    isActive(selection, context: EditorUiContext) {
+        return false;
+    }
 };
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/defaults/forms/controls.ts b/resources/js/wysiwyg/ui/defaults/forms/controls.ts
index fc461f662..8e7219d67 100644
--- a/resources/js/wysiwyg/ui/defaults/forms/controls.ts
+++ b/resources/js/wysiwyg/ui/defaults/forms/controls.ts
@@ -1,6 +1,7 @@
 import {EditorFormDefinition} from "../../framework/forms";
-import {EditorUiContext} from "../../framework/core";
+import {EditorUiContext, EditorUiElement} from "../../framework/core";
 import {setEditorContentFromHtml} from "../../../utils/actions";
+import {ExternalContent} from "../../framework/blocks/external-content";
 
 export const source: EditorFormDefinition = {
     submitText: 'Save',
@@ -15,4 +16,18 @@ export const source: EditorFormDefinition = {
             type: 'textarea',
         },
     ],
+};
+
+export const about: EditorFormDefinition = {
+    submitText: 'Close',
+    async action() {
+        return true;
+    },
+    fields: [
+        {
+            build(): EditorUiElement {
+                return new ExternalContent('/help/wysiwyg');
+            }
+        }
+    ],
 };
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/defaults/modals.ts b/resources/js/wysiwyg/ui/defaults/modals.ts
index da3859266..830a42935 100644
--- a/resources/js/wysiwyg/ui/defaults/modals.ts
+++ b/resources/js/wysiwyg/ui/defaults/modals.ts
@@ -1,6 +1,6 @@
 import {EditorFormModalDefinition} from "../framework/modals";
 import {details, image, link, media} from "./forms/objects";
-import {source} from "./forms/controls";
+import {about, source} from "./forms/controls";
 import {cellProperties, rowProperties, tableProperties} from "./forms/tables";
 
 export const modals: Record<string, EditorFormModalDefinition> = {
@@ -35,5 +35,9 @@ export const modals: Record<string, EditorFormModalDefinition> = {
     details: {
         title: 'Edit collapsible block',
         form: details,
+    },
+    about: {
+        title: 'About the WYSIWYG Editor',
+        form: about,
     }
 };
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/defaults/toolbars.ts
similarity index 88%
rename from resources/js/wysiwyg/ui/toolbars.ts
rename to resources/js/wysiwyg/ui/defaults/toolbars.ts
index 1230cbdd2..61baa3c32 100644
--- a/resources/js/wysiwyg/ui/toolbars.ts
+++ b/resources/js/wysiwyg/ui/defaults/toolbars.ts
@@ -1,12 +1,12 @@
-import {EditorButton} from "./framework/buttons";
-import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiContext, EditorUiElement} from "./framework/core";
-import {EditorFormatMenu} from "./framework/blocks/format-menu";
-import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
-import {EditorDropdownButton} from "./framework/blocks/dropdown-button";
-import {EditorColorPicker} from "./framework/blocks/color-picker";
-import {EditorTableCreator} from "./framework/blocks/table-creator";
-import {EditorColorButton} from "./framework/blocks/color-button";
-import {EditorOverflowContainer} from "./framework/blocks/overflow-container";
+import {EditorButton} from "../framework/buttons";
+import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiContext, EditorUiElement} from "../framework/core";
+import {EditorFormatMenu} from "../framework/blocks/format-menu";
+import {FormatPreviewButton} from "../framework/blocks/format-preview-button";
+import {EditorDropdownButton} from "../framework/blocks/dropdown-button";
+import {EditorColorPicker} from "../framework/blocks/color-picker";
+import {EditorTableCreator} from "../framework/blocks/table-creator";
+import {EditorColorButton} from "../framework/blocks/color-button";
+import {EditorOverflowContainer} from "../framework/blocks/overflow-container";
 import {
     cellProperties, clearTableFormatting,
     copyColumn,
@@ -29,8 +29,8 @@ import {
     rowProperties,
     splitCell,
     table, tableProperties
-} from "./defaults/buttons/tables";
-import {fullscreen, redo, source, undo} from "./defaults/buttons/controls";
+} from "./buttons/tables";
+import {about, fullscreen, redo, source, undo} from "./buttons/controls";
 import {
     blockquote, dangerCallout,
     h2,
@@ -41,7 +41,7 @@ import {
     paragraph,
     successCallout,
     warningCallout
-} from "./defaults/buttons/block-formats";
+} from "./buttons/block-formats";
 import {
     bold, clearFormating, code,
     highlightColor,
@@ -50,7 +50,7 @@ import {
     superscript,
     textColor,
     underline
-} from "./defaults/buttons/inline-formats";
+} from "./buttons/inline-formats";
 import {
     alignCenter,
     alignJustify,
@@ -58,14 +58,14 @@ import {
     alignRight,
     directionLTR,
     directionRTL
-} from "./defaults/buttons/alignments";
+} from "./buttons/alignments";
 import {
     bulletList,
     indentDecrease,
     indentIncrease,
     numberList,
     taskList
-} from "./defaults/buttons/lists";
+} from "./buttons/lists";
 import {
     codeBlock,
     details, detailsEditLabel, detailsToggle, detailsUnwrap,
@@ -75,10 +75,10 @@ import {
     image,
     link, media,
     unlink
-} from "./defaults/buttons/objects";
-import {el} from "../utils/dom";
-import {EditorButtonWithMenu} from "./framework/blocks/button-with-menu";
-import {EditorSeparator} from "./framework/blocks/separator";
+} from "./buttons/objects";
+import {el} from "../../utils/dom";
+import {EditorButtonWithMenu} from "../framework/blocks/button-with-menu";
+import {EditorSeparator} from "../framework/blocks/separator";
 
 export function getMainEditorFullToolbar(context: EditorUiContext): EditorContainerUiElement {
 
@@ -201,6 +201,7 @@ export function getMainEditorFullToolbar(context: EditorUiContext): EditorContai
         // Meta elements
         new EditorOverflowContainer(3, [
             new EditorButton(source),
+            new EditorButton(about),
             new EditorButton(fullscreen),
 
             // Test
diff --git a/resources/js/wysiwyg/ui/framework/blocks/external-content.ts b/resources/js/wysiwyg/ui/framework/blocks/external-content.ts
new file mode 100644
index 000000000..b53c43e55
--- /dev/null
+++ b/resources/js/wysiwyg/ui/framework/blocks/external-content.ts
@@ -0,0 +1,29 @@
+import {EditorUiElement} from "../core";
+import {el} from "../../../utils/dom";
+
+export class ExternalContent extends EditorUiElement {
+
+    /**
+     * The URL for HTML to be loaded from.
+     */
+    protected url: string = '';
+
+    constructor(url: string) {
+        super();
+        this.url = url;
+    }
+
+    buildDOM(): HTMLElement {
+        const wrapper = el('div', {
+            class: 'editor-external-content',
+        });
+
+        window.$http.get(this.url).then(resp => {
+            if (typeof resp.data === 'string') {
+                wrapper.innerHTML = resp.data;
+            }
+        });
+
+        return wrapper;
+    }
+}
diff --git a/resources/js/wysiwyg/ui/index.ts b/resources/js/wysiwyg/ui/index.ts
index 40df43347..fda37085e 100644
--- a/resources/js/wysiwyg/ui/index.ts
+++ b/resources/js/wysiwyg/ui/index.ts
@@ -4,7 +4,7 @@ import {
     getImageToolbarContent,
     getLinkToolbarContent,
     getMainEditorFullToolbar, getTableToolbarContent
-} from "./toolbars";
+} from "./defaults/toolbars";
 import {EditorUIManager} from "./framework/manager";
 import {EditorUiContext} from "./framework/core";
 import {CodeBlockDecorator} from "./decorators/code-block";
diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss
index 3eef4c2c6..2446c1416 100644
--- a/resources/sass/_editor.scss
+++ b/resources/sass/_editor.scss
@@ -350,6 +350,13 @@ body.editor-is-fullscreen {
   text-align: center;
   padding: 0.2em;
 }
+.editor-external-content {
+  min-width: 500px;
+  min-height: 500px;
+  h4:first-child {
+    margin-top: 0;
+  }
+}
 
 // In-editor elements
 .editor-image-wrap {
diff --git a/resources/views/help/tinymce.blade.php b/resources/views/help/tinymce.blade.php
new file mode 100644
index 000000000..8ff59c8d6
--- /dev/null
+++ b/resources/views/help/tinymce.blade.php
@@ -0,0 +1,146 @@
+@extends('layouts.plain')
+@section('document-class', 'bg-white ' .  (setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : ''))
+
+@section('content')
+    <div class="p-m">
+
+        <h4 class="mt-s">{{ trans('editor.editor_license') }}</h4>
+        <p>
+            {!! trans('editor.editor_tiny_license', ['tinyLink' => '<a href="https://www.tiny.cloud/" target="_blank" rel="noopener noreferrer">TinyMCE</a>']) !!}
+            <br>
+            <a href="{{ url('/libs/tinymce/license.txt') }}" target="_blank">{{ trans('editor.editor_tiny_license_link') }}</a>
+        </p>
+
+        <h4>{{ trans('editor.shortcuts') }}</h4>
+
+        <p>{{ trans('editor.shortcuts_intro') }}</p>
+        <table>
+            <thead>
+            <tr>
+                <th>{{ trans('editor.shortcut') }} {{ trans('editor.windows_linux') }}</th>
+                <th>{{ trans('editor.shortcut') }} {{ trans('editor.mac') }}</th>
+                <th>{{ trans('editor.description') }}</th>
+            </tr>
+            </thead>
+            <tbody>
+            <tr>
+                <td><code>Ctrl</code>+<code>S</code></td>
+                <td><code>Cmd</code>+<code>S</code></td>
+                <td>{{ trans('entities.pages_edit_save_draft') }}</td>
+            </tr>
+            <tr>
+                <td><code>Ctrl</code>+<code>Enter</code></td>
+                <td><code>Cmd</code>+<code>Enter</code></td>
+                <td>{{ trans('editor.save_continue') }}</td>
+            </tr>
+            <tr>
+                <td><code>Ctrl</code>+<code>B</code></td>
+                <td><code>Cmd</code>+<code>B</code></td>
+                <td>{{ trans('editor.bold') }}</td>
+            </tr>
+            <tr>
+                <td><code>Ctrl</code>+<code>I</code></td>
+                <td><code>Cmd</code>+<code>I</code></td>
+                <td>{{ trans('editor.italic') }}</td>
+            </tr>
+            <tr>
+                <td>
+                    <code>Ctrl</code>+<code>1</code><br>
+                    <code>Ctrl</code>+<code>2</code><br>
+                    <code>Ctrl</code>+<code>3</code><br>
+                    <code>Ctrl</code>+<code>4</code>
+                </td>
+                <td>
+                    <code>Cmd</code>+<code>1</code><br>
+                    <code>Cmd</code>+<code>2</code><br>
+                    <code>Cmd</code>+<code>3</code><br>
+                    <code>Cmd</code>+<code>4</code>
+                </td>
+                <td>
+                    {{ trans('editor.header_large') }} <br>
+                    {{ trans('editor.header_medium') }} <br>
+                    {{ trans('editor.header_small') }} <br>
+                    {{ trans('editor.header_tiny') }}
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <code>Ctrl</code>+<code>5</code><br>
+                    <code>Ctrl</code>+<code>D</code>
+                </td>
+                <td>
+                    <code>Cmd</code>+<code>5</code><br>
+                    <code>Cmd</code>+<code>D</code>
+                </td>
+                <td>{{ trans('editor.paragraph') }}</td>
+            </tr>
+            <tr>
+                <td>
+                    <code>Ctrl</code>+<code>6</code><br>
+                    <code>Ctrl</code>+<code>Q</code>
+                </td>
+                <td>
+                    <code>Cmd</code>+<code>6</code><br>
+                    <code>Cmd</code>+<code>Q</code>
+                </td>
+                <td>{{ trans('editor.blockquote') }}</td>
+            </tr>
+            <tr>
+                <td>
+                    <code>Ctrl</code>+<code>7</code><br>
+                    <code>Ctrl</code>+<code>E</code>
+                </td>
+                <td>
+                    <code>Cmd</code>+<code>7</code><br>
+                    <code>Cmd</code>+<code>E</code>
+                </td>
+                <td>{{ trans('editor.insert_code_block') }}</td>
+            </tr>
+            <tr>
+                <td>
+                    <code>Ctrl</code>+<code>8</code><br>
+                    <code>Ctrl</code>+<code>Shift</code>+<code>E</code>
+                </td>
+                <td>
+                    <code>Cmd</code>+<code>8</code><br>
+                    <code>Cmd</code>+<code>Shift</code>+<code>E</code>
+                </td>
+                <td>{{ trans('editor.inline_code') }}</td>
+            </tr>
+            <tr>
+                <td><code>Ctrl</code>+<code>9</code></td>
+                <td><code>Cmd</code>+<code>9</code></td>
+                <td>
+                    {{ trans('editor.callouts') }} <br>
+                    <small>{{ trans('editor.callouts_cycle') }}</small>
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <code>Ctrl</code>+<code>O</code> <br>
+                    <code>Ctrl</code>+<code>P</code>
+                </td>
+                <td>
+                    <code>Cmd</code>+<code>O</code> <br>
+                    <code>Cmd</code>+<code>P</code>
+                </td>
+                <td>
+                    {{ trans('editor.list_numbered') }} <br>
+                    {{ trans('editor.list_bullet') }}
+                </td>
+            </tr>
+            <tr>
+                <td>
+                    <code>Ctrl</code>+<code>Shift</code>+<code>K</code>
+                </td>
+                <td>
+                    <code>Cmd</code>+<code>Shift</code>+<code>K</code>
+                </td>
+                <td>{{ trans('editor.link_selector') }}</td>
+            </tr>
+            </tbody>
+        </table>
+
+    </div>
+@endsection
+
diff --git a/resources/views/help/wysiwyg.blade.php b/resources/views/help/wysiwyg.blade.php
index 8ff59c8d6..4fc00b0e1 100644
--- a/resources/views/help/wysiwyg.blade.php
+++ b/resources/views/help/wysiwyg.blade.php
@@ -1,146 +1,138 @@
-@extends('layouts.plain')
-@section('document-class', 'bg-white ' .  (setting()->getForCurrentUser('dark-mode-enabled') ? 'dark-mode ' : ''))
+<h4>{{ trans('editor.shortcuts') }}</h4>
 
-@section('content')
-    <div class="p-m">
-
-        <h4 class="mt-s">{{ trans('editor.editor_license') }}</h4>
-        <p>
-            {!! trans('editor.editor_tiny_license', ['tinyLink' => '<a href="https://www.tiny.cloud/" target="_blank" rel="noopener noreferrer">TinyMCE</a>']) !!}
-            <br>
-            <a href="{{ url('/libs/tinymce/license.txt') }}" target="_blank">{{ trans('editor.editor_tiny_license_link') }}</a>
-        </p>
-
-        <h4>{{ trans('editor.shortcuts') }}</h4>
-
-        <p>{{ trans('editor.shortcuts_intro') }}</p>
-        <table>
-            <thead>
-            <tr>
-                <th>{{ trans('editor.shortcut') }} {{ trans('editor.windows_linux') }}</th>
-                <th>{{ trans('editor.shortcut') }} {{ trans('editor.mac') }}</th>
-                <th>{{ trans('editor.description') }}</th>
-            </tr>
-            </thead>
-            <tbody>
-            <tr>
-                <td><code>Ctrl</code>+<code>S</code></td>
-                <td><code>Cmd</code>+<code>S</code></td>
-                <td>{{ trans('entities.pages_edit_save_draft') }}</td>
-            </tr>
-            <tr>
-                <td><code>Ctrl</code>+<code>Enter</code></td>
-                <td><code>Cmd</code>+<code>Enter</code></td>
-                <td>{{ trans('editor.save_continue') }}</td>
-            </tr>
-            <tr>
-                <td><code>Ctrl</code>+<code>B</code></td>
-                <td><code>Cmd</code>+<code>B</code></td>
-                <td>{{ trans('editor.bold') }}</td>
-            </tr>
-            <tr>
-                <td><code>Ctrl</code>+<code>I</code></td>
-                <td><code>Cmd</code>+<code>I</code></td>
-                <td>{{ trans('editor.italic') }}</td>
-            </tr>
-            <tr>
-                <td>
-                    <code>Ctrl</code>+<code>1</code><br>
-                    <code>Ctrl</code>+<code>2</code><br>
-                    <code>Ctrl</code>+<code>3</code><br>
-                    <code>Ctrl</code>+<code>4</code>
-                </td>
-                <td>
-                    <code>Cmd</code>+<code>1</code><br>
-                    <code>Cmd</code>+<code>2</code><br>
-                    <code>Cmd</code>+<code>3</code><br>
-                    <code>Cmd</code>+<code>4</code>
-                </td>
-                <td>
-                    {{ trans('editor.header_large') }} <br>
-                    {{ trans('editor.header_medium') }} <br>
-                    {{ trans('editor.header_small') }} <br>
-                    {{ trans('editor.header_tiny') }}
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <code>Ctrl</code>+<code>5</code><br>
-                    <code>Ctrl</code>+<code>D</code>
-                </td>
-                <td>
-                    <code>Cmd</code>+<code>5</code><br>
-                    <code>Cmd</code>+<code>D</code>
-                </td>
-                <td>{{ trans('editor.paragraph') }}</td>
-            </tr>
-            <tr>
-                <td>
-                    <code>Ctrl</code>+<code>6</code><br>
-                    <code>Ctrl</code>+<code>Q</code>
-                </td>
-                <td>
-                    <code>Cmd</code>+<code>6</code><br>
-                    <code>Cmd</code>+<code>Q</code>
-                </td>
-                <td>{{ trans('editor.blockquote') }}</td>
-            </tr>
-            <tr>
-                <td>
-                    <code>Ctrl</code>+<code>7</code><br>
-                    <code>Ctrl</code>+<code>E</code>
-                </td>
-                <td>
-                    <code>Cmd</code>+<code>7</code><br>
-                    <code>Cmd</code>+<code>E</code>
-                </td>
-                <td>{{ trans('editor.insert_code_block') }}</td>
-            </tr>
-            <tr>
-                <td>
-                    <code>Ctrl</code>+<code>8</code><br>
-                    <code>Ctrl</code>+<code>Shift</code>+<code>E</code>
-                </td>
-                <td>
-                    <code>Cmd</code>+<code>8</code><br>
-                    <code>Cmd</code>+<code>Shift</code>+<code>E</code>
-                </td>
-                <td>{{ trans('editor.inline_code') }}</td>
-            </tr>
-            <tr>
-                <td><code>Ctrl</code>+<code>9</code></td>
-                <td><code>Cmd</code>+<code>9</code></td>
-                <td>
-                    {{ trans('editor.callouts') }} <br>
-                    <small>{{ trans('editor.callouts_cycle') }}</small>
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <code>Ctrl</code>+<code>O</code> <br>
-                    <code>Ctrl</code>+<code>P</code>
-                </td>
-                <td>
-                    <code>Cmd</code>+<code>O</code> <br>
-                    <code>Cmd</code>+<code>P</code>
-                </td>
-                <td>
-                    {{ trans('editor.list_numbered') }} <br>
-                    {{ trans('editor.list_bullet') }}
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <code>Ctrl</code>+<code>Shift</code>+<code>K</code>
-                </td>
-                <td>
-                    <code>Cmd</code>+<code>Shift</code>+<code>K</code>
-                </td>
-                <td>{{ trans('editor.link_selector') }}</td>
-            </tr>
-            </tbody>
-        </table>
-
-    </div>
-@endsection
+<p>{{ trans('editor.shortcuts_intro') }}</p>
+<table>
+    <thead>
+    <tr>
+        <th>{{ trans('editor.shortcut') }} {{ trans('editor.windows_linux') }}</th>
+        <th>{{ trans('editor.shortcut') }} {{ trans('editor.mac') }}</th>
+        <th>{{ trans('editor.description') }}</th>
+    </tr>
+    </thead>
+    <tbody>
+    <tr>
+        <td><code>Ctrl</code>+<code>S</code></td>
+        <td><code>Cmd</code>+<code>S</code></td>
+        <td>{{ trans('entities.pages_edit_save_draft') }}</td>
+    </tr>
+    <tr>
+        <td><code>Ctrl</code>+<code>Enter</code></td>
+        <td><code>Cmd</code>+<code>Enter</code></td>
+        <td>{{ trans('editor.save_continue') }}</td>
+    </tr>
+    <tr>
+        <td><code>Ctrl</code>+<code>B</code></td>
+        <td><code>Cmd</code>+<code>B</code></td>
+        <td>{{ trans('editor.bold') }}</td>
+    </tr>
+    <tr>
+        <td><code>Ctrl</code>+<code>I</code></td>
+        <td><code>Cmd</code>+<code>I</code></td>
+        <td>{{ trans('editor.italic') }}</td>
+    </tr>
+    <tr>
+        <td>
+            <code>Ctrl</code>+<code>1</code><br>
+            <code>Ctrl</code>+<code>2</code><br>
+            <code>Ctrl</code>+<code>3</code><br>
+            <code>Ctrl</code>+<code>4</code>
+        </td>
+        <td>
+            <code>Cmd</code>+<code>1</code><br>
+            <code>Cmd</code>+<code>2</code><br>
+            <code>Cmd</code>+<code>3</code><br>
+            <code>Cmd</code>+<code>4</code>
+        </td>
+        <td>
+            {{ trans('editor.header_large') }} <br>
+            {{ trans('editor.header_medium') }} <br>
+            {{ trans('editor.header_small') }} <br>
+            {{ trans('editor.header_tiny') }}
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <code>Ctrl</code>+<code>5</code><br>
+            <code>Ctrl</code>+<code>D</code>
+        </td>
+        <td>
+            <code>Cmd</code>+<code>5</code><br>
+            <code>Cmd</code>+<code>D</code>
+        </td>
+        <td>{{ trans('editor.paragraph') }}</td>
+    </tr>
+    <tr>
+        <td>
+            <code>Ctrl</code>+<code>6</code><br>
+            <code>Ctrl</code>+<code>Q</code>
+        </td>
+        <td>
+            <code>Cmd</code>+<code>6</code><br>
+            <code>Cmd</code>+<code>Q</code>
+        </td>
+        <td>{{ trans('editor.blockquote') }}</td>
+    </tr>
+    <tr>
+        <td>
+            <code>Ctrl</code>+<code>7</code><br>
+            <code>Ctrl</code>+<code>E</code>
+        </td>
+        <td>
+            <code>Cmd</code>+<code>7</code><br>
+            <code>Cmd</code>+<code>E</code>
+        </td>
+        <td>{{ trans('editor.insert_code_block') }}</td>
+    </tr>
+    <tr>
+        <td>
+            <code>Ctrl</code>+<code>8</code><br>
+            <code>Ctrl</code>+<code>Shift</code>+<code>E</code>
+        </td>
+        <td>
+            <code>Cmd</code>+<code>8</code><br>
+            <code>Cmd</code>+<code>Shift</code>+<code>E</code>
+        </td>
+        <td>{{ trans('editor.inline_code') }}</td>
+    </tr>
+    <tr>
+        <td><code>Ctrl</code>+<code>9</code></td>
+        <td><code>Cmd</code>+<code>9</code></td>
+        <td>
+            {{ trans('editor.callouts') }} <br>
+            <small>{{ trans('editor.callouts_cycle') }}</small>
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <code>Ctrl</code>+<code>O</code> <br>
+            <code>Ctrl</code>+<code>P</code>
+        </td>
+        <td>
+            <code>Cmd</code>+<code>O</code> <br>
+            <code>Cmd</code>+<code>P</code>
+        </td>
+        <td>
+            {{ trans('editor.list_numbered') }} <br>
+            {{ trans('editor.list_bullet') }}
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <code>Ctrl</code>+<code>Shift</code>+<code>K</code>
+        </td>
+        <td>
+            <code>Cmd</code>+<code>Shift</code>+<code>K</code>
+        </td>
+        <td>{{ trans('editor.link_selector') }}</td>
+    </tr>
+    </tbody>
+</table>
 
+<h4 class="mt-s">{{ trans('editor.editor_license') }}</h4>
+<p>
+    {!! trans('editor.editor_lexical_license', ['lexicalLink' => '<a href="https://lexical.dev/" target="_blank" rel="noopener noreferrer">Lexical</a>']) !!}
+    <br>
+    <em class="text-muted">Copyright (c) Meta Platforms, Inc. and affiliates.</em>
+    <br>
+    <a href="{{ url('/licenses') }}" target="_blank">{{ trans('editor.editor_lexical_license_link') }}</a>
+</p>
\ No newline at end of file
diff --git a/routes/web.php b/routes/web.php
index 85f833528..318147ef5 100644
--- a/routes/web.php
+++ b/routes/web.php
@@ -361,6 +361,7 @@ Route::get('/password/reset/{token}', [AccessControllers\ResetPasswordController
 Route::post('/password/reset', [AccessControllers\ResetPasswordController::class, 'reset'])->middleware('throttle:public');
 
 // Metadata routes
+Route::view('/help/tinymce', 'help.tinymce');
 Route::view('/help/wysiwyg', 'help.wysiwyg');
 
 Route::fallback([MetaController::class, 'notFound'])->name('fallback');
diff --git a/tests/Meta/HelpTest.php b/tests/Meta/HelpTest.php
index e1de96bc8..acce65394 100644
--- a/tests/Meta/HelpTest.php
+++ b/tests/Meta/HelpTest.php
@@ -6,9 +6,9 @@ use Tests\TestCase;
 
 class HelpTest extends TestCase
 {
-    public function test_wysiwyg_help_shows_tiny_and_tiny_license_link()
+    public function test_tinymce_help_shows_tiny_and_tiny_license_link()
     {
-        $resp = $this->get('/help/wysiwyg');
+        $resp = $this->get('/help/tinymce');
         $resp->assertOk();
         $this->withHtml($resp)->assertElementExists('a[href="https://www.tiny.cloud/"]');
         $this->withHtml($resp)->assertElementExists('a[href="' . url('/libs/tinymce/license.txt') . '"]');
@@ -22,4 +22,12 @@ class HelpTest extends TestCase
         $contents = file_get_contents($expectedPath);
         $this->assertStringContainsString('MIT License', $contents);
     }
+
+    public function test_wysiwyg_help_shows_lexical_and_licenses_link()
+    {
+        $resp = $this->get('/help/wysiwyg');
+        $resp->assertOk();
+        $this->withHtml($resp)->assertElementExists('a[href="https://lexical.dev/"]');
+        $this->withHtml($resp)->assertElementExists('a[href="' . url('/licenses') . '"]');
+    }
 }