Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions site/public/_headers
Original file line number Diff line number Diff line change
@@ -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'
1 change: 1 addition & 0 deletions site/public/robots.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
User-agent: *
Allow: /
Content-Signal: ai-train=yes, search=yes, ai-input=yes

Sitemap: https://diffy.run/sitemap.xml
8 changes: 4 additions & 4 deletions site/scripts/check-build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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(`<link rel="canonical" href="https://diffy.run${path}"`));
assert(html.includes('content="https://diffy.run/_astro/'));
assert.equal(html.includes('noindex, follow'), path === '/thanks');
assert.equal(html.includes('noindex, follow'), path === '/thanks/');
}
const schema = JSON.parse(home.match(/<script type="application\/ld\+json">(.*?)<\/script>/s)[1]);
const app = schema['@graph'].find(entity => entity['@type'] === 'SoftwareApplication');
Expand All @@ -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('<loc>https://diffy.run/</loc>'));
assert(sitemap.includes('<loc>https://diffy.run/downloads</loc>'));
assert(sitemap.includes('<loc>https://diffy.run/downloads/</loc>'));
assert(!sitemap.includes('thanks') && !sitemap.includes('404'));
for (const file of ['llms.txt', 'llms-full.txt']) {
const markdown = read(file);
Expand All @@ -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.');
2 changes: 1 addition & 1 deletion site/src/data/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`;
}
4 changes: 2 additions & 2 deletions site/src/layouts/Page.astro
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ 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',
'@graph': [
{ '@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);
Expand Down
2 changes: 1 addition & 1 deletion site/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ if (checkout) {
<section class="features questions frame" aria-labelledby="questions-title">
<h2 id="questions-title">Before you download.</h2>
{questions.map(({ question, answer }) => <section class="product-question"><h3>{question}</h3><p>{answer}</p></section>)}
<p class="product-summary"><a href="/downloads">Downloads and installation ↗</a></p>
<p class="product-summary"><a href="/downloads/">Downloads and installation ↗</a></p>
</section>
</main>
</Page>
2 changes: 1 addition & 1 deletion site/src/pages/llms.txt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' } });
};
2 changes: 1 addition & 1 deletion site/src/pages/sitemap.xml.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { APIRoute } from 'astro';

export const GET: APIRoute = ({ site }) => new Response(`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${['/', '/downloads'].map(path => ` <url><loc>${new URL(path, site)}</loc></url>`).join('\n')}\n</urlset>\n`, {
export const GET: APIRoute = ({ site }) => new Response(`<?xml version="1.0" encoding="UTF-8"?>\n<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n${['/', '/downloads/'].map(path => ` <url><loc>${new URL(path, site)}</loc></url>`).join('\n')}\n</urlset>\n`, {
headers: { 'Content-Type': 'application/xml; charset=utf-8' },
});
2 changes: 1 addition & 1 deletion site/src/pages/thanks.astro
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@ import Downloads from '../components/Downloads.astro';
<p class="intro">Thanks for being here.</p>
<p class="compatibility">One purchase supports all platforms. Requires Git.</p>
<Downloads />
<p class="download-note">Save <a href="/downloads">diffy.run/downloads</a> for future releases.</p>
<p class="download-note">Save <a href="/downloads/">diffy.run/downloads</a> for future releases.</p>
</main>
</Page>
Loading