Compare commits

...

No commits in common. "master" and "pages" have entirely different histories.

27 changed files with 2 additions and 585 deletions

View file

@ -1,42 +0,0 @@
name: Publish
on:
push:
branches: [ master ]
permissions:
contents: read
pages: write
id-token: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Clone repository
uses: actions/checkout@v4
- name: Setup Deno
uses: denoland/setup-deno@v1
with:
deno-version: v1.x
- name: Build site
run: |
deno task build
cp src/static/.domains _site
- name: Deploy to Codeberg Pages
uses: peaceiris/actions-gh-pages@v3
if: github.ref == 'refs/heads/master'
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./_site
force_orphan: true
publish_branch: pages
- name: Deploy to Deno Deploy
uses: samtherapy/deno-deploy@master
with:
deno_deploy_token: ${{ secrets.DENO_DEPLOY_TOKEN }}
entry_point: serve.ts
project: samme
production: true

3
.gitignore vendored
View file

@ -1,3 +0,0 @@
_site
deno.lock
_cache

0
.nojekyll Normal file
View file

21
LICENSE
View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 Óscar Otero
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,27 +0,0 @@
# Simple Me
[Lume](https://lume.land) theme to create a Linktree alternative.
## Install as a remote theme
The **fastest and easiest** way to use this theme is by importing it as a remote
module. It allows to create a blog in seconds and update it at any time just by
changing the version number in the import URL. Just add the following code to
your `_config.ts` file:
```ts
import lume from "lume/mod.ts";
import me from "https://deno.land/x/lume_theme_simple_me/mod.ts";
const site = lume();
site.use(me());
export default site;
```
## Use it as a base template
To use this theme as a base template for a more customized site, clone this repo
and edit the [_config.ts](./_config.ts) file. The source files are in the
[src](./src/) folder.

83
_cms.ts
View file

@ -1,83 +0,0 @@
import lumeCMS from "lume/cms/mod.ts";
const cms = lumeCMS();
cms.document(
"home: The profile page",
"src:index.yml",
[
{
type: "hidden",
name: "layout",
value: "layouts/home.vto",
},
{
type: "object",
name: "header",
description: "The header of the page",
fields: [
"title: text",
"description: markdown",
"avatar: file",
],
},
{
type: "object",
name: "metas",
description: "Data for the meta tags",
fields: [
"title: text",
"description: text",
"image: text",
"twitter: text",
"generator: checkbox",
],
},
{
name: "links",
type: "object-list",
description: "The list of links.",
fields: [
{
type: "text",
name: "type",
description:
"The type of link. It uses the icons and colors from https://simpleicons.org/. For example, 'github', 'instagram', etc.",
options: [
"github",
"instagram",
"linkedin",
"x",
"youtube",
"facebook",
"tiktok",
"patreon",
"paypal",
"mastodon",
"discord",
"spotify",
"opencollective",
"twitch",
],
},
"text: text",
"href: text",
"only_icon: checkbox",
],
},
{
name: "extra_head",
type: "code",
description: "Extra content to include in the <head> tag",
},
{
name: "footer",
type: "markdown",
description: "The footer of the page",
},
],
);
cms.upload("uploads: Uploaded files", "src:*{.jpg,.svg}");
export default cms;

View file

@ -1,11 +0,0 @@
import lume from "lume/mod.ts";
import plugins from "./plugins.ts";
const site = lume({
src: "./src",
location: new URL("https://samtherapy.net")
});
site.use(plugins());
export default site;

BIN
apple-touch-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 144 KiB

After

Width:  |  Height:  |  Size: 144 KiB

View file

@ -1,27 +0,0 @@
{
"imports": {
"lume/": "https://deno.land/x/lume@v2.1.3/",
"lume/cms/": "https://cdn.jsdelivr.net/gh/lumeland/cms@0.3.9/"
},
"tasks": {
"lume": "echo \"import 'lume/cli.ts'\" | deno run -A -",
"build": "deno task lume",
"serve": "deno task lume -s"
},
"compilerOptions": {
"types": [
"lume/types.ts"
]
},
"exclude": [
"./_site"
],
"deploy": {
"project": "bffcfd12-e23c-4402-b99a-335185e60640",
"exclude": [
"**/node_modules"
],
"include": [],
"entrypoint": "serve.ts"
}
}

BIN
favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

1
index.html Normal file

File diff suppressed because one or more lines are too long

25
mod.ts
View file

@ -1,25 +0,0 @@
import plugins from "./plugins.ts";
import "lume/types.ts";
export default function () {
return (site: Lume.Site) => {
// Configure the site
site.use(plugins());
// Add remote files
const files = [
"_includes/css/header.css",
"_includes/css/link.css",
"_includes/layouts/base.vto",
"index.yml",
"styles.css",
"favicon.svg",
"avatar.jpg",
];
for (const file of files) {
site.remoteFile(file, import.meta.resolve(`./src/${file}`));
}
};
}

View file

@ -1,45 +0,0 @@
import "lume/types.ts";
import favicon from "lume/plugins/favicon.ts"
import postcss from "lume/plugins/postcss.ts";
import transformImages from "lume/plugins/transform_images.ts";
import metas from "lume/plugins/metas.ts";
import minifyHTML from "lume/plugins/minify_html.ts";
import svgo from "lume/plugins/svgo.ts";
import basePath from "lume/plugins/base_path.ts";
import * as si from "npm:simple-icons@11.9.0";
import type { SimpleIcon } from "npm:simple-icons@11.9.0";
import Color from "https://colorjs.io/dist/color.js";
const icons = Object.values(si) as SimpleIcon[];
/** Configure the site */
export default function () {
return (site: Lume.Site) => {
site.use(postcss())
.use(favicon())
.use(metas())
.use(svgo())
.use(basePath())
.mergeKey("extra_head", "stringArray")
.use(transformImages())
.copy("static", ".")
.copy("static/.domains")
.use(minifyHTML({
extensions: [".css", ".html", ".js"]
}));
site.data("icon", (slug?: string) => {
if (!slug) return;
return icons.find((icon) => icon.slug === slug);
});
site.data("textColor", (hex: string) => {
const color = new Color(`#${hex}`);
const onWhite = Math.abs(color.contrastWCAG21("white"));
const onBlack = Math.abs(color.contrastWCAG21("black"));
return (onWhite + 0.5) > onBlack ? "white" : "black";
});
site.copy([".jpg", ".webp", ".png"]);
};
}

View file

@ -1,24 +0,0 @@
import Server from "lume/core/server.ts";
import expires from "lume/middlewares/expires.ts"
const server = new Server({
port: 8000,
root: `${Deno.cwd()}/_site`,
});
// Set Access-Control-Allow-Origin header to allow all origins
server.use(async (request, next) => {
// Here you can modify the request before being passed to next middlewares
const response = await next(request);
response.headers.set('Access-Control-Allow-Origin', '*')
// Here you can modify the response before being returned to the previous middleware
return response;
});
server.use(expires())
server.start();
console.log("Listening on http://localhost:8000");

View file

@ -1,36 +0,0 @@
.header {
font: var(--font-body);
margin-bottom: min(5vh, 100px);
color: var(--color-text);
p {
margin: 0;
text-wrap: balance;
+ p {
margin-top: .5em;
}
}
}
.header-avatar {
border-radius: 50%;
aspect-ratio: 1;
object-fit: cover;
object-position: center center;
width: 200px;
max-width: 50vw;
}
.header-title {
font: var(--font-title);
letter-spacing: var(--font-title-spacing);
margin: .5em 0 0;
color: var(--color-base);
}
.header-theme {
position: absolute;
top: 1rem;
right: 1.5rem;
}

View file

@ -1,67 +0,0 @@
.link-list {
list-style: none;
margin: 0;
padding: 0;
display: grid;
row-gap: 10px;
.button {
display: flex;
font: var(--font-body-bold);
transition: transform 200ms;
border: solid 1px #00000022;
&:hover {
transform: scale(1.05);
box-shadow: 0 2px 10px -8px #0009;
}
}
.button:not(.is-primary) {
background: var(--bg-color);
color: var(--text-color);
}
svg {
width: 20px;
height: 20px;
fill: currentColor;
}
}
[data-theme="dark"] {
.link-list .button {
border: solid 1px #FFFFFF16;
}
}
.icon-list {
list-style: none;
margin: 0 0 min(5vh, 100px);
padding: 0;
display: flex;
gap: 10px;
justify-content: center;
svg {
width: 20px;
height: 20px;
fill: currentColor;
}
.button {
display: flex;
font: var(--font-body-bold);
transition: transform 200ms;
&:hover {
transform: scale(1.05);
box-shadow: 0 2px 10px -8px #0009;
}
}
.button:not(.is-primary) {
background: var(--bg-color);
color: var(--text-color);
}
}

View file

@ -1,75 +0,0 @@
<!doctype html>
<html lang="{{ it.lang }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ header.title }}</title>
<meta name="supported-color-schemes" content="light dark">
<meta name="theme-color" content="hsl(220, 20%, 100%)" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="hsl(220, 20%, 10%)" media="(prefers-color-scheme: dark)">
<link rel="stylesheet" href="/styles.css">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon.png">
<link rel="canonical" href="{{ url |> url(true) }}">
{{ it.extra_head?.join("\n") }}
</head>
<body>
<main>
<header class="header">
<script>
let theme = localStorage.getItem("theme") || (window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light");
document.documentElement.dataset.theme = theme;
function changeTheme() {
theme = theme === "dark" ? "light" : "dark";
localStorage.setItem("theme", theme);
document.documentElement.dataset.theme = theme;
}
</script>
<button class="button header-theme" onclick="changeTheme()">
<span class="icon">◐</span>
</button>
<img class="header-avatar" src="{{ header.avatar }}" alt="Avatar" transform-images="webp avif 200@2">
<h1 class="header-title">{{ header.title }}</h1>
{{ header.description |> md }}
</header>
{{> const icons = links.filter((link) => link.only_icon) }}
{{ if icons.length }}
<ul class="icon-list">
{{ for link of icons }}
{{> let i = icon(link.type) }}
<li>
<a href="{{ link.href }}" rel="me" class="button" style="--bg-color:{{ link.hex || `#${i?.hex || "fff" }` }}; --text-color:{{ link.textColor || textColor(i?.hex || "fff") }}" title="{{ link.text }}">
{{ i?.svg }}
</a>
</li>
{{ /for }}
</ul>
{{ /if }}
<ul class="link-list">
{{ for link of links.filter((link) => !link.only_icon) }}
{{> let i = icon(link.type) }}
<li>
<a href="{{ link.href }}" rel="me" class="button" style="--bg-color:{{ link.hex || `#${i?.hex || "fff" }` }}; --text-color:{{ link.textColor || textColor(i?.hex || "fff") }}">
{{ i?.svg }}
{{ link.text }}
</a>
</li>
{{ /for }}
</ul>
</main>
{{ if footer }}
<footer>
{{ footer |> md }}
</footer>
{{ /if }}
</body>
</html>

View file

@ -1,60 +0,0 @@
layout: layouts/base.vto
header:
title: Sam Therapy
description: Nowhere ~~and everywhere~~ all at once.
avatar: /avatar.png
metas:
title: =header.title
description: =header.description
image: =header.avatar
generator: true
twitter: '@weezerfan94'
links:
- text: Git
href: 'https://git.froth.zone/sam'
type: forgejo
only_icon: false
- type: writedotas
text: Blog
href: 'https://blog.froth.zone/sam'
only_icon: false
- type: matrix
text: Matrix
href: 'https://matrix.to/#/@samme:schizo.cafe'
only_icon: false
- type: xmpp
text: XMPP (Not Recommended)
href: 'xmpp://sam@samtherapy.net'
only_icon: false
- type: bluesky
text: Bluesky
href: 'https://bsky.app/profile/samtherapy.net'
only_icon: false
- type: nostr
text: Nostr (I don't like using it but I have one)
href: >-
https://njump.me/nprofile1qyfhwumn8ghj7mmxve3ksctfdch8qatz9uq3vamnwvaz7tmjv4kxz7fwd4hhxarj9ec82c30qythwumn8ghj7un9d3shjtnxwfhhg6pw0fhkuef0qqs9gymnvuqhq66973gsat9rnh44v9la7qmss2xk747z5tv263zydzq2nr59m
only_icon: false
- type: pleroma
text: yeah
href: 'https://froth.zone/users/sam'
only_icon: false
- type: github
href: 'https://github.com/SamTherapy'
only_icon: true
text: GitHub
- only_icon: true
type: gitlab
text: GitLab
href: 'https://gitlab.com/SamTherapy'
- type: codeberg
text: Codeberg
href: 'https://codeberg.org/sammefishe'
only_icon: true
- only_icon: true
type: sourcehut
text: SourceHut
href: 'https://sr.ht/~sammefishe/'
footer: >-
Powered by [Lume](https://lume.land) &
[SimpleMe](https://github.com/lumeland/theme-simple-me) theme

View file

@ -1,39 +0,0 @@
/* Lume's design system */
@import "https://unpkg.com/@lumeland/ds@0.5.2/ds.css";
/* Custom components */
@import "css/header.css";
@import "css/link.css";
body {
display: grid;
grid-template-columns: minmax(0, 500px);
grid-template-rows: 1fr auto;
min-height: 100vh;
text-align: center;
padding: max(20px, 5vh) 20px;
row-gap: 20px;
justify-content: center;
align-content: center;
}
main {
align-self: center;
}
footer {
font: var(--font-small);
color: var(--color-dim);
> * {
margin: 0;
}
> * + * {
margin-top: 1em;
}
a {
color: inherit;
}
}

1
styles.css Normal file
View file

@ -0,0 +1 @@
@import "https://unpkg.com/@lumeland/ds@0.5.2/ds.css"; /* Lume's design system */ /* Custom components */ .header { font: var(--font-body); margin-bottom: min(5vh, 100px); color: var(--color-text); } .header p { margin: 0; text-wrap: balance; } :is(.header p) + p { margin-top: .5em; } .header-avatar { border-radius: 50%; aspect-ratio: 1; -o-object-fit: cover; object-fit: cover; -o-object-position: center center; object-position: center center; width: 200px; max-width: 50vw; } .header-title { font: var(--font-title); letter-spacing: var(--font-title-spacing); margin: .5em 0 0; color: var(--color-base); } .header-theme { position: absolute; top: 1rem; right: 1.5rem; } .link-list { list-style: none; margin: 0; padding: 0; display: grid; row-gap: 10px; } .link-list .button { display: flex; font: var(--font-body-bold); transition: transform 200ms; border: solid 1px #00000022; } .link-list .button:hover { transform: scale(1.05); box-shadow: 0 2px 10px -8px #0009; } .link-list .button:not(.is-primary) { background: var(--bg-color); color: var(--text-color); } .link-list svg { width: 20px; height: 20px; fill: currentColor; } [data-theme="dark"] .link-list .button { border: solid 1px #FFFFFF16; } .icon-list { list-style: none; margin: 0 0 min(5vh, 100px); padding: 0; display: flex; gap: 10px; justify-content: center; } .icon-list svg { width: 20px; height: 20px; fill: currentColor; } .icon-list .button { display: flex; font: var(--font-body-bold); transition: transform 200ms; } .icon-list .button:hover { transform: scale(1.05); box-shadow: 0 2px 10px -8px #0009; } .icon-list .button:not(.is-primary) { background: var(--bg-color); color: var(--text-color); } body { display: grid; grid-template-columns: minmax(0, 500px); grid-template-rows: 1fr auto; min-height: 100vh; text-align: center; padding: max(20px, 5vh) 20px; row-gap: 20px; justify-content: center; align-content: center; } main { align-self: center; } footer { font: var(--font-small); color: var(--color-dim); } footer > * { margin: 0; } footer > * + * { margin-top: 1em; } footer a { color: inherit; }