diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..17a2477 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +# SpreadPaper website + +## Internal links end in a slash + +Pages build to directories (`/help/index.html`), and Cloudflare's `auto-trailing-slash` answers `/help` with a 307 to `/help/`. Every internal link, footer entry, nav item and 404 destination is written as `/help/`, never `/help`. A bare path works in the browser but sends every crawler through a redirect, which Ahrefs reports as "Page has links to redirect". Files keep their extension (`/favicon-32.png`). `npm test` checks every built page and fails on a link that redirects. diff --git a/src/components/sections/Download.astro b/src/components/sections/Download.astro index 96723af..7b55cee 100644 --- a/src/components/sections/Download.astro +++ b/src/components/sections/Download.astro @@ -78,7 +78,7 @@ import Icon from '../Icon.astro'
That is the wording macOS uses for any app not signed with a paid Apple Developer ID, and SpreadPaper is not signed with one. Nothing has been found in SpreadPaper, and nothing has been scanned: the check is for a signature, not for malware. You do not have to take that on trust, because the source is public and every release is built from it by GitHub Actions.
-There are two ways past it, one through System Settings and one in Terminal. Both are written out step by step in how to open SpreadPaper when macOS blocks it. It takes a minute, and only the first time.
+There are two ways past it, one through System Settings and one in Terminal. Both are written out step by step in how to open SpreadPaper when macOS blocks it. It takes a minute, and only the first time.
It is not on the Mac App Store and has no paid signing certificate, so macOS warns you the - first time and you allow it once by hand. The help page covers that in + first time and you allow it once by hand. The help page covers that in three steps. It also brings no pictures of its own: you supply the photograph, where Hologram Desktop and Wallpaperer hand you a library. And it wants macOS 15 Sequoia, which is a higher bar than anything else on this page. diff --git a/src/pages/guides/index.astro b/src/pages/guides/index.astro index d13df0c..232d027 100644 --- a/src/pages/guides/index.astro +++ b/src/pages/guides/index.astro @@ -75,7 +75,7 @@ const questions: Question[] = [
Ask for your own setup on GitHub and it becomes the next guide. For installing and first launch, the and it becomes the next guide. For installing and first launch, the help page has it.
diff --git a/src/pages/guides/span-wallpaper-across-two-monitors-mac.astro b/src/pages/guides/span-wallpaper-across-two-monitors-mac.astro index d8291a2..bb854c2 100644 --- a/src/pages/guides/span-wallpaper-across-two-monitors-mac.astro +++ b/src/pages/guides/span-wallpaper-across-two-monitors-mac.astro @@ -70,7 +70,7 @@ import TextPage from '../../layouts/TextPage.astro'Bezel compensation is the detail behind the step that - does most of the work. Help walks the first launch warning through in three steps. The guides have the rest. + does most of the work. Help walks the first launch warning through in three steps. The guides have the rest.
diff --git a/src/pages/help.astro b/src/pages/help.astro index 4d54977..0e33afe 100644 --- a/src/pages/help.astro +++ b/src/pages/help.astro @@ -46,7 +46,7 @@ const steps: Step[] = [ const breadcrumb = [ { name: 'Home', path: '/' }, - { name: 'Help', path: '/help' }, + { name: 'Help', path: '/help/' }, ] --- diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro index c25e84e..d8add1e 100644 --- a/src/pages/privacy.astro +++ b/src/pages/privacy.astro @@ -56,7 +56,7 @@ const updatedLabel = '15 September 2026'Run from Curaçao, Dutch Caribbean, by Robin van Baalen. If any of this changes, so does the - date at the top. See also the Terms of Service. + date at the top. See also the Terms of Service.
diff --git a/src/pages/terms.astro b/src/pages/terms.astro index 5aec445..9d892cd 100644 --- a/src/pages/terms.astro +++ b/src/pages/terms.astro @@ -70,7 +70,7 @@ const updatedLabel = '15 September 2026'- See also the Privacy Policy. + See also the Privacy Policy.
diff --git a/test/page.test.ts b/test/page.test.ts index 34acb99..727e972 100644 --- a/test/page.test.ts +++ b/test/page.test.ts @@ -446,7 +446,7 @@ console.log('\nthe footer columns') // Once the top nav has taken its four, these pages are reachable from nowhere // else, so a column that drifts back to all GitHub orphans them. const footerHrefs = [...doc.querySelectorAll('#site-footer a')].map((a) => a.getAttribute('href')!) -for (const href of ['/help', '/guides', '/about', '/alternatives', '/privacy', '/terms']) { +for (const href of ['/help/', '/guides/', '/about/', '/alternatives/', '/privacy/', '/terms/']) { const hits = footerHrefs.filter((candidate) => candidate === href).length check(`${href} is linked from the footer exactly once`, hits === 1, `${hits} links`) } @@ -1303,5 +1303,18 @@ check('and names both architectures the binary carries', check('while the system requirement stays at Sequoia', /15\.0 Sequoia/.test(app?.softwareRequirements ?? ''), app?.softwareRequirements ?? 'nothing') +console.log('\ninternal links') +// Pages build to directories and Cloudflare 307s `/help` to `/help/`, so a +// link without the slash sends every crawler through a redirect. Files keep +// their extension and are left alone. +const redirecting = builtPages.flatMap((file) => + [...readFileSync(file, 'utf8').matchAll(/href="(\/[^"#?]*)/g)] + .map((m) => m[1]) + .filter((href) => !href.endsWith('/') && !/\.[a-z0-9]+$/i.test(href)) + .map((href) => `${file.slice(site.length)} → ${href}`) +) +check('every internal link ends where the page lives', redirecting.length === 0, + [...new Set(redirecting)].join(', ')) + console.log(failures ? `\n${failures} FAILED` : '\nall checks passed') process.exit(failures ? 1 : 0)