diff --git a/apps/web/src/app/changelog/layout.tsx b/apps/web/src/app/changelog/layout.tsx new file mode 100644 index 0000000..e21a6b1 --- /dev/null +++ b/apps/web/src/app/changelog/layout.tsx @@ -0,0 +1,16 @@ +import type { Metadata } from 'next'; + +export const metadata: Metadata = { + title: 'Changelog | TPMJS', + description: + 'Release history for all published TPMJS packages. Track new features, improvements, and bug fixes across our SDK and tools.', + openGraph: { + title: 'TPMJS Changelog', + description: + 'Release history for all published TPMJS packages. Track new features, improvements, and bug fixes across our SDK and tools.', + }, +}; + +export default function ChangelogLayout({ children }: { children: React.ReactNode }) { + return children; +} diff --git a/apps/web/src/app/changelog/page.tsx b/apps/web/src/app/changelog/page.tsx new file mode 100644 index 0000000..e9ca637 --- /dev/null +++ b/apps/web/src/app/changelog/page.tsx @@ -0,0 +1,317 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { AppFooter } from '../../components/AppFooter'; +import { AppHeader } from '../../components/AppHeader'; + +interface ChangelogEntry { + version: string; + type: 'major' | 'minor' | 'patch'; + changes: string[]; +} + +interface PackageChangelog { + name: string; + entries: ChangelogEntry[]; +} + +function parseChangelog(content: string, packageName: string): PackageChangelog { + const entries: ChangelogEntry[] = []; + const lines = content.split('\n'); + + let currentVersion: string | null = null; + let currentType: 'major' | 'minor' | 'patch' | null = null; + let currentChanges: string[] = []; + let currentChangeText = ''; + + for (const line of lines) { + // Match version headers like "## 0.1.3" + const versionMatch = line.match(/^## (\d+\.\d+\.\d+)/); + if (versionMatch) { + // Save previous entry if exists + if (currentVersion && currentType) { + if (currentChangeText.trim()) { + currentChanges.push(currentChangeText.trim()); + } + entries.push({ + version: currentVersion, + type: currentType, + changes: currentChanges, + }); + } + currentVersion = versionMatch[1] ?? null; + currentType = null; + currentChanges = []; + currentChangeText = ''; + continue; + } + + // Match change type headers + if (line.includes('### Major Changes')) { + currentType = 'major'; + continue; + } + if (line.includes('### Minor Changes')) { + currentType = 'minor'; + continue; + } + if (line.includes('### Patch Changes')) { + currentType = 'patch'; + continue; + } + + // Match change items (lines starting with -) + if (line.startsWith('- ') && currentVersion && currentType) { + if (currentChangeText.trim()) { + currentChanges.push(currentChangeText.trim()); + } + currentChangeText = line.slice(2); + continue; + } + + // Continuation of multi-line change + if (currentChangeText && line.trim() && !line.startsWith('#')) { + currentChangeText += `\n${line}`; + } + } + + // Don't forget the last entry + if (currentVersion && currentType) { + if (currentChangeText.trim()) { + currentChanges.push(currentChangeText.trim()); + } + entries.push({ + version: currentVersion, + type: currentType, + changes: currentChanges, + }); + } + + return { name: packageName, entries }; +} + +function getChangelogs(): { sdk: PackageChangelog[]; tools: PackageChangelog[] } { + const monorepoRoot = path.resolve(process.cwd(), '../..'); + + const sdkPackages = [ + { dir: 'packages/ui', name: '@tpmjs/ui' }, + { dir: 'packages/types', name: '@tpmjs/types' }, + { dir: 'packages/utils', name: '@tpmjs/utils' }, + { dir: 'packages/env', name: '@tpmjs/env' }, + ]; + + const toolPackages = [ + { dir: 'packages/tools/registrySearch', name: '@tpmjs/registrySearch' }, + { dir: 'packages/tools/registryExecute', name: '@tpmjs/registryExecute' }, + { dir: 'packages/tools/create-basic-tools', name: '@tpmjs/create-basic-tools' }, + { dir: 'packages/tools/hello', name: '@tpmjs/hello' }, + { dir: 'packages/tools/emoji-magic', name: '@tpmjs/emoji-magic' }, + { dir: 'packages/tools/markdown-formatter', name: '@tpmjs/markdown-formatter' }, + { dir: 'packages/tools/createBlogPost', name: '@tpmjs/createBlogPost' }, + ]; + + const sdk: PackageChangelog[] = []; + const tools: PackageChangelog[] = []; + + for (const pkg of sdkPackages) { + const changelogPath = path.join(monorepoRoot, pkg.dir, 'CHANGELOG.md'); + if (fs.existsSync(changelogPath)) { + const content = fs.readFileSync(changelogPath, 'utf-8'); + const changelog = parseChangelog(content, pkg.name); + if (changelog.entries.length > 0) { + sdk.push(changelog); + } + } + } + + for (const pkg of toolPackages) { + const changelogPath = path.join(monorepoRoot, pkg.dir, 'CHANGELOG.md'); + if (fs.existsSync(changelogPath)) { + const content = fs.readFileSync(changelogPath, 'utf-8'); + const changelog = parseChangelog(content, pkg.name); + if (changelog.entries.length > 0) { + tools.push(changelog); + } + } + } + + return { sdk, tools }; +} + +function VersionBadge({ type }: { type: 'major' | 'minor' | 'patch' }) { + const colors = { + major: 'bg-red-500/20 text-red-400 border-red-500/30', + minor: 'bg-blue-500/20 text-blue-400 border-blue-500/30', + patch: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30', + }; + + return ( + {type} + ); +} + +function ChangelogCard({ changelog }: { changelog: PackageChangelog }) { + const latestVersion = changelog.entries[0]?.version || '0.0.0'; + + return ( +
+
+
+

{changelog.name}

+ v{latestVersion} +
+
+ +
+ {changelog.entries.map((entry) => ( +
+
+ {entry.version} + +
+ +
    + {entry.changes.map((change, i) => ( +
  • + - + {change} +
  • + ))} +
+
+ ))} +
+
+ ); +} + +export default function ChangelogPage() { + const { sdk, tools } = getChangelogs(); + + return ( +
+ + +
+
+ {/* Header */} +
+

Changelog

+

+ Release history for all published TPMJS packages. Track new features, improvements, + and fixes across our SDK and tools. +

+
+ + {/* Legend */} +
+
+ + Breaking changes +
+
+ + New features +
+
+ + Bug fixes +
+
+ + {/* SDK Packages */} +
+

+ + + + SDK Packages +

+

+ Core packages for building and integrating with TPMJS. +

+ +
+ {sdk.map((changelog) => ( + + ))} +
+
+ + {/* Tool Packages */} +
+

+ + + + Tool Packages +

+

+ Official TPMJS tools available on npm. These serve as examples and utilities for the + registry. +

+ +
+ {tools.map((changelog) => ( + + ))} +
+
+ + {/* CTA */} +
+

Stay Updated

+

+ Follow the repository for the latest updates and releases. +

+ + + View on GitHub + +
+
+
+ + +
+ ); +} diff --git a/apps/web/src/app/docs/page.tsx b/apps/web/src/app/docs/page.tsx index 84363a0..e6f239b 100644 --- a/apps/web/src/app/docs/page.tsx +++ b/apps/web/src/app/docs/page.tsx @@ -721,7 +721,7 @@ const result = streamText({

The tpmjs field in package.json describes your - tool's capabilities. + tool's capabilities.

  • Check that required environment variables are passed
  • Verify the toolId format is correct (package::exportName)
  • -
  • Check the tool's health status on tpmjs.com
  • +
  • Check the tool's health status on tpmjs.com
  • diff --git a/apps/web/src/components/AppHeader.tsx b/apps/web/src/components/AppHeader.tsx index e0d878a..5b161e3 100644 --- a/apps/web/src/components/AppHeader.tsx +++ b/apps/web/src/components/AppHeader.tsx @@ -58,6 +58,11 @@ export function AppHeader(): React.ReactElement { FAQ + + +