From defb193c80904f6969d2aa13885e1cedb4518d84 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Mon, 29 Jun 2026 15:47:12 -0400 Subject: [PATCH 1/3] fix(site): rewrite broken README.md and out-of-docs links MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a markdown-it core rule that: - Rewrites README.md links to directory index paths (README→index rewrite only affects routing, not link resolution) - Converts relative links that escape the docs/ directory into GitHub source URLs, fixing 37 broken links to source code files Also fixes eslint warnings (unnecessary regex escape, any type). Signed-off-by: Juntao Wang --- website/.vitepress/config.ts | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/website/.vitepress/config.ts b/website/.vitepress/config.ts index 50f880e5f..261485b39 100644 --- a/website/.vitepress/config.ts +++ b/website/.vitepress/config.ts @@ -59,7 +59,7 @@ function escapeVueSyntax(src: string): string { }).join('\n') } -const KNOWN_TAGS = /^<\/?(?:a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|menu|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|slot|small|source|span|strong|style|sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|svg|path|g|circle|rect|line|polyline|polygon|text|defs|use|symbol)[\s>\/!]/i +const KNOWN_TAGS = /^<\/?(?:a|abbr|address|area|article|aside|audio|b|base|bdi|bdo|blockquote|body|br|button|canvas|caption|cite|code|col|colgroup|data|datalist|dd|del|details|dfn|dialog|div|dl|dt|em|embed|fieldset|figcaption|figure|footer|form|h[1-6]|head|header|hgroup|hr|html|i|iframe|img|input|ins|kbd|label|legend|li|link|main|map|mark|menu|meta|meter|nav|noscript|object|ol|optgroup|option|output|p|param|picture|pre|progress|q|rp|rt|ruby|s|samp|script|search|section|select|slot|small|source|span|strong|style|sub|summary|sup|table|tbody|td|template|textarea|tfoot|th|thead|time|title|tr|track|u|ul|var|video|wbr|svg|path|g|circle|rect|line|polyline|polygon|text|defs|use|symbol)[\s>/!]/i function escapeLine(line: string): string { let result = '' @@ -316,7 +316,7 @@ export default defineConfig({ }, preConfig: (md) => { const defaultParse = md.parse.bind(md) - md.parse = (src: string, env: any) => { + md.parse = (src: string, env: Record) => { return defaultParse(escapeVueSyntax(src), env) } }, @@ -330,6 +330,38 @@ export default defineConfig({ return defaultCodeInline(tokens, idx, options, env, self) } + // Rewrite README.md links to directory index (README→index rewrite), + // and convert relative links pointing outside docs/ to GitHub URLs. + md.core.ruler.push('rewrite-links', (state) => { + for (const token of state.tokens) { + if (!token.children) continue + for (const child of token.children) { + if (child.type !== 'link_open') continue + const href = child.attrGet('href') + if (!href || href.startsWith('http') || href.startsWith('#') || href.startsWith('mailto:')) continue + + if (/README\.md(#.*)?$/.test(href)) { + child.attrSet('href', href.replace(/README\.md(#.*)?$/, (_: string, anchor: string) => anchor || './')) + } + + if (/\.\.\/(\.\.\/)*[^.][^/]*/.test(href)) { + const docPath = state.env?.relativePath || '' + const docDir = docPath.split('/').slice(0, -1) + const parts = href.split('#') + const linkPath = parts[0] + const anchor = parts[1] ? '#' + parts[1] : '' + const segments = linkPath.split('/') + let depth = 0 + for (const s of segments) { if (s === '..') depth++; else break } + if (depth > docDir.length) { + const remainder = segments.slice(depth).join('/') + child.attrSet('href', 'https://github.com/fullsend-ai/fullsend/tree/main/' + remainder + anchor) + } + } + } + } + }) + const defaultFence = md.renderer.rules.fence!.bind(md.renderer.rules) md.renderer.rules.fence = (tokens, idx, options, env, self) => { if (tokens[idx].info.trim() === 'mermaid') { From 60f02c8e4b60e81ac70fae304aa75ed648100d76 Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Mon, 29 Jun 2026 16:09:56 -0400 Subject: [PATCH 2/3] fix(site): fix out-of-docs README and dotdir link rewriting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Check out-of-docs links before README rewrite so ../../README.md gets GitHub-redirected with the filename intact - Remove [^.] guard so .github/ paths are also rewritten to GitHub - README→index rewrite only applies to links staying within docs/ Signed-off-by: Juntao Wang --- website/.vitepress/config.ts | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/website/.vitepress/config.ts b/website/.vitepress/config.ts index 261485b39..f553e543f 100644 --- a/website/.vitepress/config.ts +++ b/website/.vitepress/config.ts @@ -330,8 +330,9 @@ export default defineConfig({ return defaultCodeInline(tokens, idx, options, env, self) } - // Rewrite README.md links to directory index (README→index rewrite), - // and convert relative links pointing outside docs/ to GitHub URLs. + // Rewrite relative links that escape the docs/ directory to GitHub + // source URLs, and rewrite README.md links to directory index paths + // (only for links that stay within docs/). md.core.ruler.push('rewrite-links', (state) => { for (const token of state.tokens) { if (!token.children) continue @@ -340,23 +341,24 @@ export default defineConfig({ const href = child.attrGet('href') if (!href || href.startsWith('http') || href.startsWith('#') || href.startsWith('mailto:')) continue - if (/README\.md(#.*)?$/.test(href)) { - child.attrSet('href', href.replace(/README\.md(#.*)?$/, (_: string, anchor: string) => anchor || './')) + // Check if the link escapes docs/ (more ../ than directory depth) + const docPath = state.env?.relativePath || '' + const docDir = docPath.split('/').slice(0, -1) + const parts = href.split('#') + const linkPath = parts[0] + const anchor = parts[1] ? '#' + parts[1] : '' + const segments = linkPath.split('/') + let depth = 0 + for (const s of segments) { if (s === '..') depth++; else break } + if (depth > docDir.length) { + const remainder = segments.slice(depth).join('/') + child.attrSet('href', 'https://github.com/fullsend-ai/fullsend/tree/main/' + remainder + anchor) + continue } - if (/\.\.\/(\.\.\/)*[^.][^/]*/.test(href)) { - const docPath = state.env?.relativePath || '' - const docDir = docPath.split('/').slice(0, -1) - const parts = href.split('#') - const linkPath = parts[0] - const anchor = parts[1] ? '#' + parts[1] : '' - const segments = linkPath.split('/') - let depth = 0 - for (const s of segments) { if (s === '..') depth++; else break } - if (depth > docDir.length) { - const remainder = segments.slice(depth).join('/') - child.attrSet('href', 'https://github.com/fullsend-ai/fullsend/tree/main/' + remainder + anchor) - } + // For links staying within docs/, rewrite README.md to directory index + if (/README\.md(#.*)?$/.test(href)) { + child.attrSet('href', href.replace(/README\.md(#.*)?$/, (_: string, a: string) => a || './')) } } } From 9aca1f9d22d2a934701d33e061886e4805f7f76d Mon Sep 17 00:00:00 2001 From: Juntao Wang Date: Mon, 29 Jun 2026 16:22:40 -0400 Subject: [PATCH 3/3] fix(site): use blob vs tree for GitHub source links Files (with extensions) use /blob/main/, directories use /tree/main/, matching GitHub's URL convention. Signed-off-by: Juntao Wang --- website/.vitepress/config.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/.vitepress/config.ts b/website/.vitepress/config.ts index f553e543f..9f75b85ff 100644 --- a/website/.vitepress/config.ts +++ b/website/.vitepress/config.ts @@ -352,7 +352,8 @@ export default defineConfig({ for (const s of segments) { if (s === '..') depth++; else break } if (depth > docDir.length) { const remainder = segments.slice(depth).join('/') - child.attrSet('href', 'https://github.com/fullsend-ai/fullsend/tree/main/' + remainder + anchor) + const prefix = /\.[a-zA-Z0-9]+$/.test(remainder) && !remainder.endsWith('/') ? 'blob' : 'tree' + child.attrSet('href', `https://github.com/fullsend-ai/fullsend/${prefix}/main/${remainder}${anchor}`) continue }