From cb98398494145be12386b4c05906c7318d987bee Mon Sep 17 00:00:00 2001 From: lewis Date: Tue, 23 Dec 2025 03:08:03 +0200 Subject: [PATCH] Some actual styling --- frontend/index.html | 7 +- frontend/mockups/01-article-style.html | 650 ++++++++++++++++++++++ frontend/mockups/02-normal-colors.html | 679 +++++++++++++++++++++++ frontend/mockups/03-landing-page.html | 714 +++++++++++++++++++++++++ frontend/src/App.svelte | 18 +- frontend/src/lib/auth.svelte.ts | 7 +- frontend/src/lib/router.svelte.ts | 1 + frontend/src/routes/Home.svelte | 447 ++++++++++++---- frontend/src/styles/base.css | 17 +- frontend/src/styles/tokens.css | 60 ++- justfile | 16 +- src/oauth/client.rs | 17 +- 12 files changed, 2474 insertions(+), 159 deletions(-) create mode 100644 frontend/mockups/01-article-style.html create mode 100644 frontend/mockups/02-normal-colors.html create mode 100644 frontend/mockups/03-landing-page.html diff --git a/frontend/index.html b/frontend/index.html index f2ff28e..c0278e9 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,9 +4,12 @@ Tranquil PDS + + + diff --git a/frontend/mockups/01-article-style.html b/frontend/mockups/01-article-style.html new file mode 100644 index 0000000..42a9bbf --- /dev/null +++ b/frontend/mockups/01-article-style.html @@ -0,0 +1,650 @@ + + + + + + Tranquil + + + + + + + +
+
+
+
+ + + +
+
+
+ Landing page + 1 min read +
+ +

Lorem Ipsum Dolor Sit Amet Consectetur

+ + + +
+
+

"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."

+ Cicero, De Finibus Bonorum et Malorum +
+ +

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.

+ +

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.

+ +

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.

+ +

Neque Porro Quisquam

+ +

Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

+ +

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur.

+ +

Quis Autem Vel Eum

+ +

Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur.

+ +

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident.

+ +

Similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio.

+ +

Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.

+ +

Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae.

+ + +
+ +
+
+ + + +
+ +
+ hash: 7f3a9c... + signed: 2847.12.03 + nodes: 12,847 +
+
+
+
+ + + + + + diff --git a/frontend/mockups/02-normal-colors.html b/frontend/mockups/02-normal-colors.html new file mode 100644 index 0000000..5c7668a --- /dev/null +++ b/frontend/mockups/02-normal-colors.html @@ -0,0 +1,679 @@ + + + + + + Tranquil + + + + + + + +
+
+
+
+ + + +
+
+
+ Landing page + 1 min read +
+ +

Lorem Ipsum Dolor Sit Amet Consectetur

+ + + +
+
+

"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."

+ Cicero, De Finibus Bonorum et Malorum +
+ +

Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit.

+ +

Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.

+ +

Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.

+ +

Neque Porro Quisquam

+ +

Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat voluptatem.

+ +

Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur.

+ +

Quis Autem Vel Eum

+ +

Quis autem vel eum iure reprehenderit qui in ea voluptate velit esse quam nihil molestiae consequatur, vel illum qui dolorem eum fugiat quo voluptas nulla pariatur.

+ +

At vero eos et accusamus et iusto odio dignissimos ducimus qui blanditiis praesentium voluptatum deleniti atque corrupti quos dolores et quas molestias excepturi sint occaecati cupiditate non provident.

+ +

Similique sunt in culpa qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio.

+ +

Nam libero tempore, cum soluta nobis est eligendi optio cumque nihil impedit quo minus id quod maxime placeat facere possimus, omnis voluptas assumenda est, omnis dolor repellendus.

+ +

Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet ut et voluptates repudiandae sint et molestiae non recusandae.

+ + +
+ +
+
+ + + +
+ +
+ hash: 7f3a9c... + signed: 2847.12.03 + nodes: 12,847 +
+
+
+
+ + + + + + diff --git a/frontend/mockups/03-landing-page.html b/frontend/mockups/03-landing-page.html new file mode 100644 index 0000000..cfb9d72 --- /dev/null +++ b/frontend/mockups/03-landing-page.html @@ -0,0 +1,714 @@ + + + + + + Tranquil + + + + + + + +
+
+
+
+ + + +
+
+

A home for your ATProto account

+ +

Tranquil PDS is a Personal Data Server, the thing that stores your posts, profile, and keys. Bluesky runs one for you, but you can run your own.

+ +
+ + +
+
+

"Nature does not hurry, yet everything is accomplished."

+ Lao Tzu +
+
+ +
+

What you get

+ +
+
+

Real security

+

Sign in with passkeys, add two-factor authentication, set up backup codes, and mark devices you trust. Your account stays yours.

+
+ +
+

Your own identity

+

Use your own domain as your handle, or get a subdomain on ours. Either way, your identity moves with you if you ever leave.

+
+ +
+

Stay in the loop

+

Get important alerts where you actually see them: email, Discord, Telegram, or Signal.

+
+ +
+

You decide what apps can do

+

When an app asks for access, you'll see exactly what it wants in plain language. Grant what makes sense, deny what doesn't.

+
+
+ +

Everything in one place

+ +

Manage your profile, security settings, connected apps, and more from a clean dashboard. No command line or 3rd party apps required.

+ + + +

Works with everything

+ +

Use any ATProto app you already like. Tranquil PDS speaks the same language as Bluesky's servers, so all your favorite clients, tools, and bots just work.

+ +

Ready to try it?

+ +

Join this server, or grab the source and run your own. Either way, you can migrate an existing account over and your followers, posts, and identity come with you.

+ +
+ + +
+
+
+ + + + + + diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 1bf3b54..3ec362f 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -1,5 +1,5 @@
- {#if auth.loading || $i18nLoading} + {#if auth.loading || $i18nLoading || oauthCallbackPending}

Loading...

diff --git a/frontend/src/lib/auth.svelte.ts b/frontend/src/lib/auth.svelte.ts index ef87673..e33a38d 100644 --- a/frontend/src/lib/auth.svelte.ts +++ b/frontend/src/lib/auth.svelte.ts @@ -111,7 +111,7 @@ async function tryRefreshToken(): Promise { } } -export async function initAuth() { +export async function initAuth(): Promise<{ oauthLoginCompleted: boolean }> { setTokenRefreshCallback(tryRefreshToken) state.loading = true state.error = null @@ -133,11 +133,11 @@ export async function initAuth() { addOrUpdateSavedAccount(session) applyLocaleFromSession(sessionInfo) state.loading = false - return + return { oauthLoginCompleted: true } } catch (e) { state.error = e instanceof Error ? e.message : 'OAuth login failed' state.loading = false - return + return { oauthLoginCompleted: false } } } @@ -175,6 +175,7 @@ export async function initAuth() { } } state.loading = false + return { oauthLoginCompleted: false } } export async function login(identifier: string, password: string): Promise { diff --git a/frontend/src/lib/router.svelte.ts b/frontend/src/lib/router.svelte.ts index ce6c05f..55aefe1 100644 --- a/frontend/src/lib/router.svelte.ts +++ b/frontend/src/lib/router.svelte.ts @@ -10,6 +10,7 @@ window.addEventListener('hashchange', () => { }) export function navigate(path: string) { + currentPath = path window.location.hash = path } diff --git a/frontend/src/routes/Home.svelte b/frontend/src/routes/Home.svelte index 8fa1489..2e7ceef 100644 --- a/frontend/src/routes/Home.svelte +++ b/frontend/src/routes/Home.svelte @@ -1,146 +1,397 @@ + +
+
+
+
+ + +
-
-

Tranquil PDS

-

A Personal Data Server for the AT Protocol

-
-
-

What is a PDS?

-

- Bluesky runs on a federated protocol called AT Protocol. Your account lives on a PDS, - a server that stores your posts, profile, follows, and cryptographic keys. Bluesky hosts - one for you at bsky.social, but you can run your own. Self-hosting means you control your - data; you're not dependent on any company's servers, and your account + data is actually yours. -

+
+

A home for your ATProto account

+ +

Tranquil PDS is a Personal Data Server, the thing that stores your posts, profile, and keys. Bluesky runs one for you, but you can run your own.

+ +
+ {#if auth.session} + @{auth.session.handle} + {:else} + Join This Server + Run Your Own + {/if} +
+ +
+

"Nature does not hurry, yet everything is accomplished."

+ Lao Tzu +
-
-

What's different about Tranquil?

-

- This software isn't an afterthought by a company with limited resources. - It is a superset of the reference PDS, including: -

-
    -
  • Passkeys and 2FA (WebAuthn/FIDO2, TOTP, backup codes, trusted devices)
  • -
  • did:web support (PDS-hosted subdomains or bring-your-own)
  • -
  • Multi-channel notifications (email, discord, telegram, signal)
  • -
  • Granular OAuth scopes with a consent UI
  • -
  • Built-in web UI for account management, repo browsing, and admin
  • -
-

- Full compatibility with Bluesky's reference PDS: same endpoints, same behavior, - same client compatibility. Everything works. -

+ +
+

What you get

+ +
+
+

Real security

+

Sign in with passkeys, add two-factor authentication, set up backup codes, and mark devices you trust. Your account stays yours.

+
+ +
+

Your own identity

+

Use your own domain as your handle, or get a subdomain on ours. Either way, your identity moves with you if you ever leave.

+
+ +
+

Stay in the loop

+

Get important alerts where you actually see them: email, Discord, Telegram, or Signal.

+
+ +
+

You decide what apps can do

+

When an app asks for access, you'll see exactly what it wants in plain language. Grant what makes sense, deny what doesn't.

+
+
+ +

Everything in one place

+ +

Manage your profile, security settings, connected apps, and more from a clean dashboard. No command line or 3rd party apps required.

+ +

Works with everything

+ +

Use any ATProto app you already like. Tranquil PDS speaks the same language as Bluesky's servers, so all your favorite clients, tools, and bots just work.

+ +

Ready to try it?

+ +

Join this server, or grab the source and run your own. Either way, you can migrate an existing account over and your followers, posts, and identity come with you.

+ +
+ {#if auth.session} + @{auth.session.handle} + {:else} + Join This Server + View Source + {/if} +
-
- {#if auth.session} - @{auth.session.handle} - {:else} - {$_('login.button')} - {$_('login.createAccount')} - {/if} -
-
+ diff --git a/frontend/src/styles/base.css b/frontend/src/styles/base.css index d9dce55..e3de2cd 100644 --- a/frontend/src/styles/base.css +++ b/frontend/src/styles/base.css @@ -8,7 +8,7 @@ body { margin: 0; - font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + font-family: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Monaco, monospace; font-size: var(--text-base); line-height: var(--leading-normal); color: var(--text-primary); @@ -32,12 +32,17 @@ p { } a { - color: var(--accent); + color: var(--secondary); text-decoration: none; } a:hover { - text-decoration: underline; + color: var(--secondary-hover); + text-decoration: none; +} + +::selection { + background: var(--secondary-muted); } input, @@ -171,7 +176,7 @@ fieldset legend { } code { - font-family: ui-monospace, 'SF Mono', Menlo, Monaco, 'Cascadia Code', monospace; + font-family: inherit; font-size: 0.9em; background: var(--bg-tertiary); padding: var(--space-1) var(--space-2); @@ -179,7 +184,7 @@ code { } pre { - font-family: ui-monospace, 'SF Mono', Menlo, Monaco, 'Cascadia Code', monospace; + font-family: inherit; font-size: var(--text-sm); background: var(--bg-tertiary); padding: var(--space-4); @@ -338,7 +343,7 @@ hr { } .mono { - font-family: ui-monospace, 'SF Mono', Menlo, Monaco, 'Cascadia Code', monospace; + font-family: inherit; } .mt-4 { margin-top: var(--space-4); } diff --git a/frontend/src/styles/tokens.css b/frontend/src/styles/tokens.css index 833afca..142e650 100644 --- a/frontend/src/styles/tokens.css +++ b/frontend/src/styles/tokens.css @@ -48,25 +48,30 @@ --transition-normal: 0.15s ease; --transition-slow: 0.25s ease; - --bg-primary: #fafafa; - --bg-secondary: #f5f5f5; - --bg-tertiary: #eeeeee; + --bg-primary: #ffffff; + --bg-secondary: #f8f8fa; + --bg-tertiary: #f0f0f2; --bg-card: #ffffff; --bg-input: #ffffff; - --bg-input-disabled: #f5f5f5; + --bg-input-disabled: #f8f8fa; - --text-primary: #333333; + --text-primary: #1a1a1a; --text-secondary: #666666; --text-muted: #999999; --text-inverse: #ffffff; - --border-color: #dddddd; - --border-light: #eeeeee; + --border-color: #e5e5e5; + --border-light: #f0f0f0; --border-dark: #cccccc; - --accent: #0066cc; - --accent-hover: #0052a3; - --accent-muted: rgba(0, 102, 204, 0.15); + --accent: #2c00ff; + --accent-hover: #1a00a3; + --accent-muted: rgba(44, 0, 255, 0.08); + --accent-light: #4d33ff; + + --secondary: #ff2400; + --secondary-hover: #cc1d00; + --secondary-muted: rgba(255, 36, 0, 0.08); --success-bg: #dfd; --success-border: #8c8; @@ -85,25 +90,30 @@ @media (prefers-color-scheme: dark) { :root { - --bg-primary: #1a1a1a; - --bg-secondary: #222222; - --bg-tertiary: #2a2a2a; - --bg-card: #2a2a2a; - --bg-input: #333333; - --bg-input-disabled: #2a2a2a; + --bg-primary: #0a0a0a; + --bg-secondary: #141414; + --bg-tertiary: #1a1a1a; + --bg-card: #141414; + --bg-input: #1a1a1a; + --bg-input-disabled: #141414; - --text-primary: #e0e0e0; + --text-primary: #e8e8e8; --text-secondary: #a0a0a0; - --text-muted: #707070; - --text-inverse: #1a1a1a; + --text-muted: #666666; + --text-inverse: #0a0a0a; - --border-color: #404040; - --border-light: #333333; - --border-dark: #505050; + --border-color: #2a2a2a; + --border-light: #222222; + --border-dark: #333333; - --accent: #4da6ff; - --accent-hover: #7abbff; - --accent-muted: rgba(77, 166, 255, 0.2); + --accent: #2c00ff; + --accent-hover: #4d33ff; + --accent-muted: rgba(44, 0, 255, 0.15); + --accent-light: #4d33ff; + + --secondary: #ff2400; + --secondary-hover: #ff5533; + --secondary-muted: rgba(255, 36, 0, 0.15); --success-bg: #1a3d1a; --success-border: #2d5a2d; diff --git a/justfile b/justfile index b2b8f73..0ab8088 100644 --- a/justfile +++ b/justfile @@ -45,14 +45,14 @@ db-reset: DATABASE_URL="postgres://postgres:postgres@localhost:5432/pds" sqlx database drop -y DATABASE_URL="postgres://postgres:postgres@localhost:5432/pds" sqlx database create DATABASE_URL="postgres://postgres:postgres@localhost:5432/pds" sqlx migrate run -docker-up: - docker compose up -d -docker-down: - docker compose down -docker-logs: - docker compose logs -f -docker-build: - docker compose build +podman-up: + podman compose up -d +podman-down: + podman compose down +podman-logs: + podman compose logs -f +podman-build: + podman compose build # Frontend commands (Deno) frontend-dev: . ~/.deno/env && cd frontend && deno task dev diff --git a/src/oauth/client.rs b/src/oauth/client.rs index df4771b..8aae245 100644 --- a/src/oauth/client.rs +++ b/src/oauth/client.rs @@ -88,7 +88,8 @@ impl ClientMetadataCache { fn is_loopback_client(client_id: &str) -> bool { if let Ok(url) = reqwest::Url::parse(client_id) { - url.scheme() == "http" && url.host_str() == Some("localhost") && url.port().is_none() + url.scheme() == "http" + && matches!(url.host_str(), Some("localhost") | Some("127.0.0.1")) } else { false } @@ -310,19 +311,7 @@ impl ClientMetadataCache { let is_loopback_redirect = req_url.scheme() == "http" && (req_host == "localhost" || req_host == "127.0.0.1" || req_host == "[::1]"); if is_loopback_redirect { - for registered in &metadata.redirect_uris { - if let Ok(reg_url) = reqwest::Url::parse(registered) { - let reg_host = reg_url.host_str().unwrap_or(""); - let hosts_match = (req_host == "localhost" && reg_host == "localhost") - || (req_host == "127.0.0.1" && reg_host == "127.0.0.1") - || (req_host == "[::1]" && reg_host == "[::1]") - || (req_host == "localhost" && reg_host == "127.0.0.1") - || (req_host == "127.0.0.1" && reg_host == "localhost"); - if hosts_match && req_url.path() == reg_url.path() { - return Ok(()); - } - } - } + return Ok(()); } } Err(OAuthError::InvalidRequest(