diff --git a/apps/client/src/features/editor/components/code-block/code-block-view.tsx b/apps/client/src/features/editor/components/code-block/code-block-view.tsx index 1930f182..39b35229 100644 --- a/apps/client/src/features/editor/components/code-block/code-block-view.tsx +++ b/apps/client/src/features/editor/components/code-block/code-block-view.tsx @@ -50,10 +50,10 @@ export default function CodeBlockView(props: NodeViewProps) { {/* #146: the editable
(contentDOM) MUST come first in the DOM.
With the non-editable menu rendered before it, the browser's click
hit-testing snapped the caret up one line. Render content first; the
- menu is rendered after it and lifted back above visually via flex
- `order: -1` (the `.codeBlock` wrapper is a flex column — see
- code-block.module.css). It stays fully in flow as a full-width row
- above the code: no overlay/absolute positioning. The second #146
+ menu is rendered after it and floated into the top-right corner as an
+ absolute overlay (see `.menuGroup` in code-block.module.css, anchored
+ to the `position: relative` `.codeBlock` wrapper in code.css). It no
+ longer takes a full-width row above the code. The second #146
mitigation lives in editor-paste-handler.tsx (reflowAfterPaste). */}
-
+ {/* In read-only (published) there is no language selector at all —
+ only the copy button. When editable the selector is hidden until
+ the block is hovered/focused (or its dropdown is open) via the
+ `.languageSelect` class (see code-block.module.css). */}
+ {editor.isEditable && (
+
+ )}
{({ copied, copy }) => (
diff --git a/apps/client/src/features/editor/components/code-block/code-block.module.css b/apps/client/src/features/editor/components/code-block/code-block.module.css
index 4ecda370..e2cb6faf 100644
--- a/apps/client/src/features/editor/components/code-block/code-block.module.css
+++ b/apps/client/src/features/editor/components/code-block/code-block.module.css
@@ -17,15 +17,37 @@
justify-content: center;
}
-/* #146: the menu now follows the in the DOM (so the editable contentDOM is
- FIRST and click hit-testing is correct). Lift it back ABOVE the code visually
- with flex `order` — the .codeBlock wrapper is a flex column (see code.css) —
- so the menu still reads as a row above the code, exactly as before, without
- sitting in-flow before the contentDOM. */
+/* #146: the menu follows the in the DOM (so the editable contentDOM is
+ FIRST and click hit-testing is correct). Instead of sitting in-flow, it is
+ floated into the top-right corner as an absolute overlay anchored to the
+ `position: relative` .codeBlock wrapper (see code.css), so it no longer
+ takes a full-width row above the code. The Mantine dropdown is portaled, so
+ it is never clipped by the overlay. */
.menuGroup {
- order: -1;
+ position: absolute;
+ top: 8px;
+ right: 8px;
+ z-index: 1;
+ gap: 4px;
@media print {
display: none;
}
}
+
+/* The language selector is hidden until the block is hovered, or the selector
+ itself is focused / its dropdown is open. It keeps its width in the flex
+ Group (only opacity toggles) so the copy button never jumps, and
+ `pointer-events: none` while hidden lets clicks fall through to the code.
+ `.codeBlock` is the global NodeViewWrapper class → use :global(). */
+.languageSelect {
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 150ms ease;
+}
+
+:global(.codeBlock):hover .languageSelect,
+.languageSelect:focus-within {
+ opacity: 1;
+ pointer-events: auto;
+}
diff --git a/apps/client/src/features/editor/styles/code.css b/apps/client/src/features/editor/styles/code.css
index 100e4153..9aa1cdab 100644
--- a/apps/client/src/features/editor/styles/code.css
+++ b/apps/client/src/features/editor/styles/code.css
@@ -1,9 +1,12 @@
.ProseMirror {
.codeBlock {
- /* #146: flex column so the menu (rendered AFTER in the DOM, so the
- editable contentDOM is first) is lifted back above the code via `order`. */
+ /* #146: flex column keeps the editable (first in the DOM so click
+ hit-testing is correct) laid out above any Mermaid diagram. `position:
+ relative` anchors the control panel, which is floated into the top-right
+ corner as an absolute overlay (see `.menuGroup` in code-block.module.css). */
display: flex;
flex-direction: column;
+ position: relative;
padding: 4px;
border-radius: var(--mantine-radius-default);
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8));