Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ docs/
├── app/composables/ # useLandingForge (one clock for every live panel), useSubNavigation
├── app/utils/ # platforms table, formatting, recorded landing samples
├── app/pages/explorer.vue # explorer, own route outside the docs layout
├── public/ # fonts, favicon.svg and the icons and manifest cut from it
├── server/api/ # repo, issues, pulls, commits, ci, threads, user, platforms over the library
├── server/utils/ # forge.ts (one provider per platform), query.ts (caps, cache, rate limit, errors), slim.ts (wire shapes)
├── scripts/record-fixtures.mjs # regenerates app/utils/landing-fixtures.ts through dist/
Expand Down Expand Up @@ -57,6 +58,11 @@ Resolution traps, both caused by the repo root being a pnpm workspace:
- In production the cache lives in the KV binding `CACHE` (`$production.nitro.storage.cache`). Locally it is in memory.
- The explorer applies its deep link once after mount. A prerendered page hydrates with an empty query and Nuxt restores the address only afterwards, so reading `route.query` in setup gives you nothing.

## Favicon and SEO

- Docus links `/favicon.ico` without shipping one. `public/favicon.svg` is the source, the PNGs and the `.ico` are cut from it with `rsvg-convert` and ImageMagick, `app.head` in `nuxt.config.ts` links them with the manifest and theme colours.
- Docus emits the JSON-LD itself: `Article` and `BreadcrumbList` on every content page through `useSeo`, and on the landing a `WebSite` graph with the `SoftwareApplication` and `Organization` nodes from `seo.schema` in `app.config.ts`. `app/pages/explorer.vue` is outside the content collection, so it calls `useSeo` and `defineOgImage` on its own.

## OG images

- `app/components/OgImage/Docs.takumi.vue` and `Landing.takumi.vue` override the Docus templates of the same name and are rendered by Takumi at build time. Takumi has no CSS variables, so the theme colours from `app.css` are repeated there as literals. Annoying, but that is what it is.
Expand Down
23 changes: 21 additions & 2 deletions docs/app/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ export default defineAppConfig({
title: "@agntn/forges",
description:
"One TypeScript API over GitHub, GitLab, Gitea and GitBucket: repos, issues, pull requests, review threads, commits and CI in one shape.",
schema: {
type: "SoftwareApplication",
applicationCategory: "DeveloperApplication",
operatingSystem: "Node.js",
price: 0,
sameAs: ["https://github.com/agntn/forges", "https://npmx.dev/package/@agntn/forges"],
organization: {
name: "agntn",
url: "https://agntn.dev",
logo: "https://agntn.dev/icon-512.png",
sameAs: ["https://github.com/agntn", "https://npmx.dev/org/agntn"],
},
},
},
header: {
title: "@agntn/forges",
Expand All @@ -15,9 +28,9 @@ export default defineAppConfig({
branch: "main",
rootDir: "docs",
},
/** The GitHub link comes from `github.url`; listing it here too doubles the icon in the footer. */
socials: {
github: "https://github.com/agntn/forges",
npm: "https://www.npmjs.com/package/@agntn/forges",
npm: "https://npmx.dev/package/@agntn/forges",
},
ui: {
colors: {
Expand Down Expand Up @@ -49,6 +62,12 @@ export default defineAppConfig({
description: "text-base leading-7 text-muted",
},
},
/** Nuxt UI truncates TOC entries; headings here are sentences, so let them wrap. */
contentToc: {
slots: {
linkText: "whitespace-normal",
},
},
contentSurround: {
slots: {
link: "rounded-xl forges-frame border-0 bg-default hover:bg-muted",
Expand Down
7 changes: 4 additions & 3 deletions docs/app/components/content/ForgesExplorer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -522,12 +522,13 @@ onMounted(() => {
</button>
<button
type="button"
class="forges-copy ms-auto"
class="forges-copy ms-auto min-w-0 max-w-full"
:aria-label="copied ? 'Copied' : 'Copy tool call'"
@click="copyCall"
>
<span class="text-dimmed">tool</span> {{ clip(toolCall, 64) }}
<UIcon :name="copied ? 'i-lucide-check' : 'i-lucide-copy'" class="size-3.5" />
<span class="shrink-0 text-dimmed">tool</span>
<span class="min-w-0 truncate">{{ clip(toolCall, 64) }}</span>
<UIcon :name="copied ? 'i-lucide-check' : 'i-lucide-copy'" class="size-3.5 shrink-0" />
</button>
</div>
</form>
Expand Down
23 changes: 18 additions & 5 deletions docs/app/pages/explorer.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,32 @@
<script setup lang="ts">
definePageMeta({ layout: "default" });

useSeoMeta({
title: "Explorer · @agntn/forges",
description:
"Read a repository, its issues, pull requests, commits, CI runs and review threads on GitHub, GitLab or Gitea through the docs worker and see the normalized shape.",
const title = "Explorer";
const description =
"Read a repository, its issues, pull requests, commits, CI runs and review threads on GitHub, GitLab or Gitea through the docs worker, in the normalized shape.";

useSeo({
title,
description,
type: "article",
breadcrumbs: [{ title, path: "/explorer" }],
});

defineOgImage(
"Docs",
{ headline: "Explorer", title, description },
{
alt: "Explorer: any repository on GitHub, GitLab or Gitea read through the library, in one shape",
},
);
</script>

<template>
<div class="forges-landing not-prose">
<ToolHero
eyebrow="explorer"
title="Any repository."
accent="Every platform, one shape."
accent="One shape."
description="The docs worker runs the same calls the library exposes: repos.get, issues.list, pullRequests.list, commits.list, ciRuns.list, threads.list and users.get. Answers are cached for a while, a demo page has no business burning somebody else's rate limit."
>
<p
Expand Down
44 changes: 4 additions & 40 deletions docs/content/2.platforms/0.index.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,19 @@ Every platform answers the same nine resources with the same shapes. What change
| [GitBucket](/platforms/gitbucket) | `github` + `baseURL` | `Authorization: token` | none | none | repository only |

:::card-group
::card

---

icon: i-simple-icons-github
to: /platforms/github
title: GitHub

---

::card{icon="i-simple-icons-github" title="GitHub" to="/platforms/github"}
REST v3 for everything except review threads, which need GraphQL for their resolved flag. The reference the others are measured against.
::

::card

---

icon: i-simple-icons-gitlab
to: /platforms/gitlab
title: GitLab

---

::card{icon="i-simple-icons-gitlab" title="GitLab" to="/platforms/gitlab"}
API v4 with Private-Token auth, project ids resolved and cached for you, iid as the number, and a group fallback for owners that are not users.
::

::card

---

icon: i-simple-icons-gitea
to: /platforms/gitea
title: Gitea and Forgejo

---

::card{icon="i-simple-icons-gitea" title="Gitea and Forgejo" to="/platforms/gitea"}
API v1 with limit instead of per_page, empty strings and nulls where fields are missing, and Codeberg or any Forgejo through baseURL.
::

::card

---

icon: i-lucide-server
to: /platforms/gitbucket
title: GitBucket

---

::card{icon="i-lucide-server" title="GitBucket" to="/platforms/gitbucket"}
The GitHub provider pointed at a GitBucket /api/v3. What GitBucket does not serve fails with a sentence, not a 404.
::
:::
25 changes: 24 additions & 1 deletion docs/nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export default defineNuxtConfig({
llms: {
domain: "https://forges.agntn.dev",
},
/** Docus pages define their own OG images; the alt text is the one thing they leave unset. */
ogImage: {
defaults: {
alt: "@agntn/forges: one TypeScript API over GitHub, GitLab, Gitea and GitBucket",
},
},
icon: {
clientBundle: {
icons: [
Expand Down Expand Up @@ -60,6 +66,23 @@ export default defineNuxtConfig({
colorMode: {
preference: "dark",
},
/** Docus links /favicon.ico without shipping one; the icons and manifest are cut from public/favicon.svg. */
app: {
head: {
link: [
{ rel: "icon", type: "image/svg+xml", href: "/favicon.svg" },
{ rel: "apple-touch-icon", sizes: "180x180", href: "/apple-touch-icon.png" },
{ rel: "manifest", href: "/site.webmanifest" },
],
meta: [
{ name: "theme-color", media: "(prefers-color-scheme: dark)", content: "#0b0d10" },
{ name: "theme-color", media: "(prefers-color-scheme: light)", content: "#eef1f4" },
{ name: "apple-mobile-web-app-title", content: "forges" },
{ name: "author", content: "oritwoen" },
{ property: "og:locale", content: "en_US" },
],
},
},
/** Docus ships an MCP endpoint that needs the Cloudflare Agents SDK on Workers. The docs do not need it. */
mcp: {
enabled: false,
Expand All @@ -69,7 +92,7 @@ export default defineNuxtConfig({
compatibilityDate: "2026-09-03",
prerender: {
crawlLinks: true,
routes: ["/", "/sitemap.xml", "/robots.txt", "/llms.txt", "/llms-full.txt"],
routes: ["/", "/explorer", "/sitemap.xml", "/robots.txt", "/llms.txt", "/llms-full.txt"],
ignore: ["/api"],
},
cloudflare: {
Expand Down
Binary file added docs/public/apple-touch-icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/favicon.ico
Binary file not shown.
8 changes: 8 additions & 0 deletions docs/public/favicon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/icon-192.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/public/icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions docs/public/site.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "@agntn/forges",
"short_name": "forges",
"description": "Unified Git Provider: single TypeScript API for GitHub, GitLab, Gitea, and GitBucket",
"start_url": "/",
"display": "browser",
"background_color": "#0b0d10",
"theme_color": "#0b0d10",
"icons": [
{ "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" },
{ "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" }
]
}
Loading