Skip to content

feat: add GitHub-style alert blockquotes#174

Open
dirlligafu wants to merge 3 commits into
erictli:mainfrom
dirlligafu:feat/github-alert-blockquotes
Open

feat: add GitHub-style alert blockquotes#174
dirlligafu wants to merge 3 commits into
erictli:mainfrom
dirlligafu:feat/github-alert-blockquotes

Conversation

@dirlligafu

@dirlligafu dirlligafu commented Jun 18, 2026

Copy link
Copy Markdown

Summary

  • Adds the 5 GitHub/GFM alert types (NOTE, TIP, IMPORTANT, WARNING, CAUTION) as a dedicated TipTap node extension
  • Three creation methods: InputRule (> then [!NOTE] ), slash commands (/note, /tip…), toolbar buttons
  • Toolbar buttons show active state when cursor is inside an alert of that type
  • Full markdown round-trip: alerts in existing .md files are parsed and rendered correctly; saving and reopening preserves them
  • Modifies Tailwind Typography's default bold/italic styling on standard blockquotes, allowing styling

Changes

New file: src/components/editor/AlertBlockquote.ts
New TipTap Node extension:

  • Custom marked.js block tokenizer → parses > [!TYPE] syntax
  • parseMarkdown / renderMarkdown → bidirectional markdown serialization
  • InputRule → type > then [!NOTE] inside a blockquote to convert it to an alert
  • parseHTML → handles <blockquote data-alert-type="..."> for copy/paste

Modified file: src/components/editor/Editor.tsx

  • Registers AlertBlockquote extension
  • Adds 5 toolbar buttons after the existing blockquote button, each with a color-coded dot badge and active state detection

Modified file: src/components/editor/SlashCommand.tsx

  • Adds 5 alert items with aliases: alert, callout (shared) + info, hint, warn, danger (type-specific)

Modified file: src/App.css

  • Color-coded alert styles: left border + muted background + ::before label per type
  • Fixes Tailwind Typography default font-weight: 500 / font-style: italic on blockquotes

Known limitations

  • Inline formatting round-trip: bold/italic/code inside an alert are preserved within a WYSIWYG session, but after toggling to source mode and back, marks appear as literal markdown syntax (**text**). This is a constraint of the @tiptap/markdown v3 custom block extension API, which does not expose the block-recursive inline processing pipeline to custom extensions.
  • Lists inside alerts: rendered as plain text after a WYSIWYG ↔ source round-trip

Tested so far

  • npm run tauri build passes
  • Type > then [!NOTE] → blockquote converts to NOTE alert; repeat for all 5 types
  • Type /note in slash command → alert wraps current paragraph; repeat for all 5 types
  • Click each toolbar alert button → wraps current paragraph in the corresponding alert type
  • Click toolbar button while cursor is inside an alert → button shows active state
  • Press Enter inside an alert → new paragraph stays inside the alert
  • WYSIWYG → source mode → WYSIWYG: alert persists, rest of document intact
  • Save note with alerts → close Scratch → reopen: alerts restored correctly
  • Open an existing .md file containing > [!NOTE] syntax → rendered as styled alert
  • Cut/copy-paste an alert block → alert type and content preserved
  • All 5 types render correctly in dark mode
  • Standard blockquotes are no longer rendered bold/italic
  • PDF export: alerts render as styled blockquotes

Visuals

image image

Summary by CodeRabbit

  • New Features
    • Added alert-style blockquotes with types: Note, Tip, Important, Warning, and Caution
    • Create alerts from the formatting toolbar, slash commands, and in-editor typing using [!TYPE]
    • Added markdown support for > [!TYPE] blockquote alerts
  • Styling
    • Updated blockquote typography to remove default quote marks
    • Enabled labeled, color-coded alert variants with distinct left borders and alert type labels

Adds support for the 5 GitHub alert types (NOTE, TIP, IMPORTANT, WARNING,
CAUTION) as a new TipTap extension with full markdown round-trip support.

- AlertBlockquote.ts: new Node extension with custom marked.js tokenizer,
  parseMarkdown, renderMarkdown, and InputRule
- Editor.tsx: registers extension + adds 5 toolbar buttons with active state
- SlashCommand.tsx: adds /note, /tip, /important, /warning, /caution commands
- App.css: alert styles (color-coded border + label), fixes blockquote
  bold/italic default from Tailwind Typography
@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 49f77ace-4637-41ef-99f2-6e58cf772035

📥 Commits

Reviewing files that changed from the base of the PR and between 1068578 and 8440e6e.

📒 Files selected for processing (1)
  • src/components/editor/AlertBlockquote.ts

📝 Walkthrough

Walkthrough

Adds an AlertBlockquote TipTap node extension supporting five alert types (NOTE, TIP, IMPORTANT, WARNING, CAUTION). The extension handles HTML serialization, keyboard input rules ([!TYPE] triggers), and Markdown round-trip. CSS styles define per-type borders and labels. The editor toolbar replaces the generic blockquote button with five alert controls, and the slash-command menu gains matching entries.

Changes

Alert Blockquote Feature

Layer / File(s) Summary
AlertBlockquote TipTap node: schema, input rules, and Markdown
src/components/editor/AlertBlockquote.ts
Defines AlertType union, ALERT_TYPES constant, and ALERT_META map with label and color per type. Implements the TipTap node schema with alertType attribute and HTML serialization, input-rule detection of [!TYPE] triggers inside blockquotes, and Markdown tokenization, parsing, and rendering for the > [!TYPE] format.
CSS alert blockquote styles
src/App.css
Removes Tailwind Typography default quote marks from .prose blockquotes and adds .alert base styles (3px left border, muted background, padding, rounded end corners, and ::before uppercase label). Defines five variant selectors (alert-note, alert-tip, alert-important, alert-warning, alert-caution) with distinct border colors and label text/colors.
Editor toolbar, extension registration, and slash commands
src/components/editor/Editor.tsx, src/components/editor/SlashCommand.tsx
Registers AlertBlockquote in the Tiptap extensions array. Replaces the single Blockquote toolbar button with five per-type alert buttons, each calling wrapIn("alertBlockquote", { alertType }) with active-state reflection. Adds ALERT_ALIASES mapping and inserts five "Alert: …" slash-command items with colored quote icons, type-specific and generic aliases, and matching wrapIn handlers.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇 A blockquote once plain now wears a bright coat,
With NOTE, TIP, and WARNING each earning a dot.
I typed [!CAUTION] and watched the rule fire,
The border turned amber, the label climbed higher.
From Markdown to toolbar to slash-command glee,
Alert callouts bloom — oh, how fancy! 🌸

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add GitHub-style alert blockquotes' accurately and directly describes the main change: implementing GitHub-style alert blockquotes as a new feature. It is concise, specific, and clearly communicates the primary purpose of the PR.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/components/editor/Editor.tsx (1)

72-72: ⚡ Quick win

Centralize alert button metadata with shared AlertType typing.

Line 356 widens alertType to string, and this same mapping is duplicated in src/components/editor/SlashCommand.tsx (Lines 119-125). This increases drift risk across toolbar/slash behavior.

Proposed fix
-import { AlertBlockquote } from "./AlertBlockquote";
+import { AlertBlockquote, type AlertType } from "./AlertBlockquote";

+const ALERT_BUTTONS: Array<{ alertType: AlertType; label: string; color: string }> = [
+  { alertType: "NOTE", label: "Note", color: "`#4493f8`" },
+  { alertType: "TIP", label: "Tip", color: "`#3fb950`" },
+  { alertType: "IMPORTANT", label: "Important", color: "`#ab7df8`" },
+  { alertType: "WARNING", label: "Warning", color: "`#d29922`" },
+  { alertType: "CAUTION", label: "Caution", color: "`#f85149`" },
+];

 ...
-      {(
-        [
-          { alertType: "NOTE",      label: "Note",      color: "`#4493f8`" },
-          { alertType: "TIP",       label: "Tip",       color: "`#3fb950`" },
-          { alertType: "IMPORTANT", label: "Important", color: "`#ab7df8`" },
-          { alertType: "WARNING",   label: "Warning",   color: "`#d29922`" },
-          { alertType: "CAUTION",   label: "Caution",   color: "`#f85149`" },
-        ] as { alertType: string; label: string; color: string }[]
-      ).map(({ alertType, label, color }) => (
+      {ALERT_BUTTONS.map(({ alertType, label, color }) => (

Also applies to: 349-357

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/editor/Editor.tsx` at line 72, The alert type mapping is
duplicated across Editor.tsx and SlashCommand.tsx, causing maintenance drift
risk. Extract the alert type metadata mapping into a shared AlertType type
definition in a centralized location (such as a types or constants file).
Replace the duplicate mappings around line 356 in Editor.tsx and lines 119-125
in SlashCommand.tsx with references to this shared AlertType definition,
ensuring both files use the same type-safe alert configuration instead of
allowing alertType to be widened to string.
src/components/editor/AlertBlockquote.ts (1)

28-33: ⚡ Quick win

Expose alert type semantically (not only via CSS ::before).

The visible type label is generated in CSS, so assistive tech may miss it. Add a semantic label in the rendered node so alert type is announced consistently.

Proposed fix
  renderHTML({ node, HTMLAttributes }) {
-    const type = ((node.attrs.alertType as string) || "NOTE").toLowerCase();
+    const alertType = normalizeAlertType(node.attrs.alertType as string | undefined);
+    const type = alertType.toLowerCase();
     return [
       "blockquote",
-      mergeAttributes(HTMLAttributes, { class: `alert alert-${type}` }),
+      mergeAttributes(HTMLAttributes, {
+        class: `alert alert-${type}`,
+        "aria-label": `${alertType.toLowerCase()} alert`,
+      }),
       0,
     ];
   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/editor/AlertBlockquote.ts` around lines 28 - 33, The
renderHTML method in AlertBlockquote currently relies on CSS to display the
alert type label, which makes it invisible to assistive technology. Modify the
return statement to include a semantic HTML element (such as a div or span with
appropriate ARIA attributes) that contains the alert type text as actual
content. This element should be inserted after the blockquote opening tag so the
alert type is announced by screen readers and other assistive technology
consistently alongside the CSS-based visual indicator.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/App.css`:
- Around line 668-673: Replace the hardcoded left-side border properties with
logical CSS properties to support RTL text direction. In the alert styling
blocks at lines 668-673 and 690-703, change border-left-width to
border-inline-start-width, border-left-style to border-inline-start-style, and
update the border-radius value from "0 4px 4px 0" to use logical equivalents
(border-start-end-radius and border-end-end-radius) so that the accent border
and rounded corners automatically adapt to RTL layouts without requiring
separate RTL-specific rules.

In `@src/components/editor/AlertBlockquote.ts`:
- Around line 18-19: The parseHTML method on line 18 casts the data-alert-type
attribute directly to AlertType without validation, allowing unsupported values
to leak into markdown output and break round-trip serialization. Validate that
the alertType value is one of the supported AlertType values in the parseHTML
method, and apply the same validation in the renderHTML method and the rendering
logic at lines 90-91 and 101-105 to ensure only valid alert types are serialized
and rendered, falling back to a default value like "NOTE" for any unsupported
values.

---

Nitpick comments:
In `@src/components/editor/AlertBlockquote.ts`:
- Around line 28-33: The renderHTML method in AlertBlockquote currently relies
on CSS to display the alert type label, which makes it invisible to assistive
technology. Modify the return statement to include a semantic HTML element (such
as a div or span with appropriate ARIA attributes) that contains the alert type
text as actual content. This element should be inserted after the blockquote
opening tag so the alert type is announced by screen readers and other assistive
technology consistently alongside the CSS-based visual indicator.

In `@src/components/editor/Editor.tsx`:
- Line 72: The alert type mapping is duplicated across Editor.tsx and
SlashCommand.tsx, causing maintenance drift risk. Extract the alert type
metadata mapping into a shared AlertType type definition in a centralized
location (such as a types or constants file). Replace the duplicate mappings
around line 356 in Editor.tsx and lines 119-125 in SlashCommand.tsx with
references to this shared AlertType definition, ensuring both files use the same
type-safe alert configuration instead of allowing alertType to be widened to
string.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ccdc373-ccc9-4d30-9ca6-94e5bca67bc5

📥 Commits

Reviewing files that changed from the base of the PR and between a638f02 and 3554cbf.

📒 Files selected for processing (4)
  • src/App.css
  • src/components/editor/AlertBlockquote.ts
  • src/components/editor/Editor.tsx
  • src/components/editor/SlashCommand.tsx

Comment thread src/App.css Outdated
Comment thread src/components/editor/AlertBlockquote.ts Outdated
  - Add normalizeAlertType() to validate alertType everywhere it's read
    from HTML attrs, markdown tokens, or node attrs
  - Add aria-label to alert blockquotes for screen reader accessibility
  - Extract ALERT_TYPES/ALERT_META into AlertBlockquote.ts as a single
    source of truth, used by both Editor.tsx and SlashCommand.tsx
  - Use logical CSS properties (border-inline-start, border-*-end-radius)
    instead of border-left for RTL support

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
src/components/editor/AlertBlockquote.ts (1)

91-95: ⚡ Quick win

Build the regex dynamically from ALERT_TYPES to prevent synchronization issues.

The tokenizer regex hardcodes NOTE|TIP|IMPORTANT|WARNING|CAUTION, duplicating the ALERT_TYPES constant. If a new alert type is added to ALERT_TYPES, the regex must be manually updated or markdown files with the new type won't parse.

♻️ Proposed fix
  tokenize(src: string, _tokens: MarkdownToken[]) {
+   const alertPattern = ALERT_TYPES.join("|");
+   const regex = new RegExp(
+     `^> \\[!(${alertPattern})\\][ \\t]*\\r?\\n?((?:>[ \\t]?[^\\n]*\\r?\\n?)*)`,
+     "i"
+   );
-   const match = src.match(
-     /^> \[!(NOTE|TIP|IMPORTANT|WARNING|CAUTION)\][ \t]*\r?\n?((?:>[ \t]?[^\n]*\r?\n?)*)/i,
-   );
+   const match = src.match(regex);
    if (!match) return undefined;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/editor/AlertBlockquote.ts` around lines 91 - 95, The regex
pattern in the tokenize method hardcodes the alert types
(NOTE|TIP|IMPORTANT|WARNING|CAUTION) instead of deriving them from the
ALERT_TYPES constant, which causes maintenance issues when new types are added.
Build the regex dynamically by extracting the type keys from ALERT_TYPES,
joining them with pipe delimiters, and constructing the regex pattern at
runtime. This ensures the tokenizer automatically recognizes any new alert types
added to ALERT_TYPES without requiring manual regex updates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/components/editor/AlertBlockquote.ts`:
- Around line 91-95: The regex pattern in the tokenize method hardcodes the
alert types (NOTE|TIP|IMPORTANT|WARNING|CAUTION) instead of deriving them from
the ALERT_TYPES constant, which causes maintenance issues when new types are
added. Build the regex dynamically by extracting the type keys from ALERT_TYPES,
joining them with pipe delimiters, and constructing the regex pattern at
runtime. This ensures the tokenizer automatically recognizes any new alert types
added to ALERT_TYPES without requiring manual regex updates.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9508bbae-63db-42ec-9427-597437c6d6ec

📥 Commits

Reviewing files that changed from the base of the PR and between 3554cbf and 1068578.

📒 Files selected for processing (4)
  • src/App.css
  • src/components/editor/AlertBlockquote.ts
  • src/components/editor/Editor.tsx
  • src/components/editor/SlashCommand.tsx
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/components/editor/SlashCommand.tsx
  • src/App.css
  • src/components/editor/Editor.tsx

  Per CodeRabbit follow-up: the markdown tokenizer regex previously hardcoded the 5 alert type names separately from ALERT_TYPES. Now built dynamically from ALERT_TYPES so the two can't drift out of sync.
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.

2 participants