wip: Custom mirroring tangled knot written in Rust
rust tangled mirror knot
1

Configure Feed

Select the types of activity you want to include in your feed.

style: refine ui to be terminal-like

Clément (Jun 16, 2026, 7:35 PM +0200) e8ecf402 83f6a511

+37 -37
+2 -2
app/src/App.tsx
··· 16 16 <div class="mx-auto max-w-xl lg:max-w-4xl py-4 px-4 min-h-dvh"> 17 17 <header class="border-b-2 border-zinc-300 dark:border-zinc-300"> 18 18 <h1> 19 - <span class="text-amber-700">{hostame}</span> dashboard 19 + <span class="bg-amber-700 text-white px-2">{hostame}</span>{" "} 20 + dashboard 20 21 </h1> 21 22 <span>configure your knotty instance</span> 22 23 </header> 23 24 <main> 24 25 <SignInButton /> 25 - <MirrorButton /> 26 26 <RepositoriesList /> 27 27 </main> 28 28 </div>
+2 -3
app/src/MirrorButton.tsx
··· 86 86 description={<p>mirror a repository to your tangled account</p>} 87 87 trigger={(p) => ( 88 88 <Button 89 - variant="none" 90 - class="hover:text-green-300 hover:transition-colors" 89 + class="bg-green-300 hover:bg-green-500 dark:bg-green-600 dark:hover:bg-green-400 dark:hover:text-zinc-600" 91 90 disabled={!agent()} 92 91 {...p} 93 92 > ··· 155 154 > 156 155 {(p) => ( 157 156 <Button 158 - variant="contrast" 157 + class="hover:bg-blue-500 hover:text-white" 159 158 type="submit" 160 159 disabled={!p().canSubmit || p().isSubmitting} 161 160 >
-1
app/src/RepositoriesList.tsx
··· 1 1 import { For, Show } from "solid-js"; 2 2 import { newXrpcKnottyClient } from "./lib/atproto"; 3 3 import { createQuery, queryOptions } from "@tanstack/solid-query"; 4 - import { DidChip } from "./components/DidChip"; 5 4 import { HandleChip } from "./components/HandleChip"; 6 5 7 6 async function resolveRepositories() {
+24 -13
app/src/SigninButton.tsx
··· 10 10 import { HandleChip } from "./components/HandleChip"; 11 11 import { useSession } from "./session"; 12 12 import { OAUTH_SCOPE } from "./lib/oauth_metadata"; 13 + import { MirrorButton } from "./MirrorButton"; 13 14 14 15 type UseOAuthScopeFlowOptions = { 15 16 onError?: (error: unknown) => void; ··· 80 81 <Dialog 81 82 title="sign in" 82 83 description={<p>sign in to your tangled account</p>} 83 - trigger={(p) => <Button {...p}>sign in</Button>} 84 + trigger={(p) => ( 85 + <Button {...p} class="hover:bg-blue-500 hover:text-white"> 86 + sign in 87 + </Button> 88 + )} 84 89 initialFocusEl={() => handleInputRef!} 85 90 > 86 91 <form ··· 127 132 > 128 133 {(p) => ( 129 134 <Button 130 - variant="contrast" 135 + class="hover:bg-blue-500 hover:text-white" 131 136 type="submit" 132 137 disabled={!p().canSubmit || p().isSubmitting} 133 138 > ··· 154 159 > 155 160 <Show when={agent()} fallback={<SignInDialog />}> 156 161 {(a) => ( 157 - <div class="flex items-center gap-3"> 158 - <p> 159 - signed in as <HandleChip did={a().sub} /> 160 - </p> 161 - <Button 162 - variant="none" 163 - class="hover:text-red-400 hover:transition-colors" 164 - onClick={signOut} 165 - > 166 - sign out 167 - </Button> 162 + <div> 163 + <div class="flex items-center gap-2"> 164 + <p> 165 + signed in as{" "} 166 + <HandleChip 167 + did={a().sub} 168 + class="bg-blue-300 px-2 text-blue-900 hover:text-white" 169 + /> 170 + </p> 171 + <Button 172 + class="hover:bg-red-400 hover:text-white" 173 + onClick={signOut} 174 + > 175 + sign out 176 + </Button> 177 + </div> 178 + <MirrorButton /> 168 179 </div> 169 180 )} 170 181 </Show>
+5 -17
app/src/components/Button.tsx
··· 1 1 import { children, splitProps } from "solid-js"; 2 2 import type { JSX } from "solid-js"; 3 3 4 - type Variants = "default" | "contrast" | "green" | "none"; 5 4 type Props = { 6 5 children: JSX.Element; 7 - variant?: Variants; 8 6 } & Omit<JSX.ButtonHTMLAttributes<HTMLButtonElement>, "children">; 9 7 10 - const styles: Record<Variants, string> = { 11 - default: "bg-background text-text border-border", 12 - contrast: "bg-background-contrast text-text-contrast border-transparent", 13 - green: "bg-lime-500 text-black", 14 - none: "", 15 - }; 16 - 17 8 export function Button(props: Props) { 18 9 const safeChildren = children(() => props.children); 19 10 20 11 const [customProps, classProp, restProps] = splitProps( 21 12 props, 22 - ["children", "variant"], 13 + ["children"], 23 14 ["class"], 24 15 ); 25 16 26 - const baseClass = () => 27 - customProps.variant === "none" 28 - ? "cursor-pointer" 29 - : styles[customProps.variant || "default"] + 30 - "rounded-xl px-3 py-2 border cursor-pointer active:scale-95 active:transition-all disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100 focus-ring"; 31 - 32 17 return ( 33 - <button class={`${baseClass()} ${classProp.class}`} {...restProps}> 18 + <button 19 + class={`px-2 cursor-pointer active:scale-95 active:transition-all disabled:cursor-not-allowed disabled:opacity-50 disabled:active:scale-100 focus-ring ${classProp.class}`} 20 + {...restProps} 21 + > 34 22 {safeChildren()} 35 23 </button> 36 24 );
+4 -1
app/src/components/HandleChip.tsx
··· 43 43 <a 44 44 href={`https://tangled.org/${data().handle}`} 45 45 title={props.did} 46 - class={`${props.class ?? ""} text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-300 transition-colors`} 46 + class={ 47 + props.class ?? 48 + "text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-300 transition-colors" 49 + } 47 50 > 48 51 @{data().handle} 49 52 </a>