diff --git a/public/llms.txt b/public/llms.txt index 32a2b13..729c0e7 100644 --- a/public/llms.txt +++ b/public/llms.txt @@ -60,6 +60,34 @@ To get the higher limit, authenticate with an API key (recommended for any autom --- +## Web URLs (for linking humans to a view) + +The API paths above are for fetching data. When you need to point a person at +something in the browser, use these page URLs. The rule: a version is a path +prefix, a view is a path segment, and omitting the version prefix means "latest". +Semver appears WITHOUT the leading "v" in page URLs (/v/1.2.0), unlike API paths +(/versions/v1.2.0). Both forms resolve, but the bare form is canonical. + +https://underlay.org/:owner → account/organization profile +https://underlay.org/:owner/:slug → collection overview (latest): README, record types, stats +https://underlay.org/:owner/:slug/records → browse records (latest) — add ?type=TypeName to pick a type +https://underlay.org/:owner/:slug/schemas → schemas (latest) +https://underlay.org/:owner/:slug/files → files (latest) +https://underlay.org/:owner/:slug/versions → version history +https://underlay.org/:owner/:slug/versions/compare?from=v1.0.0&to=v1.1.0 → diff two versions +https://underlay.org/:owner/:slug/v/1.2.0 → overview pinned to v1.2.0 +https://underlay.org/:owner/:slug/v/1.2.0/records → records in v1.2.0 (?type=TypeName) +https://underlay.org/:owner/:slug/v/1.2.0/schemas → schemas as pinned in v1.2.0 +https://underlay.org/:owner/:slug/v/1.2.0/files → files in v1.2.0 +https://underlay.org/records/:hash → a record's content-addressed permalink + provenance +https://underlay.org/schemas/:id → a schema's detail page + which collections use it + +Prefer a version-pinned URL when citing data, since /records (no prefix) follows +the latest version and its contents will change on the next push. ARK identifiers +(ark:NAAN/id.v1.2.0) also resolve to these pages and are the most durable option. + +--- + ## Reading Data ### Browse collections diff --git a/src/App.tsx b/src/App.tsx index edfd22b..ffc3eee 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,6 @@ import type { LoaderFunctionArgs, RouteObject } from 'react-router' +import { RouteErrorBoundary } from '~/components/NotFound' import Root from '~/components/Root' import { fetchBase } from '~/lib/fetch-base' import { buildDataRoutes } from '~/route-gen' @@ -32,6 +33,7 @@ export const routes: RouteObject[] = [ { id: 'root', Component: Root, + ErrorBoundary: RouteErrorBoundary, loader: rootLoader, children: [...buildDataRoutes(components, dataModules), { path: '*', lazy: NotFound }], }, diff --git a/src/api/ark.ts b/src/api/ark.ts index e53e4f7..82536d9 100644 --- a/src/api/ark.ts +++ b/src/api/ark.ts @@ -270,7 +270,8 @@ export async function resolve(c: Context) { } if (version !== undefined && versionRow) { - const url = `/${ownerSlug}/${collectionSlug}/v/${versionRow.semver}` + // Page URLs use the bare semver form (/v/1.0.0); stored semver is "v1.0.0". + const url = `/${ownerSlug}/${collectionSlug}/v/${versionRow.semver.replace(/^v/, '')}` return c.json({ type: 'redirect' as const, url, diff --git a/src/api/collections.ts b/src/api/collections.ts index 0117200..1d36528 100644 --- a/src/api/collections.ts +++ b/src/api/collections.ts @@ -774,7 +774,7 @@ const app = new Hono() }) .from(schema.collections) .where(and(...conditions)) - .orderBy(schema.collections.updatedAt) + .orderBy(desc(schema.collections.updatedAt)) return c.json(results) }, diff --git a/src/components/ApiPlayground.tsx b/src/components/ApiPlayground.tsx index 5eb59cf..a5c2fc6 100644 --- a/src/components/ApiPlayground.tsx +++ b/src/components/ApiPlayground.tsx @@ -1,5 +1,7 @@ import { useCallback, useState } from 'react' +import { Button } from '~/components/ui' + interface Collection { id: string slug: string @@ -152,7 +154,7 @@ export function ApiPlayground({ slug, collections }: ApiPlaygroundProps) { setSelectedEndpoint(0) setResponse(null) }} - className="bg-parchment border-rule focus:border-ink border px-2 py-1 text-sm focus:outline-none" + className="bg-parchment border-rule focus:border-ink rounded-control cursor-pointer border px-2 py-1 text-sm focus:outline-none" > {collections.map((c) => (