Skip to content
Edytor
Esc
↑↓navigate↵open⌘Jpreview
On this page

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 in don't stays single.
  • Typing a closing character (), ], }, ", ') right before the same character steps over it instead of inserting another: f(a) types as f(a), not f(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.

Was this page helpful?