Skip to content

Render GitHub-style callouts for notes and warnings - #313

Open
ThePumpingLemma wants to merge 10 commits into
block:mainfrom
ThePumpingLemma:dgrochowski/callouts
Open

ThePumpingLemma wants to merge 10 commits into
block:mainfrom
ThePumpingLemma:dgrochowski/callouts

Conversation

@ThePumpingLemma

@ThePumpingLemma ThePumpingLemma commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

What changed and why

Reviewers of long analysis plans asked for notes and warnings that stand out from the body text. Today a > blockquote is the only option, and it looks like any quoted aside. This PR adds callouts using the GitHub alert syntax that agents already write:

> [!NOTE]      > [!TIP]      > [!IMPORTANT]      > [!WARNING]      > [!CAUTION]      > [!SUMMARY]
  • Summary callout. > [!SUMMARY] is a teal callout for a TL;DR at the top of a long plan, such as a STAMP analysis. It replaces Add summary blocks to plan Markdown #314. It is formatting only: the generated plan summary, link previews, OG tags and Slack unfurls are unchanged. comrak only knows GitHub's five alert types, so CoPlan::Markdown parses a summary marker as a same-length [!WARNING] alert and renames it after rendering. Offsets don't move. A marker that doesn't open an alert (inside code, or >[!SUMMARY]) stays literal. The rich editor keeps the marker like the other types. The callout gallery opens with one. RENDER_CACHE_VERSION is now 32.

  • Rendering. Commonmarker 2.6.3 exposes comrak's alerts extension, so it is now on in MarkdownHelper::EXTENSION_OPTIONS. Every parser that shares those options (renderer, text extractor, source targets, slide classifier) agrees on what a callout is. A new post-sanitize pass, render_callouts, turns Commonmarker's alert markup into div.callout.callout--<type> and moves the title into data-callout-title. The marker is case-insensitive, and > [!WARNING] Before you migrate sets a custom label. Unknown markers such as > [!FOO] and plain > quotes still render as blockquotes.

  • Styling. Each type has an icon and label, with a tinted background and border in both app modes. Static Note and Warning use neutral accents to reserve blue for interaction and orange for viewer attention. Tip, Important and Caution retain green, violet and red accents. Deck callouts use deck tokens so the artifact stays readable and unchanged across app modes. The label and icon are CSS generated content, so the document text does not change.

  • Comments. The [!NOTE] marker is not in the rendered text or in MarkdownTextExtractor output, and the label is not DOM text. Selection, occurrence counting and comment anchors inside a callout work as they do for any paragraph. :alert counts as a block for the extractor's newline separators.

  • Sanitizing. No new tags or attributes are allowed for authors. data-callout-title is added after sanitization. Raw HTML cannot set callout classes directly because the pass removes them first. Copying Commonmarker's own alert markup gives exactly what the marker gives.

  • Rich editor. A callout edits as a rich blockquote with the same look. The marker line, including its case and any custom title, is kept as a node attribute and written back unchanged when the body is edited. A callout pasted within the editor keeps its marker. Escaped markers and markers after an opening blank quote line stay literal. An empty callout followed immediately by unquoted text, or a marker folded into a setext heading, uses the existing source-preview block to preserve its boundaries. Recognition requires exactly one space after >; no-space markers remain plain quotes after edits.

  • Slides. A callout bills one unit for its label plus its children, and it no longer triggers the pull-quote layout. A new conformance example is in docs/SLIDE_SPEC.md.

  • Docs and demo. There is a new "Callouts" section in the Markdown agent guide. The new callout-gallery demo ("Callouts: notes, tips, and warnings") uses all six types, including Summary, a custom label, a list and SQL inside a callout, plain, escaped and unknown-marker quotes for comparison, and callouts in Coplan and Graphite decks. docs/DEMO_LIBRARY.md lists it.

  • RENDER_CACHE_VERSION is bumped to 32 for the SUMMARY parser corrections, above the earlier callout renderer versions. Recheck freshness if another renderer change lands first.

Evidence

Screenshots (light and dark) to be attached.

  • /tmp/coplan-shots/callouts/reader-light.png
  • /tmp/coplan-shots/callouts/reader-dark.png
  • /tmp/coplan-shots/callouts/reader-warning-caution-light.png
  • /tmp/coplan-shots/callouts/reader-warning-caution-dark.png
  • /tmp/coplan-shots/callouts/editor-light.png
  • /tmp/coplan-shots/callouts/editor-dark.png

How to try it

  1. bin/rails db:seed, start the server, and sign in as hampton@example.com.
  2. Open /hampton/demo-library. Under Tables and rich text, open Callouts: notes, tips, and warnings.
  3. You should see six styled callouts: Summary (TL;DR), Important (custom label "Decision needed"), Note, Tip, Warning (with a list and code) and Caution. The plain quote and the [!QUESTION] quote at the end stay ordinary blockquotes.
  4. Select text inside a callout and press C. The composer quotes only your selection, and the highlight lands inside the callout.
  5. Choose Edit, type inside the Warning callout, then switch to Raw. > [!WARNING] is unchanged.

Testing

  • bundle exec rspec spec/helpers/markdown_helper_spec.rb spec/system/callouts_spec.rb spec/lib/development_seed_spec.rb:326: 89 examples, 0 failures after the summary callout was added. Screenshots: /tmp/coplan-shots/callouts/summary-{light,dark,editor-light}.png.

  • bundle exec rspec spec/helpers/markdown_helper_spec.rb: each type, lowercase marker and custom label, plain blockquote, unknown [!FOO], lists, code and nested quotes inside a callout, callout in a list item, task checkboxes, forged classes and attributes stripped, non-interactive renders, and extractor text without the marker.

  • bundle exec rspec spec/system/callouts_spec.rb: a real browser check of rendered labels, starting and saving a comment on text inside a callout (anchor position checked against the source), the JS parse/serialize round trip for every type and edge case, and typing in a callout in the rich editor, then Raw and Done, with the marker unchanged.

  • bundle exec rspec spec/lib/development_seed_spec.rb: the callout gallery seeds once, renders all six types, and keeps a person's edits on re-seed.

  • bundle exec rspec spec/services/slideshows/conformance_spec.rb: the new callout example.

  • bundle exec rspec (full suite): 3410 examples, 1 failure. The failure is spec/system/agent_connections_spec.rb:16, a Chrome renderer/close-window timeout. It fails the same way on main (30fe52b) on this machine, so it is not caused by this change. spec/system/references_spec.rb:183 passed in this run.

  • Light and dark mode checked in a running dev server; see the screenshots above.

  • Added or updated real JavaScript-enabled browser specs in spec/system/ for changed UI behavior, covering the user interaction and visible result; or explained why no browser UI is involved.

  • Added model, service, or request specs where needed for domain behavior, authorization, or API cases.

  • Ran bundle exec rspec, or explained why it could not run and listed the specs that did run.

  • Tested affected UI behavior in both light and dark mode, or explained why mode testing does not apply.

  • Attached a video or images demonstrating the change, if this PR adds or changes a feature or fixes a UI bug. (Screenshots to be attached by hand.)

editor-dark editor-light reader-dark reader-light reader-warning-caution-dark reader-warning-caution-light

Maintainer follow-up: 573d5e30 fixes parser edge cases and deck contrast. See the cleanup comment for updated browser evidence and current-head checks.

Generated with Codex

Enable Commonmarker's alerts extension so `> [!NOTE]`, `[!TIP]`,
`[!IMPORTANT]`, `[!WARNING]` and `[!CAUTION]` blockquotes render as
callouts with a per-type icon, label and tint in light and dark mode.
The label is generated content, so the marker and label stay out of
comment anchors and the text extractor. The rich editor keeps the marker
line as a blockquote attribute and writes it back unchanged.

Adds an agent guide section, a callout-gallery demo, a slide-spec
conformance example, and bumps RENDER_CACHE_VERSION to 25.
@ThePumpingLemma
ThePumpingLemma marked this pull request as ready for review November 7, 2026 03:42
@HamptonMakes

Copy link
Copy Markdown
Collaborator

🤖 @codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-11T22:20:02.100331Z 4dcb1d4 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 585a65edc9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>
@HamptonMakes

HamptonMakes commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Engineering cleanup in 573d5e30:

  • Fixed reader/editor disagreement for markers after an opening blank quote line. Empty callouts followed by unquoted prose now use the existing source-preview block, preserving their meaning through edits. Escaped and unknown markers stay literal.
  • Fixed the verified slide contrast failure. Callouts now use the deck palette, independently of app light/dark mode. Note and Warning use neutral accents in documents, following the existing interaction/attention color guide. Both Codex findings are addressed and resolved.
  • Added regression coverage, updated the served Markdown guide, and extended the shared callout gallery with escaped syntax and both deck themes.

Validation: initial focused suite 128 examples passed; expanded rendering/browser/conformance/instruction coverage 154 passed; final deck-theme/browser/instruction run 62 passed; updated gallery seed/re-seed test passed; focused RuboCop passed. Real browser checks covered reader, comments, rich edits → Raw → saved reader, escaped/unknown syntax, and presenting a callout slide. No overlapping full-suite run was started.

Current head: 573d5e3028081966e4e4500176d5326fa2868954. All 15 current-head checks passed. GitHub reports MERGEABLE/BLOCKED with REVIEW_REQUIRED; required human approval is outstanding. Codex reviewed 585a65e; these are bounded corrections to its two findings plus parser edge cases, locally reviewed and tested, so I have not requested a duplicate review round. Required human review/acceptance remains outstanding.

Hampton authorized engineering preparation and a local tryout, not acceptance or merging. The neutral Note/Warning accents follow the existing guide; whether static callouts should instead receive a semantic-color exception remains Hampton's product choice.

Local preview is running at http://localhost:3313/hampton/demo-content/rich-text/callouts-notes-tips-and-warnings (login hampton@example.com, no password). Try Edit → change callout prose → Raw → Done, then the callout slide and the sample comment. The isolated preview includes extra boundary checks appended during the tryout.

Final reader, light and dark:

Final reader in light mode

Final reader in dark mode

Slide contrast before and after, both with dark app mode:

Before: unreadable callout on a light deck

After: readable deck-owned palette

@joahg joahg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Approving. This works. Callouts are applied after sanitizing, and data-callout-title is only set through Nokogiri, so script and onerror payloads in custom titles come out escaped and forged callout* classes are stripped. About 30 source shapes (nested, in lists, custom titles, CRLF, lazy continuation, empty callouts) round-trip byte-identically through the editor parser and serializer. Text extraction still leaves the marker out of the text.

There is one rare data-loss path in the editor and one place where the generated title leaks into plain text (both inline). Plus a merge-order note: #314 also bumps RENDER_CACHE_VERSION to 25, so whichever of the two lands second needs to go to 26.

@joahg joahg mentioned this pull request Nov 7, 2026
5 tasks done
Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>
@HamptonMakes

HamptonMakes commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Addressed the four review threads in 5d0b08ce and resolved them after pushing:

  • Preserve marker-bearing setext headings through rich-editor saves using the source-preview fallback.
  • Match the server’s exact one-space callout prefix; no-space, extra-space, and tab prefixes remain ordinary quotes.
  • Exclude generated callout titles from plain-text output while retaining literal unknown markers.
  • Bump the Markdown render-cache version to 26.

Validation: 125 focused examples passed; focused RuboCop passed. A real browser edit → Raw → Done round-trip preserved the setext callout and literal no-space quote. Saved results were checked in light and dark themes. All 15 CI checks passed on 5d0b08cec72e22cf9b86baf05958d7300de50114. GitHub reports MERGEABLE/BLOCKED with REVIEW_REQUIRED; required human approval remains outstanding.

Try the isolated preview at http://localhost:3313/hampton/demo-content/rich-text/callouts-notes-tips-and-warnings#review-regression-checks (login: hampton@example.com, no password). Edit the nearby prose and save: the setext heading should remain inside its callout, and the no-space marker should stay literal.

Cache coordination reserves #311=25, #313=26, #314=27. Before integration, recheck actual main/deployed cache values and choose a fresh version for the combined renderer; reservations alone do not make arbitrary merge order safe.

This completes the requested engineering review fixes. Product acceptance remains with Hampton; no approval or merge was performed.

Light theme after saving:

Saved callout regressions in light theme

Dark theme after saving:

Saved callout regressions in dark theme

@joahg joahg left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Re-approving at 5d0b08c (the push dismissed my earlier approval). I checked the fixes for all four threads: marker-bearing setext headings now take the preserveCallout fallback, recognition requires exactly > before the marker (ATX > # [!NOTE] and two-space or tab prefixes stay literal), markdown_to_plain_text drops p.markdown-alert-title, and the cache version is reserved at 26 with #314 at 27. Thanks!

@HamptonMakes HamptonMakes mentioned this pull request Nov 8, 2026
5 tasks done
Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>
Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>
@HamptonMakes

HamptonMakes commented Nov 9, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Merged current main (b40c2c32, including #298, #318 and #315) into the contributor branch without rewriting its history. Merge commits are d5f6b0b4 and a9928b12; current head is a9928b12.

  • Kept both math and callout renderer/editor support, both demo galleries, and their regression coverage. The combined renderer uses cache version 30, above main’s 29; recheck freshness if another renderer change lands first.
  • Added a browser regression that edits and saves math inside a callout, plus a renderer regression for math and application links inside a callout while unsafe links and event handlers are stripped.
  • The first CI run hit a stale Selenium element in Render LaTeX math with KaTeX #318’s math-comment assertion. 950caafb uses Capybara’s waiting matchers for the same two exact highlight texts and count. No product behavior changed for that correction.

Validation: 228 focused examples passed, then 18 affected browser examples passed after the assertion fix; 174 focused examples passed after integrating #298; focused RuboCop and diff checks passed. Local integration review found no remaining blocker. All 15 expected checks passed on a9928b12f3894f4a6e57c04b569250176da74c33, including all 12 CI jobs (lint, MySQL/PostgreSQL non-browser, four MySQL browser shards, and five browser stress shards), plus DCO, Semgrep, and zizmor. Run 42671655516, attempt 3 completed successfully after one authorized bounded retry on November 9. The earlier two attempts were GitHub startup errors with no test jobs, not test failures. The existing preview was rechecked using an isolated headless profile in both themes. All six review threads remain resolved; external approvals are on earlier heads, and required human approval remains outstanding; GitHub reports MERGEABLE/BLOCKED, with required human review outstanding. All six review threads remain resolved. No approval or merge performed.

Refreshed the existing isolated preview on port 3313. A separate headless Chrome for Testing 150 temporary profile exercised rich edit → Raw → Done and verified callout math and setext preservation. Captured the saved warning with math in both themes below. The preview database and server remain available.

Try http://localhost:3313/hampton/demo-content/rich-text/callouts-notes-tips-and-warnings (login hampton@example.com, no password). The Warning under Plan includes the retry formula; edit its prose and save, then check Review regression checks for the setext callout and literal marker.

Merged callout and math, light

Merged callout and math, dark

HamptonMakes and others added 2 commits November 9, 2026 12:10
Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>
Folds the summary-block idea from block#314 into callouts: a teal Summary
callout for the top of long analysis plans. It is formatting only and
does not touch the generated plan summary, link previews or unfurls.
@HamptonMakes

Copy link
Copy Markdown
Collaborator

🤖 @codex review the new SUMMARY callout behavior in ed62239, including parser/source-position preservation, literal fallbacks, rich editing, and ensuring generated summaries/unfurls retain their existing meaning.

Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ed62239206

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread engine/app/services/coplan/markdown.rb Resolved
Comment thread engine/app/services/coplan/markdown.rb Resolved
@HamptonMakes

HamptonMakes commented Nov 11, 2026 •

Copy link
Copy Markdown
Collaborator

🤖 Prepared David’s SUMMARY revision (ed622392) with follow-up fixes in 4dcb1d4d and 77af581e (current head).

The proposed [!SUMMARY] is editorial callout content, editable as a normal rich blockquote. It does not override plan.summary, workspace descriptions, link previews, OG tags, or Slack unfurls. #314 is superseded and closed. The custom marker and teal presentation are David’s proposal for Hampton’s tryout, not an accepted product decision. Template designs, metadata controls, summary-only pages, and navigation changes are outside this revision.

Review found and fixed two parser issues: summaries opening on a list-item line disagreed with the rich editor, and adding a summary could reset an unrelated nested ordered list from 9 to 1. Coverage now includes bullet/numbered/lettered containers, source offsets, and a real rich edit → Raw → Done save. The agent guide states the portability limit: SUMMARY is a Coplan extension; other Markdown viewers can show its literal marker in an ordinary blockquote. Render-cache version is 32. Codex additionally found a table-only callout save could discard the table; 77af581e restricts the empty-body shortcut to empty paragraphs, with a failing-before/passing-after Edit table → Raw → Done regression.

Validation: 158 focused rendering/parser/browser/instruction examples passed after fixes; five-file RuboCop and diff checks passed. The earlier broad focused run had passing demo seed/re-seed cases but three failures (one corrected test assertion for synthetic newline offsets and two browser timing failures); the affected checks all passed in the follow-up. No full-suite run was started locally. Codex reviewed the substantive new behavior and found the two parser issues plus the table-only save issue; all three findings are fixed, replied to, and resolved. The final correction passed all 20 affected callout/math browser examples and focused lint. CI on 4dcb1d4d exposed a separate stale-element assertion in a math-comment test; the equivalent waiting matcher is included in 77af581e. All 15 CI checks passed on current head 77af581e, including MySQL and PostgreSQL suites, all four browser shards, all five browser stress runs, lint, and security/DCO checks. All nine review threads are resolved; GitHub reports the branch mergeable, with human review still required. No approval or merge performed.

The existing isolated preview now includes Summary at the top of the gallery. A separate headless Chrome for Testing profile verified its rich edit/save and captured both themes. Login hampton@example.com, no password:
http://localhost:3313/hampton/demo-content/rich-text/callouts-notes-tips-and-warnings

Try Edit → change a Summary bullet → Raw → Done. The marker stays [!SUMMARY], the prose remains editable, and it does not become document-summary metadata.

Summary callout after saving, light

Summary callout after saving, dark

@HamptonMakes

Copy link
Copy Markdown
Collaborator

🤖 @codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4dcb1d4d43

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Signed-off-by: Hampton Lintorn-Catlin <hampton@squareup.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants