Rough Highlighter
unlistedby Tilak
Draws hand-drawn annotations over highlighted text, in reading view and live preview.
Rough Highlighter
Hand-drawn highlight annotations for Obsidian, drawn with the geometry engine behind rough-notation.
Works in reading view and in live preview.
Syntax
| In the note | Result |
|---|---|
==text== | The default style from settings |
<mark class="rn-circle rn-red">text</mark> | A red circle |
Both are plain Obsidian highlights, so a note stays readable with the plugin turned off.
Type classes: rn-highlight, rn-underline, rn-box, rn-circle,
rn-strike, rn-crossed, rn-bracket.
Color classes: rn-yellow, rn-red, rn-green, rn-blue, rn-purple,
rn-orange.
Right-click menu
Select text and right-click. A Highlight submenu holds:
- Plain, which wraps the selection in
==. - The six palette colors, each using the default type from settings.
- More styles..., which opens the full type and color list.
- Remove highlight, shown only when the selection holds one.
Submenus need a recent Obsidian. On older builds the menu falls back to two flat items: Highlight and Highlight as....
Commands
The same actions, for hotkeys:
- Highlight selection wraps the selection in
==. - Highlight selection as... picks a type and a color, and writes a
<mark>. - Remove highlight from selection strips both forms.
Changing the palette
The colors are CSS variables. Put a snippet in your vault to change them:
body {
--rn-yellow: #ffe066;
--rn-red: #ff6b6b;
}
How it works
rough-notation's public annotate() API attaches its own SVG next to a DOM
element. That works in reading view, but CodeMirror 6 owns the editor DOM and
removes foreign nodes, and the API gives no control over the random seed, so
every repaint would reshuffle the drawing.
So this plugin uses the library's lower-level renderAnnotation() instead,
which draws into an SVG we own with a seed we choose. The seed comes from a hash
of the highlighted text, so a phrase always gets the same wobble.
Two hosts feed that one renderer:
- Reading view puts the SVG inside the
<mark>itself. It moves with the text, so only a change to the mark's own box needs a redraw. AnIntersectionObserverdraws on first sight, and animates. - Live preview uses a CodeMirror
layer. Obsidian already renders==text==asspan.cm-highlightand inline<mark>as a real element, so the layer measures those elements rather than parsing the source. CodeMirror recomputes the layer on every geometry change, which keeps the drawing glued to the text.
Known limits
-
Highlight ink is translucent and drawn behind the text. Every other type is drawn over it. In live preview that split needs two CodeMirror layers, one below the content and one above.
-
No draw-on animation in live preview. That view repaints on every geometry change, so an animation would keep restarting.
-
Wrapped text gets one shape per visual line. A
boxaround three wrapped lines is three boxes, not one outline. -
The annotation hides while the cursor is inside it, because Obsidian reveals the raw source there and the text shifts on every keystroke.
Install
The repo carries a built main.js, so you can copy main.js, manifest.json
and styles.css into <vault>/.obsidian/plugins/rough-highlighter/, then turn
the plugin on under Settings > Community plugins.
License
MIT. main.js bundles rough-notation, roughjs, hachure-fill and
path-data-parser, all MIT, all Copyright (c) Preet Shihn. See
THIRD-PARTY-NOTICES.md.
Build
npm install
npm run build # type check, then bundle to main.js
npm run dev # watch
OBSIDIAN_VAULT=/path/to/vault npm run deploy # build, then copy into a vault
For plugin developers
Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.