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 ([](…)), as  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:
3 files changed
+53
-22
No files matched your search
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -31,8 +31,13 @@ const listings = [
|
||||
const README = [
|
||||
"# opencode-copilot-auth",
|
||||
" Copilot for **OpenCode**.",
|
||||
"**Curated by [Ann](https://example.test/ann)**",
|
||||
"[](https://www.npmjs.com/package/x)",
|
||||
"[](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" });
|
||||
|
||||
|
||||
Reference in new issue
Block a user