diff --git a/.env.example b/.env.example index 6c96c15..471a3c4 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,7 @@ PUBLIC_SEARCH=no # yes | no — enable the search box (req PUBLIC_LABELS= # comma-separated discussion labels (e.g., bug,feature,question) PUBLIC_NOSTRCONNECT_RELAYS= # comma-separated relays for nostrconnect:// (bunker) login; defaults to bucket.coracle.social,relay.primal.net PUBLIC_BLOSSOM_URL= # Blossom server URL for uploads (e.g., https://blossom.primal.net) +PUBLIC_EXTERNAL_CLIENT= # external client for profiles/events, {e} is the bech32 entity; default https://njump.me/{e} # Theme color overrides — quote the value, a leading # is read as a comment otherwise PUBLIC_ACCENT_COLOR= # override the accent color (default #e32a6d), e.g. "#00ff00"; hover shade derived PUBLIC_SECONDARY_COLOR= # override the secondary color (default #ffaf25), e.g. "#0000ff"; hover shade derived diff --git a/README.md b/README.md index bb279b2..99566c5 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Squalk is configured entirely through environment variables (all prefixed `PUBLI | `PUBLIC_SEARCH` | no | `no` | `yes` to show a search box at the top of the homepage. Requires a relay with NIP-50 search support. | | `PUBLIC_LABELS` | no | — | Comma-separated discussion labels offered when composing, e.g. `bug,feature,question`. | | `PUBLIC_BLOSSOM_URL` | no | — | Blossom server URL used for media uploads, e.g. `https://blossom.primal.net`. Uploads are disabled when unset. | +| `PUBLIC_EXTERNAL_CLIENT` | no | `https://njump.me/{e}` | URL template of the external Nostr client used to open profiles and events outside the forum. `{e}` is replaced with the bech32 entity (npub, nevent, naddr), e.g. `https://nostr.at/{e}`. | | `PUBLIC_ACCENT_COLOR` | no | `#e32a6d` | Override the accent (primary) color. Quote the value (`"#00ff00"`) — an unquoted leading `#` is read as a comment. The hover shade is derived automatically. | | `PUBLIC_SECONDARY_COLOR` | no | `#ffaf25` | Override the secondary color. Same quoting rule and derived hover shade as above. | diff --git a/src/lib/components/PostContent.svelte b/src/lib/components/PostContent.svelte index 2dbd18b..66a0db1 100644 --- a/src/lib/components/PostContent.svelte +++ b/src/lib/components/PostContent.svelte @@ -2,6 +2,7 @@ import { loadNostrUser, type NostrUser } from "$lib/gadgets"; import * as nip19 from "@nostr/tools/nip19"; import { profilePath } from "$lib/forum/profiles"; + import { externalLink } from "$lib/config"; import { resolveThreadRef, threadRefHref, @@ -311,7 +312,7 @@ const entity = dest.slice(6).toLowerCase(); try { nip19.decode(entity); - return { type: "link", href: `https://njump.me/${entity}`, label }; + return { type: "link", href: externalLink(entity), label }; } catch { return { type: "text", value: label }; } @@ -642,7 +643,7 @@ > {:else} {inline.label}`, ); + +// External Nostr client used to open events and profiles that live outside +// the forum. {e} is replaced with the bech32 entity (npub, nevent, naddr...). +const DEFAULT_EXTERNAL_CLIENT = "https://njump.me/{e}"; +export const EXTERNAL_CLIENT = env.PUBLIC_EXTERNAL_CLIENT?.includes("{e}") + ? env.PUBLIC_EXTERNAL_CLIENT + : DEFAULT_EXTERNAL_CLIENT; +export const EXTERNAL_CLIENT_NAME = (() => { + try { + return new URL(EXTERNAL_CLIENT.replace("{e}", "x")).hostname; + } catch { + return "external client"; + } +})(); +export function externalLink(entity: string): string { + return EXTERNAL_CLIENT.replace("{e}", entity); +} diff --git a/src/lib/linkify.ts b/src/lib/linkify.ts index abc3117..4ffbe36 100644 --- a/src/lib/linkify.ts +++ b/src/lib/linkify.ts @@ -1,5 +1,5 @@ import * as nip19 from "@nostr/tools/nip19"; -import { RELAY_URL } from "./config"; +import { RELAY_URL, externalLink } from "./config"; // Curated TLD list: gTLDs, popular new gTLDs, common ccTLDs const TLDS = [ @@ -157,7 +157,7 @@ function decodeEntity(entity: string): ChatToken | null { return { type: "entity", entity, - href: `https://njump.me/${entity}`, + href: externalLink(entity), label: shortNostrEntity(entity), id: decoded.type === "note" ? decoded.data : decoded.data.id, }; @@ -165,7 +165,7 @@ function decodeEntity(entity: string): ChatToken | null { return { type: "entity", entity, - href: `https://njump.me/${entity}`, + href: externalLink(entity), label: shortNostrEntity(entity), }; } catch { diff --git a/src/routes/profile/[id]/+page.svelte b/src/routes/profile/[id]/+page.svelte index 42b7ebc..b23f953 100644 --- a/src/routes/profile/[id]/+page.svelte +++ b/src/routes/profile/[id]/+page.svelte @@ -1,6 +1,6 @@