{"id":50884357,"url":"https://github.com/ether/etherpad-desktop","last_synced_at":"2026-06-15T15:32:43.487Z","repository":{"id":357358044,"uuid":"1228874738","full_name":"ether/etherpad-desktop","owner":"ether","description":"Etherpad Desktop and Mobile App","archived":false,"fork":false,"pushed_at":"2026-06-09T06:30:18.000Z","size":2037,"stargazers_count":5,"open_issues_count":5,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-09T08:22:10.337Z","etag":null,"topics":["app","collaborate","collaboration","desktop-app","document","editing","editor","etherpad","linux","macos","windows"],"latest_commit_sha":null,"homepage":"https://etherpad.org","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ether.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-04T13:20:20.000Z","updated_at":"2026-06-09T06:27:44.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ether/etherpad-desktop","commit_stats":null,"previous_names":["ether/etherpad-desktop"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/ether/etherpad-desktop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ether%2Fetherpad-desktop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ether%2Fetherpad-desktop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ether%2Fetherpad-desktop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ether%2Fetherpad-desktop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ether","download_url":"https://codeload.github.com/ether/etherpad-desktop/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ether%2Fetherpad-desktop/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34369842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-15T02:00:07.085Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["app","collaborate","collaboration","desktop-app","document","editing","editor","etherpad","linux","macos","windows"],"created_at":"2026-06-15T15:32:41.138Z","updated_at":"2026-06-15T15:32:43.452Z","avatar_url":"https://github.com/ether.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Etherpad apps\n\nCross-platform clients for [Etherpad](https://etherpad.org/) — a single codebase\nthat ships as an Electron desktop app **and** a Capacitor Android app, both\nrunning the same React shell.\n\nThis is a pnpm monorepo.\n\n| Package | Status | Source |\n|---|---|---|\n| `@etherpad/desktop` | v0 beta — Linux (AppImage / `.deb` / Snap), Windows (NSIS / portable), macOS (DMG arm64+x64). Auto-update on Linux + Windows. | [`packages/desktop`](packages/desktop) |\n| `@etherpad/mobile` | Beta — Android debug APK runs; iOS scaffold present, not yet exercised. Not on Play Store yet. | [`packages/mobile`](packages/mobile) |\n| `@etherpad/shell` | Workspace dep consumed as source by both apps. Owns React tree, state, i18n, IPC channel schemas, the `Platform` injection seam. | [`packages/shell`](packages/shell) |\n\n## Goal\n\n**One UX, two shells.** The instance rail, pad tabs, sidebar, dialogs, settings,\nkeyboard model, theming, and i18n live in `@etherpad/shell` and render\nidentically on desktop and mobile. Each runtime injects a `Platform`\nimplementation that handles native concerns (window management, persistence,\ndeep links, permissions) behind a typed IPC seam.\n\nThe one place we expect to **diverge** is offline editing — a desktop client\nusually has a long-lived connection, where a mobile client is offline by\ndefault. Mobile will likely grow a CRDT-based local-edit-queue path that\ndesktop doesn't need; both will keep using Etherpad's OT-based collaboration\nwhen online.\n\n## Quick start\n\n```bash\npnpm install\npnpm dev               # desktop: Electron + Vite HMR\npnpm mobile:dev        # mobile: browser preview at http://localhost:5173/\npnpm mobile:android:run # mobile: build + sync + run on attached device/emulator\npnpm test              # vitest across all packages\npnpm test:e2e          # Playwright Electron e2e + mobile smoke\npnpm typecheck         # tsc -b across the monorepo\n```\n\nPer-app developer docs:\n\n- [`packages/desktop/README.md`](packages/desktop/README.md) — install one-liners, install matrix, dev loop, packaging.\n- [`packages/mobile/README.md`](packages/mobile/README.md) — Capacitor / Android toolchain, build + adb.\n\n## Feature parity\n\nThis is the canonical \"what works where\" view. Empty cells mean **not started**;\n\"⏳\" means in flight; \"✅\" means shipped on that surface.\n\n### Etherpad instances and workspaces\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| Multiple Etherpad instances, isolated sessions | ✅ | ✅ |\n| Per-instance colour + name + URL editing | ✅ | ✅ |\n| Reorder instances in the rail | ✅ | ✅ |\n| Embedded local Etherpad server (no internet needed) | ✅ | ❌ phones don't ship Node; spec divergence noted below |\n| HTTP basic-auth challenge dialog | ✅ | ❌ planned |\n\n### Pads, tabs, navigation\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| Open pad by name into a new tab | ✅ | ✅ |\n| Multiple pads per instance, switchable tabs | ✅ | ✅ |\n| Auto-collapse rail when opening a pad (focus mode) | ✅ | ✅ |\n| Tap pad to dismiss the rail (drawer behaviour) | n/a | ✅ |\n| Restore open tabs + active pad across app restarts | ✅ | ✅ |\n| Restore rail-collapsed state across app restarts | ✅ | ✅ |\n| Native pad rendering (WebContentsView vs iframe) | WebContentsView | iframe |\n| Pad sidebar — Recent + Pinned + search | ✅ | ✅ |\n| Cross-instance fuzzy quick switcher (Ctrl+K) | ✅ name + content search | ✅ name search; content search ⏳ |\n| Pad-history persistence per workspace | ✅ | ✅ |\n| `?lang=…\u0026userName=…` threaded into pad URL | ✅ | ✅ |\n\n### Deep links and integration\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| `etherpad-app://` deep-link scheme | ✅ registered, partial handler | ✅ |\n| Open pad by URL dialog | ✅ | ✅ |\n| Receive shared URL from another app | n/a | ✅ Android share intent |\n| \"Open in browser\" escape for blocked / X-Frame-DENY pads | n/a | ✅ |\n\n### Settings, theming, i18n\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| Light / dark / auto theme | ✅ | ✅ |\n| Default zoom override | ✅ | ✅ |\n| Accent colour override | ✅ | ✅ |\n| User display name pre-fill | ✅ | ✅ |\n| \"Remember open pads on quit\" toggle | ✅ | ✅ |\n| Confirmation dialog before \"Clear all pad history\" | ✅ | ✅ |\n| Shell strings translated (en, es, fr, de, pt/pt-br, it) | ✅ | ✅ |\n| Pad-iframe locale via `?lang=` (115 etherpad-core locales) | ✅ | ✅ |\n| HTML root `lang` attribute follows active locale | ✅ | ✅ |\n\n### Keyboard, gestures, window\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| Keyboard shortcuts (`Ctrl+T` / `Ctrl+W` / `Ctrl+K` / `Ctrl+1..9` / `Ctrl+,`) | ✅ | n/a |\n| Swipe gesture between pads | n/a | ✅ |\n| Android back button collapses → switches → exits | n/a | ✅ |\n| Window bounds + active workspace restored on relaunch | ✅ | n/a |\n| Minimise-to-tray | ✅ | n/a |\n| Tray icon (B\u0026W silhouette, theme-adaptive ⏳) | ✅ | n/a |\n\n### Permissions and security\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| Per-instance isolated session partition | ✅ `persist:ws-\u003cuuid\u003e` | ✅ Capacitor Preferences keyed by workspace id |\n| Permission pre-allow for `ep_webrtc` plugins (cam/mic/fullscreen/clipboard/screen) | ✅ narrow allowlist | ⏳ Phase 6b native plugin (camera/mic delegation) |\n| Deny-by-default with prompt-on-request, persisted decisions | ⏳ planned | ⏳ planned |\n| HTTPS-with-self-signed-cert trust UX | ⏳ planned | ⏳ planned |\n\n### Packaging, distribution, updates\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| Linux AppImage + `.deb` + Snap | ✅ | n/a |\n| Windows NSIS installer + portable `.exe` | ✅ unsigned | n/a |\n| macOS DMG (arm64 + x64) | ✅ unsigned | n/a |\n| Android signed release APK + Play Store listing | n/a | ⏳ |\n| iOS build | n/a | scaffold present, not built |\n| `release-please` conventional-commits release flow | ✅ | shared release flow planned |\n| In-app auto-update | ✅ electron-updater (AppImage + `.deb` + Windows NSIS) | n/a — store handles updates |\n| Snap channel publishing (`edge` auto, `stable` gated) | ✅ | n/a |\n\n### Tests and CI\n\n| Feature | Desktop | Mobile |\n|---|---|---|\n| Vitest unit + component tests | ✅ | ✅ shared with shell |\n| Playwright Electron e2e | ✅ against in-process mock Etherpad | n/a |\n| Playwright web smoke (vite preview) | n/a | ✅ |\n| Android emulator click-through smoke (uiautomator) | n/a | ✅ scaffolded |\n| CI runs on every PR (lint / typecheck / unit / e2e) | ✅ | ✅ |\n\n## What's next\n\nRoughly in the order it'll be tackled. Items that apply to one shell are\nmarked; everything else lands on both.\n\n### Soon\n\n- **Mobile permissions plugin (Phase 6b)** — native Kotlin delegation for\n  camera, mic, clipboard, fullscreen, file picker so `ep_webrtc` and friends\n  work inside the WebView on Android.\n- **`/admin` for embedded local servers** — when a user picks \"Use a local\n  server\", expose Etherpad's `/admin` UI in a tab (or a dedicated dialog)\n  so they can install plugins, manage users, and tweak settings against\n  their local instance. Without this, the local server is read-only from\n  the user's perspective.\n- **Pre-compile Etherpad's TS at build time** — today the embedded server\n  uses `tsx/cjs` to transpile every TS file on boot. That spikes RAM hard\n  on cold start (Electron-as-Node parent + tsx + Etherpad's plugin tree)\n  and gets OOM-killed on memory-constrained machines. Shipping a pre-\n  compiled copy of `resources/etherpad/` removes the tsx-at-runtime cost\n  and halves the embedded footprint. Larger ship artifact, smaller RAM.\n- **Desktop permission UX upgrade** — replace the narrow pre-allow with a\n  deny-by-default prompt-on-request flow, decisions persisted per\n  instance+origin. Same UI ships to mobile after Phase 6b lands.\n- **Mobile content search in quick switcher** — desktop already indexes pad\n  bodies via `/export/txt`. Mobile stubs that path; wire it.\n- **Mobile Play Store release** — signed AAB, store listing, edge / stable\n  channels mirroring the Snap split.\n- **Cross-pad content search on mobile** — see above.\n- **Tray icon theme adaptation (desktop)** — detect `nativeTheme`, ship a\n  black-silhouette variant for light trays.\n\n### Later\n\n- **Offline editing — diverges between shells.** Desktop usually has a\n  long-lived connection; mobile is offline by default. Working assumption:\n  desktop gets a cache-then-OT-replay path; mobile gets a CRDT-based local\n  edit queue with sync-on-reconnect. Both surface the same UX (greyed\n  \"offline\" banner, queued-edit indicator) but the engines differ. Spec work\n  in flight.\n- **Native OS notifications** — pad mentions, presence.\n- **Drag-tab-to-reorder + tear-off-into-new-window** (desktop).\n- **HTTPS-with-self-signed-cert trust UX** — applies to both, more useful on\n  desktop where users add local Etherpad URLs.\n\n### Explicit non-goals\n\n- **No code-signing fees.** Windows + macOS will continue shipping unsigned\n  binaries — SmartScreen / Gatekeeper warn on first run; both work fine\n  afterwards. Apple's $99/yr Developer ID and Microsoft's EV / Azure Trusted\n  Signing programs gate independent open-source software behind a recurring\n  tax we won't pay. Forks / distributors / enterprises are welcome to roll\n  their own signed builds under Apache-2.0.\n\n## Fork it, brand it, ship it (white-label)\n\nThe code is Apache-2.0. If you want to ship Etherpad apps under your own\nbrand — your name on the home screen, your icon, your accent colour, your\nGitHub Releases feed — clone, edit one JSON file, and build.\n\n```bash\ngit clone https://github.com/ether/etherpad-desktop.git mypad \u0026\u0026 cd mypad\npnpm install\ncp brand.example.json brand.json\n# edit brand.json: name, appId, androidPackage, accent, description, …\npnpm white-label\npnpm package                 # desktop installers in release/\npnpm mobile:android:run      # mobile APK on attached device/emulator\n```\n\n`pnpm white-label` rewrites every identity-bearing field across\n`electron-builder.yml`, `capacitor.config.ts`, the Android Gradle and\n`strings.xml`, the shell i18n, and the CSS accent token. Re-runnable —\nno state leaks between invocations. CLI flags override `brand.json` for\nad-hoc builds: `pnpm white-label --name FooPad --accent '#ff5500'`.\n\n**Icons stay manual for v1.** The script reports which PNGs to replace:\n\n- `packages/desktop/build/icons/icon-{16,32,64,128,256,512}.png`\n- `packages/desktop/build/icons/icon.{ico,icns}`\n- `packages/mobile/android/app/src/main/res/mipmap-*/ic_launcher*.png`\n\nDrop your own assets in at those paths before `pnpm package` /\n`pnpm mobile:android:run`.\n\n**Code signing is on you.** We don't sign upstream releases (Apple's\n$99/yr Developer ID and Microsoft's EV cert programs gate independent\nopen-source software behind a recurring tax we won't pay). A fork that\nwants warning-free first launches signs with its own credentials. The\nbuild config has `mac.identity: null` and `win.signAndEditExecutable:\nfalse` by default — flip those to your own creds when you're ready.\n\n**Publishing is on you too.** Point `brand.json.publish.{owner,repo}` at\nyour own GitHub repo; electron-updater will look for new releases there.\nMirror `.github/workflows/release.yml` or use whatever distribution\nchannel you prefer.\n\n`brand.json` is gitignored so your local branding doesn't leak into\nupstream PRs.\n\n## License\n\nApache-2.0. See [LICENSE](LICENSE) and [NOTICE](NOTICE).\n\nThis project is a thin client; the Etherpad server is upstream software with\nits own license and contributors. Etherpad is at\n\u003chttps://github.com/ether/etherpad\u003e.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fether%2Fetherpad-desktop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fether%2Fetherpad-desktop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fether%2Fetherpad-desktop/lists"}