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
10 changes: 10 additions & 0 deletions apps/website/e2e/website.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,16 @@ test('docs pages render canonical and social metadata', async ({ page }) => {
);
});

test('marketing pages render canonical and page-specific social URLs', async ({ page }) => {
for (const route of ['/', '/angular', '/render', '/chat', '/pricing', '/contact', '/pilot-to-prod', '/solutions']) {
await page.goto(route);
const expectedUrl = route === '/' ? 'https://threadplane.ai' : `https://threadplane.ai${route}`;

await expect(page.locator('link[rel="canonical"]'), `${route} canonical`).toHaveAttribute('href', expectedUrl);
await expect(page.locator('meta[property="og:url"]'), `${route} og:url`).toHaveAttribute('content', expectedUrl);
}
});

test('representative docs pages do not create page-level horizontal overflow', async ({ page }) => {
const routes = [
'/docs',
Expand Down
7 changes: 5 additions & 2 deletions apps/website/src/app/angular/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import { FeatureBlock } from '../../components/landing/FeatureBlock';
import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { AngularCodeShowcase } from '../../components/landing/angular/AngularCodeShowcase';
import { createPageMetadata } from '../../lib/site-metadata';

export const metadata = {
export const metadata = createPageMetadata({
title: '@ngaf/langgraph — Agent Streaming for Angular',
description: 'Ship LangGraph agents in Angular. Signal-native streaming, thread persistence, interrupts, and deterministic testing.',
};
pathname: '/angular',
type: 'website',
});

export default async function AngularPage() {
return (
Expand Down
7 changes: 5 additions & 2 deletions apps/website/src/app/chat/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import { FeatureBlock } from '../../components/landing/FeatureBlock';
import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { ChatLandingCodeShowcase } from '../../components/landing/chat-landing/ChatLandingCodeShowcase';
import { createPageMetadata } from '../../lib/site-metadata';

export const metadata = {
export const metadata = createPageMetadata({
title: '@ngaf/chat — Batteries-Included Agent Chat for Angular',
description: 'Production agent chat UI in days, not sprints. Built on Vercel json-render and Google A2UI specs.',
};
pathname: '/chat',
type: 'website',
});

export default async function ChatPage() {
return (
Expand Down
16 changes: 6 additions & 10 deletions apps/website/src/app/contact/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// SPDX-License-Identifier: MIT
import type { Metadata } from 'next';
import React, { Suspense } from 'react';
import { tokens } from '@ngaf/design-tokens';
import { Container } from '../../components/ui/Container';
Expand All @@ -9,17 +8,14 @@ import { ContactForm } from '../../components/contact/ContactForm';
import { GitHubStarsPill } from '../../components/contact/GitHubStarsPill';
import { SlaCard } from '../../components/contact/SlaCard';
import { AltChannelRow } from '../../components/contact/AltChannelRow';
import { createPageMetadata } from '../../lib/site-metadata';

export const metadata: Metadata = {
export const metadata = createPageMetadata({
title: 'Talk to an engineer — ThreadPlane',
description:
"Tell us what you're shipping. We'll reply within one business day — usually with code, not a calendar invite.",
openGraph: {
title: 'Talk to an engineer — ThreadPlane',
description: "Tell us what you're shipping. We'll reply within one business day.",
type: 'website',
},
};
description: "Tell us what you're shipping. We'll reply within one business day — usually with code, not a calendar invite.",
pathname: '/contact',
type: 'website',
});

export default function ContactPage() {
return (
Expand Down
8 changes: 8 additions & 0 deletions apps/website/src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ import { Promises } from '../components/landing/Promises';
import { HomeFAQ } from '../components/landing/HomeFAQ';
import { FinalCTA } from '../components/landing/FinalCTA';
import { tokens } from '@ngaf/design-tokens';
import { createPageMetadata } from '../lib/site-metadata';

export const metadata = createPageMetadata({
title: 'Agent UI for Angular — Signal-Native Streaming for Angular + LangGraph',
description: 'The enterprise Angular agent framework for LangChain. Signal-native streaming, thread persistence, and production patterns for Angular 20+.',
pathname: '/',
type: 'website',
});

export default async function HomePage() {
return (
Expand Down
7 changes: 5 additions & 2 deletions apps/website/src/app/pilot-to-prod/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ import { BrowserFrame } from '../../components/ui/BrowserFrame';
import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { Promises } from '../../components/landing/Promises';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { createPageMetadata } from '../../lib/site-metadata';

export const metadata = {
export const metadata = createPageMetadata({
title: 'Pilot to Production — Agent UI for Angular',
description: 'Close the last-mile gap. The 3-month pilot engagement is included with every app deployment license. We work alongside your Angular team to ship your first agent to production.',
};
pathname: '/pilot-to-prod',
type: 'website',
});

export default function PilotToProdPage() {
return (
Expand Down
7 changes: 5 additions & 2 deletions apps/website/src/app/pricing/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ import { CompareTable } from '../../components/pricing/CompareTable';
import { CompatibilityMatrix } from '../../components/pricing/CompatibilityMatrix';
import { LeadForm } from '../../components/pricing/LeadForm';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { createPageMetadata } from '../../lib/site-metadata';

export const metadata = {
export const metadata = createPageMetadata({
title: 'Pricing — Agent UI for Angular',
description: 'Simple, transparent pricing. MIT-licensed libraries are free forever. Enterprise contracts available.',
};
pathname: '/pricing',
type: 'website',
});

export default function PricingPage() {
return (
Expand Down
7 changes: 5 additions & 2 deletions apps/website/src/app/render/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@ import { FeatureBlock } from '../../components/landing/FeatureBlock';
import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { RenderCodeShowcase } from '../../components/landing/render/RenderCodeShowcase';
import { createPageMetadata } from '../../lib/site-metadata';

export const metadata = {
export const metadata = createPageMetadata({
title: '@ngaf/render — Generative UI for Angular',
description: 'Agents that render UI without coupling to your frontend. Built on Vercel json-render spec.',
};
pathname: '/render',
type: 'website',
});

export default async function RenderPage() {
return (
Expand Down
7 changes: 5 additions & 2 deletions apps/website/src/app/solutions/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import { Pill } from '../../components/ui/Pill';
import { WhitePaperBlock } from '../../components/landing/WhitePaperBlock';
import { FinalCTA } from '../../components/landing/FinalCTA';
import { SOLUTIONS } from '../../lib/solutions-data';
import { createPageMetadata } from '../../lib/site-metadata';

export const metadata = {
export const metadata = createPageMetadata({
title: 'Solutions — Agent UI for Angular',
description: 'See how Agent UI for Angular solves enterprise challenges — compliance, analytics, and customer support.',
};
pathname: '/solutions',
type: 'website',
});

export default function SolutionsIndexPage() {
return (
Expand Down