diff --git a/editor/AGENTS.md b/editor/AGENTS.md index df4fee9458..bb2962094c 100644 --- a/editor/AGENTS.md +++ b/editor/AGENTS.md @@ -1,8 +1,59 @@ # `editor` -## Universal routing (docs-friendly links) +This package is the Next.js app that powers **`grida.co`** and tenant domains (e.g. `xyz.grida.site`, custom domains). -Grida supports **universal routing** so documentation can link to stable, tenant-agnostic URLs like `https://grida.co/_/` and have them resolved to canonical tenant/document routes at runtime. +- This doc is a curated “where to change what” map. It’s intentionally **not** exhaustive. -- When you add a **new user-facing page** that should be referenced from docs, ensure it is registered in **universal routing**. -- When debugging docs links that point to the editor, start from the universal routing spec: `docs/wg/platform/universal-docs-routing.md`. +## Key rules (things that bite later) + +- **Auth is special**: `app/(auth)` is security-critical. **Do not modify** routes/flows there. +- **Public API is versioned**: treat `app/(api)/(public)/v1` as **backwards-compatible** (additive changes only unless you’re intentionally breaking/v2-ing). +- **Layouts are per route group**: there isn’t a single shared root layout across the whole `app/` tree — top-level route groups own their root `layout.tsx`/metadata. +- **Edge entrypoint is `proxy.ts`**: on Next.js 16 this replaces `middleware.ts` (same runtime + semantics). Don’t add a new `middleware.ts`. +- **Tenant pages are tenant-aware**: follow [`app/(tenant)/README.md`]() for host-prefixed fetches (`server.HOST` / `web.HOST`) and tenant-friendly `href="/path"` patterns. +- **Shared UI boundaries matter**: + - `components/` should remain route-agnostic and override-friendly (see [`components/AGENTS.md`](components/AGENTS.md)) + - `kits/` are stateful “drop-in widgets” that must not couple to global editor/workbench state (see [`kits/AGENTS.md`](kits/AGENTS.md)) + - `scaffolds/` are feature assemblies and may bind to global/editor state +- **Stable public asset URLs**: put canonical assets under `public/` (e.g. `/brand/...png`) when you need a durable, crawlable, cache-friendly path. (If you care about image search quirks, see [`app/(www)/SEO.md`]().) + +## Directory map + +### Editor root (selected) + +| Path | What lives here | Guide | Notes | +| ------------- | ------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | +| `app/` | Next.js App Router routes | — | Route groups are listed below. Main sitemap: [`app/sitemap.ts`](app/sitemap.ts). | +| `www/` | Public-site components for `(www)` | — | Header/footer + landing components. Nav config: [`www/data/sitemap.ts`](www/data/sitemap.ts) (despite the name, it’s **not** `sitemap.xml`). | +| `components/` | Shared UI building blocks | [`components/AGENTS.md`](components/AGENTS.md) | Route-agnostic, override-friendly components. Includes primitives under `components/ui/*` and related subdirectories. | +| `scaffolds/` | Feature-sized UI assemblies | — | Bigger, feature-scoped assemblies (often app-coupled). | +| `lib/` | Stable, non-opinionated modules | — | Good candidates to promote to `/packages` once matured. | +| `grida-*` | Large domain folders (e.g. `grida-canvas*`) | — | Editor-local domain implementations that may be promoted into `/packages` once stabilized. | +| `kits/` | Stateful “drop-in” widgets | [`kits/AGENTS.md`](kits/AGENTS.md) | Opinionated, state-rich UI modules: stateful inside the kit, simple API for consumers. No route/global-store coupling. | +| `theme/` | Templates and themes | — | Email templates, enterprise templates, etc. | +| `public/` | Static assets | — | Use for **stable public asset URLs** (e.g. `/brand/...png`). | + +### `app/` route groups (selected) + +| Route group | Used for | Guide | Notes / rules | +| ------------- | ------------------------------------------------------ | ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `(workbench)` | Core editor / workbench | — | Performance-sensitive. Keep `use client` boundaries narrow and avoid heavy deps in shared layouts. | +| `(workspace)` | Dashboard / org & project management | — | Similar constraints as `(workbench)`; avoid pushing heavyweight client code into shared layouts. | +| `(tenant)` | Tenant-rooted routes (custom domains / `*.grida.site`) | [`app/(tenant)/README.md`]() | Tenant-aware routing + host-prefixed fetch rules (`server.HOST` / `web.HOST`). | +| `(api)` | Route handlers (public + private) | — | Public: `app/(api)/(public)/v1` (treat as stable). Private: `app/(api)/private` (first-party). Private editor web APIs live under `app/(api)/private/editor` (see `README.md`). | +| `(auth)` | Auth flow routes | — | **Do not modify.** | +| `(tools)` | Standalone tools | — | Tools live under `app/(tools)/tools/*`. Some tools include a local `AGENTS.md` (example: [`halftone`]()). | +| `(preview)` | Embed/preview surfaces | — | Read-only previews and embed-purpose routes (often consumed by tools/playground). | +| `(library)` | Library (open assets) pages | — | Library browsing/marketing routes. | +| `(www)` | Public marketing / SEO pages | [`app/(www)/SEO.md`]() | Public `grida.co` landing pages and SEO-first routes. | +| `(site)` | Public pages not SEO-first | — | Public routes that aren’t primarily marketing/SEO. | +| `(insiders)` | Insider/local-only routes | — | Local-only/internal tooling and flows. Don’t depend on these for production UX. | +| `(dev)` | Dev-only pages/tools | — | Development-only routes; avoid linking from production UI. | + +## Navigation, sitemaps, and docs links + +- **Header/nav config**: [`www/data/sitemap.ts`](www/data/sitemap.ts) (drives `www/header.tsx`; despite the name, this is **not** `sitemap.xml`) +- **`sitemap.xml` generator**: [`app/sitemap.ts`](app/sitemap.ts) (Next.js `MetadataRoute.Sitemap` for `grida.co`; tenant routes are handled separately) +- **Universal routing (docs-friendly links)**: [`../docs/wg/platform/universal-docs-routing.md`](../docs/wg/platform/universal-docs-routing.md) + - When docs link to editor pages, prefer `https://grida.co/_/`. + - If you add a new user-facing page that docs should reference, ensure it’s registered in universal routing. diff --git a/editor/app/(www)/(brand)/brand/page.tsx b/editor/app/(www)/(brand)/brand/page.tsx index d1f5f8b2de..3e8f75776f 100644 --- a/editor/app/(www)/(brand)/brand/page.tsx +++ b/editor/app/(www)/(brand)/brand/page.tsx @@ -123,7 +123,7 @@ export default function BrandPage() {

- {/* Not a download target — present for crawlers/SEO */} + {/* Not a download target — present for crawlers/SEO. unoptimized so HTML has direct PNG URLs for Google Image index. */}
{/* Light/Dark aware wordmark */} @@ -132,6 +132,7 @@ export default function BrandPage() { alt="Grida wordmark logo" fill priority + unoptimized sizes="(max-width: 1024px) 100vw, 40vw" className="object-contain dark:hidden" /> @@ -140,6 +141,7 @@ export default function BrandPage() { alt="Grida wordmark logo" fill priority + unoptimized sizes="(max-width: 1024px) 100vw, 40vw" className="object-contain hidden dark:block" /> @@ -286,6 +288,7 @@ function BrandAssetCard({ alt={alt} fill priority + unoptimized sizes="(max-width: 640px) 100vw, 40vw" className="object-contain" /> diff --git a/editor/app/(www)/SEO.md b/editor/app/(www)/SEO.md new file mode 100644 index 0000000000..7ead2eed25 --- /dev/null +++ b/editor/app/(www)/SEO.md @@ -0,0 +1,49 @@ +### SEO gotchas for `editor/app/(www)` (not the basics) + +This doc exists to record **easy-to-miss stack-specific issues** and **our preferred tricks**. + +--- + +### Google Image Search gotcha: Next.js `` hides the “real” image URL + +**Symptom:** the page ranks (e.g. “Grida logo png”), but **Google Images doesn’t surface the logo assets** from that page. + +**Cause:** Next.js `` commonly renders an optimized `src` like: + +- `/_next/image?url=%2Fbrand%2Fgrida-symbol-240.png&w=...&q=...` + +Google Images indexes the asset more reliably when the rendered `img src` is a **stable public URL** such as `/brand/...png` rather than only an optimized proxy URL. + +**Our trick (for brand/press/asset pages):** ensure the rendered `img src` is a **direct, stable, public asset URL**. + +- **Preferred**: keep ``, but add `unoptimized` for SEO-critical assets. +- **Alternative**: use a plain `` when you want to be maximally explicit. + +**Apply to:** pages where the images themselves are the query target (“logo png”, brand assets, downloads). + +**Sanity checklist:** + +- **Public**: returns **200** without cookies; not blocked for `Googlebot-Image` +- **Correct `Content-Type`**: `image/png`, `image/svg+xml`, etc. +- **Not blocked by robots**: `robots.txt` and `X-Robots-Tag` +- **`/_next/image` is fetchable**: not blocked by robots, authentication, or middleware (other pages may still rely on the optimizer) +- **Stable, clean URLs**: prefer `public/` paths like `/brand/...` (avoid query-string-only canonical asset URLs) +- **Alt text**: include “Grida” + asset name + format (e.g. “Grida wordmark logo (PNG)”) + +**Reminder:** JSON-LD / Open Graph help, but they **don’t replace** crawlable image URLs in the HTML for Image Search. + +--- + +### Image search boost: sitemap + canonical URL + +- **Image sitemap**: include brand PNGs in the image sitemap (one entry per canonical asset URL). +- **Canonical**: keep a single, clean canonical `/brand` URL (avoid duplicates, tracking params, and competing alternates). + +--- + +### Next.js metadata: relative OG/Twitter images need `metadataBase` + +OG/Twitter images must resolve to **absolute public URLs** that return **200** without auth. + +If `openGraph.images` uses relative paths in Next.js metadata, `metadataBase` is required so crawlers see correct absolute URLs. +