Skip to content

Commit b28ec1a

Browse files
JohnMcLearclaude
andauthored
Add "More from Etherpad" ecosystem section to homepage (#406)
* Add "More from Etherpad" ecosystem section to homepage etherpad.org only linked to the core editor, the blog, the wiki and the plugin directory — there was nowhere pointing visitors at the other official projects the Foundation maintains (desktop/mobile app, CLI, proxy, load-tester, ueberDB, web components, Home Assistant add-on). Add a new "More from Etherpad" card grid between Download and Contribute, plus an "Apps & Tools" entry in the desktop nav and mobile drawer (scroll-point id "ecosystem"). Plugins are intentionally excluded — they keep their dedicated /plugins page, which the section links to. Cards are a responsive 1/2/3-column Tailwind grid that matches the existing section styling and supports light and dark themes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add Scale Calculator (scale.etherpad.org) to ecosystem section Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add pad (terminal editor) and Printing Press to ecosystem section Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Drop Load Testing Tool from ecosystem section Niche post-deployment tool; discoverable from the Scale Calculator instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Clarify pad card: collaboration is two-way, not just watching Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Address Qodo review: accessible nav link + next/link for /plugins - Give the new 'Apps & Tools' nav item (header + mobile drawer) a real href='/#ecosystem' and move navigation into the anchor's onClick so it is keyboard-focusable and activates on Enter (Qodo bug 1, correctness). - Use next/link for the internal /plugins link instead of a plain anchor to keep client-side routing (Qodo bug 2, performance). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Drop Web Components from ecosystem section Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Add Plugins card to ecosystem section Surface the plugin directory as a first-class card (internal next/link to /plugins) rather than only an aside in the intro copy. Reword the intro to fold the plugin ecosystem into the main sentence. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent d4d1011 commit b28ec1a

5 files changed

Lines changed: 147 additions & 1 deletion

File tree

app/page.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {AIOnYourTerms} from "../src/pagesToDisplay/AIOnYourTerms.tsx";
88
import {AddFunctionalities} from "../src/pagesToDisplay/AddFunctionalities.tsx";
99
import {CustomizeAppearance} from "../src/pagesToDisplay/CustomizeAppearance.tsx";
1010
import {DownloadLatestVersion} from "../src/pagesToDisplay/DownloadLatestVersion.tsx";
11+
import {EtherpadEcosystem} from "../src/pagesToDisplay/EtherpadEcosystem.tsx";
1112
import {Contribute} from "../src/pagesToDisplay/Contribute.tsx";
1213
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
1314
import {faExternalLink} from "@fortawesome/free-solid-svg-icons";
@@ -38,6 +39,8 @@ export default function Page() {
3839
<CustomizeAppearance/>
3940
<a className="scroll-point" id="download"></a>
4041
<DownloadLatestVersion/>
42+
<a className="scroll-point" id="ecosystem"></a>
43+
<EtherpadEcosystem/>
4144
<a className="scroll-point" id="contribute"></a>
4245
<Contribute/>
4346
<a className="scroll-point" id="links"></a>

src/Constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,9 @@ export const DOC_PAGE = `/doc/v${CURRENT_VERSION}/index.html`
1818

1919
export const GITHUB_HELP = "https://docs.github.com"
2020

21+
// Official companion apps, clients and tools maintained by the Etherpad
22+
// Foundation alongside the core editor. Plugins are intentionally excluded
23+
// here — they live on the dedicated /plugins page.
24+
export const ETHERPAD_ORG = "https://github.com/ether"
25+
2126
export const TRACKING_ID = 'UA-19303815-1'

src/components/MobileDrawer.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
3-
import {faInfoCircle, faDownload, faWrench, faExternalLink, faContactCard} from '@fortawesome/free-solid-svg-icons'
3+
import {faInfoCircle, faDownload, faWrench, faExternalLink, faContactCard, faCubes} from '@fortawesome/free-solid-svg-icons'
44
import {FC, useEffect} from "react";
55
import {ThemeToggler} from "./ThemeToggler.tsx";
66
import {useUIStore} from "../store/store.ts";
@@ -42,6 +42,10 @@ export const MobileDrawer:FC<MobileDrawerProps> = ({isOpen, setOpen}) => {
4242
<FontAwesomeIcon icon={faDownload} className="self-center"/>
4343
<a title="download">Download</a>
4444
</div>
45+
<div className="contents" onClick={()=>navigateToElement('ecosystem')}>
46+
<FontAwesomeIcon icon={faCubes} className="self-center"/>
47+
<a href="/#ecosystem" onClick={(e)=>{e.preventDefault(); navigateToElement('ecosystem')}} title="apps and tools">Apps &amp; Tools</a>
48+
</div>
4549
<div className="contents" onClick={()=>navigateToElement('contribute')}>
4650
<FontAwesomeIcon icon={faWrench} className="self-center"/>
4751
<a title="contribute">Contribute</a>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
2+
import Link from "next/link";
3+
import type {IconDefinition} from "@fortawesome/fontawesome-svg-core";
4+
import {
5+
faCubes,
6+
faPlug,
7+
faDesktop,
8+
faTerminal,
9+
faNetworkWired,
10+
faDatabase,
11+
faHouse,
12+
faCalculator,
13+
faKeyboard,
14+
faRobot,
15+
} from "@fortawesome/free-solid-svg-icons";
16+
import {ETHERPAD_ORG} from "../Constants.ts";
17+
18+
type EcosystemProject = {
19+
name: string,
20+
url: string,
21+
icon: IconDefinition,
22+
description: string,
23+
// Internal routes (e.g. /plugins) use next/link for client-side
24+
// navigation; everything else opens the external project in a new tab.
25+
internal?: boolean,
26+
}
27+
28+
// Curated, user-facing companion projects and resources from the Etherpad
29+
// Foundation, surfaced alongside the core editor on the homepage.
30+
const PROJECTS: EcosystemProject[] = [
31+
{
32+
name: "Plugins",
33+
url: "/plugins",
34+
icon: faPlug,
35+
description: "Hundreds of community plugins add features, themes and integrations. Browse the directory and extend your Etherpad however you like.",
36+
internal: true,
37+
},
38+
{
39+
name: "Desktop & Mobile App",
40+
url: `${ETHERPAD_ORG}/etherpad-desktop`,
41+
icon: faDesktop,
42+
description: "Run Etherpad as a native app on Windows, macOS, Linux, Android and iOS — collaborate without opening a browser.",
43+
},
44+
{
45+
name: "Command-line Client",
46+
url: `${ETHERPAD_ORG}/etherpad-cli`,
47+
icon: faTerminal,
48+
description: "Create, read and edit pads straight from your terminal. Great for scripting and automating your Etherpad instance.",
49+
},
50+
{
51+
name: "pad — Terminal Editor",
52+
url: `${ETHERPAD_ORG}/pad`,
53+
icon: faKeyboard,
54+
description: "A nano-class terminal text editor. Edit files locally, or join any pad to collaborate in real time — your edits and everyone else's sync live, right in your terminal.",
55+
},
56+
{
57+
name: "Printing Press",
58+
url: "https://printingpress.dev",
59+
icon: faRobot,
60+
description: "Agent-native Etherpad tooling — a Go CLI, a Claude Code skill and an MCP server, generated from a spec so AI agents can drive Etherpad.",
61+
},
62+
{
63+
name: "Socket.IO Proxy",
64+
url: `${ETHERPAD_ORG}/etherpad-proxy`,
65+
icon: faNetworkWired,
66+
description: "A reference proxy for Etherpad's real-time Socket.IO traffic — a starting point for inspecting or routing pad messages.",
67+
},
68+
{
69+
name: "Scale Calculator",
70+
url: "https://scale.etherpad.org",
71+
icon: faCalculator,
72+
description: "Estimate the CPU, memory and number of instances your deployment needs for a target number of concurrent users.",
73+
},
74+
{
75+
name: "ueberDB",
76+
url: `${ETHERPAD_ORG}/ueberDB`,
77+
icon: faDatabase,
78+
description: "The database abstraction layer that powers Etherpad — one API over MySQL, PostgreSQL, Redis, MongoDB, SQLite and more.",
79+
},
80+
{
81+
name: "Home Assistant Add-on",
82+
url: `${ETHERPAD_ORG}/home-assistant-addon-etherpad`,
83+
icon: faHouse,
84+
description: "Self-host Etherpad on your Home Assistant box in a couple of clicks — collaborative notes for your smart home.",
85+
},
86+
]
87+
88+
const CARD_CLASS = "group flex flex-col h-full p-5 rounded-lg border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 transition-shadow hover:shadow-md focus:shadow-md no-underline"
89+
90+
const CardBody = ({project}: {project: EcosystemProject}) => <>
91+
<div className="flex items-center mb-2">
92+
<FontAwesomeIcon icon={project.icon} className="text-2xl text-primary mr-3"/>
93+
<span className="text-lg font-bold text-gray-800 dark:text-white group-hover:text-primary">
94+
{project.name}
95+
</span>
96+
</div>
97+
<span className="text-sm text-gray-600 dark:text-gray-300 leading-relaxed">
98+
{project.description}
99+
</span>
100+
</>
101+
102+
export const EtherpadEcosystem = () => {
103+
return <div className="content wrap">
104+
<h2 className="text-3xl text-primary font-bold mb-4 mt-16 flex items-center">
105+
<FontAwesomeIcon icon={faCubes} className="mr-4"/>
106+
More from Etherpad
107+
</h2>
108+
<p className="dark:text-gray-400">
109+
Etherpad is more than the editor. The Foundation maintains a family of official
110+
apps, clients and tools — plus a large plugin ecosystem — to help you extend, run,
111+
embed and scale Etherpad.
112+
</p>
113+
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4 mt-6">
114+
{PROJECTS.map((project) => (
115+
project.internal ? (
116+
<Link key={project.url} href={project.url} className={CARD_CLASS}>
117+
<CardBody project={project}/>
118+
</Link>
119+
) : (
120+
<a
121+
key={project.url}
122+
href={project.url}
123+
target="_blank"
124+
rel="noopener noreferrer"
125+
className={CARD_CLASS}
126+
>
127+
<CardBody project={project}/>
128+
</a>
129+
)
130+
))}
131+
</div>
132+
</div>
133+
}

src/pagesToDisplay/Header.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export const Header = () => {
3838
<ul>
3939
<li><Link className="text-[#555] dark:text-white" href="/about" title="about">About</Link></li>
4040
<li><a className="text-[#555] dark:text-white" onClick={()=>navigateToElement('download')} title="download">Download</a></li>
41+
<li><a className="text-[#555] dark:text-white" href="/#ecosystem" onClick={(e)=>{e.preventDefault(); navigateToElement('ecosystem')}} title="apps and tools">Apps &amp; Tools</a></li>
4142
<li><a className="text-[#555] dark:text-white" onClick={()=>navigateToElement('contribute')} title="contribute">Contribute</a></li>
4243
<li><a className="text-[#555] dark:text-white" onClick={()=>navigateToElement("links")} title="links">Links</a></li>
4344
<li><a className="text-[#555] dark:text-white" onClick={()=>navigateToElement('contact')} title="contact">Contact</a></li>

0 commit comments

Comments
 (0)