← All plugins

Comment on any HTML,
hand it back to your agent

A code review for your AI's plans and reports. Select any paragraph, table cell, chart, or diagram in a standalone HTML and comment inline exactly where you mean it, then hit Copy all to hand the agent a structured bundle of every note.

A commentable HTML report with '165 million' highlighted in a paragraph and an inline comment window open beside it: the quoted '165 million' selection above a 'Write your review comment' box with Cancel and Save comment buttons.
Select any text and a comment window opens right where you mean it - no alt-tabbing to the chat.

The review loop, in four steps

Turn any HTML plan or report into something you comment on inline and round-trip back to the agent - a tight review loop instead of describing changes in prose.

  1. Select

    Highlight any paragraph, table cell, chart, diagram node, or code block.

  2. Comment inline

    Leave a note in the window that opens right on your selection.

  3. Copy all

    Hand the agent one structured bundle of every open comment.

  4. Reload

    The agent edits and marks notes handled; handled comments prune away.

Demo

Why Commentable HTML

HTML has become the de-facto standard for planning and reporting with AI agents: plans, reports, design docs, and code reviews increasingly arrive as HTML, not Markdown. It is a spatial, richer medium - diffs and call-graphs as boxes and arrows, data as real charts, long documents as navigable sections. (Anthropic makes the same case in "The unreasonable effectiveness of HTML".) But the moment you want to revise that artifact the loop breaks down: you alt-tab between the render and the chat and describe in prose what to change. The richer the document, the more painful the round-trip.

Commentable HTML closes that loop - it is a code review for your plan. Select any paragraph, table cell, chart, diagram, or code block and comment in place, exactly where you mean it, then one Copy all hands the agent a structured bundle of every note. It drastically shortens the AI planning and iteration loop: you stay in the document and return to the chat only at the end.

Commentable HTML self-review loop An AI agent generates a commentable HTML report; you review it and leave inline comments; you Copy all the comments back to the agent; the agent returns the updated file and you repeat until every comment is resolved. AI agent You 1. Generates HTML 2. Comment Inline 3. Copy all back to the agent 4. Reload and repeat Commentable HTML self-review loop Step 1: the AI agent generates the HTML report and hands it down the left arrow to you. Step 2: you comment inline on it. Step 3: you copy all the notes back up the right arrow to the agent. Step 4: the agent returns the updated file and you reload and repeat until every comment is resolved. AI agent You 1 Generates HTML 3 Copy all back to the agent 2 Comment inline 4 Reload and repeat

Chat, Markdown, HTML - or a tight loop?

Your agent can draft a brilliant plan in seconds. The real bottleneck is everything after - reading it, reacting to it, and getting your changes back into the model. Where that review happens decides how fast you actually move.

Where you planHandles a big planVisual richnessInteractiveIn-place reviewReview loop
Chat / terminal PoorNoneNoNoPainful
Markdown file OK / Need viewerLimitedNoNoManual
Plain HTML GoodRichYesNoOut-of-band
Commentable HTML GoodRichYesYesTight
  • In the chat window. The plan scrolls past in a terminal or chat app and is gone. Long plans are a chore to navigate and hard to keep or share - great for a sentence, hopeless for a spec.
  • In a Markdown file. It survives, but raw Markdown is a slog to read and even a viewer stays flat: no real tables, diagrams, or charts. Few teammates read a 300-line .md.
  • In plain HTML. Dense, structured, and nice to look at. But review still breaks: you read in the browser, then switch to the chat and describe, in words, what to change and where. Out-of-band and slow.
  • With Commentable HTML. An even richer HTML - plus review in place. Comment on the exact sentence, table cell, chart, or diagram node; one Copy all hands the agent every note. The loop collapses to a single tight cycle.

Install

Works with both Claude Code and the GitHub Copilot CLI, and is invokable from each agent's CLI and Desktop app. Pick your agent, add the marketplace, then install the plugin.

Install marketplace
$
copilot plugin marketplace add https://github.com/urikanonov/ai-marketplace
Install plugin
$
copilot plugin install commentable-html@urikan-ai-marketplace
Auto-updater logo Keep it up to date automatically Install the auto-updater plugin to fetch new commentable-html releases on session start - always the latest fixes and features, no manual plugin update.

What you get

A full code-review layer that travels with the document; export to one portable HTML file when you want to share.

Comment on anything

Prose, table cells, code blocks, KQL queries, charts, images, and mermaid diagram nodes are all selectable and commentable.

Rich content, not just prose

Author Chart.js charts, Mermaid diagrams, drag-and-drop triage boards, rendered code diffs (inline or side-by-side), and syntax-highlighted code and KQL snippets - all first-class, and every one of them individually commentable.

Self-contained when you share

By default the layer loads from small companion files for cheap iteration. One click inlines the CSS, review UI, and every comment into a single HTML file you can email, archive, or reopen anywhere - no server and no build step.

Round-trip to the agent

One Copy all hands the agent every comment at once - a Markdown bundle plus a machine-readable id list - so it makes a single coordinated, coherent edit across all your notes instead of a one-at-a-time pass, then marks them handled.

Export as Portable or Offline

Export as Portable inlines the review layer and comments so a review travels in one file. Export Offline goes further: it snapshots the mermaid diagrams and charts and strips every remote loader, so the file opens with no network at all.

Handled comments stay gone

Once the agent marks a comment handled, it is pruned on reload. The HTML file is the single source of truth, not the browser cache.

Comments survive a restart

While you iterate, every comment is saved to your browser's localStorage, so closing the tab, restarting the browser, or rebooting your machine never loses your work - reopen the file and your comments are still there.

Deterministic tools

Ships stdlib-only Python tools and a strict validator so generated documents are correct by construction, not by luck.

Commentable decks

Turn a request into an animation-rich, fixed 16:9 slide deck that is also a commentable document: present it full-screen, toggle comment mode to leave notes on the live slides, navigate with the slide bar or arrow keys, and Export Offline for a network-silent handoff.

Deck engine: a curated, vendored subset of frontend-slides by Zara Zhang (MIT). See Credits.

Navigate long documents

A live table of contents, collapsible sections, and one-click jump from any comment straight to its highlight keep even a long report easy to move through.

No extension, runs everywhere

Opening a generated file to read or comment needs no browser extension, plugin, or install - the whole review layer is plain HTML, CSS, and JavaScript inside the file, so anyone you share it with opens it in any modern browser on Windows, macOS, and Linux. (Authoring new documents uses the commentable-html plugin.)

Only as big as it needs to be

Mermaid and Chart.js travel inside the file so diagrams and charts still render offline - but only in a document that actually has one. A prose-and-code review document drops that 1.3 MB renderer entirely, which is about 59 percent of its size, and what remains is kept out of the head so the top of the file stays readable to ordinary tools.

How the review loop works

Feedback goes back to the agent as structured data, not pasted prose.

Self review

Generate

The agent produces a commentable HTML report.

Comment

You select text and leave inline notes in the sidebar.

Copy all

Hand the agent a Markdown bundle of every open comment.

Reload

The agent acts and marks ids handled; handled comments prune away.

Peer review

Self review first

Run the self-review loop to get the artifact in good shape.

Export as Portable

Download one self-contained copy and share it with a peer.

Peer comments

The peer opens it, adds inline comments, and Exports as Portable again - comments now embedded in the HTML.

Back to the agent

You open their Portable file and Copy all to feed their feedback to the agent.

Review someone's plan

Receive a plan

A colleague sends a plan or report as markdown or HTML.

Convert

Ask the agent to turn it into a commentable HTML with this skill.

Review inline

Add your comments in place, on exactly the parts you mean.

Export and send back

Export as Portable and return it with your comments embedded.

Demo

Real commentable examples, including the flagship showcase deck. Select any text and the Add comment popup appears.

Full screen ↗ NYC Taxi Report

Private by design

Your document and every comment stay on your machine. There is no server, no account, no sign-in, and no telemetry - the review layer runs entirely in your browser as a static file.

Your data stays local

Comments live in one of two places only: your browser's localStorage, scoped to that file, while you iterate, or embedded inside the HTML file itself once you Export. Your document text and your notes are never uploaded or transmitted.

Nothing is sent externally

Your content and comments are never sent to us or to any service. They travel only when you choose to - when you share the exported file, or paste the Copy all bundle to your agent yourself.

You own every copy

The HTML file is the single source of truth, not a cloud account. Keep it, archive it, or delete it and the data is gone - no residual copy lives anywhere else. Comments are keyed to a stable id baked into the document, so when it is served from one web origin, moving or renaming it there keeps your browser-side notes; and an exported file always carries every comment inside it, so a shared or archived copy never depends on any browser store.

Offline and compliance-ready

The only optional network use is loading the Mermaid and Chart.js rendering libraries from a public CDN (library code, not your data). Export Offline snapshots those visuals and strips every remote loader, so the file opens with zero network - fit for air-gapped, sensitive, or regulated material.

Three portability modes

Same document, three ways to assemble it - pick based on whether it stays local, travels, or must work with no network. The graph in each card shows where every part comes from.

Non-portable default

References shared companion assets instead of inlining them, so each regeneration is far cheaper in tokens. Best for fast, personal iteration while you and the agent shape the document.

Styles + runtime
skill folder
Mermaid + charts
CDN
Comments
browser storage

Portable

One self-contained file with the CSS, runtime, and comments inlined. Best when you want to share the document with a peer or keep it as a durable, long-term artifact - one click via Export as Portable.

Styles + runtime
inlined
Mermaid + charts
CDN
Comments
seeded from HTMLthen browser storage

Offline

Everything Portable does, plus the mermaid diagrams and charts are rendered and inlined as snapshots and every remote loader is stripped, so the file opens with zero network - archive it or read it on a plane. One click via Export Offline.

Styles + runtime
inlined
Mermaid + charts
inlined snapshots
Comments
seeded from HTMLthen browser storage

CDN marks the parts that load over the network. A Non-portable or Portable report that uses mermaid diagrams or charts therefore needs an internet connection to render them - static images and the rest of the review layer still work with no network. Export Offline to inline those diagrams and charts as snapshots and drop the network dependency entirely.

Credits

The open-source projects this plugin builds on.

Deck engine: frontend-slides

The built-in commentable-decks capability is powered by a curated, hardened, vendored subset of the frontend-slides skill by Zara Zhang, used under the MIT License (© 2025 Zara Zhang). The upstream deploy and PDF-export scripts are excluded, and a CI gate keeps the vendored subtree pristine.

Changelog

Changes to Commentable HTML. See the full changelog on GitHub.

[1.264.0] - 2026-07-28

Fixed
  • A wide table no longer breaks words mid-token. Table cells used overflow-wrap: anywhere, which participates in intrinsic (min-content) sizing, so every cell reported a min-content width of roughly one character and the table layout algorithm collapsed a column to that and shredded its text even while other columns still had spare room - a 14-column table rendered NAMESPACE MOVE as NAMES / PACE / MOVE. Cells now use overflow-wrap: break-word, which breaks identically once a line is being laid out but is ignored for min-content, so a cell keeps its longest-word width and is only broken when there is genuinely nowhere left to go.
  • A table that is genuinely too wide for the page now scrolls horizontally inside its own box instead of pushing the whole document sideways. This is the other half of the fix above: break-word is ignored for min-content sizing, which is exactly what stops a column being shredded, but it also means a table whose columns cannot fit reports a min-content width larger than its container and escapes it. Every table is therefore rendered inside a .cmh-table-scroll box - the containment narrow screens already had, now at every width. The wrapper is a real element rather than display: block; overflow-x: auto on the table itself, because display: block wraps the rows in an anonymous table box that shrink-to-fits and collapsed a narrow table's columns to their content width (a 2-column table measured 99px instead of 400px) - which also means narrow screens no longer suffer that collapse. A wrapper that actually scrolls is keyboard-focusable and labelled, so it can be scrolled without a mouse, while a table that fits adds no tab stop. A table built at runtime by an author script is contained too, and a table inside a flex or grid parent keeps its width and its placement. A focused scroll box owns the arrow keys in a deck, so its clipped columns stay reachable by keyboard instead of the arrows changing slides. Printing is unaffected: the wrapper reverts to overflow: visible so a wide table is never clipped out of a PDF.

[1.262.0] - 2026-07-28

Changed
  • A document now carries the vendored rich-libraries payload (the gzip+base64 mermaid and Chart.js bundle the Offline export inlines) only when its content actually uses a diagram or a chart, and never in the document head. It used to be stamped into every document unconditionally: measured on the shipped examples it is 1,363 KB, 55 to 61 percent of a 2.3 MB file, and it sat on line 7 - so a prose-and-code review document paid for a renderer it could never call, and any tool that reads the head of a file hit a megabyte-long line immediately. A prose report now goes from 2,301 KB to 938 KB (59 percent smaller) and its longest line from 1,396,078 characters to 631.
  • The decision is re-evaluated on every finalize rather than made once when the document is created, so a document that GAINS a diagram after the payload was dropped gets it back and its Offline export keeps working. A document whose content region cannot be located is left alone entirely - never stripped, and never grown.
Show 5 older releases

[1.261.0] - 2026-07-27

Changed
  • finalize now reads the document once, threads it through the phase transforms in memory, and writes once. Each phase previously re-read and re-wrote the whole file, so a 1.4 - 2.5 MB document paid a read/write pair plus an independent full-document parse per phase - and because content_replace finalizes on every write-back, the agent edit loop paid that on every iteration. The phase order and every transform are unchanged, so the output is byte-identical. The per-phase entry points are REMOVED rather than kept: nothing called them once the pipeline threaded the document in memory, and leaving them would let a caller silently reintroduce the per-phase I/O this removes.
  • Validation and the validated provenance stamp now work on that same in-memory document, so a clean finalize --strict run costs one read and one write end to end, instead of re-reading the file to validate it and then re-reading and re-writing it to stamp it. validate.validate() takes an optional html= argument for callers that already hold the document.
Fixed
  • A quadratic scan in the KQL tokenizer. Deciding whether an identifier was a function call sliced the entire remaining query on every token, so cost grew with the square of the query length. It is now a bounded lookahead. A newline before the paren still does not make a call, so highlighted output is unchanged.

[1.260.0] - 2026-07-27

Fixed
  • A diff labelled config.xml, page.html or main.dart now highlights. _EXT_LANG had no entry for those extensions, so extension inference resolved to no language and the diff rendered monochrome unless the author passed data-diff-lang explicitly - even though both highlighters fully support them. A guard test now fails when any supported language has an obvious extension the table does not map, so the next language cannot ship with the same hole (CMH-HL-13).
  • The runtime sql family accepts double-quoted strings, matching its author-time config (string_styles is sql_single plus double). A double-quoted SQL identifier previously rendered as a string when baked and as plain text at runtime. The other dedicated families' string styles are now spot-checked against their configs by the same guard (CMH-HL-15).
Changed
  • Runtime keyword lookup is per LANGUAGE rather than one broad bucket shared by 23 languages. The hash and c families shared a single approximate set, which both over-colored (a lowercase true in Python, true/false/null in R, bool in Objective-C) and under-colored (Python's capitalized True/False/None never matched the case-sensitive lookup). Measured cost of splitting, on the built commentable-html.js rather than on the raw keyword text: 9,346 bytes (736,669 -> 746,015, about 1.27 percent), against a divergence visible in every Python block. Each set is compared to its author-time config by test, so a future drift fails there instead of shipping as a block that renders one way baked and another way live (CMH-HL-14).

[1.259.0] - 2026-07-27

Fixed
  • Editing a KQL query no longer leaves its Run in Azure Data Explorer button executing the PRE-EDIT query. The query is encoded inside the link's href, so re-highlighting only the code left a stale payload behind - silently, since nothing validated it. refresh_block() now recovers the cluster and database from the existing link and rebuilds both the code and the href, preserving the figure frame, caption and cluster copy affordance; the agent edit loop applies it to every KQL figure it writes back (CMH-KQL-10).
  • The validator classifies a code block through the shared strict scanner instead of probing for the cmh-code- substring, so a nested, malformed or hand-edited span is reported rather than silently accepted as highlighted (CMH-HL-12).
Changed
  • language-kusto / language-kql blocks are baked by the same document highlight pass as every other language rather than being skipped. Only the dispatch and the emission are shared - KQL keeps its own tokenizer and cmh-kql-* class vocabulary - so output bytes are unchanged, and the agent edit loop can now serve KQL blocks like any other code (CMH-KQL-09).

[1.258.0] - 2026-07-27

Added
  • Content-scoped editing for the agent review loop. tools/authoring/content_extract.py prints ONLY the fragment between the CONTENT markers - the 0.1 to 1.3 percent of a document an agent actually edits - instead of making it read a 1.4 to 2.5 MB file, and hands back de-highlighted SOURCE so the agent never has to hand-maintain token spans. A code block the highlight inverse refuses (hand-written markup inside a <pre><code>) is passed through verbatim, so the loop never stalls on the very blocks it exists to repair (CMH-CONTENT-01).
  • tools/authoring/content_replace.py writes a fragment back as ONE atomic transaction: check, swap, re-bake (typography, section cards, highlighting, an existing TOC), strict-validate, re-stamp the content-bound validated hash, and optionally mark Copy-all ids handled. Either every step succeeds or the original file is left byte-for-byte unchanged with no temporary file surviving - there is no half-finished state and no follow-up step to remember. A replacement that changes nothing does not touch the file at all (CMH-CONTENT-02).
  • A content edit is LOCAL: a section the agent did not touch keeps a byte-identical hash, so its Mark-reviewed marker survives, while an edited section hashes differently (CMH-CONTENT-03).
  • tools/authoring/extract_comments.py prints the embedded comment snapshot as JSON or fenced text for the peer-review path, where a returned Portable file carries comments nobody pasted. It reports what is baked into the file, never "all current comments" - a reviewer's newer edits can still sit in browser localStorage, which no command-line tool can read - and it wraps every reviewer note in an untrusted-note fence so a downstream agent treats it as data, not instructions (CMH-CONTENT-04).

[1.257.0] - 2026-07-26

Added
  • Syntax highlighting is now reversible. A new shared highlight core exposes dehighlight(), which recovers the exact source a highlighted code block was built from, for every language including KQL. The inverse is a LEFT inverse over newline-normalized input, because both emitters fold CRLF and lone CR to LF; for content already stored highlighted, dehighlight followed by re-highlight is byte-identical, so an untouched block never churns bytes. Escaped entities, a literal </span> inside a string, multi-line tokens (docstrings, block comments, KQL verbatim strings), tabs and astral Unicode all survive the round trip (CMH-HL-09).
  • The inverse is a strict single-pass scanner, so content the highlighters did not produce is REFUSED rather than corrupted: hand-written markup in a code block, a nested or malformed span, extra attributes, an unknown token kind, or non-canonical escaping the emitters never write (&quot;, &#x3C;, &eacute;, a bare &) all return None from dehighlight(), and classify() reports raw / highlighted / hand-written so callers can pass such a block through untouched. The exact-escaping rule also makes an accidental double application refuse instead of silently decoding source that legitimately contains &amp;. A repeated-substitution loop is deliberately not used - it would peel nested spans from the inside out and hand back plausible but corrupted source (CMH-HL-10).
Changed
  • The code highlighter and the KQL highlighter now share one emission point and one newline normalizer, so the reversibility guarantee covers every language instead of being reimplemented per tokenizer. The two tokenizers stay separate (their grammars genuinely differ) and both the cmh-code-* and cmh-kql-* class vocabularies are retained, so output bytes are unchanged and already-generated documents keep rendering exactly as before. Emitter output is now asserted flat, pinning the property the inverse depends on (CMH-HL-11).

218 earlier releases in the full changelog on GitHub.