initial commit
This commit is contained in:
commit
e2466b202a
50 changed files with 4356 additions and 0 deletions
22
components/Button.tsx
Normal file
22
components/Button.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Copyright (c) 2025 favewa
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import type { ComponentChildren } from "preact";
|
||||
|
||||
export interface ButtonProps {
|
||||
id?: string;
|
||||
onClick?: () => void;
|
||||
children?: ComponentChildren;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
export function Button(props: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
{...props}
|
||||
class="px-2 py-1 border-gray-500 border-2 rounded-sm bg-white hover:bg-gray-200 transition-colors"
|
||||
/>
|
||||
);
|
||||
}
|
||||
21
components/Empty.tsx
Normal file
21
components/Empty.tsx
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* Copyright (c) 2025 favewa
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export default function Empty({ path }: any) {
|
||||
return (
|
||||
<>
|
||||
<a href="/">← go back</a>
|
||||
<br /> <br />
|
||||
<div style={{ color: "var(--theme-foreground-alt)" }}>
|
||||
<span>
|
||||
at@localhost<strong></strong>:<strong>~/reports</strong>$ ls
|
||||
</span>
|
||||
<p>
|
||||
ls: cannot access <strong>'{path}'</strong>: No such file or directory
|
||||
</p>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
36
components/Fm.tsx
Normal file
36
components/Fm.tsx
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/**
|
||||
* Copyright (c) 2025 favewa
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { tracks } from "../utils/fm.ts";
|
||||
|
||||
export function Fm() {
|
||||
if (!tracks) return;
|
||||
|
||||
const content = tracks().slice(0, 4);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section id="media">
|
||||
<h2>Recently listened</h2>
|
||||
<ul class="fm-recent-tracks">
|
||||
{content.map((track) => (
|
||||
<li class="fm-recent" key={track.artist + track.name}>
|
||||
{track.cover && <img class="cover" src={track.cover} alt="" />}
|
||||
|
||||
<div class="meta">
|
||||
<strong class="title">{track.name}</strong>
|
||||
<span>{track.artist}</span>
|
||||
{track.loved && <span class="loved">❤</span>}
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
<a class="fm-more" href="https://last.fm/user/favewa">
|
||||
Check out more on Last.fm
|
||||
</a>
|
||||
</ul>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
||||
20
components/Header.tsx
Normal file
20
components/Header.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Copyright (c) 2025 favewa
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export function Header(props: any) {
|
||||
return (
|
||||
<header {...props}>
|
||||
<h1 class="typecycle">
|
||||
~
|
||||
<span>favewa</span>
|
||||
<span>lívia</span>
|
||||
<span>laura</span>
|
||||
</h1>
|
||||
<h2>
|
||||
she/her ∘ free software advocate ∘ linguistics enthusiast ૮ ˶ᵔ ᵕ ᵔ˶ sა
|
||||
</h2>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
24
components/Links.tsx
Normal file
24
components/Links.tsx
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright (c) 2025 favewa
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export default function Links(
|
||||
props: { selected: "home" | "reports" | "misc" },
|
||||
) {
|
||||
return (
|
||||
<nav>
|
||||
<ul>
|
||||
<li class={props.selected === "home" ? "selected" : ""}>
|
||||
<a href="/">home</a>
|
||||
</li>
|
||||
<li class={props.selected === "reports" ? "selected" : ""}>
|
||||
<a href="/reports">reports</a>
|
||||
</li>
|
||||
<li class={props.selected === "misc" ? "selected" : ""}>
|
||||
<a href="/misc">misc</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
67
components/ProjectCard.tsx
Normal file
67
components/ProjectCard.tsx
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/**
|
||||
* Copyright (c) 2025 favewa
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
export interface ProjectCardProps {
|
||||
name: string;
|
||||
author: string;
|
||||
url: string;
|
||||
description: string;
|
||||
languageColor?: string;
|
||||
languageName?: string;
|
||||
}
|
||||
|
||||
export default function ProjectCard({
|
||||
name,
|
||||
author,
|
||||
url,
|
||||
description,
|
||||
languageColor,
|
||||
languageName,
|
||||
}: ProjectCardProps) {
|
||||
return (
|
||||
<a
|
||||
href={url}
|
||||
class="project-card"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span class="external-icon" aria-label="Open externally">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<title>Open Externally</title>
|
||||
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
|
||||
<path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6" />
|
||||
<path d="M11 13l9 -9" />
|
||||
<path d="M15 4h5v5" />
|
||||
</svg>
|
||||
</span>
|
||||
|
||||
<div class="author">
|
||||
<strong>{author}</strong>/{name}
|
||||
</div>
|
||||
|
||||
<p class="description">{description}</p>
|
||||
|
||||
<div class="info">
|
||||
{languageColor && (
|
||||
<span
|
||||
class="language"
|
||||
style={{ "--lang-color": languageColor } as any}
|
||||
/>
|
||||
)}
|
||||
{languageName}
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
41
components/Reports.tsx
Normal file
41
components/Reports.tsx
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* Copyright (c) 2025 favewa
|
||||
* SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
*/
|
||||
|
||||
import { Report } from "../utils/atproto.ts";
|
||||
|
||||
export interface ReportsProps {
|
||||
reports: Report[];
|
||||
}
|
||||
|
||||
export default function Reports({ reports }: ReportsProps) {
|
||||
return (
|
||||
<>
|
||||
<header>
|
||||
<h1>$reports</h1>
|
||||
<h2>thoughts, ramblings, and occasional coherence</h2>
|
||||
</header>
|
||||
|
||||
<ul class="reports">
|
||||
{reports.map((report) => (
|
||||
<li key={report.rkey}>
|
||||
<a href={`/reports/${report.rkey}`} class="report">
|
||||
<span class="date">{report.createdAt}</span>
|
||||
<h3>{report.title}</h3>
|
||||
<p class="excerpt">{report.excerpt}</p>
|
||||
{report.tags && (
|
||||
<div class="tags">
|
||||
{report.tags.map((tag) => (
|
||||
<span key={tag} class="tag">{tag}</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<span class="arrow">→</span>
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue