diff --git a/README.md b/README.md index c40dff7..fea9eb0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Diffy chameleon Diffy -[Downloads and installation](https://diffy.hitslop.com/downloads) · macOS **beta** · Windows/Linux **alpha** +[Downloads and installation](https://diffy.run/downloads) · macOS **beta** · Windows/Linux **alpha** ![Diffy on macOS: a split Rust diff, project change badges, a searchable file list grouped by directory, and project import](assets/screenshot.png) diff --git a/RELEASING.md b/RELEASING.md index 9fbceb2..88512f8 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -67,7 +67,7 @@ gh release create v0.2.3 --repo hitSlop/diffy --draft --verify-tag \ The manifest archive URL points to its immutable version tag. The app checks `releases/latest/download/update-macos-aarch64.json`, so draft releases are intentionally invisible to automatic checks. Publish only after release verification and explicit release authorization. -Always upload the fixed-name DMG with every release. The website redirects `https://diffy.hitslop.com/download` to GitHub's latest published release asset with that name. Include both DMGs in `SHA256SUMS`; retain the versioned app archive and manifest used by in-app updates. +Always upload the fixed-name DMG with every release. The website redirects `https://diffy.run/download` to GitHub's latest published release asset with that name. Include both DMGs in `SHA256SUMS`; retain the versioned app archive and manifest used by in-app updates. ## Update verification diff --git a/site/README.md b/site/README.md index 9c7f4ae..921f14e 100644 --- a/site/README.md +++ b/site/README.md @@ -1,6 +1,6 @@ # Diffy landing page -Astro static site for https://diffy.hitslop.com. The page uses Commit Mono and a compact, responsive hero banner from `src/assets/hero.png`, with one small bundled script for the cursor-following SVG eye. It has no tracking or payment verification. Touch devices, reduced-motion users, and JavaScript failures keep the original static artwork. Animation stops when settled or offscreen. +Astro static site for https://diffy.run. The page uses Commit Mono and a compact, responsive hero banner from `src/assets/hero.png`, with one small bundled script for the cursor-following SVG eye. It has no tracking or payment verification. Touch devices, reduced-motion users, and JavaScript failures keep the original static artwork. Animation stops when settled or offscreen. ```sh cd site @@ -14,7 +14,7 @@ npm run preview Set `DODO_CHECKOUT_URL` to the HTTPS hosted product checkout URL (for example `https://checkout.dodopayments.com/buy/pdt_example`) at build time. Do not set a private API key. Without the URL, the purchase button is disabled and shows “Checkout coming soon.” -Configure a one-time USD $14.99 product in Dodo. The build sets the checkout return URL to `https://diffy.hitslop.com/thanks`. Put `https://diffy.hitslop.com/downloads` in a Digital Files entitlement's external delivery link and attach it to the product as well, so buyers can keep it. Dodo setup remains manual until the product is ready. +Configure a one-time USD $14.99 product in Dodo. The build sets the checkout return URL to `https://diffy.run/thanks`. Put `https://diffy.run/downloads` in a Digital Files entitlement's external delivery link and attach it to the product as well, so buyers can keep it. Dodo setup remains manual until the product is ready. ```sh DODO_CHECKOUT_URL='https://checkout.dodopayments.com/buy/YOUR_PRODUCT_ID' npm run build @@ -46,7 +46,13 @@ Production builds separately from the test matrix, so the placeholder checkout U The commands above remain available for manual deployment with your existing local Wrangler login. Rebuild with the correct production checkout setting before uploading; never deploy an artifact built with the CI checkout fixture. -Associate `diffy.hitslop.com` in the Pages project's custom domains, then point its DNS CNAME at the project's Pages hostname. Cloudflare must register the custom domain before its DNS redirect can work. +Keep domain registration at Namecheap and use Cloudflare DNS for `diffy.run`. Associate `diffy.run` in the Pages project's custom domains, then point its proxied apex CNAME at `diffy-site.pages.dev`. The Pages custom-domain association is required in addition to DNS. `www.diffy.run` redirects to the apex, preserving paths and query strings. The former subdomain has been retired without a redirect. + +## Crawlers and product facts + +The site is prerendered HTML; product information and download instructions do not require JavaScript. `robots.txt` allows all crawlers and advertises `/sitemap.xml`. Keep Cloudflare's Search, Agent, and Training crawler policies set to Allow, with no AI crawler paywall or bot challenges on public documentation. + +`src/data/product.ts` supplies the product definition, visible questions, and `/llms-full.txt`; `/llms.txt` links to the canonical documentation. These text endpoints are an additional convenience for assistants, not a ranking guarantee. `/sitemap.xml` includes the homepage and downloads; `/thanks` and the error page use `noindex, follow`. JSON-LD describes the publisher, website, pages, and software application using factual capabilities, with no fabricated reviews or unavailable purchase offers. Update the shared facts when availability or pricing changes. The post-build `scripts/write-redirects.mjs` generates `dist/_redirects` from the same platform metadata used by the pages. `/download` retains its macOS behavior. `/download/macos`, `/download/windows`, and `/download/linux` use fixed-name assets in GitHub's latest release; unavailable targets redirect to `/downloads` instead. The release script creates an identical copy of the signed and notarized versioned DMG. Upload it with every release; never point the website at an unsigned build. @@ -64,7 +70,7 @@ The landing page displays regular price $24.99 crossed out and $14.99 during bet ## Dodo dashboard setup -No Dodo CLI or product is configured for this repository. Create one **Diffy — all desktop platforms** product with a one-time **USD $14.99** price (1499 cents), covering macOS beta and Windows/Linux alpha. Create a Digital Files entitlement named **Diffy downloads**, set its external link to **https://diffy.hitslop.com/downloads**, and attach it to the product. Suggested delivery instructions: +No Dodo CLI or product is configured for this repository. Create one **Diffy — all desktop platforms** product with a one-time **USD $14.99** price (1499 cents), covering macOS beta and Windows/Linux alpha. Create a Digital Files entitlement named **Diffy downloads**, set its external link to **https://diffy.run/downloads**, and attach it to the product. Suggested delivery instructions: > Choose your platform on the download page. All versions require Git. macOS is beta; Windows and Linux are early alphas with limited hardware testing. Save the page for future releases. diff --git a/site/astro.config.mjs b/site/astro.config.mjs index d0c742d..5f97009 100644 --- a/site/astro.config.mjs +++ b/site/astro.config.mjs @@ -1,7 +1,7 @@ import { defineConfig } from 'astro/config'; export default defineConfig({ - site: 'https://diffy.hitslop.com', + site: 'https://diffy.run', output: 'static', // Keep the eye controller external so script-src 'self' can remain strict. vite: { build: { assetsInlineLimit: 0 } }, diff --git a/site/public/robots.txt b/site/public/robots.txt index c2a49f4..918699c 100644 --- a/site/public/robots.txt +++ b/site/public/robots.txt @@ -1,2 +1,4 @@ User-agent: * Allow: / + +Sitemap: https://diffy.run/sitemap.xml diff --git a/site/scripts/check-build.mjs b/site/scripts/check-build.mjs index ae0b0ad..e1e322b 100644 --- a/site/scripts/check-build.mjs +++ b/site/scripts/check-build.mjs @@ -7,7 +7,7 @@ assert(home.includes('$24.99') && home.includes('during beta')); assert(!home.includes('Downloads & installation') && !home.includes('Diffy is in beta.')); const checkout = process.env.DODO_CHECKOUT_URL; assert.equal(home.includes('checkout.dodopayments.com/buy/'), !!checkout); -if (checkout) assert(home.includes('redirect_url=https%3A%2F%2Fdiffy.hitslop.com%2Fthanks')); +if (checkout) assert(home.includes('redirect_url=https%3A%2F%2Fdiffy.run%2Fthanks')); const published = new Set((process.env.DIFFY_RELEASED_PLATFORMS || 'macos').split(',')); const redirects = read('_redirects'); for (const page of ['downloads/index.html', 'thanks/index.html']) { @@ -21,4 +21,37 @@ 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')); -console.log('Pricing, checkout, platform availability, and redirects verified.'); +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'); +assert.equal(app.url, 'https://diffy.run/'); +assert.deepEqual(app.operatingSystem, ['macOS', 'Windows', 'Linux']); +assert(app.sameAs.includes('https://github.com/hitSlop/Diffy')); +assert(!app.offers && !app.aggregateRating); +assert(home.includes('Diffy is a native Git diff viewer built in Rust with GPUI Kit.')); +const robots = read('robots.txt'); +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('thanks') && !sitemap.includes('404')); +for (const file of ['llms.txt', 'llms-full.txt']) { + const markdown = read(file); + assert(markdown.startsWith('# Diffy\n')); + assert(markdown.includes('https://diffy.run/downloads')); + assert(!markdown.includes('diffy.hitslop.com')); +} +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(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 new file mode 100644 index 0000000..670952f --- /dev/null +++ b/site/src/data/product.ts @@ -0,0 +1,14 @@ +import { platforms, price } from './releases'; + +export const source = 'https://github.com/hitSlop/Diffy'; +export const summary = 'Diffy is a native Git diff viewer built in Rust with GPUI Kit. Review live changes, keep worktrees together, and read GitHub pull requests without checking out a branch.'; +export const questions = [ + { question: 'Which platforms does Diffy support?', answer: 'Diffy supports macOS, Windows, and Linux. macOS is in beta; Windows and Linux are early alphas with limited hardware testing. Every platform requires Git.' }, + { question: 'Does Diffy work with coding agents?', answer: 'Diffy discovers local Git projects from Herdr, Codex, and Claude Code. Choose which projects to import, then review their live file changes. Discovery reads local project metadata; it does not run agents or read their conversations.' }, + { question: 'Can Diffy review GitHub pull requests?', answer: 'Yes. Diffy shows read-only diffs for open and draft GitHub.com pull requests using your existing GitHub CLI login. It does not check out a branch. Review comments, approvals, merging, and GitHub Enterprise are not supported.' }, + { question: 'Is Diffy open source, and what does it cost?', answer: `Diffy is open source under the MIT license and can be built for free. The suggested one-time purchase is ${price.beta} during beta (regular price ${price.regular}) and supports all platforms. Public downloads are not payment gated.${import.meta.env.DODO_CHECKOUT_URL?.trim() ? '' : ' Checkout is not yet available.'}` }, +]; + +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`; +} diff --git a/site/src/layouts/Page.astro b/site/src/layouts/Page.astro index b1fdf40..017f638 100644 --- a/site/src/layouts/Page.astro +++ b/site/src/layouts/Page.astro @@ -1,8 +1,20 @@ --- import '../styles/site.css'; import socialImage from '../assets/hero.png'; -const { title = 'Diffy — a native Git diff viewer, in beta', description = 'Review live Git diffs in a native app built in Rust. macOS beta; Windows and Linux alpha. $14.99 during beta, regular price $24.99. One purchase supports all platforms.' } = Astro.props; -const canonical = new URL(Astro.url.pathname, Astro.site); +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 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'] }] : []), + ], +}; const socialImageUrl = new URL(socialImage.src, Astro.site); const socialImageAlt = 'A tearful developer at a keyboard says, “thank you for changing my life.” Diffy’s colorful chameleon replies, “i literally only show you diffs.”'; --- @@ -14,7 +26,11 @@ const socialImageAlt = 'A tearful developer at a keyboard says, “thank you for {title} + + + + {!noindex &&