Code
The code plugin adds code blocks made of editable lines, highlighted with TanStack Highlight, with tab, auto-pairing and copy.
codePlugin adds code blocks. A code block is an island that holds one codeLine child per line of code; each line is highlighted as JSX with TanStack Highlight (@tanstack/highlight, a dependency of edytor).
<script lang="ts">
import { Edytor, codePlugin } from 'edytor';
</script>
<Edytor plugins={[codePlugin]} />
It takes no options. Importing it also imports its stylesheet, which gives code blocks Notion’s look.
What it adds
| Definition | Kind | Details |
|---|---|---|
code |
block | An island that renders only its children, under a header with the language and a Copy button. |
codeLine |
block | One line of code. The default child of code. |
codeToken |
mark | A highlighting token, rendered as <span class="th-…">. Computed on render, never stored. |
A code block has one preset, “Code”, with the command id block.code, the markdown shortcut ``` (three backticks typed at the start of an empty block) and the slash menu section Media. Mod + Alt + 8 converts to it when the rich text plugin is loaded, as it is by default. An empty block converts in place, into a code block of one empty line. A block with text or children stays as it is: the command (the slash menu, the chord) inserts an empty code block after it instead, and the caret moves to its line. See Presets.
A stored code block looks like this:
{
"type": "code",
"children": [
{ "type": "codeLine", "content": [{ "text": "const idea = \"start somewhere\";" }] },
{ "type": "codeLine", "content": [{ "text": "console.log(idea);" }] }
]
}
Highlighting
Each codeLine declares a transformText that tokenizes its text with a highlighter built from createHighlighter({ languages: [jsx] }). Tokens become codeToken marks carrying the token’s class name. For const idea = "x"; the tokenizer answers a keyword token, plain text, a string token and plain text, rendered as:
<span data-edytor-mark="codeToken"><span class="th-keyword">const</span></span> idea = <span data-edytor-mark="codeToken"><span class="th-string">"x"</span></span>;
The tokens are decorations: they exist only in the rendered DOM, never in the document, so collaborators and the clipboard see plain text. The language is always JSX (which also covers JavaScript), and the header label reads “JavaScript”.
The bundled stylesheet colors the th-* classes with Notion’s light syntax theme:
| Classes | Color |
|---|---|
th-comment, th-meta |
#708090 |
th-keyword, th-link |
#0077aa |
th-string, th-attr, th-selector, th-inserted |
#669900 |
th-function, th-type |
#dd4a68 |
th-number, th-literal, th-tag, th-property, th-deleted |
#990055 |
th-variable |
#ee9900 |
th-operator |
#9a6e3a |
Override those classes to theme it.
Editing
Inside a code line:
| Keys | Action |
|---|---|
| Enter, Shift + Enter | Start a new line |
| Tab | At a caret, insert a tab character, or accept an inline suggestion. Over a selection, add a tab at the start of every line it touches, as one undo step; the selection keeps its characters |
| Shift + Tab | Remove one leading tab, or up to two spaces, from the caret’s line or every line the selection touches |
| Mod + A | From a non-empty line, select the text of the whole code block |
| Escape | Dismiss an inline suggestion |
- Typing
(,[,{,"or'at a caret also inserts the closing character. A quote pairs only at a word boundary: the apostrophe indon'tstays single. - Typing a closing character (
),],},",') right before the same character steps over it instead of inserting another:f(a)types asf(a), notf(a)). - A line break inside a line splits it: each code line holds exactly one line of text.
- Backspace at the start of the first line and Delete at the end of the last line do nothing: a line never merges out of its block.
- Delete at the end of the block right before a code block removes that block if it is empty, and does nothing otherwise. The caret goes to the end of the text before the removed block, or to the start of the code when nothing comes before it.
- Backspace in an empty block right after a code block removes that block and puts the caret at the end of the last line. In a block with text it moves the caret to the end of the code and merges nothing.
- A selection that ends at the start of a line leaves that line out of Tab and Shift + Tab, as code editors do.
- The Copy button, shown while the block is hovered, writes the block’s text, lines joined with newlines, to the clipboard, and reads “Copied” for a moment.
A code block is an island: merges never cross its edge, and a block selection walked with the arrow keys does not enter it from outside. It is also not convertible, so the slash menu and markdown shortcuts do not apply inside it.
Styling
The stylesheet draws the block like Notion’s: a #f7f6f3 panel rounded 10px, padded 34px 16px 32px 32px, with the language at the top left and the Copy button at the top right. The code is set in an SFMono-Regular, Menlo, Consolas stack at 85%, with a tab size of 2. No utility classes are involved; everything goes through attributes and classes you can override:
| Selector | Element |
|---|---|
[data-edytor-type='code'] |
The block panel |
[data-edytor-code-header] |
The header, void chrome the caret never enters |
[data-edytor-code-language] |
The language label |
[data-edytor-code-header] button |
The Copy button |
pre.th-code |
The code area |
[data-edytor-type='code'] {
background: #1f1f1f;
color: #e6e6e6;
}
[data-edytor-type='code'] pre.th-code {
font-size: 14px;
}
The Notion theme reads the panel color from --notion-code-panel.