From e564c1a993ffa2a9568625539d6ff5f0aa3fec7a Mon Sep 17 00:00:00 2001 From: boojack Date: Tue, 26 May 2026 21:14:59 +0800 Subject: [PATCH] chore: update about page --- web/src/pages/About.tsx | 79 +++++++++++++++++++++++++++++++++++ web/tests/about-page.test.tsx | 39 ++++++++++++++++- 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/web/src/pages/About.tsx b/web/src/pages/About.tsx index d5de1a82c..51e5cb94e 100644 --- a/web/src/pages/About.tsx +++ b/web/src/pages/About.tsx @@ -1,4 +1,5 @@ import { ExternalLinkIcon } from "lucide-react"; +import { useEffect, useState } from "react"; import TileSpriteStrip from "@/components/Placeholder/TileSpriteStrip"; import { TILE_SPRITES, type TileSprite } from "@/components/Placeholder/tileSprites"; import SettingGroup from "@/components/Settings/SettingGroup"; @@ -15,6 +16,36 @@ const PRODUCT_LINKS = [ const PRODUCT_POINTS = ["Open. Write. Done.", "Markdown-native.", "Fully yours."]; +const SPONSORS = [ + { + label: "CodeRabbit", + href: "https://coderabbit.link/usememos", + description: "Cut code review time & bugs in half, instantly.", + lightLogo: "https://victorious-bubble-f69a016683.media.strapiapp.com/Orange_Typemark_43bf516c9d.svg", + darkLogo: "https://victorious-bubble-f69a016683.media.strapiapp.com/White_Typemark_79b9189d19.svg", + }, + { + label: "Warp", + href: "https://go.warp.dev/memos", + description: "The agentic development environment.", + lightLogo: "https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Logos/Warp-Wordmark-Black.png", + darkLogo: "https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Logos/Warp-Wordmark-White.png", + }, +]; + +type Sponsor = (typeof SPONSORS)[number]; + +const isDarkThemeName = (theme: string | null): boolean => { + return theme?.endsWith("-dark") || theme?.endsWith(".dark") || false; +}; + +const getCurrentThemeUsesDarkLogo = (): boolean => { + if (typeof document === "undefined") { + return false; + } + return isDarkThemeName(document.documentElement.getAttribute("data-theme")); +}; + const BirdSprite = ({ sprite }: { sprite: TileSprite }) => { return (
@@ -26,6 +57,29 @@ const BirdSprite = ({ sprite }: { sprite: TileSprite }) => { ); }; +const SponsorLogo = ({ sponsor }: { sponsor: Sponsor }) => { + const [usesDarkLogo, setUsesDarkLogo] = useState(getCurrentThemeUsesDarkLogo); + + useEffect(() => { + const updateLogoTheme = () => setUsesDarkLogo(getCurrentThemeUsesDarkLogo()); + + updateLogoTheme(); + + const observer = new MutationObserver(updateLogoTheme); + observer.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] }); + + return () => observer.disconnect(); + }, []); + + return ( + {sponsor.label} + ); +}; + const About = () => { return (
@@ -71,6 +125,31 @@ const About = () => { + +
+ {SPONSORS.map((sponsor) => ( + +
+ + +
+
+

{sponsor.description}

+ + Visit {sponsor.label} + +
+
+ ))} +
+
+
{TILE_SPRITES.map((sprite) => ( diff --git a/web/tests/about-page.test.tsx b/web/tests/about-page.test.tsx index b52e6f3e1..00853deb2 100644 --- a/web/tests/about-page.test.tsx +++ b/web/tests/about-page.test.tsx @@ -1,9 +1,13 @@ import { render, screen, within } from "@testing-library/react"; -import { describe, expect, it } from "vitest"; +import { afterEach, describe, expect, it } from "vitest"; import { TILE_SPRITES } from "@/components/Placeholder/tileSprites"; import About from "@/pages/About"; describe("", () => { + afterEach(() => { + document.documentElement.removeAttribute("data-theme"); + }); + it("renders the product story and current bird sprites", () => { render(); @@ -11,6 +15,24 @@ describe("", () => { expect(screen.getByText(/Capture first/i)).toBeInTheDocument(); expect(screen.getByText(/quick capture/i)).toBeInTheDocument(); + const sponsors = screen.getByRole("region", { name: "Sponsors" }); + expect(within(sponsors).getByRole("link", { name: /CodeRabbit/i })).toHaveAttribute("href", "https://coderabbit.link/usememos"); + expect(within(sponsors).getByRole("link", { name: /Warp/i })).toHaveAttribute("href", "https://go.warp.dev/memos"); + expect(within(sponsors).getByText(/Cut code review time/i)).toBeInTheDocument(); + expect(within(sponsors).getByText(/agentic development environment/i)).toBeInTheDocument(); + expect(within(sponsors).getByAltText("CodeRabbit").closest("a")).toHaveAttribute("href", "https://coderabbit.link/usememos"); + expect( + within(sponsors) + .getByText(/Cut code review time/i) + .closest("a"), + ).toHaveAttribute("href", "https://coderabbit.link/usememos"); + expect(within(sponsors).getByAltText("Warp").closest("a")).toHaveAttribute("href", "https://go.warp.dev/memos"); + expect( + within(sponsors) + .getByText(/agentic development environment/i) + .closest("a"), + ).toHaveAttribute("href", "https://go.warp.dev/memos"); + const birds = screen.getByRole("region", { name: "Birds" }); expect(within(birds).getAllByTestId("about-bird-sprite")).toHaveLength(TILE_SPRITES.length); @@ -18,4 +40,19 @@ describe("", () => { expect(within(birds).getByText(sprite.name)).toBeInTheDocument(); } }); + + it("uses dark sponsor logos when the app theme is dark", () => { + document.documentElement.setAttribute("data-theme", "default-dark"); + + render(); + + expect(screen.getByAltText("CodeRabbit")).toHaveAttribute( + "src", + "https://victorious-bubble-f69a016683.media.strapiapp.com/White_Typemark_79b9189d19.svg", + ); + expect(screen.getByAltText("Warp")).toHaveAttribute( + "src", + "https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Logos/Warp-Wordmark-White.png", + ); + }); });