From f0e4109fd78b9568b48fbf5ff30beec3df65ea1d Mon Sep 17 00:00:00 2001
From: Universe
Date: Wed, 18 Feb 2026 05:11:45 +0900
Subject: [PATCH] Enhance documentation and SEO for brand assets
- Updated AGENTS.md to include key rules and a directory map for better navigation and understanding of the project structure.
- Added SEO.md to document specific SEO considerations for the editor's public site, including image indexing and metadata requirements.
- Modified BrandPage component to use unoptimized images for better SEO performance, ensuring direct URLs are available for Google Image indexing.
---
editor/AGENTS.md | 59 +++++++++++++++++++++++--
editor/app/(www)/(brand)/brand/page.tsx | 5 ++-
editor/app/(www)/SEO.md | 49 ++++++++++++++++++++
3 files changed, 108 insertions(+), 5 deletions(-)
create mode 100644 editor/app/(www)/SEO.md
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.
+