From d59fa537553cc1ab6ed7c01755205de15de63c4b Mon Sep 17 00:00:00 2001 From: "whey.party" Date: Thu, 12 Feb 2026 07:49:17 +0700 Subject: [PATCH] about page --- src/routeTree.gen.ts | 21 ++++ src/routes/about.tsx | 241 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+) create mode 100644 src/routes/about.tsx diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 7ac299e..8fbc47a 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -14,6 +14,7 @@ import { Route as SearchRouteImport } from './routes/search' import { Route as NotificationsRouteImport } from './routes/notifications' import { Route as ModerationRouteImport } from './routes/moderation' import { Route as FeedsRouteImport } from './routes/feeds' +import { Route as AboutRouteImport } from './routes/about' import { Route as PathlessLayoutRouteImport } from './routes/_pathlessLayout' import { Route as IndexRouteImport } from './routes/index' import { Route as CallbackIndexRouteImport } from './routes/callback/index' @@ -55,6 +56,11 @@ const FeedsRoute = FeedsRouteImport.update({ path: '/feeds', getParentRoute: () => rootRouteImport, } as any) +const AboutRoute = AboutRouteImport.update({ + id: '/about', + path: '/about', + getParentRoute: () => rootRouteImport, +} as any) const PathlessLayoutRoute = PathlessLayoutRouteImport.update({ id: '/_pathlessLayout', getParentRoute: () => rootRouteImport, @@ -138,6 +144,7 @@ const ProfileDidPostRkeyImageIRoute = export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/about': typeof AboutRoute '/feeds': typeof FeedsRoute '/moderation': typeof ModerationRoute '/notifications': typeof NotificationsRoute @@ -158,6 +165,7 @@ export interface FileRoutesByFullPath { } export interface FileRoutesByTo { '/': typeof IndexRoute + '/about': typeof AboutRoute '/feeds': typeof FeedsRoute '/moderation': typeof ModerationRoute '/notifications': typeof NotificationsRoute @@ -180,6 +188,7 @@ export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/_pathlessLayout': typeof PathlessLayoutRouteWithChildren + '/about': typeof AboutRoute '/feeds': typeof FeedsRoute '/moderation': typeof ModerationRoute '/notifications': typeof NotificationsRoute @@ -203,6 +212,7 @@ export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath fullPaths: | '/' + | '/about' | '/feeds' | '/moderation' | '/notifications' @@ -223,6 +233,7 @@ export interface FileRouteTypes { fileRoutesByTo: FileRoutesByTo to: | '/' + | '/about' | '/feeds' | '/moderation' | '/notifications' @@ -244,6 +255,7 @@ export interface FileRouteTypes { | '__root__' | '/' | '/_pathlessLayout' + | '/about' | '/feeds' | '/moderation' | '/notifications' @@ -267,6 +279,7 @@ export interface FileRouteTypes { export interface RootRouteChildren { IndexRoute: typeof IndexRoute PathlessLayoutRoute: typeof PathlessLayoutRouteWithChildren + AboutRoute: typeof AboutRoute FeedsRoute: typeof FeedsRoute ModerationRoute: typeof ModerationRoute NotificationsRoute: typeof NotificationsRoute @@ -317,6 +330,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof FeedsRouteImport parentRoute: typeof rootRouteImport } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } '/_pathlessLayout': { id: '/_pathlessLayout' path: '' @@ -475,6 +495,7 @@ const ProfileDidPostRkeyRouteWithChildren = const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, PathlessLayoutRoute: PathlessLayoutRouteWithChildren, + AboutRoute: AboutRoute, FeedsRoute: FeedsRoute, ModerationRoute: ModerationRoute, NotificationsRoute: NotificationsRoute, diff --git a/src/routes/about.tsx b/src/routes/about.tsx new file mode 100644 index 0000000..5e89150 --- /dev/null +++ b/src/routes/about.tsx @@ -0,0 +1,241 @@ +import { createFileRoute } from '@tanstack/react-router' + +import { FORCED_LABELER_DIDS, HOST_ABOUT_MARKDOWN, HOST_ADMIN, HOST_DESCRIPTION, HOST_HERO, HOST_LABELMERGE, HOST_SIGNUP_PDS } from '~/../policy'; +import { Header } from '~/components/Header'; +import { defaultconstellationURL, defaultImgCDN, defaultLycanURL, defaultslingshotURL, defaultVideoCDN } from '~/utils/atoms'; + +import { ProfileSmall } from './__root'; +import { NotificationItem } from './notifications'; +//import { SettingHeading } from './settings'; + +export const Route = createFileRoute('/about')({ + component: RouteComponent, +}) + +function RouteComponent() { + return ( +
+
{ + if (window.history.length > 1) { + window.history.back(); + } else { + window.location.assign("/"); + } + }} + bottomBorderDisabled={false} + /> +
+ + {window.location.host} is a Red Dwarf instance that you can use to participate in the Bluesky social network. + {/* */} + {HOST_DESCRIPTION} +
+ ADMINISTERED BY: + +
+ + +
+
+ ) +} + +const REQUIRED_COMPONENTS = ["PolicyViewer"]; + +const COMPONENT_MAP: Record = { + // todo replace with actual policy viewer + PolicyViewer: () => , +}; + +function PolicyViewer() { + return ( + <> + {/* TODO: render all of the layered overlay enforced moderation stuff here or something idk. + still waiting on the server-sided queryLabels proxy and layered moderation spec and also feature bounded moderation spec to finish */} + + + ) +} + +function assertRequiredComponents(input: string) { + for (const name of REQUIRED_COMPONENTS) { + const pattern = new RegExp(`<${name}\\s*/>`); + if (!pattern.test(input)) { + throw new Error( + `Missing required policy component: <${name} />` + ); + } + } +} + +function renderInline(text: string) { + const parts: React.ReactNode[] = []; + let lastIndex = 0; + + const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; + let match; + + while ((match = linkRegex.exec(text))) { + const [full, label, url] = match; + const start = match.index; + + if (start > lastIndex) { + parts.push(text.slice(lastIndex, start)); + } + + parts.push( + + {label} + + ); + + lastIndex = start + full.length; + } + + if (lastIndex < text.length) { + parts.push(text.slice(lastIndex)); + } + + return parts; +} +export function Heading2({title}:{title:string}){ + return ( + + {title} + + ) +} +export function Heading3({title}:{title:string}){ + return ( + + {title} + + ) +} +export function Heading4({title}:{title:string}){ + return ( + + {title} + + ) +} +export function PolicyMarkdown({ source }: { source: string }) { + assertRequiredComponents(source); + + const blocks = source + .split(/\n{2,}/) // 2+ line breaks = new block + .map(b => b.trim()) + .filter(Boolean); + + return ( +
+ {blocks.map((block, i) => { + // Section heading + if (block.startsWith("## ")) { + const title = block.slice(3).trim(); + return ( + + ); + } + + // Self-closing component + const componentMatch = block.match(/^<([A-Z][A-Za-z0-9_]*)\s*\/>$/); + if (componentMatch) { + const name = componentMatch[1]; + const Component = COMPONENT_MAP[name]; + + if (!Component) { + throw new Error(`Unknown policy component: <${name} />`); + } + + return ; + } + + // Paragraph + return ( +

+ {renderInline(block)} +

+ ); + })} +
+ ); +} + + +function PolicyRenderer(){ + // + // policy.ts vars to show: + + // endorsed feeds (or should it be part of unauthed default experience?) + // endorsed feeds (should be shown in the explore tab too in lieu of feed discovery) + // - [ ] HOST_UNAUTHED_DEFAULT_FEEDS + // endorsed PDS + // - [ ] HOST_SIGNUP_PDS + // todo move the other default services into policy.ts + // todo re- sort policy.ts according to this component + // also the default services used like microcosm stuff and lycan and maybe the reliance of an appview for search or some other hting + + // default general host moderation policies + // todo: layerd moderataion later pls thanks + // show the labelmerge insstance responsible + // - [ ] HOST_LABELMERGE + // show both the whitelisted source and labeler dids in the same spot. + // like on hover / click it opens a dialog / popover to show what authority the labeler has + // - [x] FORCED_LABELER_DIDS + // - [ ] FORCE_HIDE_LABELS_WHITELISTED_SOURCE + // - [ ] FORCE_HIDE_LABELS + const hostmandate = FORCED_LABELER_DIDS; + + // unauthed experience + // - [ ] UNAUTHED_FORCE_WARN_LABELS + // - [ ] UNAUTHED_PREVENT_OPENING_WARNS + + + return ( + <> + {/* settings heading or about heading? */} + +
+ PDS (User Account Storage): + {HOST_SIGNUP_PDS || "not set"} + + Labelmerge (Label Cache): + {HOST_LABELMERGE || "not set"} + + Constellation (Backlink Index): + {defaultconstellationURL || "not set"} + + Slingshot (Record Cache): + {defaultslingshotURL || "not set"} + + Image Provider (CDN): + {defaultImgCDN || "not set"} + + Video Provider (CDN): + {defaultVideoCDN || "not set"} + + Lycan (Personal Search): + {defaultLycanURL || "not set"} +
+ {/* {hostmandate && ()} */} + + {hostmandate && ()} + {hostmandate?.map((labeler) => { + return ( + // todo this sucks + + ); + })} +
+ + + ) +} \ No newline at end of file -- 2.51.2