From 1d0f19d833c4264ddc333969a437ae868fbb4c5c Mon Sep 17 00:00:00 2001 From: Jordan Howlett Date: Sat, 24 Oct 2026 12:04:26 -0600 Subject: [PATCH] Align crawler links with Pages directory URLs --- site/public/_headers | 1 + site/public/robots.txt | 1 + site/scripts/check-build.mjs | 8 ++++---- site/src/data/product.ts | 2 +- site/src/layouts/Page.astro | 4 ++-- site/src/pages/index.astro | 2 +- site/src/pages/llms.txt.ts | 2 +- site/src/pages/sitemap.xml.ts | 2 +- site/src/pages/thanks.astro | 2 +- 9 files changed, 13 insertions(+), 11 deletions(-) diff --git a/site/public/_headers b/site/public/_headers index 0903ea1..5676994 100644 --- a/site/public/_headers +++ b/site/public/_headers @@ -1,4 +1,5 @@ /* X-Content-Type-Options: nosniff Referrer-Policy: strict-origin-when-cross-origin + Content-Signal: ai-train=yes, search=yes, ai-input=yes Content-Security-Policy: default-src 'self'; img-src 'self'; style-src 'self'; font-src 'self'; script-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'none' diff --git a/site/public/robots.txt b/site/public/robots.txt index 918699c..061478e 100644 --- a/site/public/robots.txt +++ b/site/public/robots.txt @@ -1,4 +1,5 @@ User-agent: * Allow: / +Content-Signal: ai-train=yes, search=yes, ai-input=yes Sitemap: https://diffy.run/sitemap.xml diff --git a/site/scripts/check-build.mjs b/site/scripts/check-build.mjs index e1e322b..e218b87 100644 --- a/site/scripts/check-build.mjs +++ b/site/scripts/check-build.mjs @@ -21,12 +21,12 @@ for (const page of ['downloads/index.html', 'thanks/index.html']) { } } assert(redirects.includes('/download https://github.com/hitSlop/diffy/releases/latest/download/Diffy-macos-aarch64.dmg 302')); -for (const [page, path] of [['index.html', '/'], ['downloads/index.html', '/downloads'], ['thanks/index.html', '/thanks']]) { +for (const [page, path] of [['index.html', '/'], ['downloads/index.html', '/downloads/'], ['thanks/index.html', '/thanks/']]) { const html = read(page); assert(!html.includes('diffy.hitslop.com')); assert(html.includes(`(.*?)<\/script>/s)[1]); const app = schema['@graph'].find(entity => entity['@type'] === 'SoftwareApplication'); @@ -40,7 +40,7 @@ assert(robots.includes('User-agent: *\nAllow: /')); assert(robots.includes('Sitemap: https://diffy.run/sitemap.xml')); const sitemap = read('sitemap.xml'); assert(sitemap.includes('https://diffy.run/')); -assert(sitemap.includes('https://diffy.run/downloads')); +assert(sitemap.includes('https://diffy.run/downloads/')); assert(!sitemap.includes('thanks') && !sitemap.includes('404')); for (const file of ['llms.txt', 'llms-full.txt']) { const markdown = read(file); @@ -51,7 +51,7 @@ for (const file of ['llms.txt', 'llms-full.txt']) { const full = read('llms-full.txt'); assert.equal(full.includes('Checkout is not yet available.'), !checkout); for (const platform of ['macos', 'windows', 'linux']) { - assert.equal(full.includes(`/downloads#${platform}`), published.has(platform)); + assert.equal(full.includes(`/downloads/#${platform}`), published.has(platform)); } assert(read('404.html').includes('noindex, follow')); console.log('Pricing, checkout, downloads, canonical URLs, crawler access, schema, sitemap, and AI documentation verified.'); diff --git a/site/src/data/product.ts b/site/src/data/product.ts index 670952f..3a09f29 100644 --- a/site/src/data/product.ts +++ b/site/src/data/product.ts @@ -10,5 +10,5 @@ export const questions = [ ]; export function productMarkdown(root: string) { - return `# Diffy\n\n> ${summary}\n\nOfficial website: ${root}\nSource code: ${source}\nPublisher: hitSlop (https://labs.hitslop.com)\nLicense: MIT\n\n## Features\n\n- Live staged, unstaged, and untracked Git diffs.\n- Split or unified views, syntax highlighting, expandable context, and source copying.\n- Worktree tabs that remember the selected file and reading position.\n- Whole-file staging and unstaging, with keyboard navigation and a command palette.\n- Read-only GitHub.com pull request diffs using an existing GitHub CLI login.\n- Optional discovery of local projects from Herdr, Codex, and Claude Code.\n\n## Platforms and downloads\n\nAll platforms require Git. macOS is beta; Windows/Linux are early alphas with limited hardware testing.\n\n${platforms.map(platform => `- ${platform.name} (${platform.stage.toLowerCase()}): ${platform.requirements}. ${platform.available ? `Download and installation: ${new URL(`/downloads#${platform.id}`, root)}` : 'A published binary is not yet available.'}`).join('\n')}\n\n## Questions\n\n${questions.map(({ question, answer }) => `### ${question}\n\n${answer}`).join('\n\n')}\n\n## Official links\n\n- Product and features: ${root}\n- Downloads and installation: ${new URL('/downloads', root)}\n- Source and issue tracker: ${source}\n\nDiffy is a separate product from other tools named diffy. Use the official website and repository above to identify it.\n`; + return `# Diffy\n\n> ${summary}\n\nOfficial website: ${root}\nSource code: ${source}\nPublisher: hitSlop (https://labs.hitslop.com)\nLicense: MIT\n\n## Features\n\n- Live staged, unstaged, and untracked Git diffs.\n- Split or unified views, syntax highlighting, expandable context, and source copying.\n- Worktree tabs that remember the selected file and reading position.\n- Whole-file staging and unstaging, with keyboard navigation and a command palette.\n- Read-only GitHub.com pull request diffs using an existing GitHub CLI login.\n- Optional discovery of local projects from Herdr, Codex, and Claude Code.\n\n## Platforms and downloads\n\nAll platforms require Git. macOS is beta; Windows/Linux are early alphas with limited hardware testing.\n\n${platforms.map(platform => `- ${platform.name} (${platform.stage.toLowerCase()}): ${platform.requirements}. ${platform.available ? `Download and installation: ${new URL(`/downloads/#${platform.id}`, root)}` : 'A published binary is not yet available.'}`).join('\n')}\n\n## Questions\n\n${questions.map(({ question, answer }) => `### ${question}\n\n${answer}`).join('\n\n')}\n\n## Official links\n\n- Product and features: ${root}\n- Downloads and installation: ${new URL('/downloads/', root)}\n- Source and issue tracker: ${source}\n\nDiffy is a separate product from other tools named diffy. Use the official website and repository above to identify it.\n`; } diff --git a/site/src/layouts/Page.astro b/site/src/layouts/Page.astro index 017f638..6579575 100644 --- a/site/src/layouts/Page.astro +++ b/site/src/layouts/Page.astro @@ -4,7 +4,7 @@ import socialImage from '../assets/hero.png'; import { summary, source } from '../data/product'; import { platforms } from '../data/releases'; const { title = 'Diffy — native Git diff viewer for macOS, Windows & Linux', description = summary, applicationSchema = false, noindex = false } = Astro.props; -const canonical = new URL(Astro.url.pathname.replace(/\/+$/, '') || '/', Astro.site); +const canonical = new URL(Astro.url.pathname, Astro.site); const root = new URL('/', Astro.site).href; const schema = { '@context': 'https://schema.org', @@ -12,7 +12,7 @@ const schema = { { '@type': 'Organization', '@id': `${root}#publisher`, name: 'hitSlop', url: 'https://labs.hitslop.com' }, { '@type': 'WebSite', '@id': `${root}#website`, name: 'Diffy', url: root, publisher: { '@id': `${root}#publisher` }, inLanguage: 'en' }, { '@type': 'WebPage', '@id': `${canonical}#page`, url: canonical.href, name: title, description, isPartOf: { '@id': `${root}#website` }, ...(applicationSchema ? { mainEntity: { '@id': `${root}#app` } } : {}) }, - ...(applicationSchema ? [{ '@type': 'SoftwareApplication', '@id': `${root}#app`, name: 'Diffy', url: root, description: summary, applicationCategory: 'DeveloperApplication', operatingSystem: platforms.map(platform => platform.name), publisher: { '@id': `${root}#publisher` }, sameAs: [source], license: 'https://opensource.org/license/mit', screenshot: new URL('/diffy.webp', root).href, downloadUrl: new URL('/downloads', root).href, featureList: ['Live Git diffs', 'Split and unified views', 'Git worktree tabs', 'Read-only GitHub pull request diffs', 'Whole-file staging and unstaging', 'Project discovery from Herdr, Codex, and Claude Code'] }] : []), + ...(applicationSchema ? [{ '@type': 'SoftwareApplication', '@id': `${root}#app`, name: 'Diffy', url: root, description: summary, applicationCategory: 'DeveloperApplication', operatingSystem: platforms.map(platform => platform.name), publisher: { '@id': `${root}#publisher` }, sameAs: [source], license: 'https://opensource.org/license/mit', screenshot: new URL('/diffy.webp', root).href, downloadUrl: new URL('/downloads/', root).href, featureList: ['Live Git diffs', 'Split and unified views', 'Git worktree tabs', 'Read-only GitHub pull request diffs', 'Whole-file staging and unstaging', 'Project discovery from Herdr, Codex, and Claude Code'] }] : []), ], }; const socialImageUrl = new URL(socialImage.src, Astro.site); diff --git a/site/src/pages/index.astro b/site/src/pages/index.astro index 324cdab..ab69699 100644 --- a/site/src/pages/index.astro +++ b/site/src/pages/index.astro @@ -91,7 +91,7 @@ if (checkout) {

Before you download.

{questions.map(({ question, answer }) =>

{question}

{answer}

)} -

Downloads and installation ↗

+

Downloads and installation ↗

diff --git a/site/src/pages/llms.txt.ts b/site/src/pages/llms.txt.ts index f995b3e..0ff4985 100644 --- a/site/src/pages/llms.txt.ts +++ b/site/src/pages/llms.txt.ts @@ -3,5 +3,5 @@ import { summary, source } from '../data/product'; export const GET: APIRoute = ({ site }) => { const root = new URL('/', site).href; - return new Response(`# Diffy\n\n> ${summary}\n\nDiffy is published by hitSlop. macOS is beta; Windows and Linux are alpha. All platforms require Git.\n\n## Official documentation\n\n- [Product and features](${root}): What Diffy does and its current capabilities.\n- [Downloads and installation](${new URL('/downloads', root)}): Requirements, availability, and installation for each platform.\n- [Complete product documentation](${new URL('/llms-full.txt', root)}): Plain-text features, limitations, pricing, and platform facts.\n- [Source code](${source}): The official MIT-licensed repository.\n`, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }); + return new Response(`# Diffy\n\n> ${summary}\n\nDiffy is published by hitSlop. macOS is beta; Windows and Linux are alpha. All platforms require Git.\n\n## Official documentation\n\n- [Product and features](${root}): What Diffy does and its current capabilities.\n- [Downloads and installation](${new URL('/downloads/', root)}): Requirements, availability, and installation for each platform.\n- [Complete product documentation](${new URL('/llms-full.txt', root)}): Plain-text features, limitations, pricing, and platform facts.\n- [Source code](${source}): The official MIT-licensed repository.\n`, { headers: { 'Content-Type': 'text/plain; charset=utf-8' } }); }; diff --git a/site/src/pages/sitemap.xml.ts b/site/src/pages/sitemap.xml.ts index dea5177..f1c10b2 100644 --- a/site/src/pages/sitemap.xml.ts +++ b/site/src/pages/sitemap.xml.ts @@ -1,5 +1,5 @@ import type { APIRoute } from 'astro'; -export const GET: APIRoute = ({ site }) => new Response(`\n\n${['/', '/downloads'].map(path => ` ${new URL(path, site)}`).join('\n')}\n\n`, { +export const GET: APIRoute = ({ site }) => new Response(`\n\n${['/', '/downloads/'].map(path => ` ${new URL(path, site)}`).join('\n')}\n\n`, { headers: { 'Content-Type': 'application/xml; charset=utf-8' }, }); diff --git a/site/src/pages/thanks.astro b/site/src/pages/thanks.astro index 16fc64a..923caef 100644 --- a/site/src/pages/thanks.astro +++ b/site/src/pages/thanks.astro @@ -9,6 +9,6 @@ import Downloads from '../components/Downloads.astro';

Thanks for being here.

One purchase supports all platforms. Requires Git.

-

Save diffy.run/downloads for future releases.

+

Save diffy.run/downloads for future releases.