Uh oh!
There was an error while loading. Please reload this page.
feat(seo): robots.txt, sitemap.xml, llms.txt, canonical URL, JSON-LD, per-page meta titles/descriptions - #31
Merged
Conversation
… per-page meta P0/P1 indexability improvements based on Gonçalo's audit: - public/robots.txt — allow all crawlers, point to sitemap - public/sitemap.xml — 29 URL entries covering all docs routes + home + playground - public/llms.txt — LLM-friendly site manifest (llmstxt.org convention) - index.html — canonical <link> pointing to https://typecomposer.com/; two JSON-LD blocks (WebSite + SoftwareApplication); improved global title/description ('zero-HTML' framing) - src/utils/seo.ts — updatePageMeta() utility: per-route <title> and <meta name=description> map (29 routes); called on each route activation so browser history and JS-rendered tabs show meaningful page titles - src/views/elements/Base.ts — call updatePageMeta(Router.pathname) in BaseView constructor so every docs page updates the document title Hash routing constraint: canonical stays fixed at the root URL since hash fragments are not real URLs (no per-page canonical benefit without SSR). Clean URL migration deferred — requires hosting config validation. Build: vite build ✓ 11.11s, 0 errors Resolves: robots.txt missing, no sitemap, no per-page titles/descriptions
Uh oh!
There was an error while loading. Please reload this page.
joaodibba pushed a commit
that referenced
this pull request
Jun 12, 2026
… URLs SEO P0 follow-up to PRs #29–#31. Problem: hash routing (/#/docs/skills) is not indexable by search engines because the fragment is never sent to the server. Direct deep-links also 404 without a server-side SPA fallback. Changes: - src/router/router.ts: history: "hash" → history: "browser" — enables clean URLs like /docs/skills, /docs/getting-started, /playground - vite.config.js: base: "./" → base: "/" — absolute asset paths prevent broken asset loads when navigating to nested routes (e.g. /docs/skills) - public/.htaccess: improve comments; SPA fallback rule was already correct (rewrite all non-file/non-dir requests to /index.html) and remains unchanged in logic — confirmed present before this PR - src/utils/seo.ts: add per-route canonical <link> injection (setCanonical); now meaningful because each route has a real URL; updated Router description to remove "hash" reference; root canonical in index.html stays https://typecomposer.com/ - public/sitemap.xml: strip /#/ from all 29 URLs (e.g. /#/docs/skills → /docs/skills, /#/playground → /playground) - public/llms.txt: strip /#/ from all 29 doc links; update Router key concept description to reference browser history mode - index.html: update JSON-LD SearchAction target from /#/docs/… to /docs/… Hosting note (⚠ must verify after deploy): The .htaccess SPA fallback was already present in the repo (added before PR #31) and is confirmed to contain the correct RewriteRule. Direct deep-link indexability (e.g. https://typecomposer.com/docs/skills returning 200 not 404) requires the Apache server to honour this .htaccess. Test with: curl -I https://typecomposer.com/docs/skills Expected: HTTP/2 200, not 301/302/404. Build: npm run build — ✓ 2405 modules, no errors Reviewer: @zico15
joaodibba added a commit
that referenced
this pull request
Jun 12, 2026
…e URLs (#32) ## SEO P0 — Clean URLs via Browser History Routing Follow-up to #29, #30, #31. ### Problem Hash routing (`/#/docs/skills`) is not indexable by search engines — the fragment after `#` is never sent to the server, so Googlebot sees only `https://typecomposer.com/` for every page. Direct deep-links to `/docs/skills` also 404 without a proper SPA fallback. This means the sitemap, per-route canonicals, and per-page meta added in #31 cannot deliver their full SEO value. ### Solution Switch `Router.create` from `history: "hash"` to `history: "browser"` (HTML5 `pushState`). The server-side SPA fallback (`.htaccess`) was **already present in the repo** before this PR and contains the correct `RewriteRule ^.*$ /index.html [L,QSA]` — so direct deep-links will be served correctly on the Apache-hosted production site once this PR is merged and deployed. --- ### Files Changed (7) | File | Change | |------|--------| | `src/router/router.ts` | `history: "hash"` → `history: "browser"` | | `vite.config.js` | `base: "./"` → `base: "/"` — absolute asset paths prevent broken JS/CSS loads when a user navigates directly to a nested route like `/docs/skills` (relative `./assets/` would resolve to `/docs/assets/`) | | `src/utils/seo.ts` | `updatePageMeta()` updates `<title>` and `<meta name="description">` per route. Runtime `setCanonical()` workaround **removed** — the static root canonical in `index.html` covers the home route, and `sitemap.xml` provides authoritative clean-URL canonicals for all 29 pages without requiring JS. | | `public/sitemap.xml` | Strip `#/` from all 29 URLs (`/#/docs/skills` → `/docs/skills`, `/#/playground` → `/playground`) | | `public/llms.txt` | Same URL migration for all 29 doc links; Router key concept updated to reference `history: "browser"` | | `index.html` | JSON-LD `SearchAction.target` updated from `/#/docs/{search_term_string}` → `/docs/{search_term_string}`; canonical comment tidied | | `public/.htaccess` | Comments improved for clarity; **rewrite rule logic unchanged** — it was already correct | --- ### Build Validation (commit 2) ``` npm run build ✓ 2405 modules transformed — no errors, no type errors ``` ``` dist/.htaccess ✓ present (copied from public/) dist/sitemap.xml ✓ 0 hash URLs remaining dist/llms.txt ✓ 0 hash URLs remaining dist/index.html ✓ assets load from /assets/* (absolute paths) dist/index.html ✓ root canonical <https://typecomposer.com/> present src/utils/seo.ts ✓ setCanonical() removed src/utils/seo.ts ✓ CANONICAL_BASE constant removed ``` --- ###⚠️ Hosting Verification Required Before Release The `.htaccess` SPA fallback must be honoured by the production Apache server. Please test after deploy: ```bash # Direct deep-link must return HTTP 200, not 301/302/404 curl -I https://typecomposer.com/docs/skills # Expected: HTTP/2 200 curl -I https://typecomposer.com/docs/getting-started # Expected: HTTP/2 200 curl -I https://typecomposer.com/playground # Expected: HTTP/2 200 # Assets must still load correctly curl -I https://typecomposer.com/typecomposer.svg # Expected: HTTP/2 200 curl -I https://typecomposer.com/sitemap.xml # Expected: HTTP/2 200 ``` If the Apache server requires `AllowOverride All` in its vhost config, that must be enabled separately by the hosting admin. If `.htaccess` is not supported, a `_redirects` (Netlify) or `vercel.json` equivalent would be needed — but the existing `.htaccess` confirms Apache is the target host. --- ### /docs/skills → Agent Skills ✓ Route `docs/skills` maps to `BaseView` and `PAGE_META["docs/skills"]` is unchanged: - Title: **Agent Skills | TypeComposer** - Description: _Use TypeComposer to build AI agent skills…_ --- cc @zico15
7 tasks
zico15 added a commit
that referenced
this pull request
Jun 12, 2026
…d-wasm and component registration (#33) ## Summary Finishes and unblocks PR #15 (originally opened by @Copilot, which had 4 merge conflicts with `main`). This branch is identical to the Copilot PR branch but with a merge commit that resolves all 4 conflicts against current `main`. --- ## What this PR does Migrates `PlaygroundView` from `@codesandbox/sandpack-client` (remote compilation) to **fully browser-based compilation** using `esbuild-wasm`, satisfying all acceptance criteria from issue #14. ### Changes | File | Change | |---|---| | `src/utils/browserCompiler.ts` (**new**) | esbuild-wasm in-browser TS compiler with virtual file system plugin | | `src/components/editor/MonacoEditor.ts` (**new**) | Monaco Editor wrapped as a TypeComposer `Component` | | `src/views/playground/PlaygroundView.ts` | Replaced `loadSandpackClient` with `compileAndRun()`, iframe injection via blob URL + import map, Monaco split-view | | `src/styles/` | Refactored `src/style.scss` → `src/styles/index.css` + `src/styles/style.scss` (Tailwind v4) | | `package.json` | Removed `@codesandbox/sandpack-client`; added `esbuild-wasm ^0.25.10`, `monaco-editor ^0.54.0`, `@monaco-editor/loader ^1.5.0`, Tailwind v4 + `@tailwindcss/vite`; moved `typecomposer-plugin` to `devDependencies`; bumped `typecomposer` to `^0.1.56` | | `vite.config.ts` | Renamed from `.js`, added `tailwindcss()` plugin, `base: "/"` (browser-history routing), scss preprocessor config | ### TypeComposer Component Registration TypeComposer components (`DivElement`, `VBox`, `HBox`, etc.) are Web Components that must be registered via `customElements.define()` before instantiation. The playground injects an import map pointing `typecomposer` at `esm.sh/typecomposer`, which makes the CDN-loaded classes available to user code. `typecomposer` is marked `external` in esbuild so imports pass through to the import map. ### Merge Conflict Resolution (vs PR #15) The original Copilot branch diverged from `main` after `main` received SEO/routing commits (#31, #32). This PR adds one merge commit that: 1. Bumps `typecomposer` `^0.1.54` → `^0.1.56` 2. Moves `typecomposer-plugin` to `devDependencies` 3. Fixes `vite.config.ts` `base: "./"` → `base: "/"` (required for browser-history routing from main) 4. Adds `scss: { api: "modern-compiler" }` preprocessor config from main 5. Regenerates `package-lock.json` --- ## Build result ``` ✓ 3452 modules transformed. ✓ built in 15.16s ``` --- ## Acceptance criteria checklist - [x] Remove `loadSandpackClient` and `bundlerURL` dependency - [x] Integrate `esbuild-wasm` to compile TypeScript in the browser - [x] Preserve `files` object / multi-file compilation - [x] `IFrameElement` executes compiled JS via blob URL in sandboxed iframe - [x] Compilation and runtime errors displayed clearly - [x] Current layout/styles maintained - [x] TypeComposer Web Components registered before user code runs (import map + CDN) Closes#14 Supersedes #15 (conflict-free replacement) cc @zico15@joaodibba
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed and why
SEO/indexability improvements based on Gonçalo's audit, as requested by João. Highest-value static wins with no new npm dependencies, no unrelated churn, and no conflict with #30 (already merged).
Architecture findings (pre-PR)
history: "hash"— all docs URLs are#/docs/…public/.htaccessalready rewrites all paths →index.html✓public/verbatim todist/✓index.htmlhad generic OG/Twitter tags but no canonical, no JSON-LD, no robots.txt, no sitemap, no per-page titlesdocs/skillsroute is live ✓try_files/ Apache rewrite for every/docs/*path; .htaccess is present but hosting needs confirmationChanges
public/robots.txtSitemap:directive pointing to sitemappublic/sitemap.xml<url>entries for every registered route (home, all docs pages, playground)public/llms.txtindex.html<link rel="canonical">→ root URL; two JSON-LD blocks (WebSite + SoftwareApplication); improved title/description copy ("zero-HTML" framing)src/utils/seo.tsupdatePageMeta(pathname)with per-route<title>+<meta name="description">map (29 routes)src/views/elements/Base.tsupdatePageMeta(Router.pathname)inBaseViewconstructor — fires on every docs page navigationWhat was deferred / not included
/docs/*URLs before enablinghistory: "browser"in the router — wrong call without server verification; the.htaccessis present but needs testingValidation
Build confirms all 3 static files are copied to
dist/(robots.txt,sitemap.xml,llms.txt),index.htmlcontains canonical and JSON-LD, andseo.tsis tree-shaken cleanly into the bundle.Diff: 6 files — 430 insertions, 9 deletions (index.html copy improvements).
Reviewer
@zico15