plspace
It's always Pleroma.
A static, client-side frontend for Pleroma that looks and behaves like MySpace circa 2005.
This is a 100% Claude and ChatGPT vibe-coded application.
Point it at your server. It talks to that server's REST API directly from the
browser — there is no plspace backend, and dist/ is a folder of static files
you can host anywhere.
Repository
The canonical repository is hosted on Radicle:
rad://z2gAKC6ESt5ZBV419uVPf2vFtEHCT.
What it looks like
| MySpace | plspace |
|---|---|
| Profile page: photo, headline, vitals, Contacting box, Interests table | #/@user@server |
| Latest Blog Entries with "(view more)" | Account statuses, headline list |
| Blurbs: "About me", "Who I'd like to meet" | The bio, split on a heading if you wrote one |
| Friend Space grid | Followers |
| Mail Center, Friend Request Manager | Notifications, follow requests |
| Bulletin Space | The local timeline |
| Kudos | Favourites |
| Custom profile layouts | CSS in a profile field named css |
Running it
npm install && npm run dev
npm run build
npm run build type-checks and then bundles to dist/. Because routing is
hash-based, the output works from any path — a subdirectory, an S3 bucket,
GitHub Pages — with no server rewrite rules.
Signing in
plspace registers itself as an OAuth app on your server the first time you sign
in there, then redirects you to that server's own consent screen. Your password
is never entered into plspace; it only ever receives OAuth tokens, which are
stored in localStorage and used directly from your browser. Short-lived access
tokens are renewed with a server-issued refresh token when available.
PKCE is used where the server supports it, with an automatic fallback for servers that don't.
You can also browse without signing in — click Just look around. Note that
many servers set restrict_unauthenticated and refuse timeline reads from
anonymous callers; the suggested servers on the sign-in page are ones verified
to allow it.
Making it yours
Settings has a CSS editor, five starter layouts, and a full class reference.
Everything about the appearance is overridable — see
src/styles/README.md for the contract. In short:
no CSS framework, no Svelte scoped styles, no hashed class names, every colour
and metric is a custom property, and user CSS is always last in the cascade so
plain single-class selectors win without !important.
There is no built-in dark mode. A dark theme is just token overrides, so it ships as a preset (Midnight, Terminal) you can apply, edit or replace, rather than as a toggle you can't.
To publish a layout other people see on your profile, put CSS in a profile field
named css on your server. It is scoped to your profile's subtree and filtered
before it is applied.
Your profile fields also drive the profile page: name one Music, Movies,
Television, Books or Heroes and it fills the Interests table; name one
Mood, Location, Gender or Headline and it fills the block beside your
photo.
Layout
src/
lib/
api/ client (fetch, Link-header pagination), OAuth, endpoints, entity types
stores/ session, Feed (cursor pagination), theme (user + profile CSS)
util/ HTML sanitizing, 2005-flavoured date formats, account -> profile mapping
router.svelte.ts
themes.ts starter layouts, written only in terms of tokens
components/
chrome/ header, nav, footer
common/ Module, RichText, Avatar, Pager, TabBar
profile/ identity, contacting box, interests, details, friend space
blog/ entry, attachments, poll, preview card, composer
people/ person row and list
routes/ Home, Profile, Timeline, StatusPage, Mail, Browse, Search, Compose, Login, Settings
styles/ tokens, base, layout, chrome, module, profile, blog, forms
Safety notes
All HTML from the API — post content, bios, profile field values — is sanitized
with DOMPurify before it reaches {@html}, in exactly one component
(RichText.svelte) plus the profile-field helper. Mention and hashtag links are
rewritten to in-app routes; every other link gets target="_blank" with
rel="noopener noreferrer".
Compatibility
Written against the standard /api/v1 REST API and the local Egregoros
implementation, as well as live servers.
Anything implementations differ on degrades rather than fails: /api/v2/instance
falls back to v1, /api/v1/accounts/lookup falls back to search, pagination
falls back to the last item's id when a server drops the Link header, and
per-account privacy flags are read in both their spellings. Egregoros'
hour-long access tokens and rotating refresh tokens are supported; notification
folders are filtered defensively because its current API ignores types[].
Profile controls are capability-aware, so unsupported Egregoros fields are not
shown as though they could be saved. Other software speaking the same API
therefore works, but Pleroma is what this targets.