From bc204a40beac81ab3a3a90f0555ec59313d76292 Mon Sep 17 00:00:00 2001 From: juprodh Date: Fri, 26 Jun 2026 15:50:47 +0800 Subject: [PATCH] Improve the home page --- src/lib/i18n/en.ts | 23 ++++++++++--- src/lib/i18n/fr.ts | 23 ++++++++++--- src/lib/i18n/index.ts | 10 ++++++ src/lib/limits.ts | 2 +- src/server/routes/home.ts | 8 ++--- src/views/home.ts | 68 ++++++++++++++++++++++++++------------- src/views/icons.ts | 3 ++ src/views/layout.ts | 4 +-- 8 files changed, 100 insertions(+), 41 deletions(-) diff --git a/src/lib/i18n/en.ts b/src/lib/i18n/en.ts index ff94359..42223d8 100644 --- a/src/lib/i18n/en.ts +++ b/src/lib/i18n/en.ts @@ -29,9 +29,10 @@ export const en: Messages = { private: "private", tagline: "Shared knowledge, owned by you", heroDescription: - "Build a knowledge base with friends, for your hobbies, activism, or anything else!
Each wiki is yours, built on ATProto, data is stored on your personal data server.", + "Build wikis together on ATProto. Every note stays portable across the network.", createAWiki: "Create a wiki", - explorePublicWikis: "Explore public wikis", + explorePublicWikis: "Public wikis", + recentlyActive: "Recently active wikis", allLanguages: "All", noteCount: "{count} notes", builtOnAtproto: "Built on ATProto", @@ -41,9 +42,21 @@ export const en: Messages = { sortBy: "Sort by", filterByLanguage: "Filter by language", exploreMore: "Explore more", - step1: "Log in with your ATProto account", - step2: "Create a wiki or get invited to collaborate", - step3: "Start editing!", + exploreWikis: "Explore wikis", + step1: "Log in", + step2: "Create a wiki", + step3: "Start editing", + loginHint: "Use your Bluesky handle, or any account in the atmosphere.", + whyLichen: "Why Lichen?", + featureOwnedTitle: "Yours to keep", + featureOwnedDesc: + "Every wiki is an ATProto record on your own PDS, never locked into a single app.", + featureCollabTitle: "Built together", + featureCollabDesc: + "Invite friends, assign roles, and edit in real time, synced over the ATProto firehose.", + featureMarkdownTitle: "Just markdown", + featureMarkdownDesc: + "Plain markdown with [[wikilinks]], backlinks, and full revision history on every note.", }, pagination: { previous: "Previous", diff --git a/src/lib/i18n/fr.ts b/src/lib/i18n/fr.ts index b45c960..0ffb4a9 100644 --- a/src/lib/i18n/fr.ts +++ b/src/lib/i18n/fr.ts @@ -30,9 +30,10 @@ export const fr: PartialMessages = { private: "privé", tagline: "Plateforme libre de savoirs collaboratifs", heroDescription: - "Ecrivez avec vos amis et partagez, sur vos passions, le militantisme, ou n'importe quoi d'autre.
Construit sur ATProto, vos contributions vous appartiennent et sont stockées sur votre PDS.", + "Créez des wikis à plusieurs, vos notes restent portable dans l'atmosphère", createAWiki: "Créer un wiki", - explorePublicWikis: "Explorer les wikis publics", + explorePublicWikis: "Wikis publics", + recentlyActive: "Wikis actifs récemment", allLanguages: "Tous", noteCount: "{count} notes", builtOnAtproto: "Construit sur ATProto", @@ -42,9 +43,21 @@ export const fr: PartialMessages = { sortBy: "Trier par", filterByLanguage: "Filtrer par langue", exploreMore: "Explorer plus", - step1: "Connectez-vous avec votre compte ATProto", - step2: "Créez un wiki ou faites-vous inviter pour collaborer", - step3: "Commencez à éditer !", + exploreWikis: "Explorer les wikis", + step1: "Connectez-vous", + step2: "Créez un wiki", + step3: "Enrichissez le !", + loginHint: + "Utilisez votre identifiant Bluesky, ou n'importe quel compte de l'atmosphère.", + whyLichen: "Pourquoi Lichen ?", + featureOwnedTitle: "Vos données vous appartiennent", + featureOwnedDesc: "Chaque wiki est un enregistrement ATProto sur votre PDS", + featureCollabTitle: "Collaboratif", + featureCollabDesc: + "Invitez vos amis, attribuez des rôles et éditez en temps réel, synchronisé via le firehose ATProto.", + featureMarkdownTitle: "En markdown", + featureMarkdownDesc: + "Du markdown simple avec [[wikilinks]], rétroliens et historique complet des révisions.", }, pagination: { previous: "Précédent", diff --git a/src/lib/i18n/index.ts b/src/lib/i18n/index.ts index 307cb60..78e9a38 100644 --- a/src/lib/i18n/index.ts +++ b/src/lib/i18n/index.ts @@ -34,6 +34,7 @@ export interface Messages { heroDescription: string; createAWiki: string; explorePublicWikis: string; + recentlyActive: string; allLanguages: string; noteCount: string; builtOnAtproto: string; @@ -43,9 +44,18 @@ export interface Messages { sortBy: string; filterByLanguage: string; exploreMore: string; + exploreWikis: string; step1: string; step2: string; step3: string; + loginHint: string; + whyLichen: string; + featureOwnedTitle: string; + featureOwnedDesc: string; + featureCollabTitle: string; + featureCollabDesc: string; + featureMarkdownTitle: string; + featureMarkdownDesc: string; }; pagination: { previous: string; diff --git a/src/lib/limits.ts b/src/lib/limits.ts index 0dff992..6a49864 100644 --- a/src/lib/limits.ts +++ b/src/lib/limits.ts @@ -29,7 +29,7 @@ export const LIMITS = { maxBytes: 2 * 1024 * 1024, }, page: { - home: 6, + home: 3, explore: 12, searchDefault: 6, searchMax: 12, diff --git a/src/server/routes/home.ts b/src/server/routes/home.ts index ddb7b6d..846e0d2 100644 --- a/src/server/routes/home.ts +++ b/src/server/routes/home.ts @@ -5,10 +5,7 @@ import { resolvePublicUrl } from "../../lib/urls.ts"; import { homePage } from "../../views/home.ts"; import { baseLayoutOptions } from "../../views/view-options.ts"; import { contextPlugin } from "../context-plugin.ts"; -import { - getWikiLanguages, - listPublicWikisPaginated, -} from "../db/queries/index.ts"; +import { listPublicWikisPaginated } from "../db/queries/index.ts"; export const homeRoute = new Elysia() .use(contextPlugin) @@ -18,10 +15,9 @@ export const homeRoute = new Elysia() limit: LIMITS.page.home, offset: 0, }); - const languages = getWikiLanguages(); const publicUrl = resolvePublicUrl(request); return htmlResponse( - homePage(wikis, languages, { + homePage(wikis, { ...baseLayoutOptions(ctx), ogUrl: `${publicUrl}/`, ogImage: `${publicUrl}/public/og-home.png`, diff --git a/src/views/home.ts b/src/views/home.ts index 11edc0c..1d7347b 100644 --- a/src/views/home.ts +++ b/src/views/home.ts @@ -1,53 +1,77 @@ import { t } from "../lib/i18n/index.ts"; -import { LIMITS } from "../lib/limits.ts"; import { profileUrl } from "../lib/urls.ts"; import type { WikiWithNoteCount } from "../server/db/queries/index.ts"; +import { ICONS } from "./icons.ts"; import { type LayoutOptions, layout } from "./layout.ts"; import { outlineSmallButtonClass, primaryButtonClass, THEME, } from "./theme/index.ts"; -import { wikiGridWithExploreLink, wikiToolbar } from "./wiki-list.ts"; +import { wikiGridWithExploreLink } from "./wiki-list.ts"; + +function feature(icon: string, title: string, desc: string): string { + return `
+
${icon}
+

${title}

+

${desc}

+
`; +} export function homePage( wikis: WikiWithNoteCount[], - languages: string[], options?: LayoutOptions, ): string { const locale = options?.locale ?? "en"; const msg = t(locale); - const toolbar = wikiToolbar(languages, locale, { - sort: "updated", - gridTargetId: "wiki-grid", - limit: LIMITS.page.home, - }); + const myWikisHref = options?.session + ? profileUrl(options.session.did) + : "/login?returnTo=/profile-redirect"; - const gridHtml = wikiGridWithExploreLink(wikis, locale); + const step = (n: number, label: string, href?: string): string => { + const inner = `${n}${label}`; + const cls = `inline-flex items-center gap-2 ${THEME.textSecondary}`; + return href + ? `${inner}` + : `${inner}`; + }; return layout( "Lichen", ` -
-

${msg.home.tagline}

-

${msg.home.heroDescription}

-
- 1. ${msg.home.step1} - 2. ${msg.home.step2} - 3. ${msg.home.step3} +
+
+
-
+

${msg.home.tagline}

+

${msg.home.heroDescription}

+ +
+ ${step(1, msg.home.step1, "/login")} + + ${step(2, msg.home.step2, "/wiki/new")} + + ${step(3, msg.home.step3)} +
+

${msg.home.loginHint}

+
+ +
+
+ ${feature(ICONS.shield, msg.home.featureOwnedTitle, msg.home.featureOwnedDesc)} + ${feature(ICONS.users, msg.home.featureCollabTitle, msg.home.featureCollabDesc)} + ${feature(ICONS.link, msg.home.featureMarkdownTitle, msg.home.featureMarkdownDesc)}
-
-

${msg.home.explorePublicWikis}

- ${toolbar} +
+

${msg.home.recentlyActive}

- ${gridHtml} + ${wikiGridWithExploreLink(wikis, locale)}
`, diff --git a/src/views/icons.ts b/src/views/icons.ts index cb048db..6ef118a 100644 --- a/src/views/icons.ts +++ b/src/views/icons.ts @@ -10,6 +10,9 @@ export const ICONS = { download: ``, edit: ``, theme: ``, + shield: ``, + users: ``, + link: ``, // Inlined for fill="currentColor" (follows theme accent); geometry matches /public/logo.svg. logo: ``, } as const; diff --git a/src/views/layout.ts b/src/views/layout.ts index 64bb8ad..2fa461d 100644 --- a/src/views/layout.ts +++ b/src/views/layout.ts @@ -70,7 +70,7 @@ export interface LayoutOptions { // Defaults so even pages without per-page overrides emit a usable Bluesky/Twitter card. const OG_DEFAULT_DESCRIPTION = - "Build a knowledge base with friends — wikis owned by you, built on ATProto."; + "Build a knowledge base with friends. Wikis owned by you, built on ATProto."; function renderOpenGraph( options: LayoutOptions | undefined, @@ -291,7 +291,7 @@ export function layout( - + ${escapeHtml( options?.wikiName ? options.currentNoteSlug -- 2.51.2