This is my personal website
1
fork

Configure Feed

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

move to deno fresh framework

+662 -475
+3 -1
.github/workflows/deploy.yml
··· 15 15 with: 16 16 wasm: true 17 17 plugin: deno 18 - args: deploy main.tsx --project=$DENO_PROJECT 18 + args: | 19 + task build 20 + deploy main.ts --project=$DENO_PROJECT 19 21 env: 20 22 DENO_PROJECT: ${{ secrets.DENO_PROJECT }} 21 23 DENO_DEPLOY_TOKEN: ${{ secrets.DENO_DEPLOY_TOKEN }}
+11
.gitignore
··· 1 + # dotenv environment variable files 2 + .env 3 + .env.development.local 4 + .env.test.local 5 + .env.production.local 6 + .env.local 7 + 8 + # Fresh build directory 9 + _fresh/ 10 + # npm dependencies 11 + node_modules/
+6
.vscode/extensions.json
··· 1 + { 2 + "recommendations": [ 3 + "denoland.vscode-deno", 4 + "bradlc.vscode-tailwindcss" 5 + ] 6 + }
+18 -1
.vscode/settings.json
··· 1 1 { 2 - "deno.enable": true 2 + "deno.enable": true, 3 + "deno.lint": true, 4 + "editor.defaultFormatter": "denoland.vscode-deno", 5 + "[typescriptreact]": { 6 + "editor.defaultFormatter": "denoland.vscode-deno" 7 + }, 8 + "[typescript]": { 9 + "editor.defaultFormatter": "denoland.vscode-deno" 10 + }, 11 + "[javascriptreact]": { 12 + "editor.defaultFormatter": "denoland.vscode-deno" 13 + }, 14 + "[javascript]": { 15 + "editor.defaultFormatter": "denoland.vscode-deno" 16 + }, 17 + "css.customData": [ 18 + ".vscode/tailwind.json" 19 + ] 3 20 }
+55
.vscode/tailwind.json
··· 1 + { 2 + "version": 1.1, 3 + "atDirectives": [ 4 + { 5 + "name": "@tailwind", 6 + "description": "Use the `@tailwind` directive to insert Tailwind's `base`, `components`, `utilities` and `screens` styles into your CSS.", 7 + "references": [ 8 + { 9 + "name": "Tailwind Documentation", 10 + "url": "https://tailwindcss.com/docs/functions-and-directives#tailwind" 11 + } 12 + ] 13 + }, 14 + { 15 + "name": "@apply", 16 + "description": "Use the `@apply` directive to inline any existing utility classes into your own custom CSS. This is useful when you find a common utility pattern in your HTML that you’d like to extract to a new component.", 17 + "references": [ 18 + { 19 + "name": "Tailwind Documentation", 20 + "url": "https://tailwindcss.com/docs/functions-and-directives#apply" 21 + } 22 + ] 23 + }, 24 + { 25 + "name": "@responsive", 26 + "description": "You can generate responsive variants of your own classes by wrapping their definitions in the `@responsive` directive:\n```css\n@responsive {\n .alert {\n background-color: #E53E3E;\n }\n}\n```\n", 27 + "references": [ 28 + { 29 + "name": "Tailwind Documentation", 30 + "url": "https://tailwindcss.com/docs/functions-and-directives#responsive" 31 + } 32 + ] 33 + }, 34 + { 35 + "name": "@screen", 36 + "description": "The `@screen` directive allows you to create media queries that reference your breakpoints by **name** instead of duplicating their values in your own CSS:\n```css\n@screen sm {\n /* ... */\n}\n```\n…gets transformed into this:\n```css\n@media (min-width: 640px) {\n /* ... */\n}\n```\n", 37 + "references": [ 38 + { 39 + "name": "Tailwind Documentation", 40 + "url": "https://tailwindcss.com/docs/functions-and-directives#screen" 41 + } 42 + ] 43 + }, 44 + { 45 + "name": "@variants", 46 + "description": "Generate `hover`, `focus`, `active` and other **variants** of your own utilities by wrapping their definitions in the `@variants` directive:\n```css\n@variants hover, focus {\n .btn-brand {\n background-color: #3182CE;\n }\n}\n```\n", 47 + "references": [ 48 + { 49 + "name": "Tailwind Documentation", 50 + "url": "https://tailwindcss.com/docs/functions-and-directives#variants" 51 + } 52 + ] 53 + } 54 + ] 55 + }
+17 -6
README.md
··· 2 2 3 3 # Hi, I'm Tsiry Sandratraina 4 4 5 - [![deno compatibility](https://shield.deno.dev/deno/^1.41)](https://deno.com) 5 + [![deno compatibility](https://shield.deno.dev/deno/^2.2.11)](https://deno.com) 6 6 [![ci](https://github.com/tsirysndr/me/actions/workflows/deploy.yml/badge.svg)](https://github.com/tsirysndr/me/actions/workflows/deploy.yml) 7 7 8 - This is my personal website. I'm a software engineer and I'm passionate about technology and innovation. I'm also a fan of entrepreneurship and I'm always looking for new opportunities to learn and grow. I'm currently working on a few projects and I'm always looking for new opportunities to collaborate with other people. 8 + This is my personal website. I'm a software engineer and I'm passionate about 9 + technology and innovation. I'm also a fan of entrepreneurship and I'm always 10 + looking for new opportunities to learn and grow. I'm currently working on a few 11 + projects and I'm always looking for new opportunities to collaborate with other 12 + people. 13 + 14 + See this website is live at 15 + [tsiry-sandratraina.com](https://tsiry-sandratraina.com). 16 + 17 + ### Usage 9 18 10 - See this website is live at [tsiry-sandratraina.com](https://tsiry-sandratraina.com). 19 + Make sure to install Deno: https://deno.land/manual/getting_started/installation 11 20 12 - ## Usage 21 + Then start the project: 13 22 14 - ```bash 15 - deno run -A main.tsx 23 + ``` 24 + deno task start 16 25 ``` 26 + 27 + This will watch the project directory and restart as necessary.
+12
components/Button.tsx
··· 1 + import { JSX } from "preact"; 2 + import { IS_BROWSER } from "$fresh/runtime.ts"; 3 + 4 + export function Button(props: JSX.HTMLAttributes<HTMLButtonElement>) { 5 + return ( 6 + <button 7 + {...props} 8 + disabled={!IS_BROWSER || props.disabled} 9 + class="px-2 py-1 border-gray-500 border-2 rounded bg-white hover:bg-gray-200 transition-colors" 10 + /> 11 + ); 12 + }
+79
components/Footer.tsx
··· 1 + import dayjs from "dayjs"; 2 + 3 + export function Footer() { 4 + return ( 5 + <div class="flex flex-row justify-between mb-[20px] mt-[100px]"> 6 + <div class="text-[rgb(109,109,156)] text-[17px]"> 7 + © {dayjs().format("YYYY")} Tsiry Sandratraina. 8 + </div> 9 + <div class="flex flex-row justify-around w-[100px]"> 10 + <a href="https://github.com/tsirysndr" target="_blank" class="social"> 11 + <svg 12 + viewBox="0 0 16 16" 13 + height="20" 14 + width="20" 15 + aria-hidden="true" 16 + focusable="false" 17 + fill="rgb(109, 109, 156)" 18 + xmlns="http://www.w3.org/2000/svg" 19 + class="StyledIconBase-sc-ea9ulj-0 hFaFXW" 20 + > 21 + <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"> 22 + </path> 23 + </svg> 24 + </a> 25 + <a 26 + href="https://bsky.app/profile/tsiry-sandratraina.com" 27 + target="_blank" 28 + class="social" 29 + > 30 + <svg fill="none" viewBox="0 0 64 57" width="21" height="21"> 31 + <path 32 + fill="rgb(109, 109, 156)" 33 + d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805ZM50.127 3.805C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745Z" 34 + > 35 + </path> 36 + </svg> 37 + </a> 38 + <a href="https://twitter.com/tsiry_sndr" target="_blank" class="social"> 39 + <svg 40 + viewBox="0 0 16 16" 41 + height="21" 42 + width="21" 43 + aria-hidden="true" 44 + focusable="false" 45 + fill="rgb(109, 109, 156)" 46 + xmlns="http://www.w3.org/2000/svg" 47 + class="StyledIconBase-sc-ea9ulj-0 hFaFXW" 48 + > 49 + <path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"> 50 + </path> 51 + </svg> 52 + </a> 53 + <a 54 + href="https://www.linkedin.com/in/tsiry-sandratraina" 55 + class="social" 56 + target="_blank" 57 + > 58 + <svg 59 + viewBox="0 0 448 512" 60 + height="20" 61 + width="20" 62 + focusable="false" 63 + role="img" 64 + fill="rgb(109, 109, 156)" 65 + xmlns="http://www.w3.org/2000/svg" 66 + class="StyledIconBase-sc-ea9ulj-0 hRnJPC" 67 + > 68 + <title>LinkedinIn icon</title> 69 + <path 70 + fill="rgb(109, 109, 156)" 71 + d="M100.3 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.6 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.3 61.9 111.3 142.3V448z" 72 + > 73 + </path> 74 + </svg> 75 + </a> 76 + </div> 77 + </div> 78 + ); 79 + }
+38 -3
deno.json
··· 1 1 { 2 + "lock": false, 2 3 "tasks": { 3 - "dev": "deno run --watch -A main.tsx" 4 - } 5 - } 4 + "check": "deno fmt --check && deno lint && deno check **/*.ts && deno check **/*.tsx", 5 + "cli": "echo \"import '\\$fresh/src/dev/cli.ts'\" | deno run --unstable -A -", 6 + "manifest": "deno task cli manifest $(pwd)", 7 + "start": "deno run -A --watch=static/,routes/ dev.ts", 8 + "build": "deno run -A dev.ts build", 9 + "preview": "deno run -A main.ts", 10 + "update": "deno run -A -r https://fresh.deno.dev/update ." 11 + }, 12 + "lint": { 13 + "rules": { 14 + "tags": [ 15 + "fresh", 16 + "recommended" 17 + ] 18 + } 19 + }, 20 + "exclude": [ 21 + "**/_fresh/*" 22 + ], 23 + "imports": { 24 + "$fresh/": "https://deno.land/x/fresh@1.7.3/", 25 + "dayjs": "npm:dayjs@^1.11.13", 26 + "preact": "https://esm.sh/preact@10.22.0", 27 + "preact/": "https://esm.sh/preact@10.22.0/", 28 + "@preact/signals": "https://esm.sh/*@preact/signals@1.2.2", 29 + "@preact/signals-core": "https://esm.sh/*@preact/signals-core@1.5.1", 30 + "tailwindcss": "npm:tailwindcss@3.4.1", 31 + "tailwindcss/": "npm:/tailwindcss@3.4.1/", 32 + "tailwindcss/plugin": "npm:/tailwindcss@3.4.1/plugin.js", 33 + "$std/": "https://deno.land/std@0.216.0/" 34 + }, 35 + "compilerOptions": { 36 + "jsx": "react-jsx", 37 + "jsxImportSource": "preact" 38 + }, 39 + "nodeModulesDir": "auto" 40 + }
-20
deno.lock
··· 1 - { 2 - "version": "3", 3 - "packages": { 4 - "specifiers": { 5 - "npm:dayjs": "npm:dayjs@1.11.10" 6 - }, 7 - "npm": { 8 - "dayjs@1.11.10": { 9 - "integrity": "sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==", 10 - "dependencies": {} 11 - } 12 - } 13 - }, 14 - "remote": { 15 - "https://deno.land/std@0.192.0/fmt/colors.ts": "d67e3cd9f472535241a8e410d33423980bec45047e343577554d3356e1f0ef4e", 16 - "https://deno.land/x/htm@0.2.1/html.tsx": "afcf0500d9199e8efe6877d9e7bf4f7907d0bba09906b6036cfe5e90458083b3", 17 - "https://deno.land/x/htm@0.2.1/jsx.ts": "2dfbc2b208981b9348aee02494ba064eac72f686d2e5ecc19d7f3fcdb9734e3d", 18 - "https://deno.land/x/htm@0.2.1/mod.ts": "d930056a7c8c645bea5f9c116d65bc309d12cfebafa8db98f7c406e1984caaec" 19 - } 20 - }
-5
deps.ts
··· 1 - import html, { h } from "https://deno.land/x/htm@0.2.1/mod.ts"; 2 - export { html, h }; 3 - import dayjs from "npm:dayjs"; 4 - export { dayjs }; 5 - export { cyan } from "https://deno.land/std@0.192.0/fmt/colors.ts";
+8
dev.ts
··· 1 + #!/usr/bin/env -S deno run -A --watch=static/,routes/ 2 + 3 + import dev from "$fresh/dev.ts"; 4 + import config from "./fresh.config.ts"; 5 + 6 + import "$std/dotenv/load.ts"; 7 + 8 + await dev(import.meta.url, "./main.ts", config);
+6
fresh.config.ts
··· 1 + import tailwind from "$fresh/plugins/tailwind.ts"; 2 + import { defineConfig } from "$fresh/server.ts"; 3 + 4 + export default defineConfig({ 5 + plugins: [tailwind()], 6 + });
+29
fresh.gen.ts
··· 1 + // DO NOT EDIT. This file is generated by Fresh. 2 + // This file SHOULD be checked into source version control. 3 + // This file is automatically updated during development when running `dev.ts`. 4 + 5 + import * as $_404 from "./routes/_404.tsx"; 6 + import * as $_app from "./routes/_app.tsx"; 7 + import * as $api_joke from "./routes/api/joke.ts"; 8 + import * as $greet_name_ from "./routes/greet/[name].tsx"; 9 + import * as $index from "./routes/index.tsx"; 10 + import * as $Counter from "./islands/Counter.tsx"; 11 + import * as $NowPlaying from "./islands/NowPlaying.tsx"; 12 + import type { Manifest } from "$fresh/server.ts"; 13 + 14 + const manifest = { 15 + routes: { 16 + "./routes/_404.tsx": $_404, 17 + "./routes/_app.tsx": $_app, 18 + "./routes/api/joke.ts": $api_joke, 19 + "./routes/greet/[name].tsx": $greet_name_, 20 + "./routes/index.tsx": $index, 21 + }, 22 + islands: { 23 + "./islands/Counter.tsx": $Counter, 24 + "./islands/NowPlaying.tsx": $NowPlaying, 25 + }, 26 + baseUrl: import.meta.url, 27 + } satisfies Manifest; 28 + 29 + export default manifest;
+16
islands/Counter.tsx
··· 1 + import type { Signal } from "@preact/signals"; 2 + import { Button } from "../components/Button.tsx"; 3 + 4 + interface CounterProps { 5 + count: Signal<number>; 6 + } 7 + 8 + export default function Counter(props: CounterProps) { 9 + return ( 10 + <div class="flex gap-8 py-6"> 11 + <Button onClick={() => props.count.value -= 1}>-1</Button> 12 + <p class="text-3xl tabular-nums">{props.count}</p> 13 + <Button onClick={() => props.count.value += 1}>+1</Button> 14 + </div> 15 + ); 16 + }
+157
islands/NowPlaying.tsx
··· 1 + import { useEffect, useState } from "preact/hooks"; 2 + 3 + const did = "did:plc:7vdlgi2bflelz7mmuxoqjfcr"; 4 + 5 + function NowPlaying() { 6 + const [label, setLabel] = useState("Now Playing"); 7 + const [song, setSong] = useState< 8 + { 9 + title: string; 10 + artist: string; 11 + albumArt?: string | null; 12 + albumUri?: string | null; 13 + songUri?: string | null; 14 + artistUri?: string | null; 15 + } | null 16 + >(null); 17 + 18 + const fetchNowPlaying = async () => { 19 + const response = await fetch( 20 + `https://api.rocksky.app/now-playing?did=${did}`, 21 + ); 22 + const data = await response.json(); 23 + if (Object.keys(data).length === 0) { 24 + await fetchSpotifyNowPlaying(); 25 + return; 26 + } 27 + }; 28 + 29 + const fetchSpotifyNowPlaying = async () => { 30 + const response = await fetch( 31 + `https://api.rocksky.app/spotify/currently-playing\?did=${did}`, 32 + ); 33 + const data = await response.json(); 34 + 35 + if (Object.keys(data).length === 0) { 36 + await fetchLastPlayedSong(); 37 + return; 38 + } 39 + 40 + setSong({ 41 + title: data.item.name, 42 + artist: data.item.artists.map((artist: { name: string }) => artist.name) 43 + .join(", "), 44 + albumArt: data.item.album.images[0].url, 45 + albumUri: data.albumUri 46 + ? `https://rocksky.app/${data.albumUri.split("at://")[1]}` 47 + : null, 48 + songUri: data.songUri 49 + ? `https://rocksky.app/${data.songUri.split("at://")[1]}` 50 + : null, 51 + artistUri: data.artistUri 52 + ? `https://rocksky.app/${data.artistUri.split("at://")[1]}` 53 + : null, 54 + }); 55 + }; 56 + 57 + const fetchLastPlayedSong = async () => { 58 + const response = await fetch( 59 + "https://api.rocksky.app/users/tsiry-sandratraina.com/scrobbles?size=1&offset=0", 60 + ); 61 + const data = await response.json(); 62 + 63 + setLabel("Last Played"); 64 + 65 + setSong({ 66 + title: data[0].title, 67 + artist: data[0].artist, 68 + albumArt: data[0].album_art, 69 + albumUri: data[0].album_uri 70 + ? `https://rocksky.app/${data[0].album_uri.split("at://")[1]}` 71 + : null, 72 + songUri: data[0].track_uri 73 + ? `https://rocksky.app/${data[0].track_uri.split("at://")[1]}` 74 + : null, 75 + artistUri: data[0].artist_uri 76 + ? `https://rocksky.app/${data[0].artist_uri.split("at://")[1]}` 77 + : null, 78 + }); 79 + }; 80 + 81 + useEffect(() => { 82 + const interval = setInterval(() => { 83 + fetchNowPlaying(); 84 + }, 10000); 85 + fetchNowPlaying(); 86 + return () => clearInterval(interval); 87 + }, []); 88 + 89 + return ( 90 + <div> 91 + {song && ( 92 + <div class="flex flex-row"> 93 + {song?.albumUri && ( 94 + <a href={song.albumUri} target="_blank"> 95 + <img 96 + class="w-[96px] h-[96px] rounded-[10px] mr-[20px]" 97 + src={song?.albumArt!} 98 + /> 99 + </a> 100 + )} 101 + {!song?.albumUri && ( 102 + <img 103 + class="w-[96px] h-[96px] rounded-[10px] mr-[20px]" 104 + src={song?.albumArt!} 105 + /> 106 + )} 107 + <div> 108 + <p class="text-[16px] text-[rgb(109,109,156)]"> 109 + {label} 110 + {" on "} 111 + <a 112 + href={`https://rocksky.app/profile/tsiry-sandratraina.com`} 113 + target="_blank" 114 + > 115 + <b>Rocksky</b> 116 + </a> 117 + </p> 118 + <div> 119 + {song?.songUri && ( 120 + <a 121 + href={song?.songUri} 122 + class="text-[20px] line-clamp-1 overflow-hidden text-ellipsis max-w-[600px]" 123 + target="_blank" 124 + > 125 + {song?.title} 126 + </a> 127 + )} 128 + {!song?.songUri && ( 129 + <p class="text-[20px] line-clamp-1 overflow-hidden text-ellipsis max-w-[600px]"> 130 + {song?.title} 131 + </p> 132 + )} 133 + </div> 134 + <div> 135 + {song?.artistUri && ( 136 + <a 137 + href={song?.artistUri} 138 + class="line-clamp-2 overflow-hidden text-ellipsis max-w-[600px]" 139 + target="_blank" 140 + > 141 + {song?.artist} 142 + </a> 143 + )} 144 + {!song?.artistUri && ( 145 + <p class="line-clamp-2 overflow-hidden text-ellipsis max-w-[600px]"> 146 + {song?.artist} 147 + </p> 148 + )} 149 + </div> 150 + </div> 151 + </div> 152 + )} 153 + </div> 154 + ); 155 + } 156 + 157 + export default NowPlaying;
+13
main.ts
··· 1 + /// <reference no-default-lib="true" /> 2 + /// <reference lib="dom" /> 3 + /// <reference lib="dom.iterable" /> 4 + /// <reference lib="dom.asynciterable" /> 5 + /// <reference lib="deno.ns" /> 6 + 7 + import "$std/dotenv/load.ts"; 8 + 9 + import { start } from "$fresh/server.ts"; 10 + import manifest from "./fresh.gen.ts"; 11 + import config from "./fresh.config.ts"; 12 + 13 + await start(manifest, config);
-56
main.tsx
··· 1 - /** @jsx h */ 2 - import { html, h, cyan } from "./deps.ts"; 3 - import { styles } from "./src/styles.ts"; 4 - import { links } from "./src/links.ts"; 5 - import { footer } from "./src/footer.tsx"; 6 - import { bio } from "./src/bio.tsx"; 7 - import { header } from "./src/header.tsx"; 8 - import { dashboard } from "./src/stats/dashboard.tsx"; 9 - import { recentWork } from "./src/stats/recent_work.tsx"; 10 - import { contributions } from "./src/stats/contributions.tsx"; 11 - 12 - const handler = (_req: Request) => 13 - html({ 14 - title: "Tsiry Sandratraina", 15 - links, 16 - styles, 17 - body: ( 18 - <body> 19 - <div class="wrapper"> 20 - {header()} 21 - {bio()} 22 - <br /> 23 - <br /> 24 - {dashboard()} 25 - <div class="dashboard"> 26 - <div style="flex: 1; margin-right: 20px;">{recentWork()}</div> 27 - <div style="flex: 1">{contributions()}</div> 28 - </div> 29 - <div> 30 - <img 31 - class="stats" 32 - src="https://stats.quine.sh/tsirysndr/topics-over-time?theme=dark" 33 - style="margin-bottom: 15px" 34 - /> 35 - <img 36 - class="stats" 37 - src="https://stats.quine.sh/tsirysndr/languages-over-time?theme=dark" 38 - style="margin-bottom: 15px" 39 - /> 40 - </div> 41 - {footer()} 42 - </div> 43 - </body> 44 - ), 45 - }); 46 - 47 - const port = Deno.env.get("PORT") ? Number(Deno.env.get("PORT")) : 8000; 48 - 49 - Deno.serve( 50 - { 51 - port, 52 - onListen: () => 53 - console.log(`Server started on ${cyan(`http://localhost:${port}`)} 🚀`), 54 - }, 55 - handler 56 - );
+27
routes/_404.tsx
··· 1 + import { Head } from "$fresh/runtime.ts"; 2 + 3 + export default function Error404() { 4 + return ( 5 + <> 6 + <Head> 7 + <title>404 - Page not found</title> 8 + </Head> 9 + <div class="px-4 py-8 mx-auto bg-[#86efac]"> 10 + <div class="max-w-screen-md mx-auto flex flex-col items-center justify-center"> 11 + <img 12 + class="my-6" 13 + src="/logo.svg" 14 + width="128" 15 + height="128" 16 + alt="the Fresh logo: a sliced lemon dripping with juice" 17 + /> 18 + <h1 class="text-4xl font-bold">404 - Page not found</h1> 19 + <p class="my-4"> 20 + The page you were looking for doesn't exist. 21 + </p> 22 + <a href="/" class="underline">Go back home</a> 23 + </div> 24 + </div> 25 + </> 26 + ); 27 + }
+16
routes/_app.tsx
··· 1 + import { type PageProps } from "$fresh/server.ts"; 2 + export default function App({ Component }: PageProps) { 3 + return ( 4 + <html> 5 + <head> 6 + <meta charset="utf-8" /> 7 + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 8 + <title>Tsiry Sandratraina</title> 9 + <link rel="stylesheet" href="/styles.css" /> 10 + </head> 11 + <body> 12 + <Component /> 13 + </body> 14 + </html> 15 + ); 16 + }
+21
routes/api/joke.ts
··· 1 + import { FreshContext } from "$fresh/server.ts"; 2 + 3 + // Jokes courtesy of https://punsandoneliners.com/randomness/programmer-jokes/ 4 + const JOKES = [ 5 + "Why do Java developers often wear glasses? They can't C#.", 6 + "A SQL query walks into a bar, goes up to two tables and says “can I join you?”", 7 + "Wasn't hard to crack Forrest Gump's password. 1forrest1.", 8 + "I love pressing the F5 key. It's refreshing.", 9 + "Called IT support and a chap from Australia came to fix my network connection. I asked “Do you come from a LAN down under?”", 10 + "There are 10 types of people in the world. Those who understand binary and those who don't.", 11 + "Why are assembly programmers often wet? They work below C level.", 12 + "My favourite computer based band is the Black IPs.", 13 + "What programme do you use to predict the music tastes of former US presidential candidates? An Al Gore Rhythm.", 14 + "An SEO expert walked into a bar, pub, inn, tavern, hostelry, public house.", 15 + ]; 16 + 17 + export const handler = (_req: Request, _ctx: FreshContext): Response => { 18 + const randomIndex = Math.floor(Math.random() * JOKES.length); 19 + const body = JOKES[randomIndex]; 20 + return new Response(body); 21 + };
+5
routes/greet/[name].tsx
··· 1 + import { PageProps } from "$fresh/server.ts"; 2 + 3 + export default function Greet(props: PageProps) { 4 + return <div>Hello {props.params.name}</div>; 5 + }
+92
routes/index.tsx
··· 1 + import dayjs from "dayjs"; 2 + import { Footer } from "../components/Footer.tsx"; 3 + import NowPlaying from "../islands/NowPlaying.tsx"; 4 + 5 + export default function Home() { 6 + return ( 7 + <div className="bg-[#1b0642] min-h-screen w-full text-white font-outfit"> 8 + <div class="md:w-[704px] mx-auto pt-[23px] px-5 md:px-0"> 9 + <h2 class="font-medium mb-0 text-[28px]"> 10 + Hello I'm 11 + </h2> 12 + <h1 class="mb-[21.44px] text-4xl font-semibold mt-0"> 13 + Tsiry Sandratraina 14 + <b class="ml-[5px] animate-blinker text-[28px]">_</b> 15 + </h1> 16 + <p class="text-[19px] mb-[40px]"> 17 + I'm a developer and{" "} 18 + <a 19 + href="https://github.com/tsirysndr" 20 + class="text-[#ff5dae]" 21 + target="_blank" 22 + > 23 + open source enthusiast 24 + </a>{" "} 25 + with over {dayjs().diff("2015-01-01", "year")}{" "} 26 + years of experience in software engineering living in Antananarivo, 27 + Madagascar. I like Rust, Deno, Bun, Gleam, TypeScript, ReScript and 28 + PureScript. I build{" "} 29 + <a 30 + href="https://rocksky.app" 31 + class="text-[#ff5dae]" 32 + target="_blank" 33 + > 34 + Rocksky 35 + </a>,{" "} 36 + <a 37 + href="https://github.com/fluentci-io/fluentci" 38 + class="text-[#ff5dae]" 39 + target="_blank" 40 + > 41 + Fluent CI 42 + </a>,{" "} 43 + <a 44 + href="https://github.com/pocketenv-io/pocketenv" 45 + class="text-[#ff5dae]" 46 + target="_blank" 47 + > 48 + Pocketenv 49 + </a>,{" "} 50 + <a 51 + href="https://github.com/tsirysndr/envhub" 52 + class="text-[#ff5dae]" 53 + target="_blank" 54 + > 55 + Envhub 56 + </a>,{" "} 57 + <a 58 + href="https://github.com/tsirysndr/tunein-cli" 59 + class="text-[#ff5dae]" 60 + target="_blank" 61 + > 62 + TuneIn CLI 63 + </a>,{" "} 64 + <a 65 + href="https://github.com/tsirysndr/rockbox-zig" 66 + class="text-[#ff5dae]" 67 + target="_blank" 68 + > 69 + Rockbox Zig 70 + </a>{" "} 71 + and few other open source projects 🦀 🦕 🧑‍🔬 ❄️ 💻 🚀. 72 + <br /> 73 + <br /> 74 + Wanna talk about something? Send me a message on{" "} 75 + <a 76 + href="https://keybase.io/tsiry/chat" 77 + class="text-[#ff5dae]" 78 + target="_blank" 79 + > 80 + Keybase 81 + </a>{" "} 82 + or email me at{" "} 83 + <a href="mailto:tsiry.sndr@rocksky.app" class="text-[#ff5dae]"> 84 + tsiry.sndr@rocksky.app 85 + </a>. 86 + </p> 87 + <NowPlaying /> 88 + <Footer /> 89 + </div> 90 + </div> 91 + ); 92 + }
-40
src/bio.tsx
··· 1 - /** @jsx h */ 2 - import { dayjs, h } from "../deps.ts"; 3 - 4 - export function bio() { 5 - return ( 6 - <div style="line-height: 1.6"> 7 - I'm a developer and{" "} 8 - <a href="https://github.com/tsirysndr" target="_blank"> 9 - open source enthusiast 10 - </a>{" "} 11 - with over <b>{dayjs().diff(dayjs("2015"), "year")} years of experience</b>{" "} 12 - in <b>software engineering</b> living in Antananarivo, Madagascar. I like 13 - Rust, Deno, Bun, Gleam, TypeScript, ReScript and PureScript. I build{" "} 14 - <a href="https://github.com/fluentci-io/fluentci" target="_blank"> 15 - Fluent CI 16 - </a> 17 - ,{" "} 18 - <a href="https://github.com/pocketenv-io/pocketenv" target="_blank"> 19 - Pocketenv 20 - </a> 21 - ,{" "} 22 - <a href="https://github.com/tsirysndr/envhub" target="_blank"> 23 - Envhub 24 - </a> 25 - ,{" "} 26 - <a href="https://github.com/tsirysndr/tunein-cli" target="_blank"> 27 - TuneIn CLI 28 - </a> 29 - ,{" "} 30 - <a href="https://github.com/tsirysndr/rockbox-zig" target="_blank"> 31 - Rockbox Zig 32 - </a>{" "} 33 - and few other open source projects 🦀 🦕 🧑‍🔬 ❄️ 💻 🚀. <br /> 34 - <br /> 35 - Wanna talk about something? Send me a message on{" "} 36 - <a href="https://keybase.io/tsiry/chat">Keybase</a> or email me at{" "} 37 - <a href="mailto:tsiry.sndr@fluentci.io">tsiry.sndr@fluentci.io</a>. 38 - </div> 39 - ); 40 - }
-14
src/footer.tsx
··· 1 - /** @jsx h */ 2 - import { dayjs, h } from "../deps.ts"; 3 - import { social } from "./social/mod.tsx"; 4 - 5 - export function footer() { 6 - return ( 7 - <div style="display: flex; flex-direction: row; justify-content: space-between; margin-bottom:20px; margin-top: 100px;"> 8 - <div style="color: rgb(109, 109, 156); font-size: 17px;"> 9 - © {dayjs().format("YYYY")} Tsiry Sandratraina. 10 - </div> 11 - {social()} 12 - </div> 13 - ); 14 - }
-14
src/header.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../deps.ts"; 3 - 4 - export function header() { 5 - return ( 6 - <div> 7 - <h2 style="font-weight: 500;margin-bottom: 0">Hello, I'm</h2> 8 - <h1 style="font-size: 2rem; font-weight: 600; margin-top: 0"> 9 - Tsiry Sandratraina 10 - <b class="blinking_cursor">_</b> 11 - </h1> 12 - </div> 13 - ); 14 - }
-14
src/links.ts
··· 1 - export const links = [ 2 - { 3 - rel: "preconnect", 4 - href: "https://fonts.googleapis.com", 5 - }, 6 - { 7 - rel: "preconnect", 8 - href: "https://fonts.gstatic.com", 9 - }, 10 - { 11 - rel: "stylesheet", 12 - href: "https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap", 13 - }, 14 - ];
-19
src/social/bluesky.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - 4 - export function bluesky() { 5 - return ( 6 - <a 7 - href="https://bsky.app/profile/tsiry-sandratraina.com" 8 - target="_blank" 9 - class="social" 10 - > 11 - <svg fill="none" viewBox="0 0 64 57" width="21" height="21"> 12 - <path 13 - fill="rgb(109, 109, 156)" 14 - d="M13.873 3.805C21.21 9.332 29.103 20.537 32 26.55v15.882c0-.338-.13.044-.41.867-1.512 4.456-7.418 21.847-20.923 7.944-7.111-7.32-3.819-14.64 9.125-16.85-7.405 1.264-15.73-.825-18.014-9.015C1.12 23.022 0 8.51 0 6.55 0-3.268 8.579-.182 13.873 3.805ZM50.127 3.805C42.79 9.332 34.897 20.537 32 26.55v15.882c0-.338.13.044.41.867 1.512 4.456 7.418 21.847 20.923 7.944 7.111-7.32 3.819-14.64-9.125-16.85 7.405 1.264 15.73-.825 18.014-9.015C62.88 23.022 64 8.51 64 6.55c0-9.818-8.578-6.732-13.873-2.745Z" 15 - ></path> 16 - </svg> 17 - </a> 18 - ); 19 - }
-21
src/social/discord.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - 4 - export function discord() { 5 - return ( 6 - <a href="https://discord.com" target="_blank" class="social"> 7 - <svg 8 - viewBox="0 0 16 16" 9 - height="21" 10 - width="21" 11 - aria-hidden="true" 12 - focusable="false" 13 - fill="currentColor" 14 - xmlns="http://www.w3.org/2000/svg" 15 - class="StyledIconBase-sc-ea9ulj-0 hFaFXW" 16 - > 17 - <path d="M13.545 2.907a13.227 13.227 0 0 0-3.257-1.011.05.05 0 0 0-.052.025c-.141.25-.297.577-.406.833a12.19 12.19 0 0 0-3.658 0 8.258 8.258 0 0 0-.412-.833.051.051 0 0 0-.052-.025c-1.125.194-2.22.534-3.257 1.011a.041.041 0 0 0-.021.018C.356 6.024-.213 9.047.066 12.032c.001.014.01.028.021.037a13.276 13.276 0 0 0 3.995 2.02.05.05 0 0 0 .056-.019c.308-.42.582-.863.818-1.329a.05.05 0 0 0-.01-.059.051.051 0 0 0-.018-.011 8.875 8.875 0 0 1-1.248-.595.05.05 0 0 1-.02-.066.051.051 0 0 1 .015-.019c.084-.063.168-.129.248-.195a.05.05 0 0 1 .051-.007c2.619 1.196 5.454 1.196 8.041 0a.052.052 0 0 1 .053.007c.08.066.164.132.248.195a.051.051 0 0 1-.004.085 8.254 8.254 0 0 1-1.249.594.05.05 0 0 0-.03.03.052.052 0 0 0 .003.041c.24.465.515.909.817 1.329a.05.05 0 0 0 .056.019 13.235 13.235 0 0 0 4.001-2.02.049.049 0 0 0 .021-.037c.334-3.451-.559-6.449-2.366-9.106a.034.034 0 0 0-.02-.019Zm-8.198 7.307c-.789 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.45.73 1.438 1.613 0 .888-.637 1.612-1.438 1.612Zm5.316 0c-.788 0-1.438-.724-1.438-1.612 0-.889.637-1.613 1.438-1.613.807 0 1.451.73 1.438 1.613 0 .888-.631 1.612-1.438 1.612Z"></path> 18 - </svg> 19 - </a> 20 - ); 21 - }
-21
src/social/github.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - 4 - export function github() { 5 - return ( 6 - <a href="https://github.com/tsirysndr" target="_blank" class="social"> 7 - <svg 8 - viewBox="0 0 16 16" 9 - height="20" 10 - width="20" 11 - aria-hidden="true" 12 - focusable="false" 13 - fill="currentColor" 14 - xmlns="http://www.w3.org/2000/svg" 15 - class="StyledIconBase-sc-ea9ulj-0 hFaFXW" 16 - > 17 - <path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path> 18 - </svg> 19 - </a> 20 - ); 21 - }
-29
src/social/linkedin.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - 4 - export function linkedin() { 5 - return ( 6 - <a 7 - href="https://www.linkedin.com/in/tsiry-sandratraina" 8 - class="social" 9 - target="_blank" 10 - > 11 - <svg 12 - viewBox="0 0 448 512" 13 - height="20" 14 - width="20" 15 - focusable="false" 16 - role="img" 17 - fill="currentColor" 18 - xmlns="http://www.w3.org/2000/svg" 19 - class="StyledIconBase-sc-ea9ulj-0 hRnJPC" 20 - > 21 - <title>LinkedinIn icon</title> 22 - <path 23 - fill="currentColor" 24 - d="M100.3 448H7.4V148.9h92.88zM53.79 108.1C24.09 108.1 0 83.5 0 53.8a53.79 53.79 0 0 1 107.6 0c0 29.7-24.1 54.3-53.79 54.3zM447.9 448h-92.68V302.4c0-34.7-.7-79.2-48.29-79.2-48.29 0-55.69 37.7-55.69 76.7V448h-92.78V148.9h89.08v40.8h1.3c12.4-23.5 42.69-48.3 87.88-48.3 94 0 111.3 61.9 111.3 142.3V448z" 25 - ></path> 26 - </svg> 27 - </a> 28 - ); 29 - }
-17
src/social/mod.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - import { twitter } from "../twitter.tsx"; 4 - import { bluesky } from "./bluesky.tsx"; 5 - import { github } from "./github.tsx"; 6 - import { linkedin } from "./linkedin.tsx"; 7 - 8 - export function social() { 9 - return ( 10 - <div style="display: flex; flex-direction: row; justify-content: space-around; width: 100px;"> 11 - {github()} 12 - {bluesky()} 13 - {twitter()} 14 - {linkedin()} 15 - </div> 16 - ); 17 - }
-22
src/stats/contributions.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - 4 - export function contributions() { 5 - return ( 6 - <a 7 - href="https://next.ossinsight.io/widgets/official/analyze-user-contribution-time-distribution?user_id=15877106&period=all_times&color_scheme=light" 8 - target="_blank" 9 - style="display: block" 10 - align="center" 11 - > 12 - <picture> 13 - <img 14 - alt="Contribution Time Distribution of @tsirysndr" 15 - src="https://next.ossinsight.io/widgets/official/analyze-user-contribution-time-distribution/thumbnail.png?user_id=15877106&period=all_times&image_size=auto&color_scheme=light" 16 - style="width: 100%; " 17 - height="164px" 18 - /> 19 - </picture> 20 - </a> 21 - ); 22 - }
-44
src/stats/dashboard.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - 4 - export function dashboard() { 5 - return ( 6 - <div> 7 - <a 8 - href="https://next.ossinsight.io/widgets/official/compose-user-dashboard-stats?user_id=15877106" 9 - target="_blank" 10 - style="display: block" 11 - align="center" 12 - > 13 - <picture> 14 - <img 15 - alt="Dashboard stats of @tsirysndr" 16 - src="https://next.ossinsight.io/widgets/official/compose-user-dashboard-stats/thumbnail.png?user_id=15877106&image_size=auto&color_scheme=light" 17 - style="width: 100%;margin-bottom: 20px;" 18 - height="auto" 19 - /> 20 - </picture> 21 - </a> 22 - <div class="gh-trends"> 23 - <a 24 - href="https://www.githubtrends.io/wrapped/tsirysndr" 25 - style="margin-right: 20px;" 26 - > 27 - <img src="https://api.githubtrends.io/user/svg/tsirysndr/langs?time_range=one_year&theme=ferns" /> 28 - </a> 29 - <a href="https://www.githubtrends.io/wrapped/tsirysndr"> 30 - <img src="https://api.githubtrends.io/user/svg/tsirysndr/repos?time_range=one_year&theme=ferns" /> 31 - </a> 32 - </div> 33 - <a href="https://ossinsight.io/analyze/tsirysndr"> 34 - <picture> 35 - <img 36 - src="https://github-readme-activity-graph.vercel.app/graph?username=tsirysndr&theme=react-light&hide_border=true&hide_title=false&area=true&custom_title=Total%20contribution%20graph%20in%20all%20repo" 37 - style="width: 100%; margin-bottom: 20px;" 38 - alt="activity graph" 39 - /> 40 - </picture> 41 - </a> 42 - </div> 43 - ); 44 - }
-22
src/stats/recent_work.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../../deps.ts"; 3 - 4 - export function recentWork() { 5 - return ( 6 - <a 7 - href="https://next.ossinsight.io/widgets/official/compose-currently-working-on?user_id=15877106&activity_type=all" 8 - target="_blank" 9 - style="display: block" 10 - align="center" 11 - > 12 - <picture> 13 - <img 14 - alt="@tsirysndr's Recent Work - Last 28 days" 15 - src="https://next.ossinsight.io/widgets/official/compose-currently-working-on/thumbnail.png?user_id=15877106&activity_type=all&image_size=auto&color_scheme=light" 16 - style="width: 100%;" 17 - height="auto" 18 - /> 19 - </picture> 20 - </a> 21 - ); 22 - }
-85
src/styles.ts
··· 1 - export const styles = [ 2 - "html, body { margin: 0; height: 100%; }", 3 - `body { 4 - font-family: "Outfit", sans-serif; 5 - background: #12152b; 6 - color: #fff; 7 - display: flex; 8 - flex-direction: column; 9 - font-size: 19px; 10 - }`, 11 - `a { 12 - color: #1effc3; 13 - text-decoration: none; 14 - }`, 15 - ` 16 - .social { 17 - color: rgb(109, 109, 156); 18 - } 19 - 20 - .social:hover { 21 - color: #ffffffec; 22 - } 23 - `, 24 - ` 25 - .wrapper { 26 - width: 704px; margin: 0 auto; 27 - } 28 - @media (max-width: 768px) { 29 - .wrapper { 30 - width: 100%; 31 - padding: 0 20px; 32 - } 33 - } 34 - `, 35 - ` 36 - .blinking_cursor { 37 - margin-left: 5px; 38 - -webkit-animation: blinker 1s none infinite; 39 - animation: blinker 1s none infinite; 40 - font-size: 28px; 41 - }@-webkit-keyframes blinker { 42 - 50% { 43 - opacity: 0; 44 - } 45 - }@keyframes blinker { 46 - 50% { 47 - opacity: 0; 48 - } 49 - } 50 - `, 51 - ` 52 - .dashboard { 53 - display: flex; 54 - flex-direction: row; 55 - margin-bottom: 15px; 56 - } 57 - .stats { 58 - width: 50%; 59 - } 60 - @media (max-width: 768px) { 61 - .dashboard { 62 - display: flex; 63 - flex-direction: column; 64 - margin-bottom: 15px; 65 - } 66 - .stats { 67 - width: 100%; 68 - } 69 - } 70 - 71 - `, 72 - ` 73 - .gh-trends { 74 - display: flex; 75 - flex-direction: row; 76 - margin-bottom: 20px; 77 - } 78 - 79 - @media (max-width: 612px) { 80 - .gh-trends { 81 - flex-direction: column; 82 - } 83 - } 84 - `, 85 - ];
-21
src/twitter.tsx
··· 1 - /** @jsx h */ 2 - import { h } from "../deps.ts"; 3 - 4 - export function twitter() { 5 - return ( 6 - <a href="https://twitter.com/tsiry_sndr" target="_blank" class="social"> 7 - <svg 8 - viewBox="0 0 16 16" 9 - height="21" 10 - width="21" 11 - aria-hidden="true" 12 - focusable="false" 13 - fill="currentColor" 14 - xmlns="http://www.w3.org/2000/svg" 15 - class="StyledIconBase-sc-ea9ulj-0 hFaFXW" 16 - > 17 - <path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"></path> 18 - </svg> 19 - </a> 20 - ); 21 - }
static/favicon.ico

This is a binary file and will not be displayed.

+6
static/logo.svg
··· 1 + <svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 + <path d="M34.092 8.845C38.929 20.652 34.092 27 30 30.5c1 3.5-2.986 4.222-4.5 2.5-4.457 1.537-13.512 1.487-20-5C2 24.5 4.73 16.714 14 11.5c8-4.5 16-7 20.092-2.655Z" fill="#FFDB1E"/> 3 + <path d="M14 11.5c6.848-4.497 15.025-6.38 18.368-3.47C37.5 12.5 21.5 22.612 15.5 25c-6.5 2.587-3 8.5-6.5 8.5-3 0-2.5-4-5.183-7.75C2.232 23.535 6.16 16.648 14 11.5Z" fill="#fff" stroke="#FFDB1E"/> 4 + <path d="M28.535 8.772c4.645 1.25-.365 5.695-4.303 8.536-3.732 2.692-6.606 4.21-7.923 4.83-.366.173-1.617-2.252-1.617-1 0 .417-.7 2.238-.934 2.326-1.365.512-4.223 1.29-5.835 1.29-3.491 0-1.923-4.754 3.014-9.122.892-.789 1.478-.645 2.283-.645-.537-.773-.534-.917.403-1.546C17.79 10.64 23 8.77 25.212 8.42c.366.014.82.35.82.629.41-.14 2.095-.388 2.503-.278Z" fill="#FFE600"/> 5 + <path d="M14.297 16.49c.985-.747 1.644-1.01 2.099-2.526.566.121.841-.08 1.29-.701.324.466 1.657.608 2.453.701-.715.451-1.057.852-1.452 2.106-1.464-.611-3.167-.302-4.39.42Z" fill="#fff"/> 6 + </svg>
+5
static/styles.css
··· 1 + @import url("https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&display=swap"); 2 + 3 + @tailwind base; 4 + @tailwind components; 5 + @tailwind utilities;
+22
tailwind.config.ts
··· 1 + import { type Config } from "tailwindcss"; 2 + 3 + export default { 4 + content: [ 5 + "{routes,islands,components}/**/*.{ts,tsx,js,jsx}", 6 + ], 7 + theme: { 8 + extend: { 9 + fontFamily: { 10 + outfit: ["Outfit", "sans-serif"], 11 + }, 12 + keyframes: { 13 + blinker: { 14 + "50%": { opacity: "0" }, 15 + }, 16 + }, 17 + animation: { 18 + blinker: "blinker 1s infinite", 19 + }, 20 + }, 21 + }, 22 + } satisfies Config;