diff --git a/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx b/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx index cb887f902..02271cc93 100644 --- a/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx +++ b/apps/website/src/app/docs/[library]/[section]/[slug]/page.tsx @@ -1,6 +1,5 @@ import type { Metadata } from 'next'; import { notFound } from 'next/navigation'; -import { tokens } from '@threadplane/design-tokens'; import { DocsSidebar } from '../../../../../components/docs/DocsSidebar'; import { MdxRenderer } from '../../../../../components/docs/MdxRenderer'; import { DocsSearch } from '../../../../../components/docs/DocsSearch'; @@ -86,18 +85,12 @@ export default async function DocsPage({ params }: DocsRouteProps) { ]); return ( -
+
-
+
diff --git a/apps/website/src/app/docs/choosing-an-adapter/page.tsx b/apps/website/src/app/docs/choosing-an-adapter/page.tsx index 8b622e605..ee0b161d2 100644 --- a/apps/website/src/app/docs/choosing-an-adapter/page.tsx +++ b/apps/website/src/app/docs/choosing-an-adapter/page.tsx @@ -71,8 +71,8 @@ export default function ChoosingAnAdapterPage() { <>
-
- +
+ Documentation
@@ -83,10 +83,9 @@ export default function ChoosingAnAdapterPage() {
-
- Documentation -

+
+ Documentation +

Licensing

-

+

How the Threadplane licensing model works, who needs a paid license, and how to install your license token.

@@ -121,35 +32,35 @@ export default function LicensingPage() {
-
-

The model

-

+

+

The model

+

Threadplane is a suite of libraries. Most are{' '} - MIT-licensed and free for any use, - commercial or not. Only @threadplane/chat is + MIT-licensed and free for any use, + commercial or not. Only @threadplane/chat is dual-licensed.

-

- @threadplane/chat is source-available under{' '} - PolyForm Noncommercial 1.0.0 for free - noncommercial use, or a Threadplane Commercial license{' '} +

+ @threadplane/chat is source-available under{' '} + PolyForm Noncommercial 1.0.0 for free + noncommercial use, or a Threadplane Commercial license{' '} for production use inside a for-profit context. The same source ships under both — you don't get a different build.

-

Do you need a paid license?

-

- You need a Threadplane Commercial license if you use @threadplane/chat{' '} +

Do you need a paid license?

+

+ You need a Threadplane Commercial license if you use @threadplane/chat{' '} in any of:

-
    +
    • A commercial product or SaaS
    • An internal business tool inside a for-profit company
    • An agency deliverable or paid client project
    • Any application operated by or for a for-profit entity
    -

    You do not need a paid license for:

    -
      +

      You do not need a paid license for:

      +
      • Personal, hobby, student, academic, or nonprofit projects
      • Public demos and tutorials
      • Open-source applications released under an OSI-approved license
      • @@ -161,14 +72,14 @@ export default function LicensingPage() {
        -
        -

        Install your license

        -

        +

        +

        Install your license

        +

        After purchase, Threadplane emails a signed license token to the address on your receipt. Paste it - into your app's provideChat(){' '} + into your app's provideChat(){' '} configuration:

        -
        {`// app.config.ts
        +            
        {`// app.config.ts
         import { ApplicationConfig } from '@angular/core';
         import { provideChat } from '@threadplane/chat';
         
        @@ -179,13 +90,13 @@ export const appConfig: ApplicationConfig = {
             }),
           ],
         };`}
        -

        +

        The library verifies the token's Ed25519 signature on boot. The check is{' '} - advisory-only: a missing, expired, or - tampered token logs a console.warn but + advisory-only: a missing, expired, or + tampered token logs a console.warn but never blocks rendering. Verification is fully offline; no calls leave your app at runtime.

        -

        +

        The token is safe to commit to a private repository, or to read from a build-time environment variable for public repos. Public-repo demos are exempt from the commercial-use definition, but if your public repo backs a commercial product, the deployed bundle does need a license. @@ -196,41 +107,41 @@ export const appConfig: ApplicationConfig = {

        -
        -

        Tier scoping

        -

        +

        +

        Tier scoping

        +

        Pick the tier that matches how you'll deploy. All paid tiers grant the same{' '} Threadplane Commercial license; the difference is the scope of use and the number of seats.

        -
        - +
        +
        - - - + + + - - - + + + - - - + + + - - - + + +
        TierDevelopersBest forTierDevelopersBest for
        Developer Seat — $29/dev/mo or $299/dev/yrPer seatSolo devs, growing teamsDeveloper Seat — $29/dev/mo or $299/dev/yrPer seatSolo devs, growing teams
        Team — $149/mo or $1,495/yr5 seats includedSmall teams that want a single SKU and renewalTeam — $149/mo or $1,495/yr5 seats includedSmall teams that want a single SKU and renewal
        Enterprise — from $4,000/moCustomSLA, security review, Pilot-to-Prod engagement, Slack ConnectEnterprise — from $4,000/moCustomSLA, security review, Pilot-to-Prod engagement, Slack Connect
        -

        +

        Paid tiers are recurring subscriptions. Annual saves ~15% vs monthly. Cancel anytime — the license stays valid through the end of the current paid period.

        @@ -240,47 +151,36 @@ export const appConfig: ApplicationConfig = {
        -
        -

        Evaluation

        -

        - You may use @threadplane/chat commercially - for 30 calendar days from your first +

        +

        Evaluation

        +

        + You may use @threadplane/chat commercially + for 30 calendar days from your first commercial use as a good-faith evaluation. There is no telemetry, no registration, no email check — we trust you to count the days. After 30 days you must either purchase a license or stop the commercial use.

        -

        Refunds

        -

        +

        Refunds

        +

        If you refund a license through Stripe, the token is revoked automatically and we email a confirmation. The verification check warns on boot. There's no clawback of the source code you already have — everything is source-available under PolyForm Noncommercial by default.

        -

        Questions

        -

        +

        Questions

        +

        Volume pricing, multi-app licensing, audit clauses, custom terms — any of those, reach out and we'll work it out.

        -
        +
        - + Pricing FAQ →
        diff --git a/apps/website/src/app/docs/page.tsx b/apps/website/src/app/docs/page.tsx index 67a764ef4..066baa729 100644 --- a/apps/website/src/app/docs/page.tsx +++ b/apps/website/src/app/docs/page.tsx @@ -1,6 +1,5 @@ import type { ReactNode } from 'react'; 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'; @@ -136,181 +135,36 @@ function MiddlewareGlyph() { const GLYPHS = { key: KeyGlyph, middleware: MiddlewareGlyph, pulse: PulseGlyph } as const; -const stepLabelStyle = { - fontFamily: tokens.typography.eyebrow.family, - fontSize: tokens.typography.eyebrow.size, - fontWeight: tokens.typography.eyebrow.weight, - letterSpacing: tokens.typography.eyebrow.letterSpacing, - textTransform: tokens.typography.eyebrow.transform, - lineHeight: tokens.typography.eyebrow.line, - color: tokens.colors.textMuted, - margin: 0, - marginBottom: 16, - display: 'flex', - alignItems: 'center', - gap: 10, -} as const; - -const stepBadgeStyle = { - width: 20, - height: 20, - borderRadius: tokens.radius.full, - background: tokens.colors.accent, - color: '#fff', - fontSize: 12, - fontWeight: 700, - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - flex: '0 0 auto', -} as const; - function StepLabel({ id, step, children }: { id: string; step?: number; children: ReactNode }) { return ( -

        +

        {step != null ? ( - + ) : null} {children}

        ); } -const logoChipStyle = { - width: 30, - height: 30, - borderRadius: tokens.radius.md, - background: tokens.surfaces.surface, - border: `1px solid ${tokens.surfaces.border}`, - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - flex: '0 0 auto', -} as const; - -const glyphChipStyle = { - borderRadius: tokens.radius.md, - background: tokens.colors.accentSurface, - border: `1px solid ${tokens.colors.accentBorder}`, - color: tokens.colors.accent, - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - flex: '0 0 auto', -} as const; - function LogoChip({ src }: { src: string }) { return ( - + ); } function GlyphChip({ size, children }: { size: number; children: ReactNode }) { - return {children}; + return {children}; } -const cardHeaderStyle = { - display: 'flex', - alignItems: 'center', - gap: 12, - marginBottom: 12, -} as const; - -const cardTitleStyle = { - fontFamily: tokens.typography.h3.family, - fontSize: 18, - lineHeight: 1.3, - fontWeight: 600, - color: tokens.colors.textPrimary, - margin: 0, -} as const; - -const attributionStyle = { - fontFamily: tokens.typography.fontMono, - fontSize: 10, - lineHeight: 1.4, - textTransform: 'uppercase', - letterSpacing: '0.08em', - color: tokens.colors.textMuted, - marginTop: 2, -} as const; - -const cardBlurbStyle = { - fontFamily: tokens.typography.body.family, - fontSize: tokens.typography.body.size, - lineHeight: tokens.typography.body.line, - color: tokens.colors.textSecondary, - margin: 0, -} as const; - -const ctaStyle = { - fontFamily: tokens.typography.fontSans, - fontSize: 14, - fontWeight: 600, - color: tokens.colors.accent, -} as const; - -const snippetRowStyle = { - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - gap: 8, - background: tokens.surfaces.surface, - border: `1px solid ${tokens.surfaces.border}`, - borderRadius: tokens.radius.md, - padding: '5px 6px 5px 12px', - margin: '14px 0 16px', -} as const; - -const snippetCodeStyle = { - fontFamily: tokens.typography.fontMono, - fontSize: 13, - color: tokens.colors.textSecondary, - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'nowrap', -} as const; - -const gridStyle = { - display: 'grid', - gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', - gap: 16, -} as const; - -const helperStyle = { - fontFamily: tokens.typography.body.family, - fontSize: 14, - color: tokens.colors.textSecondary, - margin: 0, - marginTop: 16, - textAlign: 'center', -} as const; - -const helperLinkStyle = { - color: tokens.colors.accent, - fontWeight: 600, -} as const; - -const fillHeightStyle = { - height: '100%', -} as const; - -const dividerStyle = { - height: 1, - background: tokens.surfaces.border, - border: 'none', - margin: '0 0 40px', -} as const; - export default function DocsLandingPage() { return ( <> @@ -318,35 +172,14 @@ export default function DocsLandingPage() { {/* Hero */}
        -
        - +
        + Documentation -

        +

        Start building with Threadplane

        -

        +

        Streaming agent interfaces with runtime adapters, a shared Agent contract, and a drop-in chat surface. Most packages are MIT; @threadplane/chat is dual-licensed for noncommercial/evaluation and commercial production use. @@ -359,34 +192,34 @@ export default function DocsLandingPage() {

        Pick your backend -
        +
        {BACKENDS.map((b) => ( - - -
        + + +
        -

        {b.title}

        -
        {b.attribution}
        +

        {b.title}

        +
        {b.attribution}
        -

        {b.blurb}

        -
        - {b.install} +

        {b.blurb}

        +
        + {b.install}
        - Adapter quickstart → + Adapter quickstart → ))}
        -

        +

        Not sure which to use?{' '} - + Choosing an adapter → {' '}Want the drop-in UI first?{' '} - + Chat quickstart →

        @@ -396,28 +229,28 @@ export default function DocsLandingPage() { {/* Step 2 — generative UI */}
        -
        +
        Generative UI -
        +
        {GENERATIVE_UI.map((g) => ( - - -
        + + +
        -

        {g.title}

        -
        {g.attribution}
        +

        {g.title}

        +
        {g.attribution}
        -

        {g.blurb}

        - Get started → +

        {g.blurb}

        + Get started →
        ))}
        -

        +

        Which fits my use case?{' '} - + json-render vs A2UI →

        @@ -427,18 +260,18 @@ export default function DocsLandingPage() { {/* Step 3 — chat */}
        -
        +
        Chat UI - - -
        + + +
        -

        Chat

        -
        Threadplane
        +

        Chat

        +
        Threadplane
        -

        +

        Drop-in chat components — message list, input, streaming, tool calls, interrupts, subagents. Renders A2UI & json-render surfaces inline. @@ -451,19 +284,19 @@ export default function DocsLandingPage() { {/* Supporting libraries */}

        -
        +
        Supporting libraries -
        +
        {SUPPORTING.map((s) => { const Glyph = GLYPHS[s.glyph]; return ( - - -
        + + +
        -

        {s.title}

        -

        {s.blurb}

        +

        {s.title}

        +

        {s.blurb}

        @@ -477,33 +310,11 @@ export default function DocsLandingPage() { {/* Search prompt */}
        -
        -

        +
        +

        Looking for something specific?

        -

        +

        Press ⌘K to search the docs.

        diff --git a/apps/website/src/styles/pages.css b/apps/website/src/styles/pages.css index 55730ebf4..df170a587 100644 --- a/apps/website/src/styles/pages.css +++ b/apps/website/src/styles/pages.css @@ -8,3 +8,364 @@ * * Migration: docs/superpowers/plans/2026-08-29-inline-style-substrate-migration.md */ + +/* Licensing page — app/docs/licensing/page.tsx */ +.licensing-hero-inner { + max-width: 720px; +} +.licensing-eyebrow-spaced { + margin-bottom: 16px; +} +.licensing-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.02em; +} +.licensing-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0; + max-width: 60ch; +} +.licensing-section-inner { + max-width: 760px; +} +.licensing-section-inner-wide { + max-width: 900px; +} +.licensing-h2 { + font-family: var(--font-garamond); + font-size: var(--text-h2); + line-height: var(--text-h2--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.01em; +} +.licensing-h2-spaced { + margin-top: 32px; +} +.licensing-h3 { + font-family: var(--font-inter); + font-size: var(--text-h3); + line-height: var(--text-h3--line-height); + font-weight: 600; + color: var(--color-text-primary); + margin: 0; + margin-top: 24px; + margin-bottom: 8px; +} +.licensing-body { + font-family: var(--font-inter); + font-size: var(--text-body); + line-height: var(--text-body--line-height); + color: var(--color-text-secondary); + margin: 0 0 16px; + max-width: 64ch; +} +.licensing-list { + padding-left: 24px; +} +.licensing-strong { + color: var(--color-text-primary); +} +.licensing-code-inline { + /* raw font stack, not the `.family` var — see the font-exception note atop ui.css */ + font-family: "JetBrains Mono", monospace; +} +.licensing-code-block { + font-family: "JetBrains Mono", monospace; + font-size: 13px; + line-height: 1.6; + background: var(--color-surface-tinted); + border: 1px solid var(--color-border); + border-radius: 8px; + padding: 16px; + overflow: auto; + color: var(--color-text-primary); + margin: 0 0 16px; + white-space: pre; +} +.licensing-table-scroll { + overflow: auto; +} +.licensing-table { + width: 100%; + border-collapse: collapse; + font-family: var(--font-inter); + font-size: 14px; + color: var(--color-text-secondary); + margin: 0 0 24px; +} +.licensing-cell { + padding: 10px 12px; + border-bottom: 1px solid var(--color-border); + vertical-align: top; +} +.licensing-header-cell { + color: var(--color-text-primary); + font-weight: 600; + background: var(--color-surface-tinted); +} +.licensing-footnote { + font-size: 13px; + color: var(--color-text-muted); +} +.licensing-cta-row { + display: flex; + gap: 12px; + flex-wrap: wrap; + margin-top: 16px; +} +.licensing-faq-link { + display: inline-flex; + align-items: center; + font-family: var(--font-inter); + font-size: 14px; + color: var(--color-accent); + text-decoration: none; + padding: 8px 12px; +} + +/* Docs index page — app/docs/page.tsx */ +.docs-index-hero-inner { + max-width: 720px; +} +.docs-index-eyebrow-spaced { + margin-bottom: 16px; +} +.docs-index-h1 { + font-family: var(--font-garamond); + font-size: var(--text-h1); + line-height: var(--text-h1--line-height); + font-weight: 700; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 16px; + letter-spacing: -0.02em; +} +.docs-index-subtitle { + font-family: var(--font-inter); + font-size: var(--text-body-lg); + line-height: var(--text-body-lg--line-height); + color: var(--color-text-secondary); + margin: 0; + max-width: 52ch; +} +.docs-index-step-label { + font-family: var(--font-mono); + font-size: var(--text-eyebrow); + font-weight: var(--text-eyebrow--font-weight); + letter-spacing: var(--text-eyebrow--letter-spacing); + text-transform: uppercase; + line-height: var(--text-eyebrow--line-height); + color: var(--color-text-muted); + margin: 0; + margin-bottom: 16px; + display: flex; + align-items: center; + gap: 10px; +} +.docs-index-step-badge { + width: 20px; + height: 20px; + border-radius: var(--radius-full); + background: var(--color-accent); + color: #fff; + font-size: 12px; + font-weight: 700; + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; +} +.docs-index-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); + gap: 16px; +} +.docs-index-card-link { + text-decoration: none; +} +.docs-index-fill-height { + height: 100%; +} +.docs-index-card-header { + display: flex; + align-items: center; + gap: 12px; + margin-bottom: 12px; +} +/* Supporting-libraries card header omits the bottom margin in the source — + * a separate class rather than a shared one plus a zeroing modifier, since + * "cancel a shorthand's margin-bottom back to nothing" is not otherwise + * needed anywhere else in this file. */ +.docs-index-card-header-inline { + display: flex; + align-items: center; + gap: 12px; +} +.docs-index-logo-chip { + width: 30px; + height: 30px; + border-radius: var(--radius-md); + background: var(--color-surface); + border: 1px solid var(--color-border); + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; +} +.docs-index-logo-img { + width: 18px; + height: 18px; + object-fit: contain; +} +/* GlyphChip's width/height come from a `size: number` prop (unbounded) and + * stay inline; everything else in glyphChipStyle moves here. */ +.docs-index-glyph-chip { + border-radius: var(--radius-md); + background: var(--color-accent-surface); + border: 1px solid var(--color-accent-border); + color: var(--color-accent); + display: inline-flex; + align-items: center; + justify-content: center; + flex: 0 0 auto; +} +.docs-index-card-title { + font-family: var(--font-inter); + font-size: 18px; + line-height: 1.3; + font-weight: 600; + color: var(--color-text-primary); + margin: 0; +} +.docs-index-card-title-sm { + font-size: 16px; +} +.docs-index-attribution { + /* raw font stack, not the `.family` var — see the font-exception note atop ui.css */ + font-family: "JetBrains Mono", monospace; + font-size: 10px; + line-height: 1.4; + text-transform: uppercase; + letter-spacing: 0.08em; + color: var(--color-text-muted); + margin-top: 2px; +} +.docs-index-card-blurb { + font-family: var(--font-inter); + font-size: var(--text-body); + line-height: var(--text-body--line-height); + color: var(--color-text-secondary); + margin: 0; +} +.docs-index-card-blurb-sm { + font-size: 13px; + margin-top: 2px; +} +.docs-index-cta { + /* raw font stack, not the `.family` var — see the font-exception note atop ui.css */ + font-family: Inter, system-ui, sans-serif; + font-size: 14px; + font-weight: 600; + color: var(--color-accent); +} +.docs-index-cta-block { + display: inline-block; + margin-top: 14px; +} +.docs-index-snippet-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + background: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: 5px 6px 5px 12px; + margin: 14px 0 16px; +} +.docs-index-snippet-code { + /* raw font stack, not the `.family` var — see the font-exception note atop ui.css */ + font-family: "JetBrains Mono", monospace; + font-size: 13px; + color: var(--color-text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.docs-index-helper { + font-family: var(--font-inter); + font-size: 14px; + color: var(--color-text-secondary); + margin: 0; + margin-top: 16px; + text-align: center; +} +.docs-index-helper-link { + color: var(--color-accent); + font-weight: 600; +} +.docs-index-divider { + height: 1px; + background: var(--color-border); + border: none; + margin: 0 0 40px; +} +.docs-index-search-inner { + text-align: center; + max-width: 640px; + margin: 0 auto; +} +.docs-index-search-heading { + font-family: var(--font-inter); + font-size: 22px; + line-height: 1.3; + font-weight: 600; + color: var(--color-text-primary); + margin: 0; + margin-bottom: 12px; +} +.docs-index-search-copy { + font-family: var(--font-inter); + font-size: var(--text-body); + line-height: var(--text-body--line-height); + color: var(--color-text-secondary); + margin: 0; + display: inline-flex; + align-items: center; + gap: 8px; +} + +/* Choosing-an-adapter page — app/docs/choosing-an-adapter/page.tsx */ +.adapter-hero-inner { + max-width: 720px; +} +.adapter-eyebrow-spaced { + margin-bottom: 16px; +} +.adapter-article { + max-width: 760px; +} + +/* Docs shell — app/docs/[library]/[section]/[slug]/page.tsx + * paddingTop: 80 and the overflow-x-hidden utility are known defects + * (nav-height coupling) migrated VERBATIM here; a later project fixes them, + * not this migration. */ +.docs-shell-page { + background: var(--color-canvas); + padding-top: 80px; +} +.docs-shell-body { + background: var(--color-surface); +}