beatufitull front end for ozone modration ,, wit catpucoin and ebergarden !
0
fork

Configure Feed

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

evergarden evergarden evergarden evergaden woawww

+161 -14
+38 -8
src/components/Header.svelte
··· 1 1 <script lang="ts"> 2 2 import { session } from '$lib/stores/auth'; 3 - import { accentColours, inputAccentColor, type InputAccentColor } from '$lib/stores/ui'; 3 + import { 4 + accentColours, 5 + appTheme, 6 + inputAccentColor, 7 + type AppTheme, 8 + type InputAccentColor 9 + } from '$lib/stores/ui'; 4 10 import { createQuery } from '@tanstack/svelte-query'; 5 11 import { Cat, LogOut, MenuIcon, Paintbrush, SearchIcon, TriangleAlert } from 'lucide-svelte'; 6 12 import Button from './ui/Button.svelte'; ··· 25 31 26 32 function setInputAccentColor(color: InputAccentColor) { 27 33 inputAccentColor.set(color); 34 + } 35 + 36 + function setAppTheme(theme: AppTheme) { 37 + appTheme.set(theme); 28 38 } 29 39 30 40 function isActive(href: string) { ··· 59 69 <Button 60 70 variant="ghost" 61 71 size="icon" 62 - on:click={() => onSearch()} 72 + onclick={() => onSearch()} 63 73 className="items-center size-10" 64 74 aria-label="Menu" 65 75 > ··· 73 83 <Button 74 84 variant="ghost" 75 85 size="icon" 76 - on:click={() => toggle()} 86 + onclick={() => toggle()} 77 87 className="items-center size-10" 78 88 aria-label="Menu" 79 89 > ··· 100 110 <Paintbrush size={16} /> 101 111 <span>Theme</span> 102 112 </div> 113 + <div class="grid grid-cols-2 gap-2 px-2 py-1"> 114 + <button 115 + type="button" 116 + onclick={() => setAppTheme('catppuccin')} 117 + class="cursor-pointer rounded border border-ctp-surface1 px-2 py-1 text-xs text-ctp-text hover:bg-ctp-surface0" 118 + class:bg-ctp-surface0={$appTheme === 'catppuccin'} 119 + > 120 + Catppuccin 121 + </button> 122 + <button 123 + type="button" 124 + onclick={() => setAppTheme('evergarden')} 125 + class="cursor-pointer rounded border border-ctp-surface1 px-2 py-1 text-xs text-ctp-text hover:bg-ctp-surface0" 126 + class:bg-ctp-surface0={$appTheme === 'evergarden'} 127 + > 128 + Evergarden 129 + </button> 130 + </div> 131 + <!-- <div class="my-2 border-t border-ctp-surface1"></div> 132 + <div class="mb-1 px-2 py-1 text-xs text-ctp-subtext0">Accent</div> 103 133 <div class="flex gap-2 px-2 py-1"> 104 134 {#each accentColours as colour} 105 135 <button ··· 110 140 class:ring-2={$inputAccentColor === colour} 111 141 class:ring-ctp-overlay1={$inputAccentColor === colour} 112 142 class:text-ctp-subtext0={$inputAccentColor !== colour} 113 - on:click={() => setInputAccentColor(colour)} 143 + onclick={() => setInputAccentColor(colour)} 114 144 aria-label={`Set input accent color to ${colour}`} 115 145 > 116 146 </button> 117 147 {/each} 118 - </div> 148 + </div> --> 119 149 <div class="my-2 border-t border-ctp-surface1"></div> 120 150 <div> 121 151 <div class="flex items-center gap-2 px-2 py-1"> ··· 124 154 id="ads-toggle" 125 155 class="cursor-pointer rounded accent-ctp-sapphire" 126 156 bind:checked={$ads} 127 - on:change={handleChange} 157 + onchange={handleChange} 128 158 /> 129 159 <label for="ads-toggle" class="cursor-pointer text-ctp-text">Enable ads</label> 130 160 </div> ··· 133 163 variant="danger" 134 164 fullWidth={true} 135 165 size="sm" 136 - on:click={handleLogout} 166 + onclick={handleLogout} 137 167 className="justify-start py-1.5 rounded-sm" 138 168 > 139 169 <LogOut size={18} class="mr-3" /> ··· 150 180 <Button 151 181 variant="ghost" 152 182 size="icon" 153 - on:click={() => toggle()} 183 + onclick={() => toggle()} 154 184 className="items-center size-10" 155 185 aria-label="Menu" 156 186 >
+31 -2
src/components/Modal.svelte
··· 1 1 <script lang="ts"> 2 2 import { X } from 'lucide-svelte'; 3 + import { cubicOut } from 'svelte/easing'; 4 + import { scale } from 'svelte/transition'; 3 5 4 6 export let isOpen = false; 5 7 export let title = ''; ··· 25 27 role="presentation" 26 28 tabindex="-1" 27 29 ></div> 28 - <div class="fixed inset-0 z-50 flex items-center justify-center md:p-4"> 30 + <div 31 + class="fixed inset-0 z-50 flex items-center justify-center p-4 md:p-4" 32 + style=" 33 + padding-top: max(1rem, env(safe-area-inset-top)); 34 + padding-right: max(1rem, env(safe-area-inset-right)); 35 + padding-bottom: max(1rem, env(safe-area-inset-bottom)); 36 + padding-left: max(1rem, env(safe-area-inset-left)); 37 + " 38 + > 29 39 <div 30 - class="relative flex h-screen max-h-screen w-full max-w-3xl flex-col overflow-hidden rounded-lg border border-ctp-surface1 bg-ctp-base shadow-lg md:h-auto md:max-h-[calc(100vh-2rem)]" 40 + out:scale={{ duration: 100, start: 0.98, easing: cubicOut }} 41 + class="modal-scale-in relative flex h-full max-h-full w-full max-w-3xl flex-col overflow-hidden rounded-lg border border-ctp-surface1 bg-ctp-base shadow-lg transition-transform md:h-auto md:max-h-[calc(100vh-2rem)]" 31 42 role="dialog" 32 43 aria-modal="true" 33 44 tabindex="0" ··· 48 59 </div> 49 60 </div> 50 61 {/if} 62 + 63 + <style> 64 + @media (prefers-reduced-motion: no-preference) { 65 + .modal-scale-in { 66 + animation: modal-scale-in 240ms cubic-bezier(0.22, 1, 0.36, 1); 67 + } 68 + } 69 + 70 + @keyframes modal-scale-in { 71 + from { 72 + transform: scale(0.88); 73 + } 74 + 75 + to { 76 + transform: scale(1); 77 + } 78 + } 79 + </style>
+4 -1
src/components/Search.svelte
··· 5 5 import { AtpAgent, type AppBskyActorDefs } from '@atproto/api'; 6 6 import { createQuery } from '@tanstack/svelte-query'; 7 7 import { CircleQuestionMark, HistoryIcon, SearchIcon, TrashIcon } from 'lucide-svelte'; 8 + import { cubicOut } from 'svelte/easing'; 9 + import { fade, scale } from 'svelte/transition'; 8 10 import Avatar from './ui/Avatar.svelte'; 9 11 10 12 type SearchSuggestion = { ··· 157 159 ></div> 158 160 <div class="pointer-events-none fixed inset-0 z-50 flex items-start justify-center p-4 md:pt-15"> 159 161 <div 160 - class="pointer-events-auto relative flex max-h-[calc(100vh-2rem)] w-full max-w-lg flex-col overflow-hidden rounded-lg border border-ctp-surface1 bg-ctp-surface0 shadow-lg" 162 + class="pointer-events-auto relative flex max-h-[calc(100vh-2rem)] w-full max-w-lg origin-top flex-col overflow-hidden rounded-lg border border-ctp-surface1 bg-ctp-surface0 shadow-lg" 163 + transition:scale={{ duration: 80, start: 0.96, easing: cubicOut }} 161 164 role="dialog" 162 165 aria-modal="true" 163 166 tabindex="0"
+38
src/lib/stores/ui.ts
··· 4 4 // TODO: redo colour system 5 5 export const accentColours = ["sapphire", "mauve", "lavender"] as const; 6 6 export type InputAccentColor = typeof accentColours[number]; 7 + export const appThemes = ['catppuccin', 'evergarden'] as const; 8 + export type AppTheme = typeof appThemes[number]; 7 9 8 10 const storageKey = 'ui-input-accent-color'; 11 + const appThemeStorageKey = 'ui-app-theme'; 9 12 const defaultColor: InputAccentColor = 'mauve'; 13 + const defaultAppTheme: AppTheme = 'catppuccin'; 10 14 11 15 function isInputAccentColor(value: string): value is InputAccentColor { 12 16 return value === 'sapphire' || value === 'mauve' || value === 'lavender'; 13 17 } 14 18 19 + function isAppTheme(value: string): value is AppTheme { 20 + return value === 'catppuccin' || value === 'evergarden'; 21 + } 22 + 23 + function applyTheme(theme: AppTheme) { 24 + if (!browser) return; 25 + 26 + const root = document.documentElement; 27 + root.dataset.theme = theme; 28 + } 29 + 15 30 function createInputAccentColorStore() { 16 31 const store = writable<InputAccentColor>(defaultColor); 17 32 ··· 30 45 } 31 46 32 47 export const inputAccentColor = createInputAccentColorStore(); 48 + 49 + function createAppThemeStore() { 50 + const store = writable<AppTheme>(defaultAppTheme); 51 + 52 + if (browser) { 53 + const savedTheme = localStorage.getItem(appThemeStorageKey); 54 + if (savedTheme && isAppTheme(savedTheme)) { 55 + store.set(savedTheme); 56 + applyTheme(savedTheme); 57 + } else { 58 + applyTheme(defaultAppTheme); 59 + } 60 + 61 + store.subscribe((theme) => { 62 + applyTheme(theme); 63 + localStorage.setItem(appThemeStorageKey, theme); 64 + }); 65 + } 66 + 67 + return store; 68 + } 69 + 70 + export const appTheme = createAppThemeStore();
+2
src/routes/+layout.svelte
··· 17 17 import Search from '$components/Search.svelte'; 18 18 import UserModal from '$components/view/User.svelte'; 19 19 import PostModal from '$components/view/Post.svelte'; 20 + import { appTheme } from '$lib/stores/ui'; 20 21 21 22 let { children } = $props(); 22 23 let currentPath = $derived(page.url.pathname); ··· 96 97 let searchOpen = $state(false); 97 98 98 99 const modalView = $derived(page.url.searchParams.get('view')); 100 + const _activeTheme = $derived($appTheme); 99 101 const modalDid = $derived(page.url.searchParams.get('did')); 100 102 const modalUri = $derived.by(() => { 101 103 const uriValue = page.url.searchParams.get('uri');
+1 -2
src/routes/+page.svelte
··· 69 69 | 'tools.ozone.moderation.defs#reviewNone'; 70 70 let filter = $state<State | undefined>(undefined); 71 71 const reportsQuery = createInfiniteQuery(() => ({ 72 - queryKey: ['reports'], 72 + queryKey: ['reports', filter], 73 73 queryFn: async ({ pageParam }) => { 74 74 if (!$session) { 75 75 throw new Error('No active session'); ··· 162 162 activeTab={filter} 163 163 onTabChange={(tabId) => { 164 164 filter = tabId as State | undefined; 165 - reportsQuery.refetch(); 166 165 }} 167 166 size="sm" 168 167 className="ml-auto"
+47 -1
src/routes/layout.css
··· 1 1 @import 'tailwindcss'; 2 - @import "@catppuccin/tailwindcss/mocha.css"; 2 + @import "@catppuccin/tailwindcss/mocha.css"; 3 + 4 + :root[data-theme='evergarden'] { 5 + --catppuccin-color-base: #1e2528; 6 + --catppuccin-color-crust: #171c1f; 7 + --catppuccin-color-mantle: #191e21; 8 + --catppuccin-color-surface0: #262f33; 9 + --catppuccin-color-surface1: #374145; 10 + --catppuccin-color-surface2: #465256; 11 + --catppuccin-color-overlay0: #5a696d; 12 + --catppuccin-color-overlay1: #6b7f7e; 13 + --catppuccin-color-overlay2: #7d9991; 14 + --catppuccin-color-subtext0: #89a69c; 15 + --catppuccin-color-subtext1: #96b4aa; 16 + --catppuccin-color-text: #f8f9e8; 17 + --catppuccin-color-blue: #b2caed; 18 + --catppuccin-color-lavender: #b2caed; 19 + --catppuccin-color-sapphire: #b3e3ca; 20 + --catppuccin-color-mauve: #f3c0e5; 21 + --catppuccin-color-red: #f57f82; 22 + --catppuccin-color-yellow: #f5d098; 23 + --catppuccin-color-green: #cbe3b3; 24 + } 25 + 26 + @media (prefers-color-scheme: light) { 27 + :root[data-theme='evergarden'] { 28 + --catppuccin-color-base: #d7e1eb; 29 + --catppuccin-color-crust: #bdcbdb; 30 + --catppuccin-color-mantle: #c8d5e1; 31 + --catppuccin-color-surface0: #ced9e0; 32 + --catppuccin-color-surface1: #b4c6cc; 33 + --catppuccin-color-surface2: #a4b7bf; 34 + --catppuccin-color-overlay0: #93a6ac; 35 + --catppuccin-color-overlay1: #7f9095; 36 + --catppuccin-color-overlay2: #6b7c81; 37 + --catppuccin-color-subtext0: #5f7277; 38 + --catppuccin-color-subtext1: #526469; 39 + --catppuccin-color-text: #171c1f; 40 + --catppuccin-color-blue: #8ca0bb; 41 + --catppuccin-color-lavender: #8ca0bb; 42 + --catppuccin-color-sapphire: #7baa92; 43 + --catppuccin-color-mauve: #ca9ebd; 44 + --catppuccin-color-red: #c0696b; 45 + --catppuccin-color-yellow: #bc9c6b; 46 + --catppuccin-color-green: #91ac75; 47 + } 48 + }