Fix #340: autolink issue/PR refs #354

Merged
crueber merged 2 commits from fix/issue-340 into main 2026-09-11 23:36:05 +00:00
Owner

Implements Forgejo #340: #N → /{o}/{r}/issues/N and PRN (any case) → /{o}/{r}/pull/N in comment and issue/PR bodies, boundary-delimited only (never in-word, in URLs, or in existing markdown links; #3.2 excluded, sentence-final #3. links).\n\n- Pure string layer over marked HTML inside renderMarkdownHtml (linkifyIssueRefs, core linkifyRefText in web/src/lib/render-md.js), running AFTER resolveMarkdownUrls so the #182/#185 resolver can never mangle the fresh hrefs; skips <a>/<code>/<pre> contents, so code spans/fences, existing links, and URL fragments pass through untouched.\n- Thread pages (Issue.jsx, Pull.jsx) pass mdCtx={{ owner, repo }} — every renderBody consumer gets refs for free.\n- Decisions: leading zeros link as written with numeric hrefs (#003 → /issues/3); dead refs still link (honest 404, pipeline is sync/headless); & excluded from the leading boundary so entities never corrupt; zero sanitizer changes (bare relative href, allowlist pin in test).\n- Follow-up flagged, out of scope: commit bodies (linkifyBody, diff.js) — linkifyRefText is exported reusable for exactly that.\n- Tests: web/test/unit/refs-autolink.test.js (21 cases, full match table); related suites 80/80 green; full suite 685/688 (3 smoke.test.js failures are environmental — they fetch a live :8080, unrelated to this diff); vite build + esbuild SDK bundle green. Doc decision appended to docs/go/12_web_ui.md (law 12).\n\nNo new deps. Browser proof open (shared-daemon loopback guard).

Implements Forgejo #340: `#N → /{o}/{r}/issues/N` and `PRN` (any case) → `/{o}/{r}/pull/N` in comment and issue/PR bodies, boundary-delimited only (never in-word, in URLs, or in existing markdown links; `#3.2` excluded, sentence-final `#3.` links).\n\n- Pure string layer over marked HTML inside `renderMarkdownHtml` (`linkifyIssueRefs`, core `linkifyRefText` in `web/src/lib/render-md.js`), running AFTER `resolveMarkdownUrls` so the #182/#185 resolver can never mangle the fresh hrefs; skips `<a>`/`<code>`/`<pre>` contents, so code spans/fences, existing links, and URL fragments pass through untouched.\n- Thread pages (`Issue.jsx`, `Pull.jsx`) pass `mdCtx={{ owner, repo }}` — every `renderBody` consumer gets refs for free.\n- Decisions: leading zeros link as written with numeric hrefs (`#003 → /issues/3`); dead refs still link (honest 404, pipeline is sync/headless); `&` excluded from the leading boundary so entities never corrupt; zero sanitizer changes (bare relative href, allowlist pin in test).\n- Follow-up flagged, out of scope: commit bodies (`linkifyBody`, `diff.js`) — `linkifyRefText` is exported reusable for exactly that.\n- Tests: `web/test/unit/refs-autolink.test.js` (21 cases, full match table); related suites 80/80 green; full suite 685/688 (3 `smoke.test.js` failures are environmental — they fetch a live :8080, unrelated to this diff); `vite build` + esbuild SDK bundle green. Doc decision appended to `docs/go/12_web_ui.md` (law 12).\n\nNo new deps. Browser proof open (shared-daemon loopback guard).
#N links to /{o}/{r}/issues/N, PRN (any case) to /{o}/{r}/pull/N,
boundary-delimited only. Pure string layer over marked HTML inside
renderMarkdownHtml (after resolveMarkdownUrls, skipping a/code/pre),
so every renderBody consumer gets it; thread pages pass {owner, repo}
mdCtx. Leading zeros link as written with numeric hrefs; dead refs
still link; zero sanitizer changes. Headless cover:
web/test/unit/refs-autolink.test.js (21 cases). Doc decision in
docs/go/12_web_ui.md (diff.js linkifyBody flagged as follow-up).
The IssueNew preview placeholder advertises '(markdown; #N links issues)'
but rendered without ctx, so refs stayed plain until posted. Pass the
same { owner, repo } mdCtx the thread pages use (no ref/dir — relative
URLs stay verbatim). Wiring pin added to refs-autolink.test.js; doc
decision clause updated (law 12).
Sign in to join this conversation.
No description provided.