Polish home page: clamp tile teasers, balance hero heading, fix embed gap

This commit is contained in:
tleininger committed 2026-10-01 14:44:55 +02:00
1 parent c24ca16a41
commit 6e5a30607e
4 files changed
+44 -47

No files matched your search

+15 -36
View File
@@ -84,18 +84,12 @@ public static partial class Markdown
/// short description on a home page tile.
/// </summary>
/// <param name="markdown">The editor's Markdown; may be <see langword="null"/>.</param>
/// <param name="maxLength">
/// The longest sentence kept as is; a longer one is cut at the last word
/// boundary before the limit and ends in <c>…</c>.
/// </param>
/// <returns>
/// The first sentence of the first top-level paragraph, with all Markdown
/// removed and whitespace collapsed, or an empty string when there is no such
/// paragraph (empty body, or only headings, lists and blocks).
/// removed, whitespace collapsed and a trailing colon dropped, or an empty
/// string when there is no such paragraph (empty body, or only headings, lists
/// and blocks).
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="maxLength"/> is zero or negative.
/// </exception>
/// <remarks>
/// Only top-level paragraphs count: a heading repeats the title, and the text in
/// a list or a <c>:::</c> block is rarely a sentence that describes the page.
@@ -106,6 +100,12 @@ public static partial class Markdown
/// longer abbreviation such as <c>bzw.</c> still does; that costs the rest
/// of a teaser, never the page.
/// </para>
/// <para>
/// A paragraph that leads into a list often ends in <c>:</c> without a
/// period; as a teaser the colon would point at a list that is not there.
/// The sentence is returned whole: a long one is cut by the tile's CSS,
/// which knows the space it has, not by a character count.
/// </para>
/// </remarks>
/// <example>
/// <code>
@@ -113,14 +113,12 @@ public static partial class Markdown
/// // "Wir sind sieben Eltern."
/// </code>
/// </example>
public static string FirstSentence(string? markdown, int maxLength = 140) =>
maxLength <= 0
? throw new ArgumentOutOfRangeException(nameof(maxLength), maxLength, "Must be positive.")
: string.IsNullOrWhiteSpace(markdown)
? ""
: Parse(markdown).OfType<ParagraphBlock>().FirstOrDefault()?.Inline is { } inline
? Shorten(UpToSentenceEnd(PlainText(inline)), maxLength)
: "";
public static string FirstSentence(string? markdown) =>
string.IsNullOrWhiteSpace(markdown)
? ""
: Parse(markdown).OfType<ParagraphBlock>().FirstOrDefault()?.Inline is { } inline
? UpToSentenceEnd(PlainText(inline)).TrimEnd(':', ' ')
: "";
/// <summary>
/// Parses Markdown with the site's pipeline, for callers that take the document
@@ -194,25 +192,6 @@ public static partial class Markdown
private static string UpToSentenceEnd(string text) =>
SentenceEnd().Match(text) is { Success: true } end ? text[..(end.Index + 1)] : text;
/// <summary>
/// Shortens a text to at most <paramref name="maxLength"/> characters plus an
/// ellipsis, cutting between words.
/// </summary>
/// <param name="text">Plain text.</param>
/// <param name="maxLength">The longest text kept as is.</param>
/// <returns>The text, shortened if needed.</returns>
/// <remarks>
/// The search for a space starts at <paramref name="maxLength"/> itself, so a
/// word that ends exactly at the limit is kept. A single word longer than the
/// limit is cut hard.
/// </remarks>
private static string Shorten(string text, int maxLength) =>
text.Length <= maxLength
? text
: text.LastIndexOf(' ', maxLength) is > 0 and var space
? $"{text[..space].TrimEnd(',', ';', ':', '-', '–')}…"
: $"{text[..maxLength]}…";
/// <summary>
/// Matches the end mark of a sentence: <c>.</c>, <c>!</c> or <c>?</c> before a
/// space or the end, unless it follows a lone letter (<c>z.</c>) or a number