From 2abce81246e9cc2d034b4cca04e58b0b7a416cab Mon Sep 17 00:00:00 2001 From: juprodh Date: Fri, 21 Aug 2026 12:29:31 +0800 Subject: [PATCH] Size images from a digits-only alt Signed-off-by: juprodh --- README.md | 2 +- src/lib/markdown/image-plugin.ts | 40 +++++++++++++++++++++----------- tests/lib/markdown.test.ts | 19 +++++++++++++++ 3 files changed, 46 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2370ec6..a506f19 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ imports here; see [Bases and Canvas](#bases-and-canvas) below. | YouTube embed `![](url)` | ✅ | ✅ | — | | Tweet embed `![](url)` | — plain link, by design | ✅ | — | | Image sizing `![alt\|300](…)` | ✅ | ✅ | — | -| Image sizing, no alt `![300](…)` | — | ✅ | — | +| Image sizing, no alt `![300](…)` | ✅ | ✅ | — | | PDF / audio embeds `![[file.pdf]]` | — | ✅ | — | | Transclusion `![[note]]` | — | ✅ | — | | Block references `[[note#^id]]` | ✅ ids you type | ✅ auto-minted too | — | diff --git a/src/lib/markdown/image-plugin.ts b/src/lib/markdown/image-plugin.ts index 19b122a..0976cc4 100644 --- a/src/lib/markdown/image-plugin.ts +++ b/src/lib/markdown/image-plugin.ts @@ -1,24 +1,36 @@ import type { MarkdownIt, Token } from "markdown-it"; -const SIZE_RE = /\|(\d+)(?:x(\d+))?$/; +const SUFFIX_SIZE_RE = /\|(\d+)(?:x(\d+))?$/; +const BARE_SIZE_RE = /^(\d+)(?:x(\d+))?$/; -// Obsidian sizes an image with `![alt|300](…)` or `![alt|300x200](…)`. The alt is built -// from the token's children, so the suffix has to come off there too — otherwise a -// screen reader announces the number as part of the description. +function applySize(token: Token, width: string, height?: string): void { + if (token.attrIndex("width") < 0) token.attrPush(["width", width]); + if (height && token.attrIndex("height") < 0) + token.attrPush(["height", height]); +} + +// Obsidian sizes an image with `![alt|300](…)`, `![alt|300x200](…)`, or — when there is no +// alt to hang the suffix on — a wholly numeric alt, `![300](…)`. Either way the number is a +// dimension, not a description: the alt is built from the token's children, so it has to +// come off there too, or a screen reader announces the number as the image. function applySizeFromAlt(token: Token): void { - const match = SIZE_RE.exec(token.content); + const bare = BARE_SIZE_RE.exec(token.content); + if (bare) { + token.children = []; + token.content = ""; + applySize(token, bare[1] as string, bare[2]); + return; + } + + const match = SUFFIX_SIZE_RE.exec(token.content); if (!match) return; const last = token.children?.at(-1); - if (last?.type !== "text" || !SIZE_RE.test(last.content)) return; - - last.content = last.content.replace(SIZE_RE, ""); - token.content = token.content.replace(SIZE_RE, ""); - if (token.attrIndex("width") < 0) - token.attrPush(["width", match[1] as string]); - if (match[2] && token.attrIndex("height") < 0) { - token.attrPush(["height", match[2]]); - } + if (last?.type !== "text" || !SUFFIX_SIZE_RE.test(last.content)) return; + + last.content = last.content.replace(SUFFIX_SIZE_RE, ""); + token.content = token.content.replace(SUFFIX_SIZE_RE, ""); + applySize(token, match[1] as string, match[2]); } // Content images (`![](…)`) render eager by default, so a note with many embedded diff --git a/tests/lib/markdown.test.ts b/tests/lib/markdown.test.ts index d7d1537..5a62fb5 100644 --- a/tests/lib/markdown.test.ts +++ b/tests/lib/markdown.test.ts @@ -45,6 +45,25 @@ describe("renderMarkdown", () => { expect(html).toContain('alt="diagram"'); }); + test("reads an Obsidian width off a wholly numeric alt", () => { + const { html } = renderMarkdown("![300](https://example.com/x.png)"); + expect(html).toContain('width="300"'); + expect(html).toContain('alt=""'); + }); + + test("reads width and height off a wholly numeric alt", () => { + const { html } = renderMarkdown("![300x200](https://example.com/x.png)"); + expect(html).toContain('width="300"'); + expect(html).toContain('height="200"'); + expect(html).toContain('alt=""'); + }); + + test("leaves an alt that only starts with digits alone", () => { + const { html } = renderMarkdown("![300 dpi](https://example.com/x.png)"); + expect(html).toContain('alt="300 dpi"'); + expect(html).not.toContain("width="); + }); + test("leaves a non-numeric pipe in the alt alone", () => { const { html } = renderMarkdown("![a|b](https://example.com/x.png)"); expect(html).toContain('alt="a|b"'); -- 2.51.2