Skip to content

Add GFM Alert Styling - #168

Open
Sam Clark (28pins) wants to merge 4 commits into
microsoft:mainfrom
28pins:28pins-add-alerts
Open

Sam Clark (28pins) wants to merge 4 commits into
microsoft:mainfrom
28pins:28pins-add-alerts

Conversation

@28pins

@28pins Sam Clark (28pins) commented Aug 15, 2026

Copy link
Copy Markdown

Summary

What changed: Added support for alerts, like [!NOTE]
Why: to give users and LLMs more flexibility and better UX

API change: BlockQuoteType now includes BlockQuoteAlertKind as an optional. See below:

Old:

text(String)
case nested([BlockQuoteType])

New:

case text(String, BlockQuoteAlertKind?)
case nested([BlockQuoteType], BlockQuoteAlertKind?)

Validation

Checked demos and stress-tested kitchen sink demo

OSS readiness

  • No secrets, internal URLs, private identifiers, or product-only service names were added.
  • Public docs, fixtures, or notices were updated if behavior or dependencies changed.
  • Third-party dependency changes (adds, removes, version bumps) are intentional and reviewed.
  • Streaming/incomplete markdown behavior remains covered by fixtures or tests.

@28pins
Sam Clark (28pins) requested review from a team and a lite review from Copilot August 15, 2026 22:04

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@28pins
Sam Clark (28pins) requested a lite review from Copilot August 15, 2026 22:07

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Copilot AI 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.

Pull request overview

Copilot reviewed 11 out of 11 changed files in this pull request and generated 5 comments.

Suppressed comments (1)

Sources/MarkdownText/UI/BlockQuoteView.swift:104

  • These lines contain tab-indentation, which is likely to trip SwiftLint and makes the new alert view code inconsistent with the surrounding file’s spacing-based indentation.
			Image(systemName: style.imageName)
				.foregroundStyle(style.accentColor)
				.padding(.leading, 8.0)

Comment thread Sources/MarkdownText/Block/BlockQuote+.swift
Comment thread Sources/MarkdownText/UI/BlockQuoteView.swift Outdated
Comment thread Sources/MarkdownText/Models/MarkdownRenderConfig+Builders.swift Outdated
Comment thread Tests/MarkdownTextTests/BlockQuoteViewSnapshotTests.swift
Comment thread Sources/MarkdownText/Resources/Localizable.xcstrings Outdated
@28pins Sam Clark (28pins) changed the title 28pins add alerts Add GFM Alert Styling Sep 1, 2026
Sam Clark (28pins) and others added 2 commits September 1, 2026 18:38
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@28pins

Copy link
Copy Markdown
Author

Jun Yan (@junyan72) would you mind allowing the workflow runs? These are my first PRs on this repo, so let me know if I need to do anything else.

Copilot AI 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.

🟡 Changes recommended

Unresolved parser, rendering, test-coverage, lint, accessibility, and documentation findings remain.

Get a fresh assessment by requesting another Copilot review.

Review details

Suppressed comments (5)

Sources/MarkdownText/Block/BlockQuote+.swift:24

  • The added parser test keeps [!NOTE] and its body in one paragraph, so it does not exercise a multi-paragraph alert where a later child must inherit alertKind. Add a focused case with a quoted blank line and a second paragraph, and verify that both paragraphs remain inside the alert.
        if alertKind == nil, let (kind, rest) = Self.parseAlertTag(text) {
          alertKind = kind
          if !rest.isEmpty {
            finalQuoteTypes.append(.text(rest, alertKind))
          }
        } else {
          finalQuoteTypes.append(.text(text, alertKind))

Sources/MarkdownText/Models/MarkdownRenderConfig.swift:110

  • This public initializer also requires an imageName, but its documentation only describes the two colors. Document the SF Symbol parameter so callers know what controls the icon shown by the alert.
    /// Create an alert style with the supplied accent and background colors.
		public init(accentColor: Color, backgroundColor: Color, imageName: String) {

Sources/MarkdownText/UI/BlockQuoteView.swift:50

  • Nested quote items are rendered directly through InternalBlockQuoteView, so a nested .nested(..., .warning) never passes through BlockQuoteView's alert wrapper. A nested GFM alert is consequently rendered as an ordinary quote; recurse through BlockQuoteView (or apply the same alert wrapper) for each child.
        case .nested(let subItems, _):
          ForEach(subItems.indices, id: \.self) { index in
            InternalBlockQuoteView(item: subItems[index])

Sources/MarkdownText/UI/BlockQuoteView.swift:104

  • After [!NOTE] is stripped, the icon is the only indication of the alert kind, but this Image has no accessibility label. VoiceOver users can receive the body without knowing that it is a note/warning, and custom imageName values have no reliable semantic label; expose the kind as the icon's label.
			Image(systemName: style.imageName)
				.foregroundStyle(style.accentColor)
				.padding(.leading, 8.0)

Tests/MarkdownTextTests/MarkdownParserTests.swift:74

  • This test only uses [!NOTE] and its body on the same quoted line, while the canonical GFM form in the fixture puts the marker on its own line and supports subsequent paragraphs. Add coverage for the marker-only first paragraph and multiline content; otherwise a regression in the actual syntax advertised by the README could pass the tests.
    let markdown = """
    > [!NOTE] This is a note.
    """
  • Files reviewed: 11/11 changed files
  • Comments generated: 3
  • Review effort level: Lite

imageConfig: value
)
}

Comment on lines +18 to +20
if alertKind == nil, let (kind, rest) = Self.parseAlertTag(text) {
alertKind = kind
if !rest.isEmpty {
Comment on lines +17 to +20
if let kind = item.alertKind {
AlertBlockQuoteView(kind: kind) {
InternalBlockQuoteView(item: item, isForAlertQuote: true)
}
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