diff --git a/AGENTS.md b/AGENTS.md index 95a0e08..69214d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -34,6 +34,41 @@ - Any new sub-project directory **must** be added to the root `tsconfig.json` `exclude` list AND to the `exclude` regex in `webpack.config.js` before committing - After adding a sub-project, always run `npm run build` and `npm test` from the **root** to verify isolation +## Website / Astro link strategy + +**The trailing-slash rule:** GitHub Pages serves every page at a URL ending in `/` +(e.g. `/AstroChart/quickstart/`). The browser resolves `./` relative to that directory, +so `./guides/foo` from a root page resolves to `/AstroChart/quickstart/guides/foo` — **broken**. + +Use this rule for all links inside `src/content/docs/`: + +| From page depth | Link target | Correct prefix | Example | +|---|---|---|---| +| Root page (`quickstart.md`) | Any other page (sibling OR subdir) | `../` | `../installation`, `../guides/radix-chart` | +| Subdir page (`guides/radix-chart.mdx`) | Sibling in same subdir | `./` | `./transit-chart` | +| Subdir page (`guides/radix-chart.mdx`) | Root page or other subdir | `../` | `../api/settings` | +| Nested subdir (`guides/frameworks/react.md`) | Sibling in same nested subdir | `./` | `./vue` | +| Nested subdir (`guides/frameworks/react.md`) | Parent subdir | `../` | `../radix-chart` | +| Nested subdir (`guides/frameworks/react.md`) | Root or other top-level subdir | `../../` | `../../api/chart` | + +> **Why root pages always use `../`:** GitHub Pages (and `trailingSlash: 'always'`) serves +> every page at a URL ending in `/` (e.g. `/AstroChart/installation/`). The browser treats +> that as a directory, so `./quickstart` resolves to `/AstroChart/installation/quickstart` — +> **wrong even for siblings**. Use `../` to escape to `/AstroChart/` first. + +- **In `.astro` templates:** use `import.meta.env.BASE_URL + '/path'` (already correct in `index.astro`). +- **In Starlight config (`astro.config.mjs`):** use `slug:` values — never `link:` with absolute paths. +- **Never** use root-absolute paths like `/guides/foo` inside `.md`/`.mdx` — they ignore the `base` setting. +- **Future domain migration** (`astrochart.dev`): change only `site` and `base` in `astro.config.mjs` — no content files change. + +> **⚠️ Do not set `trailingSlash: 'always'`** in `astro.config.mjs`. +> Astro's markdown pipeline emits relative link hrefs verbatim (`../guides/foo`, no trailing +> slash). Setting `'always'` makes the dev server 404 every one of the ~50 relative links in +> the content tree. GitHub Pages issues a silent 301 for slash-less URLs in production, so +> links work correctly without the strict setting. The default (`'ignore'`) is correct here. + +**⚠️ Link audit rule:** Any task that adds/edits content files OR changes `base` config **must** end with a full grep audit of all `./` links across the entire `src/content/docs/` tree to confirm no root-level page has a `./` prefix remaining. + ## Website / Astro content rules - **MDX required for component imports:** Starlight content files that use `import` and JSX component tags **must** have a `.mdx` extension. A `.md` file will print the import statement as plain text and silently ignore all component tags. - **Multi-instance inline script loading:** When an Astro `is:inline` script dynamically loads an external JS bundle, multiple component instances on the same page will all run simultaneously. Use a shared queue pattern to avoid race conditions: diff --git a/website/astro.config.mjs b/website/astro.config.mjs index c514a99..649f740 100644 --- a/website/astro.config.mjs +++ b/website/astro.config.mjs @@ -1,15 +1,18 @@ import { defineConfig } from 'astro/config' import starlight from '@astrojs/starlight' import sitemap from '@astrojs/sitemap' +import starlightLinksValidator from 'starlight-links-validator' + +const isDev = process.env.NODE_ENV === 'development' export default defineConfig({ site: 'https://astrodraw.github.io/AstroChart', base: '/AstroChart', integrations: [ starlight({ + plugins: [...(isDev ? [] : [starlightLinksValidator({ errorOnRelativeLinks: false })])], title: 'AstroChart', description: 'Pure SVG astrology charts for the web', - favicon: '/favicon.svg', logo: { src: './public/img/logo.svg', alt: 'AstroChart Logo' diff --git a/website/package-lock.json b/website/package-lock.json index add876b..2fb0699 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -14,6 +14,7 @@ "sharp": "^0.33.0" }, "devDependencies": { + "starlight-links-validator": "^0.21.0", "typescript": "^5.3.3" }, "engines": { @@ -1944,6 +1945,13 @@ "undici-types": "~7.16.0" } }, + "node_modules/@types/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/sax": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz", @@ -1986,6 +1994,22 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", @@ -3090,6 +3114,19 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/es-module-lexer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", @@ -3380,6 +3417,19 @@ "uncrypto": "^0.1.3" } }, + "node_modules/has-flag": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-5.0.1.tgz", + "integrity": "sha512-CsNUt5x9LUdx6hnk/E2SZLsDyvfqANZSUq4+D3D8RzDJ2M+HDTIkF60ibS1vHaK55vzgiZw1bEPFG9yH7l33wA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/hast-util-embedded": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/hast-util-embedded/-/hast-util-embedded-3.0.0.tgz", @@ -3815,6 +3865,19 @@ "url": "https://github.com/sponsors/brc-dd" } }, + "node_modules/is-absolute-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-5.0.0.tgz", + "integrity": "sha512-sdJyNpBnQHuVnBunfzjAecOhZr2+A30ywfFvu3EnxtKLUWfwGgyWUmqHbGZiU6vTfHpCPm5GvLe4BAvlU9n8VQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", @@ -6012,6 +6075,32 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/starlight-links-validator": { + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/starlight-links-validator/-/starlight-links-validator-0.21.0.tgz", + "integrity": "sha512-X7pQC/5Dc7peDoZW/F4L+fD5qs9JmVXo3klNHsxRcVs3mSJXp2wFKZ/38hgr7yGLkp126IFKeHLy6jmOvnwvCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/picomatch": "^4.0.2", + "github-slugger": "^2.0.0", + "hast-util-from-html": "^2.0.3", + "is-absolute-url": "^5.0.0", + "mdast-util-mdx-jsx": "^3.2.0", + "mdast-util-to-hast": "^13.2.1", + "picomatch": "^4.0.3", + "terminal-link": "^5.0.0", + "unist-util-visit": "^5.1.0", + "yaml": "^2.8.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "@astrojs/starlight": ">=0.38.0", + "astro": ">=6.0.0" + } + }, "node_modules/stream-replace-string": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/stream-replace-string/-/stream-replace-string-2.0.0.tgz", @@ -6050,6 +6139,36 @@ "inline-style-parser": "0.2.7" } }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-hyperlinks": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-4.4.0.tgz", + "integrity": "sha512-UKbpT93hN5Nr9go5UY7bopIB9YQlMz9nm/ct4IXt/irb5YRkn9WaqrOBJGZ5Pwvsd5FQzSVeYlGdXoCAPQZrPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^5.0.1", + "supports-color": "^10.2.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, "node_modules/svgo": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/svgo/-/svgo-4.0.1.tgz", @@ -6075,6 +6194,23 @@ "url": "https://opencollective.com/svgo" } }, + "node_modules/terminal-link": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-5.0.0.tgz", + "integrity": "sha512-qFAy10MTMwjzjU8U16YS4YoZD+NQLHzLssFMNqgravjbvIPNiqkGFR4yjhJfmY9R5OFU7+yHxc6y+uGHkKwLRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^4.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/tiny-inflate": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", @@ -6628,6 +6764,22 @@ "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", "license": "MIT" }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs-parser": { "version": "22.0.0", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", diff --git a/website/package.json b/website/package.json index 7204e42..a1d2240 100644 --- a/website/package.json +++ b/website/package.json @@ -10,12 +10,13 @@ "astro": "astro" }, "dependencies": { - "astro": "^6.0.0", - "@astrojs/starlight": "^0.38.0", "@astrojs/sitemap": "^3.1.0", + "@astrojs/starlight": "^0.38.0", + "astro": "^6.0.0", "sharp": "^0.33.0" }, "devDependencies": { + "starlight-links-validator": "^0.21.0", "typescript": "^5.3.3" }, "engines": { diff --git a/website/src/content/docs/changelog.md b/website/src/content/docs/changelog.md index 6085af9..0768625 100644 --- a/website/src/content/docs/changelog.md +++ b/website/src/content/docs/changelog.md @@ -53,7 +53,7 @@ This project adheres to [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Changed - **Breaking:** Package renamed to `@astrodraw/astrochart` -- **Breaking:** `AstroData` shape changed to `{ planets: Record, cusps: number[] }` — see the [Getting Started guide](./guides/getting-started) +- **Breaking:** `AstroData` shape changed to `{ planets: Record, cusps: number[] }` — see the [Introduction](../introduction) - Cusps array must contain exactly 12 values; validation throws a descriptive error on mismatch - `SHIFT_IN_DEGREES` default changed to `180` (0° on the West / Ascendant side) diff --git a/website/src/content/docs/guides/frameworks/angular.md b/website/src/content/docs/guides/frameworks/angular.md index 91dc13b..8fb12d3 100644 --- a/website/src/content/docs/guides/frameworks/angular.md +++ b/website/src/content/docs/guides/frameworks/angular.md @@ -160,4 +160,4 @@ export class AstroChartComponent implements AfterViewInit, OnDestroy { - **[React Integration](./react)** — Use with React - **[Vue Integration](./vue)** — Use with Vue -- **[Multiple Charts](../multiple-charts)** — Render several instances on one page +- **[Multiple Charts](../../multiple-charts)** — Render several instances on one page diff --git a/website/src/content/docs/guides/frameworks/vue.md b/website/src/content/docs/guides/frameworks/vue.md index f64dd3e..b392fb5 100644 --- a/website/src/content/docs/guides/frameworks/vue.md +++ b/website/src/content/docs/guides/frameworks/vue.md @@ -203,4 +203,4 @@ const chartData = { planets: { Sun: [120.5] }, cusps: [0,30,60,90,120,150,180,21 - **[React Integration](./react)** — Use with React - **[Angular Integration](./angular)** — Use with Angular -- **[Multiple Charts](../multiple-charts)** — Render several instances on one page +- **[Multiple Charts](../../multiple-charts)** — Render several instances on one page diff --git a/website/src/content/docs/installation.md b/website/src/content/docs/installation.md index 6c360c6..6a3f379 100644 --- a/website/src/content/docs/installation.md +++ b/website/src/content/docs/installation.md @@ -81,9 +81,9 @@ No additional `@types/` package needed. | Browsers (modern) | ✅ Supported | | IE11 | ⚠️ Requires polyfills | | Mobile browsers | ✅ Supported | -| React | ✅ See [React guide](./guides/frameworks/react) | -| Vue | ✅ See [Vue guide](./guides/frameworks/vue) | -| Angular | ✅ See [Angular guide](./guides/frameworks/angular) | +| React | ✅ See [React guide](../guides/frameworks/react) | +| Vue | ✅ See [Vue guide](../guides/frameworks/vue) | +| Angular | ✅ See [Angular guide](../guides/frameworks/angular) | ## Verification @@ -98,6 +98,6 @@ console.log('AstroChart version:', version) ## Next Steps -- **[Quick Start](./quickstart)** — Render your first chart -- **[API Reference](./api/chart)** — Learn all available methods -- **[Guides](./guides/radix-chart)** — Explore common use cases +- **[Quick Start](../quickstart)** — Render your first chart +- **[API Reference](../api/chart)** — Learn all available methods +- **[Guides](../guides/radix-chart)** — Explore common use cases diff --git a/website/src/content/docs/introduction.mdx b/website/src/content/docs/introduction.mdx index 7a698de..7cd1b17 100644 --- a/website/src/content/docs/introduction.mdx +++ b/website/src/content/docs/introduction.mdx @@ -88,10 +88,10 @@ Here's a basic radix chart rendered with AstroChart: ## Next Steps -- **[Installation Guide](./installation)** — Detailed setup instructions for npm, CDN, and more -- **[Quick Start Guide](./quickstart)** — A step-by-step walkthrough with examples -- **[Radix Chart Guide](./guides/radix-chart)** — Learn how to render complete natal charts -- **[API Reference](./api/chart)** — Complete documentation of all classes and methods +- **[Installation Guide](../installation)** — Detailed setup instructions for npm, CDN, and more +- **[Quick Start Guide](../quickstart)** — A step-by-step walkthrough with examples +- **[Radix Chart Guide](../guides/radix-chart)** — Learn how to render complete natal charts +- **[API Reference](../api/chart)** — Complete documentation of all classes and methods ## Browser Support @@ -105,8 +105,8 @@ AstroChart is released under the **MIT License**. See the [GitHub repository](ht ## Contributing -Found a bug? Have a feature request? We'd love your help! See the [Contributing Guide](./contributing) for instructions. +Found a bug? Have a feature request? We'd love your help! See the [Contributing Guide](../contributing) for instructions. --- -Ready to dive in? [Get started now](./quickstart). +Ready to dive in? [Get started now](../quickstart). diff --git a/website/src/content/docs/quickstart.mdx b/website/src/content/docs/quickstart.mdx index 9a968d1..050f674 100644 --- a/website/src/content/docs/quickstart.mdx +++ b/website/src/content/docs/quickstart.mdx @@ -127,11 +127,11 @@ Here's a live example you can interact with: ## Next Steps -- **[Radix Chart Guide](./guides/radix-chart)** — Learn more about radix charts and all available planets -- **[Transit Charts](./guides/transit-chart)** — Add a transit ring to overlay current positions -- **[Animation](./guides/animation)** — Animate chart transitions -- **[Custom Settings](./guides/custom-settings)** — Customize colors, fonts, and more -- **[API Reference](./api/chart)** — See all available methods and options +- **[Radix Chart Guide](../guides/radix-chart)** — Learn more about radix charts and all available planets +- **[Transit Charts](../guides/transit-chart)** — Add a transit ring to overlay current positions +- **[Animation](../guides/animation)** — Animate chart transitions +- **[Custom Settings](../guides/custom-settings)** — Customize colors, fonts, and more +- **[API Reference](../api/chart)** — See all available methods and options ## Troubleshooting @@ -146,5 +146,5 @@ Here's a live example you can interact with: **Need help?** - [Open an issue on GitHub](https://github.com/AstroDraw/AstroChart/issues) -- Check the [API Reference](./api/chart) -- See [Common Guides](./guides/radix-chart) +- Check the [API Reference](../api/chart) +- See [Common Guides](../guides/radix-chart) diff --git a/website/src/utils/url.ts b/website/src/utils/url.ts index 568f337..cf782f3 100644 --- a/website/src/utils/url.ts +++ b/website/src/utils/url.ts @@ -1,11 +1,15 @@ /** - * Returns `import.meta.env.BASE_URL` without a trailing slash, so it can - * be safely concatenated with a leading-slash path: + * Prepends `import.meta.env.BASE_URL` to a path and ensures a trailing + * slash on the result, consistent with `trailingSlash: 'always'` in + * astro.config.mjs (which mirrors GitHub Pages behaviour in dev): * - * withBase('/installation') → '/AstroChart/installation' + * withBase('/installation') → '/AstroChart/installation/' + * withBase('/changelog') → '/AstroChart/changelog/' * * The base itself comes from `base` in astro.config.mjs — the only place * that ever needs to change when the deployment URL changes. */ -export const withBase = (path: string): string => - `${import.meta.env.BASE_URL.replace(/\/$/, '')}${path}` +export const withBase = (path: string): string => { + const url = `${import.meta.env.BASE_URL.replace(/\/$/, '')}${path}` + return url.endsWith('/') ? url : `${url}/` +}