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) {
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.