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
8 changes: 6 additions & 2 deletions apps/website/scripts/check-style-migration.mts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,12 @@ function normProp(p: string): string {
return p.replace(/[A-Z]/g, (c) => '-' + c.toLowerCase());
}
function normValue(prop: string, raw: string): string {
let v = raw.trim().replace(/^['"`]|['"`]$/g, '');
const tok = v.match(/^tokens\.([a-zA-Z.]+)$/);
let v = raw.trim();
// Strip quotes only when one pair wraps the WHOLE value; stripping a lone
// leading quote mangled internally-quoted font stacks ('"X", monospace').
const q = v[0];
if ((q === '"' || q === "'" || q === '`') && v.endsWith(q)) v = v.slice(1, -1);
const tok = v.match(/^tokens\.([a-zA-Z0-9.]+)$/);
if (tok) v = tokenValue(tok[1]) ?? v;
v = v.replace(/var\((--[a-z0-9-]+)\)/g, (_, name) => cssVars.get(name) ?? _);
if (/^-?\d+(\.\d+)?$/.test(v) && !UNITLESS.has(prop)) v = `${v}px`;
Expand Down
79 changes: 23 additions & 56 deletions apps/website/src/app/about/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: MIT
import Link from 'next/link';
import { tokens } from '@threadplane/design-tokens';
import { Container } from '../../components/ui/Container';
import { Section } from '../../components/ui/Section';
import { Eyebrow } from '../../components/ui/Eyebrow';
Expand Down Expand Up @@ -31,68 +30,36 @@ export const metadata = createPageMetadata({
type: 'website',
});

const bodyStyle = {
fontFamily: tokens.typography.bodyLg.family,
fontSize: tokens.typography.bodyLg.size,
lineHeight: tokens.typography.bodyLg.line,
color: tokens.colors.textSecondary,
margin: 0,
marginBottom: 16,
maxWidth: '60ch',
} as const;

const headingStyle = {
fontFamily: tokens.typography.h2.family,
fontSize: tokens.typography.h2.size,
color: tokens.colors.textPrimary,
margin: 0,
marginBottom: 12,
} as const;

const linkStyle = { color: tokens.colors.accent } as const;

export default function AboutPage() {
return (
<>
<JsonLd data={aboutPageJsonLd(author)} />

<Section surface="canvas" ariaLabelledBy="about-heading">
<Container>
<div style={{ maxWidth: 720 }}>
<Eyebrow tone="accent" style={{ marginBottom: 16 }}>About</Eyebrow>
<h1
id="about-heading"
style={{
fontFamily: tokens.typography.h1.family,
fontSize: tokens.typography.h1.size,
lineHeight: tokens.typography.h1.line,
fontWeight: 700,
color: tokens.colors.textPrimary,
margin: 0,
marginBottom: 16,
letterSpacing: '-0.02em',
}}
>
<div className="about-section-inner">
<Eyebrow tone="accent" className="about-eyebrow-spaced">About</Eyebrow>
<h1 id="about-heading" className="about-h1">
Who writes Threadplane
</h1>
<p style={bodyStyle}>{ABOUT_INTRO}</p>
<p className="about-body">{ABOUT_INTRO}</p>

<h2 style={{ ...headingStyle, marginTop: 32 }}>{ABOUT_HISTORY_HEADING}</h2>
<h2 className="about-h2 about-h2-spaced">{ABOUT_HISTORY_HEADING}</h2>
{ABOUT_HISTORY.map((paragraph) => (
<p key={paragraph} style={bodyStyle}>
<p key={paragraph} className="about-body">
{paragraph}
</p>
))}

<p style={bodyStyle}>{ABOUT_PRINCIPLES}</p>
<p style={bodyStyle}>{ABOUT_PERSONAL}</p>
<p className="about-body">{ABOUT_PRINCIPLES}</p>
<p className="about-body">{ABOUT_PERSONAL}</p>

<p style={{ ...bodyStyle, marginBottom: 0 }}>
<p className="about-body about-body-last">
<a
href={`https://github.com/${author.github}`}
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
className="about-link"
>
github.com/{author.github}
</a>
Expand All @@ -103,12 +70,12 @@ export default function AboutPage() {

<Section surface="tinted">
<Container>
<div style={{ maxWidth: 720 }}>
<h2 style={headingStyle}>What Threadplane is</h2>
<p style={bodyStyle}>{LONG_SUBHEAD}</p>
<p style={{ ...bodyStyle, marginBottom: 0 }}>
<div className="about-section-inner">
<h2 className="about-h2">What Threadplane is</h2>
<p className="about-body">{LONG_SUBHEAD}</p>
<p className="about-body about-body-last">
The source is public at{' '}
<a href={REPOSITORY_URL} target="_blank" rel="noopener noreferrer" style={linkStyle}>
<a href={REPOSITORY_URL} target="_blank" rel="noopener noreferrer" className="about-link">
github.com/cacheplane/angular-agent-framework
</a>
.
Expand All @@ -119,23 +86,23 @@ export default function AboutPage() {

<Section surface="canvas">
<Container>
<div style={{ maxWidth: 720 }}>
<h2 style={headingStyle}>How it is licensed</h2>
<p style={bodyStyle}>
<div className="about-section-inner">
<h2 className="about-h2">How it is licensed</h2>
<p className="about-body">
<code>@threadplane/chat</code> is free for noncommercial use under PolyForm
Noncommercial 1.0.0; commercial production use requires a Threadplane Commercial
license. The other libraries are MIT. The{' '}
<Link href="/docs/licensing" style={linkStyle}>
<Link href="/docs/licensing" className="about-link">
licensing docs
</Link>{' '}
and the <Link href="/pricing" style={linkStyle}>pricing page</Link> have the details.
and the <Link href="/pricing" className="about-link">pricing page</Link> have the details.
</p>
<p style={{ ...bodyStyle, marginBottom: 0 }}>
<p className="about-body about-body-last">
Questions about a specific build go to{' '}
<Link href="/contact" style={linkStyle}>
<Link href="/contact" className="about-link">
contact
</Link>
; ongoing writing is on the <Link href="/blog" style={linkStyle}>blog</Link>.
; ongoing writing is on the <Link href="/blog" className="about-link">blog</Link>.
</p>
</div>
</Container>
Expand Down
65 changes: 11 additions & 54 deletions apps/website/src/app/ag-ui/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import Link from 'next/link';
import { tokens } from '@threadplane/design-tokens';
import { Container } from '../../components/ui/Container';
import { Section } from '../../components/ui/Section';
import { Eyebrow } from '../../components/ui/Eyebrow';
Expand All @@ -24,61 +23,29 @@ export default async function AgUiPage() {
{/* Hero */}
<Section surface="canvas" ariaLabelledBy="ag-ui-hero-heading">
<Container>
<div style={{ maxWidth: 820, margin: '0 auto', textAlign: 'center' }}>
<Eyebrow tone="accent" style={{ marginBottom: 16 }}>@threadplane/ag-ui</Eyebrow>
<h1
id="ag-ui-hero-heading"
style={{
fontFamily: tokens.typography.h1.family,
fontSize: tokens.typography.h1.size,
lineHeight: tokens.typography.h1.line,
fontWeight: 700,
color: tokens.colors.textPrimary,
margin: 0,
marginBottom: 24,
letterSpacing: '-0.02em',
}}
>
<div className="ag-ui-page-hero-inner">
<Eyebrow tone="accent" className="ag-ui-page-eyebrow-spaced">@threadplane/ag-ui</Eyebrow>
<h1 id="ag-ui-hero-heading" className="ag-ui-page-h1">
One adapter. Eight backends.
</h1>
<p
style={{
fontFamily: tokens.typography.bodyLg.family,
fontSize: tokens.typography.bodyLg.size,
lineHeight: tokens.typography.bodyLg.line,
color: tokens.colors.textSecondary,
margin: '0 auto 32px',
maxWidth: 680,
}}
>
<p className="ag-ui-page-hero-subtitle">
Build an Angular agent UI on any AG-UI-compatible runtime — CrewAI, Mastra, Microsoft Agent Framework, AG2, Pydantic AI, AWS Strands, CopilotKit, or LangGraph fronted by AG-UI. Same Agent contract and chat surface; runtime-specific history and checkpoint behavior stays with the backend.
</p>
<div style={{ display: 'flex', justifyContent: 'center', gap: 12, flexWrap: 'wrap', marginBottom: 12 }}>
<div className="ag-ui-page-hero-buttons">
<Button variant="primary" size="lg" href="/docs/ag-ui/getting-started/quickstart">Get started</Button>
<Button variant="secondary" size="lg" href="https://github.com/cacheplane/angular-agent-framework" target="_blank" rel="noopener noreferrer">View source</Button>
</div>
<p style={{ fontSize: 13, color: tokens.colors.textMuted, marginBottom: 20 }}>
Talking to LangGraph Platform directly? See <a href="/docs/choosing-an-adapter" style={{ color: tokens.colors.accent }}>Choosing an adapter</a>.
<p className="ag-ui-page-adapter-note">
Talking to LangGraph Platform directly? See <a href="/docs/choosing-an-adapter" className="ag-ui-page-adapter-link">Choosing an adapter</a>.
</p>
<div style={{ display: 'flex', justifyContent: 'center', gap: 8, flexWrap: 'wrap', marginBottom: 24 }}>
<div className="ag-ui-page-hero-pills">
<Pill variant="accent">MIT</Pill>
<Pill variant="angular">Angular 20+</Pill>
<Pill variant="neutral">AG-UI protocol</Pill>
</div>
<p
style={{
fontFamily: tokens.typography.fontSans,
fontSize: 13,
color: tokens.colors.textMuted,
margin: '0 auto',
maxWidth: 520,
}}
>
<p className="ag-ui-page-langgraph-note">
Already on LangGraph?{' '}
<Link
href="/langgraph"
style={{ color: tokens.colors.accent, textDecoration: 'none', fontWeight: 600 }}
>
<Link href="/langgraph" className="ag-ui-page-langgraph-link">
See @threadplane/langgraph
</Link>{' '}
for native streaming, checkpoints, and the typed LangGraph SDK path.
Expand Down Expand Up @@ -127,17 +94,7 @@ export default async function AgUiPage() {
visualLeft
visual={
<BrowserFrame url="app.config.ts" elevation="md">
<pre style={{
margin: 0,
padding: '20px 22px',
background: '#1a1b26',
color: '#a9b1d6',
fontFamily: tokens.typography.fontMono,
fontSize: 13,
lineHeight: 1.6,
minHeight: 320,
overflow: 'auto',
}}>
<pre className="ag-ui-page-code-pre">
{`import { provideAgent, injectAgent } from '@threadplane/ag-ui';
import { ChatComponent } from '@threadplane/chat';

Expand Down
45 changes: 8 additions & 37 deletions apps/website/src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Metadata } from 'next';
import { notFound } from 'next/navigation';
import { tokens } from '@threadplane/design-tokens';
import { MdxRenderer } from '../../../components/docs/MdxRenderer';
import { DocsTOC } from '../../../components/docs/DocsTOC';
import { AuthorByline } from '../../../components/blog/AuthorByline';
Expand Down Expand Up @@ -88,50 +87,22 @@ export default async function BlogPostPage({ params }: Params) {
]);

return (
<div style={{ paddingTop: 80, background: tokens.surfaces.canvas, minHeight: '100vh' }}>
<div className="blog-post-page">
{postData ? <JsonLd data={postData} /> : null}
<JsonLd data={breadcrumbs} />
<div
style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'flex-start',
maxWidth: 1280,
margin: '0 auto',
gap: 0,
}}
>
<article style={{ width: '100%', maxWidth: 768, padding: '64px 24px', flexShrink: 0 }}>
<header style={{ marginBottom: 48 }}>
<Eyebrow tone="accent" style={{ marginBottom: 24 }}>
<div className="blog-post-layout">
<article className="blog-post-article">
<header className="blog-post-header">
<Eyebrow tone="accent" className="blog-post-eyebrow-spaced">
{primaryTag} · {formatPostDate(post.frontmatter.date)} · {minutes} min read
</Eyebrow>
<h1
style={{
fontFamily: tokens.typography.h1.family,
fontSize: tokens.typography.h1.size,
lineHeight: tokens.typography.h1.line,
fontWeight: 700,
letterSpacing: '-0.02em',
color: tokens.colors.textPrimary,
margin: '0 0 24px',
}}
>
<h1 className="blog-post-h1">
{post.frontmatter.title}
</h1>
<p
style={{
fontFamily: tokens.typography.bodyLg.family,
fontSize: tokens.typography.bodyLg.size,
lineHeight: tokens.typography.bodyLg.line,
color: tokens.colors.textSecondary,
margin: '0 0 32px',
maxWidth: '60ch',
}}
>
<p className="blog-post-subtitle">
{post.frontmatter.description}
</p>
<div style={{ display: 'flex', alignItems: 'center', gap: 20, flexWrap: 'wrap', marginBottom: 20 }}>
<div className="blog-post-byline-row">
<AuthorByline author={author} />
</div>
{post.frontmatter.tags && post.frontmatter.tags.length > 0 ? (
Expand Down
Loading
Loading