diff --git a/resources/js/wysiwyg/helpers.ts b/resources/js/wysiwyg/helpers.ts
index 64bcb6490..3708c2b25 100644
--- a/resources/js/wysiwyg/helpers.ts
+++ b/resources/js/wysiwyg/helpers.ts
@@ -1,14 +1,14 @@
 import {
+    $createNodeSelection,
     $createParagraphNode, $getRoot,
     $getSelection,
-    $isTextNode,
-    BaseSelection, ElementNode,
+    $isTextNode, $setSelection,
+    BaseSelection,
     LexicalEditor, LexicalNode, TextFormatType
 } from "lexical";
-import {LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
+import {getNodesForPageEditor, LexicalElementNodeCreator, LexicalNodeMatcher} from "./nodes";
 import {$getNearestBlockElementAncestorOrThrow} from "@lexical/utils";
 import {$setBlocksType} from "@lexical/selection";
-import {$createDetailsNode} from "./nodes/details";
 
 export function el(tag: string, attrs: Record<string, string|null> = {}, children: (string|HTMLElement)[] = []): HTMLElement {
     const el = document.createElement(tag);
@@ -93,4 +93,25 @@ export function insertNewBlockNodeAtSelection(node: LexicalNode, insertAfter: bo
     } else {
         $getRoot().append(node);
     }
+}
+
+export function selectSingleNode(node: LexicalNode) {
+    const nodeSelection = $createNodeSelection();
+    nodeSelection.add(node.getKey());
+    $setSelection(nodeSelection);
+}
+
+export function selectionContainsNode(selection: BaseSelection|null, node: LexicalNode): boolean {
+    if (!selection) {
+        return false;
+    }
+
+    const key = node.getKey();
+    for (const node of selection.getNodes()) {
+        if (node.getKey() === key) {
+            return true;
+        }
+    }
+
+    return false;
 }
\ No newline at end of file
diff --git a/resources/js/wysiwyg/index.ts b/resources/js/wysiwyg/index.ts
index d1d96b172..8cbaccd79 100644
--- a/resources/js/wysiwyg/index.ts
+++ b/resources/js/wysiwyg/index.ts
@@ -2,11 +2,12 @@ import {createEditor, CreateEditorArgs, LexicalEditor} from 'lexical';
 import {createEmptyHistoryState, registerHistory} from '@lexical/history';
 import {registerRichText} from '@lexical/rich-text';
 import {mergeRegister} from '@lexical/utils';
-import {getNodesForPageEditor} from './nodes';
+import {getNodesForPageEditor, registerCommonNodeMutationListeners} from './nodes';
 import {buildEditorUI} from "./ui";
 import {getEditorContentAsHtml, setEditorContentFromHtml} from "./actions";
 import {registerTableResizer} from "./ui/framework/helpers/table-resizer";
 import {el} from "./helpers";
+import {EditorUiContext} from "./ui/framework/core";
 
 export function createPageEditorInstance(container: HTMLElement, htmlContent: string): SimpleWysiwygEditorInterface {
     const config: CreateEditorArgs = {
@@ -59,7 +60,8 @@ export function createPageEditorInstance(container: HTMLElement, htmlContent: st
         }
     });
 
-    buildEditorUI(container, editArea, editor);
+    const context: EditorUiContext = buildEditorUI(container, editArea, editor);
+    registerCommonNodeMutationListeners(context);
 
     return new SimpleWysiwygEditorInterface(editor);
 }
diff --git a/resources/js/wysiwyg/nodes/index.ts b/resources/js/wysiwyg/nodes/index.ts
index e2c6902d3..a2c739576 100644
--- a/resources/js/wysiwyg/nodes/index.ts
+++ b/resources/js/wysiwyg/nodes/index.ts
@@ -1,6 +1,14 @@
 import {HeadingNode, QuoteNode} from '@lexical/rich-text';
 import {CalloutNode} from './callout';
-import {ElementNode, KlassConstructor, LexicalNode, LexicalNodeReplacement, ParagraphNode} from "lexical";
+import {
+    $getNodeByKey,
+    ElementNode,
+    KlassConstructor,
+    LexicalEditor,
+    LexicalNode,
+    LexicalNodeReplacement, NodeMutation,
+    ParagraphNode
+} from "lexical";
 import {CustomParagraphNode} from "./custom-paragraph";
 import {LinkNode} from "@lexical/link";
 import {ImageNode} from "./image";
@@ -11,6 +19,8 @@ import {CustomTableNode} from "./custom-table";
 import {HorizontalRuleNode} from "./horizontal-rule";
 import {CodeBlockNode} from "./code-block";
 import {DiagramNode} from "./diagram";
+import {EditorUIManager} from "../ui/framework/manager";
+import {EditorUiContext} from "../ui/framework/core";
 
 /**
  * Load the nodes for lexical.
@@ -47,5 +57,25 @@ export function getNodesForPageEditor(): (KlassConstructor<typeof LexicalNode> |
     ];
 }
 
+export function registerCommonNodeMutationListeners(context: EditorUiContext): void {
+    const decorated = [ImageNode, CodeBlockNode, DiagramNode];
+
+    const decorationDestroyListener = (mutations: Map<string, NodeMutation>): void => {
+        for (let [nodeKey, mutation] of mutations) {
+            if (mutation === "destroyed") {
+                const decorator = context.manager.getDecoratorByNodeKey(nodeKey);
+                if (decorator) {
+                    decorator.destroy(context);
+                }
+            }
+        }
+    };
+
+    for (let decoratedNode of decorated) {
+        // Have to pass a unique function here since they are stored by lexical keyed on listener function.
+        context.editor.registerMutationListener(decoratedNode, (mutations) => decorationDestroyListener(mutations));
+    }
+}
+
 export type LexicalNodeMatcher = (node: LexicalNode|null|undefined) => boolean;
 export type LexicalElementNodeCreator = () => ElementNode;
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/decorators/code-block.ts b/resources/js/wysiwyg/ui/decorators/code-block.ts
index 80dcef3bd..11cc02e8f 100644
--- a/resources/js/wysiwyg/ui/decorators/code-block.ts
+++ b/resources/js/wysiwyg/ui/decorators/code-block.ts
@@ -1,7 +1,9 @@
 import {EditorDecorator} from "../framework/decorator";
 import {EditorUiContext} from "../framework/core";
 import {$openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
-import {ImageNode} from "../../nodes/image";
+import {selectionContainsNode, selectSingleNode} from "../../helpers";
+import {context} from "esbuild";
+import {BaseSelection} from "lexical";
 
 
 export class CodeBlockDecorator extends EditorDecorator {
@@ -32,12 +34,26 @@ export class CodeBlockDecorator extends EditorDecorator {
 
         const startTime = Date.now();
 
+        element.addEventListener('click', event => {
+            context.editor.update(() => {
+                selectSingleNode(this.getNode());
+            })
+        });
+
         element.addEventListener('dblclick', event => {
             context.editor.getEditorState().read(() => {
                 $openCodeEditorForNode(context.editor, (this.getNode() as CodeBlockNode));
             });
         });
 
+        const selectionChange = (selection: BaseSelection|null): void => {
+            element.classList.toggle('selected', selectionContainsNode(selection, codeNode));
+        };
+        context.manager.onSelectionChange(selectionChange);
+        this.onDestroy(() => {
+            context.manager.offSelectionChange(selectionChange);
+        });
+
         // @ts-ignore
         const renderEditor = (Code) => {
             this.editor = Code.wysiwygView(element, document, this.latestCode, this.latestLanguage);
diff --git a/resources/js/wysiwyg/ui/decorators/image.ts b/resources/js/wysiwyg/ui/decorators/image.ts
index 1e8bfd165..1bc1ea543 100644
--- a/resources/js/wysiwyg/ui/decorators/image.ts
+++ b/resources/js/wysiwyg/ui/decorators/image.ts
@@ -1,5 +1,5 @@
 import {EditorDecorator} from "../framework/decorator";
-import {el} from "../../helpers";
+import {el, selectSingleNode} from "../../helpers";
 import {$createNodeSelection, $setSelection} from "lexical";
 import {EditorUiContext} from "../framework/core";
 import {ImageNode} from "../../nodes/image";
@@ -41,9 +41,7 @@ export class ImageDecorator extends EditorDecorator {
             tracker = this.setupTracker(decorateEl, context);
 
             context.editor.update(() => {
-                const nodeSelection = $createNodeSelection();
-                nodeSelection.add(this.getNode().getKey());
-                $setSelection(nodeSelection);
+                selectSingleNode(this.getNode());
             });
         };
 
diff --git a/resources/js/wysiwyg/ui/defaults/button-definitions.ts b/resources/js/wysiwyg/ui/defaults/button-definitions.ts
index 9f83fbea3..c6ea85b0d 100644
--- a/resources/js/wysiwyg/ui/defaults/button-definitions.ts
+++ b/resources/js/wysiwyg/ui/defaults/button-definitions.ts
@@ -53,6 +53,7 @@ import codeBlockIcon from "@icons/editor/code-block.svg"
 import detailsIcon from "@icons/editor/details.svg"
 import sourceIcon from "@icons/editor/source-view.svg"
 import fullscreenIcon from "@icons/editor/fullscreen.svg"
+import editIcon from "@icons/edit.svg"
 import {$createHorizontalRuleNode, $isHorizontalRuleNode} from "../../nodes/horizontal-rule";
 import {$createCodeBlockNode, $isCodeBlockNode, $openCodeEditorForNode, CodeBlockNode} from "../../nodes/code-block";
 
@@ -344,7 +345,7 @@ export const codeBlock: EditorButtonDefinition = {
     action(context: EditorUiContext) {
         context.editor.getEditorState().read(() => {
             const selection = $getSelection();
-            const codeBlock = getNodeFromSelection(selection, $isCodeBlockNode) as (CodeBlockNode|null);
+            const codeBlock = getNodeFromSelection(context.lastSelection, $isCodeBlockNode) as (CodeBlockNode|null);
             if (codeBlock === null) {
                 context.editor.update(() => {
                     const codeBlock = $createCodeBlockNode();
@@ -363,6 +364,11 @@ export const codeBlock: EditorButtonDefinition = {
     }
 };
 
+export const editCodeBlock: EditorButtonDefinition = Object.assign({}, codeBlock, {
+    label: 'Edit code block',
+    icon: editIcon,
+});
+
 export const details: EditorButtonDefinition = {
     label: 'Insert collapsible block',
     icon: detailsIcon,
diff --git a/resources/js/wysiwyg/ui/framework/decorator.ts b/resources/js/wysiwyg/ui/framework/decorator.ts
index a9917ab23..570b8222b 100644
--- a/resources/js/wysiwyg/ui/framework/decorator.ts
+++ b/resources/js/wysiwyg/ui/framework/decorator.ts
@@ -11,6 +11,8 @@ export abstract class EditorDecorator {
     protected node: LexicalNode | null = null;
     protected context: EditorUiContext;
 
+    private onDestroyCallbacks: (() => void)[] = [];
+
     constructor(context: EditorUiContext) {
         this.context = context;
     }
@@ -27,6 +29,13 @@ export abstract class EditorDecorator {
         this.node = node;
     }
 
+    /**
+     * Register a callback to be ran on destroy of this decorator's node.
+     */
+    protected onDestroy(callback: () => void) {
+        this.onDestroyCallbacks.push(callback);
+    }
+
     /**
      * Render the decorator.
      * Can run on both creation and update for a node decorator.
@@ -35,4 +44,14 @@ export abstract class EditorDecorator {
      */
     abstract render(context: EditorUiContext, decorated: HTMLElement): HTMLElement|void;
 
+    /**
+     * Destroy this decorator. Used for tear-down operations upon destruction
+     * of the underlying node this decorator is attached to.
+     */
+    destroy(context: EditorUiContext): void {
+        for (const callback of this.onDestroyCallbacks) {
+            callback();
+        }
+    }
+
 }
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/framework/manager.ts b/resources/js/wysiwyg/ui/framework/manager.ts
index 6477c4a1a..cfa94e8ae 100644
--- a/resources/js/wysiwyg/ui/framework/manager.ts
+++ b/resources/js/wysiwyg/ui/framework/manager.ts
@@ -1,11 +1,13 @@
 import {EditorFormModal, EditorFormModalDefinition} from "./modals";
 import {EditorContainerUiElement, EditorUiContext, EditorUiElement, EditorUiStateUpdate} from "./core";
 import {EditorDecorator, EditorDecoratorAdapter} from "./decorator";
-import {$getSelection, COMMAND_PRIORITY_LOW, LexicalEditor, SELECTION_CHANGE_COMMAND} from "lexical";
+import {$getSelection, BaseSelection, COMMAND_PRIORITY_LOW, LexicalEditor, SELECTION_CHANGE_COMMAND} from "lexical";
 import {DecoratorListener} from "lexical/LexicalEditor";
 import type {NodeKey} from "lexical/LexicalNode";
 import {EditorContextToolbar, EditorContextToolbarDefinition} from "./toolbars";
 
+export type SelectionChangeHandler = (selection: BaseSelection|null) => void;
+
 export class EditorUIManager {
 
     protected modalDefinitionsByKey: Record<string, EditorFormModalDefinition> = {};
@@ -15,6 +17,7 @@ export class EditorUIManager {
     protected toolbar: EditorContainerUiElement|null = null;
     protected contextToolbarDefinitionsByKey: Record<string, EditorContextToolbarDefinition> = {};
     protected activeContextToolbars: EditorContextToolbar[] = [];
+    protected selectionChangeHandlers: Set<SelectionChangeHandler> = new Set();
 
     setContext(context: EditorUiContext) {
         this.context = context;
@@ -72,6 +75,10 @@ export class EditorUIManager {
         return decorator;
     }
 
+    getDecoratorByNodeKey(nodeKey: string): EditorDecorator|null {
+        return this.decoratorInstancesByNodeKey[nodeKey] || null;
+    }
+
     setToolbar(toolbar: EditorContainerUiElement) {
         if (this.toolbar) {
             this.toolbar.getDOMElement().remove();
@@ -94,7 +101,7 @@ export class EditorUIManager {
         for (const toolbar of this.activeContextToolbars) {
             toolbar.updateState(update);
         }
-        // console.log('selection update', update.selection);
+        this.triggerSelectionChange(update.selection);
     }
 
     triggerStateRefresh(): void {
@@ -104,6 +111,24 @@ export class EditorUIManager {
         });
     }
 
+    protected triggerSelectionChange(selection: BaseSelection|null): void {
+        if (!selection) {
+            return;
+        }
+
+        for (const handler of this.selectionChangeHandlers) {
+            handler(selection);
+        }
+    }
+
+    onSelectionChange(handler: SelectionChangeHandler): void {
+        this.selectionChangeHandlers.add(handler);
+    }
+
+    offSelectionChange(handler: SelectionChangeHandler): void {
+        this.selectionChangeHandlers.delete(handler);
+    }
+
     protected updateContextToolbars(update: EditorUiStateUpdate): void {
         for (const toolbar of this.activeContextToolbars) {
             toolbar.empty();
diff --git a/resources/js/wysiwyg/ui/index.ts b/resources/js/wysiwyg/ui/index.ts
index 50307fa61..748370959 100644
--- a/resources/js/wysiwyg/ui/index.ts
+++ b/resources/js/wysiwyg/ui/index.ts
@@ -1,5 +1,10 @@
 import {LexicalEditor} from "lexical";
-import {getImageToolbarContent, getLinkToolbarContent, getMainEditorFullToolbar} from "./toolbars";
+import {
+    getCodeToolbarContent,
+    getImageToolbarContent,
+    getLinkToolbarContent,
+    getMainEditorFullToolbar
+} from "./toolbars";
 import {EditorUIManager} from "./framework/manager";
 import {image as imageFormDefinition, link as linkFormDefinition, source as sourceFormDefinition} from "./defaults/form-definitions";
 import {ImageDecorator} from "./decorators/image";
@@ -7,7 +12,7 @@ import {EditorUiContext} from "./framework/core";
 import {CodeBlockDecorator} from "./decorators/code-block";
 import {DiagramDecorator} from "./decorators/diagram";
 
-export function buildEditorUI(container: HTMLElement, element: HTMLElement, editor: LexicalEditor) {
+export function buildEditorUI(container: HTMLElement, element: HTMLElement, editor: LexicalEditor): EditorUiContext {
     const manager = new EditorUIManager();
     const context: EditorUiContext = {
         editor,
@@ -48,9 +53,15 @@ export function buildEditorUI(container: HTMLElement, element: HTMLElement, edit
         selector: 'a',
         content: getLinkToolbarContent(),
     });
+    manager.registerContextToolbar('code', {
+        selector: '.editor-code-block-wrap',
+        content: getCodeToolbarContent(),
+    });
 
     // Register image decorator listener
     manager.registerDecoratorType('image', ImageDecorator);
     manager.registerDecoratorType('code', CodeBlockDecorator);
     manager.registerDecoratorType('diagram', DiagramDecorator);
+
+    return context;
 }
\ No newline at end of file
diff --git a/resources/js/wysiwyg/ui/toolbars.ts b/resources/js/wysiwyg/ui/toolbars.ts
index 25a7e7815..d512b58e2 100644
--- a/resources/js/wysiwyg/ui/toolbars.ts
+++ b/resources/js/wysiwyg/ui/toolbars.ts
@@ -1,7 +1,7 @@
 import {EditorButton} from "./framework/buttons";
 import {
     blockquote, bold, bulletList, clearFormating, code, codeBlock,
-    dangerCallout, details, fullscreen,
+    dangerCallout, details, editCodeBlock, fullscreen,
     h2, h3, h4, h5, highlightColor, horizontalRule, image,
     infoCallout, italic, link, numberList, paragraph,
     redo, source, strikethrough, subscript,
@@ -9,7 +9,7 @@ import {
     undo, unlink,
     warningCallout
 } from "./defaults/button-definitions";
-import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiContext, EditorUiElement} from "./framework/core";
+import {EditorContainerUiElement, EditorSimpleClassContainer, EditorUiElement} from "./framework/core";
 import {el} from "../helpers";
 import {EditorFormatMenu} from "./framework/blocks/format-menu";
 import {FormatPreviewButton} from "./framework/blocks/format-preview-button";
@@ -111,4 +111,10 @@ export function getLinkToolbarContent(): EditorUiElement[] {
         new EditorButton(link),
         new EditorButton(unlink),
     ];
+}
+
+export function getCodeToolbarContent(): EditorUiElement[] {
+    return [
+        new EditorButton(editCodeBlock),
+    ];
 }
\ No newline at end of file
diff --git a/resources/sass/_editor.scss b/resources/sass/_editor.scss
index 1f932e147..99045dd5a 100644
--- a/resources/sass/_editor.scss
+++ b/resources/sass/_editor.scss
@@ -312,6 +312,9 @@ body.editor-is-fullscreen {
   > * {
     pointer-events: none;
   }
+  &.selected .cm-editor {
+    border: 1px dashed var(--editor-color-primary);
+  }
 }
 
 // Editor form elements