gui: a plugin's README reads as it does on GitHub (这里的Markdown你没有渲染) — a link inside bold (**Curated by [Numman Ali](…)**) was shown as its source, and a badge, a picture in a link ([![npm version](…)](…)), as ![npm version](…) text; bold and italics now hold links and code, a badge goes whole, a link written as HTML (<a href>) is a link, and a link to one of the README's headings ([Install](#-quick-start), GitHub's anchors) goes to it in the page instead of nowhere. The publisher line under the name ends in an ellipsis rather than cut, and a box drawn in │ and ─ joins up. plugin-market.test.cjs (Chromium, WebKit, en/zh) fails without the change; old-webkit, click-scroll pass. go build -tags nogui (darwin, GOOS=linux), GOOS=windows go build, go test ./internal/gui ./internal/plugin pass.

This commit is contained in:
yetone committed 2026-09-30 19:07:34 +08:00
1 parent e54ebf397a
commit 8a4283256d
3 files changed
+53 -22

No files matched your search

+1 -1
View File
@@ -159,7 +159,7 @@
.pm-md a:hover { text-decoration: underline; }
.pm-md code { font: 11.5px var(--code); padding: 1px 4px; border-radius: 4px; background: var(--pill); color: var(--fg); }
.pm-md pre { margin: 0 0 10px; padding: 9px 11px; border-radius: 8px; background: var(--card-2); border: 1px solid var(--line-2); overflow-x: auto; }
.pm-md pre code { padding: 0; background: none; font-size: 11.5px; line-height: 1.5; white-space: pre; }
.pm-md pre code { padding: 0; background: none; font-size: 11.5px; line-height: 1.35; white-space: pre; } /* close enough for a box drawn in │ and ─ to join */
.pm-md blockquote { margin: 0 0 9px; padding: 7px 11px; border-radius: 8px; background: var(--card-2); color: var(--fg-2); }
.pm-md hr { border: 0; border-top: 1px solid var(--line-2); margin: 12px 0; }
.pm-table { overflow-x: auto; margin: 0 0 10px; }
+32 -20
View File
@@ -446,7 +446,9 @@
const who = el("div", "pm-who");
const nm = el("div", "pm-name");
nm.append(el("b", "", l.name));
who.append(nm, el("div", "pm-by", l.community ? t("magpie community") + " · " + l.package : [l.npm?.publisher, l.package].filter(Boolean).join(" · ")));
const by = el("div", "pm-by"); // the words in a span of their own, or a flex row cuts them without the ellipsis
by.append(el("span", "", l.community ? t("magpie community") + " · " + l.package : [l.npm?.publisher, l.package].filter(Boolean).join(" · ")));
who.append(nm, by);
const actBox = el("div", "pm-dact");
// redrawn only when what it would say changes: a button made afresh
// loses the pointer's hover
@@ -513,34 +515,43 @@
// pictures (a README's badges and screenshots come from anywhere)
function markdown(src) {
const root = el("div", "pm-md");
const lines = src.replace(/\r\n?/g, "\n").replace(/<!--[\s\S]*?-->|<(script|style)\b[\s\S]*?<\/\1\s*>/gi, "").split("\n");
const lines = src.replace(/\r\n?/g, "\n").replace(/<!--[\s\S]*?-->|<(script|style)\b[\s\S]*?<\/\1\s*>/gi, "")
.replace(/\[\s*!\[[^\]]*\]\([^)]*\)\s*\]\([^)]*\)/g, "") // a badge: a picture in a link
.split("\n");
// a link: to the web, opened outside; to a heading of the README
// ("#-quick-start"), there, as the reader asked
const link = (href, text, into) => {
const a = el("a", "", "");
a.href = href;
inline(text.replace(/!\[[^\]]*\]\([^)]*\)/g, "").trim() || href, a);
a.onclick = (ev) => {
ev.preventDefault();
if (href[0] !== "#") return api("open", { url: href }).catch(() => {});
const to = root.querySelector(`[data-slug="${CSS.escape(decodeURIComponent(href.slice(1)).toLowerCase())}"]`);
if (to && window.scrollOnPurpose?.(ev)) to.scrollIntoView({ block: "start", behavior: "smooth" });
};
into.append(a);
};
const inline = (text, into) => {
const re = /(`[^`]+`)|\*\*([^*]+)\*\*|__([^_]+)__|\*([^*\s][^*]*)\*|!\[[^\]]*\]\([^)]*\)|\[([^\]]+)\]\(([^)\s]+)[^)]*\)|<(https?:\/\/[^>\s]+)>|<\/?[a-zA-Z][^>]*>/g;
const re = /(`[^`]+`)|\*\*(.+?)\*\*|__(.+?)__|\*([^*\s][^*]*)\*|!\[[^\]]*\]\([^)]*\)|\[([^\]]+)\]\(([^)\s]+)[^)]*\)|<(https?:\/\/[^>\s]+)>|<a\s[^>]*?href\s*=\s*["']([^"']+)["'][^>]*>([\s\S]*?)<\/a\s*>|<\/?[a-zA-Z][^>]*>/g;
let at = 0, m;
while ((m = re.exec(text))) {
if (m.index > at) into.append(text.slice(at, m.index));
if (m[1]) into.append(el("code", "", m[1].slice(1, -1)));
else if (m[2] || m[3]) into.append(el("strong", "", m[2] || m[3]));
else if (m[4]) into.append(el("em", "", m[4]));
else if (m[5]) {
const href = m[6];
if (/^https?:\/\//.test(href)) {
const a = el("a", "", "");
a.href = href;
inline(m[5].replace(/!\[[^\]]*\]\([^)]*\)/g, "").trim() || href, a);
a.onclick = (ev) => { ev.preventDefault(); api("open", { url: href }).catch(() => {}); };
into.append(a);
} else inline(m[5], into);
} else if (m[7]) {
const a = el("a", "", m[7]);
a.href = m[7];
a.onclick = (ev) => { ev.preventDefault(); api("open", { url: m[7] }).catch(() => {}); };
into.append(a);
}
else if (m[2] || m[3]) { const b = el("strong"); inline(m[2] || m[3], b); into.append(b); }
else if (m[4]) { const e = el("em"); inline(m[4], e); into.append(e); }
else if (m[5] || m[8]) {
const href = m[6] || m[8], text = m[5] || m[9].replace(/<[^>]*>/g, "");
if (/^https?:\/\//.test(href) || /^#./.test(href)) link(href, text, into);
else inline(text, into);
} else if (m[7]) link(m[7], m[7], into);
at = re.lastIndex;
}
if (at < text.length) into.append(text.slice(at));
};
// GitHub's anchor for a heading: lower case, its punctuation and
// emoji gone, spaces as dashes ("💡 Philosophy" is "-philosophy")
const slug = (s) => s.trim().toLowerCase().replace(/[^\p{L}\p{N}\s_-]/gu, "").replace(/\s/g, "-");
let i = 0, list = null, para = null;
const flush = () => { list = null; para = null; };
while (i < lines.length) {
@@ -562,6 +573,7 @@
flush();
const e = el("h" + Math.min(6, h[1].length + 2));
inline(h[2], e);
e.dataset.slug = slug(e.textContent);
if (e.textContent.trim()) root.append(e);
i++;
continue;
+20 -1
View File
@@ -31,8 +31,13 @@ const listings = [
const README = [
"# opencode-copilot-auth",
"![badge](https://img.shields.io/npm/v/x.svg) Copilot for **OpenCode**.",
"**Curated by [Ann](https://example.test/ann)**",
"[![npm version](https://img.shields.io/npm/v/x.svg)](https://www.npmjs.com/package/x)",
"[![Tests](https://github.com/x/y/badge.svg)](https://github.com/x/y/actions)",
"[Jump](#-install) · <a href=\"https://example.test/html\"><b>HTML link</b></a>",
...Array.from({ length: 40 }, (_, i) => "\nFiller paragraph " + i + " so the page scrolls."),
"",
"## Install",
"## 🚀 Install",
"```sh",
"opencode auth login",
"```",
@@ -151,6 +156,20 @@ for (const engine of (process.env.BROWSER ? [process.env.BROWSER] : ["chromium",
assert.doesNotMatch(await dlg.locator(".pm-md").innerText(), /pwned/);
assert.equal(await dlg.locator(".pm-md pre code").innerText(), "opencode auth login");
await shot(page.locator("#modal .dialog"), `plugins-page-${engine}-${lang}`);
// markdown in bold is still markdown; a badge (a picture in a link) goes whole; nothing shows as source
assert.equal((await dlg.locator(".pm-md strong a").innerText()).trim(), "Ann");
assert.doesNotMatch(await dlg.locator(".pm-md").innerText(), /!\[|\]\(|npm version|Tests|<\/?[ab]\b/);
assert.equal((await dlg.locator(".pm-md a", { hasText: "HTML link" }).innerText()).trim(), "HTML link");
// a link to a heading goes to it, in the page, not out
const body = dlg.locator(".ebody");
assert.equal(await body.evaluate((b) => b.scrollTop), 0);
await dlg.locator(".pm-md a", { hasText: "Jump" }).click();
await page.waitForFunction(() => {
const b = document.querySelector("#modal .pm-detail .ebody"), h = b.querySelector("h4[data-slug='-install']");
const r = h?.getBoundingClientRect(), v = b.getBoundingClientRect();
return h && b.scrollTop > 0 && r.top >= v.top - 1 && r.bottom <= v.bottom + 1;
});
assert(!asked.some(([k]) => k === "open"), "a heading's link isn't opened outside");
await dlg.locator(".pm-md a", { hasText: "the docs" }).click();
assert.deepEqual(asked.find(([k]) => k === "open")[1], { url: "https://example.test/docs" });