Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- Code-block CARD rendering (opt-in): `CodeBlockStyle.cornerRadius` draws a
fenced block as one continuous rounded card spanning the whole block —
wrapped lines included — instead of square per-paragraph fills, and
`CodeBlockStyle.cardVerticalPadding` pins the hidden fence lines' height so
they read as the card's interior padding (fences revert to their natural
height while the caret reveals them). Selection stays visible above the
card. `nil` (the default) keeps the historical rendering exactly.
- Inline-code CHIP rendering (opt-in): `InlineCodeStyle.chipCornerRadius`
draws a small rounded background hugging each inline `code` span (per
wrapped line) with `chipHorizontalPadding` of breathing room, replacing the
square glyph-run fill. `MarkdownEditorTheme.inlineCodeBackground` colors the
chip (nil = `codeBackground`, then the syntax-highlighter background).
- Code typography and background knobs: `CodeBlockStyle.fontName` and
`InlineCodeStyle.fontName` swap the code face (nil = the
syntax-highlighter service's font, as before; inline follows the block
face unless set independently), and `MarkdownEditorTheme.codeBackground`
replaces the background behind fenced blocks and inline spans (nil = the
service's background, as before).
- Custom heading typeface and color: `HeadingStyle.fontName` renders headings
in a specific PostScript face (honored exactly, so the chosen weight is
respected; an unresolvable name falls back to the stock bold base font),
and `MarkdownEditorTheme.headingText` colors heading text independently of
`bodyText` — the `#` glyphs stay on `headingMarker`, and inline constructs
inside a heading keep their own ink (both opt-in; the defaults are
unchanged).

## [0.11.0] - 2026-07-31

### Added
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,21 +249,46 @@ public struct MarkerStyle: Sendable {

/// Styling for fenced code blocks (```language ... ```).
public struct CodeBlockStyle: Sendable {
/// PostScript name of the typeface used for code-block text, for example
/// `"GeistMono-Regular"`. `nil` (the default) keeps the historical
/// behavior: the syntax-highlighter service's code font. A name that
/// doesn't resolve degrades to that service font as well.
public var fontName: String?
/// Code-block font size as a fraction of the document base font size.
public var fontSizeScale: CGFloat
/// Vertical paragraph spacing applied above and below the code block.
public var paragraphSpacing: CGFloat
/// Left/right indent (in points) so code blocks don't run into the gutter.
public var horizontalIndent: CGFloat
/// Corner radius of the code-block CARD. Setting it opts the block into
/// card rendering: one continuous rounded background spanning the whole
/// fenced block (wrapped lines included), with the corners rounded on the
/// block's first and last lines only. `nil` (the default) keeps the
/// historical rendering — a square per-paragraph fill from the
/// `.backgroundColor` attribute.
public var cornerRadius: CGFloat?
/// Interior vertical padding of the card: the hidden fence lines' pinned
/// line height, i.e. the space between the card's edge and the first/last
/// code line. Only takes effect in card mode while the fences are hidden;
/// while the caret reveals the fences they keep their natural code line
/// height so editing them stays comfortable. `nil` (the default) keeps
/// the fences' natural height.
public var cardVerticalPadding: CGFloat?

public init(
fontName: String? = nil,
fontSizeScale: CGFloat = 0.85,
paragraphSpacing: CGFloat = 2.0,
horizontalIndent: CGFloat = 12.0
horizontalIndent: CGFloat = 12.0,
cornerRadius: CGFloat? = nil,
cardVerticalPadding: CGFloat? = nil
) {
self.fontName = fontName
self.fontSizeScale = fontSizeScale
self.paragraphSpacing = paragraphSpacing
self.horizontalIndent = horizontalIndent
self.cornerRadius = cornerRadius
self.cardVerticalPadding = cardVerticalPadding
}

public static let `default` = CodeBlockStyle()
Expand All @@ -275,9 +300,32 @@ public struct CodeBlockStyle: Sendable {
public struct InlineCodeStyle: Sendable {
/// Inline-code reuses the code block font size scale by default.
public var fontSizeScale: CGFloat
/// PostScript name of the typeface used for inline-code text. `nil`
/// (the default) keeps the historical behavior: inline code renders in
/// the code-block font. A name that doesn't resolve degrades the same
/// way.
public var fontName: String?
/// Corner radius of the inline-code CHIP. Setting it opts inline `code`
/// spans into chip rendering: a small rounded background drawn behind the
/// span (per wrapped line) with `chipHorizontalPadding` of breathing room
/// on each side, hugging the code text's own height instead of the whole
/// line box. `nil` (the default) keeps the historical rendering — a
/// square glyph-run fill from the `.backgroundColor` attribute.
public var chipCornerRadius: CGFloat?
/// Horizontal padding (in points) the chip extends beyond the span's
/// first and last glyph. Only read in chip mode.
public var chipHorizontalPadding: CGFloat

public init(fontSizeScale: CGFloat = 0.85) {
public init(
fontSizeScale: CGFloat = 0.85,
fontName: String? = nil,
chipCornerRadius: CGFloat? = nil,
chipHorizontalPadding: CGFloat = 3.0
) {
self.fontSizeScale = fontSizeScale
self.fontName = fontName
self.chipCornerRadius = chipCornerRadius
self.chipHorizontalPadding = chipHorizontalPadding
}

public static let `default` = InlineCodeStyle()
Expand Down Expand Up @@ -349,15 +397,30 @@ public struct TaskCheckboxStyle: Sendable {
/// Per-level heading metrics. Defaults follow the historical Nodes ratios,
/// which are loosely based on browser default heading sizes.
public struct HeadingStyle: Sendable {
/// PostScript name of the typeface used for heading text, for example
/// `"AvenirNext-DemiBold"`. `nil` (the default) keeps the historical
/// behavior: headings render in the editor's base font with the bold
/// trait added.
///
/// The name is honored exactly, so the chosen face's weight and style
/// are respected — pick a `-Bold` / `-Semibold` face for heavier
/// headings. Emphasis inside a heading still composes on top of it:
/// bold / italic add their traits while the family and the per-level
/// size are kept. A name that doesn't resolve falls back to the default
/// heading font at draw time, so a typo degrades to the stock look
/// instead of changing metrics.
public var fontName: String?
/// Font-size multiplier per heading level (1...6).
public var fontMultipliers: [CGFloat]
/// Top spacing in `em` units per heading level (1...6).
public var topSpacingEm: [CGFloat]

public init(
fontName: String? = nil,
fontMultipliers: [CGFloat] = [2.0, 1.5, 1.17, 1.0, 0.83, 0.67],
topSpacingEm: [CGFloat] = [0.35, 0.30, 0.25, 0.20, 0.15, 0.10]
) {
self.fontName = fontName
self.fontMultipliers = fontMultipliers
self.topSpacingEm = topSpacingEm
}
Expand Down
30 changes: 29 additions & 1 deletion Sources/MarkdownEngine/Configuration/MarkdownEditorTheme.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ public struct MarkdownEditorTheme: Sendable {
/// Foreground color for content the engine wants to deemphasize further
/// than `mutedText` — for example, broken wiki-links.
public var disabledText: NSColor
/// Foreground color for heading text. `nil` (the default) keeps the
/// historical behavior: headings render in ``bodyText`` like the rest
/// of the document.
///
/// Only the heading's own text takes this color. The `#` marker glyphs
/// stay on ``headingMarker``, and inline constructs inside a heading
/// (links, inline code, extension spans) keep their own colors, exactly
/// as they do over ``bodyText``.
public var headingText: NSColor?
/// Foreground color for heading marker glyphs (`#`, `##`, …).
public var headingMarker: NSColor

Expand Down Expand Up @@ -79,12 +88,26 @@ public struct MarkdownEditorTheme: Sendable {
/// Background color used for `==highlight==` inline markup.
public var highlightColor: NSColor

// MARK: Code

/// Background color behind fenced code blocks and inline `` `code` ``
/// spans. `nil` (the default) keeps the historical behavior: the
/// syntax-highlighter service's background color.
public var codeBackground: NSColor?

/// Background color of the inline-code CHIP
/// (``InlineCodeStyle/chipCornerRadius`` set). `nil` (the default) falls
/// back to ``codeBackground`` and then to the syntax-highlighter
/// service's background color.
public var inlineCodeBackground: NSColor?

// MARK: Init

public init(
bodyText: NSColor = .labelColor,
mutedText: NSColor = .secondaryLabelColor,
disabledText: NSColor = .tertiaryLabelColor,
headingText: NSColor? = nil,
headingMarker: NSColor = .gray,
link: NSColor = .linkColor,
incompleteLink: NSColor = .systemBlue,
Expand All @@ -93,11 +116,14 @@ public struct MarkdownEditorTheme: Sendable {
latexLightModeText: NSColor = .black,
latexDarkModeText: NSColor = .white,
strikethroughColor: NSColor = .labelColor,
highlightColor: NSColor = .systemOrange.withAlphaComponent(0.4)
highlightColor: NSColor = .systemOrange.withAlphaComponent(0.4),
codeBackground: NSColor? = nil,
inlineCodeBackground: NSColor? = nil
) {
self.bodyText = bodyText
self.mutedText = mutedText
self.disabledText = disabledText
self.headingText = headingText
self.headingMarker = headingMarker
self.link = link
self.incompleteLink = incompleteLink
Expand All @@ -107,6 +133,8 @@ public struct MarkdownEditorTheme: Sendable {
self.latexDarkModeText = latexDarkModeText
self.strikethroughColor = strikethroughColor
self.highlightColor = highlightColor
self.codeBackground = codeBackground
self.inlineCodeBackground = inlineCodeBackground
}

/// System-native palette built from `NSColor` dynamic system colors.
Expand Down
Loading