Dieser Commit ist enthalten in:
user0x42 2025-02-28 04:30:41 +01:00
Ursprung f9763d7cc4
Commit 3e5f846e8d
Signiert von: user0x42
SSH-Key-Fingerabdruck: SHA256:l/BokOyIeoKHPSk6adSWa2v9yD4TQV9QLRngyAHmoJ0
24 geänderte Dateien mit 6429 neuen und 1 gelöschten Zeilen

3
.gitmodules gevendort Normale Datei
Datei anzeigen

@ -0,0 +1,3 @@
[submodule "themes/ametrine"]
path = themes/ametrine
url = https://codeberg.org/daudix/ametrine.git

Datei anzeigen

@ -1,2 +1,10 @@
# home
# xsiz.eu
## Credits
- [Zola](https://getzola.org) static site engine
- [Duckquill](https://ametrine.daudix.one) great theme
## Disclaimer
The project itself is licensed under the [**MIT**](LICENSE) license, and the images and logos owned by [**XSIZ**](https://u42.dev) are licensed under the [**CC BY-NC 4.0**](https://creativecommons.org/licenses/by-nc/4.0/) license

169
config.toml Normale Datei
Datei anzeigen

@ -0,0 +1,169 @@
theme = "ametrine"
title = "XSIZ"
base_url = "https://xsiz.eu"
description = "german dudes"
compile_sass = true
minify_html = true
build_search_index = true
author = "XSIZ"
# Based on https://github.com/welpo/tabi
#
# To translate the entire theme, there must be a file with the same ISO 639-1
# or BCP 47 language code in the "i18n" directory of your site or the Ametrine
# theme, for example, "i18n/fr.toml" for French or "i18n/zh-Hans.toml" for
# Simplified Chinese, otherwise the theme will be in English.
#
# ISO 639-1: https://localizely.com/iso-639-1-list/
# BCP 47: https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
default_language = "en"
taxonomies = [
{ name = "tags", feed = true, paginate_by = 10 },
{ name = "categories", feed = true },
]
[markdown]
highlight_code = true
extra_syntaxes_and_themes = ["sublime"]
highlight_theme = "css"
highlight_themes_css = [
{ theme = "monokai-pro-dark", filename = "syntax-theme-dark.css" },
{ theme = "monokai-pro-light", filename = "syntax-theme-light.css" },
]
smart_punctuation = true
bottom_footnotes = true
[search]
# Accepted values are "elasticlunr_javascript" and "elasticlunr_json"
index_format = "elasticlunr_json"
[extra]
categories = [
{ name = "Archived", description = "Posts that have been archived due to their age.", color = "purple", icon = "box-arrow-down" },
{ name = "Featured", description = "Posts that are somewhat decently written.", color = "yellow", icon = "star" },
{ name = "Hot", description = "Posts that did numbers compared to others.", color = "red", icon = "fire" },
]
# Accent color used by Ametrine.
#
# Has a few modes:
# - Use one of the predefinded colors:
# accent_color = "<red, orange, yellow, green, blue, purple>"
# - Use a single color for both light and dark modes:
# accent_color = "hsl(270 50% 60%)"
# - Use separate colors for light and dark modes
# accent_color = ["hsl(270 50% 60%)", "hsl(27.567572 87% 67%)"]
# While using custom colors, make sure they are in the HSL color format.
accent_color = "hsl(26 100% 50%)"
# Additional CSS styles; expects them to be in the "./static/" directory.
# If you are using Sass these will be generated there automatically.
#
# styles = [
# "YOUR_STYLE.css",
# "ALSO_YOUR_STYLE.css"
# ]
#
# Additional JavaScript scripts; expects them to be in the "./static/"
# directory.
#
# scripts = [
# "YOUR_SCRIPT.js",
# "ALSO_YOUR_SCRIPT.js"
# ]
# Path to the page that is being considered "Home", e.g. for the "Go Home"
# button on 404.
# home_url = "@/home/index.md"
# URL to website's source code.
source_url = "https://code.xsiz.eu/xsiz/home"
# URL to website's issue tracker.
issues_url = "https://code.xsiz.eu/xsiz/home/issues"
# Whether to show copy button on all code blocks that have the language set.
# See https://www.getzola.org/documentation/content/syntax-highlighting/
show_copy_button = true
# Whether to show estimated read time in posts.
show_reading_time = true
# Whether to show a share button in article's quick actions.
# Powered by https://shareopenly.org.
show_share_button = true
# show_focus_button = true
show_backlinks = true
# Custom separator used throughout the theme.
# separator = "•"
# Custom separator used in website's title and article metadata.
# title_separator = "-"
date_format = "%d.%B.%Y"
date_format_long = "%d.%B.%Y, %R (%Z)"
date_locale = "en_IE"
timezone = "Europe/Berlin"
analytics = false
speed_insights = false
show_comments_qr = true
[extra.meta]
# Variables bellow accept either "true" or a filename.
# Favicon can also accept an emoji instead of a filename.
# File extension are being respected, so e.g. if favicon has .gif extension
# it'll be treated as an animated one.
# If set to "true" these will use the default filenames:
# favicon = favicon.png
# apple_touch_icon = apple-touch-icon.png
# card = card.png
favicon = true
apple_touch_icon = true
card = true
[extra.nav]
# When set to true and [extra.fediverse] is defined, it will use avatar from
# said Mastodon account, otherwise it'll fallback to apple_touch_icon specified
# in [extra.meta] or its default value.
# Also accepts arbitrary filenames and URLs for custom images.
icon = true
# Ignore the default behavior of using sidebar for navigation only when there's
# more than 5 items (links, categories) in it.
force_sidebar = true
# Icon is required to be set on each link if "force_sidebar" is set to true
# or there's more than 5 links.
# Any icon name from https://phosphoricons.com can be used.
links = [
{ name = "Random 🦄", category = [
{ url = "https://example.org", name = "Rob-boot", icon = "robot" },
{ url = "https://example.org", name = "Eggman", icon = "egg" },
{ url = "https://example.org", name = "Honse", icon = "horse" },
{ url = "https://example.org", name = "Lie", icon = "cake" },
] },
]
[extra.footer]
# Set to false to disable copyright text, or set to anything else to use it as
# the copyright text (Markdown supported).
# $YEAR will be replaced with current year.
copyright = "© XSIZ $YEAR"
show_powered_by = true
# show_fediring = true
show_last_updated = true
#[extra.fediverse]
# Fediverse integration.
# Used for:
# - Commenting
# - Custom emoji shortcode
# - Website verification
# - Author attribution
#
# Mastodon-powered commenting is based on
# https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/
#
# Values can be overridden in the front-matter, e.g.
# for multi-author blogs or guest posts.
#host = "wetdry.world"
#user = "daudix"
[extra.debug]
layout = false
no_styles = false
no_emojis = false

103
content/_index.md Normale Datei
Datei anzeigen

@ -0,0 +1,103 @@
+++
title = "Ametrine"
[extra]
no_header = true
+++
<img id="logo" class="transparent drop-shadow" src="logo.svg" alt="VTuber-style Ametrine logo.">
<div class="buttons centered big">
<a href="https://codeberg.org/daudix/ametrine">Repository →</a>
</div>
***
{% alert(caution=true) %}
Ametrine is currently in the pre-alpha state and SHOULD NOT be used in production, it is far from being ready.
{% end %}
[Ametrine](https://en.wikipedia.org/wiki/Ametrine) is a "one of a kind" [Zola](https://www.getzola.org) theme made specifically for personal websites and blogs. It provides good defaults and easy configuration, while being powerful on demand. Its design is unique and made with great care and attention to details, it changes from time to time, and the development pace is rather rapid.
Some of Ametrine's features:
- Handwritten CSS (Sass); no Tailwind, React, or anything like that.
- No essential JavaScript; pop-ups, sidebars, and such will work just fine without it.
- Relatively lightweight, weights under 512kB.
- Uses modern CSS.
- Includes Monokai Pro theme for syntax highlighting out of the box.
- Will make you regret using this theme.
## What Is This Again
This is a theme for the [Zola](https://www.getzola.org) static site generator; thingy that converts [Markdown](https://www.markdownguide.org) files (which is used by Reddit, Tumblr, Discord, any many others) into a fully functional websites. Zola cannot build websites without a set of templates and styles, and this theme is exactly that. Ametrine also provides some custom functionality that is not present in Zola, such as Mastodon-powered comments, various useful shortcodes for simplifying various tasks, and more.
You can learn more about Zola and its themes at <https://www.getzola.org>.
## Installation
{% alert(caution=true) %}
Ametrine is currently in the pre-alpha state and SHOULD NOT be used in production, it is far from being ready.
{% end %}
If you have Git set up in your project, add Ametrine as a submodule:
```bash
git submodule init
git submodule add https://codeberg.org/daudix/ametrine.git themes/ametrine
```
{% alert(important=true) %}
It is highly recommended to switch from the `main` branch to the latest release:
{% end %}
```bash
cd themes/ametrine
git checkout tags/v0.1.0 # There is currently no tagged release
```
Then, enable Ametrine in your `config.toml`:
```toml
theme = "ametrine"
```
To update Ametrine, simply switch to a new tag:
{% alert(important=true) %}
Check the changelog for all versions after the one you are using; there may be breaking changes that require manual involvement.
{% end %}
```bash
git submodule update --remote --merge
cd themes/ametrine
git checkout tags/v0.1.0
```
## Why It Looks the Way It Does
Personally, I'm sick of flat, sterile, dead UIs all over the place, and I've always liked skeuomorphism because it's fun, alive, and pleasant to look at. While it's not very feasible to make things look overly realistic, some edge highlights, nice shadows, and a vibrant palette make a big difference. The design system that Ametrine uses is made of slightly frosted colored acrylic, everything is rounded, but the edges are not so rounded, so the edge highlight is rather thin, you can think of it as Lego bricks, fun and nice to touch. The animations are very bouncy to raise the fun level even higher. Still, the balance between fun and not being annoying is maintained. Did I succeed with this premise? I don't know, you tell me :P
## To-Do
Right now Ametrine is not ready to be used in production and is in active development, here's a short roadmap of features that need to be added and things to be done before the initial release:
- [x] Make sure everything looks nice on mobile
- [x] Add i18n support
- [x] Ensure good compatibility with iOS 15, and perhaps lower
- [x] Remove leftover unused code from [Duckquill](https://duckquill.daudix.one)
- [ ] Support custom analytics scripts
- [ ] Add some utility classes
- [ ] Simplify and optimize some logic in the templates
- [ ] Organize Sass files
- [ ] Write good documentation
- [ ] Credit all the stuff that needs to be credited
<style>
#logo {
width: min(calc(var(--container-width) / 2), 100%);
}
#logo:hover {
transform: var(--hover) rotate(-5deg);
}
</style>

1
content/logo.svg Normale Datei

Dateidiff unterdrückt, weil mindestens eine Zeile zu lang ist

Nachher

Breite:  |  Höhe:  |  Größe: 43 KiB

BIN
static/404-static.gif Normale Datei

Binäre Datei nicht angezeigt.

Nachher

Breite:  |  Höhe:  |  Größe: 178 B

BIN
static/404.gif Normale Datei

Binäre Datei nicht angezeigt.

Nachher

Breite:  |  Höhe:  |  Größe: 715 B

BIN
static/apple-touch-icon.png Normale Datei

Binäre Datei nicht angezeigt.

Nachher

Breite:  |  Höhe:  |  Größe: 230 KiB

11
static/audio.js Normale Datei
Datei anzeigen

@ -0,0 +1,11 @@
var audioButton = document.querySelectorAll('.audio');
for (var i = 0; i < audioButton.length; i++) {
audioButton[i].addEventListener('click', function (event) {
playAudio(this.dataset.audio);
});
}
function playAudio(url) {
new Audio(url).play();
}

BIN
static/card.png Normale Datei

Binäre Datei nicht angezeigt.

Nachher

Breite:  |  Höhe:  |  Größe: 31 KiB

41
static/closable.js Normale Datei
Datei anzeigen

@ -0,0 +1,41 @@
const closable = document.querySelectorAll("details.closable");
closable.forEach((detail) => {
detail.addEventListener("toggle", () => {
if (detail.open) setTargetDetail(detail);
});
});
function setTargetDetail(targetDetail) {
closable.forEach((detail) => {
if (detail !== targetDetail) {
detail.open = false;
}
});
}
document.addEventListener("click", function (event) {
const isClickInsideDetail = [...closable].some((detail) =>
detail.contains(event.target)
);
if (!isClickInsideDetail) {
closable.forEach((detail) => {
detail.open = false;
});
}
});
const toggleNavbar = document.getElementById("toggle-navbar");
const siteNavbar = document.getElementById("site-navbar");
const mainContent = document.getElementById("main-content");
toggleNavbar.addEventListener("change", () => {
if (toggleNavbar.checked) {
toggleSidebar.checked = false;
}
});
mainContent.addEventListener("click", () => {
if (toggleNavbar.checked) toggleNavbar.checked = false;
});

420
static/comments.js Normale Datei
Datei anzeigen

@ -0,0 +1,420 @@
// Based on https://carlschwan.eu/2020/12/29/adding-comments-to-your-static-blog-with-mastodon/
// Attachment, card, and spoiler code is from https://github.com/cassidyjames/cassidyjames.github.io/blob/99782788a7e3ba3cc52d6803010873abd1b02b9e/_includes/comments.html#L251-L296
let lazyAsyncImage = document.getElementById("lazy-async-image").textContent;
let relAttributes = document.getElementById("rel-attributes").textContent;
let dateLocale = document.getElementById("date-locale").textContent;
let host = document.getElementById("host").textContent;
let user = document.getElementById("user").textContent;
let id = document.getElementById("id").textContent;
let articleAuthorText = document.getElementById("article-author-text").textContent;
let loadingText = document.getElementById("loading-text").textContent;
let noCommentsText = document.getElementById("no-comments-text").textContent;
let reloadText = document.getElementById("reload-text").textContent;
let sensitiveText = document.getElementById("sensitive-text").textContent;
document.getElementById("load-comments").addEventListener("click", loadComments);
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#x27;");
}
function emojify(input, emojis) {
let output = input;
emojis.forEach((emoji) => {
let picture = document.createElement("picture");
let source = document.createElement("source");
source.setAttribute("srcset", escapeHtml(emoji.url));
source.setAttribute("media", "(prefers-reduced-motion: no-preference)");
let img = document.createElement("img");
img.className = "emoji";
img.setAttribute("src", escapeHtml(emoji.static_url));
img.setAttribute("title", `:${emoji.shortcode}:`);
img.setAttribute("width", "24");
img.setAttribute("height", "24");
if (lazyAsyncImage == "true") {
img.setAttribute("decoding", "async");
img.setAttribute("loading", "lazy");
}
picture.appendChild(source);
picture.appendChild(img);
output = output.replace(`:${emoji.shortcode}:`, picture.outerHTML);
});
return output;
}
function loadComments() {
let commentsWrapper = document.getElementById("comments-wrapper");
commentsWrapper.innerHTML = "";
let loadCommentsButton = document.getElementById("load-comments");
loadCommentsButton.innerHTML = loadingText;
loadCommentsButton.disabled = true;
fetch(`https://${host}/api/v1/statuses/${id}/context`)
.then(function (response) {
return response.json();
})
.then(function (data) {
let descendants = data["descendants"];
if (
descendants &&
Array.isArray(descendants) &&
descendants.length > 0
) {
commentsWrapper.innerHTML = "";
descendants.forEach(function (status) {
console.log(descendants);
if (status.account.display_name.length > 0) {
status.account.display_name = escapeHtml(
status.account.display_name
);
status.account.display_name = emojify(
status.account.display_name,
status.account.emojis
);
} else {
status.account.display_name = status.account.username;
}
let instance = "";
if (status.account.acct.includes("@")) {
instance = status.account.acct.split("@")[1];
} else {
instance = host;
}
const isReply = status.in_reply_to_id !== id;
let op = false;
if (status.account.acct == user) {
op = true;
}
status.content = emojify(status.content, status.emojis);
let comment = document.createElement("article");
comment.id = `comment-${status.id}`;
comment.className = isReply ? "comment comment-reply" : "comment";
comment.setAttribute("itemprop", "comment");
comment.setAttribute("itemtype", "http://schema.org/Comment");
let avatarSource = document.createElement("source");
avatarSource.setAttribute(
"srcset",
escapeHtml(status.account.avatar)
);
avatarSource.setAttribute(
"media",
"(prefers-reduced-motion: no-preference)"
);
let avatarImg = document.createElement("img");
avatarImg.className = "avatar";
avatarImg.setAttribute(
"src",
escapeHtml(status.account.avatar_static)
);
avatarImg.setAttribute(
"alt",
`@${status.account.username}@${instance} avatar`
);
if (lazyAsyncImage == "true") {
avatarImg.setAttribute("decoding", "async");
avatarImg.setAttribute("loading", "lazy");
}
let avatarPicture = document.createElement("picture");
avatarPicture.appendChild(avatarSource);
avatarPicture.appendChild(avatarImg);
let avatar = document.createElement("a");
avatar.className = "avatar-link";
avatar.setAttribute("href", status.account.url);
avatar.setAttribute("rel", relAttributes);
avatar.appendChild(avatarPicture);
comment.appendChild(avatar);
let display = document.createElement("a");
display.className = "display";
display.setAttribute("href", status.account.url);
display.setAttribute("rel", relAttributes);
display.setAttribute("itemprop", "author");
display.setAttribute("itemtype", "http://schema.org/Person");
display.innerHTML = status.account.display_name;
let instanceBadge = document.createElement("span");
instanceBadge.className = "instance";
instanceBadge.textContent = `@${status.account.username}@${instance}`;
let permalink = document.createElement("a");
permalink.setAttribute("href", status.url);
permalink.setAttribute("itemprop", "url");
permalink.setAttribute("rel", relAttributes);
permalink.textContent = new Date(
status.created_at
).toLocaleString(dateLocale, {
dateStyle: "long",
timeStyle: "short",
});
let timestamp = document.createElement("time");
timestamp.setAttribute("datetime", status.created_at);
timestamp.classList.add("timestamp");
timestamp.appendChild(permalink);
permalink.classList.add("external");
let header = document.createElement("header");
header.appendChild(display);
header.appendChild(instanceBadge);
header.appendChild(timestamp);
comment.appendChild(header);
let main = document.createElement("main");
main.setAttribute("itemprop", "text");
if (status.sensitive == true || status.spoiler_text != "") {
let summary = document.createElement("summary");
if (status.spoiler_text == "") {
status.spoiler_text == sensitiveText;
}
summary.innerHTML = status.spoiler_text;
let spoiler = document.createElement("details");
spoiler.appendChild(summary);
spoiler.innerHTML += status.content;
main.appendChild(spoiler);
} else {
main.innerHTML = status.content;
}
comment.appendChild(main);
let attachments = status.media_attachments;
let SUPPORTED_MEDIA = ["image", "video", "gifv", "audio"];
let media = document.createElement("div");
media.className = "attachments";
if (
attachments &&
Array.isArray(attachments) &&
attachments.length > 0
) {
attachments.forEach((attachment) => {
if (SUPPORTED_MEDIA.includes(attachment.type)) {
let mediaElement;
switch (attachment.type) {
case "image":
mediaElement = document.createElement("img");
mediaElement.setAttribute("src", attachment.preview_url);
if (attachment.description != null) {
mediaElement.setAttribute("title", attachment.description);
}
if (lazyAsyncImage == "true") {
mediaElement.setAttribute("decoding", "async");
mediaElement.setAttribute("loading", "lazy");
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "video":
mediaElement = document.createElement("video");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("controls", "");
if (attachment.description != null) {
mediaElement.setAttribute("title", attachment.description);
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "gifv":
mediaElement = document.createElement("video");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("autoplay", "");
mediaElement.setAttribute("playsinline", "");
mediaElement.setAttribute("loop", "");
if (attachment.description != null) {
mediaElement.setAttribute("title", attachment.description);
}
if (status.sensitive == true) {
mediaElement.classList.add("spoiler");
}
media.appendChild(mediaElement);
break;
case "audio":
mediaElement = document.createElement("audio");
mediaElement.setAttribute("src", attachment.url);
mediaElement.setAttribute("controls", "");
if (attachment.description != null) {
mediaElement.setAttribute("title", attachment.description);
}
media.appendChild(mediaElement);
break;
}
let mediaLink = document.createElement("a");
mediaLink.setAttribute("href", attachment.url);
mediaLink.setAttribute("rel", relAttributes);
mediaLink.appendChild(mediaElement);
media.appendChild(mediaLink);
}
});
comment.appendChild(media);
}
let interactions = document.createElement("footer");
let boosts = document.createElement("a");
boosts.className = "boosts";
boosts.setAttribute("href", `${status.url}/reblogs`);
let boostsIcon = document.createElement("i");
boostsIcon.classList.add("ph-bold", "ph-repeat");
boosts.appendChild(boostsIcon);
boosts.insertAdjacentHTML('beforeend', ` ${status.reblogs_count}`);
interactions.appendChild(boosts);
let faves = document.createElement("a");
faves.className = "faves";
faves.setAttribute("href", `${status.url}/favourites`);
let favesIcon = document.createElement("i");
favesIcon.classList.add("ph-bold", "ph-star");
faves.appendChild(favesIcon);
faves.insertAdjacentHTML('beforeend', ` ${status.favourites_count}`);
interactions.appendChild(faves);
if (
status.reactions &&
Array.isArray(status.reactions) &&
status.reactions.length > 0
) {
let reactions = document.createElement("div");
reactions.classList.add("reactions", "overshoot-row");
status.reactions.forEach(reaction => {
let reactionElement = document.createElement("span");
reactionElement.className = "reaction";
if (reaction.url) {
// Custom emoji
let img = document.createElement("img");
img.className = "emoji";
img.setAttribute("src", escapeHtml(reaction.url));
img.setAttribute("title", `${reaction.name}`);
img.setAttribute("width", "24");
img.setAttribute("height", "24");
reactionElement.appendChild(img);
} else {
// Unicode emoji
let emoji = document.createElement("span");
emoji.textContent = reaction.name;
reactionElement.appendChild(emoji);
}
// Append the count
let count = document.createElement("span");
count.textContent = reaction.count;
reactionElement.appendChild(count);
reactions.appendChild(reactionElement);
});
interactions.appendChild(reactions);
}
comment.appendChild(interactions);
if (status.card != null) {
let cardFigure = document.createElement("figure");
if (status.card.image != null) {
let cardImg = document.createElement("img");
cardImg.setAttribute("src", status.card.image);
cardImg.classList.add("no-hover");
cardFigure.appendChild(cardImg);
}
let cardCaption = document.createElement("figcaption");
let cardTitle = document.createElement("strong");
cardTitle.innerHTML = status.card.title;
cardCaption.appendChild(cardTitle);
if (status.card.description != null && status.card.description.length > 0) {
let cardDescription = document.createElement("p");
cardDescription.innerHTML = status.card.description;
cardCaption.appendChild(cardDescription);
}
cardFigure.appendChild(cardCaption);
let card = document.createElement("a");
card.className = "card";
card.setAttribute("href", status.card.url);
card.setAttribute("rel", relAttributes);
card.appendChild(cardFigure);
comment.appendChild(card);
}
if (op === true) {
comment.classList.add("op");
instanceBadge.classList.add("op");
instanceBadge.setAttribute("title", articleAuthorText);
}
commentsWrapper.appendChild(comment);
});
}
else {
var statusText = document.createElement("p");
statusText.innerHTML = noCommentsText;
statusText.setAttribute("id", "comments-status");
commentsWrapper.appendChild(statusText);
}
loadCommentsButton.innerHTML = reloadText;
})
.catch(function (error) {
console.error('Error loading comments:', error);
})
.finally(function () {
loadCommentsButton.disabled = false;
});
}

66
static/copy-button.js Normale Datei
Datei anzeigen

@ -0,0 +1,66 @@
// Based on https://www.roboleary.net/2022/01/13/copy-code-to-clipboard-blog.html
document.addEventListener("DOMContentLoaded", function () {
let blocks = document.querySelectorAll("pre[class^='language-']");
blocks.forEach((block) => {
if (navigator.clipboard) {
// Code block header title
let title = document.createElement("span");
let lang = block.getAttribute("data-lang");
title.innerHTML = lang;
// Copy button icon
let iconCopy = document.createElement("i");
iconCopy.classList.add("ph-bold", "ph-copy");
let iconCheck = document.createElement("i");
iconCheck.classList.add("ph-bold", "ph-check-square-offset");
// Copy button
let button = document.createElement("button");
let copyCodeText = document.getElementById("copy-code-text").textContent;
button.setAttribute("title", copyCodeText);
button.appendChild(iconCopy);
button.appendChild(iconCheck);
// Code block header
let header = document.createElement("div");
header.classList.add("header");
if (block.classList.contains("z-code")) {
header.classList.add("z-code");
}
header.appendChild(title);
header.appendChild(button);
// Container that holds header and the code block itself
let container = document.createElement("div");
container.classList.add("pre-container");
container.appendChild(header);
// Move code block into the container
block.parentNode.insertBefore(container, block);
container.appendChild(block);
button.addEventListener("click", async () => {
await copyCode(block, header, button);
});
}
});
async function copyCode(block, header, button) {
let code = block.querySelector("code");
let text = code.innerText;
await navigator.clipboard.writeText(text);
header.classList.add("active");
button.setAttribute("disabled", true);
header.addEventListener("animationend", () => {
header.classList.remove("active");
button.removeAttribute("disabled");
}, { once: true });
}
});

BIN
static/favicon.png Normale Datei

Binäre Datei nicht angezeigt.

Nachher

Breite:  |  Höhe:  |  Größe: 817 B

BIN
static/fonts/geist-mono.woff2 Normale Datei

Binäre Datei nicht angezeigt.

BIN
static/fonts/geist.woff2 Normale Datei

Binäre Datei nicht angezeigt.

Binäre Datei nicht angezeigt.

4623
static/phosphor-bold.css Normale Datei

Datei-Diff unterdrückt, da er zu groß ist Diff laden

236
static/search.js Normale Datei
Datei anzeigen

@ -0,0 +1,236 @@
// Based on https://github.com/cydave/zola-theme-papermod/blob/fab7cd04833f0c78264b433a4fb1f4b999ef0399/static/js/search.js
// Debounce function definition
function debounce(func, wait, immediate) {
var timeout;
return function () {
var context = this, args = arguments;
var later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
// Taken from mdbook
// The strategy is as follows:
// First, assign a value to each word in the document:
// Words that correspond to search terms (stemmer aware): 40
// Normal words: 2
// First word in a sentence: 8
// Then use a sliding window with a constant number of words and count the
// sum of the values of the words within the window. Then use the window that got the
// maximum sum. If there are multiple maximas, then get the last one.
// Enclose the terms in <b>.
function makeTeaser(body, terms) {
var TERM_WEIGHT = 40;
var NORMAL_WORD_WEIGHT = 2;
var FIRST_WORD_WEIGHT = 8;
var TEASER_MAX_WORDS = 30;
var stemmedTerms = terms.map(function (w) {
return elasticlunr.stemmer(w.toLowerCase());
});
var termFound = false;
var index = 0;
var weighted = []; // contains elements of ["word", weight, index_in_document]
// split in sentences, then words
var sentences = body.toLowerCase().split(". ");
for (var i in sentences) {
var words = sentences[i].split(" ");
var value = FIRST_WORD_WEIGHT;
for (var j in words) {
var word = words[j];
if (word.length > 0) {
for (var k in stemmedTerms) {
if (elasticlunr.stemmer(word).startsWith(stemmedTerms[k])) {
value = TERM_WEIGHT;
termFound = true;
}
}
weighted.push([word, value, index]);
value = NORMAL_WORD_WEIGHT;
}
index += word.length;
index += 1; // ' ' or '.' if last word in sentence
}
index += 1; // because we split at a two-char boundary '. '
}
if (weighted.length === 0) {
return body;
}
var windowWeights = [];
var windowSize = Math.min(weighted.length, TEASER_MAX_WORDS);
// We add a window with all the weights first
var curSum = 0;
for (var i = 0; i < windowSize; i++) {
curSum += weighted[i][1];
}
windowWeights.push(curSum);
for (var i = 0; i < weighted.length - windowSize; i++) {
curSum -= weighted[i][1];
curSum += weighted[i + windowSize][1];
windowWeights.push(curSum);
}
// If we didn't find the term, just pick the first window
var maxSumIndex = 0;
if (termFound) {
var maxFound = 0;
// backwards
for (var i = windowWeights.length - 1; i >= 0; i--) {
if (windowWeights[i] > maxFound) {
maxFound = windowWeights[i];
maxSumIndex = i;
}
}
}
var teaser = [];
var startIndex = weighted[maxSumIndex][2];
for (var i = maxSumIndex; i < maxSumIndex + windowSize; i++) {
var word = weighted[i];
if (startIndex < word[2]) {
// missing text from index to start of `word`
teaser.push(body.substring(startIndex, word[2]));
startIndex = word[2];
}
// add <strong> around search terms
if (word[1] === TERM_WEIGHT) {
teaser.push("<strong>");
}
startIndex = word[2] + word[0].length;
teaser.push(body.substring(word[2], startIndex));
if (word[1] === TERM_WEIGHT) {
teaser.push("</strong>");
}
}
teaser.push("…");
return teaser.join("");
}
function formatSearchResultItem(item, terms) {
// Adjust this to match your desired result item structure
return '<div class="item">'
+ `<a href="${item.ref}">${item.doc.title}</a>`
+ `<span>${makeTeaser(item.doc.body, terms)}</span>`
+ '</div>';
}
function initSearch() {
var searchModal = document.getElementById("search-modal"); // Full-screen modal
var searchModalContent = document.getElementById("search-modal-content"); // Actual modal box
var searchInput = document.getElementById("search-input"); // Search input
var searchResults = document.getElementById("search-results"); // Search results
var searchButton = document.getElementById("search"); // Search button
var MAX_ITEMS = 10;
var options = {
bool: "AND",
fields: {
title: { boost: 2 },
body: { boost: 1 },
}
};
var currentTerm = "";
var index;
var initIndex = async function () {
if (index === undefined) {
if (typeof window.searchIndex !== "undefined") {
index = elasticlunr.Index.load(window.searchIndex);
} else {
let response = await fetch(`/search_index.${document.documentElement.lang}.json`);
index = elasticlunr.Index.load(await response.json());
}
}
return index;
};
// Open search modal when clicking the search button
if (searchButton) {
searchButton.addEventListener("click", function () {
searchModal.classList.add("active");
searchModal.addEventListener("transitionend", function handler() {
searchInput.focus();
searchModal.removeEventListener("transitionend", handler);
}, { once: true });
});
}
// Open search modal on "/" key press
window.addEventListener("keydown", (event) => {
if (event.key === "/" && document.activeElement.tagName !== "INPUT" && document.activeElement.tagName !== "TEXTAREA") {
event.preventDefault();
searchModal.classList.add("active");
searchModal.addEventListener("transitionend", function handler() {
searchInput.focus();
searchModal.removeEventListener("transitionend", handler);
}, { once: true });
}
});
// Close search modal on Escape key
window.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
searchModal.classList.remove("active");
}
});
// Close search modal when clicking outside search-modal-content
searchModal.addEventListener("click", function (e) {
if (!searchModalContent.contains(e.target)) {
searchModal.classList.remove("active");
}
});
// Prevent clicks inside modal content from closing it
searchModalContent.addEventListener("click", function (e) {
e.stopPropagation(); // Stops event from reaching searchModal click handler
});
// Search input event
searchInput.addEventListener("keyup", debounce(async function () {
var term = searchInput.value.trim();
if (term === currentTerm) return;
searchResults.style.display = term === "" ? "none" : "flex";
searchResults.innerHTML = ""; // Clear previous results
currentTerm = term;
if (term === "") return;
var results = (await initIndex()).search(term, options);
if (results.length === 0) {
searchResults.style.display = "none";
return;
}
// Insert formatted search result items
for (var i = 0; i < Math.min(results.length, MAX_ITEMS); i++) {
searchResults.innerHTML += formatSearchResultItem(results[i], term.split(" "));
}
}, 150));
}
if (document.readyState === "complete" ||
(document.readyState !== "loading" && !document.documentElement.doScroll)
) {
initSearch();
} else {
document.addEventListener("DOMContentLoaded", initSearch);
}

78
static/syntax-theme-dark.css Normale Datei
Datei anzeigen

@ -0,0 +1,78 @@
/*
* theme "Monokai Pro" generated by syntect
*/
.z-code {
color: #fcfcfa;
background-color: #2d2a2e;
}
.z-comment {
color: #727072;
}
.z-string {
color: #ffd866;
}
.z-constant.z-numeric {
color: #ab9df2;
}
.z-constant.z-language {
color: #ab9df2;
}
.z-constant.z-character, .z-constant.z-other {
color: #ab9df2;
}
.z-variable {
}
.z-keyword {
color: #ff6188;
}
.z-storage {
color: #ff6188;
}
.z-storage.z-type {
color: #78dce8;
font-style: italic;
}
.z-entity.z-name.z-class {
color: #a9dc76;
text-decoration: underline;
}
.z-entity.z-other.z-inherited-class {
color: #a9dc76;
text-decoration: underline;
font-style: italic;
}
.z-entity.z-name.z-function {
color: #a9dc76;
}
.z-variable.z-parameter {
color: #fc9867;
font-style: italic;
}
.z-entity.z-name.z-tag {
color: #ff6188;
}
.z-entity.z-other.z-attribute-name {
color: #a9dc76;
font-style: italic;
}
.z-support.z-function {
color: #78dce8;
}
.z-support.z-constant {
color: #78dce8;
}
.z-support.z-type, .z-support.z-class {
color: #fcfcfa;
}
.z-support.z-other.z-variable {
}
.z-invalid {
color: #fcfcfa;
background-color: #ff6188;
}
.z-invalid.z-deprecated {
color: #fcfcfa;
background-color: #ae81ff;
}

Datei anzeigen

@ -0,0 +1,78 @@
/*
* theme "Monokai Pro Light" generated by syntect
*/
.z-code {
color: #29242a;
background-color: #faf4f2;
}
.z-comment {
color: #918c8e;
}
.z-string {
color: #cc7a0a;
}
.z-constant.z-numeric {
color: #7058be;
}
.z-constant.z-language {
color: #7058be;
}
.z-constant.z-character, .z-constant.z-other {
color: #7058be;
}
.z-variable {
}
.z-keyword {
color: #e14775;
}
.z-storage {
color: #e14775;
}
.z-storage.z-type {
color: #1c8ca8;
font-style: italic;
}
.z-entity.z-name.z-class {
color: #269d69;
text-decoration: underline;
}
.z-entity.z-other.z-inherited-class {
color: #269d69;
text-decoration: underline;
font-style: italic;
}
.z-entity.z-name.z-function {
color: #269d69;
}
.z-variable.z-parameter {
color: #e16032;
font-style: italic;
}
.z-entity.z-name.z-tag {
color: #e14775;
}
.z-entity.z-other.z-attribute-name {
color: #269d69;
font-style: italic;
}
.z-support.z-function {
color: #1c8ca8;
}
.z-support.z-constant {
color: #1c8ca8;
}
.z-support.z-type, .z-support.z-class {
color: #29242a;
}
.z-support.z-other.z-variable {
}
.z-invalid {
color: #fcfcfa;
background-color: #ff6188;
}
.z-invalid.z-deprecated {
color: #fcfcfa;
background-color: #7058be;
}

Datei anzeigen

@ -0,0 +1,295 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!-- Generated by: TmTheme-Editor -->
<!-- ============================================ -->
<!-- app: http://tmtheme-editor.herokuapp.com -->
<!-- code: https://github.com/aziz/tmTheme-Editor -->
<plist version="1.0">
<dict>
<key>name</key>
<string>Monokai Pro</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#2D2A2E</string>
<key>caret</key>
<string>#FCFCFA</string>
<key>foreground</key>
<string>#FCFCFA</string>
<key>invisibles</key>
<string>#5B595C</string>
<key>lineHighlight</key>
<string>#403E41</string>
<key>selection</key>
<string>#403E41</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#727072</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#FFD866</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#AB9DF2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Built-in constant</string>
<key>scope</key>
<string>constant.language</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#AB9DF2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>User-defined constant</string>
<key>scope</key>
<string>constant.character, constant.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#AB9DF2</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#FF6188</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage</string>
<key>scope</key>
<string>storage</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#FF6188</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage type</string>
<key>scope</key>
<string>storage.type</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#78DCE8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Class name</string>
<key>scope</key>
<string>entity.name.class</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>underline</string>
<key>foreground</key>
<string>#A9DC76</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Inherited class</string>
<key>scope</key>
<string>entity.other.inherited-class</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic underline</string>
<key>foreground</key>
<string>#A9DC76</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function name</string>
<key>scope</key>
<string>entity.name.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#A9DC76</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function argument</string>
<key>scope</key>
<string>variable.parameter</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#FC9867</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag name</string>
<key>scope</key>
<string>entity.name.tag</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#FF6188</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag attribute</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string> italic</string>
<key>foreground</key>
<string>#A9DC76</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library function</string>
<key>scope</key>
<string>support.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#78DCE8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library constant</string>
<key>scope</key>
<string>support.constant</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#78DCE8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library class&#x2f;type</string>
<key>scope</key>
<string>support.type, support.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#FCFCFA</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library variable</string>
<key>scope</key>
<string>support.other.variable</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid</string>
<key>scope</key>
<string>invalid</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#FF6188</string>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#FCFCFA</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid deprecated</string>
<key>scope</key>
<string>invalid.deprecated</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#AE81FF</string>
<key>foreground</key>
<string>#FCFCFA</string>
</dict>
</dict>
</array>
<key>uuid</key>
<string>D8D5E82E-3D5B-46B5-B38E-8C841C21347D</string>
<key>colorSpaceName</key>
<string>sRGB</string>
<key>semanticClass</key>
<string>theme.dark.monokai</string>
</dict>
</plist>

Datei anzeigen

@ -0,0 +1,295 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!-- Generated by: TmTheme-Editor -->
<!-- ============================================ -->
<!-- app: http://tmtheme-editor.herokuapp.com -->
<!-- code: https://github.com/aziz/tmTheme-Editor -->
<plist version="1.0">
<dict>
<key>name</key>
<string>Monokai Pro Light</string>
<key>settings</key>
<array>
<dict>
<key>settings</key>
<dict>
<key>background</key>
<string>#FAF4F2</string>
<key>caret</key>
<string>#29242A</string>
<key>foreground</key>
<string>#29242A</string>
<key>invisibles</key>
<string>#918C8E</string>
<key>lineHighlight</key>
<string>#29242A0D</string>
<key>selection</key>
<string>#29242A0D</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Comment</string>
<key>scope</key>
<string>comment</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#918C8E</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>String</string>
<key>scope</key>
<string>string</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#CC7A0A</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Number</string>
<key>scope</key>
<string>constant.numeric</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#7058BE</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Built-in constant</string>
<key>scope</key>
<string>constant.language</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#7058BE</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>User-defined constant</string>
<key>scope</key>
<string>constant.character, constant.other</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#7058BE</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Variable</string>
<key>scope</key>
<string>variable</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Keyword</string>
<key>scope</key>
<string>keyword</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#E14775</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage</string>
<key>scope</key>
<string>storage</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#E14775</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Storage type</string>
<key>scope</key>
<string>storage.type</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#1C8CA8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Class name</string>
<key>scope</key>
<string>entity.name.class</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>underline</string>
<key>foreground</key>
<string>#269D69</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Inherited class</string>
<key>scope</key>
<string>entity.other.inherited-class</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic underline</string>
<key>foreground</key>
<string>#269D69</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function name</string>
<key>scope</key>
<string>entity.name.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#269D69</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Function argument</string>
<key>scope</key>
<string>variable.parameter</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string>italic</string>
<key>foreground</key>
<string>#E16032</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag name</string>
<key>scope</key>
<string>entity.name.tag</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#E14775</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Tag attribute</string>
<key>scope</key>
<string>entity.other.attribute-name</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string> italic</string>
<key>foreground</key>
<string>#269D69</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library function</string>
<key>scope</key>
<string>support.function</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#1C8CA8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library constant</string>
<key>scope</key>
<string>support.constant</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#1C8CA8</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library class&#x2f;type</string>
<key>scope</key>
<string>support.type, support.class</string>
<key>settings</key>
<dict>
<key>foreground</key>
<string>#29242A</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Library variable</string>
<key>scope</key>
<string>support.other.variable</string>
<key>settings</key>
<dict>
<key>fontStyle</key>
<string></string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid</string>
<key>scope</key>
<string>invalid</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#FF6188</string>
<key>fontStyle</key>
<string></string>
<key>foreground</key>
<string>#FCFCFA</string>
</dict>
</dict>
<dict>
<key>name</key>
<string>Invalid deprecated</string>
<key>scope</key>
<string>invalid.deprecated</string>
<key>settings</key>
<dict>
<key>background</key>
<string>#7058BE</string>
<key>foreground</key>
<string>#FCFCFA</string>
</dict>
</dict>
</array>
<key>uuid</key>
<string>D8D5E82E-3D5B-46B5-B38E-8C841C21347D</string>
<key>colorSpaceName</key>
<string>sRGB</string>
<key>semanticClass</key>
<string>theme.dark.monokai_pro</string>
</dict>
</plist>

1
themes/ametrine Submodul

@ -0,0 +1 @@
Subproject commit ea8c9710c4596b39de8e01ee1697cd042c009bdf