this repo has no description
0
fork

Configure Feed

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

burger

Juliet 5d0f3de3 3015ba17

+192 -167
+7 -7
src/components/account.tsx
··· 1 1 import { createSignal, onMount, For, Show } from "solid-js"; 2 - import Tooltip from "./tooltip.jsx"; 3 2 import { deleteStoredSession, getSession, OAuthUserAgent } from "@atcute/oauth-browser-client"; 4 3 import { agent, Login, retrieveSession, setAgent } from "./login.jsx"; 5 4 import { Did } from "@atcute/lexicons"; ··· 109 108 <Login /> 110 109 </div> 111 110 </Modal> 112 - <button onclick={() => setOpenManager(true)}> 113 - <Tooltip text="Accounts"> 114 - {agent() && avatar() ? 115 - <img src={avatar()} class="dark:shadow-dark-900/80 size-5 rounded-full shadow-sm" /> 116 - : <span class="iconify lucide--circle-user-round text-xl"></span>} 117 - </Tooltip> 111 + <button 112 + onclick={() => setOpenManager(true)} 113 + class="flex items-center rounded-full p-1 hover:bg-neutral-200 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" 114 + > 115 + {agent() && avatar() ? 116 + <img src={avatar()} class="dark:shadow-dark-900/80 size-5 rounded-full shadow-sm" /> 117 + : <span class="iconify lucide--circle-user-round text-xl"></span>} 118 118 </button> 119 119 </> 120 120 );
+2 -2
src/components/create.tsx
··· 3 3 import { agent } from "../components/login.jsx"; 4 4 import { editor, Editor } from "../components/editor.jsx"; 5 5 import * as monaco from "monaco-editor"; 6 - import { theme } from "../components/settings.jsx"; 6 + import { theme } from "./theme.jsx"; 7 7 import Tooltip from "./tooltip.jsx"; 8 8 import { useNavigate, useParams } from "@solidjs/router"; 9 9 import { remove } from "@mary/exif-rm"; ··· 282 282 </Modal> 283 283 <Tooltip text={`${props.create ? "Create" : "Edit"} record`}> 284 284 <button 285 - class={`flex items-center ${props.create ? "" : "rounded-sm p-1 hover:bg-neutral-100 active:bg-neutral-100 dark:hover:bg-neutral-600 dark:active:bg-neutral-600"}`} 285 + class={`flex items-center p-1 ${props.create ? "rounded-lg hover:bg-neutral-200 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" : "rounded-sm hover:bg-neutral-100 active:bg-neutral-100 dark:hover:bg-neutral-600 dark:active:bg-neutral-600"}`} 286 286 onclick={() => { 287 287 createModel(); 288 288 setOpenDialog(true);
+1 -1
src/components/json.tsx
··· 3 3 import { A } from "@solidjs/router"; 4 4 import { pds } from "./navbar"; 5 5 import Tooltip from "./tooltip"; 6 - import { hideMedia } from "./settings"; 6 + import { hideMedia } from "../views/settings"; 7 7 8 8 interface AtBlob { 9 9 $type: string;
-134
src/components/settings.tsx
··· 1 - import { createSignal, onMount, onCleanup } from "solid-js"; 2 - import Tooltip from "./tooltip.jsx"; 3 - import { TextInput } from "./text-input.jsx"; 4 - import { Modal } from "./modal.jsx"; 5 - 6 - const getInitialTheme = () => { 7 - const isDarkMode = 8 - localStorage.theme === "dark" || 9 - (!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches); 10 - return { color: isDarkMode ? "dark" : "light", system: !("theme" in localStorage) }; 11 - }; 12 - 13 - export const [theme, setTheme] = createSignal(getInitialTheme()); 14 - export const [hideMedia, setHideMedia] = createSignal(localStorage.hideMedia === "true"); 15 - 16 - const Settings = () => { 17 - const [openSettings, setOpenSettings] = createSignal(false); 18 - 19 - const themeEvent = () => { 20 - if (!theme().system) return; 21 - const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches; 22 - setTheme({ color: isDark ? "dark" : "light", system: theme().system }); 23 - document.documentElement.classList.toggle("dark", isDark); 24 - }; 25 - 26 - onMount(() => { 27 - window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", themeEvent); 28 - }); 29 - 30 - onCleanup(() => { 31 - window.matchMedia("(prefers-color-scheme: dark)").removeEventListener("change", themeEvent); 32 - }); 33 - 34 - const updateTheme = (newTheme: { color: string; system: boolean }) => { 35 - setTheme(newTheme); 36 - document.documentElement.classList.toggle("dark", newTheme.color === "dark"); 37 - if (newTheme.system) localStorage.removeItem("theme"); 38 - else localStorage.theme = newTheme.color; 39 - }; 40 - 41 - return ( 42 - <> 43 - <Modal open={openSettings()} onClose={() => setOpenSettings(false)}> 44 - <div class="dark:bg-dark-800/70 dark:shadow-dark-900/80 absolute top-12 left-[50%] w-[22rem] -translate-x-1/2 rounded-lg border-[0.5px] border-neutral-300 bg-neutral-200/70 p-4 text-neutral-900 shadow-md backdrop-blur-xs transition-opacity duration-300 dark:border-neutral-700 dark:text-neutral-200 starting:opacity-0"> 45 - <div class="mb-2 flex items-center gap-1 font-bold"> 46 - <span class="iconify lucide--settings"></span> 47 - <span>Settings</span> 48 - </div> 49 - <div class="flex flex-col gap-2"> 50 - <div class="flex flex-col gap-0.5"> 51 - <label for="plcDirectory" class="select-none"> 52 - PLC Directory 53 - </label> 54 - <TextInput 55 - id="plcDirectory" 56 - value={localStorage.plcDirectory || "https://plc.directory"} 57 - onInput={(e) => { 58 - e.currentTarget.value.length ? 59 - (localStorage.plcDirectory = e.currentTarget.value) 60 - : localStorage.removeItem("plcDirectory"); 61 - }} 62 - /> 63 - </div> 64 - <div class="flex justify-between"> 65 - <div class="flex items-center gap-1"> 66 - <input 67 - id="disableMedia" 68 - class="size-4" 69 - type="checkbox" 70 - checked={localStorage.hideMedia === "true"} 71 - onChange={(e) => { 72 - localStorage.hideMedia = e.currentTarget.checked; 73 - setHideMedia(e.currentTarget.checked); 74 - }} 75 - /> 76 - <label for="disableMedia" class="select-none"> 77 - Hide media embeds 78 - </label> 79 - </div> 80 - <div class="dark:shadow-dark-900/80 dark:bg-dark-100 flex items-center gap-1 rounded-full bg-white p-0.5 shadow-sm"> 81 - <button 82 - name="System Theme" 83 - classList={{ 84 - "p-1 flex items-center rounded-full": true, 85 - "bg-neutral-200 dark:bg-dark-400": theme().system, 86 - }} 87 - onclick={() => 88 - updateTheme({ 89 - color: 90 - window.matchMedia("(prefers-color-scheme: dark)").matches ? 91 - "dark" 92 - : "light", 93 - system: true, 94 - }) 95 - } 96 - > 97 - <span class="iconify lucide--monitor"></span> 98 - </button> 99 - <button 100 - name="Light Theme" 101 - classList={{ 102 - "p-1 flex items-center rounded-full": true, 103 - "bg-neutral-200": theme().color === "light" && !theme().system, 104 - }} 105 - onclick={() => updateTheme({ color: "light", system: false })} 106 - > 107 - <span class="iconify lucide--sun"></span> 108 - </button> 109 - <button 110 - name="Dark Theme" 111 - classList={{ 112 - "p-1 flex items-center rounded-full": true, 113 - "bg-dark-400": theme().color === "dark" && !theme().system, 114 - }} 115 - onclick={() => updateTheme({ color: "dark", system: false })} 116 - > 117 - <span class="iconify lucide--moon"></span> 118 - </button> 119 - </div> 120 - </div> 121 - </div> 122 - </div> 123 - </Modal> 124 - <button onclick={() => setOpenSettings(true)}> 125 - <Tooltip 126 - text="Settings" 127 - children={<span class="iconify lucide--settings text-xl"></span>} 128 - /> 129 - </button> 130 - </> 131 - ); 132 - }; 133 - 134 - export { Settings };
+66
src/components/theme.tsx
··· 1 + import { createSignal } from "solid-js"; 2 + 3 + const getInitialTheme = () => { 4 + const isDarkMode = 5 + localStorage.theme === "dark" || 6 + (!("theme" in localStorage) && window.matchMedia("(prefers-color-scheme: dark)").matches); 7 + return { color: isDarkMode ? "dark" : "light", system: !("theme" in localStorage) }; 8 + }; 9 + 10 + export const themeEvent = () => { 11 + if (!theme().system) return; 12 + const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches; 13 + setTheme({ color: isDark ? "dark" : "light", system: theme().system }); 14 + document.documentElement.classList.toggle("dark", isDark); 15 + }; 16 + 17 + export const [theme, setTheme] = createSignal(getInitialTheme()); 18 + 19 + export const ThemeSelection = () => { 20 + const updateTheme = (newTheme: { color: string; system: boolean }) => { 21 + setTheme(newTheme); 22 + document.documentElement.classList.toggle("dark", newTheme.color === "dark"); 23 + if (newTheme.system) localStorage.removeItem("theme"); 24 + else localStorage.theme = newTheme.color; 25 + }; 26 + 27 + return ( 28 + <div class="mt-2 flex items-center justify-between gap-1 text-base"> 29 + <button 30 + name="System Theme" 31 + classList={{ 32 + "p-1.5 flex items-center rounded-full": true, 33 + "bg-neutral-200 dark:bg-neutral-600": theme().system, 34 + }} 35 + onclick={() => 36 + updateTheme({ 37 + color: window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light", 38 + system: true, 39 + }) 40 + } 41 + > 42 + <span class="iconify lucide--monitor"></span> 43 + </button> 44 + <button 45 + name="Light Theme" 46 + classList={{ 47 + "p-1.5 flex items-center rounded-full": true, 48 + "bg-neutral-200": theme().color === "light" && !theme().system, 49 + }} 50 + onclick={() => updateTheme({ color: "light", system: false })} 51 + > 52 + <span class="iconify lucide--sun"></span> 53 + </button> 54 + <button 55 + name="Dark Theme" 56 + classList={{ 57 + "p-1.5 flex items-center rounded-full": true, 58 + "bg-neutral-600": theme().color === "dark" && !theme().system, 59 + }} 60 + onclick={() => updateTheme({ color: "dark", system: false })} 61 + > 62 + <span class="iconify lucide--moon"></span> 63 + </button> 64 + </div> 65 + ); 66 + };
+2
src/index.tsx
··· 10 10 import { LabelView } from "./views/labels.tsx"; 11 11 import { StreamView } from "./views/stream.tsx"; 12 12 import { RecordView } from "./views/record.tsx"; 13 + import { Settings } from "./views/settings.tsx"; 13 14 14 15 render( 15 16 () => ( 16 17 <Router root={Layout}> 17 18 <Route path="/" component={Home} /> 18 19 <Route path={["/jetstream", "/firehose"]} component={StreamView} /> 20 + <Route path="/settings" component={Settings} /> 19 21 <Route path="/:pds" component={PdsView} /> 20 22 <Route path="/:pds/:repo" component={RepoView} /> 21 23 <Route path="/:pds/:repo/labels" component={LabelView} />
+65 -23
src/layout.tsx
··· 1 - import { createEffect, createSignal, ErrorBoundary, Show, Suspense } from "solid-js"; 1 + import { createEffect, createSignal, ErrorBoundary, onMount, Show, Suspense } from "solid-js"; 2 2 import { A, RouteSectionProps, useLocation, useNavigate } from "@solidjs/router"; 3 3 import { agent } from "./components/login.jsx"; 4 4 import { RecordEditor } from "./components/create.jsx"; 5 - import Tooltip from "./components/tooltip.jsx"; 6 5 import { NavBar } from "./components/navbar.jsx"; 7 6 import { Search } from "./components/search.jsx"; 8 7 import { AccountManager } from "./components/account.jsx"; 9 8 import { resolveHandle } from "./utils/api.js"; 10 9 import { Meta, MetaProvider } from "@solidjs/meta"; 11 - import { Settings } from "./components/settings.jsx"; 12 10 import { Handle } from "@atcute/lexicons"; 11 + import { themeEvent, ThemeSelection } from "./components/theme.jsx"; 13 12 14 13 export const [notif, setNotif] = createSignal<{ 15 14 show: boolean; ··· 21 20 const location = useLocation(); 22 21 const navigate = useNavigate(); 23 22 let timeout: number; 23 + const [showMenu, setShowMenu] = createSignal(false); 24 + const [menu, setMenu] = createSignal<HTMLDivElement>(); 25 + const [menuButton, setMenuButton] = createSignal<HTMLButtonElement>(); 24 26 25 27 createEffect(async () => { 26 28 if (props.params.repo && !props.params.repo.startsWith("did:")) { ··· 36 38 } 37 39 }); 38 40 41 + const clickEvent = (event: MouseEvent) => { 42 + if (!menuButton()?.contains(event.target as Node) && !menu()?.contains(event.target as Node)) 43 + setShowMenu(false); 44 + }; 45 + 46 + onMount(() => { 47 + window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", themeEvent); 48 + window.addEventListener("click", clickEvent); 49 + }); 50 + 39 51 return ( 40 52 <div id="main" class="m-4 flex flex-col items-center text-neutral-900 dark:text-neutral-200"> 41 53 <MetaProvider> ··· 43 55 <Meta name="robots" content="noindex, nofollow" /> 44 56 </Show> 45 57 </MetaProvider> 46 - <header class="mb-4 flex w-[22rem] items-center sm:w-[24rem]"> 47 - <div class="flex basis-1/3 gap-x-2"> 48 - <AccountManager /> 58 + <header class="mb-4 flex w-[22.5rem] items-center justify-between sm:w-[24.5rem]"> 59 + <A 60 + href="/" 61 + style='font-feature-settings: "cv05"' 62 + class="flex items-center gap-1 rounded-lg px-1 text-lg font-bold hover:bg-neutral-200 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" 63 + > 64 + <span class="iconify tabler--binary-tree-filled text-[#76c4e5]"></span> 65 + <span>PDSls</span> 66 + </A> 67 + <div class="relative flex items-center gap-1"> 49 68 <Show when={agent()}> 50 69 <RecordEditor create={true} /> 51 70 </Show> 52 - </div> 53 - <div class="flex basis-1/3 justify-center"> 54 - <A 55 - href="/" 56 - style='font-feature-settings: "cv05"' 57 - class="flex items-center gap-1 rounded-lg px-1 font-bold hover:bg-neutral-200 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" 58 - > 59 - <span class="iconify tabler--binary-tree-filled text-[#76c4e5]"></span> 60 - <span>PDSls</span> 61 - </A> 62 - </div> 63 - <div class="flex basis-1/3 items-center justify-end gap-x-2"> 64 - <Tooltip text="Relay"> 65 - <A href="/jetstream" class="iconify lucide--radio-tower text-xl"></A> 66 - </Tooltip> 67 - <Settings /> 71 + <AccountManager /> 72 + <div class="relative"> 73 + <button 74 + onClick={() => setShowMenu(!showMenu())} 75 + ref={setMenuButton} 76 + class="flex items-center rounded-lg p-1 hover:bg-neutral-200 active:bg-neutral-200 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" 77 + > 78 + <span class="iconify lucide--menu text-xl"></span> 79 + </button> 80 + <Show when={showMenu()}> 81 + <div 82 + ref={setMenu} 83 + class="dark:bg-dark-300 absolute top-8 right-0 z-20 flex flex-col rounded-lg border-[0.5px] border-neutral-300 bg-neutral-50 p-3 text-sm shadow-md dark:border-neutral-700" 84 + > 85 + <A 86 + href="/jetstream" 87 + onClick={() => setShowMenu(false)} 88 + class="rounded-lg p-1 hover:bg-neutral-200/50 active:bg-neutral-200/50 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" 89 + > 90 + <span>Jetstream</span> 91 + </A> 92 + <A 93 + href="/firehose" 94 + onClick={() => setShowMenu(false)} 95 + class="rounded-lg p-1 hover:bg-neutral-200/50 active:bg-neutral-200/50 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" 96 + > 97 + <span>Firehose</span> 98 + </A> 99 + <A 100 + href="/settings" 101 + onClick={() => setShowMenu(false)} 102 + class="rounded-lg p-1 hover:bg-neutral-200/50 active:bg-neutral-200/50 dark:hover:bg-neutral-700 dark:active:bg-neutral-700" 103 + > 104 + <span>Settings</span> 105 + </A> 106 + <ThemeSelection /> 107 + </div> 108 + </Show> 109 + </div> 68 110 </div> 69 111 </header> 70 112 <div class="dark:bg-dark-500 z-1 mb-4 flex max-w-full min-w-[22rem] flex-col items-center bg-neutral-100 text-pretty sm:min-w-[24rem] md:max-w-[48rem]"> 71 - <Show when={location.pathname !== "/jetstream" && location.pathname !== "/firehose"}> 113 + <Show when={!["/jetstream", "/firehose", "/settings"].includes(location.pathname)}> 72 114 <Search /> 73 115 </Show> 74 116 <Show when={props.params.pds}>
+49
src/views/settings.tsx
··· 1 + import { createSignal } from "solid-js"; 2 + import { TextInput } from "../components/text-input.jsx"; 3 + 4 + export const [hideMedia, setHideMedia] = createSignal(localStorage.hideMedia === "true"); 5 + 6 + const Settings = () => { 7 + return ( 8 + <div class="w-[22rem] sm:w-[24rem]"> 9 + <div class="mb-2 flex items-center gap-1 font-bold"> 10 + <span>Settings</span> 11 + </div> 12 + <div class="flex flex-col gap-2"> 13 + <div class="flex flex-col gap-0.5"> 14 + <label for="plcDirectory" class="select-none"> 15 + PLC Directory 16 + </label> 17 + <TextInput 18 + id="plcDirectory" 19 + value={localStorage.plcDirectory || "https://plc.directory"} 20 + onInput={(e) => { 21 + e.currentTarget.value.length ? 22 + (localStorage.plcDirectory = e.currentTarget.value) 23 + : localStorage.removeItem("plcDirectory"); 24 + }} 25 + /> 26 + </div> 27 + <div class="flex justify-between"> 28 + <div class="flex items-center gap-1"> 29 + <input 30 + id="disableMedia" 31 + class="size-4" 32 + type="checkbox" 33 + checked={localStorage.hideMedia === "true"} 34 + onChange={(e) => { 35 + localStorage.hideMedia = e.currentTarget.checked; 36 + setHideMedia(e.currentTarget.checked); 37 + }} 38 + /> 39 + <label for="disableMedia" class="select-none"> 40 + Hide media embeds 41 + </label> 42 + </div> 43 + </div> 44 + </div> 45 + </div> 46 + ); 47 + }; 48 + 49 + export { Settings };