Docs in the shape of docs.continue.dev
Tabs under the header (Documentation, API reference), each with its own sidebar of groups that do not fold. Page titles carry their description as a subtitle and a Copy page menu: copy as Markdown, view as Markdown, or open in Claude or ChatGPT. Every page has a Markdown twin at its path plus .md. Sections open with space instead of rules, code blocks lose their window chrome, inline code is quieter, and the home page is a doc page, "What is g1t?", with cards to start from.
9 files+426−1810/9 viewed
| 9 | 9 | title: 'g1t docs', | |
| 10 | 10 | description: 'Guides and reference for g1t, the git forge for teams of agents.', | |
| 11 | 11 | components: { | |
| 12 | + | Header: './src/components/Header.astro', | |
| 13 | + | PageTitle: './src/components/PageTitle.astro', | |
| 12 | 14 | SiteTitle: './src/components/SiteTitle.astro', | |
| 13 | 15 | SocialIcons: './src/components/SocialIcons.astro', | |
| 14 | 16 | }, | |
| 17 | + | // Each tab under the header shows its own part of the sidebar. | |
| 18 | + | routeMiddleware: './src/route-data.ts', | |
| 15 | 19 | expressiveCode: { | |
| 16 | 20 | themes: ['github-dark-default'], | |
| 21 | + | // Plain panels with a copy button, without window chrome. | |
| 22 | + | defaultProps: { frame: 'none' }, | |
| 17 | 23 | styleOverrides: { | |
| 18 | 24 | borderRadius: '0.75rem', | |
| 19 | 25 | borderColor: 'var(--g1t-line)', | |
| 102 | 108 | label: 'Reference', | |
| 103 | 109 | items: [ | |
| 104 | 110 | { label: 'API overview', slug: 'reference/api' }, | |
| 105 | − | { label: 'API reference', link: '/api/reference/', attrs: { target: '_self' } }, | |
| 111 | + | { label: 'API explorer', link: '/api/reference/', attrs: { target: '_self' } }, | |
| 106 | 112 | { label: 'MCP tools', slug: 'reference/mcp' }, | |
| 107 | 113 | { label: 'OpenAPI document', link: 'https://api.g1t.sh/openapi.json' }, | |
| 108 | 114 | { label: 'llms.txt', link: 'https://g1t.sh/llms.txt' }, |
| 1 | + | --- | |
| 2 | + | // Starlight's header, with the tabs that divide the docs beneath it. | |
| 3 | + | import Default from '@astrojs/starlight/components/Header.astro'; | |
| 4 | + | ||
| 5 | + | import { TABS, tabFor } from '../tabs'; | |
| 6 | + | ||
| 7 | + | const current = tabFor(Astro.locals.starlightRoute.id); | |
| 8 | + | --- | |
| 9 | + | ||
| 10 | + | <div class="g1t-header"> | |
| 11 | + | <div class="g1t-header-top"><Default /></div> | |
| 12 | + | <nav class="g1t-tabs" aria-label="Sections"> | |
| 13 | + | { | |
| 14 | + | TABS.map((tab) => ( | |
| 15 | + | <a href={tab.href} aria-current={tab === current ? 'page' : undefined}> | |
| 16 | + | {tab.label} | |
| 17 | + | </a> | |
| 18 | + | )) | |
| 19 | + | } | |
| 20 | + | </nav> | |
| 21 | + | </div> | |
| 22 | + | ||
| 23 | + | <style> | |
| 24 | + | .g1t-header { | |
| 25 | + | display: flex; | |
| 26 | + | flex-direction: column; | |
| 27 | + | height: 100%; | |
| 28 | + | } | |
| 29 | + | .g1t-header-top { | |
| 30 | + | height: 3.5rem; | |
| 31 | + | padding: 0 var(--sl-nav-pad-x); | |
| 32 | + | } | |
| 33 | + | /* On phones the menu button sits at the top row's end: leave it room. */ | |
| 34 | + | @media (max-width: 49.99rem) { | |
| 35 | + | :global([data-has-sidebar]) .g1t-header-top { | |
| 36 | + | padding-inline-end: calc(var(--sl-nav-pad-x) + var(--sl-menu-button-size) + var(--sl-nav-gap)); | |
| 37 | + | } | |
| 38 | + | } | |
| 39 | + | .g1t-tabs { | |
| 40 | + | display: flex; | |
| 41 | + | gap: 1.5rem; | |
| 42 | + | height: 2.75rem; | |
| 43 | + | padding: 0 var(--sl-nav-pad-x); | |
| 44 | + | overflow-x: auto; | |
| 45 | + | scrollbar-width: none; | |
| 46 | + | } | |
| 47 | + | .g1t-tabs a { | |
| 48 | + | display: flex; | |
| 49 | + | align-items: center; | |
| 50 | + | border-bottom: 1.5px solid transparent; | |
| 51 | + | font-size: 0.875rem; | |
| 52 | + | font-weight: 500; | |
| 53 | + | white-space: nowrap; | |
| 54 | + | color: var(--g1t-muted); | |
| 55 | + | text-decoration: none; | |
| 56 | + | transition: color 0.15s; | |
| 57 | + | } | |
| 58 | + | .g1t-tabs a:hover { | |
| 59 | + | color: var(--g1t-fg); | |
| 60 | + | } | |
| 61 | + | .g1t-tabs a[aria-current='page'] { | |
| 62 | + | border-color: var(--g1t-fg); | |
| 63 | + | color: var(--g1t-fg); | |
| 64 | + | } | |
| 65 | + | </style> |
| 1 | + | --- | |
| 2 | + | // A page's title, what it is about beneath it, and a way to take the page | |
| 3 | + | // to an AI assistant: copied as Markdown, read as Markdown, or opened in | |
| 4 | + | // Claude or ChatGPT. | |
| 5 | + | const { entry, id } = Astro.locals.starlightRoute; | |
| 6 | + | const { title, description } = entry.data; | |
| 7 | + | const markdown = `/${id === '' || id === 'index' ? 'index' : id}.md`; | |
| 8 | + | const absolute = new URL(markdown, Astro.site).href; | |
| 9 | + | const ask = encodeURIComponent(`Read ${absolute} so I can ask questions about it.`); | |
| 10 | + | --- | |
| 11 | + | ||
| 12 | + | <div class="g1t-page-title"> | |
| 13 | + | <div class="g1t-page-title-row"> | |
| 14 | + | <h1 id="_top">{title}</h1> | |
| 15 | + | <div class="g1t-copy"> | |
| 16 | + | <button type="button" class="g1t-copy-main" data-markdown={markdown}> | |
| 17 | + | <svg viewBox="0 0 16 16" aria-hidden="true"> | |
| 18 | + | <rect x="5" y="5" width="9" height="9" rx="1.5"></rect> | |
| 19 | + | <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5"></path> | |
| 20 | + | </svg> | |
| 21 | + | <span>Copy page</span> | |
| 22 | + | </button> | |
| 23 | + | <details class="g1t-copy-more"> | |
| 24 | + | <summary aria-label="More ways to use this page"> | |
| 25 | + | <svg viewBox="0 0 16 16" aria-hidden="true"><path d="m4 6 4 4 4-4"></path></svg> | |
| 26 | + | </summary> | |
| 27 | + | <div class="g1t-copy-menu"> | |
| 28 | + | <a href={markdown} target="_blank" rel="noopener"> | |
| 29 | + | <strong>View as Markdown</strong> | |
| 30 | + | <span>This page as plain text</span> | |
| 31 | + | </a> | |
| 32 | + | <a href={`https://claude.ai/new?q=${ask}`} target="_blank" rel="noopener"> | |
| 33 | + | <strong>Open in Claude</strong> | |
| 34 | + | <span>Ask questions about this page</span> | |
| 35 | + | </a> | |
| 36 | + | <a href={`https://chatgpt.com/?hints=search&q=${ask}`} target="_blank" rel="noopener"> | |
| 37 | + | <strong>Open in ChatGPT</strong> | |
| 38 | + | <span>Ask questions about this page</span> | |
| 39 | + | </a> | |
| 40 | + | </div> | |
| 41 | + | </details> | |
| 42 | + | </div> | |
| 43 | + | </div> | |
| 44 | + | {description && <p class="g1t-description">{description}</p>} | |
| 45 | + | </div> | |
| 46 | + | ||
| 47 | + | <script> | |
| 48 | + | document.querySelectorAll<HTMLButtonElement>('.g1t-copy-main').forEach((button) => { | |
| 49 | + | button.addEventListener('click', async () => { | |
| 50 | + | const label = button.querySelector('span')!; | |
| 51 | + | try { | |
| 52 | + | const text = await (await fetch(button.dataset.markdown!)).text(); | |
| 53 | + | await navigator.clipboard.writeText(text); | |
| 54 | + | label.textContent = 'Copied'; | |
| 55 | + | } catch { | |
| 56 | + | label.textContent = 'Could not copy'; | |
| 57 | + | } | |
| 58 | + | setTimeout(() => (label.textContent = 'Copy page'), 1600); | |
| 59 | + | }); | |
| 60 | + | }); | |
| 61 | + | // A menu closes when you click elsewhere. | |
| 62 | + | document.addEventListener('click', (event) => { | |
| 63 | + | document.querySelectorAll<HTMLDetailsElement>('.g1t-copy-more[open]').forEach((menu) => { | |
| 64 | + | if (!menu.contains(event.target as Node)) menu.open = false; | |
| 65 | + | }); | |
| 66 | + | }); | |
| 67 | + | </script> | |
| 68 | + | ||
| 69 | + | <style> | |
| 70 | + | .g1t-page-title { | |
| 71 | + | margin-top: 0.75rem; | |
| 72 | + | } | |
| 73 | + | .g1t-page-title-row { | |
| 74 | + | display: flex; | |
| 75 | + | align-items: flex-start; | |
| 76 | + | justify-content: space-between; | |
| 77 | + | gap: 1.5rem; | |
| 78 | + | } | |
| 79 | + | h1 { | |
| 80 | + | margin: 0; | |
| 81 | + | font-size: 2rem; | |
| 82 | + | font-weight: 600; | |
| 83 | + | letter-spacing: -0.025em; | |
| 84 | + | line-height: 1.2; | |
| 85 | + | color: var(--g1t-fg); | |
| 86 | + | } | |
| 87 | + | .g1t-description { | |
| 88 | + | margin-top: 0.6rem; | |
| 89 | + | max-width: 42rem; | |
| 90 | + | font-size: 1.125rem; | |
| 91 | + | line-height: 1.55; | |
| 92 | + | color: var(--g1t-muted); | |
| 93 | + | } | |
| 94 | + | .g1t-copy { | |
| 95 | + | position: relative; | |
| 96 | + | display: flex; | |
| 97 | + | flex-shrink: 0; | |
| 98 | + | margin-top: 0.35rem; | |
| 99 | + | border-radius: 0.5rem; | |
| 100 | + | box-shadow: inset 0 0 0 1px var(--g1t-line); | |
| 101 | + | } | |
| 102 | + | .g1t-copy svg { | |
| 103 | + | width: 0.9rem; | |
| 104 | + | height: 0.9rem; | |
| 105 | + | fill: none; | |
| 106 | + | stroke: currentColor; | |
| 107 | + | stroke-width: 1.3; | |
| 108 | + | stroke-linecap: round; | |
| 109 | + | stroke-linejoin: round; | |
| 110 | + | } | |
| 111 | + | .g1t-copy-main, | |
| 112 | + | .g1t-copy-more summary { | |
| 113 | + | display: flex; | |
| 114 | + | align-items: center; | |
| 115 | + | gap: 0.4rem; | |
| 116 | + | border: 0; | |
| 117 | + | background: transparent; | |
| 118 | + | padding: 0.3rem 0.6rem; | |
| 119 | + | font: inherit; | |
| 120 | + | font-size: 0.8125rem; | |
| 121 | + | color: var(--g1t-muted); | |
| 122 | + | cursor: pointer; | |
| 123 | + | transition: color 0.15s, background 0.15s; | |
| 124 | + | } | |
| 125 | + | .g1t-copy-main { | |
| 126 | + | border-radius: 0.5rem 0 0 0.5rem; | |
| 127 | + | } | |
| 128 | + | .g1t-copy-more summary { | |
| 129 | + | list-style: none; | |
| 130 | + | border-left: 1px solid var(--g1t-line); | |
| 131 | + | border-radius: 0 0.5rem 0.5rem 0; | |
| 132 | + | padding-inline: 0.45rem; | |
| 133 | + | } | |
| 134 | + | .g1t-copy-more summary::-webkit-details-marker { | |
| 135 | + | display: none; | |
| 136 | + | } | |
| 137 | + | .g1t-copy-main:hover, | |
| 138 | + | .g1t-copy-more summary:hover { | |
| 139 | + | background: var(--g1t-surface); | |
| 140 | + | color: var(--g1t-fg); | |
| 141 | + | } | |
| 142 | + | .g1t-copy-menu { | |
| 143 | + | position: absolute; | |
| 144 | + | top: calc(100% + 0.4rem); | |
| 145 | + | right: 0; | |
| 146 | + | z-index: 10; | |
| 147 | + | width: 15rem; | |
| 148 | + | border-radius: 0.75rem; | |
| 149 | + | padding: 0.35rem; | |
| 150 | + | background: var(--g1t-raised); | |
| 151 | + | box-shadow: | |
| 152 | + | inset 0 0 0 1px var(--g1t-line-strong), | |
| 153 | + | 0 12px 32px rgb(0 0 0 / 0.45); | |
| 154 | + | } | |
| 155 | + | .g1t-copy-menu a { | |
| 156 | + | display: block; | |
| 157 | + | border-radius: 0.5rem; | |
| 158 | + | padding: 0.45rem 0.6rem; | |
| 159 | + | text-decoration: none; | |
| 160 | + | } | |
| 161 | + | .g1t-copy-menu a:hover { | |
| 162 | + | background: var(--g1t-surface); | |
| 163 | + | } | |
| 164 | + | .g1t-copy-menu strong { | |
| 165 | + | display: block; | |
| 166 | + | font-size: 0.8125rem; | |
| 167 | + | font-weight: 500; | |
| 168 | + | color: var(--g1t-fg); | |
| 169 | + | } | |
| 170 | + | .g1t-copy-menu span { | |
| 171 | + | display: block; | |
| 172 | + | font-size: 0.75rem; | |
| 173 | + | color: var(--g1t-faint); | |
| 174 | + | } | |
| 175 | + | @media (max-width: 40rem) { | |
| 176 | + | .g1t-copy { | |
| 177 | + | display: none; | |
| 178 | + | } | |
| 179 | + | } | |
| 180 | + | </style> |
| 3 | 3 | --- | |
| 4 | 4 | ||
| 5 | 5 | <nav class="g1t-nav" aria-label="g1t"> | |
| 6 | − | <a href="/api/reference/">API reference</a> | |
| 6 | + | <a href="/api/reference/">API explorer</a> | |
| 7 | 7 | <a href="https://g1t.sh/">g1t.sh</a> | |
| 8 | 8 | <a href="https://g1t.sh/register" class="g1t-nav-cta">Sign up</a> | |
| 9 | 9 | <a href="https://github.com/syntaqx/g1t" aria-label="Source on GitHub" class="g1t-nav-icon"> |
| 1 | 1 | --- | |
| 2 | − | title: g1t docs | |
| 3 | − | description: Guides and reference for g1t, the git forge for teams of agents. | |
| 4 | − | template: splash | |
| 2 | + | title: What is g1t? | |
| 3 | + | description: A git forge for teams of agents. Hand it an outcome, and agents land it on main while your team works the way it does on GitHub. | |
| 5 | 4 | tableOfContents: false | |
| 5 | + | prev: false | |
| 6 | 6 | --- | |
| 7 | 7 | ||
| 8 | − | <div class="g1t-home-hero"> | |
| 9 | − | <p class="g1t-eyebrow">Documentation</p> | |
| 10 | − | <h1>Build with g1t</h1> | |
| 11 | − | <p> | |
| 12 | − | Hand g1t an outcome and a team of agents converges it onto main, while you and | |
| 13 | − | your team work exactly as you would on GitHub. Start with the path that fits. | |
| 14 | − | </p> | |
| 15 | − | </div> | |
| 8 | + | import { Card, CardGrid, LinkCard } from '@astrojs/starlight/components'; | |
| 9 | + | ||
| 10 | + | **g1t hosts your repositories and runs a team of agents on them.** Write | |
| 11 | + | what you want done; agents split it into issues, make a pull request for | |
| 12 | + | each, test it in a clean sandbox, review it, fix what the review finds and | |
| 13 | + | merge it under your repository's rules. People work alongside them with | |
| 14 | + | the same issues, pull requests and reviews they know from GitHub. | |
| 16 | 15 | ||
| 17 | − | <div class="g1t-paths"> | |
| 18 | − | <a class="g1t-path" href="/quickstart/"> | |
| 19 | − | <em>5 minutes</em> | |
| 20 | − | <strong>Quickstart</strong> | |
| 21 | − | <span>Create a workspace, push a repository, and land your first change with an agent.</span> | |
| 22 | − | </a> | |
| 23 | − | <a class="g1t-path" href="/guides/outcomes/"> | |
| 24 | − | <em>Agents</em> | |
| 25 | − | <strong>Hand off an outcome</strong> | |
| 26 | − | <span>Write what you want. A planner splits it into issues, and agents land them in order.</span> | |
| 27 | − | </a> | |
| 28 | − | <a class="g1t-path" href="/guides/bring-your-own-agent/"> | |
| 29 | − | <em>Your agent</em> | |
| 30 | − | <strong>Bring your own agent</strong> | |
| 31 | − | <span>Connect Claude Code or any MCP client, and record its sessions onto pull requests.</span> | |
| 32 | − | </a> | |
| 33 | − | <a class="g1t-path" href="/reference/api/"> | |
| 34 | − | <em>Build</em> | |
| 35 | − | <strong>The API</strong> | |
| 36 | − | <span>Everything on g1t over REST and MCP, with an explorer to call it from the page.</span> | |
| 37 | − | </a> | |
| 38 | − | </div> | |
| 16 | + | <CardGrid> | |
| 17 | + | <LinkCard | |
| 18 | + | title="Quickstart" | |
| 19 | + | description="Create a workspace, push a repository and land your first change with an agent, in five minutes." | |
| 20 | + | href="/quickstart/" | |
| 21 | + | /> | |
| 22 | + | <LinkCard | |
| 23 | + | title="Move from GitHub" | |
| 24 | + | description="Push your repository, rename .github to .g1t, and your Actions workflows run here as they are." | |
| 25 | + | href="/guides/actions/" | |
| 26 | + | /> | |
| 27 | + | </CardGrid> | |
| 28 | + | ||
| 29 | + | ## What you can do | |
| 30 | + | ||
| 31 | + | <CardGrid> | |
| 32 | + | <Card title="Hand off an outcome" icon="rocket"> | |
| 33 | + | Describe a result. A planner turns it into issues in the order they land, and | |
| 34 | + | [agents work them in parallel](/guides/outcomes/). | |
| 35 | + | </Card> | |
| 36 | + | <Card title="Agents that finish" icon="approve-check"> | |
| 37 | + | Each change is checked, reviewed and revised until it passes, then merged | |
| 38 | + | through the [merge queue](/guides/merge-queue/). You step in only when asked. | |
| 39 | + | </Card> | |
| 40 | + | <Card title="Bring your own agent" icon="laptop"> | |
| 41 | + | Claude Code or any MCP client works on g1t as a teammate, and | |
| 42 | + | [records its session](/guides/bring-your-own-agent/) onto its pull requests. | |
| 43 | + | </Card> | |
| 44 | + | <Card title="GitHub Actions, unchanged" icon="github"> | |
| 45 | + | Workflows run from `.g1t/workflows` [as GitHub runs them](/guides/actions/), | |
| 46 | + | and failing ones send the agent back to fix the cause. | |
| 47 | + | </Card> | |
| 48 | + | <Card title="Your models, your tools" icon="puzzle"> | |
| 49 | + | Use g1t's models or [your own providers](/guides/models/), and pull context | |
| 50 | + | from [Sentry, Jira and Linear](/guides/integrations/). | |
| 51 | + | </Card> | |
| 52 | + | <Card title="Everything has an API" icon="open-book"> | |
| 53 | + | Every action is a [REST endpoint and an MCP tool](/reference/api/), with | |
| 54 | + | [webhooks](/guides/webhooks/) for every event. | |
| 55 | + | </Card> | |
| 56 | + | </CardGrid> | |
| 57 | + | ||
| 58 | + | ## Using an AI assistant? | |
| 39 | 59 | ||
| 40 | − | <div class="g1t-columns"> | |
| 41 | − | <div> | |
| 42 | − | <h3>Agents</h3> | |
| 43 | − | <ul> | |
| 44 | − | <li><a href="/guides/g1t-agents/">g1t agents</a></li> | |
| 45 | − | <li><a href="/guides/outcomes/">Outcomes and plans</a></li> | |
| 46 | − | <li><a href="/guides/talking-to-agents/">Talking to agents</a></li> | |
| 47 | − | <li><a href="/guides/bring-your-own-agent/">Bring your own agent</a></li> | |
| 48 | − | <li><a href="/guides/integrations/">Integrations: Sentry, Jira, Linear</a></li> | |
| 49 | − | <li><a href="/guides/models/">Model providers: Anthropic, OpenAI, Gemini</a></li> | |
| 50 | − | <li><a href="/guides/webhooks/">Webhooks</a></li> | |
| 51 | − | <li><a href="/guides/actions/">GitHub Actions: rename .github to .g1t</a></li> | |
| 52 | − | <li><a href="/guides/automations/">Automations</a></li> | |
| 53 | − | </ul> | |
| 54 | − | </div> | |
| 55 | − | <div> | |
| 56 | − | <h3>Landing changes</h3> | |
| 57 | − | <ul> | |
| 58 | − | <li><a href="/concepts/overview/">How g1t works</a></li> | |
| 59 | − | <li><a href="/guides/merge-queue/">The merge queue</a></li> | |
| 60 | − | <li><a href="/guides/why-blame/">Sessions and why-blame</a></li> | |
| 61 | − | <li><a href="/concepts/forks/">Forks and branches</a></li> | |
| 62 | − | </ul> | |
| 63 | − | </div> | |
| 64 | − | <div> | |
| 65 | − | <h3>Workspaces</h3> | |
| 66 | − | <ul> | |
| 67 | − | <li><a href="/guides/authentication/">Accounts and sign-in</a></li> | |
| 68 | − | <li><a href="/guides/workspaces/">Workspaces and tokens</a></li> | |
| 69 | − | <li><a href="/guides/usage-and-billing/">Usage and billing</a></li> | |
| 70 | − | <li><a href="/guides/git/">Git</a></li> | |
| 71 | − | </ul> | |
| 72 | − | </div> | |
| 73 | − | <div> | |
| 74 | − | <h3>Reference</h3> | |
| 75 | − | <ul> | |
| 76 | − | <li><a href="/reference/api/">API overview</a></li> | |
| 77 | − | <li><a href="/api/reference/">API reference</a></li> | |
| 78 | − | <li><a href="/reference/mcp/">MCP tools</a></li> | |
| 79 | − | <li><a href="https://api.g1t.sh/openapi.json">OpenAPI document</a></li> | |
| 80 | − | <li><a href="https://g1t.sh/llms.txt">llms.txt</a></li> | |
| 81 | − | </ul> | |
| 82 | − | </div> | |
| 83 | − | </div> | |
| 60 | + | Give it [g1t.sh/llms.txt](https://g1t.sh/llms.txt), which describes g1t for | |
| 61 | + | agents, and ask it to set you up. Every page here also has **Copy page** at | |
| 62 | + | the top, to paste into a conversation, and a plain Markdown version at its | |
| 63 | + | address plus `.md`. |
| 1 | + | import type { APIRoute, GetStaticPaths } from 'astro'; | |
| 2 | + | import { getCollection } from 'astro:content'; | |
| 3 | + | ||
| 4 | + | /** | |
| 5 | + | * Every page as Markdown, at its path plus `.md`, for "Copy page" and for | |
| 6 | + | * agents that read the docs: the title, what it is about, then the page. | |
| 7 | + | */ | |
| 8 | + | export const getStaticPaths: GetStaticPaths = async () => { | |
| 9 | + | const pages = await getCollection('docs'); | |
| 10 | + | return pages.map((page) => ({ | |
| 11 | + | params: { slug: page.id === '' ? 'index' : page.id }, | |
| 12 | + | props: { page }, | |
| 13 | + | })); | |
| 14 | + | }; | |
| 15 | + | ||
| 16 | + | export const GET: APIRoute = ({ props }) => { | |
| 17 | + | const { page } = props as { page: Awaited<ReturnType<typeof getCollection<'docs'>>>[number] }; | |
| 18 | + | const { title, description } = page.data; | |
| 19 | + | // MDX pages keep their markup; strip imports and leave the rest readable. | |
| 20 | + | const body = (page.body ?? '').replace(/^import .*$/gm, '').trim(); | |
| 21 | + | const text = `# ${title}\n\n${description ? `> ${description}\n\n` : ''}${body}\n`; | |
| 22 | + | return new Response(text, { headers: { 'content-type': 'text/markdown; charset=utf-8' } }); | |
| 23 | + | }; |
| 1 | + | import { defineRouteMiddleware } from '@astrojs/starlight/route-data'; | |
| 2 | + | ||
| 3 | + | import { tabFor } from './tabs'; | |
| 4 | + | ||
| 5 | + | /** Each page's sidebar holds only its tab's groups. */ | |
| 6 | + | export const onRequest = defineRouteMiddleware((context) => { | |
| 7 | + | const route = context.locals.starlightRoute; | |
| 8 | + | const tab = tabFor(route.id); | |
| 9 | + | route.sidebar = route.sidebar.filter((entry) => entry.type === 'group' && tab.groups.includes(entry.label)); | |
| 10 | + | }); |
| 37 | 37 | --sl-text-body: 0.9375rem; | |
| 38 | 38 | --sl-line-height: 1.75; | |
| 39 | 39 | --sl-content-width: 46rem; | |
| 40 | − | --sl-nav-height: 3.5rem; | |
| 40 | + | /* The header's row, and the tabs beneath it. */ | |
| 41 | + | --sl-nav-height: 6.25rem; | |
| 41 | 42 | --sl-sidebar-width: 17rem; | |
| 42 | 43 | ||
| 43 | 44 | color-scheme: dark; | |
| 51 | 52 | /* --- Header --------------------------------------------------------------- */ | |
| 52 | 53 | ||
| 53 | 54 | header.header { | |
| 55 | + | padding: 0 !important; | |
| 54 | 56 | backdrop-filter: blur(10px); | |
| 55 | 57 | border-bottom: 1px solid var(--g1t-line); | |
| 56 | 58 | } | |
| 59 | + | site-search { | |
| 60 | + | width: 100%; | |
| 61 | + | max-width: 30rem; | |
| 62 | + | } | |
| 63 | + | /* On phones the menu button sits in the header's top row, not its middle. */ | |
| 64 | + | .sl-menu-button { | |
| 65 | + | top: calc((3.5rem - var(--sl-menu-button-size)) / 2) !important; | |
| 66 | + | } | |
| 57 | 67 | site-search button[data-open-modal] { | |
| 58 | 68 | border-radius: 0.5rem; | |
| 59 | 69 | background: var(--g1t-surface); | |
| 70 | 80 | .sidebar-content { | |
| 71 | 81 | padding-top: 1.25rem; | |
| 72 | 82 | } | |
| 83 | + | /* Groups always show their pages: no caret, no folding. */ | |
| 84 | + | .sidebar-content details > summary { | |
| 85 | + | pointer-events: none; | |
| 86 | + | padding-top: 0.25rem; | |
| 87 | + | } | |
| 88 | + | .sidebar-content details > summary .caret { | |
| 89 | + | display: none; | |
| 90 | + | } | |
| 91 | + | .sidebar-content .top-level > li + li { | |
| 92 | + | margin-top: 1rem; | |
| 93 | + | } | |
| 73 | 94 | /* Group labels: small and quiet, like section names, not links. */ | |
| 74 | 95 | .sidebar-content details > summary .group-label .large, | |
| 75 | 96 | .sidebar-content .top-level > li > details > summary { | |
| 109 | 130 | .content-panel { | |
| 110 | 131 | padding-top: 2rem; | |
| 111 | 132 | } | |
| 112 | − | .sl-markdown-content h1, | |
| 113 | − | h1#_top { | |
| 133 | + | /* The title and the page read as one, without a rule between them. */ | |
| 134 | + | .content-panel + .content-panel { | |
| 135 | + | border-top: 0; | |
| 136 | + | padding-top: 0.5rem; | |
| 137 | + | } | |
| 138 | + | .sl-markdown-content h1 { | |
| 114 | 139 | font-size: 2.25rem; | |
| 115 | 140 | font-weight: 650; | |
| 116 | 141 | letter-spacing: -0.03em; | |
| 117 | 142 | line-height: 1.15; | |
| 118 | 143 | color: var(--g1t-fg); | |
| 119 | 144 | } | |
| 120 | − | /* Each section opens with a rule across the full column. */ | |
| 145 | + | /* Sections open with space, not rules. */ | |
| 121 | 146 | .sl-markdown-content .sl-heading-wrapper.level-h2 { | |
| 122 | 147 | margin-top: 3rem; | |
| 123 | − | padding-top: 1.75rem; | |
| 124 | − | border-top: 1px solid var(--g1t-line); | |
| 125 | 148 | } | |
| 126 | 149 | .sl-markdown-content h2 { | |
| 127 | − | font-size: 1.4rem; | |
| 128 | − | font-weight: 650; | |
| 150 | + | font-size: 1.5rem; | |
| 151 | + | font-weight: 600; | |
| 129 | 152 | letter-spacing: -0.02em; | |
| 130 | 153 | color: var(--g1t-fg); | |
| 131 | 154 | } | |
| 150 | 173 | } | |
| 151 | 174 | .sl-markdown-content :not(pre) > code { | |
| 152 | 175 | border-radius: 0.3rem; | |
| 153 | − | padding: 0.1rem 0.35rem; | |
| 176 | + | padding: 0.1rem 0.3rem; | |
| 154 | 177 | font-size: 0.85em; | |
| 155 | 178 | color: var(--g1t-fg); | |
| 156 | − | box-shadow: inset 0 0 0 1px var(--g1t-line); | |
| 179 | + | background: var(--g1t-raised); | |
| 157 | 180 | } | |
| 158 | 181 | ||
| 159 | 182 | /* Tables: ruled, not boxed. */ | |
| 277 | 300 | color: var(--g1t-faint); | |
| 278 | 301 | } | |
| 279 | 302 | ||
| 280 | − | /* --- The home page -------------------------------------------------------- */ | |
| 303 | + | /* --- Cards ---------------------------------------------------------------- */ | |
| 281 | 304 | ||
| 282 | − | /* The home page brings its own heading, so Starlight's title bar goes. */ | |
| 283 | − | main:has(.g1t-home-hero) > .content-panel:first-child { | |
| 284 | − | display: none; | |
| 305 | + | .sl-markdown-content .card-grid { | |
| 306 | + | gap: 0.75rem; | |
| 285 | 307 | } | |
| 286 | − | main:has(.g1t-home-hero) > .content-panel { | |
| 287 | − | border-top: 0; | |
| 308 | + | .sl-markdown-content .card { | |
| 309 | + | padding: 1.1rem 1.2rem; | |
| 288 | 310 | } | |
| 289 | − | .g1t-home-hero { | |
| 290 | − | padding: 1rem 0 0.5rem; | |
| 311 | + | .sl-markdown-content .card .title { | |
| 312 | + | font-size: 1rem; | |
| 313 | + | font-weight: 600; | |
| 314 | + | color: var(--g1t-fg); | |
| 291 | 315 | } | |
| 292 | − | .sl-markdown-content .g1t-eyebrow { | |
| 293 | − | font-family: var(--g1t-font-mono); | |
| 294 | − | font-size: 0.72rem; | |
| 295 | − | letter-spacing: 0.2em; | |
| 296 | − | text-transform: uppercase; | |
| 316 | + | .sl-markdown-content .card .icon { | |
| 297 | 317 | color: var(--g1t-merged); | |
| 298 | − | } | |
| 299 | − | .g1t-home-hero h1 { | |
| 300 | − | margin: 0.75rem 0 0; | |
| 301 | − | font-size: clamp(2.25rem, 5vw, 3.25rem); | |
| 302 | − | font-weight: 650; | |
| 303 | − | letter-spacing: -0.035em; | |
| 304 | − | line-height: 1.05; | |
| 305 | − | color: var(--g1t-fg); | |
| 318 | + | background: color-mix(in srgb, var(--g1t-merged) 12%, transparent); | |
| 306 | 319 | border: 0; | |
| 307 | − | padding: 0; | |
| 308 | − | } | |
| 309 | − | .g1t-home-hero p { | |
| 310 | − | max-width: 38rem; | |
| 311 | − | margin-top: 1rem; | |
| 312 | − | font-size: 1.05rem; | |
| 313 | − | color: var(--g1t-muted); | |
| 320 | + | border-radius: 0.5rem; | |
| 321 | + | padding: 0.3rem; | |
| 314 | 322 | } | |
| 315 | − | .g1t-paths { | |
| 316 | − | display: grid; | |
| 317 | − | gap: 0.75rem; | |
| 318 | − | grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); | |
| 319 | − | margin-top: 2rem; | |
| 323 | + | .sl-link-card { | |
| 324 | + | padding: 1rem 1.2rem; | |
| 320 | 325 | } | |
| 321 | − | .g1t-path { | |
| 322 | − | display: block; | |
| 323 | − | border-radius: 1rem; | |
| 324 | − | padding: 1.1rem 1.2rem; | |
| 325 | − | background: var(--g1t-surface); | |
| 326 | − | border: 1px solid var(--g1t-line); | |
| 327 | − | text-decoration: none !important; | |
| 328 | − | transition: border-color 0.15s, transform 0.15s; | |
| 329 | − | } | |
| 330 | − | .g1t-path:hover { | |
| 331 | − | border-color: var(--g1t-line-strong); | |
| 332 | − | transform: translateY(-1px); | |
| 333 | − | } | |
| 334 | − | .g1t-path strong { | |
| 335 | − | display: block; | |
| 326 | + | .sl-link-card .title { | |
| 336 | 327 | font-size: 0.95rem; | |
| 328 | + | font-weight: 600; | |
| 337 | 329 | color: var(--g1t-fg); | |
| 338 | 330 | } | |
| 339 | − | .g1t-path span { | |
| 340 | − | display: block; | |
| 341 | − | margin-top: 0.35rem; | |
| 331 | + | .sl-link-card .description { | |
| 342 | 332 | font-size: 0.85rem; | |
| 343 | − | line-height: 1.55; | |
| 344 | 333 | color: var(--g1t-muted); | |
| 345 | 334 | } | |
| 346 | − | .g1t-path em { | |
| 347 | − | display: inline-block; | |
| 348 | − | margin-bottom: 0.6rem; | |
| 335 | + | ||
| 336 | + | /* Steps: numbered, on a quiet rail. */ | |
| 337 | + | .sl-steps > li::before { | |
| 338 | + | background: var(--g1t-raised) !important; | |
| 339 | + | color: var(--g1t-fg) !important; | |
| 340 | + | box-shadow: inset 0 0 0 1px var(--g1t-line-strong); | |
| 349 | 341 | font-family: var(--g1t-font-mono); | |
| 350 | − | font-size: 0.68rem; | |
| 351 | − | font-style: normal; | |
| 352 | − | letter-spacing: 0.14em; | |
| 353 | − | text-transform: uppercase; | |
| 354 | − | color: var(--g1t-merged); | |
| 355 | − | } | |
| 356 | − | .g1t-columns { | |
| 357 | − | display: grid; | |
| 358 | − | gap: 2rem; | |
| 359 | − | grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr)); | |
| 360 | − | margin-top: 3rem !important; | |
| 361 | − | } | |
| 362 | − | .g1t-columns > div, | |
| 363 | − | .g1t-paths > a { | |
| 364 | − | margin-top: 0 !important; | |
| 365 | − | } | |
| 366 | − | .g1t-columns h3 { | |
| 367 | − | margin: 0 0 0.5rem; | |
| 368 | 342 | font-size: 0.8rem; | |
| 369 | − | font-weight: 600; | |
| 370 | − | letter-spacing: 0.06em; | |
| 371 | − | text-transform: uppercase; | |
| 372 | − | color: var(--g1t-faint); | |
| 373 | 343 | } | |
| 374 | − | .g1t-columns ul { | |
| 375 | − | margin: 0; | |
| 376 | − | padding: 0; | |
| 377 | − | list-style: none; | |
| 378 | − | } | |
| 379 | − | .g1t-columns li { | |
| 380 | − | margin: 0.3rem 0; | |
| 381 | − | } | |
| 382 | − | .g1t-columns a { | |
| 383 | − | font-size: 0.9rem; | |
| 384 | − | color: var(--g1t-fg-soft) !important; | |
| 385 | − | text-decoration: none !important; | |
| 386 | − | } | |
| 387 | − | .g1t-columns a:hover { | |
| 388 | − | color: #cfc6ff !important; | |
| 344 | + | .sl-steps > li::after { | |
| 345 | + | background: var(--g1t-line) !important; | |
| 389 | 346 | } |
| 1 | + | /** | |
| 2 | + | * The tabs under the header. Each shows its own part of the sidebar, as | |
| 3 | + | * the groups named here; a page belongs to the tab whose `match` it fits. | |
| 4 | + | */ | |
| 5 | + | export type Tab = { label: string; href: string; groups: string[]; match: (slug: string) => boolean }; | |
| 6 | + | ||
| 7 | + | export const TABS: Tab[] = [ | |
| 8 | + | { | |
| 9 | + | label: 'Documentation', | |
| 10 | + | href: '/', | |
| 11 | + | groups: ['Get started', 'Agents', 'Connect your tools', 'Landing changes', 'Workspaces'], | |
| 12 | + | match: (slug) => !slug.startsWith('reference/'), | |
| 13 | + | }, | |
| 14 | + | { | |
| 15 | + | label: 'API reference', | |
| 16 | + | href: '/reference/api/', | |
| 17 | + | groups: ['Reference'], | |
| 18 | + | match: (slug) => slug.startsWith('reference/'), | |
| 19 | + | }, | |
| 20 | + | ]; | |
| 21 | + | ||
| 22 | + | export function tabFor(slug: string): Tab { | |
| 23 | + | return TABS.find((tab) => tab.match(slug)) ?? TABS[0]; | |
| 24 | + | } |