// @ts-check import { fileURLToPath } from "node:url"; import { defineConfig } from "astro/config"; import starlight from "@astrojs/starlight"; import starlightLlmActions from "starlight-llm-actions"; import starlightLinksValidator from "starlight-links-validator"; import sitemap from "@astrojs/sitemap"; import svelte from "@astrojs/svelte"; import tailwindcss from "@tailwindcss/vite"; import Icons from "unplugin-icons/vite"; import { fontsBase } from "../web/src/fontsBase.mjs"; // the ui components are shared with the web app, imported straight from its // source rather than published as a package const webLib = fileURLToPath(new URL("../web/src/lib", import.meta.url)); const base = "/docs"; const svelteStyleFallback = { name: "svelte-style-fallback", enforce: "pre", transform(code, id) { if (!id.includes("svelte&type=style&lang.css")) return; if (!code.includes("]*>([\s\S]*?)<\/style>/i); return { code: match ? match[1] : "", map: null }; }, }; export default defineConfig({ site: "https://tangled.org", // served at tangled.org/docs by the docs worker. the build lands in dist/docs // because static assets on workers are matched against the full request path base, outDir: "./dist/docs", integrations: [ starlight({ title: "Tangled Docs", titleDelimiter: "·", disable404Route: true, routeMiddleware: ["./src/starlightRouteData.ts"], tableOfContents: { minHeadingLevel: 2, maxHeadingLevel: 2 }, plugins: [ starlightLlmActions({ renderMarkdown: "simple", linkAlternate: true, llmsTxt: { exclude: ["index"] }, }), starlightLinksValidator(), ], description: "Documentation for Tangled — decentralized code collaboration built on the AT Protocol.", social: [ { icon: "x.com", label: "X", href: "https://x.com/tangled_org" }, { icon: "blueSky", label: "Bluesky", href: "https://bsky.app/profile/tangled.org", }, ], favicon: "/favicon.svg", head: [ { tag: "link", attrs: { rel: "preload", href: `${base}/fonts/TimelessSansVF.woff2`, as: "font", type: "font/woff2", crossorigin: "anonymous", }, }, { tag: "link", // head entries are emitted verbatim; starlight only rebases `favicon` attrs: { rel: "apple-touch-icon", href: "/docs/apple-touch-icon.png" }, }, ], editLink: { baseUrl: "https://tangled.org/@tangled.org/core/blob/master/docs/", }, customCss: [ "./src/styles/global.css", "./src/styles/tangled.css", "./src/styles/custom.css", "./src/styles/diagrams.css", ], components: { Header: "./src/components/Header.astro", Hero: "./src/components/Hero.astro", MarkdownContent: "./src/components/MarkdownContent.astro", MobileMenuFooter: "./src/components/MobileMenuFooter.astro", PageTitle: "./src/components/PageTitle.astro", Sidebar: "./src/components/Sidebar.astro", SiteTitle: "./src/components/SiteTitle.astro", ThemeProvider: "./src/components/ThemeProvider.astro", }, expressiveCode: { // same palettes as the web app's syntax tokens themes: ["catppuccin-macchiato", "catppuccin-latte"], // a plain bordered block like tangled.org's, no editor or terminal chrome defaultProps: { frame: "none" }, styleOverrides: { // expressive-code adds borderWidth to this for the outer // radius, so subtract it to land on --radius-md exactly borderRadius: "calc(var(--radius-md) - 1px)", borderColor: "var(--color-border-default)", borderWidth: "1px", codeBackground: "var(--color-background-canvas)", codeFontFamily: "var(--font-mono)", codeFontSize: "0.8125rem", codeLineHeight: "1.5385", codePaddingBlock: "0.75rem", codePaddingInline: "0.75rem", frames: { shadowColor: "transparent", frameBoxShadowCssValue: "none", }, }, }, sidebar: [ { label: "Getting started", items: [ "introduction", "quick-start", "comparison-with-github", "migrating-from-github", "why-atproto", ], }, { label: "Using Tangled", items: [ "guides/profile", "guides/repositories", "guides/pull-requests", "guides/access", "guides/vouching", "guides/notifications", "guides/organizations", "guides/signing-commits", "guides/importing", "sites", "troubleshooting", ], }, { label: "Repositories", items: ["knots/repositories", "knots/self-hosting"], }, { label: "Running CI", items: [ "spindles", "spindles/writing-workflows", "spindles/workflows", "spindles/secrets", "spindles/webhooks", "spindles/self-hosting", ], }, { label: "Self-hosting", items: [ "self-hosting", "self-hosting/knot", "self-hosting/spindle", "self-hosting/frontend", "self-hosting/indexer", ], }, { label: "Integrations", items: ["integrations/discord", "integrations/contributing"], }, { label: "Upgrading", items: ["upgrading/knots-and-spindles", "upgrading/knot-2"], }, { label: "API", items: ["api/knot", "api/spindle", "api/bobbin"], }, { label: "Reference", items: [ "reference/records", "reference/identifiers", "reference/knot-config", "reference/spindle-config", ], }, { label: "Contributing", items: ["contributing/hacking", "contributing/guide"], }, ], }), sitemap(), svelte(), ], vite: { plugins: [svelteStyleFallback, tailwindcss(), Icons({ compiler: "svelte" }), fontsBase(base)], resolve: { // same aliases the web app's components import through alias: { $lib: webLib, // the web app's own imports (sveltekit 3 subpath imports) "#lib": webLib, "$icon/": "~icons/lucide/", // routing and environment are sveltekit-only; components that build hrefs or // guard browser apis import them "$app/paths": fileURLToPath(new URL("./src/stubs/app-paths.ts", import.meta.url)), "$app/env": fileURLToPath(new URL("./src/stubs/app-env.ts", import.meta.url)), }, // web/ has no node_modules in the nix build: resolve the components' // runtime deps (and svelte itself, which must be a single copy) from here dedupe: ["svelte", "tailwind-variants", "tailwind-merge"], }, server: { fs: { allow: [ fileURLToPath(new URL(".", import.meta.url)), fileURLToPath(new URL("../web/src", import.meta.url)), ], }, }, }, });