diff --git a/bun.lock b/bun.lock index 56c2203..4eb6625 100644 --- a/bun.lock +++ b/bun.lock @@ -11,6 +11,7 @@ "chalk": "^5.6.2", "cli-highlight": "^2.1.11", "cli-table3": "^0.6.5", + "terminal-link": "^5.0.0", }, "devDependencies": { "@biomejs/biome": "2.4.12", @@ -62,6 +63,8 @@ "ai": ["ai@6.0.161", "", { "dependencies": { "@ai-sdk/gateway": "3.0.98", "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-ufhmijmx2YyWTPAicGgtpLOB/xD7mG8zKs1pT1Trj+JL/3r1rS8fkMi/cHZoChSAQSGB4pgmcWVxDrVTUvK2IQ=="], + "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -84,13 +87,15 @@ "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], - "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + "has-flag": ["has-flag@5.0.1", "", {}, "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA=="], "highlight.js": ["highlight.js@10.7.3", "", {}, "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="], @@ -114,6 +119,10 @@ "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "supports-hyperlinks": ["supports-hyperlinks@4.4.0", "", { "dependencies": { "has-flag": "^5.0.1", "supports-color": "^10.2.2" } }, "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg=="], + + "terminal-link": ["terminal-link@5.0.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "supports-hyperlinks": "^4.1.0" } }, "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA=="], + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], @@ -136,6 +145,10 @@ "parse5-htmlparser2-tree-adapter/parse5": ["parse5@6.0.1", "", {}, "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="], + "supports-color/has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "supports-hyperlinks/supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="], + "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "cli-highlight/chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], diff --git a/package.json b/package.json index 64867c3..3c6ff3d 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "ansi-styles": "^6.2.3", "chalk": "^5.6.2", "cli-highlight": "^2.1.11", - "cli-table3": "^0.6.5" + "cli-table3": "^0.6.5", + "terminal-link": "^5.0.0" } } diff --git a/src/render/index.ts b/src/render/index.ts index f8e6f44..cbc38f4 100644 --- a/src/render/index.ts +++ b/src/render/index.ts @@ -81,7 +81,7 @@ export default class Renderer { const [columns] = pipe.getWindowSize(); const rendered = await renderMarkdown(str, columns); - pipe.write(rendered); + pipe.write(rendered.replace(/\n\n$/, "\n")); } async renderStream(stream: AsyncIterableStream>) { diff --git a/src/render/markdown.ts b/src/render/markdown.ts index 05baa83..e0d67c9 100644 --- a/src/render/markdown.ts +++ b/src/render/markdown.ts @@ -1,6 +1,13 @@ import chalk from "chalk"; import { highlight } from "cli-highlight"; import CliTable3 from "cli-table3"; +import terminalLink from "terminal-link"; + +const originalErr = console.error; +console.error = (...data: unknown[]) => { + if (data.join("").includes("Could not find the language")) return; + originalErr(...data); +}; export async function renderMarkdown( text: PromiseLike | string, @@ -13,12 +20,12 @@ export async function renderMarkdown( return Bun.markdown.render(md, { heading: (children, { level }) => { const colors = [ - chalk.cyanBright.underline, - chalk.blueBright, - chalk.greenBright, - chalk.cyan, + chalk.cyan.underline, chalk.blue, chalk.green, + chalk.cyanBright, + chalk.blueBright, + chalk.greenBright, ]; const color = colors[level - 1] ?? chalk.white; return color.bold(`${"#".repeat(level)} ${children}\n\n`); @@ -44,7 +51,7 @@ export async function renderMarkdown( } catch {} } - return `\`\`\`${chalk.green(meta?.language)}\n${toRender}\n\`\`\`\n`; + return `\`\`\`${chalk.green(meta?.language)}\n${toRender}\n\`\`\``; }, list: (children, { depth }) => `${depth > 0 ? "\n" : ""}${children}${depth === 0 ? "\n" : ""}`, @@ -61,15 +68,12 @@ export async function renderMarkdown( } return `${indent}${marker} ${children}\n`; }, - hr: () => `\n${chalk.dim("─".repeat(columns))}\n`, + hr: () => `${chalk.dim("─".repeat(columns))}\n\n`, table: (_children) => { const toRender = currentTable.toString(); currentTable = new CliTable3(); return `${toRender}\n\n`; }, - thead: () => { - return ""; - }, tr: (children) => { if (children) currentTable.push(children.split("|").slice(1)); return ""; @@ -82,9 +86,9 @@ export async function renderMarkdown( strong: (children) => chalk.bold(children), emphasis: (children) => chalk.italic(children), strikethrough: (children) => chalk.grey.strikethrough(children), - codespan: (children) => chalk.bgWhite.black(children), + codespan: (children) => chalk.bgWhite.black(`\`${children}\``), link: (children, { href }) => - chalk.blueBright(`${children}: ${chalk.underline(href)}`), + chalk.blueBright.underline(terminalLink(children, href)), image: () => "", text: (children) => children, html: () => "",