funny sidebart

This commit is contained in:
sophie 2024-04-26 01:07:34 +01:00
parent 0bd0cd2f25
commit 5726b1d74d
56 changed files with 463 additions and 143 deletions

View File

@ -1,11 +1,20 @@
(respond_file) {
root * {args[0]}
try_files {args[1:]}
file_server
}
zvava.org {
# discrimination
@brave {
header_regexp Sec-Ch-Ua "(?i)(Brave)"
header_regexp Cookie "(?i)(Brave-User)"
}
respond @brave "you are not brave enough" 400
@operagx header_regexp Sec-Ch-Ua "(?i)(opera gx)"
respond @operagx "gamers not allowed" 400
@brave header_regexp Cookie "(?i)(Brave-User)"
respond @brave "you are not brave enough" 400
# media
@media path /media /media/*
route @media {
@ -18,27 +27,41 @@ zvava.org {
}
}
# api
@schema path /schema /schema/ /api/jsonld /api/jsonld/
@api path /api /api/
@viewcount path /api/viewcount /api/viewcount/ /api/viewcount/*
@hue path /api/hue /api/hue/ /api/hue/*
@ntfy path /api/ntfy /api/ntfy/
@sophia path /api/sophia /api/sophia/
respond @api `["jsonld", "viewcount", "hue", "ntfy", "sophia"]`
# jsonld
handle @schema {
header Content-Type "application/ld+json"
import respond_file /var/lib/syncthing/zvava.org.media/misc /schema.jsonld
}
# api endpoints
reverse_proxy @viewcount localhost:8001
reverse_proxy @hue localhost:8003
handle @ntfy {
import respond_file /etc/caddy /api.ntfy.txt
}
handle @sophia {
redir https://zvava.org/schema permanent
}
# wiki
root * /var/www
file_server
# firefox fix
rewrite /favicon.ico /images/favicon.ico
# inline files fix
@inline path *.ass *.bat *.txt
@inline path *.ass *.bat *.txt *.pub *.asc
header @inline {
Content-Type text/plain
Content-Disposition inline
}
# api
@api path /api /api/
@viewcount path /api/viewcount /api/viewcount/*
@ntfy path /api/ntfy /api/ntfy/*
reverse_proxy @viewcount localhost:8001
reverse_proxy @ntfy localhost:8002
respond @api `["viewcount", "ntfy"]`
handle_errors {
handle /api/* {
header Content-Type application/json
@ -57,19 +80,88 @@ zvava.org {
respond "{err.status_code} {err.status_text}"
}
}
file_server
}
http://www.zvava.org, www.zvava.org {
redir https://zvava.org{uri} permanent
}
### robogirl site ###
737a6f6669e1.id {
header Content-Type application/ld+json
header Content-Disposition inline
@mobile header_regexp User-Agent (?i)(mobile|android)
header @mobile Content-Type text/plain
import respond_file /var/lib/syncthing/zvava.org.media /737A6F6669E1.jsonld
}
http://www.737a6f6669e1.id, www.737a6f6669e1.id {
redir https://737a6f6669e1.id{uri} permanent
}
### programming language site ###
magenta.zvava.org {
root * /var/magenta.www
file_server
handle_errors {
@404 expression {err.status_code} == 404
rewrite @404 /404.html
file_server
}
file_server
}
### ntfy server ###
ntfy.zvava.org, http://nfty.zvava.org {
reverse_proxy localhost:8002
@httpget {
protocol http
method GET
path_regexp ^/([-_a-z0-9]{0,64}$|docs/|static/)
}
redir @httpget https://{host}{uri}
@websockets {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @websockets localhost:8002 {
header_up Host {host}
header_up X-Real-IP {remote_host}
header_up X-Forwarded-For {remote_host}
header_up X-Forwarded-Proto {scheme}
}
}
### git forgejo ###
git.zvava.org {
reverse_proxy * localhost:8080
reverse_proxy localhost:8080
}
### firefish ###
fish.zvava.org {
reverse_proxy localhost:8080
}
### personal server 1 ###
isopropyl.zvava.org {
reverse_proxy localhost:8080
}
### personal server 2 ###
hierophant.zvava.org {
reverse_proxy localhost:8080
}
### personal server 3 ###
zerov.zvava.org {
reverse_proxy localhost:8080
}
### ofical magyar smosh ###
http://www.smosh.hu/, http://smosh.hu, www.smosh.hu, smosh.hu {
root * /var/smosh.hu.www
file_server
}

View File

@ -29,7 +29,7 @@
✉️
{{- else if .HasExt ".crt" ".pem" ".x509" ".cer" ".ca-bundle"}}
🧾
{{- else if .HasExt ".key" ".keystore" ".jks" ".p12" ".pfx" ".pub"}}
{{- else if .HasExt ".key" ".keystore" ".jks" ".p12" ".pfx" ".pub" ".asc"}}
🔑
{{- else}}
📄
@ -102,7 +102,7 @@ a[href]:hover, a[href]:focus {
<hr>
<div class="entry">
<span class="date"></span>
</div>
</div>
</body>

169
make.js
View File

@ -201,6 +201,14 @@ function generateTemplates() {
+ "\n```\n " + `[${p.modified}] [${category}]` + "\n```"
}).join("\n") // stringify
// → {html_wiki_recent} template
// remove stubs, get first five pages, and render template
templates["html_wiki_recent"] = pages.filter(page => !page.category.includes("stub")).slice(0, 5).map((p, i) => {
let category = prependRelevantEmoji(p.category[0]).split(" ")[0] // get category emoji
// render
return `<a class="sidebar__link" href="/wiki/${p.page}.html">${category} ${p.title}<br><pre> ${p.modified}</pre></a>`
}).join("\n") // stringify
// → {wiki_all} template
// remove stubs and render template
templates["wiki_all"] = pages.filter(page => !page.category.includes("stub")).map(p => {
@ -232,6 +240,14 @@ function generateTemplates() {
templates["wiki_category__" + c] = catMeta + catBody + "\n" + ps.join("\n\n")
})
// → {wiki_pinned} and {html_wiki_pinned}
templates["wiki_pinned"] = templates["pinned-pages.gmi"]
templates["html_wiki_pinned"] = parseGemini("", templates["wiki_pinned"]).replace(/<p>|<\/p>|<br>/g, "")
.replace(/<a/g, "<a class=\"sidebar__link\"")
// → {head.html} template
templates["head.html"] = useTemplate("head.html", [ "html_wiki_pinned", "html_wiki_recent" ])
// → {html_filter} template
templates["html_filter__inputs"] = '<input type="radio" name="category" id="all" checked>\n' +
ctgs.filter(x => x != "stub").map(x => `<input type="radio" name="category" id="${x}">`).join("\n")
@ -278,7 +294,7 @@ function generateGemini() {
// use templates
// → index
files["index"] = useTemplate("index.gmi", [ "wiki_recent", "build_info_summary" ])
files["index"] = useTemplate("index.gmi", [ "wiki_recent", "build_info_summary", "wiki_pinned" ])
// → stats
files["stats"] = useTemplate("stats.gmi", [ "build_info" ])
@ -341,80 +357,23 @@ function generateHTML(files) {
let output = templates["head.html"].replace("{title}", title)
if (isCategoryPage) output = output.replace("<div class=\"wrap\">", `<div class="wrap wrap__category">`)
let _c = files[f]
.replace(/{gmi[a-z_]*}\n/g, "") // remove gmi-only templates
.replace(/\.ln/g, ".html") // replace ambiguous links
.replace(/stay:\/\//g, "https://") // replace ambiguous protocols
.split("\n```\n") // split into variable for optimized access to .length
_c.forEach((x, i) => {
// if file contains a code block and you are currently in one
if (_c.length > 1 && i % 2 !== 0)
return output += x + "</pre>\n"
output = parseGemini(output, files[f], (l) => {
if (f == "index" && /href="https:\/\/(mk\.catgirlsfor\.science|git\.zvava\.org|www\.buymeacoffee\.com|matrix\.to)/.test(l))
l = l.replace(">", " rel=\"me\">") // add rel=me attribute to some links
// parse remaining content
output += x.split(/\n/).map((l, i, a) => {
l = l.replace(/</g, "&lt;") // escape html tag opening brackets
.replace(/^({br}|>)$/i, "<br>") // add line breaks
// convert headers
.replace(/^### +(.*)/, "<h3>$1</h3>")
.replace(/^## +(.*)/, "<h2>$1</h2>")
.replace(/^# +(.*)/, "<h1>$1</h1>")
// convert images
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.(png|jpg) +(.*)/i, '<img src="$1.$2" alt="$3" title="$3">')
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.(png|jpg)/i, '<img src="$1.$2">')
// convert audio
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.mp3 *(.*)/i,
'<audio controls><source src="$1.mp3" type="audio/mpeg">🔈 audio</audio>')
// convert video
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.mp4 *(.*)/i,
'<video controls><source src="$1.mp4" type="video/mp4">📼 video</video>')
// convert links
.replace(/^=> +([a-z0-9\-_\/\.\(\)%,+:@?!&=#~']+) +(.*)/i, '<a href="$1">$2</a>')
.replace(/^=> +([a-z0-9\-_\/\.\(\)%,+:@?!&=#~']+)/i, '<a href="$1">$1</a>')
// convert block quotes
.replace(/^> *(.*)/, "<blockquote>$1</blockquote>")
// convert lists
.replace(/^[-*+] +(.*)/, "<span class=\"ui\">$1</span>")
.replace(/^([0-9a-bA-B\.]+)\. +(.*)/, "<span class=\"oi\" data-i=\"$1\">$2</span>")
if (f == "wiki/index" && /<a href="\/wiki\/(?!category).+"/.test(l)) { // to append data-category attribute for filters
let start = l.indexOf("/wiki/") + 6, end = l.indexOf(".html", start)
let page = pages.find(x => x.page == l.substring(start, end)) // get linked page
l = l.replace(">", ` data-category="${page ? page.category.join(" ") : "⚠️"}">`) // add
}
if (f == "index" && /href="https:\/\/(mk\.catgirlsfor\.science|git\.zvava\.org|www\.buymeacoffee\.com|matrix\.to)/.test(l))
l = l.replace(">", "rel=\"me\">") // add rel=me attribute to some links
if (f == "wiki/index" && /<a href="\/wiki\/(?!category).+"/.test(l)) { // to append data-category attribute for filters
let start = l.indexOf("/wiki/") + 6, end = l.indexOf(".html", start)
let page = pages.find(x => x.page == l.substring(start, end)) // get linked page
l = l.replace(">", ` data-category="${page ? page.category.join(" ") : "⚠️"}">`) // add
}
// will this line be considered for its spacing?
if (!l.startsWith("<h") && !l.startsWith("<b") && l.replace(/\n/g, "").length > 0) {
// fetch previous/next lines (default to empty string if not able to acquire)
let previousLine = (a[i - 1] || ""); let nextLine = (a[i + 1] || "")
// check if previous/next line is empty or contains a heading or blockquote
let pLineEmpty = previousLine.length == 0 || previousLine.startsWith("#") || previousLine.startsWith(">")
let nLineEmpty = nextLine.length == 0 || nextLine.startsWith("#") || nextLine.startsWith(">")
if (pLineEmpty && nLineEmpty) // 0 & 0
l = "<p>" + l + "</p>" // single lonely paragraph
else if (pLineEmpty && !nLineEmpty) // 0 & 1
l = "<p>" + l + "<br>" // start a paragraph with line breaks
else if (!pLineEmpty && nLineEmpty) // 1 & 0
l = l + "</p>" // end a paragraph with line breaks
else if (!pLineEmpty && !nLineEmpty) // 1 & 1
l += "<br>" // end a line in a paragraph with a line break
}
return l
}) // remove empty sections and join into one
.filter(x => x.length > 0).join("\n") + "\n"
// if file contains a code block and you _just_ aren't at the end of file
if (_c.length > 1 && i != _c.length - 1) output += "<pre>"
return l
})
if (f == "index") { // home page
output = output.replace("{html_buttons}", templates["html_buttons"]) // buttons!!
.replace("{html_webrings}", templates["webrings.html"]) // fancy webrings
.replace("{html_color}", templates["color.html"]) // hue-server client
// add viewcounter script that doesn't display the count
+ templates["viewcounter.html"].replace("{url}", "/" + f + ".html")
} else if (f == "stats") { // statistics page
@ -433,6 +392,10 @@ function generateHTML(files) {
.replace("{url}", "/" + f + ".html")
}
output = output
.replace(/{html_sbcont_start}(<br>)?/g, "<div class=\"sidebar-content\">")
.replace(/{html_sbcont_end}(<br>)?/g, "</div>")
let _f = std.open("out/www/" + f + ".html", "w")
if (_f.error()) reject(_f.error())
_f.puts(output + "</div></body>\n</html>\n")
@ -464,3 +427,73 @@ function generateAss() {
print("\r\x1b[32m-->\x1b[0m finished make script")
}
//
function parseGemini(_output, file, hooks = (l) => l) {
let output = _output
let _c = file
.replace(/{gmi[a-z_]*}\n/g, "") // remove gmi-only templates
.replace(/\.ln/g, ".html") // replace ambiguous links
.replace(/stay:\/\//g, "https://") // replace ambiguous protocols
.split("\n```\n") // split into variable for optimized access to .length
_c.forEach((x, i) => {
// if file contains a code block and you are currently in one
if (_c.length > 1 && i % 2 !== 0)
return output += x + "</pre>\n"
// parse remaining content
output += x.split(/\n/).map((l, i, a) => {
l = l.replace(/</g, "&lt;") // escape html tag opening brackets
.replace(/^({br}|>)$/i, "<br>") // add line breaks
// convert headers
.replace(/^### +(.*)/, "<h3>$1</h3>")
.replace(/^## +(.*)/, "<h2>$1</h2>")
.replace(/^# +(.*)/, "<h1>$1</h1>")
// convert images
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.(png|jpg) +(.*)/i, '<img src="$1.$2" alt="$3" title="$3">')
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.(png|jpg)/i, '<img src="$1.$2">')
// convert audio
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.mp3 *(.*)/i,
'<audio controls><source src="$1.mp3" type="audio/mpeg">🔈 audio</audio>')
// convert video
.replace(/^=> +([a-z0-9\-_\/\.\(\),+:@?!&=#~']+)\.mp4 *(.*)/i,
'<video controls><source src="$1.mp4" type="video/mp4">📼 video</video>')
// convert links
.replace(/^=> +([a-z0-9\-_\/\.\(\)%,+:@?!&=#~']+) +(.*)/i, '<a href="$1">$2</a>')
.replace(/^=> +([a-z0-9\-_\/\.\(\)%,+:@?!&=#~']+)/i, '<a href="$1">$1</a>')
// convert block quotes
.replace(/^> *(.*)/, "<blockquote>$1</blockquote>")
// convert lists
.replace(/^[-*+] +(.*)/, "<span class=\"ui\">$1</span>")
.replace(/^([0-9a-bA-B\.]+)\. +(.*)/, "<span class=\"oi\" data-i=\"$1\">$2</span>")
l = hooks(l)
// will this line be considered for its spacing?
if (!l.startsWith("<h") && !l.startsWith("<b") && l.replace(/\n/g, "").length > 0) {
// fetch previous/next lines (default to empty string if not able to acquire)
let previousLine = (a[i - 1] || ""); let nextLine = (a[i + 1] || "")
// check if previous/next line is empty or contains a heading or blockquote
let pLineEmpty = previousLine.length == 0 || previousLine.startsWith("#") || previousLine.startsWith(">")
let nLineEmpty = nextLine.length == 0 || nextLine.startsWith("#") || nextLine.startsWith(">")
if (pLineEmpty && nLineEmpty) // 0 & 0
l = "<p>" + l + "</p>" // single lonely paragraph
else if (pLineEmpty && !nLineEmpty) // 0 & 1
l = "<p>" + l + "<br>" // start a paragraph with line breaks
else if (!pLineEmpty && nLineEmpty) // 1 & 0
l = l + "</p>" // end a paragraph with line breaks
else if (!pLineEmpty && !nLineEmpty) // 1 & 1
l += "<br>" // end a line in a paragraph with a line break
}
return l
}) // remove empty sections and join into one
.filter(x => x.length > 0).join("\n") + "\n"
// if file contains a code block and you _just_ aren't at the end of file
if (_c.length > 1 && i != _c.length - 1) output += "<pre>"
})
return output
}

View File

@ -11,7 +11,8 @@ else
echo "-> running generator script"
/usr/local/bin/qjs make.js
echo "-> copying files"
cp -ru out/gemini/* /var/gemini/content/
cp -ru out/www/* /var/www/
rsync --recursive --fsync --delete out/gemini/* /var/gemini/content/.
rsync --recursive --fsync --delete out/www/* /var/www/.
# TODO: update caddy config?
echo "-> done!"
fi

BIN
src/images/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
src/images/bg2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
src/images/sophia.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

View File

@ -1,5 +1,7 @@
{html_sbcont_start}
=> / 🏡 go home...
=> /wiki/ 🗃️ go back...
{html_sbcont_end}
# categories
=> /images/t/categories.png thumbnail

View File

@ -1,4 +1,6 @@
{html_sbcont_start}
=> / 🏡 go home...
{html_sbcont_end}
=> /wiki/category/ 📚 go back...
# {category_title}

11
src/templates/color.html Normal file
View File

@ -0,0 +1,11 @@
</p>
<div class="sidebar-content">
<span class="webring__title">color of the moment</span>
<div class="webring__wrapper" style="height: unset; padding: .6rem 0 0 1.2rem">
<iframe style="width: 50%; height: 31px;" src="https://zvava.org/api/hue/client" frameborder="0"></iframe>
</div>
<div class="webring__wrapper">
<a href="/wiki/hue-server.html">powered by hue-server</a> <span style="opacity: .5">((and decided by viewers like yourself!)</span>
</div>
</div>

View File

@ -9,4 +9,28 @@
<script src="/zvava.js"></script>
<title>{title}</title>
</head>
<body><div class="wrap">
<body><nav>
<div class="window fake" style="margin-right: -1px">
<div class="window-titlebar"><div class="window__title">sophia.jpg</div><div class="window-titlebar__maximize"></div><div class="window-titlebar__close"></div></div>
<div style="aspect-ratio: 1 / 1; background: url('/images/sophia.jpg') center / contain" title="sophia's old profile picture tinted green"></div>
</div>
<div class="sidebar-content">
<div class="sidebar__header">navigation</div>
<a class="sidebar__link" href="/">🏡 home</a>
<a class="sidebar__link" href="/wiki/">🗃️ wiki</a>
<a class="sidebar__link" href="/wiki/category/">📚 categories</a>
<div class="sidebar__header">pinned pages</div>
{html_wiki_pinned}
<div class="sidebar__header">recently updated</div>
{html_wiki_recent}
</div>
<div class="sidebar-bottom">
<div class="sidebar__header">a color <a class="nob4" href="/wiki/hue-server.html">&lt;?&gt;</a></div>
<iframe style="width: 100%; height: 31px; margin-bottom: -6px" src="https://zvava.org/api/hue/client" frameborder="0"></iframe>
<div class="sidebar__header">link my 88x31 &lt;3</div>
<a class="nob4" href="/images/buttons/zvava.org.png"><div style="height: 42px; background: url('/images/buttons/zvava.org.png') center"></div></a>
</div>
</nav><div class="wrap">

View File

@ -1,18 +1,13 @@
=> /images/header.png welcome to zvava.org ({build_info_summary})
hey, i'm sophia (she/her) but u can call me sophie ^-^!! you are currently looking at my brain-out-on-a-table, where i keep a constantly evolving collection of thoughts and projects — the first link takes you to the categories of the articles on here, then there are a few highlight pages. CYBERPUNK IS NOW wawawawa
hey, i'm sophia (she/her) but u can call me sophie ^-^!! you are currently looking at my brain-out-on-a-table, where i keep a constantly evolving collection of thoughts and projects — the first link takes you to the categories of the articles on here, then there are a handful of highlight pages. CYBERPUNK IS NOW squeeek i am just a little mouse squeak squeak wawawawa
{html_sbcont_start}
## navigation
=> /wiki/category/ 📚 categories
=> /wiki/about-sophie.ln 🏌️‍♀️ about me
=> /wiki/favorites.ln ⭐ favorites
=> /wiki/category/music.ln 🎶 music
=> /wiki/category/video.ln 📼 videos
=> /wiki/category/internet.ln 🌐 internet
=> /wiki/wren.ln 🐦‍⬛ wren
=> /wiki/config.ln ⚙️ config
=> /wiki/category/rainmeter.ln 💧 rainmeter
=> /wiki/about-site.ln ❓ about site
{wiki_pinned}
{html_sbcont_end}
{html_color}
## links
=> https://mk.catgirlsfor.science/@zvava 🔮 fediverse
@ -21,14 +16,13 @@ hey, i'm sophia (she/her) but u can call me sophie ^-^!! you are currently looki
=> https://keyoxide.org/5386D9E7ED1C49BEE15B9F32074FE2D02D45891C 🔏 keyoxide
=> https://www.buymeacoffee.com/zvavava 🍺 buy me a beer!
{gmi_webrings}
{html_webrings}
{html_sbcont_start}
## wiki
=> /wiki/ 🗃️ wiki
=> /wiki/category/ 📚 categories
=> /stats.ln 📊 statistics
{wiki_recent}
{html_sbcont_end}
## contact
```
@ -41,7 +35,9 @@ discord @zvava
## exit the wiki
=> /wiki/gemini-sites.ln 🪐 gemini directory
{br}
{gmi_webrings}
{html_webrings}
```
「 the internet is not a big truck

View File

@ -0,0 +1,9 @@
=> /wiki/about-sophie.ln 🏌️‍♀️ about me
=> /wiki/favorites.ln ⭐ favorites
=> /wiki/category/music.ln 🎶 music
=> /wiki/category/video.ln 📼 videos
=> /wiki/category/internet.ln 🌐 internet
=> /wiki/wren.ln 🐦‍⬛ wren
=> /wiki/config.ln ⚙️ config
=> /wiki/category/rainmeter.ln 💧 rainmeter
=> /wiki/about-site.ln ❓ about site

View File

@ -1,3 +1,4 @@
### webrings
hacker girls~
=> https://lunabee.space/ (prev) lunabee
=> https://nthia.dev/ (next) synthia

View File

@ -1,43 +1,25 @@
</p><!-- my own folly -->
<span class="webring__title">hacker girls</span>
<div class="webring webring__wrapper">
<a class="nob4" href="https://lunabee.space/">lunabee ⇐</a>
<span style="opacity: .5">soph</span>
<a class="nob4" href="https://nthia.dev/">⇒ synthia</a>
</div>
<div class="webring webring__wrapper">
</p>
<p style="margin: 1.15rem 0 1.75rem">
<span style="color: #c27ed5"></span> hackergirls webring<br>
<a style="margin-left: 3.5ch" class="nob4" href="https://lunabee.space/">lunabee ⇐</a> <span style="opacity: .5">soph</span> <a class="nob4" href="https://nthia.dev/">⇒ synthia</a><br>
<span style="margin: .5ch 0 1ch 3.5ch" class="webring">
<img src="/images/buttons/hackergirls.gif" alt="hackergirls' badge">
<img src="/images/buttons/hackergirls2.gif" alt="hackergirls' badge">
<img src="/images/buttons/hackergirls3.gif" alt="hackergirls' badge">
<span style="opacity: .5">hack the planet!!</span>
</div>
<p></p>
<span class="webring__title">merveilles</span>
<div class="webring webring__wrapper">
<a class="nob4 webring" href="https://webring.xxiivv.com/#icons" title="merveilles webring">
<svg width="235" height="211" viewBox="0 0 235 211"><title>webring</title><g stroke="currentColor" stroke-width="28" fill="none" fill-rule="evenodd" stroke-linecap="square"><path d="M70.038 166.32c16.569 28.698 53.264 38.53 81.962 21.962 28.698-16.569 38.53-53.264 21.962-81.961l-50-86.603"></path><path d="M173.962 164.68c16.568-28.698 6.736-65.393-21.962-81.962-28.698-16.569-65.393-6.736-81.962 21.961l-50 86.603"></path><path d="M121 75c-33.137 0-60 26.863-60 60s26.863 60 60 60h100"></path></g></svg>
<span>webrimg</span>
</a>
</div>
</span>
<span style="color: #c27ed5"></span> merveilles webring<br>
<a style="margin-left: 3.5ch" class="nob4 webring" href="https://webring.xxiivv.com/#icons" title="merveilles webring">
<svg width="235" height="211" viewBox="0 0 235 211"><title>webring</title><g stroke="currentColor" stroke-width="28" fill="none" fill-rule="evenodd" stroke-linecap="square"><path d="M70.038 166.32c16.569 28.698 53.264 38.53 81.962 21.962 28.698-16.569 38.53-53.264 21.962-81.961l-50-86.603"></path><path d="M173.962 164.68c16.568-28.698 6.736-65.393-21.962-81.962-28.698-16.569-65.393-6.736-81.962 21.961l-50 86.603"></path><path d="M121 75c-33.137 0-60 26.863-60 60s26.863 60 60 60h100"></path></g></svg>
<span>webrimg</span>
</a>
</p>
<style>
.webring__title {
display: inline-block;
border-bottom: 1px solid #777; }
.webring {
display: flex;
align-items: center;
gap: .5rem; }
.webring__wrapper {
height: 2rem;
margin-left: 1rem;
padding: .3rem 0 .3rem 1.2rem;
border-left: 1px solid #777; }
.webring svg { width: 1rem; height: 1rem; }
a.webring:hover svg * { stroke-width: 40px; }
</style>

View File

@ -1,10 +1,14 @@
{html_sbcont_start}
=> / 🏡 go home...
{html_sbcont_end}
# wiki
=> /images/t/wiki.png thumbnail
a collection of thoughts and projects i have accumulated over the years
{html_sbcont_start}
=> /wiki/category/ 📚 categories
{html_sbcont_end}
{html_filter}
{wiki_all}

View File

@ -1,4 +1,6 @@
{html_sbcont_start}
=> / 🏡 go home...
=> /wiki/ 🗃️ go back...
{html_sbcont_end}
{content}

View File

@ -6,14 +6,14 @@ modified 2024/03/15
category text
```
hey! this is sophie, the swamp witch android of charleville. im a """cyborg plant""" who only types in lowercase. i like to fuck around with computers, electronics, and creating/manipulating digital media. thoughtful simplicity fascinates me, and i love learning to make sense of the physical world through fundamentals, crafting (NOT engineering), and alchemy — botany is neat too but i'm not very good at it yet
understand life as a chore list, what a blessing this boredom is. you are a spiritual being having a human experience, go meditate next to the biggest alder tree you can find (or any blåhaj)
hey! this is your favorite mousegirl sophie, the swamp witch android of charleville, but also a """cyborg plant""". i can only can type in lowercase. i like to fuck around with computers, electronics, and creating/manipulating digital media. thoughtful simplicity fascinates me, and i love learning to make sense of the physical world through fundamentals, crafting (NOT engineering), and alchemy — botany is neat too but i'm not very good at it yet
=> /wiki/ 🧠 sophie's brian
=> /wiki/favorites.ln ⭐ favorites
=> /wiki/serial-number.ln 0x 737A6F6669E1
=> https://mk.catgirlsfor.science/ daily dose of sophie
=> https://mk.catgirlsfor.science/@zvava daily dose of sophie
squeek i'm just a little mouse squeak squeak
## bio
sophia is a young adult woman trying her best to survive through the arduous process of self-fulfillment, making only the cutest projects doing so — focusing on doing it in my own way, aiming for sophistication through simplicity, and with respect to the natural environment and all its organisms working together

View File

@ -0,0 +1,24 @@
# color server
```
created 2024/03/28
category software
```
hue-server is a little color server i wrote. it will advertise a color and you may change it at will. it also comes with a little web client!
=> https://zvava.org/api/hue/client zvava.org's current color!
additionally, it can connect to a ntfy.sh topic to advertise changes in real time, and it also supports http authentication
=> https://git.zvava.org/zvava/hue-server source code
24/04/25 — i have disabled authentication on zvava.org 👀
## usage
very simple!
```
$ curl -d "eebfff" https://zvava.org/api/hue
$ curl -d "#667557" https://zvava.org/api/hue
```
=> https://zvava.org/api/hue zvava.org's hue api endpoint

View File

@ -8,12 +8,14 @@ body {
padding: 0 1rem;
margin: 3rem auto 20vh;
background-color: black;
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" fill="dimgray" viewBox="0 0 31 31"><rect x="30" y="30" width="1" height="1"></rect></svg>');
background-image: url("/images/bg.png");
background-position: center;
color: #dddddd;
font-family: Ubuntu Mono, monospace;
line-height: 1.5rem;
font-size: 1.1rem; }
font-size: 1.1rem; }
nav { display: none; }
.wrap { background: rgba(0, 0, 0, .66); }
@ -60,7 +62,7 @@ pre, blockquote {
color: #aaa;
background: rgba(0, 0, 0, .5); }
a {
a:not(.sidebar__link) {
text-decoration: none;
color: #c27ed5;
background: rgba(0, 0, 0, .5);
@ -68,7 +70,7 @@ a {
font-family: Ubuntu Mono, monospace;
white-space: pre; }
a:not(.nob4)::before {
a:not(.sidebar__link):not(.nob4)::before {
content: "\21D2 "; }
a:focus, a:hover, a.focus {
@ -88,6 +90,137 @@ a.focus {
max-width: 100%; }
}
@media (min-width: 90ch) {
body {
max-width: unset !important;
margin-left: 20ch !important;
margin-right: 0 !important;
display: flex;
justify-content: center;
overflow-x: hidden; }
.wrap .sidebar-content { display: none; }
.wrap { max-width: 80ch; width: 80ch }
nav { display: flex; }
}
@media (min-width: 1300px) {
body { margin-left: 10ch; }
}
/* sidebar */
nav {
height: 100vh;
width: 20ch;
background-color: black;
background-image: url("/images/bg2.png");
animation: scrollbg 5s infinite linear;
border-right: 1.5px solid #313131;
flex-direction: column;
position: fixed;
left: 0;
top: 0;
z-index: 1; }
@keyframes scrollbg {
0% { background-position: 0px 0px; }
100% { background-position: 128px 128px; }
}
nav .sidebar-content {
border-bottom: 1px solid #313131;
padding-bottom: 2ch;
flex: 1;
overflow: hidden scroll;
scrollbar-width: thin; }
nav .sidebar-content::-webkit-scrollbar { width: 4px; }
nav .sidebar-content::-webkit-scrollbar-thumb { background: #ffffff; }
.sidebar__header { line-height: 1.7; }
.sidebar__header a { line-height: unset; background: transparent; }
.sidebar__header:has(a) { display: flex; justify-content: space-between; }
.sidebar-content .sidebar__header {
padding: 1ch 2ch;
color: #eebfff; }
.sidebar-bottom .sidebar__header {
padding: 0 1ch;
background: linear-gradient(to right, #422c55 0%, #0005 100%); }
.sidebar-content pre {
padding: .5ch 0 1ch;
background: unset;
font-weight: normal; }
.sidebar__link {
padding: 0 2ch;
display: block;
color: #ddd;
text-decoration: none;
position: relative;
overflow: hidden;
text-wrap: nowrap;
text-overflow: ellipsis; }
.sidebar__link::before {
content: "";
display: block;
width: 100%;
height: 3ch;
position: absolute;
top: 0;
left: 0;
opacity: 0;
background: linear-gradient(to bottom, transparent 0%, #c884ff55 50%, transparent 100%);
transition: opacity .1s;
z-index: -9; }
.sidebar__link:hover::before, .sidebar__link.focus::before { opacity: 1; }
/* windows */
.window {
border: 2px solid #5e126f; }
.window-titlebar {
display: flex;
align-items: center;
color: #eebfff;
font-size: .8em;
border-bottom: 2px solid #5e126f;
background: #18002c;
cursor: grab;
user-select: none; }
.window__title { padding: 0 .75ch; flex: 1; }
.window-titlebar__maximize, .window-titlebar__close {
margin-right: 3px;
width: 2.25ch; height: 2.25ch;
color: #5e126f;
background: #000000;
border: 1px solid currentColor;
cursor: pointer; }
.window-titlebar__maximize:hover, .window-titlebar__close:hover {
color: #c884ff; }
.window-titlebar__maximize {
width: 1.45ch; height: 1.45ch;
border-width: .66ch; border-style: double; }
.window-titlebar__close::before {
content: "\02E3";
font-size: 3ch;
padding-left: .15ch;
margin-top: -.15ch;
display: block; }
.window:not(.fake) { /* TODO */ }
/* lazy mode */
.lazy-mode {
@ -123,7 +256,8 @@ a.focus {
user-select: text; }
.lazy-mode img {
pointer-events: none }
box-sizing: content-box;
pointer-events: none; }
.lazy-mode a {
cursor: none;
@ -131,11 +265,14 @@ a.focus {
pointer-events: none;
transition: color .1s, text-shadow .3s; }
.lazy-mode a.focus::after {
.lazy-mode a:not(.sidebar__link):not(:has(img)).focus::after {
color: #777;
font-weight: normal;
content: " " attr(href); }
.lazy-mode a:has(img).focus img {
box-shadow: 0 0 .45rem #c884ff; }
.lazy-mode .cursor {
width: 18px;
height: 18px;