Files
gitmost/apps/client/src/features/editor/styles/code.css
claude code agent 227 38544e2ddc fix(editor): render NodeViewContent first so click hit-testing isn't offset (#146)
Three editable NodeViews rendered a contentEditable=false "chrome" element IN
FLOW BEFORE NodeViewContent. On macOS the browser's click hit-testing
(posAtCoords → caretRangeFromPoint) then misses the contentDOM and snaps the
caret to the previous node — the caret/selection lands a line (code block) or
several lines (footnotes, into the body) above where the user clicked.

Fix (the transclusion pattern / issue #146 plan): make the editable
NodeViewContent the FIRST child in the DOM and move the non-editable chrome
AFTER it, restoring its visual position with CSS:

- code-block-view: <pre><NodeViewContent/></pre> first; the language/copy menu
  follows and is lifted above via flex `order` (.codeBlock is now a flex column).
- footnotes-list-view: NodeViewContent first; the "Footnotes" heading follows and
  is lifted above via flex `order` (.list is a flex column; the separator border
  stays on the container).
- footnote-definition-view: NodeViewContent first; the "N." marker follows with
  `order:-1` to stay on the left; the ↩ back-link stays on the right.

Layout is visually unchanged. Verified in a real browser (Chromium): the
contentDOM is now the first child of every editable NodeView wrapper (no
contentEditable=false element precedes it), and the menu/heading/marker still
render in their original positions.

NOTE: the caret-offset itself is macOS-specific text hit-testing and does not
reproduce in headless Chromium/WebKit on Linux (verified extensively), so the
visible fix is best confirmed on macOS.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-23 21:48:21 +03:00

120 lines
2.7 KiB
CSS

.ProseMirror {
.codeBlock {
/* #146: flex column so the menu (rendered AFTER <pre> in the DOM, so the
editable contentDOM is first) is lifted back above the code via `order`. */
display: flex;
flex-direction: column;
padding: 4px;
border-radius: var(--mantine-radius-default);
background-color: light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-8));
}
pre {
padding: var(--mantine-spacing-xs) var(--mantine-spacing-md);
margin: 4px;
font-family: "JetBrainsMono", var(--mantine-font-family-monospace);
border-radius: var(--mantine-radius-default);
tab-size: 4;
@mixin light {
background-color: var(--mantine-color-gray-0);
color: var(--mantine-color-gray-9);
}
@mixin dark {
background-color: var(--mantine-color-dark-8);
color: var(--mantine-color-dark-1);
}
code {
color: inherit;
padding: 0;
background: none;
font-size: var(--mantine-font-size-sm);
}
/* Code styling */
.hljs-comment,
.hljs-quote {
color: light-dark(
var(--mantine-color-gray-6),
var(--mantine-color-dark-2)
);
}
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: light-dark(var(--mantine-color-red-7), var(--mantine-color-red-5));
}
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: light-dark(
var(--mantine-color-blue-7),
var(--mantine-color-cyan-5)
);
}
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: light-dark(var(--mantine-color-red-7), var(--mantine-color-red-5));
}
.hljs-title,
.hljs-section {
color: light-dark(
var(--mantine-color-pink-7),
var(--mantine-color-yellow-5)
);
}
.hljs-keyword,
.hljs-selector-tag {
color: light-dark(
var(--mantine-color-violet-7),
var(--mantine-color-violet-3)
);
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}
}
:not(pre) > code {
font-family: "JetBrainsMono", var(--mantine-font-family-monospace);
line-height: var(--mantine-line-height);
padding: 2px calc(var(--mantine-spacing-xs) / 2);
border-radius: var(--mantine-radius-sm);
margin: 0;
@mixin where-light {
background-color: var(--mantine-color-gray-1);
color: var(--mantine-color-text);
}
@mixin where-dark {
background-color: var(--mantine-color-dark-5) !important;
color: var(--mantine-color-text);
}
}
}