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
1 change: 0 additions & 1 deletion public/robots.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ Crawl-delay: 10
# Default
User-agent: *
Allow: /
Disallow: /_next/static/
Disallow: /api/
Crawl-delay: 2

Expand Down
2 changes: 2 additions & 0 deletions src/app/(home)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Footer } from "@/components/footer";
import { HeroSection } from "@/components/hero-section";
import { HomeCtaSection } from "@/components/home-cta-section";
import { HomeDeploySection } from "@/components/home-deploy-section";
import { HomeDiscoverSection } from "@/components/home-discover-section";
import { HomeFeaturesSection } from "@/components/home-features-section";
import { HomeScratchpadSection } from "@/components/home-scratchpad-section";
import { HomeStatsSection } from "@/components/home-stats-section";
Expand Down Expand Up @@ -55,6 +56,7 @@ export default function HomePage() {
primaryCta={{ text: "Get Started", href: "/docs/getting-started" }}
secondaryCta={{ text: "Live Demo", href: "https://demo.usememos.com/", external: true }}
/>
<HomeDiscoverSection />
<HomeFeaturesSection />
<HomeUseCasesSection />
<HomeStatsSection />
Expand Down
14 changes: 13 additions & 1 deletion src/app/blog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import { BlogArticleBody } from "@/components/blog-article-body";
import { BlogPostFooter } from "@/components/blog-post-footer";
import { BlogPostHeader } from "@/components/blog-post-header";
import { BlogPostHeroImage } from "@/components/blog-post-hero-image";
import { Breadcrumbs } from "@/components/breadcrumbs";
import { Footer } from "@/components/footer";
import { getAbsoluteBlogImageUrl } from "@/lib/blog";
import { buildBreadcrumbJsonLd } from "@/lib/seo";
import { blogSource } from "@/lib/source";

interface BlogPageProps {
Expand All @@ -27,6 +29,12 @@ export default async function BlogPostPage({ params }: BlogPageProps) {

const { data } = page;
const Content = page.data.body;
const breadcrumbItems = [
{ href: "/", name: "Home" },
{ href: "/blog", name: "Blog" },
{ href: `/blog/${slug}`, name: data.title },
];
const breadcrumbJsonLd = buildBreadcrumbJsonLd(breadcrumbItems);

// JSON-LD structured data for blog post
const jsonLd = {
Expand Down Expand Up @@ -59,9 +67,13 @@ export default async function BlogPostPage({ params }: BlogPageProps) {
return (
<HomeLayout {...baseOptions}>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<main className="flex flex-1 flex-col bg-[linear-gradient(180deg,rgba(255,255,255,0.94)_0%,rgba(245,247,244,0.98)_26%,rgba(245,247,244,1)_100%)] dark:bg-[linear-gradient(180deg,rgba(10,10,10,0.96)_0%,rgba(18,18,18,1)_28%,rgba(10,10,10,1)_100%)]">
<section className="px-4 pb-8 pt-8 sm:pt-12 lg:pb-10">
<div className="mx-auto max-w-6xl">
<div className="mb-8">
<Breadcrumbs items={breadcrumbItems} className={undefined} />
</div>
<BlogPostHeader title={data.title} description={data.description} publishedAt={data.published_at} tags={data.tags} />

{/* Feature Image */}
Expand Down Expand Up @@ -103,7 +115,7 @@ export async function generateMetadata({ params }: BlogPageProps): Promise<Metad
const absoluteImageUrl = getAbsoluteBlogImageUrl(data.feature_image);

return {
title: `${data.title} - Memos Blog`,
title: data.title,
description: data.description,
alternates: {
canonical: pageUrl,
Expand Down
38 changes: 16 additions & 22 deletions src/app/blog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,27 @@ import { UserIcon } from "lucide-react";
import type { Metadata } from "next";
import { baseOptions } from "@/app/layout.config";
import { BlogListItem } from "@/components/blog-list-item";
import { Breadcrumbs } from "@/components/breadcrumbs";
import { Footer } from "@/components/footer";
import { BLOG_COLUMN_CLASS } from "@/lib/blog";
import { buildBreadcrumbJsonLd, buildMarketingMetadata } from "@/lib/seo";
import { blogSource } from "@/lib/source";

export const dynamic = "force-static";
export const revalidate = 1800;

export const metadata: Metadata = {
title: "Blogs",
export const metadata: Metadata = buildMarketingMetadata({
title: "Blog",
description: "Insights, updates, and stories from the team building Memos, the open-source note-taking tool for instant capture.",
alternates: {
canonical: "https://usememos.com/blog",
},
openGraph: {
title: "Memos Blog - Insights & Updates",
description: "Insights, updates, and stories from the team building Memos, the open-source note-taking tool for instant capture.",
url: "https://usememos.com/blog",
siteName: "Memos",
images: [
{
url: "https://usememos.com/og-image.png",
width: 1200,
height: 630,
alt: "Memos Blog",
},
],
type: "website",
},
};
path: "/blog",
});

const breadcrumbItems = [
{ href: "/", name: "Home" },
{ href: "/blog", name: "Blog" },
];

const breadcrumbJsonLd = buildBreadcrumbJsonLd(breadcrumbItems);

export default function BlogPage() {
const posts = blogSource.getPages().sort((a, b) => {
Expand All @@ -43,12 +35,14 @@ export default function BlogPage() {
return (
<HomeLayout {...baseOptions}>
<main className="flex flex-1 flex-col">
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<section className="bg-[linear-gradient(180deg,rgba(255,255,255,0.94)_0%,rgba(245,247,244,0.98)_26%,rgba(245,247,244,1)_100%)] px-4 py-12 dark:bg-[linear-gradient(180deg,rgba(10,10,10,0.96)_0%,rgba(18,18,18,1)_28%,rgba(10,10,10,1)_100%)] sm:py-16 lg:py-24">
<div className={BLOG_COLUMN_CLASS}>
{/* Hero Section */}
<div className="mb-12 sm:mb-16 lg:mb-20">
<Breadcrumbs items={breadcrumbItems} className="mb-8" />
<h1 className="mb-4 font-serif text-3xl font-bold leading-[1.02] tracking-tight text-gray-950 dark:text-gray-50 sm:mb-6 sm:text-4xl lg:text-5xl xl:text-[4.25rem]">
Blogs
Blog
</h1>
<p className="max-w-2xl text-base leading-8 text-gray-600 dark:text-gray-300 sm:text-lg">
Insights, updates, and stories from the team building Memos.
Expand Down
21 changes: 15 additions & 6 deletions src/app/brand/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@ import { DownloadIcon, PaletteIcon } from "lucide-react";
import type { Metadata } from "next";
import Image from "next/image";
import { baseOptions } from "@/app/layout.config";
import { Breadcrumbs } from "@/components/breadcrumbs";
import { Footer } from "@/components/footer";
import { buildBreadcrumbJsonLd, buildMarketingMetadata } from "@/lib/seo";

export const metadata: Metadata = {
title: "Brand - Memos",
export const metadata: Metadata = buildMarketingMetadata({
title: "Brand",
description: "Official Memos brand assets, logos, and usage guidelines for the open-source note-taking tool built for instant capture.",
alternates: {
canonical: "https://usememos.com/brand",
},
};
path: "/brand",
});

const breadcrumbItems = [
{ href: "/", name: "Home" },
{ href: "/brand", name: "Brand" },
];

const breadcrumbJsonLd = buildBreadcrumbJsonLd(breadcrumbItems);

const LOGO_ASSETS = [
{
Expand Down Expand Up @@ -59,10 +66,12 @@ export default function BrandPage() {
return (
<HomeLayout {...baseOptions}>
<main className="flex flex-1 flex-col">
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<section className="py-12 sm:py-16 lg:py-24 px-4 bg-gradient-to-b from-white to-gray-50/50 dark:from-gray-900 dark:to-gray-800/50">
<div className="max-w-4xl mx-auto">
{/* Hero */}
<div className="text-center mb-12 sm:mb-16 lg:mb-20">
<Breadcrumbs items={breadcrumbItems} className="mb-8 text-left" />
<div className="flex justify-center mb-6">
<div className="inline-flex items-center justify-center w-14 h-14 sm:w-16 sm:h-16 bg-gradient-to-br from-teal-100 to-cyan-100 dark:from-teal-900/30 dark:to-cyan-900/30 text-teal-600 dark:text-teal-400 rounded-2xl">
<PaletteIcon className="w-7 h-7 sm:w-8 sm:h-8" />
Expand Down
12 changes: 11 additions & 1 deletion src/app/changelog/[slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Link from "next/link";
import { notFound } from "next/navigation";
import { baseOptions } from "@/app/layout.config";
import { AdsSectionDesktop, AdsSectionMobile } from "@/components/ads-section";
import { Breadcrumbs } from "@/components/breadcrumbs";
import { ChangelogArticleBody } from "@/components/changelog-article-body";
import { ChangelogFooter } from "@/components/changelog-footer";
import { ChangelogHeader } from "@/components/changelog-header";
Expand All @@ -17,6 +18,7 @@ import {
getChangelogVersion,
sortChangelogPages,
} from "@/lib/changelog";
import { buildBreadcrumbJsonLd } from "@/lib/seo";
import { changelogSource } from "@/lib/source";

interface ChangelogPageProps {
Expand All @@ -39,14 +41,22 @@ export default async function ChangelogEntryPage({ params }: ChangelogPageProps)
const version = getChangelogVersion(data.title);
const sortedEntries = sortChangelogPages(changelogSource.getPages());
const isLatest = sortedEntries[0]?.url === page.url;
const breadcrumbItems = [
{ href: "/", name: "Home" },
{ href: "/changelog", name: "Changelog" },
{ href: `/changelog/${slug}`, name: version },
];
const breadcrumbJsonLd = buildBreadcrumbJsonLd(breadcrumbItems);

return (
<HomeLayout {...baseOptions}>
<main className="flex flex-1 flex-col bg-[linear-gradient(180deg,rgba(255,255,255,0.94)_0%,rgba(245,247,244,0.98)_26%,rgba(245,247,244,1)_100%)] dark:bg-[linear-gradient(180deg,rgba(10,10,10,0.96)_0%,rgba(18,18,18,1)_28%,rgba(10,10,10,1)_100%)]">
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<section className="px-4 pb-8 pt-8 sm:pt-12 lg:pb-10">
<div className={CHANGELOG_DETAIL_LAYOUT_CLASS}>
<div className="min-w-0">
<div className={CHANGELOG_ARTICLE_COLUMN_CLASS}>
<Breadcrumbs items={breadcrumbItems} className="mb-6" />
<Link
href="/changelog"
className="group mb-8 inline-flex items-center gap-2 text-sm font-medium text-gray-600 transition-colors hover:text-teal-600 dark:text-gray-300 dark:hover:text-teal-400 sm:mb-12"
Expand Down Expand Up @@ -130,7 +140,7 @@ export async function generateMetadata({ params }: ChangelogPageProps): Promise<
const pageUrl = `https://usememos.com/changelog/${slug}`;

return {
title: `${version} Release Notes - Memos`,
title: `${version} Release Notes`,
description: getChangelogDescription(version, data.description),
alternates: {
canonical: pageUrl,
Expand Down
38 changes: 16 additions & 22 deletions src/app/changelog/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,28 @@ import { HomeLayout } from "fumadocs-ui/layouts/home";
import { CalendarIcon } from "lucide-react";
import type { Metadata } from "next";
import { baseOptions } from "@/app/layout.config";
import { Breadcrumbs } from "@/components/breadcrumbs";
import { ChangelogListItem } from "@/components/changelog-list-item";
import { Footer } from "@/components/footer";
import { CHANGELOG_COLUMN_CLASS, getChangelogDescription, getChangelogVersion, sortChangelogPages } from "@/lib/changelog";
import { buildBreadcrumbJsonLd, buildMarketingMetadata } from "@/lib/seo";
import { changelogSource } from "@/lib/source";

export const dynamic = "force-static";
export const revalidate = 1800;

export const metadata: Metadata = {
title: "Changelogs",
export const metadata: Metadata = buildMarketingMetadata({
title: "Changelog",
description: "Stay up to date with new features, improvements, and bug fixes in Memos.",
alternates: {
canonical: "https://usememos.com/changelog",
},
openGraph: {
title: "Memos Changelog - Release Notes & Updates",
description: "Stay up to date with new features, improvements, and bug fixes in Memos.",
url: "https://usememos.com/changelog",
siteName: "Memos",
images: [
{
url: "https://usememos.com/og-image.png",
width: 1200,
height: 630,
alt: "Memos Changelog",
},
],
type: "website",
},
};
path: "/changelog",
});

const breadcrumbItems = [
{ href: "/", name: "Home" },
{ href: "/changelog", name: "Changelog" },
];

const breadcrumbJsonLd = buildBreadcrumbJsonLd(breadcrumbItems);

export default function ChangelogPage() {
const entries = sortChangelogPages(changelogSource.getPages());
Expand All @@ -41,15 +33,17 @@ export default function ChangelogPage() {
return (
<HomeLayout {...baseOptions}>
<main className="flex flex-1 flex-col">
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<section className="bg-[linear-gradient(180deg,rgba(255,255,255,0.94)_0%,rgba(245,247,244,0.98)_26%,rgba(245,247,244,1)_100%)] px-4 py-12 dark:bg-[linear-gradient(180deg,rgba(10,10,10,0.96)_0%,rgba(18,18,18,1)_28%,rgba(10,10,10,1)_100%)] sm:py-16 lg:py-24">
<div className={CHANGELOG_COLUMN_CLASS}>
<div className="mb-12 sm:mb-16 lg:mb-20">
<Breadcrumbs items={breadcrumbItems} className="mb-8" />
<div className="mb-4 flex flex-wrap items-center gap-3 text-[11px] font-semibold uppercase tracking-[0.14em] text-gray-500 dark:text-gray-400 sm:mb-6">
<span className="text-teal-700/90 dark:text-teal-300/90">Release History</span>
{latestVersion && <span>Latest {latestVersion}</span>}
</div>
<h1 className="mb-4 font-serif text-3xl font-bold leading-[1.02] tracking-tight text-gray-950 dark:text-gray-50 sm:mb-6 sm:text-4xl lg:text-5xl xl:text-[4.25rem]">
Changelogs
Changelog
</h1>
<p className="max-w-2xl text-base leading-8 text-gray-600 dark:text-gray-300 sm:text-lg">
Stay up to date with new features, improvements, and bug fixes in Memos.
Expand Down
47 changes: 25 additions & 22 deletions src/app/docs/[...slug]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page
import type { Metadata } from "next";
import { notFound, redirect } from "next/navigation";
import { AdsSectionMobile } from "@/components/ads-section";
import { Breadcrumbs } from "@/components/breadcrumbs";
import { getApiDocsVersionFromSlug, getApiDocsVersionLabel, normalizeApiDocsSlug } from "@/lib/api-docs";
import { buildBreadcrumbJsonLd } from "@/lib/seo";
import { source } from "@/lib/source";
import { tocConfig } from "@/lib/toc-config";
import { getMDXComponents } from "@/mdx-components";
Expand Down Expand Up @@ -34,29 +36,29 @@ export default async function Page(props: { params: Promise<{ slug: string[] }>

// Build breadcrumb items from URL path
const pathParts = page.url.split("/").filter(Boolean);
const breadcrumbItems = pathParts.map((part, index) => {
const path = `/${pathParts.slice(0, index + 1).join("/")}`;
const name =
index === pathParts.length - 1
? page.data.title
: index === 1
? "API"
: index === 2 && apiVersionLabel
? apiVersionLabel
: part.charAt(0).toUpperCase() + part.slice(1);
return {
"@type": "ListItem",
position: index + 1,
name,
item: `https://usememos.com${path}`,
};
});
const uiBreadcrumbItems = [
{ href: "/", name: "Home" },
...pathParts.map((part, index) => {
const path = `/${pathParts.slice(0, index + 1).join("/")}`;
const name =
index === pathParts.length - 1
? page.data.title
: index === 0
? "Documentation"
: index === 1
? "API"
: index === 2 && apiVersionLabel
? apiVersionLabel
: part.charAt(0).toUpperCase() + part.slice(1);

const breadcrumbJsonLd = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: breadcrumbItems,
};
return { href: path, name };
}),
];
const breadcrumbItems = uiBreadcrumbItems.map((item) => ({
href: item.href,
name: item.name,
}));
const breadcrumbJsonLd = buildBreadcrumbJsonLd(breadcrumbItems);

const jsonLd = isApi
? {
Expand Down Expand Up @@ -84,6 +86,7 @@ export default async function Page(props: { params: Promise<{ slug: string[] }>
<DocsPage {...fullProp} {...tocProps} {...tocConfig}>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<Breadcrumbs items={breadcrumbItems} className="mb-6" />
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
Expand Down
9 changes: 9 additions & 0 deletions src/app/docs/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import { AdsSectionMobile } from "@/components/ads-section";
import { Breadcrumbs } from "@/components/breadcrumbs";
import { buildBreadcrumbJsonLd } from "@/lib/seo";
import { source } from "@/lib/source";
import { tocConfig } from "@/lib/toc-config";
import { getMDXComponents } from "@/mdx-components";
Expand All @@ -16,6 +18,10 @@ export default async function Page() {

const MDXContent = page.data.body;
const isApi = page.url.startsWith("/docs/api");
const breadcrumbItems = [
{ href: "/", name: "Home" },
{ href: page.url, name: "Documentation" },
];

const jsonLd = isApi
? {
Expand All @@ -38,10 +44,13 @@ export default async function Page() {
name: "Memos Team",
},
};
const breadcrumbJsonLd = buildBreadcrumbJsonLd(breadcrumbItems);

return (
<DocsPage toc={page.data.toc} full={page.data.full} {...tocConfig}>
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbJsonLd) }} />
<Breadcrumbs items={breadcrumbItems} className="mb-6" />
<DocsTitle>{page.data.title}</DocsTitle>
<DocsDescription>{page.data.description}</DocsDescription>
<DocsBody>
Expand Down
Loading
Loading