Angle Guard

by Yuriy Vereshchagin
5
4
3
2
1
Score: 50/100

Description

Captures and wraps HTML tags inside pasted text so they don't break the note's formatting.

Reviews

No reviews yet.

Stats

0
stars
31
downloads
0
forks
22
days
22
days
22
days
0
total PRs
0
open PRs
0
closed PRs
0
merged PRs
0
total issues
0
open issues
0
closed issues
1
commits

Latest Version

23 days ago

Changelog

This is the first release of Angle Guard, a plugin for Obsidian that captures and wraps HTML tags inside pasted text so they don't break the note's formatting.

README file from

Github

Angle Guard

Captures and wraps HTML tags in pasted text so they don't break the note's formatting.

Paste a paragraph that contains <name> somewhere in the middle, and everything below it stops rendering. Headings go flat, lists lose their bullets, callouts unroll. The source still holds every character, but the note no longer displays it.

Why it happens

Markdown allows raw HTML, and a tag name can be any identifier — it does not have to be a real element. So <name> is read as an opening HTML tag. The renderer passes it to the browser, the browser opens an element that is never closed, and each sibling after it is pulled into that element.

This is not a fault in Obsidian. It is Markdown following its specification, applied to text that was never meant to be markup.

What it does

On paste, it finds tag-shaped text and moves it where the renderer will not read it as markup. The characters are left unchanged.

Situation Result
A tag inside a sentence Wrapped in a code span: `<name>`
A tag on its own line Placed in a code block
Several lines of HTML The whole run is placed in one code block
A full HTML document One code block, not one edit per tag

It leaves the following alone:

  • A bare < in prose, as in a < b.
  • Anything already inside a code span, code fence, or YAML frontmatter.
  • Autolinks such as <https://example.com> and <[email protected]>.
  • Inline and block math, and wikilinks.
  • Text already escaped by hand as \<name>.
  • Tags on the ignore list — br, img, hr, sup, sub, u, mark, kbd by default.

Where text is copied from

Where the text is copied from changes what lands on the clipboard.

Copying from a Markdown code block inside Obsidian puts the literal <name> on the clipboard as plain text, so it is caught before it is inserted. Copying from a rendered HTML preview — a web page, the reading view, a browser — does not. There, <name> renders as an empty element with no visible text, so the plain-text copy drops it and only the clipboard's HTML flavor still carries it. Obsidian then pastes its own conversion of that HTML, which puts <name> back into the note after the plugin has already looked at the paste.

The plugin handles this by recording the note at paste time and comparing against it once the paste settles, then protecting whatever was inserted. The check retries across a few frames, so it does not depend on the paste being synchronous.

Commands

Command What it does
Protect raw HTML in current note Repairs a note that is already broken
Protect raw HTML in selection The same, for a selection. Also in the right-click menu
Toggle protection on paste Turns protection on or off. Also the status bar icon

Settings

Protect on paste is the master switch, on after install. Ask first turns silent protection into a confirmation prompt. Tell me what was fixed shows a short note after each change. Status bar shortcut adds the </> icon, which is struck through when protection is off.

How much to catch sets the scope. Anything that looks like a tag is the default. Only unknown or unclosed tags leaves correctly paired HTML such as <b>bold</b> as written. Tags to leave alone is your own ignore list. Check pastes from the web covers the HTML-preview case above.

Tags in a sentence chooses between a code span and a backslash escape. Where a line already contains an unpaired backtick, the plugin escapes regardless, because a code span would not survive there. Blocks of HTML puts standalone or multi-line HTML in a code block. Code block label is the language written after the opening fence.

Building from source

npm install
npm run build     # type-checks, then writes main.js
npm run lint      # the official Obsidian plugin review rules
npm test          # 44 unit tests, plus a runtime smoke test
npm run dev       # rebuild on change

src/protect.ts holds the protection logic and has no Obsidian dependency, so it is tested on its own. The plugin bundles no third-party code at runtime; it uses only the Obsidian API.

Acknowledgements

  • Build setup, release workflow, and manifest.json / versions.json conventions follow the Obsidian sample plugin (MIT).
  • Code is checked against eslint-plugin-obsidianmd, the official plugin review rules.
  • The tag grammar in src/protect.ts implements the raw-HTML rules from the CommonMark specification.
  • This plugin was built using Claude Opus 5.0 and Opus 4.8. The problem was first worked through with the help of DeepSeek and Poolside, whose design discussions are recorded in the project notes. The angle-bracket rendering behaviour it addresses was identified in day-to-day use.

License

MIT.