From 8a96736bb0a065e9f4bd6715e6f3215669526211 Mon Sep 17 00:00:00 2001 From: Abdulfatah Bahbouh <60443686+abooodbah@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:06:05 -0600 Subject: [PATCH 1/6] Test the site's structured data, install lines and task page The site test now requires sameAs listings instead of codeRepository, a screenshot and image, the author's page, no ratings, the releases feed link, the Scoop and Homebrew commands, and a task page at open-md-files-on-windows/ that is linked and in the sitemap. The browser smoke test audits that page at the same three sizes. These checks fail until the site changes land in the next commit. --- tests/Invoke-SiteBrowserSmoke.mjs | 99 ++++++++++++++++++------------- tests/Test-Site.ps1 | 98 +++++++++++++++++++++++++----- 2 files changed, 141 insertions(+), 56 deletions(-) diff --git a/tests/Invoke-SiteBrowserSmoke.mjs b/tests/Invoke-SiteBrowserSmoke.mjs index eb1b726..b47f704 100644 --- a/tests/Invoke-SiteBrowserSmoke.mjs +++ b/tests/Invoke-SiteBrowserSmoke.mjs @@ -70,8 +70,9 @@ async function startStaticServer(siteRoot) { const requestUrl = new URL(request.url || "/", "http://127.0.0.1/"); let pathname = decodeURIComponent(requestUrl.pathname); - if (pathname === "/") { - pathname = "/index.html"; + // A folder URL is served as that folder's index.html, as on Pages. + if (pathname.endsWith("/")) { + pathname += "index.html"; } const candidate = resolve(siteRoot, "." + pathname.split("/").join(sep)); if (candidate !== siteRoot && !candidate.startsWith(rootPrefix)) { @@ -536,51 +537,65 @@ async function main() { { name: "tablet", width: 820, height: 1000, scheme: "light" }, { name: "mobile", width: 390, height: 844, scheme: "dark" }, ]; + // The landing page and the task page share the header, buttons, and + // stylesheet, so the same audit applies to both. + const pages = [ + { name: "", path: "" }, + { name: "task page ", path: "open-md-files-on-windows/" }, + ]; - for (const viewport of viewports) { - await client.send("Emulation.setDeviceMetricsOverride", { - width: viewport.width, - height: viewport.height, - deviceScaleFactor: 1, - mobile: viewport.width <= 390, - screenWidth: viewport.width, - screenHeight: viewport.height, - }); - await client.send("Emulation.setEmulatedMedia", { - features: [{ - name: "prefers-color-scheme", - value: viewport.scheme, - }], - }); - await client.send("Page.navigate", { - url: staticServer.url + "?viewport=" + viewport.name, - }); - await waitForDocument(client); - const audit = await evaluate(client, PAGE_AUDIT_EXPRESSION); - assertAudit(viewport.name, viewport, audit); - if (captureRoot) { - const screenshot = await client.send("Page.captureScreenshot", { - captureBeyondViewport: true, - format: "png", - fromSurface: true, + for (const page of pages) { + for (const viewport of viewports) { + await client.send("Emulation.setDeviceMetricsOverride", { + width: viewport.width, + height: viewport.height, + deviceScaleFactor: 1, + mobile: viewport.width <= 390, + screenWidth: viewport.width, + screenHeight: viewport.height, }); - await fs.writeFile( - join(captureRoot, "site-" + viewport.name + ".png"), - Buffer.from(screenshot.data, "base64"), - ); + await client.send("Emulation.setEmulatedMedia", { + features: [{ + name: "prefers-color-scheme", + value: viewport.scheme, + }], + }); + await client.send("Page.navigate", { + url: staticServer.url + page.path + "?viewport=" + viewport.name, + }); + await waitForDocument(client); + const audit = await evaluate(client, PAGE_AUDIT_EXPRESSION); + assertAudit(page.name + viewport.name, viewport, audit); + if (captureRoot) { + const screenshot = await client.send("Page.captureScreenshot", { + captureBeyondViewport: true, + format: "png", + fromSurface: true, + }); + await fs.writeFile( + join( + captureRoot, + "site-" + (page.path ? "task-" : "") + viewport.name + ".png", + ), + Buffer.from(screenshot.data, "base64"), + ); + } } - } - const accessibility = await client.send("Accessibility.getFullAXTree"); - const interactiveWithoutName = accessibility.nodes.filter((node) => { - const role = node.role && node.role.value; - const name = node.name && node.name.value; - return (role === "link" || role === "button") && !String(name || "").trim(); - }); - if (interactiveWithoutName.length > 0) { - fail("The accessibility tree contains an unnamed link or button."); + const accessibility = await client.send("Accessibility.getFullAXTree"); + const interactiveWithoutName = accessibility.nodes.filter((node) => { + const role = node.role && node.role.value; + const name = node.name && node.name.value; + return (role === "link" || role === "button") && !String(name || "").trim(); + }); + if (interactiveWithoutName.length > 0) { + fail("The " + (page.name || "landing page ") + + "accessibility tree contains an unnamed link or button."); + } + console.log( + "PASS " + page.name + "accessibility tree exposes names for every link and button", + ); } - console.log("PASS accessibility tree exposes names for every link and button"); if (browserErrors.length > 0) { fail("Browser console or resource errors: " + browserErrors.join(" | ")); diff --git a/tests/Test-Site.ps1 b/tests/Test-Site.ps1 index 90b284e..6ec0ab8 100644 --- a/tests/Test-Site.ps1 +++ b/tests/Test-Site.ps1 @@ -102,8 +102,59 @@ Assert-Equal ([string]$structured.'@type') 'SoftwareApplication' 'JSON-LD must d Assert-Equal ([string]$structured.softwareVersion) $version 'JSON-LD softwareVersion must match package.json.' Assert-Equal ([string]$structured.offers.price) '0' 'JSON-LD must offer LeanMark at price 0.' Assert-Equal ([string]$structured.author.name) 'Abdulfatah Bahbouh' 'JSON-LD must name the author.' +function Get-JsonProperty { + param($Object, [string]$Name) + $property = $Object.PSObject.Properties[$Name] + if ($null -eq $property) { return $null } + $property.Value +} +Assert-Equal ([string](Get-JsonProperty $structured.author 'url')) 'https://abooodbah.github.io/' 'JSON-LD author must link the Lean Apps page.' +# codeRepository belongs to SoftwareSourceCode, not SoftwareApplication; the +# repository and the package listings go in sameAs instead. +Assert-True ($null -eq $structured.PSObject.Properties['codeRepository']) 'JSON-LD must not use codeRepository on a SoftwareApplication.' +# There are no real ratings or reviews; never claim any. +Assert-NotMatch $jsonLd.Groups[1].Value '(?i)aggregateRating|"review' 'JSON-LD must not carry ratings or reviews.' +$sameAs = @(Get-JsonProperty $structured 'sameAs' | ForEach-Object { [string]$_ }) +foreach ($listing in @( + 'https://github.com/abooodbah/leanmark', + 'https://apps.microsoft.com/detail/9MSS4WKJ22ZV', + 'https://github.com/microsoft/winget-pkgs/tree/master/manifests/a/abooodbah/LeanMark', + 'https://community.chocolatey.org/packages/leanmark', + 'https://github.com/abooodbah/homebrew-tap')) { + Assert-True ($sameAs -contains $listing) "JSON-LD sameAs is missing $listing" +} +Assert-Equal ([string](Get-JsonProperty $structured 'screenshot')) 'https://abooodbah.github.io/leanmark/assets/leanmark-window.png' 'JSON-LD screenshot must be the real product screenshot on the page.' +Assert-Equal ([string](Get-JsonProperty $structured 'image')) 'https://abooodbah.github.io/leanmark/assets/social-preview.png' 'JSON-LD image must be the social preview.' +Assert-Match $html '' 'The page must point feed readers to the releases feed.' +Assert-Match $html 'scoop install lean/leanmark' 'The install section must give the Scoop command.' +Assert-Match $html 'brew install --cask abooodbah/tap/leanmark' 'The install section must give the Homebrew command.' +Assert-Match $html 'href="open-md-files-on-windows/"' 'The landing page must link the task page.' Write-TestPass 'semantic metadata, release CTA, and script-free markup' +# The task page: one plain-HTML page that answers "how do I open .md files on +# Windows" with every real option, LeanMark being one of them. +$taskRelative = 'open-md-files-on-windows/index.html' +$taskPath = Join-Path $resolvedSite $taskRelative +Assert-True (Test-Path -LiteralPath $taskPath -PathType Leaf) "Task page is missing: $taskRelative" +$taskHtml = Read-TextFile $taskPath +Assert-Match $taskHtml '' 'The task page language must be explicit.' +Assert-Equal ([regex]::Matches($taskHtml, '(?is))').Count) 1 'The task page must have exactly one H1.' +Assert-Match $taskHtml '[^<]*\.md files on Windows[^<]*' 'The task page title must name the task.' +Assert-Match $taskHtml '' 'The task page must use the site stylesheet.' +Assert-Match $taskHtml '' 'The task page needs the skip link.' +Assert-Match $taskHtml '' 'The task page needs a main landmark.' +Assert-Match $taskHtml 'href="\.\./"' 'The task page must link back to the LeanMark page.' +Assert-Match $taskHtml 'href="https://abooodbah\.github\.io/"' 'The task page must link the Lean Apps hub.' +Assert-Match $taskHtml '74\.15(?:\s|)*ms' 'The task page must quote the measured window time as published.' +Assert-Match $taskHtml '96\.79(?:\s|)*MiB' 'The task page must quote the measured memory as published.' +Assert-Match $taskHtml '0\.3\.0' 'Measured numbers must keep the version they were measured on.' +Assert-NotMatch $taskHtml '(?is)]+\bsrc\s*=' 'The task page must not execute external or bundled JavaScript.' +Assert-NotMatch $taskHtml '(?i)javascript\s*:' 'The task page must not contain javascript: URLs.' +Assert-NotMatch $taskHtml '(?i)aggregateRating' 'The task page must not carry ratings.' +Write-TestPass 'task page metadata, landmarks, and sourced numbers' + Assert-Match $css ':focus-visible' 'Visible keyboard focus styling is required.' Assert-Match $css '@media\s*\(prefers-reduced-motion:\s*reduce\)' 'Reduced-motion support is required.' Assert-Match $css '@media\s*\(prefers-color-scheme:\s*dark\)' 'Dark color-scheme support is required.' @@ -116,20 +167,28 @@ Write-TestPass 'responsive, motion-safe, keyboard-visible local styling' $sitePrefix = $resolvedSite.TrimEnd([char[]]@('\', '/')) + [System.IO.Path]::DirectorySeparatorChar -$references = [regex]::Matches($html, '(?is)(?:href|src)\s*=\s*"([^"]+)"') -foreach ($reference in $references) { - $value = $reference.Groups[1].Value - if ($value.StartsWith('#') -or - $value.StartsWith('https://') -or - $value.StartsWith('http://') -or - $value.StartsWith('mailto:') -or - $value.StartsWith('data:')) { - continue +foreach ($page in @( + @{ Html = $html; Directory = $resolvedSite }, + @{ Html = $taskHtml; Directory = (Split-Path -Parent $taskPath) })) { + $references = [regex]::Matches($page.Html, '(?is)(?:href|src)\s*=\s*"([^"]+)"') + foreach ($reference in $references) { + $value = $reference.Groups[1].Value + if ($value.StartsWith('#') -or + $value.StartsWith('https://') -or + $value.StartsWith('http://') -or + $value.StartsWith('mailto:') -or + $value.StartsWith('data:')) { + continue + } + $relativePath = ($value -split '[?#]', 2)[0] + # A link to a folder is served as that folder's index.html. + if ($relativePath.EndsWith('/')) { + $relativePath += 'index.html' + } + $candidate = [System.IO.Path]::GetFullPath((Join-Path $page.Directory $relativePath)) + Assert-True ($candidate.StartsWith($sitePrefix, [StringComparison]::OrdinalIgnoreCase)) "A local site reference escapes the site root: $value" + Assert-True (Test-Path -LiteralPath $candidate -PathType Leaf) "A local site reference is broken: $value" } - $relativePath = ($value -split '[?#]', 2)[0] - $candidate = [System.IO.Path]::GetFullPath((Join-Path $resolvedSite $relativePath)) - Assert-True ($candidate.StartsWith($sitePrefix, [StringComparison]::OrdinalIgnoreCase)) "A local site reference escapes the site root: $value" - Assert-True (Test-Path -LiteralPath $candidate -PathType Leaf) "A local site reference is broken: $value" } Write-TestPass 'all local page references resolve inside the deployable site' @@ -152,7 +211,18 @@ try { } catch { throw "sitemap.xml is not well-formed XML: $($_.Exception.Message)" } -Assert-Match ([string]$sitemap.urlset.url.lastmod) '^\d{4}-\d{2}-\d{2}$' 'sitemap.xml must give a lastmod date.' +$sitemapUrls = @($sitemap.urlset.url) +foreach ($entry in $sitemapUrls) { + $lastmod = $entry['lastmod'] + $lastmodText = if ($null -eq $lastmod) { '' } else { [string]$lastmod.InnerText } + Assert-Match $lastmodText '^\d{4}-\d{2}-\d{2}$' ("sitemap.xml must give a lastmod date for {0}." -f $entry.loc) +} +$sitemapLocs = @($sitemapUrls | ForEach-Object { [string]$_.loc }) +foreach ($loc in @( + 'https://abooodbah.github.io/leanmark/', + 'https://abooodbah.github.io/leanmark/open-md-files-on-windows/')) { + Assert-True ($sitemapLocs -contains $loc) "sitemap.xml must list $loc" +} $manifest = Get-Content -LiteralPath (Join-Path $resolvedSite 'site.webmanifest') -Raw | ConvertFrom-Json Assert-Equal ([string]$manifest.name) 'LeanMark' 'Web app manifest name changed unexpectedly.' From eecbdd44778afa48d5dd0ad7a9dd32a1cfaedc82 Mon Sep 17 00:00:00 2001 From: Abdulfatah Bahbouh <60443686+abooodbah@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:09:43 -0600 Subject: [PATCH 2/6] Replace codeRepository with sameAs listings in the site's structured data codeRepository is a SoftwareSourceCode property, not a SoftwareApplication one. The repository, Microsoft Store, winget, Chocolatey and Homebrew tap pages now go in sameAs. The JSON-LD also gets the product screenshot, the social preview as image, and the author's page. No rating or review is added. The page head links the GitHub releases feed. --- site/index.html | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/site/index.html b/site/index.html index 20c08a9..adbe39a 100644 --- a/site/index.html +++ b/site/index.html @@ -13,6 +13,7 @@ + @@ -32,9 +33,17 @@ "softwareVersion": "0.7.1", "license": "https://opensource.org/license/mit", "downloadUrl": "https://github.com/abooodbah/leanmark/releases/latest", - "codeRepository": "https://github.com/abooodbah/leanmark", "description": "A free, read-only Markdown viewer for Windows, Linux, and macOS with offline GitHub-flavored Markdown and Mermaid rendering.", "url": "https://abooodbah.github.io/leanmark/", + "screenshot": "https://abooodbah.github.io/leanmark/assets/leanmark-window.png", + "image": "https://abooodbah.github.io/leanmark/assets/social-preview.png", + "sameAs": [ + "https://github.com/abooodbah/leanmark", + "https://apps.microsoft.com/detail/9MSS4WKJ22ZV", + "https://github.com/microsoft/winget-pkgs/tree/master/manifests/a/abooodbah/LeanMark", + "https://community.chocolatey.org/packages/leanmark", + "https://github.com/abooodbah/homebrew-tap" + ], "offers": { "@type": "Offer", "price": "0", @@ -42,7 +51,8 @@ }, "author": { "@type": "Person", - "name": "Abdulfatah Bahbouh" + "name": "Abdulfatah Bahbouh", + "url": "https://abooodbah.github.io/" } } From ad3b417372351b63c8c6141f6ec7d24c5c71da84 Mon Sep 17 00:00:00 2001 From: Abdulfatah Bahbouh <60443686+abooodbah@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:09:43 -0600 Subject: [PATCH 3/6] List the Scoop and Homebrew commands in the site's install section The FAQ already says LeanMark is on Scoop and Homebrew, and the README has both commands; the install section now shows them too. --- site/index.html | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/site/index.html b/site/index.html index adbe39a..d0d7de2 100644 --- a/site/index.html +++ b/site/index.html @@ -359,6 +359,15 @@

Native packages for three desktops.

With Chocolatey, from an administrator shell choco install leanmark -y +
+ With Scoop, adding the Lean bucket first + scoop bucket add lean https://github.com/abooodbah/scoop-bucket + scoop install lean/leanmark +
+
+ On macOS, with Homebrew + brew install --cask abooodbah/tap/leanmark +

LeanMark is also in the Microsoft Store. From d8071421641a2dc7c7efb3623252e1e9ad3e2a30 Mon Sep 17 00:00:00 2001 From: Abdulfatah Bahbouh <60443686+abooodbah@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:09:43 -0600 Subject: [PATCH 4/6] Give the two labelled illustrations a group role html-validate reports aria-label on a div without a role. role=group keeps the existing label and changes nothing on screen. role=figure draws html-validate's prefer-native-element error instead, and a figure element would bring its default margins. --- site/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/index.html b/site/index.html index d0d7de2..e614ebc 100644 --- a/site/index.html +++ b/site/index.html @@ -166,7 +166,7 @@

A document opens as a document.

  • Live refresh after the file changes on disk
  • -
    +
    architecture.md local file @@ -186,7 +186,7 @@

    A document opens as a document.

    -
    +
    LeanMark rendering flow A Markdown file is parsed by MD4C, shown in a trusted local view, and diagrams are rendered by the bundled Mermaid library only when needed. From a6fa5d732e20ac426c507c1f8844b97a7d2e2e6d Mon Sep 17 00:00:00 2001 From: Abdulfatah Bahbouh <60443686+abooodbah@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:09:43 -0600 Subject: [PATCH 5/6] Add a page on opening .md files on Windows The page lists Notepad, VS Code, a browser extension and LeanMark with what each one leaves out, covers Markdown saved by ChatGPT, Claude or a coding agent, and quotes the 0.3.0 Windows measurement from the README with its machine. The FAQ links it and the sitemap lists both pages with today's date. --- site/index.html | 2 +- site/open-md-files-on-windows/index.html | 230 +++++++++++++++++++++++ site/sitemap.xml | 6 +- 3 files changed, 236 insertions(+), 2 deletions(-) create mode 100644 site/open-md-files-on-windows/index.html diff --git a/site/index.html b/site/index.html index e614ebc..2e1a62d 100644 --- a/site/index.html +++ b/site/index.html @@ -404,7 +404,7 @@

    Reading Markdown files on the desktop

    How do I open an .md file on Windows?

    -

    Install LeanMark from the Microsoft Store, with winget install abooodbah.LeanMark, or from GitHub Releases, then open the file. Headings, tables, task lists, local images, and Mermaid diagrams render as a formatted page.

    +

    Install LeanMark from the Microsoft Store, with winget install abooodbah.LeanMark, or from GitHub Releases, then open the file. Headings, tables, task lists, local images, and Mermaid diagrams render as a formatted page. How to open .md files on Windows compares LeanMark with Notepad, VS Code, and browser extensions.

    How do I read a Markdown file that Claude, ChatGPT, or a coding agent saved?

    diff --git a/site/open-md-files-on-windows/index.html b/site/open-md-files-on-windows/index.html new file mode 100644 index 0000000..844058c --- /dev/null +++ b/site/open-md-files-on-windows/index.html @@ -0,0 +1,230 @@ + + + + + + + + How to open .md files on Windows: Notepad, VS Code, LeanMark + + + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    +

    Guide

    +

    How to open .md files on Windows

    +

    + A .md file is plain text written in Markdown. Any text + editor opens it, but you only see the headings, tables, and diagrams + as a formatted page in a program that renders Markdown. These are + the common ways to do that on Windows, and what each one leaves out. +

    + +
    +
    +
    + +
    +
    +
    +

    Your options

    +

    Four ways to read a Markdown file

    +
    +
    +
    +

    Notepad

    +

    Notepad on Windows 11 can show Markdown with formatting and switch between the formatted view and the Markdown source, as Microsoft describes in its Notepad formatting announcement. That announcement lists bold, italic, links, lists, and headings; Mermaid diagrams are not among them. Notepad is an editor, so the file can be changed by accident.

    +
    +
    +

    Visual Studio Code

    +

    VS Code has a built-in Markdown preview: open the file and press Ctrl+Shift+V, or Ctrl+K V for the preview beside the source. Its Markdown documentation says the preview renders Mermaid diagrams. It is a full code editor, so reading one file means opening an editor first.

    +
    +
    +

    A browser extension

    +

    Markdown viewer extensions for Edge, Chrome, and Firefox render a .md file that you open in the browser. In Edge and Chrome the extension can read a file on your disk only after you turn on its access to file URLs in the extension's settings. Support for tables and Mermaid differs from one extension to the next.

    +
    +
    +

    A Markdown viewer such as LeanMark

    +

    LeanMark opens the file as a formatted, read-only page with GitHub-flavored Markdown tables, task lists, local images, and Mermaid diagrams rendered offline. It cannot edit, and it does not load remote images. The GitHub ZIP is unsigned; the Microsoft Store build is signed by Microsoft.

    +
    +
    +
    +
    + +
    +
    +
    +

    Files from AI tools

    +

    Markdown saved by ChatGPT, Claude, or a coding agent

    +

    + Chat assistants and coding agents often hand you Markdown: a + downloaded answer, a plan, or a README written into a project + folder. Opening it in a viewer shows the tables and diagrams instead + of the raw symbols. +

    +
    +
    +
    +

    Keep it open while it is written

    +

    When the file changes on disk, LeanMark refreshes the page and keeps your approximate reading position, so you can watch an agent's plan or report grow without reopening it.

    +
    +
    +

    Nothing in the file runs

    +

    Raw HTML in a document is not run, a strict Content Security Policy limits scripts, and remote images are not loaded. Links to websites open in your browser only after you click them.

    +
    +
    +
    +
    + +
    +
    +
    +

    Double-click to read

    +

    Make .md files open in your viewer

    +

    + Right-click a .md file, choose Open with, then Choose + another app, pick the program, and select Always. You can change it + later under Settings, Apps, Default apps. +

    +

    + For LeanMark, the optional installer inside the GitHub ZIP adds it + to Open With and to Default Apps for the current user. LeanMark + opens .md, .markdown, .mdown, + and .mkd files. +

    +
    +
    +
    + +
    +
    +
    +

    Measured

    +

    What LeanMark costs to open

    +

    + Median of ten warm launches of LeanMark 0.3.0 with a simple + document, counting LeanMark and every WebView2 process it starts. +

    +

    + Measured on Windows 11 Pro build 26200 with an Intel Core + i7-13620H, 15.6 GiB of RAM, and WebView2 Runtime 153. Hardware, + system state, and document complexity change the result. Linux and + macOS are not measured, and no comparison with other programs is + claimed. +

    + + Read the full measurement + + +
    +
    +
    +
    74.15 ms
    +
    Native window visible, simple document, 0.3.0
    +
    +
    +
    96.79 MiB
    +
    Peak private memory, full process tree, 0.3.0
    +
    +
    +
    +
    + +
    +
    +
    +

    Install

    +

    Get LeanMark on Windows

    +
    +
    + With winget + winget install abooodbah.LeanMark +
    +
    + From the Microsoft Store, with winget + winget install --id 9MSS4WKJ22ZV --source msstore +
    +
    + With Chocolatey, from an administrator shell + choco install leanmark -y +
    +
    + With Scoop, adding the Lean bucket first + scoop bucket add lean https://github.com/abooodbah/scoop-bucket + scoop install lean/leanmark +
    +

    + Builds for Linux and macOS are on the + releases page. + LeanMark is MIT licensed and free. +

    +
    +
    +
    + + + + diff --git a/site/sitemap.xml b/site/sitemap.xml index 436c459..2c212a6 100644 --- a/site/sitemap.xml +++ b/site/sitemap.xml @@ -2,6 +2,10 @@ https://abooodbah.github.io/leanmark/ - 2026-11-06 + 2026-11-07 + + + https://abooodbah.github.io/leanmark/open-md-files-on-windows/ + 2026-11-07 From 83b5e27c8b326a0e1009b0b0ca4e9d2eba834300 Mon Sep 17 00:00:00 2001 From: Abdulfatah Bahbouh <60443686+abooodbah@users.noreply.github.com> Date: Sat, 7 Nov 2026 23:10:14 -0600 Subject: [PATCH 6/6] Add a badge line and the releases feed to the README Four badges under the links line: the winget version, the Microsoft Store listing, the license, and total GitHub release downloads. No stars or ratings badge. The GitHub Releases section links the releases feed. --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c1d36d5..5eafbdf 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Read .md files and Mermaid diagrams offline, without opening an editor. About 74 [Website](https://abooodbah.github.io/leanmark/) · [Microsoft Store](https://apps.microsoft.com/detail/9MSS4WKJ22ZV) · `winget install abooodbah.LeanMark` · `choco install leanmark` · [All Lean apps](https://abooodbah.github.io/) +[![winget version](https://img.shields.io/winget/v/abooodbah.LeanMark?label=winget)](https://github.com/microsoft/winget-pkgs/tree/master/manifests/a/abooodbah/LeanMark) [![Microsoft Store](https://img.shields.io/badge/Microsoft%20Store-9MSS4WKJ22ZV-0078d4)](https://apps.microsoft.com/detail/9MSS4WKJ22ZV) [![License: MIT](https://img.shields.io/github/license/abooodbah/leanmark)](LICENSE) [![GitHub release downloads](https://img.shields.io/github/downloads/abooodbah/leanmark/total)](https://github.com/abooodbah/leanmark/releases) + **Read Markdown and Mermaid diagrams without opening an IDE. Everything renders offline, with no Electron and no telemetry.** @@ -98,7 +100,9 @@ Security. Later launches open normally. ### GitHub Releases Windows, Linux (.deb), and macOS builds are also on the -[Releases](https://github.com/abooodbah/leanmark/releases) page. +[Releases](https://github.com/abooodbah/leanmark/releases) page. To hear about +new versions, follow the [releases feed](https://github.com/abooodbah/leanmark/releases.atom) +in a feed reader. ### Android (closed test)