diff options
Diffstat (limited to '')
96 files changed, 5391 insertions, 112 deletions
diff --git a/.gitignore b/.gitignore index 9a899cc..aab76ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .direnv /result +__pycache__ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..3ffb2b3 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "b-peetz.de/themes/serene"] + path = b-peetz.de/themes/serene + url = https://github.com/isunjn/serene.git + branch = latest diff --git a/flake.lock b/flake.lock index 2d03a28..5b636e4 100644 --- a/flake.lock +++ b/flake.lock @@ -2,11 +2,11 @@ "nodes": { "crane": { "locked": { - "lastModified": 1738652123, - "narHash": "sha256-zdZek5FXK/k95J0vnLF0AMnYuZl4AjARq83blKuJBYY=", + "lastModified": 1745454774, + "narHash": "sha256-oLvmxOnsEKGtwczxp/CwhrfmQUG2ym24OMWowcoRhH8=", "owner": "ipetkov", "repo": "crane", - "rev": "c7e015a5fcefb070778c7d91734768680188a9cd", + "rev": "efd36682371678e2b6da3f108fdb5c613b3ec598", "type": "github" }, "original": { @@ -199,11 +199,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1738546358, - "narHash": "sha256-nLivjIygCiqLp5QcL7l56Tca/elVqM9FG1hGd9ZSsrg=", + "lastModified": 1745391562, + "narHash": "sha256-sPwcCYuiEopaafePqlG826tBhctuJsLx/mhKKM5Fmjo=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c6e957d81b96751a3d5967a0fd73694f303cc914", + "rev": "8a2f738d9d1f1d986b5a4cd2fd2061a7127237d7", "type": "github" }, "original": { @@ -235,11 +235,11 @@ ] }, "locked": { - "lastModified": 1738635966, - "narHash": "sha256-5MbJhh6nz7tx8FYVOJ0+ixMaEn0ibGzV/hScPMmqVTE=", + "lastModified": 1745548521, + "narHash": "sha256-xyliq8oS5OnzXjHRGr92RtmrtYI/dflf2gSEo0wMFjc=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "1ff8663cd75a11e61f8046c62f4dbb05d1907b44", + "rev": "eb0afb4ac0720d55c29e88eb29432103d73ae11d", "type": "github" }, "original": { @@ -270,11 +270,11 @@ ] }, "locked": { - "lastModified": 1738680491, - "narHash": "sha256-8X7tR3kFGkE7WEF5EXVkt4apgaN85oHZdoTGutCFs6I=", + "lastModified": 1744961264, + "narHash": "sha256-aRmUh0AMwcbdjJHnytg1e5h5ECcaWtIFQa6d9gI85AI=", "owner": "numtide", "repo": "treefmt-nix", - "rev": "64dbb922d51a42c0ced6a7668ca008dded61c483", + "rev": "8d404a69efe76146368885110f29a2ca3700bee6", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 34c0ef8..6bc34fc 100644 --- a/flake.nix +++ b/flake.nix @@ -7,9 +7,7 @@ crane = { url = "github:ipetkov/crane"; - inputs = { - nixpkgs.follows = "nixpkgs"; - }; + inputs = {}; }; rust-overlay = { url = "github:oxalica/rust-overlay"; @@ -98,33 +96,28 @@ system: let pkgs = nixpkgs.legacyPackages.${system}; treefmtEval = import ./treefmt.nix {inherit treefmt-nix pkgs;}; - build = pkgs.stdenv.mkDerivation { - pname = "b-peetz.de"; - version = "v1.0"; + + build = pkgs.callPackage ./package.nix {inherit essay facharbeit;}; + check = pkgs.stdenv.mkDerivation { + # Add the source name, to force this to rerun. + name = "zola-check-${./src}"; src = ./src; + # Allow network access for link checking + outputHash = "sha256-d6xi4mKdjkX2JFicDIv5niSzpyI0m/Hnm8GGAIU04kY="; + outputHashMode = "recursive"; + # Run local preferLocalBuild = true; allowSubstitutes = false; - nativeBuildInputs = []; - buildPhase = '' - mkdir --parents ./dead-trees{,/chemistry,/philosophy} - - install -D ${essay.outputs.packages."${system}".default}/philosophy/kant_and_free_software.pdf ./writings/philosophy/kant_and_free_software.pdf - # NOTE: This link is for backward compatibility, as I have given out links with that url <2024-07-13> - ln --symbolic --relative ./writings/philosophy/kant_and_free_software.pdf ./dead-trees/kant_and_free_software.pdf - ln --symbolic --relative ./writings/philosophy/kant_and_free_software.pdf ./dead-trees/philosophy/kant_and_free_software.pdf + nativeBuildInputs = [pkgs.zola pkgs.findutils]; - install -D ${facharbeit.outputs.packages."${system}".default}/chemistry/facharbeit.pdf ./writings/chemistry/facharbeit.pdf - # NOTE: This link is for backward compatibility, as I have given out links with that url <2024-07-13> - ln --symbolic --relative ./writings/chemistry/facharbeit.pdf ./dead-trees/raman_spectrometer.pdf - ln --symbolic --relative ./writings/chemistry/facharbeit.pdf ./dead-trees/chemistry/facharbeit.pdf - ''; - installPhase = '' - install -d $out/ - cp --recursive . $out/ + buildPhase = '' + zola check ''; + + installPhase = "touch $out"; }; in { packages = { @@ -132,7 +125,7 @@ }; checks = { - inherit build; + inherit build check; formatting = treefmtEval.config.build.check self; }; @@ -140,7 +133,10 @@ devShells = { default = pkgs.mkShell { - packages = []; + packages = [ + pkgs.zola + pkgs.python3 + ]; }; }; } diff --git a/package.nix b/package.nix new file mode 100644 index 0000000..be7a10e --- /dev/null +++ b/package.nix @@ -0,0 +1,78 @@ +{ + stdenv, + system, + # Deps + zola, + fd, + # Data + facharbeit, + essay, +}: +stdenv.mkDerivation { + pname = "b-peetz.de"; + version = "v1.0"; + src = ./src; + + # Run local + preferLocalBuild = true; + allowSubstitutes = false; + + nativeBuildInputs = [ + zola + fd + ]; + + buildPhase = let + installWriting = name: source: + # bash + '' + pdf_path="./writings/${name}" + rm --recursive "$pdf_path/" + + install -D "${source}" "$pdf_path" + fd . --type file --exec sed --in-place "s|$pdf_path/|$pdf_path|g" + + export base="$pdf_path" + ''; + in + # bash + '' + zola build + + cd ./public || exit 1 + + # Remove some useless stuff + rm --recursive ./categories + + mkdir --parents ./dead-trees{,/chemistry,/philosophy} + + { + ${installWriting "kant-and-free-software.pdf" "${essay.outputs.packages."${system}".default}/philosophy/kant_and_free_software.pdf"} + + # NOTE: This link is for backward compatibility, as I have given out links with that url <2024-07-13> + ln --symbolic --relative "$base" ./dead-trees/kant_and_free_software.pdf + ln --symbolic --relative "$base" ./dead-trees/philosophy/kant_and_free_software.pdf + } + + { + ${installWriting "raman-spectroscopy.pdf" "${facharbeit.outputs.packages."${system}".default}/chemistry/facharbeit.pdf"} + + # NOTE: This link is for backward compatibility, as I have given out links with that url <2024-07-13> + ln --symbolic --relative "$base" ./dead-trees/raman_spectrometer.pdf + ln --symbolic --relative "$base" ./dead-trees/chemistry/facharbeit.pdf + } + ''; + + installPhase = '' + install -d $out/ + cp --recursive . $out/ + + # # Make the website relative (this allows serving it from the ./result via python for + # # debugging.) + # fd . $out --type file --exec sed --in-place 's|https://b-peetz.de/|/|g' + # fd . $out --type file --exec sed --in-place 's|https://b-peetz.de/|/|g' + # + # fd . $out --type file --exec sed --in-place 's|https://b-peetz.de|/|g' + # fd . $out --type file --exec sed --in-place 's|https://b-peetz.de|/|g' + ''; +} diff --git a/server.py b/server.py new file mode 100755 index 0000000..9e81820 --- /dev/null +++ b/server.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python + +import http.server +import os + + +class NoCacheHTTPRequestHandler(http.server.SimpleHTTPRequestHandler): + def end_headers(self): + self.send_header('Cache-Control', 'no-cache, no-store, must-revalidate') # HTTP 1.1 + self.send_header('Pragma', 'no-cache') # HTTP 1.0 + self.send_header('Expires', '0') # Proxies + super().end_headers() + + +def run(server_class=http.server.HTTPServer, port=8080, directory="./result"): + # Set the directory for the handler + handler_class = NoCacheHTTPRequestHandler + handler_class.directory = os.path.abspath(directory) + + # Change the current working directory to the specified directory + os.chdir(os.path.abspath(directory)) + + # Create the server on 127.0.0.1 + with server_class(("127.0.0.1", port), handler_class) as httpd: + print( + f"Serving on http://127.0.0.1:{port} from directory '{handler_class.directory}'" + ) + httpd.serve_forever() + + +if __name__ == "__main__": + run(port=8080, directory="./result") diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..c75eecc --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +/public diff --git a/src/config.toml b/src/config.toml new file mode 100644 index 0000000..cabb9a9 --- /dev/null +++ b/src/config.toml @@ -0,0 +1,68 @@ +# serene v5.2.1 +# +# - docs: https://github.com/isunjn/serene/blob/latest/USAGE.md +# - check for updates: https://github.com/isunjn/serene/releases +# +#========================================================================================= + +base_url = "https://b-peetz.de" +title = "Benedikt Peetz" +description = "My website" +default_language = "en" +theme = "serene" +output_dir = "public" +compile_sass = true +minify_html = false # Keep this false, as it may cause issues with some styles +build_search_index = false # Keep this false, search is temporarily unsupported +generate_feeds = false # Whether to generate a feed file in root, read docs for more info about rss feed +feed_filenames = ["feed.xml"] # "feed.xml" | "atom.xml" | "rss.xml", read docs for more info + +taxonomies = [ + { name = "science", feed = true}, + { name = "tags" }, + { name = "categories" } +] + +[markdown] +highlight_code = true +highlight_theme = "css" +extra_syntaxes_and_themes = ["highlight_themes"] +highlight_themes_css = [ + { theme = "serene-light", filename = "hl-light.css" }, + { theme = "serene-dark", filename = "hl-dark.css" }, +] +render_emoji = false +external_links_target_blank = false +external_links_no_follow = true +external_links_no_referrer = true +smart_punctuation = false + +[slugify] +paths = "on" +taxonomies = "on" +anchors = "on" + +#========================================================================================= + +[extra] + +sections = [ + { name = "blog", path = "/blog", is_external = false }, + { name = "writings", path = "/writings", is_external = false }, + { name = "contact", path = "/contact", is_external = false }, + { name = "git", path = "https://git.foss-syndicate.org/bpeetz", is_external = true }, +] +blog_section_path = "/blog" + +back_link_text = "Back" # Text of the back button +force_theme = "dark" # false | "light" | "dark" + +footer_copyright = "© 2025 Benedikt Peetz" +footer_credits = true # Whether to show "Built with zola and serene" in footer + +not_found_error_text = "404 Not Found" +not_found_recover_text = "« back to home »" + +reaction = false # Whether to enable anonymous emoji reactions (Note: You need to set up a working api endpoint to enable this feature) +reaction_align = "right" # "left" | "center" | "right" +reaction_endpoint = "https://example.com/api/reaction" diff --git a/src/content/_index.md b/src/content/_index.md new file mode 100644 index 0000000..2916843 --- /dev/null +++ b/src/content/_index.md @@ -0,0 +1,27 @@ ++++ +template = 'home.html' + +[extra] +lang = 'en' + +# Show footer in home page +footer = true + +# If you don't want to display id/bio/avatar, simply comment out that line +name = "Benedikt Peetz" +id = "bpeetz" +bio = "he/him" +# avatar = "img/avatar.webp" +links = [ + { name = "Git", icon = "git-white", url = "https://git.foss-syndicate.org/bpeetz" }, + { name = "Email", icon = "email", url = "mailto:benedikt.peetz@b-peetz.de" }, + { name = "Matrix", icon = "matrix-white", url = "https://matrix.to/#/@soispha:matrix.vhack.eu" }, + { name = "Signal", icon = "signal-white", url = "https://signal.me/#eu/r03RJhrEynGMYVMhhpGuirRRMJYm_Yjgys3gfQPayVlQmb0Bv25Pt4_HT9QYpa7w" }, +] + +# Show a few recent posts in home page +recent = true +recent_max = 15 +recent_more_text = "more »" +date_format = "%b %-d, %Y" ++++ diff --git a/src/content/blog/_index.md b/src/content/blog/_index.md new file mode 100644 index 0000000..838b240 --- /dev/null +++ b/src/content/blog/_index.md @@ -0,0 +1,28 @@ ++++ +title = "List of blog posts" +description = "My blog posts." +sort_by = "date" +template = "blog.html" +page_template = "post.html" +insert_anchor_links = "right" +generate_feeds = true + +[extra] +lang = "en" + +title = "Blog" +# subtitle = "I write about ...." + +date_format = "%b %-d, %Y" + +categorized = false # posts can be categorized +back_to_top = true # show back-to-top button +toc = true # show table-of-contents +comment = true # enable comment +copy = true # show copy button in code block + +outdate_alert = true +outdate_alert_days = 90 +outdate_alert_text_before = "This article was last updated " +outdate_alert_text_after = " days ago and may be out of date." ++++ diff --git a/src/content/blog/distributed-hooks.md b/src/content/blog/distributed-hooks.md new file mode 100644 index 0000000..b32c743 --- /dev/null +++ b/src/content/blog/distributed-hooks.md @@ -0,0 +1,105 @@ ++++ +title = "An collection of my toughs regarding hooks in a fully distributed system" +date = 2025-04-25 ++++ +<!-- LTeX: language=en-GB --> + +## Hooks in a distributed system + +We assume that our distributed system (system for short), contains in +total one task set. This task set is synced via multiple replicas and, +most importantly, not one replica owns it. As such a replica needs to +synchronize with every other replica to be able to claim, that they own +the full task set. + +We just assume, that each of the replicas can perfectly synchronize +itself. + +What if a user wanted to run a hook on ever new input to this task set +(i.e., on every new task)? + +Where would the hook execution happen? + + +### 1. The naive way (e.g. Taskwarrior or git) + +You could simply run the hook in the client that adds/modifies the task to the task set. +At first, this approach might look very promising: +It is easy, gives the user direct feedback about the hook return status (and thus allows to use the hook as a filter) and most importantly it is completely transparent for the user. + +The problem with this approach is unfortunately not fixable. +Take for example following setup: + +``` + | Desktop |---------------------------- | Smartphone | + | | + +--------------| Laptop |-----------------+ +``` + +And assume, that all of them have access to a client, that can add task to the task set. +Now assume, that I have a hook that connects to a server and starts a time there. + +If I were to start a task on my desktop, the hook would fire and tell the server to start time tracking. +If I later stop the task on my laptop, the hook would fire again and tell the server to stop tracking time. +This works flawlessly, as the server was already tracking time and as such can stop doing so. + +Let's imagine a different approach: +I start the task on my smartphone, which has a client that is not able to run hooks directly, as my smartphone lacks a full Linux system. +Now, trying to stop the task on my laptop, raises an error with the server, as the task was never started. + +In this case, we would need a way to track that the smartphone has not yet started the time on the server. +And that this should happen once the replica on the laptop got a hold of the task (i.e., it synchronized itself with the replica on the smartphone). + +### 2. Centralized approach (e.g. Git on servers) + +The “easy” way out is simply promoting on of the replicas to be our point of centralization. +This replica could then run the hook for every new input it receives via synchronization. + +After a full synchronization with every other replica out there, we know that the hook was run exactly once for each task. + +The problem with this approach is quite apparent: +We need to promote one of the replicas. +This means that the hook can only be run, _after_ this central replica synchronized itself. +As such, filter hooks, that prevent certain tasks to be inserted into the whole task set are only run after the fact. +This also makes it necessary that the other replicas wait for this central replica to advance before they advance themselves. + +This approach is quite similar to git's branches. +Our central replica would be the main branch, and all the other replicas would than rebase themselves regularly on the main branch. + +### 3. Distributed Tracking + +Having now explained why both running the task directly on the client, and running it in a centralized replica has downsides, I would like to point out my third idea. + +What if we combine these approaches? + +A client marks a task at replica addition time, with the hooks that it has already executed (in our example above the server timer start/stop hook). +If a replica synchronizes itself and receives a task, which has not yet recorded hook execution, it will execute them instead of the original client. + +Have you noticed the problem in this approach? + +Exactly! Hooks now need to be idempotent and can possibly be executed at an arbitrary time _after_ the original task was added: + +My smartphone client, marks the new task as having not run any hooks, as such _both_ my desktop and my laptop will run the server time tracking hook. +This will than fail on the later run, as the server cannot start time tracking for an already started task. +Additionally, the second hook run could also happen _after_ the task was already stopped (marking it started again)! + +As such we exchanged having no hook execution, for one prone to race conditions. + + +### 4. Hook execution on every client + +Having seen, that working around client hook execution does not really work (cf. approach 2 or 3), we could also go the other way and give all clients the possibility to execute hooks. + +This would require two things: +1. Hooks need to be somehow synchronized between clients (you cannot expect someone, to manually sync a hook script with a mobile client) +2. Hooks can no longer be undefined executable blobs. They need to be constricted, to a subset of executables (e.g., to Lua/python/web assembly). + +With these two requirements in place, a client could ship a Lua/python/web assembly runtime and thus guarantee that it can execute all possible hooks. + +There is a big problem with this approach. +It breaks probably most of the current hooks, because they are either written in a not-included language like POSIX shell (which cannot be included, because it probably hard-depends on binary dependencies (GNU `coreutils` as the most prevalent) which cannot be introspected from the outside) or are written in one of the included languages, but depend on external dependencies (many python hooks, for example, try to execute `task` to perform further task operations). + +In general, this approach would probably require a sandbox of some sort for hooks, so that hook authors know that their hook will also work on other platforms. +If we limit hooks to a subset of possible options, we should also enforce it on the platforms with more possibilities, so that hook authors can be confident that their hook actually works everywhere. + + diff --git a/src/content/contact/_index.md b/src/content/contact/_index.md new file mode 100644 index 0000000..3276460 --- /dev/null +++ b/src/content/contact/_index.md @@ -0,0 +1,31 @@ ++++ +title = "Contact" +description = "How to contact me" +template = "prose.html" +insert_anchor_links = "none" + +[extra] +lang = 'en' + +title = "Contact" +subtitle = "How to contact me" + + +math = false +mermaid = false +copy = false +comment = false +reaction = false ++++ + +Currently, you can reach me via the following methods (in order of preference): +- [Email – benedikt.peetz@b-peetz.de](mailto:benedikt.peetz@b-peetz.de) +- [Matrix – @soispha:vhack.eu](https://matrix.to/#/@soispha:matrix.vhack.eu) +- [Signal – benedikt.32](https://signal.me/#/eu/r03RJhrEynGMYVMhhpGuirRRMJYm_Yjgys3gfQPayVlQmb0Bv25Pt4_HT9QYpa7w) + +You can find my published PGP key via the web key directory: +``` +sq network wkd search benedikt.peetz@b-peetz.de --output - +``` + +Feel free to use that, if you want to use encrypted email. diff --git a/src/content/writings/_index.md b/src/content/writings/_index.md new file mode 100644 index 0000000..720c953 --- /dev/null +++ b/src/content/writings/_index.md @@ -0,0 +1,28 @@ ++++ +title = "Writings" +description = "Sort of academically written things" +sort_by = "date" +template = "blog.html" +page_template = "post.html" +insert_anchor_links = "right" +generate_feeds = true + +[extra] +lang = "en" + +title = "Writings" +subtitle = "Sort of academically written things." + +date_format = "%b %-d, %Y" + +categorized = true # posts can be categorized +back_to_top = true # show back-to-top button +toc = true # show table-of-contents +comment = true # enable comment +copy = true # show copy button in code block + +outdate_alert = true +outdate_alert_days = 90 +outdate_alert_text_before = "This article was last updated " +outdate_alert_text_after = " days ago and may be out of date." ++++ diff --git a/src/content/writings/kant-and-free-software.md b/src/content/writings/kant-and-free-software.md new file mode 100644 index 0000000..ec491fa --- /dev/null +++ b/src/content/writings/kant-and-free-software.md @@ -0,0 +1,9 @@ ++++ +title = "Kant and free software" +date = "2024-03-20" + +path = "writings/kant-and-free-software.pdf" + +[taxonomies] +categories=["philosophy"] ++++ diff --git a/src/content/writings/raman-spectroscopy.md b/src/content/writings/raman-spectroscopy.md new file mode 100644 index 0000000..2d05da4 --- /dev/null +++ b/src/content/writings/raman-spectroscopy.md @@ -0,0 +1,10 @@ ++++ +title = "Bau und Evaluation eines günstigen Raman Spektrometers" +date = "2024-06-01" +lang = "de" + +path = "writings/raman-spectroscopy.pdf" + +[taxonomies] +categories=["chemistry"] ++++ diff --git a/src/highlight_themes b/src/highlight_themes new file mode 120000 index 0000000..5109ab5 --- /dev/null +++ b/src/highlight_themes @@ -0,0 +1 @@ +themes/serene/highlight_themes \ No newline at end of file diff --git a/src/index.html b/src/index.html deleted file mode 100644 index 63f1039..0000000 --- a/src/index.html +++ /dev/null @@ -1,13 +0,0 @@ -<!doctype html> -<html lang="en"> -<head> - <meta charset="UTF-8"> - <meta name="viewport" content="width=device-width"/> - <title>Benedikt's Website</title> - <link rel="stylesheet" href="/style.css"> -</head> -<body> - <h1>Hi!</h1> - <p>For a list of my written things look <a href="./writings/index.html">here</a></p> -</body> -</html> diff --git a/src/static/hl-dark.css b/src/static/hl-dark.css new file mode 100644 index 0000000..da26983 --- /dev/null +++ b/src/static/hl-dark.css @@ -0,0 +1,72 @@ +/* + * theme "Tomorrow Night" generated by syntect + */ + +.z-code { + color: #c5c8c6; +} + +.z-comment, .z-string.z-quoted.z-double.z-block.z-python { + color: #999999; +} +.z-keyword.z-operator.z-class, .z-constant.z-other, .z-source.z-php.z-embedded.z-line { + color: #ced1cf; +} +.z-variable, .z-support.z-other.z-variable, .z-string.z-other.z-link, .z-string.z-regexp, .z-entity.z-name.z-tag, .z-entity.z-other.z-attribute-name, .z-meta.z-tag, .z-declaration.z-tag { + color: #a67878; +} +.z-constant.z-numeric, .z-constant.z-language, .z-support.z-constant, .z-constant.z-character, .z-variable.z-parameter, .z-punctuation.z-section.z-embedded, .z-keyword.z-other.z-unit { + color: #e08355; +} +.z-type, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class, .z-support.z-type, .z-support.z-class { + color: #83aaa5; +} +.z-string, .z-constant.z-other.z-symbol, .z-entity.z-other.z-inherited-class, .z-markup.z-heading { + color: #85ad74; +} +.z-keyword.z-operator, .z-constant.z-other.z-color { + color: #83aaa5; +} +.z-entity.z-name.z-function, .z-meta.z-function-call, .z-support.z-function, .z-keyword.z-other.z-special-method, .z-meta.z-block-level { + color: #81a2be; +} +.z-keyword, .z-storage, .z-storage.z-type, .z-entity.z-name.z-tag.z-css { + color: #b294bb; +} +.z-invalid { + color: #ced2cf; + background-color: #df5f5f; +} +.z-meta.z-separator { + color: #ced2cf; + background-color: #82a3bf; +} +.z-invalid.z-deprecated { + color: #ced2cf; + background-color: #b798bf; +} +.z-markup.z-inserted.z-diff, .z-markup.z-deleted.z-diff, .z-meta.z-diff.z-header.z-to-file, .z-meta.z-diff.z-header.z-from-file { + color: #ffffff; +} +.z-markup.z-inserted.z-diff, .z-meta.z-diff.z-header.z-to-file { + color: #4baf5c; +} +.z-markup.z-deleted.z-diff, .z-meta.z-diff.z-header.z-from-file { + color: #d46565; +} +.z-meta.z-diff.z-header.z-from-file, .z-meta.z-diff.z-header.z-to-file { + color: #4271ae; +} +.z-meta.z-diff.z-range { + color: #3e999f; +font-style: italic; +} +.z-markup.z-deleted { + color: #f92672; +} +.z-markup.z-inserted { + color: #a6e22e; +} +.z-markup.z-changed { + color: #967efb; +} diff --git a/src/static/hl-light.css b/src/static/hl-light.css new file mode 100644 index 0000000..acb83c5 --- /dev/null +++ b/src/static/hl-light.css @@ -0,0 +1,60 @@ +/* + * theme "Tomorrow" generated by syntect + */ + +.z-code { + color: #4d4d4c; +} + +.z-comment, .z-string.z-quoted.z-double.z-block.z-python { + color: #999999; +} +.z-keyword.z-operator.z-class, .z-constant.z-other, .z-source.z-php.z-embedded.z-line { + color: #666969; +} +.z-variable, .z-support.z-other.z-variable, .z-string.z-other.z-link, .z-string.z-regexp, .z-entity.z-name.z-tag, .z-entity.z-other.z-attribute-name, .z-meta.z-tag, .z-declaration.z-tag { + color: #a67878; +} +.z-constant.z-numeric, .z-constant.z-language, .z-support.z-constant, .z-constant.z-character, .z-variable.z-parameter, .z-punctuation.z-section.z-embedded, .z-keyword.z-other.z-unit { + color: #e08355; +} +.z-type, .z-entity.z-name.z-class, .z-entity.z-name.z-type.z-class, .z-support.z-type, .z-support.z-class { + color: #568a8f; +} +.z-string, .z-constant.z-other.z-symbol, .z-entity.z-other.z-inherited-class, .z-markup.z-heading { + color: #85ad74; +} +.z-keyword.z-operator, .z-constant.z-other.z-color { + color: #568a8f; +} +.z-entity.z-name.z-function, .z-meta.z-function-call, .z-support.z-function, .z-keyword.z-other.z-special-method, .z-meta.z-block-level { + color: #4271ae; +} +.z-keyword, .z-storage, .z-storage.z-type { + color: #8959a8; +} +.z-invalid { + color: #ffffff; + background-color: #df5f5f; +} +.z-meta.z-separator { + color: #ffffff; + background-color: #4271ae; +} +.z-invalid.z-deprecated { + color: #ffffff; + background-color: #8959a8; +} +.z-markup.z-inserted.z-diff, .z-meta.z-diff.z-header.z-to-file { + color: #229545; +} +.z-markup.z-deleted.z-diff, .z-meta.z-diff.z-header.z-from-file { + color: #c82829; +} +.z-meta.z-diff.z-header.z-from-file, .z-meta.z-diff.z-header.z-to-file { + color: #4271ae; +} +.z-meta.z-diff.z-range { + color: #3e999f; +font-style: italic; +} diff --git a/src/static/icon/git-black.svg b/src/static/icon/git-black.svg new file mode 100644 index 0000000..a4704b9 --- /dev/null +++ b/src/static/icon/git-black.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 92 92"><defs><clipPath id="a"><path d="M0 .113h91.887V92H0Zm0 0"/></clipPath></defs><g clip-path="url(#a)"><path style="stroke:none;fill-rule:nonzero;fill:#100f0d;fill-opacity:1" d="M90.156 41.965 50.036 1.848a5.913 5.913 0 0 0-8.368 0l-8.332 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.043 7.043 0 0 1 1.673 7.277l10.183 10.184a7.026 7.026 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.045 7.045 0 0 1-9.961 0 7.038 7.038 0 0 1-1.532-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.034 7.034 0 0 1 2.308-1.539V33.926a7.001 7.001 0 0 1-2.308-1.535 7.049 7.049 0 0 1-1.516-7.7L29.242 14.273 1.734 41.777a5.918 5.918 0 0 0 0 8.371L41.855 90.27a5.92 5.92 0 0 0 8.368 0l39.933-39.934a5.925 5.925 0 0 0 0-8.371"/></g></svg> diff --git a/src/static/icon/git-white.svg b/src/static/icon/git-white.svg new file mode 100644 index 0000000..6ada92c --- /dev/null +++ b/src/static/icon/git-white.svg @@ -0,0 +1,10 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 92 92"> + <defs> + <clipPath id="a"> + <path d="M0 .113h91.887V92H0Zm0 0"/> + </clipPath> + </defs> + <g clip-path="url(#a)"> + <path style="stroke:none;fill-rule:nonzero;fill:#fff;fill-opacity:1" d="M90.156 41.965 50.036 1.848a5.918 5.918 0 0 0-8.372 0l-8.328 8.332 10.566 10.566a7.03 7.03 0 0 1 7.23 1.684 7.034 7.034 0 0 1 1.669 7.277l10.187 10.184a7.028 7.028 0 0 1 7.278 1.672 7.04 7.04 0 0 1 0 9.957 7.05 7.05 0 0 1-9.965 0 7.044 7.044 0 0 1-1.528-7.66l-9.5-9.497V59.36a7.04 7.04 0 0 1 1.86 11.29 7.04 7.04 0 0 1-9.957 0 7.04 7.04 0 0 1 0-9.958 7.06 7.06 0 0 1 2.304-1.539V33.926a7.049 7.049 0 0 1-3.82-9.234L29.242 14.272 1.73 41.777a5.925 5.925 0 0 0 0 8.371L41.852 90.27a5.925 5.925 0 0 0 8.37 0l39.934-39.934a5.925 5.925 0 0 0 0-8.371"/> + </g> +</svg> diff --git a/src/static/icon/matrix-white.svg b/src/static/icon/matrix-white.svg new file mode 100644 index 0000000..0d3cf54 --- /dev/null +++ b/src/static/icon/matrix-white.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg version="1.1" width="18" height="18" viewBox="0 0 27.9 32" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> + <title>Matrix (protocol) logo</title> + <g transform="translate(-.095 .005)" fill="#FFFFFF"> + <path d="m27.1 31.2v-30.5h-2.19v-0.732h3.04v32h-3.04v-0.732z"/> + <path d="m8.23 10.4v1.54h0.044c0.385-0.564 0.893-1.03 1.49-1.37 0.58-0.323 1.25-0.485 1.99-0.485 0.72 0 1.38 0.14 1.97 0.42 0.595 0.279 1.05 0.771 1.36 1.48 0.338-0.5 0.796-0.941 1.38-1.32 0.58-0.383 1.27-0.574 2.06-0.574 0.602 0 1.16 0.074 1.67 0.22 0.514 0.148 0.954 0.383 1.32 0.707 0.366 0.323 0.653 0.746 0.859 1.27 0.205 0.522 0.308 1.15 0.308 1.89v7.63h-3.13v-6.46c0-0.383-0.015-0.743-0.044-1.08-0.0209-0.307-0.103-0.607-0.242-0.882-0.133-0.251-0.336-0.458-0.584-0.596-0.257-0.146-0.606-0.22-1.05-0.22-0.44 0-0.796 0.085-1.07 0.253-0.272 0.17-0.485 0.39-0.639 0.662-0.159 0.287-0.264 0.602-0.308 0.927-0.052 0.347-0.078 0.697-0.078 1.05v6.35h-3.13v-6.4c0-0.338-7e-3 -0.673-0.021-1-0.0114-0.314-0.0749-0.623-0.188-0.916-0.108-0.277-0.3-0.512-0.55-0.673-0.258-0.168-0.636-0.253-1.14-0.253-0.198 0.0083-0.394 0.042-0.584 0.1-0.258 0.0745-0.498 0.202-0.705 0.374-0.228 0.184-0.422 0.449-0.584 0.794-0.161 0.346-0.242 0.798-0.242 1.36v6.62h-3.13v-11.4z"/> + <path d="m0.936 0.732v30.5h2.19v0.732h-3.04v-32h3.03v0.732z"/> + </g> +</svg> diff --git a/src/static/icon/signal-black.svg b/src/static/icon/signal-black.svg new file mode 100644 index 0000000..8f0fbd7 --- /dev/null +++ b/src/static/icon/signal-black.svg @@ -0,0 +1 @@ +<svg fill="none" height="18" viewBox="0 0 160 160" width="18" xmlns="http://www.w3.org/2000/svg"><g fill="#000"><path d="m80 0c4.1505 0 8.2271.31607 12.2072.925452l-1.1444 7.413248c-3.6069-.55226-7.3014-.8387-11.0628-.8387-3.7612 0-7.4555.28641-11.0623.83862l-1.1444-7.413245c3.9799-.609332 8.0564-.925375 12.2067-.925375z"/><path d="m98.9849 2.26619-1.7798 7.28755c7.3099 1.77896 14.1849 4.66606 20.4389 8.47306l3.895-6.411c-6.901-4.20091-14.488-7.38658-22.5541-9.34961z"/><path d="m127.279 15.4591-4.432 6.0507c5.977 4.3861 11.257 9.6664 15.643 15.6437l6.051-4.4324c-4.84-6.5957-10.666-12.4222-17.262-17.262z"/><path d="m148.384 38.4618-6.411 3.8942c3.807 6.2541 6.694 13.1299 8.473 20.4395l7.288-1.7798c-1.963-8.0657-5.149-15.6528-9.35-22.5539z"/><path d="m159.075 67.7934-7.414 1.1444c.553 3.6067.839 7.301.839 11.0622 0 3.7614-.286 7.4559-.839 11.0628l7.414 1.1444c.609-3.9801.925-8.0567.925-12.2072 0-4.1503-.316-8.2267-.925-12.2066z"/><path d="m141.973 117.645c3.807-6.255 6.694-13.13 8.473-20.44l7.288 1.7798c-1.963 8.0662-5.149 15.6532-9.35 22.5542z"/><path d="m138.49 122.847 6.051 4.432c-4.84 6.596-10.666 12.422-17.262 17.262l-4.433-6.051c5.978-4.386 11.258-9.666 15.644-15.643z"/><path d="m117.644 141.973 3.894 6.411c-6.901 4.201-14.488 7.387-22.5537 9.35l-1.7798-7.288c7.3095-1.779 14.1855-4.666 20.4395-8.473z"/><path d="m91.0622 151.661 1.1445 7.414c-3.9799.609-8.0564.925-12.2067.925-4.1505 0-8.2272-.316-12.2073-.925l1.1442-7.413c3.6054.552 7.2997.838 11.0631.838 3.7612 0 7.4555-.286 11.0622-.839z"/><path d="m62.7945 150.448-1.7794 7.286c-6.0589-1.475-11.8477-3.639-17.2785-6.406l-7.5927 1.772-1.7042-7.304 10.2604-2.394 2.4408 1.243c4.9187 2.506 10.1623 4.467 15.6536 5.803z"/><path d="m28.1097 147.273 1.7042 7.304-13.0145 3.036c-8.66079 2.021-16.433718-5.752-14.41286-14.412l3.03673-13.015 7.30383 1.704-3.03675 13.015c-.75782 3.248 2.15705 6.162 5.40485 5.405z"/><path d="m14.2041 125.56-7.30383-1.704 1.77163-7.593c-2.76664-5.431-4.93123-11.22-6.40585-17.2787l7.28586-1.7794c1.33599 5.4911 3.29709 10.7351 5.80279 15.6541l1.2435 2.441z"/><path d="m8.33759 91.0624-7.412228 1.1442c-.609324-3.9799-.925362-8.0563-.925362-12.2066 0-4.1505.316067-8.2271.925446-12.2072l7.413244 1.1444c-.55225 3.607-.83869 7.3014-.83869 11.0628 0 3.7631.28613 7.4572.83759 11.0624z"/><path d="m9.55373 62.795-7.28755-1.7798c1.96302-8.0657 5.1487-15.6528 9.34962-22.5539l6.411 3.8942c-3.807 6.2541-6.6941 13.1299-8.47307 20.4395z"/><path d="m21.5098 37.1531-6.0507-4.4324c4.8398-6.5957 10.6663-12.4221 17.262-17.2619l4.4324 6.0507c-5.9773 4.3861-11.2576 9.6663-15.6437 15.6436z"/><path d="m42.356 18.0266-3.8943-6.4111c6.9011-4.20082 14.4882-7.38645 22.554-9.34944l1.7798 7.28755c-7.3096 1.77899-14.1854 4.66589-20.4395 8.47299z"/><path d="m145 80c0 35.899-29.101 65-65 65-11.3866 0-22.0893-2.928-31.3965-8.072-.8961-.495-1.9417-.658-2.9389-.426l-28.9134 6.747 6.7465-28.914c.2326-.997.0692-2.043-.426-2.939-5.1439-9.307-8.0717-20.0095-8.0717-31.396 0-35.8985 29.1015-65 65-65 35.899 0 65 29.1015 65 65z"/></g></svg> diff --git a/src/static/icon/signal-ultramarine.svg b/src/static/icon/signal-ultramarine.svg new file mode 100644 index 0000000..87f7a77 --- /dev/null +++ b/src/static/icon/signal-ultramarine.svg @@ -0,0 +1 @@ +<svg fill="none" height="18" viewBox="0 0 160 160" width="18" xmlns="http://www.w3.org/2000/svg"><g fill="#3b45fd"><path d="m80 0c4.1505 0 8.2271.31607 12.2072.925452l-1.1444 7.413248c-3.6069-.55226-7.3014-.8387-11.0628-.8387-3.7612 0-7.4555.28641-11.0623.83862l-1.1444-7.413245c3.9799-.609332 8.0564-.925375 12.2067-.925375z"/><path d="m98.9849 2.26619-1.7798 7.28755c7.3099 1.77896 14.1849 4.66606 20.4389 8.47306l3.895-6.411c-6.901-4.20091-14.488-7.38658-22.5541-9.34961z"/><path d="m127.279 15.4591-4.432 6.0507c5.977 4.3861 11.257 9.6664 15.643 15.6437l6.051-4.4324c-4.84-6.5957-10.666-12.4222-17.262-17.262z"/><path d="m148.384 38.4618-6.411 3.8942c3.807 6.2541 6.694 13.1299 8.473 20.4395l7.288-1.7798c-1.963-8.0657-5.149-15.6528-9.35-22.5539z"/><path d="m159.075 67.7934-7.414 1.1444c.553 3.6067.839 7.301.839 11.0622 0 3.7614-.286 7.4559-.839 11.0628l7.414 1.1444c.609-3.9801.925-8.0567.925-12.2072 0-4.1503-.316-8.2267-.925-12.2066z"/><path d="m141.973 117.645c3.807-6.255 6.694-13.13 8.473-20.44l7.288 1.7798c-1.963 8.0662-5.149 15.6532-9.35 22.5542z"/><path d="m138.49 122.847 6.051 4.432c-4.84 6.596-10.666 12.422-17.262 17.262l-4.433-6.051c5.978-4.386 11.258-9.666 15.644-15.643z"/><path d="m117.644 141.973 3.894 6.411c-6.901 4.201-14.488 7.387-22.5537 9.35l-1.7798-7.288c7.3095-1.779 14.1855-4.666 20.4395-8.473z"/><path d="m91.0622 151.661 1.1445 7.414c-3.9799.609-8.0564.925-12.2067.925-4.1505 0-8.2272-.316-12.2073-.925l1.1442-7.413c3.6054.552 7.2997.838 11.0631.838 3.7612 0 7.4555-.286 11.0622-.839z"/><path d="m62.7945 150.448-1.7794 7.286c-6.0589-1.475-11.8477-3.639-17.2785-6.406l-7.5927 1.772-1.7042-7.304 10.2604-2.394 2.4408 1.243c4.9187 2.506 10.1623 4.467 15.6536 5.803z"/><path d="m28.1097 147.273 1.7042 7.304-13.0145 3.036c-8.66079 2.021-16.433718-5.752-14.41286-14.412l3.03673-13.015 7.30383 1.704-3.03675 13.015c-.75782 3.248 2.15705 6.162 5.40485 5.405z"/><path d="m14.2041 125.56-7.30383-1.704 1.77163-7.593c-2.76664-5.431-4.93123-11.22-6.40585-17.2787l7.28586-1.7794c1.33599 5.4911 3.29709 10.7351 5.80279 15.6541l1.2435 2.441z"/><path d="m8.33759 91.0624-7.412228 1.1442c-.609324-3.9799-.925362-8.0563-.925362-12.2066 0-4.1505.316067-8.2271.925446-12.2072l7.413244 1.1444c-.55225 3.607-.83869 7.3014-.83869 11.0628 0 3.7631.28613 7.4572.83759 11.0624z"/><path d="m9.55373 62.795-7.28755-1.7798c1.96302-8.0657 5.1487-15.6528 9.34962-22.5539l6.411 3.8942c-3.807 6.2541-6.6941 13.1299-8.47307 20.4395z"/><path d="m21.5098 37.1531-6.0507-4.4324c4.8398-6.5957 10.6663-12.4221 17.262-17.2619l4.4324 6.0507c-5.9773 4.3861-11.2576 9.6663-15.6437 15.6436z"/><path d="m42.356 18.0266-3.8943-6.4111c6.9011-4.20082 14.4882-7.38645 22.554-9.34944l1.7798 7.28755c-7.3096 1.77899-14.1854 4.66589-20.4395 8.47299z"/><path d="m145 80c0 35.899-29.101 65-65 65-11.3866 0-22.0893-2.928-31.3965-8.072-.8961-.495-1.9417-.658-2.9389-.426l-28.9134 6.747 6.7465-28.914c.2326-.997.0692-2.043-.426-2.939-5.1439-9.307-8.0717-20.0095-8.0717-31.396 0-35.8985 29.1015-65 65-65 35.899 0 65 29.1015 65 65z"/></g></svg> diff --git a/src/static/icon/signal-white.svg b/src/static/icon/signal-white.svg new file mode 100644 index 0000000..e869680 --- /dev/null +++ b/src/static/icon/signal-white.svg @@ -0,0 +1 @@ +<svg fill="none" height="18" viewBox="0 0 160 160" width="18" xmlns="http://www.w3.org/2000/svg"><g fill="#fff"><path d="m80 0c4.1505 0 8.2271.31607 12.2072.925452l-1.1444 7.413248c-3.6069-.55226-7.3014-.8387-11.0628-.8387-3.7612 0-7.4555.28641-11.0623.83862l-1.1444-7.413245c3.9799-.609332 8.0564-.925375 12.2067-.925375z"/><path d="m98.9849 2.26619-1.7798 7.28755c7.3099 1.77896 14.1849 4.66606 20.4389 8.47306l3.895-6.411c-6.901-4.20091-14.488-7.38658-22.5541-9.34961z"/><path d="m127.279 15.4591-4.432 6.0507c5.977 4.3861 11.257 9.6664 15.643 15.6437l6.051-4.4324c-4.84-6.5957-10.666-12.4222-17.262-17.262z"/><path d="m148.384 38.4618-6.411 3.8942c3.807 6.2541 6.694 13.1299 8.473 20.4395l7.288-1.7798c-1.963-8.0657-5.149-15.6528-9.35-22.5539z"/><path d="m159.075 67.7934-7.414 1.1444c.553 3.6067.839 7.301.839 11.0622 0 3.7614-.286 7.4559-.839 11.0628l7.414 1.1444c.609-3.9801.925-8.0567.925-12.2072 0-4.1503-.316-8.2267-.925-12.2066z"/><path d="m141.973 117.645c3.807-6.255 6.694-13.13 8.473-20.44l7.288 1.7798c-1.963 8.0662-5.149 15.6532-9.35 22.5542z"/><path d="m138.49 122.847 6.051 4.432c-4.84 6.596-10.666 12.422-17.262 17.262l-4.433-6.051c5.978-4.386 11.258-9.666 15.644-15.643z"/><path d="m117.644 141.973 3.894 6.411c-6.901 4.201-14.488 7.387-22.5537 9.35l-1.7798-7.288c7.3095-1.779 14.1855-4.666 20.4395-8.473z"/><path d="m91.0622 151.661 1.1445 7.414c-3.9799.609-8.0564.925-12.2067.925-4.1505 0-8.2272-.316-12.2073-.925l1.1442-7.413c3.6054.552 7.2997.838 11.0631.838 3.7612 0 7.4555-.286 11.0622-.839z"/><path d="m62.7945 150.448-1.7794 7.286c-6.0589-1.475-11.8477-3.639-17.2785-6.406l-7.5927 1.772-1.7042-7.304 10.2604-2.394 2.4408 1.243c4.9187 2.506 10.1623 4.467 15.6536 5.803z"/><path d="m28.1097 147.273 1.7042 7.304-13.0145 3.036c-8.66079 2.021-16.433718-5.752-14.41286-14.412l3.03673-13.015 7.30383 1.704-3.03675 13.015c-.75782 3.248 2.15705 6.162 5.40485 5.405z"/><path d="m14.2041 125.56-7.30383-1.704 1.77163-7.593c-2.76664-5.431-4.93123-11.22-6.40585-17.2787l7.28586-1.7794c1.33599 5.4911 3.29709 10.7351 5.80279 15.6541l1.2435 2.441z"/><path d="m8.33759 91.0624-7.412228 1.1442c-.609324-3.9799-.925362-8.0563-.925362-12.2066 0-4.1505.316067-8.2271.925446-12.2072l7.413244 1.1444c-.55225 3.607-.83869 7.3014-.83869 11.0628 0 3.7631.28613 7.4572.83759 11.0624z"/><path d="m9.55373 62.795-7.28755-1.7798c1.96302-8.0657 5.1487-15.6528 9.34962-22.5539l6.411 3.8942c-3.807 6.2541-6.6941 13.1299-8.47307 20.4395z"/><path d="m21.5098 37.1531-6.0507-4.4324c4.8398-6.5957 10.6663-12.4221 17.262-17.2619l4.4324 6.0507c-5.9773 4.3861-11.2576 9.6663-15.6437 15.6436z"/><path d="m42.356 18.0266-3.8943-6.4111c6.9011-4.20082 14.4882-7.38645 22.554-9.34944l1.7798 7.28755c-7.3096 1.77899-14.1854 4.66589-20.4395 8.47299z"/><path d="m145 80c0 35.899-29.101 65-65 65-11.3866 0-22.0893-2.928-31.3965-8.072-.8961-.495-1.9417-.658-2.9389-.426l-28.9134 6.747 6.7465-28.914c.2326-.997.0692-2.043-.426-2.939-5.1439-9.307-8.0717-20.0095-8.0717-31.396 0-35.8985 29.1015-65 65-65 35.899 0 65 29.1015 65 65z"/></g></svg> diff --git a/src/style.css b/src/style.css deleted file mode 100644 index c245d20..0000000 --- a/src/style.css +++ /dev/null @@ -1,50 +0,0 @@ -a:link, a:visited { - color: #005386; -} - -ul a { - text-decoration: none; -} - -body { - margin:40px auto; - max-width:650px; - line-height:1.6; - font-size:18px; - padding:0 10px -} - -img { - max-width:100%; - display: block; - margin: 0 auto; -} - -@media (prefers-color-scheme: dark) { - body { - color: #ffffff; - background: #212121; - margin:40px auto; - max-width:650px; - line-height:1.6; - font-size:18px; - padding:0 10px - } - - h1, h2, h3 { - line-height:1.2 - } - - a:link, a:visited { - color: #58a6ff; - } - - ul a { - text-decoration: none; - } - - ul a:visited { - text-decoration: none; - color: #8e96f0; - } -} diff --git a/src/themes/serene/.gitignore b/src/themes/serene/.gitignore new file mode 100644 index 0000000..87760eb --- /dev/null +++ b/src/themes/serene/.gitignore @@ -0,0 +1,8 @@ +public/ +content/ +static/img/ +static/hl-light.css +static/hl-dark.css +config.toml + +.DS_Store diff --git a/src/themes/serene/CHANGELOG.md b/src/themes/serene/CHANGELOG.md new file mode 100644 index 0000000..872470b --- /dev/null +++ b/src/themes/serene/CHANGELOG.md @@ -0,0 +1,347 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## [5.2.1] - 2025-04-19 + +- fix: homepage avatar style + +## [5.2.0] - 2025-04-10 + +- ui: a few tweaks + +## [5.1.0] - 2025-04-08 + +- feat: add support for zola v0.20.0 codeblock [name annotation](https://www.getzola.org/documentation/content/syntax-highlighting/#annotations), previous `codeblock` shortcode is deprecated +- refactor: remove `static/` prefix from icon paths, eliminates the need to copy icon files [@koyokr](https://github.com/koyokr) ([#76](https://github.com/isunjn/serene/pull/76)) + +## [5.0.1] - 2025-03-31 + +- fix: external links & recent posts on the homepage [@Hiramiya](https://github.com/Hiramiya) ([#74](https://github.com/isunjn/serene/pull/74)) + +## [5.0.0] - 2025-03-20 + +> **Warning** +> +> This version is a big redesign and contains lots of breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +- New style: headerless, section title and subtitle, improved typography... +- Collections: special blocks for showcasing your list (a more general form of previous 'projects' page) +- Default icon size changed from 20 to 18 +- Some config options are moved from `config.toml` to specific `_index.md` +- Added options: `date_format` `back_link_text`, (section) `title` `subtitle`, (homepage) `footer` +- Removed options: `display_*` `nav_*` `blur_effect` `display_tags` `truncate_summary` `not_found_title` +- Callouts: `question` removed, `alert` renamed to `caution`, attribution `header` renamed to `title` +- Typst math rendering removed +- Added CSS variables: `--primary-decoration-color` `--text-decoration-color` `--highlight-mark-color` `--font-size` `--line-height` +- Removed CSS variables: `--homepage-font-size` `--homepage-line-height` `--paragraph-font-size` `--paragraph-line-height` `--aside-font-size` +- Lots of UI tweaks +- feat: Support subpath `base_url` [@b-d-e](https://github.com/b-d-e) ([#68](https://github.com/isunjn/serene/pull/68)) +- fix: `force_theme` option [@teh-banana](https://github.com/teh-banana) ([#71](https://github.com/isunjn/serene/pull/71)) +- fix: Add content-type header to reaction fetch [@sorokya](https://github.com/sorokya) ([#72](https://github.com/isunjn/serene/pull/72)) + +## [4.5.0] - 2024-11-03 + +### UI: + +- A few tweaks + +## [4.4.0] - 2024-11-02 + +### Add: + +- New feature: Anonymous emoji reactions +- New options: `display_bio` `display_avatar` `recent` + +### Remove: + +- Removed options: `homepage_layout` (use `recent` instead), `recent_more` +- Removed css variable: `--icon-size` + +### UI: + +- A few tweaks + +## [4.3.0] - 2024-10-13 + +### Add: + +- Add katex [copy-tex](https://github.com/KaTeX/KaTeX/tree/main/contrib/copy-tex) extension & bump katex version to 0.16.11 + +## [4.2.0] - 2024-10-04 + +### Fix: + +- Fix anchor link style issue, now `#` should no be present in the RSS file + +## [4.1.0] - 2024-09-16 + +### Add: + +- `force_theme` option to only use light or dark theme [@bruceoberg](https://github.com/bruceoberg) ([#62](https://github.com/isunjn/serene/issues/62)) +- A few more icons [@bruceoberg](https://github.com/bruceoberg) ([#63](https://github.com/isunjn/serene/issues/63)) + + +## [4.0.0] - 2024-08-11 + +- Deal with breaking changes of zola 19 config options: +> - Changed config options named `generate_feed` to `generate_feeds` (both in config.toml and in section front-matter) +> - Changed config option `feed_filename: String` to `feed_filenames: Vec<String>` + +## [3.4.0] - 2024-04-25 + +### Add: + +- Math rending with [Typst](https://typst.app) [@Lambdaris](https://github.com/Lambdaris) ([#57](https://github.com/isunjn/serene/pull/57)) + +## [3.3.1] - 2024-03-10 + +### Fix: + +- Callout content overflow issue + +### UI: + +- Change highlight color of `diff` syntax +- A few tweaks + +## [3.3.0] - 2024-03-01 + +### Add: + +- New css variables: `--callout-border-radius` `--detail-border-radius` + +### Fix: + +- Overflow issue on mobile screens + +### UI: + +- Update quote icon +- A few tweaks + +## [3.2.0] - 2024-01-26 + +### Add: + +- Dark mode img/chart brightness option + +## [3.1.0] - 2024-01-20 + +### Add: + +- New shortcode: `quote` and `detail` + +### Fix: + +- Add `word-wrap: break-word` to inline code + + +## [3.0.0] - 2024-01-14 + +> **Warning** +> +> This version contains several breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +### Add: + +- `recent` homepage layout +- `featured` mark +- Add title to ToC when it's too long +- A way to sort categories +- Project item image +- prerender/prefetch when hover, using `speculationrules` or `prefetch` +- RSS mask +- A few more css variables + +### Fix: + +- Theme init logic +- Mobile sidebar ui + +### UI: + +- A few tweaks +- Default icon size set to 20 (You should re-copy the `static/icon` folder) + + +## [2.3.0] - 2024-01-09 + +### Fix: + +- `z-index` of mobile sidebar + +### UI: + +- Color change and some small tweaks + +### Remove: + +- Default custom font removed + + +## [2.2.1] - 2024-01-02 + +### Fix: + +- Use `sessionStorage` for theme init in `_base.html` + +## [2.2.0] - 2023-12-29 + +### Fix: + +- Use `sessionStorage` for theme restore +- Fix an issue when initializing giscus theme +- Hide `#` anchor link in feed file + +## [2.1.2] - 2023-09-19 + +### Fix: + +- Outdate alert not 'hidden' ([#49](https://github.com/isunjn/serene/issues/49)) + +## [2.1.1] - 2023-09-16 + +### Add: + +- Custom 404 page + +## [2.0.1] - 2023-09-13 + +### Fix: + +- Min height of prose page & post page + +## [2.0.0] - 2023-09-01 + +> **Warning** +> +> This version contains several breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +### UI: + +- Text selction now is styled +- Other minor tweaks +- Change defalut bg color of codeblock to transparent + +### Add: + +- Option `dispaly_tags` and `truncate_summary` [@woojiq](https://github.com/woojiq) ([#40](https://github.com/isunjn/serene/issues/40)) +- Support for footnote and backlink +- Active TOC indicator +- Generay `prose` section/page +- Config option `sections`, now you can rename `blog` to somthing else, e.g. `posts` +- Support for header nav fold/unfold +- Option for homepage layout, can be `about` or `list` +- A separate `_custom_css.html` for css customization + +### Fix: + +- Codeblock distance calculation +- Codeblock highlight style +- Add description tag only when it's available +- Post 3 column layout issue +- Inline code style in list item +- Link text-decoration style on mobile + + +## [1.2.0] - 2023-08-19 + +### UI: + +- Use noborder theme of giscus by default +- Post list item and callout styles changed +- Code block styles improved +- Default colors changed + +### Add: + +- Outline styles [@mrtnvgr](https://github.com/mrtnvgr) ([#26](https://github.com/isunjn/serene/pull/26)) +- Support self-host font ([#29](https://github.com/isunjn/serene/pull/29)) +- Copy button for code blocks ([#30](https://github.com/isunjn/serene/pull/30)) +- Support light/dark switch for code blocks ([#33](https://github.com/isunjn/serene/pull/33)) +- Support tags for project page +- Back-to-top button +- A shortcode for code block with file name: `codeblock` ([#39](https://github.com/isunjn/serene/pull/39)) + +### Fix: + +- Update theme toggle icon on page load [@mrtnvgr](https://github.com/mrtnvgr) ([#25](https://github.com/isunjn/serene/pull/25)) +- Layout shift problem on post page ([#27](https://github.com/isunjn/serene/pull/27)) + +## [1.1.1] - 2023-08-09 + +- Allow no tags in front matter [@mrtnvgr](https://github.com/mrtnvgr) +- Fix figcaption width issue + +## [1.1.0] - 2023-05-27 + +- Fix theme auto-toggle logic +- A few ui tweaks + +## [1.0.0] - 2023-05-24 + +> **Warning** +> The 1.0.0 version contains many breaking changes. +> If you came from a previous version and want to upgrade, I suggest you start all over again. + +### Breaking + +- `config.toml` restructured, config items are renamed +- All analytics configs removed, use `_head_extend.html` instead +- All comment-support configs removed, replace with [giscus](https://giscus.app) +- Icons now using svg files +- Callout renamed: `info -> note`, `caution -> warning`, `warning -> alert` +- Callout removed: `good`, `bad`, `happy`, `unhappy`, `check`, `wrong`, `flag`, `star` +- `cc_license` removed +- Reading-progress-bar removed +- Back-to-top button removed +- Many other tweaks + +## [0.2.0] - 2022-02-16 + +### Add: +- KaTeX support +- Mermaid support + +### Fix: +- Style issue of table-of-contents +- A few non-critical bugs + +## [0.1.0] - 2022-01-14 + +First release 🎉 + +[5.2.1]: https://github.com/isunjn/serene/compare/v5.2.0...v5.2.1 +[5.2.0]: https://github.com/isunjn/serene/compare/v5.1.0...v5.2.0 +[5.1.0]: https://github.com/isunjn/serene/compare/v5.0.1...v5.1.0 +[5.0.1]: https://github.com/isunjn/serene/compare/v5.0.0...v5.0.1 +[5.0.0]: https://github.com/isunjn/serene/compare/v4.5.0...v5.0.0 +[4.5.0]: https://github.com/isunjn/serene/compare/v4.4.0...v4.5.0 +[4.4.0]: https://github.com/isunjn/serene/compare/v4.3.0...v4.4.0 +[4.3.0]: https://github.com/isunjn/serene/compare/v4.2.0...v4.3.0 +[4.2.0]: https://github.com/isunjn/serene/compare/v4.1.0...v4.2.0 +[4.1.0]: https://github.com/isunjn/serene/compare/v4.0.0...v4.1.0 +[4.0.0]: https://github.com/isunjn/serene/compare/v3.4.0...v4.0.0 +[3.4.0]: https://github.com/isunjn/serene/compare/v3.3.1...v3.4.0 +[3.3.1]: https://github.com/isunjn/serene/compare/v3.3.0...v3.3.1 +[3.3.0]: https://github.com/isunjn/serene/compare/v3.2.0...v3.3.0 +[3.2.0]: https://github.com/isunjn/serene/compare/v3.1.0...v3.2.0 +[3.1.0]: https://github.com/isunjn/serene/compare/v3.0.0...v3.1.0 +[3.0.0]: https://github.com/isunjn/serene/compare/v2.3.0...v3.0.0 +[2.3.0]: https://github.com/isunjn/serene/compare/v2.2.1...v2.3.0 +[2.2.1]: https://github.com/isunjn/serene/compare/v2.2.0...v2.2.1 +[2.2.0]: https://github.com/isunjn/serene/compare/v2.1.2...v2.2.0 +[2.1.2]: https://github.com/isunjn/serene/compare/v2.1.1...v2.1.2 +[2.1.1]: https://github.com/isunjn/serene/compare/v2.0.1...v2.1.1 +[2.0.1]: https://github.com/isunjn/serene/compare/v2.0.0...v2.0.1 +[2.0.0]: https://github.com/isunjn/serene/compare/v1.2.0...v2.0.0 +[1.2.0]: https://github.com/isunjn/serene/compare/v1.1.1...v1.2.0 +[1.1.1]: https://github.com/isunjn/serene/compare/v1.1.0...v1.1.1 +[1.1.0]: https://github.com/isunjn/serene/compare/v1.0.0...v1.1.0 +[1.0.0]: https://github.com/isunjn/serene/compare/v0.2.0...v1.0.0 +[0.2.0]: https://github.com/isunjn/serene/compare/v0.1.0...v0.2.0 +[0.1.0]: https://github.com/isunjn/serene/releases/tag/v0.1.0 diff --git a/src/themes/serene/LICENSE b/src/themes/serene/LICENSE new file mode 100644 index 0000000..23f481c --- /dev/null +++ b/src/themes/serene/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021-present isunjn + +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. \ No newline at end of file diff --git a/src/themes/serene/README.md b/src/themes/serene/README.md new file mode 100644 index 0000000..4fad7a7 --- /dev/null +++ b/src/themes/serene/README.md @@ -0,0 +1,10 @@ +<p align="center"> + A minimal blog theme for <a href="https://www.getzola.org">zola</a>, well crafted +</p> + +<p align="center"> + <a href="https://serene-demo.pages.dev">Demo</a> · + <a href="https://github.com/isunjn/serene/blob/latest/USAGE.md">Usage</a> +</p> + +<img alt="Screenshot" src="https://github.com/user-attachments/assets/fa0c9529-f83b-4e4d-aed9-5327a44bc828" /> diff --git a/src/themes/serene/USAGE.md b/src/themes/serene/USAGE.md new file mode 100644 index 0000000..1b91d88 --- /dev/null +++ b/src/themes/serene/USAGE.md @@ -0,0 +1,536 @@ +This is the detailed guide on how to use zola-theme-serene. You should also check zola's [documentation](https://www.getzola.org/documentation/getting-started/overview/). + +## Installation + +Create a zola site and add serene theme (assuming your site is called `myblog`): + +```sh +zola init myblog +cd myblog +git init +git submodule add -b latest https://github.com/isunjn/serene.git themes/serene +``` + +Copy the content of `myblog/themes/serene/config.example.toml` to `myblog/config.toml`. + +## Sections and Pages + +There is a `sections` config option in your `config.toml`, which enumerates the sections your site has. You should have at least one `blog` section. + +The name and path can be changed, be noticed that if you changed the blog section path (e.g. from `/posts` to `/blog`), then you should also change `blog_section_path` option. + +For home page, create `myblog/content/_index.md`: + +``` ++++ +template = 'home.html' + +[extra] +lang = 'en' + +# Show footer in home page +footer = false + +# If you don't want to display id/bio/avatar, simply comment out that line +name = "Serene Demo" +id = "serene" +bio = "programmer, he/him, we are young and life is fun" +avatar = "img/avatar.webp" +links = [ + { name = "GitHub", icon = "github", url = "https://github.com/<your-username>" }, + { name = "Twitter", icon = "twitter", url = "https://twitter.com/<your-username>" }, + { name = "Email", icon = "email", url = "mailto:<your-email-address>" }, +] + +# Show a few recent posts in home page +recent = false +recent_max = 15 +recent_more_text = "more »" +date_format = "%b %-d, %Y" ++++ + +Hi, I'm ... +``` + +For blog section, create `myblog/content/posts/_index.md`: + +``` ++++ +title = "My Blog" +description = "My blog site." +sort_by = "date" +template = "blog.html" +page_template = "post.html" +insert_anchor_links = "right" +generate_feeds = true + +[extra] +lang = "en" + +title = "Posts" +subtitle = "I write about ...." + +date_format = "%b %-d, %Y" + +categorized = false # posts can be categorized +back_to_top = true # show back-to-top button +toc = true # show table-of-contents +comment = false # enable comment +copy = true # show copy button in code block + +outdate_alert = false +outdate_alert_days = 12 +outdate_alert_text_before = "This article was last updated " +outdate_alert_text_after = " days ago and may be out of date." ++++ +``` + +Blog section is defined by `blog.html` and `post.html`. Serene also has a special template called `prose.html`, it applies the same styles of blog post page. You can use it as a section template for a custom section page, for example if you want a separate `about` page, you can add a `{ name = "about", path = "/about", is_external = false }` to the `sections` and create a `myblog/content/about/_index.md`: + +``` ++++ +title = "About me" +description = "About page of ..." +template = "prose.html" +insert_anchor_links = "none" + +[extra] +lang = 'en' + +title = "Posts" +subtitle = "I write about ...." + +math = false +mermaid = false +copy = false +comment = false +reaction = false ++++ + +Hi, My name is .... +``` + +The default date format is "%b %-d, %Y", e.g. "Dec 13, 2025", check [this page](https://docs.rs/chrono/0.4.40/chrono/format/strftime/index.html) if you want to customize it, for example change to "%Y-%-m-%-d", e.g. "2025-2-13". + +Now the myblog directory may looks like this: + +``` +├── config.toml +├── content/ +│ ├── posts/ +│ │ └── _index.md +│ ├── about/ +│ │ └── _index.md +│ └── _index.md +├── sass/ +├── static/ +├── templates/ +└── themes/ + └── serene/ +``` + +## Favicon + +Create a new directory `img` under `myblog/static`, put favicon related files here, you can use tools like [favicon.io](https://favicon.io/favicon-converter/) to generate those files. If you want to display avatar in home page, also put your avatar picture file `avatar.webp` here, webp format is recommended. + +``` +... +├── static/ +│ └── img/ +│ ├── favicon-16x16.png +│ ├── favicon-32x32.png +│ ├── apple-touch-icon.png +│ └── avatar.webp +... +``` + +## Icon + +The default icons are placed in `myblog/themes/serene/static/icon`, the `icon` value in `links` of home section is the file name of the svg file. + +To customize, find the svg file you want, modify (in case you don't kown, a svg file is just a plian text file) its width and height to `18`, and the color to `currentColor`: + +`... width="18" height="18" ... fill="currentColor" ...` + +and then put it in `myblog/static/icon`, file in this folder with the same name will override the default one. + +The default icons mostly came from [Remix Icon](https://remixicon.com/). + +## Theme + +By default there is theme toggle button to switch between light and dark mode, you can set `force_theme` in `config.toml` to force a specific mode only. + +## Code Highlighting + +Copy `myblog/themes/serene/highlight_themes` directory to `myblog/highlight_themes`. + +By default serene use different highlight themes for light/dark mode, configured by `highlight_theme`, `extra_syntaxes_and_themes` and `highlight_themes_css`. The default highlight theme `serene-light` `serene-dark` is a modified version of [Tomorrow](https://github.com/ChrisKempson/Tomorrow-Theme) theme. + +If you set `highlight_theme` in `config.toml` to one of zola's [built-in highlight themes](https://www.getzola.org/documentation/getting-started/configuration/#syntax-highlighting), you will get that theme used in both light and dark mode. + +If you want a different theme, find the `.tmTheme` TextMate file of your theme, put it in `myblog/static/highlight_themes`, and then modify the `theme` value of `highlight_themes_css` to that file's name. This will generate a `hl-light.css` and a `hl-dark.css` file in `myblog/static/`, you may have to delete them first before you change the `theme` value, so zola can re-generate. You can find some TextMate themes on [this website](https://tmtheme-editor.glitch.me/). + +## RSS + +Zola's default feed file is located in the root directory of the site, set `generate_feeds = true` in `config.toml`, `feed_filenames` can be set to `["atom.xml"]` or `["rss.xml"] ` , corresponding to two different rss file standards, you should also set `generate_feeds = false` in `myblog/content/posts/_index.md` + +The serene theme looks more like a personal website, the posts are in the `/posts` directory, you may want the feed file to be in the `/posts` directory instead of the root directory, this requires you to set `generate_feeds = false ` `feed_filenames = ["feed.xml"]` in `config.toml`, and set `generate_feeds = true` in `myblog/content/posts/_index.md`. + +`feed.xml` uses `title` and `description` from `myblog/content/posts/_index.md`, the other two use `config.toml`'s. + +## Analytics + +To add scripts for analytics tools (such as Google Analytics, Umami, etc.), you can create `myblog/templates/_head_extend.html`. The content of this file will be added to the html head of each page. + +## Custom CSS + +Copy `myblog/themes/serene/templates/_custom_css.html` to `myblog/templates/_custom_css.html`, variables in this file are used to control styles, such as the theme color `--primary-color`, modify them as you want. + +If you want to customize more, you need to copy that file under the `templates`, `static`, `sass` directory in the corresponding `themes/serene` to the same name directory of `myblog`, and modify it. Be careful not to directly modify the files under the serene directory, because these modifications may cause conflicts if the theme is updated. + +If you want to use a custom font, create a new `myblog/templates/_custom_font.html` and put the font link tags (for eample, from [google fonts](https://fonts.google.com/)) into it, and then modify `--main-font` or `--code-font` in `myblog/sass/templates/_custom_css.html`. For performance reason, you may want to self-host font files, but it's optional: + +1. Open [google-webfonts-helper](https://gwfh.mranftl.com) and choose your font. +2. Modify `Customize folder prefix` of step 3 to `/font/` and then copy the css. +3. Replace the content of `myblog/templates/_custom_font.html` with a `<style> </style>` tag, with the css you just copied. +4. Download step 4 font files and put them in `myblog/static/font/` folder. + +## Front Matter + +The content inside the two `+++` at the top of the post markdown file is called front matter, used to provide metadata and config options of that post. Supported options: + +```md ++++ +title = "" +description = "" +date = 2022-01-01 +updated = 2025-01-01 +draft = true + +[taxonomies] +categories = ["one"] +tags = ["one", "two", "three"] + +[extra] +lang = "en" +toc = true +comment = false +copy = true +outdate_alert = true +outdate_alert_days = 120 +math = false +mermaid = false +featured = false +reaction = false ++++ + +new post about something... +``` + +Some of these options can also be configured in `myblog/content/posts/_index.md`, as the default value for all posts. + +If you set `blog_categorized = true`, posts will be sorted alphabetically by default, you can manually set the order by adding a prefix `__[0-9]{2}__` in front of the category name, for example, `categories = ["__01__CatXXX"]` + +## Table of Contents + +Set `toc = true` to display of table-of-contents. + +## Math & Chart + +Set `math = true` to enable formula rendering with KaTeX. + +Set `mermaid = true` to enable chart rendering with Mermaid. + +## Featured Mark + +Set `featured = true` to display an asterisk(*) mark in front of the title. + +## Outdate Alert + +If one of your posts has strong timeliness, you can display an outdate alert after certain days. + +Set `outdate_alert` and `outdate_alert_days` to enable the alert. + +In `myblog/content/posts/_index.md`, options `outdate_alert_text_before` and `outdate_alert_text_after` are the text content of the alert. + +## Comment + +You can use [giscus](https://giscus.app) as the comment system. + +To enable it, you need to create `myblog/templates/_giscus_script.html` and put the script configured on the giscus website into it, then change the value of `data-theme` to `https://<your-domain-name>/giscus_light.css`, replace `<your-domain-name>` with you domain name, same as `base_url` in `config.toml`, if you set `force_theme` to `dark`, replace `giscus_light.css` with `giscus_dark.css`. + +Then set `comment = true` to enable comment. + +## Reaction + +This theme supports a feature called anonymous emoji reaction, visitors of you site can react to your post with emojis, without the need to log in or register. + +You need to setup a backend api endpoint to enable it. Your endpoint should handle both `GET` and `POST` request: + +- `GET` + + Request query: + + `slug`: the slug of the post + + Response: + + ```jsonc + { + "👍": [123, true], // emoji: [count, reacted] + "👀": [456, false] + } + ``` + +- `POST` + + Request body: + + ```json + { + "slug": "post-slug", + "target": "👍", + "reacted": true + } + ``` + + Response: + + ```json + { + "success": true + } + ``` + +For conivence, you can use one template repo to to setup your own endpoint: + +- [isunjn/reaction](https://github.com/isunjn/reaction): All you need is a [Cloudflare](https://cloudflare.com) account. The free tier is good enough for a low-traffic personal blog. + +- [mildronize/reaction](https://github.com/mildronize/reaction): Specific to [Azure](https://azure.microsoft.com/) platform. +- [sorokya/reaction](https://github.com/sorokya/reaction): A self-contained one, you can run it with docker. + +After you setup your endpoint, set `reaction_endpoint = "<your-endpoint>"` and `reaction = true` to enable it. + +Giscus also support a reaction feature, but it requires visitors to log in to GitHub, you can disable it in giscus's settings. + +## Codeblock + +Zola supports some [annotations for code blocks](https://www.getzola.org/documentation/content/syntax-highlighting/#annotations). + +## Shortcodes + +[Shortcodes](https://www.getzola.org/documentation/content/shortcodes/) are some special templates. + +- Use `figure` to add caption to the image: + + ```md + {{ figure(src="/path/to/img", alt="alt text", caption="caption text") }} + ``` + + Adding attribution information to an image is very common, you can directly use the `via` attribute, which will display a link named 'via' below the image: + + ```md + {{ figure(src="/path/to/img", alt="some alt text", via="https://example.com") }} + ``` + +- Use `quote` to display a special quote block, `cite` is optional: + + ```md + {% quote(cite="") %} + // content... + {% end %} + ``` + +- Use `detail` to add an expandable detail block, `default_open` is optional: + + ```md + {% detail(title="", default_open=false) %} + // content... + {% end %} + ``` + +- As you can see in [this page](https://serene-demo.pages.dev/posts/callouts) of the demo site, callouts are special blockquote blocks, just like [github's](https://github.com/orgs/community/discussions/16925). There are currently 5 types: `note` `tip` `important` `warning` `caution`. + + `title` is optional: + + ```md + {% note(title="Note") %} + note text + {% end %} + ``` + +- Use `mermaid` to add a mermaid chart: + + ```md + {% mermaid() %} + flowchart LR + A[Hard] -->|Text| B(Round) + B --> C{Decision} + C -->|One| D[Result 1] + C -->|Two| E[Result 2] + {% end %} + ``` + +## Collection + +This theme has several special shortcodes for creating a collection of items. These collections can be used to showcase various types of your list, such as projects, publications, blogroll, bookmarks, etc. Check [this page](http://serene-demo.pages.dev/collections) on demo site to see some examples. + +Currently, there are 7 types of collection item: + +- `card` + + ```toml + [[collection]] + type = "card" + title = "Title" + subtitle = "Subtitle" # optional + date = "Date" # optional + link = "https://example.com" # optional + icon = "https://example.com/image.png" # optional + content = "Content" + tags = ["tag1", "tag2"] # optional + featured = false # optional + ``` + +- `card_simple` + + ```toml + [[collection]] + type = "card_simple" + title = "Title" + date = "Date" # optional + link = "https://example.com" # optional + icon = "https://example.com/image.png" # optional + content = "Content" + featured = false # optional + ``` + +- `entry` + + ```toml + [[collection]] + type = "entry" + title = "Title" # optional + subtitle = "Subtitle" # optional + link = "https://example.com" # optional + icon = "https://example.com/image.png" # optional + ``` + +- `box` + + ```toml + [[collection]] + type = "box" + title = "Title" + subtitle = "Subtitle" # optional + link = "https://example.com" # optional + img = "https://example.com/image.png" # optional + ``` + +- `art` + + ```toml + [[collection]] + type = "art" + title = "Title" + subtitle = "Subtitle" # optional + link = "https://example.com" # optional + img = "https://example.com/image.png" + content = "Content" # optional + footer = "Footer" # optional + ``` + +- `art_simple` + + ```toml + [[collection]] + type = "art_simple" + title = "Title" + subtitle = "Subtitle" # optional + link = "https://example.com" # optional + img = "https://example.com/image.png" + ``` + + +List your items in a toml file and then use a `collection` shortcode to render them. + +For example, to create a "projects" section page: + +1. Create `myblog/content/projects/projects.toml`: + + ```toml + [[collection]] + type = "card" + title = "Tokio" + link = "https://example.com" + content = "Tokio is an asynchronous runtime for the Rust programming language. It provides the building blocks needed for writing network applications. It gives the flexibility to target a wide range of systems, from large servers with dozens of cores to small embedded devices." + tags = ["rust", "async", "runtime"] + + [[collection]] + type = "card" + title = "Kubernetes" + link = "https://example.com" + content = "Kubernetes, also known as K8s, is an open source system for managing containerized applications across multiple hosts. It provides basic mechanisms for the deployment, maintenance, and scaling of applications." + tags = ["k8s", "golang"] + + [[collection]] + type = "card" + title = "Next.js" + link = "https://example.com" + content = "Next.js is a React framework for building full-stack web applications. You use React Components to build user interfaces, and Next.js for additional features and optimizations." + tags = ["typescript", "react", "frontend"] + + ``` + +2. Create `myblog/content/projects/_index.md`: + + ``` + +++ + title = "My projects" + description = "Projects page of ..." + template = "prose.html" + + [extra] + title = "Projects" + subtitle = "Some cool projects I made" + +++ + + {{ collection(file="projects.toml") }} + ``` + +3. Add projects section in `sections` of `config.toml` + + ```toml + sections = [ + # ... + { name = "projects", path = "/projects", is_external = false }, + ] + ``` + +## Build & Deploy + +Local preview: + +```sh +zola serve +``` + +Build the site: + +```sh +zola build +``` + +To deploy a static site, refer to zola's [documentation about deployment](https://www.getzola.org/documentation/deployment/overview/). + +## Update + +Check the [CHANGELOG.md](https://github.com/isunjn/serene/blob/main/CHANGELOG.md) on github for breaking changes before you update. + +If you copied some files from `myblog/themes/serene` to `myblog/` for customization, such as `_custom_css.html` or `main.scss`, then you should record what you have modified before you update, re-copy those files and re-apply your modification after updating. The `config.toml` should be re-copied too. + +You can watch (`watch > custom > releases > apply`) this project on github to be reminded of a new release. + +```sh +git submodule update --remote themes/serene +``` diff --git a/src/themes/serene/config.example.toml b/src/themes/serene/config.example.toml new file mode 100644 index 0000000..e35d6c4 --- /dev/null +++ b/src/themes/serene/config.example.toml @@ -0,0 +1,62 @@ +# serene v5.2.1 +# +# - docs: https://github.com/isunjn/serene/blob/latest/USAGE.md +# - check for updates: https://github.com/isunjn/serene/releases +# +#========================================================================================= + +base_url = "https://example.com" +title = "xxxx" +description = "xxxx xxxx xxxx" +default_language = "en" +theme = "serene" +output_dir = "public" +compile_sass = true +minify_html = false # Keep this false, as it may cause issues with some styles +build_search_index = false # Keep this false, search is temporarily unsupported +generate_feeds = false # Whether to generate a feed file in root, read docs for more info about rss feed +feed_filenames = ["feed.xml"] # "feed.xml" | "atom.xml" | "rss.xml", read docs for more info +taxonomies = [{ name = "tags" }, { name = "categories" }] + +[markdown] +highlight_code = true +highlight_theme = "css" +extra_syntaxes_and_themes = ["highlight_themes"] +highlight_themes_css = [ + { theme = "serene-light", filename = "hl-light.css" }, + { theme = "serene-dark", filename = "hl-dark.css" }, +] +render_emoji = false +external_links_target_blank = false +external_links_no_follow = true +external_links_no_referrer = true +smart_punctuation = false + +[slugify] +paths = "on" +taxonomies = "on" +anchors = "on" + +#========================================================================================= + +[extra] + +sections = [ + { name = "posts", path = "/posts", is_external = false }, + # { name = "tags", path = "/tags", is_external = false }, + # { name = "github", path = "https://github.com/<your-username>", is_external = true }, +] +blog_section_path = "/posts" + +back_link_text = "Back" # Text of the back button +force_theme = false # false | "light" | "dark" + +footer_copyright = "© 2025 <your-name>" +footer_credits = true # Whether to show "Built with zola and serene" in footer + +not_found_error_text = "404 Not Found" +not_found_recover_text = "« back to home »" + +reaction = false # Whether to enable anonymous emoji reactions (Note: You need to set up a working api endpoint to enable this feature) +reaction_align = "right" # "left" | "center" | "right" +reaction_endpoint = "https://example.com/api/reaction" diff --git a/src/themes/serene/highlight_themes/serene-dark.tmTheme b/src/themes/serene/highlight_themes/serene-dark.tmTheme new file mode 100644 index 0000000..f4fd49c --- /dev/null +++ b/src/themes/serene/highlight_themes/serene-dark.tmTheme @@ -0,0 +1,283 @@ +<?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>comment</key> + <string>http://chriskempson.com</string> + <key>name</key> + <string>Tomorrow Night</string> + <key>settings</key> + <array> + <dict> + <key>settings</key> + <dict> + <key>caret</key> + <string>#AEAFAD</string> + <key>foreground</key> + <string>#C5C8C6</string> + <key>invisibles</key> + <string>#4B4E55</string> + <key>lineHighlight</key> + <string>#282A2E</string> + <key>selection</key> + <string>#373B41</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Comment</string> + <key>scope</key> + <string>comment, string.quoted.double.block.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#999999</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Foreground</string> + <key>scope</key> + <string>keyword.operator.class, constant.other, source.php.embedded.line</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CED1CF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Variable, String Link, Regular Expression, Tag Name</string> + <key>scope</key> + <string>variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#A67878</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Number, Constant, Function Argument, Tag Attribute, Embedded</string> + <key>scope</key> + <string>constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#E08355</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Class, Support</string> + <key>scope</key> + <string>type, +entity.name.class, entity.name.type.class, support.type, support.class</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#83aaa5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>String, Symbols, Inherited Class, Markup Heading</string> + <key>scope</key> + <string>string, constant.other.symbol, entity.other.inherited-class, markup.heading</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#85AD74</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Operator, Misc</string> + <key>scope</key> + <string>keyword.operator, constant.other.color</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#83aaa5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Function, Special Method, Block Level</string> + <key>scope</key> + <string>entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#81A2BE</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Keyword, Storage</string> + <key>scope</key> + <string>keyword, storage, storage.type, entity.name.tag.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B294BB</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Invalid</string> + <key>scope</key> + <string>invalid</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#DF5F5F</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CED2CF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Separator</string> + <key>scope</key> + <string>meta.separator</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#82A3BF</string> + <key>foreground</key> + <string>#CED2CF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Deprecated</string> + <key>scope</key> + <string>invalid.deprecated</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#B798BF</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CED2CF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff foreground</string> + <key>scope</key> + <string>markup.inserted.diff, markup.deleted.diff, meta.diff.header.to-file, meta.diff.header.from-file</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#FFFFFF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff insertion</string> + <key>scope</key> + <string>markup.inserted.diff, meta.diff.header.to-file</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#4baf5c</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff deletion</string> + <key>scope</key> + <string>markup.deleted.diff, meta.diff.header.from-file</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#D46565</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff header</string> + <key>scope</key> + <string>meta.diff.header.from-file, meta.diff.header.to-file</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#4271ae</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff range</string> + <key>scope</key> + <string>meta.diff.range</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#3e999f</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff.deleted</string> + <key>scope</key> + <string>markup.deleted</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#F92672</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff.inserted</string> + <key>scope</key> + <string>markup.inserted</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#A6E22E</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff.changed</string> + <key>scope</key> + <string>markup.changed</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#967EFB</string> + </dict> + </dict> + </array> + <key>uuid</key> + <string>F96223EB-1A60-4617-92F3-D24D4F13DB09</string> + <key>colorSpaceName</key> + <string>sRGB</string> + <key>semanticClass</key> + <string>theme.dark.tomorrow_night</string> +</dict> +</plist> \ No newline at end of file diff --git a/src/themes/serene/highlight_themes/serene-light.tmTheme b/src/themes/serene/highlight_themes/serene-light.tmTheme new file mode 100644 index 0000000..c4b8d1b --- /dev/null +++ b/src/themes/serene/highlight_themes/serene-light.tmTheme @@ -0,0 +1,239 @@ +<?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>comment</key> + <string>http://chriskempson.com</string> + <key>name</key> + <string>Tomorrow</string> + <key>settings</key> + <array> + <dict> + <key>settings</key> + <dict> + <key>caret</key> + <string>#AEAFAD</string> + <key>foreground</key> + <string>#4D4D4C</string> + <key>invisibles</key> + <string>#D1D1D1</string> + <key>lineHighlight</key> + <string>#EFEFEF</string> + <key>selection</key> + <string>#D6D6D6</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Comment</string> + <key>scope</key> + <string>comment, string.quoted.double.block.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#999999</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Foreground</string> + <key>scope</key> + <string>keyword.operator.class, constant.other, source.php.embedded.line</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#666969</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Variable, String Link, Regular Expression, Tag Name</string> + <key>scope</key> + <string>variable, support.other.variable, string.other.link, string.regexp, entity.name.tag, entity.other.attribute-name, meta.tag, declaration.tag</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#A67878</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Number, Constant, Function Argument, Tag Attribute, Embedded</string> + <key>scope</key> + <string>constant.numeric, constant.language, support.constant, constant.character, variable.parameter, punctuation.section.embedded, keyword.other.unit</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#E08355</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Type, Class, Support</string> + <key>scope</key> + <string>type, +entity.name.class, entity.name.type.class, support.type, support.class</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#568A8F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>String, Symbols, Inherited Class, Markup Heading</string> + <key>scope</key> + <string>string, constant.other.symbol, entity.other.inherited-class, markup.heading</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#85AD74</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Operator, Misc</string> + <key>scope</key> + <string>keyword.operator, constant.other.color</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#568A8F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Function, Special Method, Block Level</string> + <key>scope</key> + <string>entity.name.function, meta.function-call, support.function, keyword.other.special-method, meta.block-level</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#4271AE</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Keyword, Storage</string> + <key>scope</key> + <string>keyword, storage, storage.type</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#8959A8</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Invalid</string> + <key>scope</key> + <string>invalid</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#DF5F5F</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#FFFFFF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Separator</string> + <key>scope</key> + <string>meta.separator</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#4271AE</string> + <key>foreground</key> + <string>#FFFFFF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Deprecated</string> + <key>scope</key> + <string>invalid.deprecated</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#8959A8</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#FFFFFF</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff insertion</string> + <key>scope</key> + <string>markup.inserted.diff, meta.diff.header.to-file</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#229545</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff deletion</string> + <key>scope</key> + <string>markup.deleted.diff, meta.diff.header.from-file</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#C8282966</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff header</string> + <key>scope</key> + <string>meta.diff.header.from-file, meta.diff.header.to-file</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#4271ae</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Diff range</string> + <key>scope</key> + <string>meta.diff.range</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#3e999f</string> + </dict> + </dict> + </array> + <key>uuid</key> + <string>82CCD69C-F1B1-4529-B39E-780F91F07604</string> + <key>colorSpaceName</key> + <string>sRGB</string> + <key>semanticClass</key> + <string>theme.light.tomorrow</string> +</dict> +</plist> \ No newline at end of file diff --git a/src/themes/serene/sass/giscus_dark.scss b/src/themes/serene/sass/giscus_dark.scss new file mode 100644 index 0000000..ea0572f --- /dev/null +++ b/src/themes/serene/sass/giscus_dark.scss @@ -0,0 +1,100 @@ +main { + --primary-default: 88, 113, 162; + --bg-default: 22, 22, 24; + --color-prettylights-syntax-comment: #8b949e; + --color-prettylights-syntax-constant: #79c0ff; + --color-prettylights-syntax-entity: #d2a8ff; + --color-prettylights-syntax-storage-modifier-import: #c9d1d9; + --color-prettylights-syntax-entity-tag: #7ee787; + --color-prettylights-syntax-keyword: #ff7b72; + --color-prettylights-syntax-string: #a5d6ff; + --color-prettylights-syntax-variable: #ffa657; + --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; + --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; + --color-prettylights-syntax-invalid-illegal-bg: #8e1519; + --color-prettylights-syntax-carriage-return-text: #f0f6fc; + --color-prettylights-syntax-carriage-return-bg: #b62324; + --color-prettylights-syntax-string-regexp: #7ee787; + --color-prettylights-syntax-markup-list: #f2cc60; + --color-prettylights-syntax-markup-heading: #1f6feb; + --color-prettylights-syntax-markup-italic: #c9d1d9; + --color-prettylights-syntax-markup-bold: #c9d1d9; + --color-prettylights-syntax-markup-deleted-text: #ffdcd7; + --color-prettylights-syntax-markup-deleted-bg: #67060c; + --color-prettylights-syntax-markup-inserted-text: #aff5b4; + --color-prettylights-syntax-markup-inserted-bg: #033a16; + --color-prettylights-syntax-markup-changed-text: #ffdfb6; + --color-prettylights-syntax-markup-changed-bg: #5a1e02; + --color-prettylights-syntax-markup-ignored-text: #c9d1d9; + --color-prettylights-syntax-markup-ignored-bg: #1158c7; + --color-prettylights-syntax-meta-diff-range: #d2a8ff; + --color-prettylights-syntax-brackethighlighter-angle: #8b949e; + --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; + --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; + --color-btn-text: rgb(235 235 245 / 86%); + --color-btn-bg: rgba(var(--bg-default), 1); + --color-btn-border: rgba(var(--bg-default), 1); + --color-btn-shadow: 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-inset-shadow: inset 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-hover-bg: rgba(var(--bg-default), 0.5); + --color-btn-hover-border: rgba(var(--bg-default), 0.5); + --color-btn-active-bg: rgba(var(--primary-default), 0.2); + --color-btn-active-border: rgba(var(--primary-default), 1); + --color-btn-selected-bg: rgba(var(--primary-default), 0.15); + --color-btn-primary-text: rgb(255 255 255 / 100%); + --color-btn-primary-bg: rgba(var(--primary-default), 0.45); + --color-btn-primary-border: rgba(var(--primary-default), 0.5); + --color-btn-primary-shadow: 0 1px 0 rgb(27 31 36 / 10%); + --color-btn-primary-inset-shadow: inset 0 1px 0 hsl(0deg 0% 100% / 3%); + --color-btn-primary-hover-bg: rgba(var(--primary-default), 0.53); + --color-btn-primary-hover-border: rgba(var(--primary-default), 0.75); + --color-btn-primary-selected-bg: rgba(var(--primary-default), 0.45); + --color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%); + --color-btn-primary-disabled-text: rgb(255 255 255 / 80%); + --color-btn-primary-disabled-bg: rgba(var(--primary-default), 0.5); + --color-btn-primary-disabled-border: rgba(var(--primary-default), 0.5); + --color-action-list-item-default-hover-bg: rgb(177 186 196 / 12%); + --color-segmented-control-bg: rgb(110 118 129 / 10%); + --color-segmented-control-button-bg: #0d1117; + --color-segmented-control-button-selected-border: rgba(var(--bg-default), 0.85); + --color-fg-default: rgb(235 235 245 / 86%); + --color-fg-muted: rgb(235 235 245 / 60%); + --color-fg-subtle: rgb(235 235 245 / 50%); + --color-canvas-default: rgb(30 30 32 / 100%); + --color-canvas-overlay: rgb(30 30 32 / 100%); + --color-canvas-inset: rgba(var(--bg-default), 0.85); + --color-canvas-subtle: rgba(var(--bg-default), 1); + --color-border-default: rgba(var(--bg-default), 0.85); + --color-border-muted: rgb(175 184 193 / 20%); + --color-neutral-muted: rgb(175 184 193 / 20%); + --color-accent-fg: rgba(var(--primary-default), 0.85); + --color-accent-emphasis: rgba(var(--primary-default), 0.95); + --color-accent-muted: rgba(var(--primary-default), 0.4); + --color-accent-subtle: rgba(var(--primary-default), 0.1); + --color-success-fg: #3fb950; + --color-attention-fg: #d29922; + --color-attention-muted: rgb(187 128 9 / 40%); + --color-attention-subtle: rgb(187 128 9 / 15%); + --color-danger-fg: #f85149; + --color-danger-muted: rgb(248 81 73 / 40%); + --color-danger-subtle: rgb(248 81 73 / 10%); + --color-primer-shadow-inset: 0 1px 0 rgba(var(--bg-default), 1), inset 0 1px 0 rgba(var(--bg-default), 1); + --color-scale-gray-7: rgb(22 22 24 / 100%); + --color-scale-blue-8: rgb(16 185 129 / 15%); + + /*! Extensions from @primer/css/alerts/flash.scss */ + --color-social-reaction-bg-hover: var(--color-scale-gray-7); + --color-social-reaction-bg-reacted-hover: var(--color-scale-blue-8); +} + +main .pagination-loader-container { + background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line-dark.svg"); +} + +main .gsc-loading-image { + background-image: url("https://github.githubassets.com/images/mona-loading-dark.gif"); +} + +.gsc-comment-box-buttons a { + border-radius: 0.25rem !important; +} diff --git a/src/themes/serene/sass/giscus_light.scss b/src/themes/serene/sass/giscus_light.scss new file mode 100644 index 0000000..9433d60 --- /dev/null +++ b/src/themes/serene/sass/giscus_light.scss @@ -0,0 +1,104 @@ +main { + --primary-default: 88, 113, 162; + --bg-default: 246, 246, 247; + --color-prettylights-syntax-comment: #6e7781; + --color-prettylights-syntax-constant: #0550ae; + --color-prettylights-syntax-entity: #8250df; + --color-prettylights-syntax-storage-modifier-import: #24292f; + --color-prettylights-syntax-entity-tag: #116329; + --color-prettylights-syntax-keyword: #cf222e; + --color-prettylights-syntax-string: #0a3069; + --color-prettylights-syntax-variable: #953800; + --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; + --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; + --color-prettylights-syntax-invalid-illegal-bg: #82071e; + --color-prettylights-syntax-carriage-return-text: #f6f8fa; + --color-prettylights-syntax-carriage-return-bg: #cf222e; + --color-prettylights-syntax-string-regexp: #116329; + --color-prettylights-syntax-markup-list: #3b2300; + --color-prettylights-syntax-markup-heading: #0550ae; + --color-prettylights-syntax-markup-italic: #24292f; + --color-prettylights-syntax-markup-bold: #24292f; + --color-prettylights-syntax-markup-deleted-text: #82071e; + --color-prettylights-syntax-markup-deleted-bg: #ffebe9; + --color-prettylights-syntax-markup-inserted-text: #116329; + --color-prettylights-syntax-markup-inserted-bg: #dafbe1; + --color-prettylights-syntax-markup-changed-text: #953800; + --color-prettylights-syntax-markup-changed-bg: #ffd8b5; + --color-prettylights-syntax-markup-ignored-text: #eaeef2; + --color-prettylights-syntax-markup-ignored-bg: #0550ae; + --color-prettylights-syntax-meta-diff-range: #8250df; + --color-prettylights-syntax-brackethighlighter-angle: #57606a; + --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f; + --color-prettylights-syntax-constant-other-reference-link: #0a3069; + --color-btn-text: #24292f; + --color-btn-bg: rgba(var(--bg-default), 1); + --color-btn-border: rgba(var(--bg-default), 1); + --color-btn-shadow: 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-inset-shadow: inset 0 1px 0 rgba(var(--bg-default), 1); + --color-btn-hover-bg: rgba(var(--bg-default), 0.5); + --color-btn-hover-border: rgba(var(--bg-default), 0.5); + --color-btn-active-bg: rgba(var(--primary-default), 0.2); + --color-btn-active-border: rgba(var(--primary-default), 1); + --color-btn-selected-bg: rgba(var(--primary-default), 0.15); + --color-btn-primary-text: rgb(255 255 255 / 100%); + --color-btn-primary-bg: rgba(var(--primary-default), 1); + --color-btn-primary-border: rgba(var(--primary-default), 1); + --color-btn-primary-shadow: 0 1px 0 rgb(31 35 40 / 10%); + --color-btn-primary-inset-shadow: inset 0 1px 0 rgb(255 255 255 / 3%); + --color-btn-primary-hover-bg: rgba(var(--primary-default), 0.9); + --color-btn-primary-hover-border: rgba(var(--primary-default), 0.75); + --color-btn-primary-selected-bg: rgba(var(--primary-default), 1); + --color-btn-primary-selected-shadow: inset 0 1px 0 rgb(0 45 17 / 20%); + --color-btn-primary-disabled-text: rgb(255 255 255 / 80%); + --color-btn-primary-disabled-bg: rgba(var(--primary-default), 0.5); + --color-btn-primary-disabled-border: rgba(var(--primary-default), 0.5); + --color-action-list-item-default-hover-bg: rgb(208 215 222 / 32%); + --color-segmented-control-bg: #eaeef2; + --color-segmented-control-button-bg: #fff; + --color-segmented-control-button-selected-border: rgba(var(--bg-default), 0.85); + --color-fg-default: rgb(60 60 67); + --color-fg-muted: rgb(60 60 67 / 75%); + --color-fg-subtle: rgb(60 60 67 / 33%); + --color-canvas-default: rgb(255 255 255); + --color-canvas-overlay: rgb(255 255 255); + --color-canvas-inset: rgba(var(--bg-default), 0.85); + --color-canvas-subtle: rgba(var(--bg-default), 1); + --color-border-default: rgba(var(--bg-default), 0.85); + --color-border-muted: rgb(175 184 193 / 20%); + --color-neutral-muted: rgb(175 184 193 / 20%); + --color-accent-fg: rgba(var(--primary-default), 0.85); + --color-accent-emphasis: rgba(var(--primary-default), 0.95); + --color-accent-muted: rgba(var(--primary-default), 0.4); + --color-accent-subtle: rgba(var(--primary-default), 0.1); + --color-success-fg: #1a7f37; + --color-attention-fg: #9a6700; + --color-attention-muted: rgb(212 167 44 / 40%); + --color-attention-subtle: #fff8c5; + --color-danger-fg: #d1242f; + --color-danger-muted: rgb(255 129 130 / 40%); + --color-danger-subtle: #ffebe9; + --color-primer-shadow-inset: 0 1px 0 rgba(var(--bg-default), 1), inset 0 1px 0 rgba(var(--bg-default), 1); + --color-scale-gray-1: rgb(234 238 242 / 100%); + --color-scale-blue-1: rgb(16 185 129 / 15%); + + /*! Extensions from @primer/css/alerts/flash.scss */ + --color-social-reaction-bg-hover: var(--color-scale-gray-1); + --color-social-reaction-bg-reacted-hover: var(--color-scale-blue-1); +} + +main .pagination-loader-container { + background-image: url("https://github.com/images/modules/pulls/progressive-disclosure-line.svg"); +} + +main .gsc-loading-image { + background-image: url("https://github.githubassets.com/images/mona-loading-default.gif"); +} + +.gsc-comment:not(.gsc-reply-box) .gsc-replies { + border-radius: unset; +} + +.gsc-comment-box-buttons a { + border-radius: 0.25rem !important; +} diff --git a/src/themes/serene/sass/main.scss b/src/themes/serene/sass/main.scss new file mode 100644 index 0000000..f5628a2 --- /dev/null +++ b/src/themes/serene/sass/main.scss @@ -0,0 +1,1839 @@ +/* base + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +*, +*::before, +*::after { + box-sizing: border-box; +} + +body { + font-family: var(--main-font); + background-color: var(--bg-color); + color: var(--text-color); + font-size: var(--font-size); + line-height: var(--line-height); + overflow-y: scroll; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +button:focus-visible, +a:focus-visible { + outline: var(--primary-color) solid 3px; +} + +::selection { + background-color: var(--primary-color); + color: var(--bg-color); +} + +::-webkit-scrollbar { + width: 8px; + height: 8px; + background-color: transparent; +} +::-webkit-scrollbar-track { + background: transparent; +} +::-webkit-scrollbar-thumb { + background: var(--text-decoration-color); + background-clip: padding-box; + border-radius: 4px; + border: 2px solid transparent; +} +@supports not selector(::-webkit-scrollbar) { + * { + scrollbar-width: thin; + scrollbar-color: var(--text-decoration-color) transparent; + } + body { + overflow-y: auto; + } +} + +/* prose + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.prose { + h1 { + font-size: 1.15em; + font-weight: bolder; + margin: 1.5em 0 0.75em; + } + + h2 { + font-size: 1.1em; + margin: 3em 0 1.5em; + } + + h3, h4, h5, h6 { + font-size: 1em; + margin: 2.5em 0 1.5em; + } + + .zola-anchor { + visibility: hidden; + margin-left: 0.75em; + border: none; + + &::after { + content: '#'; + } + } + + h1, h2, h3, h4, h5, h6 { + width: fit-content; + &:hover a.zola-anchor { + visibility: visible !important; + } + } + + p { + font-size: 1em; + line-height: inherit; + word-wrap: break-word; + margin: 1.5em 0; + } + + a { + border-bottom: 1.5px solid var(--primary-color); + color: var(--primary-color); + text-decoration: none; + overflow-wrap: anywhere; + + @media (hover: hover) { + &:hover { + opacity: 0.9; + } + } + } + + img { + max-width: 100%; + display: block; + margin: 0 auto; + border-radius: var(--img-border-radius); + } + + figure { + margin: 0 auto; + } + + figcaption { + width: 100%; + text-align: center; + margin: 1em auto 2em; + color: var(--text-pale-color); + font-size: 0.9em; + } + + blockquote { + border-left: 1.5px var(--text-decoration-color) solid; + padding-left: 16px; + margin: 1em 0; + color: var(--text-pale-color); + + p { + margin: 1em 0; + } + } + + ol, + ul { + padding-left: 1.5em; + } + + li { + margin: 1em 0; + p { + margin: 1em 0; + } + } + + li::marker { + color: var(--primary-color); + } + + hr { + border: none; + background-color: var(--text-decoration-color); + opacity: 0.25; + height: 2px; + margin: 3em 0; + } + + table { + width: 100%; + border-spacing: 0; + border-collapse: collapse; + margin: 1.5em 0; + } + + th, + td { + line-height: 2; + text-align: center; + border: 1px solid var(--primary-color); + padding: 1px 10px; + } + + :not(pre) > code { + font-family: var(--code-font); + font-size: 0.85em; + padding: 1px 6px; + color: var(--primary-color); + background-color: var(--inline-code-bg-color); + border-radius: var(--inline-code-border-radius); + word-wrap: break-word; + } + + pre { + font-size: 0.8em; + margin: 1.25em 0; + padding: 12px 48px 12px 16px; + line-height: 1.5; + border: 1.5px solid var(--primary-color); + border-color: var(--block-code-border-color); + border-radius: var(--block-code-border-radius); + overflow: auto; + + code { + font-family: var(--code-font); + } + + &[data-linenos] { + padding-left: 0px; + } + + table { + width: 100%; + margin: 0; + border-collapse: collapse; + border: none; + th, td { + line-height: 1.5; + } + } + + + table tr td:first-of-type { + color: var(--text-decoration-color); + } + + table td { + padding: 0; + padding-right: 48px; + text-align: initial; + border: initial; + } + + table td:nth-of-type(1) { + text-align: right; + user-select: none; + padding-right: 1em; + mark::before { + left: -8px; + width: calc(100% + 1em + 8px); + } + } + + mark { + display: block; + color: inherit; + background-color: transparent; + position: relative; + overflow: visible; + + &::before { + pointer-events: none; + content: ''; + position: absolute; + top: 0; + bottom: 0; + width: calc(100% + 48px + 48px); + background-color: var(--highlight-mark-color); + } + } + + &.mermaid { + padding-right: 16px; + border: none; + svg { + display: block; + margin: 0 auto; + } + } + } + + pre > code > mark::before { + width: calc(100% + 48px + 16px); + left: -16px; + } + + .codeblock { + margin: 1.5em 0; + position: relative; + overflow: auto; + + pre { + margin: 0; + } + pre[data-name] { + padding-top: calc(36px + 1em * var(--line-height)); + &::before { + content: attr(data-name); + display: block; + position: absolute; + left: 0px; + top: 0px; + padding: 12px 16px; + color: var(--text-pale-color); + width: 100%; + border-bottom: 1px solid var(--primary-pale-color); + } + } + + .copy { + display: none; + z-index: 9; + position: absolute; + right: 0.6em; + top: 0.75em; + width: 24px; + height: 24px; + padding: 2px; + cursor: pointer; + background: transparent; + border: none; + color: var(--text-pale-color); + + &.copied, &:hover { + display: block; + color: var(--primary-color); + } + } + + @media (hover: hover) { + &:hover .copy { + display: block; + } + } + } + + .footnote-definition { + position: relative; + padding-left: 1.5em; + font-size: 0.9em; + margin: 3em 0 1em; + + .footnote-definition { + margin: 1em 0; + } + + .footnote-definition-label { + position: absolute; + top: 0; + left: 0; + font-size: 1em; + line-height: inherit; + vertical-align: auto; + &::after { + content: "."; + } + } + + p { + margin: 1em 0; + } + + button.backlink { + border: none; + background: none; + color: var(--primary-color); + line-height: inherit; + &:hover { + cursor: pointer; + text-decoration: underline; + } + } + } + + .callout { + margin: 1.5em 0; + + .icon { + height: 1.75em; + display: flex; + align-items: center; + } + + p { + margin: 0; + + p { + margin: 1em 0; + } + } + + &.has-title { + padding-left: 1em; + .title { + display: flex; + align-items: center; + gap: 0.5em; + margin-bottom: 0.5em; + } + } + + &.no-title { + padding-left: 0; + border: none; + display: flex; + align-items: start; + gap: 0.75em; + .content { + max-width: calc(100% - 30px); + } + } + + &.note { + color: var(--callout-note-color); + border-color: var(--callout-note-color); + } + + &.tip { + color: var(--callout-tip-color); + border-color: var(--callout-tip-color); + } + + &.important { + color: var(--callout-important-color); + border-color: var(--callout-important-color); + } + + &.warning { + color: var(--callout-warning-color); + border-color: var(--callout-warning-color); + } + + &.caution { + color: var(--callout-caution-color); + border-color: var(--callout-caution-color); + } + } + + .quote { + border: none; + position: relative; + margin: 2em 0; + padding: 1.5em 0 0 1.25em; + color: var(--text-pale-color); + + .icon { + color: var(--text-decoration-color); + display: block !important; + position: absolute; + left: 0; + top: 0; + } + + .content > p:first-of-type { + margin-top: 0; + } + .content > p:last-of-type { + margin-bottom: 0; + } + + .from { + display: flex; + justify-content: end; + align-items: center; + gap: 0.5em; + p { + margin: 0; + } + } + } + + details { + border: 1.5px solid var(--detail-border-color); + border-radius: var(--detail-border-radius); + margin: 1em 0; + padding: 0.5em 1em; + -webkit-tap-highlight-color: transparent; + + summary span { + margin-left: 0.25em; + } + } + + .mermaid { + background: #fff; + } +} + +body.dark .prose { + img { + filter: brightness(var(--dark-mode-img-brightness)); + } + .mermaid { + filter: brightness(var(--dark-mode-chart-brightness)); + } +} + +/* prose pages + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.prose-page { + main { + min-height: 100vh; + min-height: 100dvh; + margin: 0 auto; + max-width: var(--main-max-width); + display: flex; + flex-direction: column; + justify-content: space-between; + } + + article { + padding: 0 15px; + } + + .giscus { + padding: 0 15px; + } +} + +/* collections + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.prose { + .collection-wrapper { + margin: 1.5em 0; + } + + .collection { + border: none; + margin: 0; + padding: 0; + color: var(--text-color); + img { + margin: 0; + } + a, button { + -webkit-tap-highlight-color: transparent; + } + } + + .collection.card { + margin: 3em 0; + position: relative; + + &.featured::before { + content: '*'; + position: absolute; + top: 0; + bottom: 0; + left: 0; + transform: translateX(-200%); + line-height: 2; + color: var(--primary-color); + } + @media (max-width: 768px) { + &.featured::before { + transform: translateX(-150%); + } + } + + .meta { + display: flex; + gap: 0.5em; + .icon-wrapper { + height: calc(1em * var(--line-height)); + display: flex; + align-items: center; + flex-shrink: 0; + } + .icon { + height: 16px; + width: 16px; + } + .title { + color: var(--primary-color); + } + a.title { + text-decoration: none; + border-bottom: 1.5px solid transparent; + &:hover { + opacity: unset; + border-color: var(--primary-color); + } + } + .date { + margin-left: auto; + flex-shrink: 0; + font-size: 0.9em; + color: var(--text-pale-color); + } + } + + .subtitle { + font-size: 0.9em; + color: var(--text-pale-color); + margin: 0.75em 0; + } + + .content { + font-size: 1em; + p { + margin: 0.75em 0; + } + } + + .tags { + font-size: 0.9em; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0.75em 1em; + color: var(--text-pale-color); + + div { + span { + font-size: 0.9em; + margin-right: 4px; + } + } + } + } + + .collection.card-simple { + margin: 0.5em 0; + position: relative; + + &.featured::before { + content: '*'; + position: absolute; + top: 0; + bottom: 0; + left: 0; + transform: translateX(-200%); + line-height: 2; + color: var(--primary-color); + } + @media (max-width: 768px) { + &.featured::before { + transform: translateX(-150%); + } + } + + .meta { + display: flex; + align-items: start; + gap: 0.5em; + } + + .icon-wrapper { + height: calc(1em * var(--line-height)); + display: flex; + align-items: center; + flex-shrink: 0; + } + .icon { + height: 16px; + width: 16px; + } + .title { + flex-shrink: 0; + max-width: 100%; + color: var(--primary-color); + margin-right: 0.5em; + } + a.title { + text-decoration: none; + border-bottom: 1.5px solid transparent; + &:hover { + opacity: unset; + border-color: var(--primary-color); + } + } + .content, + .content-narrow { + p { + margin: 0; + + p { + margin-top: 0.5em; + } + } + } + .content-narrow { + display: none; + margin: 0.25em 0 2em; + } + @media (max-width: 425px) { + .content { + display: none; + } + .content-narrow { + display: block; + } + } + .date { + margin-left: auto; + flex-shrink: 0; + font-size: 0.9em; + line-height: calc(var(--line-height) * 1.111); + color: var(--text-pale-color); + } + + } + + .collection.entry { + display: inline-flex; + width: fit-content; + gap: 0.5em; + padding: 4px 0; + margin: 0.25em 1.5em 0.25em 0; + + .icon-wrapper { + flex-shrink: 0; + display: flex; + align-items: center; + height: calc(1em * var(--line-height)); + } + .icon { + width: 16px; + height: 16px; + } + + .text div { + display: inline; + } + .title:has(+.subtitle) { + margin-right: 0.5em; + } + .subtitle { + font-size: 0.9em; + color: var(--text-pale-color); + } + } + a.collection.entry { + -webkit-tap-highlight-color: transparent; + color: var(--text-color); + border-bottom: 1.5px solid transparent; + &:hover { + border-color: var(--primary-color); + opacity: unset; + cursor: pointer; + } + } + + .collection.box { + width: 100%; + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid var(--primary-color); + overflow: hidden; + + img { + width: 48px; + height: 48px; + &.rotate { + transition: transform 0.15s; + transform: rotate(-20deg) translate(-2px, 12px); + } + } + .placehold { + height: 48px; + } + + .text { + min-height: 48px; + line-height: 1.5; + padding: 0 1em; + display: flex; + flex-direction: column; + justify-content: center; + overflow: hidden; + + .title { + font-size: 0.9em; + color: var(--text-color); + } + + .subtitle { + font-size: 0.8em; + color: var(--text-pale-color); + overflow: hidden; + text-wrap: nowrap; + text-overflow: ellipsis; + } + } + + @media (hover: hover) { + &:hover { + opacity: unset; + img.rotate { + transform: rotate(0deg) translate(0px, 0px); + } + } + } + } + a.collection.box { + -webkit-tap-highlight-color: transparent; + } + .collection-box-wrapper { + max-width: 100%; + width: fit-content; + display: inline-flex; + align-items: center; + justify-content: space-between; + border: 0.5px solid transparent; + overflow: hidden; + margin-right: 0.5em; + margin-bottom: 0.8em; + @media (hover: hover) { + &:has(a):hover { + border-color: var(--primary-color); + } + } + @media (max-width: 425px) { + & { + display: flex; + width: 100%; + } + } + } + + .collection.art { + --art-h: 160px; + --art-w: 115px; + + margin: 2em 0; + display: flex; + gap: 1.5em; + + .img-wrapper { + flex-shrink: 0; + width: var(--art-w); + height: var(--art-h); + img { + height: 100%; + width: 100%; + object-fit: contain; + } + } + .text { + height: var(--art-h); + display: flex; + flex-direction: column; + gap: 3px; + } + .title { + color: var(--primary-color); + } + a.title { + width: fit-content; + border-color: transparent; + &:hover { + opacity: unset; + border-color: var(--primary-color); + } + } + .subtitle, + .footer { + font-size: 0.8em; + color: var(--text-pale-color); + } + .content { + font-size: 0.9em; + overflow: hidden; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + } + .footer { + margin-top: auto; + } + + @media (max-width: 425px) { + & { + --art-h: 120px; + --art-w: 85px; + gap: 0.75em; + .text { + gap: 2px; + } + .subtitle, + .footer { + font-size: 0.7em; + } + .content { + font-size: 0.8em; + -webkit-line-clamp: 2; + } + } + } + + } + + + .collection.art-simple { + --art-h: 160px; + --art-w: 115px; + + max-width: calc(50vw - 30px); + margin: 1.5em 0.75em; + display: inline-flex; + flex-direction: column; + align-items: center; + gap: 1em; + + .img-wrapper { + flex-shrink: 0; + width: var(--art-w); + height: var(--art-h); + img { + height: 100%; + width: 100%; + object-fit: contain; + } + } + .text { + max-width: calc(var(--art-w) * 1.5); + display: flex; + flex-direction: column; + align-items: center; + gap: 3px; + } + .title { + font-size: 0.9em; + color: var(--primary-color); + } + a.title { + width: fit-content; + border-color: transparent; + &:hover { + opacity: unset; + border-color: var(--primary-color); + } + } + .subtitle { + font-size: 0.8em; + color: var(--text-pale-color); + } + + @media (max-width: 425px) { + & { + --art-h: 120px; + --art-w: 85px; + width: calc(50vw - 50px); + } + } + + } +} + +/* layout post list ( home / blog / tag ) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.layout-list { + .category { + font-size: 0.9em; + margin: 1em 15px; + font-weight: 400; + } + + .post-list { + padding: 0 15px; + + &.categorized { + margin: 1em 0 3em 0; + } + } + + .post { + display: flex; + justify-content: space-between; + align-items: flex-end; + gap: 0.5em; + padding: 4px 0px; + margin: 6px 0; + line-height: 1.2; + text-decoration: none; + color: var(--primary-color); + + border-bottom: 1.5px solid transparent; + @media (hover: hover) { + &:hover { + border-bottom-color: var(--primary-color); + } + } + + .date { + white-space: nowrap; + } + + position: relative; + &.featured::before { + content: '*'; + position: absolute; + top: 0; + bottom: 0; + left: 0; + transform: translateX(-200%); + line-height: 2; + height: 100%; + } + @media (max-width: 768px) { + &.featured::before { + transform: translateX(-150%); + } + } + } + + .read-more { + display: flex; + justify-content: end; + margin: 1.5em 15px 0px; + + a { + color: var(--primary-color); + text-decoration: none; + border-bottom: 1.5px solid var(--primary-color); + } + } +} + +/* theme toggle + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +#theme-toggle { + .sun-icon { + display: none; + } +} + +body.dark #theme-toggle { + .sun-icon { + display: initial; + } + .moon-icon { + display: none; + } +} + +/* homepage + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.homepage { + #wrapper { + min-height: 100vh; + min-height: 100dvh; + max-width: var(--homepage-max-width); + margin: 0 auto; + display: flex; + flex-direction: column; + justify-content: center; + } + + main { + width: 100%; + min-height: 75vh; + min-height: 75dvh; + padding: 4em 0; + @media (max-width: 425px) { + & { + padding: 2em 0; + } + } + } + + #info { + padding: 0 15px; + display: flex; + align-items: center; + gap: 1em; + + img { + height: var(--avatar-size); + width: var(--avatar-size); + border-radius: 50%; + } + + #text { + display: flex; + flex-direction: column; + justify-content: space-around; + line-height: 1.5; + gap: 1em; + } + + #id { + margin-left: 0.75em; + color: var(--primary-color); + } + + #bio { + color: var(--text-pale-color); + } + } + + #links { + padding: 0 15px; + margin: 2em 0 2.5em; + display: flex; + justify-content: space-between; + align-items: center; + flex-wrap: wrap; + gap: 1em; + + a, + button { + -webkit-tap-highlight-color: transparent; + text-decoration: none; + line-height: 0; + color: var(--text-color); + + &:hover { + color: var(--primary-color); + } + } + + #left { + display: flex; + flex-wrap: wrap; + gap: 1em; + + a { + border-bottom: 1.5px solid var(--primary-color); + line-height: 1.5; + } + } + + #right { + display: flex; + gap: 0.7em; + + button { + padding: 0; + border: none; + background-color: transparent; + cursor: pointer; + } + } + + @media (max-width: 425px) { + #left { + gap: 0.75em; + } + #right { + gap: 0.5em; + } + } + } + + #brief { + padding: 0 15px; + } +} + +/* header + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +header { + font-size: 0.8em; + padding: 0 15px; + a { + -webkit-tap-highlight-color: transparent; + display: inline-block; + margin-top: 1em; + padding: 1em 1em 1em 0; + color: var(--text-pale-color); + text-decoration: none; + &:hover { + color: var(--primary-color); + } + } +} + +.section-title { + padding: 0 15px; + h1 { + font-size: 1.15em; + margin: 1.5em 0 0.75em; + } + p { + margin: 0; + margin-bottom: 1em; + font-size: 0.9em; + color: var(--text-pale-color); + } +} + +/* rss mask + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +dialog#rss-mask { + margin: 0 auto; + padding: 0px; + border: none; + overflow: visible; + background: transparent; + + &::backdrop { + background-color: initial; + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + } + + div { + pointer-events: none; + height: 100vh; + height: 100dvh; + padding: 2em 0; + width: fit-content; + display: flex; + justify-content: center; + align-items: end; + flex-wrap: wrap; + gap: 1em; + font-size: 0.9em; + } + + a { + pointer-events: auto; + border-bottom: 1.5px solid var(--primary-color); + color: var(--primary-color); + text-decoration: none; + line-height: 1.5; + line-break: anywhere; + } + + button { + pointer-events: auto; + background: transparent; + border: none; + color: var(--text-pale-color); + padding: 0; + cursor: pointer; + &.copied, &:hover { + color: var(--primary-color); + } + } +} + +/* footer + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +footer { + font-size: 0.8em; + line-height: 18px; + margin-top: auto; + padding: 15px; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 0 0.5em; + color: var(--text-pale-color); + + .left { + margin: 15px 0; + margin-right: auto; + display: flex; + align-items: center; + span { + padding: 0 0.5em; + } + a { + color: var(--text-pale-color); + text-decoration: none; + text-underline-offset: 4px; + &:hover { + color: var(--text-pale-color); + text-decoration: underline; + } + } + } + + .right { + display: flex; + align-items: center; + gap: 4px; + margin: 11px 0; + + #rss-btn, + #theme-toggle { + -webkit-tap-highlight-color: transparent; + border: none; + background-color: transparent; + text-decoration: none; + cursor: pointer; + color: var(--text-pale-color); + &:hover { + color: var(--primary-color); + } + } + + #rss-btn { + padding: 4px 0; + font-size: 0.85em; + line-height: 18px; + } + #theme-toggle { + padding: 4px; + transform: translateX(6px); + svg { + scale: 0.7; + } + line-height: 0; + } + } +} + +/* blog page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.blog { + #wrapper { + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + } + + main { + margin: 1em 0; + } +} + +/* post page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.post { + #wrapper { + display: flex; + justify-content: space-between; + } + + #blank { + order: 1; + position: sticky; + width: calc((100% - var(--main-max-width)) / 2); + } + + main { + order: 2; + width: 100%; + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + justify-content: space-between; + } + + article { + padding: 0 15px 2em; + + #post-info { + display: flex; + align-items: center; + flex-wrap: wrap; + margin-bottom: 1.5em; + font-size: 0.9em; + } + + #date { + color: var(--text-pale-color); + margin-bottom: 1em; + + #publish, + #updated { + margin-right: 1em; + } + } + + #tags { + margin-bottom: 1em; + display: flex; + gap: 1em; + flex-wrap: wrap; + + a { + color: var(--primary-color); + text-decoration: none; + line-height: 1.25; + span { + font-size: 0.95em; + margin-right: 2px; + } + + border-bottom: 1.5px solid transparent; + &:hover { + opacity: 1; + border-bottom-color: var(--primary-color); + } + } + } + + #outdate_alert { + font-style: italic; + &.hidden { + display: none; + } + } + } + + .mermaid { + background: #fff; + } + + .giscus { + padding: 0 15px; + } + + aside { + order: 3; + width: calc((100% - var(--main-max-width)) / 2); + position: sticky; + margin-top: 13em; + top: 0; + height: min-content; + font-size: 0.9em; + + nav { + padding: 10px 1em 10px 2em; + min-width: 60%; + overflow-y: auto; + max-height: calc(100vh - 6em); + scrollbar-width: none; + + &::-webkit-scrollbar { + width: 0; + } + } + + ul { + list-style-type: none; + padding: 0; + line-height: 2; + margin: 0; + } + + a { + text-decoration: none; + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--text-pale-color); + position: relative; + padding: 0 1em; + + &.h3 { + padding-left: 2em; + } + + &::before { + display: block; + content: ""; + width: 1.5px; + position: absolute; + top: 0.7em; + bottom: 0.7em; + left: 0em; + background: transparent; + } + + &:hover { + color: var(--primary-color); + } + + &:hover::before { + background-color: var(--primary-color); + } + } + + #back-to-top { + line-height: 0; + z-index: 99; + position: fixed; + bottom: 15px; + margin-left: 1.5em; + color: var(--text-pale-color); + background: transparent; + border: none; + cursor: pointer; + padding: 15px; + transform: translateY(-5px) scale(0); + transition: transform 0.15s; + + svg { + scale: 0.8; + } + + &.shown { + transform: translateY(0px) scale(1); + } + + &:hover { + color: var(--primary-color); + } + } + } + + @media (max-width: 1024px) { + aside, #blank { + display: none; + } + } +} + +/* tag list page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.tag-list { + #wrapper { + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + } + + main { + margin: 0 15px; + } + + .title { + font-size: 1.15em; + margin: 1.5em 0 0.75em; + } + + .tags { + margin: 2em 0; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 1em 2em; + + a { + color: var(--primary-color); + text-decoration: none; + line-height: 1.25; + + border-bottom: 1.5px solid transparent; + &:hover { + border-bottom-color: var(--primary-color); + } + } + } + +} + +/* tag single page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.tag-single { + #wrapper { + margin: 0 auto; + max-width: var(--main-max-width); + min-height: 100vh; + min-height: 100dvh; + display: flex; + flex-direction: column; + } + + main { + width: 100%; + margin: 1em 0; + } + + .title { + font-size: 1.1em; + margin: 0.5em 15px 2em; + } +} + +/* 404 page + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +body.not-found { + height: 100vh; + height: 100dvh; + display: flex; + justify-content: center; + align-items: center; + + .wrapper { + display: flex; + flex-direction: column; + align-items: center; + gap: 2em; + margin-bottom: 6em; + } + + .error { + margin: 0; + color: var(--text-pale-color); + } + + a { + color: var(--primary-color); + text-decoration: none; + border-bottom: 1.5px solid transparent; + &:hover { + border-color: var(--primary-color); + } + } +} + +/* reaction + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +.reaction { + font-size: 0.8em; + padding: 15px 15px 30px; + min-height: 75px; + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 8px; + + &.left { + justify-content: start; + } + + &.right { + justify-content: end; + } + + &.center { + justify-content: center; + } + + &.error { + button { + color: var(--callout-caution-color) !important; + } + } + + button { + line-height: 1; + padding: 0.5em; + color: var(--text-pale-color); + display: flex; + justify-content: center; + align-items: center; + border: none; + gap: 4px; + background: transparent; + user-select: none; + cursor: pointer; + + span { + min-width: 16px; + } + + &:hover { + color: var(--primary-color); + } + + &.reacted { + color: var(--primary-color); + } + } +} + + +/* normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ + +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; + text-size-adjust: 100%; +} + +body { + margin: 0; +} + +main { + display: block; +} + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +pre { + font-family: monospace, monospace; + font-size: 1em; +} + +a { + background-color: transparent; +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted; +} + +b, +strong { + font-weight: bolder; +} + +code, +kbd, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +img { + border-style: none; +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; + appearance: button; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +textarea { + overflow: auto; +} + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + padding: 0; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + -webkit-appearance: textfield; + appearance: textfield; + outline-offset: -2px; +} + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +details { + display: block; +} + +summary { + display: list-item; +} + +template { + display: none; +} + +[hidden] { + display: none; +} diff --git a/src/themes/serene/screenshot.png b/src/themes/serene/screenshot.png new file mode 100644 index 0000000..b824cde --- /dev/null +++ b/src/themes/serene/screenshot.png Binary files differdiff --git a/src/themes/serene/static/icon/arrow-up.svg b/src/themes/serene/static/icon/arrow-up.svg new file mode 100644 index 0000000..f2430d4 --- /dev/null +++ b/src/themes/serene/static/icon/arrow-up.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-arrow-up"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg> diff --git a/src/themes/serene/static/icon/bluesky.svg b/src/themes/serene/static/icon/bluesky.svg new file mode 100644 index 0000000..faa3039 --- /dev/null +++ b/src/themes/serene/static/icon/bluesky.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M4.90727 3.43929C5.61594 3.56009 6.44016 3.94139 7.48302 4.68685C9.27977 5.97119 10.7687 7.73907 12 9.56361C13.2313 7.73907 14.7202 5.97119 16.517 4.68685C17.5598 3.94139 18.3841 3.56009 19.0927 3.43929C19.8605 3.3084 20.3825 3.50358 20.7082 3.63931C21.7166 4.05956 22 5.22508 22 6.21461C22 6.41649 21.9144 7.5166 21.8148 8.57508C21.7634 9.12088 21.7057 9.68306 21.6486 10.1515C21.5963 10.5804 21.5337 11.0321 21.4587 11.2849C21.1161 12.4395 20.3965 13.2618 19.508 13.8021C20.4453 14.5092 20.7854 15.6583 20.4359 16.7856C19.8393 18.71 17.6991 21.1833 15.6005 21.4037C13.8281 21.5898 12.6662 20.0794 12 18.6449C11.3338 20.0794 10.1719 21.5898 8.39954 21.4037C6.30095 21.1833 4.1607 18.71 3.56408 16.7856C3.21457 15.6583 3.55466 14.5092 4.49197 13.8021C3.60345 13.2618 2.88394 12.4395 2.54132 11.2849C2.46631 11.0321 2.40367 10.5804 2.35139 10.1515C2.29429 9.68306 2.23658 9.12088 2.18521 8.57508C2.08559 7.5166 2 6.41649 2 6.21461C2 5.22508 2.28343 4.05956 3.29182 3.63931C3.61753 3.50358 4.13949 3.3084 4.90727 3.43929ZM4.04911 6.91709C4.11331 7.73486 4.22889 9.02507 4.33669 9.90947C4.36927 10.1767 4.39214 10.4536 4.45863 10.7156C4.85637 12.056 6.38779 12.7978 8.14506 12.603C8.68686 12.5429 9.17695 12.9278 9.24697 13.4684C9.31698 14.009 8.94113 14.5061 8.40191 14.586C7.64608 14.6981 5.08656 14.9425 5.47438 16.1934C5.8312 17.3443 7.32212 19.2796 8.60842 19.4146C9.53606 19.5121 10.1084 18.0211 10.3741 17.3697C10.6489 16.6958 10.8622 15.9903 11.0417 15.3885C11.1681 14.9648 11.5578 14.6744 12 14.6744C12.4422 14.6744 12.8319 14.9648 12.9583 15.3885C13.1378 15.9903 13.3511 16.6958 13.6259 17.3697C13.8916 18.0211 14.4639 19.5121 15.3916 19.4146C16.6779 19.2796 18.1688 17.3443 18.5256 16.1934C18.9134 14.9425 16.3539 14.6981 15.5981 14.586C15.0589 14.5061 14.683 14.009 14.753 13.4684C14.8231 12.9278 15.3131 12.5429 15.8549 12.603C17.6122 12.7978 19.1436 12.0563 19.5413 10.7159C19.61 10.45 19.63 10.18 19.6633 9.90948C19.7711 9.02507 19.8867 7.73486 19.9509 6.91709C19.9876 6.44922 20.1985 5.27964 19.4288 5.41084C19.1429 5.45959 18.6059 5.65205 17.68 6.31391C15.7374 7.70252 13.9749 9.82666 12.891 11.954C12.7203 12.289 12.376 12.5 12 12.5C11.624 12.5 11.2797 12.289 11.109 11.954C10.0251 9.82666 8.26258 7.70252 6.31998 6.31391C5.39406 5.65205 4.85713 5.45959 4.57117 5.41084C3.7874 5.27724 4.01205 6.44504 4.04911 6.91709Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/caution.svg b/src/themes/serene/static/icon/caution.svg new file mode 100644 index 0000000..c8bb5ee --- /dev/null +++ b/src/themes/serene/static/icon/caution.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M4.00098 20V14C4.00098 9.58172 7.5827 6 12.001 6C16.4193 6 20.001 9.58172 20.001 14V20H21.001V22H3.00098V20H4.00098ZM6.00098 20H18.001V14C18.001 10.6863 15.3147 8 12.001 8C8.68727 8 6.00098 10.6863 6.00098 14V20ZM11.001 2H13.001V5H11.001V2ZM19.7792 4.80761L21.1934 6.22183L19.0721 8.34315L17.6578 6.92893L19.7792 4.80761ZM2.80859 6.22183L4.22281 4.80761L6.34413 6.92893L4.92991 8.34315L2.80859 6.22183ZM7.00098 14C7.00098 11.2386 9.23956 9 12.001 9V11C10.3441 11 9.00098 12.3431 9.00098 14H7.00098Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/check.svg b/src/themes/serene/static/icon/check.svg new file mode 100644 index 0000000..9e030ce --- /dev/null +++ b/src/themes/serene/static/icon/check.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M10.0007 15.1709L19.1931 5.97852L20.6073 7.39273L10.0007 17.9993L3.63672 11.6354L5.05093 10.2212L10.0007 15.1709Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/copy.svg b/src/themes/serene/static/icon/copy.svg new file mode 100644 index 0000000..e5fb178 --- /dev/null +++ b/src/themes/serene/static/icon/copy.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M6.9998 6V3C6.9998 2.44772 7.44752 2 7.9998 2H19.9998C20.5521 2 20.9998 2.44772 20.9998 3V17C20.9998 17.5523 20.5521 18 19.9998 18H16.9998V20.9991C16.9998 21.5519 16.5499 22 15.993 22H4.00666C3.45059 22 3 21.5554 3 20.9991L3.0026 7.00087C3.0027 6.44811 3.45264 6 4.00942 6H6.9998ZM5.00242 8L5.00019 20H14.9998V8H5.00242ZM8.9998 6H16.9998V16H18.9998V4H8.9998V6Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/email.svg b/src/themes/serene/static/icon/email.svg new file mode 100644 index 0000000..1593667 --- /dev/null +++ b/src/themes/serene/static/icon/email.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M3 3H21C21.5523 3 22 3.44772 22 4V20C22 20.5523 21.5523 21 21 21H3C2.44772 21 2 20.5523 2 20V4C2 3.44772 2.44772 3 3 3ZM20 7.23792L12.0718 14.338L4 7.21594V19H20V7.23792ZM4.51146 5L12.0619 11.662L19.501 5H4.51146Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/facebook.svg b/src/themes/serene/static/icon/facebook.svg new file mode 100644 index 0000000..991d924 --- /dev/null +++ b/src/themes/serene/static/icon/facebook.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M14 19H19V5H5V19H12V14H10V12H12V10.3458C12 9.00855 12.1392 8.52362 12.4007 8.03473C12.6622 7.54584 13.0458 7.16216 13.5347 6.9007C13.9174 6.69604 14.3922 6.57252 15.2217 6.51954C15.551 6.49851 15.9771 6.52533 16.5 6.6V8.5H16C15.0827 8.5 14.7042 8.54332 14.4779 8.66433C14.3376 8.73939 14.2394 8.83758 14.1643 8.97793C14.0433 9.20418 14 9.42853 14 10.3458V12H16.5L16 14H14V19ZM4 3H20C20.5523 3 21 3.44772 21 4V20C21 20.5523 20.5523 21 20 21H4C3.44772 21 3 20.5523 3 20V4C3 3.44772 3.44772 3 4 3Z"></path></svg> diff --git a/src/themes/serene/static/icon/fingerprint.svg b/src/themes/serene/static/icon/fingerprint.svg new file mode 100644 index 0000000..a15985b --- /dev/null +++ b/src/themes/serene/static/icon/fingerprint.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M17.0003 13V14C17.0003 16.7696 16.3364 19.445 15.0853 21.8455L14.8585 22.2663L13.1116 21.2924C14.2716 19.2115 14.9211 16.8817 14.9935 14.4559L15.0003 14V13H17.0003ZM11.0003 10H13.0003V14L12.9948 14.3787C12.9153 17.1495 11.9645 19.7731 10.3038 21.928L10.073 22.2189L8.52406 20.9536C10.0408 19.0969 10.9145 16.8017 10.9943 14.3663L11.0003 14V10ZM12.0003 6C14.7617 6 17.0003 8.23858 17.0003 11H15.0003C15.0003 9.34315 13.6571 8 12.0003 8C10.3434 8 9.00025 9.34315 9.00025 11V14C9.00025 16.2354 8.1806 18.3444 6.72928 19.9768L6.51767 20.2067L5.06955 18.8273C6.23328 17.6056 6.92099 16.0118 6.99381 14.3027L7.00025 14V11C7.00025 8.23858 9.23883 6 12.0003 6ZM12.0003 2C16.9708 2 21.0003 6.02944 21.0003 11V14C21.0003 15.6979 20.7985 17.3699 20.4035 18.9903L20.2647 19.5285L18.3349 19.0032C18.726 17.5662 18.9475 16.0808 18.9919 14.5684L19.0003 14V11C19.0003 7.13401 15.8662 4 12.0003 4C10.4279 4 8.97663 4.51841 7.80805 5.39364L6.38308 3.96769C7.92267 2.73631 9.87547 2 12.0003 2ZM4.96794 5.38282L6.39389 6.8078C5.5635 7.91652 5.0543 9.27971 5.00431 10.7593L4.99961 10.999L5.00378 13C5.00378 14.1195 4.73991 15.2026 4.24263 16.1772L4.08648 16.4663L2.34961 15.4747C2.72889 14.8103 2.95077 14.0681 2.99539 13.2924L3.00378 13L3.00361 11C3.00025 8.87522 3.73656 6.92242 4.96794 5.38282Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/github.svg b/src/themes/serene/static/icon/github.svg new file mode 100644 index 0000000..e15be38 --- /dev/null +++ b/src/themes/serene/static/icon/github.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M5.88401 18.6531C5.58404 18.4523 5.32587 18.1972 5.0239 17.8366C4.91473 17.7062 4.47283 17.1521 4.55811 17.258C4.09533 16.6831 3.80296 16.4168 3.50156 16.3087C2.9817 16.1223 2.7114 15.5497 2.89784 15.0298C3.08428 14.51 3.65685 14.2397 4.17672 14.4261C4.92936 14.696 5.43847 15.1609 6.12425 16.0141C6.03025 15.8972 6.46364 16.4408 6.55731 16.5526C6.74784 16.7802 6.88732 16.918 6.99629 16.9909C7.20118 17.128 7.58451 17.1871 8.14709 17.1308C8.17065 16.7487 8.24136 16.378 8.34919 16.0355C5.38097 15.3101 3.70116 13.3949 3.70116 9.63947C3.70116 8.4006 4.0704 7.28368 4.75917 6.34756C4.5415 5.45368 4.57433 4.37259 5.06092 3.15611C5.1725 2.87715 5.40361 2.66314 5.69031 2.57328C5.77242 2.54949 5.81791 2.5389 5.89878 2.52648C6.70167 2.40319 7.83573 2.69681 9.31449 3.62311C10.181 3.41855 11.0885 3.31476 12.0012 3.31476C12.9129 3.31476 13.8196 3.41835 14.6854 3.62253C16.1619 2.68976 17.2986 2.39625 18.1072 2.52627C18.1919 2.53988 18.2645 2.55758 18.3249 2.57741C18.6059 2.66967 18.8316 2.88155 18.9414 3.15611C19.4279 4.37232 19.4608 5.45319 19.2433 6.34695C19.9342 7.28313 20.3012 8.39184 20.3012 9.63947C20.3012 13.3966 18.627 15.3046 15.6588 16.0318C15.7837 16.4467 15.8496 16.9103 15.8496 17.4118C15.8496 18.0763 15.8471 18.7108 15.8424 19.4223C15.8412 19.6124 15.8397 19.8156 15.8375 20.1279C16.2129 20.2107 16.5229 20.5074 16.6031 20.9086C16.7114 21.4502 16.3602 21.977 15.8186 22.0853C14.6794 22.3132 13.8353 21.5535 13.8353 20.5608C13.8353 20.4705 13.836 20.3414 13.8375 20.1142C13.8398 19.8012 13.8412 19.5987 13.8425 19.4092C13.8471 18.7017 13.8496 18.0714 13.8496 17.4118C13.8496 16.7145 13.6664 16.26 13.4237 16.0508C12.7627 15.481 13.0977 14.3971 13.965 14.2996C16.9314 13.9663 18.3012 12.8174 18.3012 9.63947C18.3012 8.68484 17.9893 7.89547 17.3881 7.23534C17.1301 6.95209 17.0567 6.54634 17.199 6.19062C17.3647 5.77639 17.4354 5.2336 17.2941 4.57678L17.2847 4.57944C16.7928 4.71861 16.1744 5.01956 15.4261 5.52826C15.182 5.69413 14.8772 5.74377 14.5932 5.66388C13.7729 5.43319 12.8913 5.31476 12.0012 5.31476C11.111 5.31476 10.2294 5.43319 9.40916 5.66388C9.12662 5.74335 8.82344 5.69468 8.57997 5.53077C7.8274 5.02414 7.2056 4.72355 6.71079 4.58352C6.56735 5.23672 6.63814 5.77758 6.80336 6.19062C6.94565 6.54634 6.87219 6.95209 6.61423 7.23534C6.01715 7.89096 5.70116 8.69352 5.70116 9.63947C5.70116 12.8114 7.07225 13.9681 10.023 14.2996C10.8883 14.3969 11.2246 15.4767 10.5675 16.048C10.3751 16.2153 10.1384 16.7799 10.1384 17.4118V20.5608C10.1384 21.5472 9.30356 22.2866 8.17878 22.0898C7.63476 21.9946 7.27093 21.4764 7.36613 20.9324C7.43827 20.5201 7.75331 20.2114 8.13841 20.1274V19.1379C7.22829 19.1991 6.47656 19.0496 5.88401 18.6531Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/important.svg b/src/themes/serene/static/icon/important.svg new file mode 100644 index 0000000..b3dcb13 --- /dev/null +++ b/src/themes/serene/static/icon/important.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 15H13V17H11V15ZM11 7H13V13H11V7Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/instagram.svg b/src/themes/serene/static/icon/instagram.svg new file mode 100644 index 0000000..6323fa8 --- /dev/null +++ b/src/themes/serene/static/icon/instagram.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12.001 9C10.3436 9 9.00098 10.3431 9.00098 12C9.00098 13.6573 10.3441 15 12.001 15C13.6583 15 15.001 13.6569 15.001 12C15.001 10.3427 13.6579 9 12.001 9ZM12.001 7C14.7614 7 17.001 9.2371 17.001 12C17.001 14.7605 14.7639 17 12.001 17C9.24051 17 7.00098 14.7629 7.00098 12C7.00098 9.23953 9.23808 7 12.001 7ZM18.501 6.74915C18.501 7.43926 17.9402 7.99917 17.251 7.99917C16.5609 7.99917 16.001 7.4384 16.001 6.74915C16.001 6.0599 16.5617 5.5 17.251 5.5C17.9393 5.49913 18.501 6.0599 18.501 6.74915ZM12.001 4C9.5265 4 9.12318 4.00655 7.97227 4.0578C7.18815 4.09461 6.66253 4.20007 6.17416 4.38967C5.74016 4.55799 5.42709 4.75898 5.09352 5.09255C4.75867 5.4274 4.55804 5.73963 4.3904 6.17383C4.20036 6.66332 4.09493 7.18811 4.05878 7.97115C4.00703 9.0752 4.00098 9.46105 4.00098 12C4.00098 14.4745 4.00753 14.8778 4.05877 16.0286C4.0956 16.8124 4.2012 17.3388 4.39034 17.826C4.5591 18.2606 4.7605 18.5744 5.09246 18.9064C5.42863 19.2421 5.74179 19.4434 6.17187 19.6094C6.66619 19.8005 7.19148 19.9061 7.97212 19.9422C9.07618 19.9939 9.46203 20 12.001 20C14.4755 20 14.8788 19.9934 16.0296 19.9422C16.8117 19.9055 17.3385 19.7996 17.827 19.6106C18.2604 19.4423 18.5752 19.2402 18.9074 18.9085C19.2436 18.5718 19.4445 18.2594 19.6107 17.8283C19.8013 17.3358 19.9071 16.8098 19.9432 16.0289C19.9949 14.9248 20.001 14.5389 20.001 12C20.001 9.52552 19.9944 9.12221 19.9432 7.97137C19.9064 7.18906 19.8005 6.66149 19.6113 6.17318C19.4434 5.74038 19.2417 5.42635 18.9084 5.09255C18.573 4.75715 18.2616 4.55693 17.8271 4.38942C17.338 4.19954 16.8124 4.09396 16.0298 4.05781C14.9258 4.00605 14.5399 4 12.001 4ZM12.001 2C14.7176 2 15.0568 2.01 16.1235 2.06C17.1876 2.10917 17.9135 2.2775 18.551 2.525C19.2101 2.77917 19.7668 3.1225 20.3226 3.67833C20.8776 4.23417 21.221 4.7925 21.476 5.45C21.7226 6.08667 21.891 6.81333 21.941 7.8775C21.9885 8.94417 22.001 9.28333 22.001 12C22.001 14.7167 21.991 15.0558 21.941 16.1225C21.8918 17.1867 21.7226 17.9125 21.476 18.55C21.2218 19.2092 20.8776 19.7658 20.3226 20.3217C19.7668 20.8767 19.2076 21.22 18.551 21.475C17.9135 21.7217 17.1876 21.89 16.1235 21.94C15.0568 21.9875 14.7176 22 12.001 22C9.28431 22 8.94514 21.99 7.87848 21.94C6.81431 21.8908 6.08931 21.7217 5.45098 21.475C4.79264 21.2208 4.23514 20.8767 3.67931 20.3217C3.12348 19.7658 2.78098 19.2067 2.52598 18.55C2.27848 17.9125 2.11098 17.1867 2.06098 16.1225C2.01348 15.0558 2.00098 14.7167 2.00098 12C2.00098 9.28333 2.01098 8.94417 2.06098 7.8775C2.11014 6.8125 2.27848 6.0875 2.52598 5.45C2.78014 4.79167 3.12348 4.23417 3.67931 3.67833C4.23514 3.1225 4.79348 2.78 5.45098 2.525C6.08848 2.2775 6.81348 2.11 7.87848 2.06C8.94514 2.0125 9.28431 2 12.001 2Z"></path></svg> diff --git a/src/themes/serene/static/icon/linkedin.svg b/src/themes/serene/static/icon/linkedin.svg new file mode 100644 index 0000000..dfb0d9f --- /dev/null +++ b/src/themes/serene/static/icon/linkedin.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M4.00098 3H20.001C20.5533 3 21.001 3.44772 21.001 4V20C21.001 20.5523 20.5533 21 20.001 21H4.00098C3.44869 21 3.00098 20.5523 3.00098 20V4C3.00098 3.44772 3.44869 3 4.00098 3ZM5.00098 5V19H19.001V5H5.00098ZM7.50098 9C6.67255 9 6.00098 8.32843 6.00098 7.5C6.00098 6.67157 6.67255 6 7.50098 6C8.3294 6 9.00098 6.67157 9.00098 7.5C9.00098 8.32843 8.3294 9 7.50098 9ZM6.50098 10H8.50098V17.5H6.50098V10ZM12.001 10.4295C12.5854 9.86534 13.2665 9.5 14.001 9.5C16.072 9.5 17.501 11.1789 17.501 13.25V17.5H15.501V13.25C15.501 12.2835 14.7175 11.5 13.751 11.5C12.7845 11.5 12.001 12.2835 12.001 13.25V17.5H10.001V10H12.001V10.4295Z"></path></svg> diff --git a/src/themes/serene/static/icon/mastodon.svg b/src/themes/serene/static/icon/mastodon.svg new file mode 100644 index 0000000..601640f --- /dev/null +++ b/src/themes/serene/static/icon/mastodon.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M3.019 12.0075C2.98744 10.7478 3.00692 9.5598 3.00692 8.56644C3.00692 4.22767 5.84954 2.95597 5.84954 2.95597C7.28286 2.29767 9.74238 2.0209 12.2993 2H12.3621C14.919 2.0209 17.3801 2.29767 18.8134 2.95597C18.8134 2.95597 21.656 4.22767 21.656 8.56644C21.656 8.56644 21.6916 11.7674 21.2596 13.9898C20.9852 15.4007 18.8034 16.9446 16.2974 17.2438C14.9906 17.3999 13.7042 17.5431 12.3322 17.4802C10.0885 17.3775 8.31815 16.9446 8.31815 16.9446C8.31815 17.1631 8.33166 17.3711 8.35853 17.5655C8.44182 18.1978 8.65659 18.6604 8.96296 19C9.72944 19.8497 11.0692 19.9301 12.3577 19.9743C14.178 20.0366 15.7986 19.5254 15.7986 19.5254L15.8735 21.1712C15.8735 21.1712 14.6003 21.8548 12.3322 21.9805C11.0815 22.0493 9.52858 21.9491 7.71969 21.4704C6.18802 21.065 5.15153 20.1804 4.45091 19C3.35714 17.1573 3.08191 14.5938 3.019 12.0075ZM6.31815 16.9446V14.3967L8.79316 15.0018C8.8405 15.0134 8.95098 15.0383 9.11692 15.0723C9.40521 15.1313 9.73416 15.1908 10.0959 15.2467C10.8485 15.3628 11.6341 15.4462 12.4237 15.4823C13.4425 15.529 14.3249 15.4652 16.0603 15.2579C17.7233 15.0594 19.208 14.0622 19.2963 13.6082C19.3783 13.1861 19.4472 12.6858 19.5021 12.1261C19.5714 11.4205 19.6155 10.6558 19.6388 9.88068C19.654 9.37026 19.6582 8.93648 19.6564 8.62452L19.656 8.56644C19.656 7.1368 19.2873 6.12756 18.6928 5.40793C18.5008 5.17553 18.3004 4.99408 18.1087 4.85958C18.0183 4.79617 17.9737 4.77136 17.9787 4.77345C16.9662 4.30844 14.8859 4.02069 12.3621 3.99993H12.3156C9.77596 4.02069 7.6969 4.30836 6.66627 4.78161C6.68919 4.77136 6.64459 4.79617 6.55423 4.85958C6.36257 4.99408 6.16214 5.17553 5.97016 5.40793C5.37568 6.12756 5.00692 7.1368 5.00692 8.56644C5.00692 8.7976 5.00628 8.96339 5.00392 9.44137C4.9981 10.6238 5.00004 11.2256 5.01841 11.9589C5.07185 14.156 5.2822 15.7941 5.71797 17C5.93023 17.5874 6.19005 18.0709 6.49741 18.4507C6.37791 18.0162 6.31815 17.5142 6.31815 16.9446ZM8.08576 6.37135C8.71735 6.37135 9.22924 6.88324 9.22924 7.51482C9.22924 8.14626 8.71735 8.6583 8.08576 8.6583C7.45432 8.6583 6.94229 8.14626 6.94229 7.51482C6.94229 6.88324 7.45432 6.37135 8.08576 6.37135Z"></path></svg> diff --git a/src/themes/serene/static/icon/moon.svg b/src/themes/serene/static/icon/moon.svg new file mode 100644 index 0000000..fe7bdc8 --- /dev/null +++ b/src/themes/serene/static/icon/moon.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M10 7C10 10.866 13.134 14 17 14C18.9584 14 20.729 13.1957 21.9995 11.8995C22 11.933 22 11.9665 22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C12.0335 2 12.067 2 12.1005 2.00049C10.8043 3.27098 10 5.04157 10 7ZM4 12C4 16.4183 7.58172 20 12 20C15.0583 20 17.7158 18.2839 19.062 15.7621C18.3945 15.9187 17.7035 16 17 16C12.0294 16 8 11.9706 8 7C8 6.29648 8.08133 5.60547 8.2379 4.938C5.71611 6.28423 4 8.9417 4 12Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/note.svg b/src/themes/serene/static/icon/note.svg new file mode 100644 index 0000000..6afebac --- /dev/null +++ b/src/themes/serene/static/icon/note.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 7H13V9H11V7ZM11 11H13V17H11V11Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/question.svg b/src/themes/serene/static/icon/question.svg new file mode 100644 index 0000000..2aaafe7 --- /dev/null +++ b/src/themes/serene/static/icon/question.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 15H13V17H11V15ZM13 13.3551V14H11V12.5C11 11.9477 11.4477 11.5 12 11.5C12.8284 11.5 13.5 10.8284 13.5 10C13.5 9.17157 12.8284 8.5 12 8.5C11.2723 8.5 10.6656 9.01823 10.5288 9.70577L8.56731 9.31346C8.88637 7.70919 10.302 6.5 12 6.5C13.933 6.5 15.5 8.067 15.5 10C15.5 11.5855 14.4457 12.9248 13 13.3551Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/quote.svg b/src/themes/serene/static/icon/quote.svg new file mode 100644 index 0000000..24156f7 --- /dev/null +++ b/src/themes/serene/static/icon/quote.svg @@ -0,0 +1,8 @@ +<svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="796 698 200 200"> +<g> + <path d="M885.208,749.739v-40.948C836.019,708.791,796,748.81,796,798v89.209h89.208V798h-48.26 + C836.948,771.39,858.598,749.739,885.208,749.739z"/> + <path d="M996,749.739v-40.948c-49.19,0-89.209,40.019-89.209,89.209v89.209H996V798h-48.26 + C947.74,771.39,969.39,749.739,996,749.739z"/> +</g> +</svg> diff --git a/src/themes/serene/static/icon/sun.svg b/src/themes/serene/static/icon/sun.svg new file mode 100644 index 0000000..1025381 --- /dev/null +++ b/src/themes/serene/static/icon/sun.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M12 18C8.68629 18 6 15.3137 6 12C6 8.68629 8.68629 6 12 6C15.3137 6 18 8.68629 18 12C18 15.3137 15.3137 18 12 18ZM12 16C14.2091 16 16 14.2091 16 12C16 9.79086 14.2091 8 12 8C9.79086 8 8 9.79086 8 12C8 14.2091 9.79086 16 12 16ZM11 1H13V4H11V1ZM11 20H13V23H11V20ZM3.51472 4.92893L4.92893 3.51472L7.05025 5.63604L5.63604 7.05025L3.51472 4.92893ZM16.9497 18.364L18.364 16.9497L20.4853 19.0711L19.0711 20.4853L16.9497 18.364ZM19.0711 3.51472L20.4853 4.92893L18.364 7.05025L16.9497 5.63604L19.0711 3.51472ZM5.63604 16.9497L7.05025 18.364L4.92893 20.4853L3.51472 19.0711L5.63604 16.9497ZM23 11V13H20V11H23ZM4 11V13H1V11H4Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/threads.svg b/src/themes/serene/static/icon/threads.svg new file mode 100644 index 0000000..deccc24 --- /dev/null +++ b/src/themes/serene/static/icon/threads.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18" fill="currentColor"><path d="M12.1835 1.41016L12.1822 1.41016C9.09012 1.43158 6.70036 2.47326 5.09369 4.51569C3.66581 6.33087 2.93472 8.86436 2.91016 12.0068V12.0082C2.93472 15.1508 3.66586 17.6696 5.09369 19.4847C6.70043 21.5271 9.10257 22.5688 12.1946 22.5902H12.1958C14.944 22.5711 16.8929 21.8504 18.4985 20.2463C20.6034 18.1434 20.5408 15.5048 19.8456 13.8832C19.3163 12.6493 18.2709 11.6618 16.8701 11.0477C16.6891 8.06345 15.0097 6.32178 12.2496 6.30415C10.6191 6.29409 9.14792 7.02378 8.24685 8.39104L9.90238 9.5267C10.4353 8.71818 11.2789 8.32815 12.2371 8.33701C13.6244 8.34586 14.5362 9.11128 14.7921 10.4541C14.02 10.3333 13.1902 10.2982 12.3076 10.3488C9.66843 10.5008 7.9399 12.061 8.05516 14.2244C8.17571 16.4862 10.367 17.7186 12.4476 17.605C14.9399 17.4684 16.4209 15.6292 16.7722 13.2836C17.3493 13.6575 17.7751 14.1344 18.0163 14.6969C18.4559 15.7222 18.4838 17.4132 17.1006 18.7952C15.8838 20.0108 14.4211 20.5407 12.1891 20.5572C9.71428 20.5388 7.85698 19.746 6.65154 18.2136C5.51973 16.7748 4.92843 14.6882 4.90627 12.0002C4.92843 9.31211 5.51973 7.22549 6.65154 5.78673C7.85698 4.25433 9.71424 3.46156 12.189 3.44303C14.6819 3.4617 16.5728 4.25837 17.8254 5.79937C18.5162 6.64934 18.949 7.66539 19.2379 8.71407L21.1776 8.19656C20.8148 6.85917 20.2414 5.58371 19.363 4.50305C17.7098 2.46918 15.2816 1.43166 12.1835 1.41016ZM12.4204 12.3782C13.3044 12.3272 14.1239 12.3834 14.8521 12.5345C14.7114 14.1116 14.0589 15.4806 12.3401 15.575C11.2282 15.6376 10.1031 15.1413 10.0484 14.114C10.0077 13.3503 10.5726 12.4847 12.4204 12.3782Z"></path></svg> diff --git a/src/themes/serene/static/icon/tip.svg b/src/themes/serene/static/icon/tip.svg new file mode 100644 index 0000000..21b107a --- /dev/null +++ b/src/themes/serene/static/icon/tip.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M9.97308 18H11V13H13V18H14.0269C14.1589 16.7984 14.7721 15.8065 15.7676 14.7226C15.8797 14.6006 16.5988 13.8564 16.6841 13.7501C17.5318 12.6931 18 11.385 18 10C18 6.68629 15.3137 4 12 4C8.68629 4 6 6.68629 6 10C6 11.3843 6.46774 12.6917 7.31462 13.7484C7.40004 13.855 8.12081 14.6012 8.23154 14.7218C9.22766 15.8064 9.84103 16.7984 9.97308 18ZM10 20V21H14V20H10ZM5.75395 14.9992C4.65645 13.6297 4 11.8915 4 10C4 5.58172 7.58172 2 12 2C16.4183 2 20 5.58172 20 10C20 11.8925 19.3428 13.6315 18.2443 15.0014C17.624 15.7748 16 17 16 18.5V21C16 22.1046 15.1046 23 14 23H10C8.89543 23 8 22.1046 8 21V18.5C8 17 6.37458 15.7736 5.75395 14.9992Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/twitter.svg b/src/themes/serene/static/icon/twitter.svg new file mode 100644 index 0000000..5ba5a47 --- /dev/null +++ b/src/themes/serene/static/icon/twitter.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M15.3499 5.5498C13.7681 5.5498 12.4786 6.81785 12.4504 8.39633L12.4223 9.97138C12.4164 10.3027 12.143 10.5665 11.8117 10.5606C11.7881 10.5602 11.7646 10.5584 11.7413 10.5552L10.1805 10.3423C8.12699 10.0623 6.15883 9.11711 4.27072 7.54387C3.67275 10.8535 4.84 13.147 7.65342 14.9157L9.40041 16.014C9.68095 16.1904 9.7654 16.5608 9.58903 16.8413C9.54861 16.9056 9.49636 16.9616 9.43504 17.0064L7.84338 18.1693C8.78973 18.2288 9.68938 18.1873 10.435 18.0385C15.1526 17.097 18.2897 13.5468 18.2897 7.69084C18.2897 7.21275 17.2774 5.5498 15.3499 5.5498ZM10.4507 8.36066C10.4983 5.69559 12.6735 3.5498 15.3499 3.5498C16.7132 3.5498 17.9465 4.10658 18.8348 5.00515C19.5462 4.9998 20.1514 5.17966 21.5035 4.35943C21.1693 5.9998 21.0034 6.71177 20.2897 7.69084C20.2897 15.3324 15.5926 19.0487 10.8264 19.9998C7.5587 20.6519 2.80646 19.5812 1.44531 18.1584C2.13874 18.1051 4.95928 17.8018 6.58895 16.6089C5.20994 15.6984 -0.278631 12.4679 3.32772 3.78617C5.02119 5.76283 6.73797 7.10831 8.47807 7.82262C9.63548 8.29774 9.91978 8.28825 10.4507 8.36066Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/icon/warning.svg b/src/themes/serene/static/icon/warning.svg new file mode 100644 index 0000000..0d70434 --- /dev/null +++ b/src/themes/serene/static/icon/warning.svg @@ -0,0 +1 @@ +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="18" height="18"><path d="M12.865 3.00017L22.3912 19.5002C22.6674 19.9785 22.5035 20.5901 22.0252 20.8662C21.8732 20.954 21.7008 21.0002 21.5252 21.0002H2.47266C1.92037 21.0002 1.47266 20.5525 1.47266 20.0002C1.47266 19.8246 1.51886 19.6522 1.60663 19.5002L11.1329 3.00017C11.4091 2.52187 12.0206 2.358 12.4989 2.63414C12.651 2.72191 12.7772 2.84815 12.865 3.00017ZM4.20471 19.0002H19.7932L11.9989 5.50017L4.20471 19.0002ZM10.9989 16.0002H12.9989V18.0002H10.9989V16.0002ZM10.9989 9.00017H12.9989V14.0002H10.9989V9.00017Z" fill="currentColor"></path></svg> diff --git a/src/themes/serene/static/js/lightense.min.js b/src/themes/serene/static/js/lightense.min.js new file mode 100644 index 0000000..1f979a6 --- /dev/null +++ b/src/themes/serene/static/js/lightense.min.js @@ -0,0 +1,2 @@ +/*! lightense-images v1.0.17 | © Tunghsiao Liu | MIT */ +!function (e, t) { "object" == typeof exports && "object" == typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define([], t) : "object" == typeof exports ? exports.Lightense = t() : e.Lightense = t() }(this, (function () { return e = { 352: e => { function t(e, t) { var n = Object.keys(e); if (Object.getOwnPropertySymbols) { var r = Object.getOwnPropertySymbols(e); t && (r = r.filter((function (t) { return Object.getOwnPropertyDescriptor(e, t).enumerable }))), n.push.apply(n, r) } return n } function n(e) { for (var n = 1; n < arguments.length; n++) { var i = null != arguments[n] ? arguments[n] : {}; n % 2 ? t(Object(i), !0).forEach((function (t) { r(e, t, i[t]) })) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(i)) : t(Object(i)).forEach((function (t) { Object.defineProperty(e, t, Object.getOwnPropertyDescriptor(i, t)) })) } return e } function r(e, t, n) { return t in e ? Object.defineProperty(e, t, { value: n, enumerable: !0, configurable: !0, writable: !0 }) : e[t] = n, e } function i(e) { return (i = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (e) { return typeof e } : function (e) { return e && "function" == typeof Symbol && e.constructor === Symbol && e !== Symbol.prototype ? "symbol" : typeof e })(e) } var o = function () { "use strict"; var e, t = { time: 300, padding: 40, offset: 40, keyboard: !0, cubicBezier: "cubic-bezier(.2, 0, .1, 1)", background: "var(--bg-color-80, rgba(255, 255, 255, .98))", zIndex: 1e6, beforeShow: void 0, afterShow: void 0, beforeHide: void 0, afterHide: void 0 }, r = {}; function o(e) { var t = r[e]; if (t) { if ("function" != typeof t) throw "config.".concat(e, " must be a function!"); Reflect.apply(t, r, [r]) } } function a(e) { switch (i(e)) { case "undefined": throw "You need to pass an element!"; case "string": return document.querySelectorAll(e); case "object": return e } } function c(e) { var t = e.length; if (t) for (var n = 0; n < t; n++)s(e[n]); else s(e) } function s(e) { e.src && !e.classList.contains("lightense-target") && (e.classList.add("lightense-target"), e.addEventListener("click", (function (i) { if (r.keyboard && (i.metaKey || i.ctrlKey)) return window.open(e.src, "_blank"); !function (e) { if (r.target = e, r.target.classList.contains("lightense-open")) return g(); o("beforeShow"), r.scrollY = window.scrollY, function (e, t, n) { e.addEventListener(t, (function r(i) { Reflect.apply(n, this, i), e.removeEventListener(t, r) })) }(r.target, "transitionend", (function () { o("afterShow") })); var i = new Image; i.onload = function () { !function (e) { var n = e.width, i = e.height, o = window.pageYOffset || document.documentElement.scrollTop || 0, a = window.pageXOffset || document.documentElement.scrollLeft || 0, c = r.target.getBoundingClientRect(), s = n / c.width, d = window.innerWidth || document.documentElement.clientWidth || 0, l = window.innerHeight || document.documentElement.clientHeight || 0, u = r.target.getAttribute("data-lightense-padding") || r.target.getAttribute("data-padding") || r.padding, g = d > u ? d - u : d - t.padding, p = l > u ? l - u : l - t.padding, f = n / i, b = g / p; r.scaleFactor = n < g && i < p ? s : f < b ? p / i * s : g / n * s; var h = d / 2, m = o + l / 2, v = c.left + a + c.width / 2, y = c.top + o + c.height / 2; r.translateX = Math.round(h - v), r.translateY = Math.round(m - y) }(this), function () { r.target.classList.add("lightense-open"), r.wrap = document.createElement("div"), r.wrap.className = "lightense-wrap", setTimeout((function () { r.target.style.transform = "scale(" + r.scaleFactor + ")" }), 20), r.target.parentNode.insertBefore(r.wrap, r.target), r.wrap.appendChild(r.target), setTimeout((function () { r.wrap.style.transform = "translate3d(" + r.translateX + "px, " + r.translateY + "px, 0)" }), 20); var e = { cubicBezier: r.target.getAttribute("data-lightense-cubic-bezier") || r.cubicBezier, background: r.target.getAttribute("data-lightense-background") || r.target.getAttribute("data-background") || r.background, zIndex: r.target.getAttribute("data-lightense-z-index") || r.zIndex }, t = n(n({}, r), e); d("lightense-images-css-computed", "\n :root {\n --lightense-z-index: ".concat(t.zIndex - 1, ";\n --lightense-backdrop: ").concat(t.background, ";\n --lightense-duration: ").concat(t.time, "ms;\n --lightense-timing-func: ").concat(t.cubicBezier, ";\n }")), r.container.style.visibility = "visible", setTimeout((function () { r.container.style.opacity = "1" }), 20) }(), window.addEventListener("keyup", f, !1), window.addEventListener("scroll", p, !1), r.container.addEventListener("click", g, !1) }, i.src = r.target.src }(this) }), !1)) } function d(e, t) { var n = document.head || document.getElementsByTagName("head")[0]; document.getElementById(e) && document.getElementById(e).remove(); var r = document.createElement("style"); r.id = e, r.styleSheet ? r.styleSheet.cssText = t : r.appendChild(document.createTextNode(t)), n.appendChild(r) } function l() { d("lightense-images-css", "\n:root {\n --lightense-z-index: ".concat(r.zIndex - 1, ";\n --lightense-backdrop: ").concat(r.background, ";\n --lightense-duration: ").concat(r.time, "ms;\n --lightense-timing-func: ").concat(r.cubicBezier, ";\n}\n\n.lightense-backdrop {\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n position: fixed;\n top: 0;\n left: 0;\n overflow: hidden;\n z-index: calc(var(--lightense-z-index) - 1);\n padding: 0;\n margin: 0;\n transition: opacity var(--lightense-duration) ease;\n cursor: zoom-out;\n opacity: 0;\n background-color: var(--lightense-backdrop);\n visibility: hidden;\n}\n\n@supports (-webkit-backdrop-filter: blur(30px)) {\n .lightense-backdrop {\n background-color: var(--lightense-backdrop);\n -webkit-backdrop-filter: blur(30px);\n }\n}\n\n@supports (backdrop-filter: blur(30px)) {\n .lightense-backdrop {\n background-color: var(--lightense-backdrop);\n backdrop-filter: blur(30px);\n }\n}\n\n.lightense-wrap {\n position: relative;\n transition: transform var(--lightense-duration) var(--lightense-timing-func);\n z-index: var(--lightense-z-index);\n pointer-events: none;\n}\n\n.lightense-target {\n cursor: zoom-in;\n transition: transform var(--lightense-duration) var(--lightense-timing-func);\n pointer-events: auto;\n}\n\n.lightense-open {\n cursor: zoom-out;\n}\n\n.lightense-transitioning {\n pointer-events: none;\n}")) } function u() { null === document.querySelector(".lightense-backdrop") ? (r.container = document.createElement("div"), r.container.className = "lightense-backdrop", document.body.appendChild(r.container)) : r.container = document.querySelector(".lightense-backdrop") } function g() { o("beforeHide"), window.removeEventListener("keyup", f, !1), window.removeEventListener("scroll", p, !1), r.container.removeEventListener("click", g, !1), r.target.classList.remove("lightense-open"), r.wrap.style.transform = "", r.target.style.transform = "", r.target.classList.add("lightense-transitioning"), r.container.style.opacity = "", setTimeout((function () { o("afterHide"), r.container.style.visibility = "", r.container.style.backgroundColor = "", r.wrap.parentNode.replaceChild(r.target, r.wrap), r.target.classList.remove("lightense-transitioning") }), r.time) } function p() { Math.abs(r.scrollY - window.scrollY) >= r.offset && g() } function f(e) { e.preventDefault(), 27 === e.keyCode && g() } return function (i) { var o = arguments.length > 1 && void 0 !== arguments[1] ? arguments[1] : {}; e = a(i), r = n(n({}, t), o), l(), u(), c(e) } }(); e.exports = o } }, t = {}, function n(r) { var i = t[r]; if (void 0 !== i) return i.exports; var o = t[r] = { exports: {} }; return e[r](o, o.exports, n), o.exports }(352); var e, t })); \ No newline at end of file diff --git a/src/themes/serene/static/js/main.js b/src/themes/serene/static/js/main.js new file mode 100644 index 0000000..b0aee0c --- /dev/null +++ b/src/themes/serene/static/js/main.js @@ -0,0 +1,259 @@ +function enableThemeToggle() { + const themeToggle = document.querySelector('#theme-toggle'); + if (!themeToggle) return; + const hlLink = document.querySelector('link#hl'); + const preferDark = window.matchMedia("(prefers-color-scheme: dark)"); + function toggleTheme(theme) { + if (theme == "dark") document.body.classList.add('dark'); else document.body.classList.remove('dark'); + if (hlLink) hlLink.href = `/hl-${theme}.css`; + sessionStorage.setItem("theme", theme); + toggleGiscusTheme(theme); + } + function toggleGiscusTheme(theme) { + const iframe = document.querySelector('iframe.giscus-frame'); + if (iframe) iframe.contentWindow.postMessage({ giscus: { setConfig: { theme: `${location.origin}/giscus_${theme}.css` } } }, 'https://giscus.app'); + } + function initGiscusTheme(evt) { + if (evt.origin !== 'https://giscus.app') return; + if (!(typeof evt.data === 'object' && evt.data.giscus)) return; + toggleGiscusTheme(sessionStorage.getItem("theme") || (preferDark.matches ? "dark" : "light")); + window.removeEventListener('message', initGiscusTheme); + } + window.addEventListener('message', initGiscusTheme); + themeToggle.addEventListener('click', () => toggleTheme(sessionStorage.getItem("theme") == "dark" ? "light" : "dark")); + preferDark.addEventListener("change", e => toggleTheme(e.matches ? "dark" : "light")); + if (!sessionStorage.getItem("theme") && preferDark.matches) toggleTheme("dark"); + if (sessionStorage.getItem("theme") == "dark") toggleTheme("dark"); +} + +function enablePrerender() { + const prerender = (a) => { + if (!a.classList.contains('instant')) return; + const script = document.createElement('script'); + script.type = 'speculationrules'; + script.textContent = JSON.stringify({ prerender: [{ source: 'list', urls: [a.href] }] }); + document.body.append(script); + a.classList.remove('instant'); + } + const prefetch = (a) => { + if (!a.classList.contains('instant')) return; + const link = document.createElement('link'); + link.rel = 'prefetch'; + link.href = a.href; + document.head.append(link); + a.classList.remove('instant'); + } + const support = HTMLScriptElement.supports && HTMLScriptElement.supports('speculationrules'); + const handle = support ? prerender : prefetch; + document.querySelectorAll('a.instant').forEach(a => { + if (a.href.endsWith(window.location.pathname)) return; + let timer; + a.addEventListener('mouseenter', () => { + timer = setTimeout(() => handle(a), 50); + }); + a.addEventListener('mouseleave', () => clearTimeout(timer)); + a.addEventListener('touchstart', () => handle(a), { passive: true }); + }); +} + +function enableRssMask() { + const rssBtn = document.querySelector('#rss-btn'); + const mask = document.querySelector('#rss-mask'); + const copyBtn = document.querySelector('#rss-mask button'); + if (!rssBtn || !mask) return; + rssBtn.addEventListener('click', (e) => { + e.preventDefault(); + mask.showModal(); + }); + const close = (e) => { + if (e.target == mask) mask.close(); + }; + mask.addEventListener('click', close); + const copy = () => { + navigator.clipboard.writeText(copyBtn.dataset.link).then(() => { + copyBtn.innerHTML = copyBtn.dataset.checkIcon; + copyBtn.classList.add('copied'); + copyBtn.removeEventListener('click', copy); + setTimeout(() => { + mask.close(); + copyBtn.innerHTML = copyBtn.dataset.copyIcon; + copyBtn.classList.remove('copied'); + copyBtn.addEventListener('click', copy); + }, 400); + }); + } + copyBtn.addEventListener('click', copy); +} + +function enableOutdateAlert() { + const alert = document.querySelector('#outdate_alert'); + if (!alert) return; + const publish = document.querySelector('#publish'); + const updated = document.querySelector('#updated'); + const updateDate = new Date(updated ? updated.textContent : publish.textContent); + const intervalDays = Math.floor((Date.now() - updateDate.getTime()) / (24 * 60 * 60 * 1000)); + const alertDays = parseInt(alert.dataset.days); + if (intervalDays >= alertDays) { + const msg = alert.dataset.alertTextBefore + intervalDays + alert.dataset.alertTextAfter; + alert.querySelector('.content').textContent = msg; + alert.classList.remove('hidden'); + } +} + +function enableTocTooltip() { + const anchors = document.querySelectorAll('aside nav a'); + if (anchors.length == 0) return; + const toggleTooltip = () => { + anchors.forEach(anchor => { + if (anchor.offsetWidth < anchor.scrollWidth) { + anchor.setAttribute('title', anchor.textContent); + } else { + anchor.removeAttribute('title'); + } + }); + }; + window.addEventListener('resize', toggleTooltip); + toggleTooltip(); +} + +function addCopyBtns() { + const cfg = document.querySelector('#copy-cfg'); + if (!cfg) return; + const copyIcon = cfg.dataset.copyIcon; + const checkIcon = cfg.dataset.checkIcon; + document.querySelectorAll('pre').forEach(block => { + if (block.classList.contains('mermaid')) return; + const wrapper = document.createElement('div'); + wrapper.className = 'codeblock'; + const btn = document.createElement('button'); + btn.className = 'copy'; + btn.ariaLabel = 'copy'; + btn.innerHTML = copyIcon; + const copy = () => { + navigator.clipboard.writeText(block.textContent).then(() => { + btn.innerHTML = checkIcon; + btn.classList.add('copied'); + btn.removeEventListener('click', copy); + setTimeout(() => { + btn.innerHTML = copyIcon; + btn.classList.remove('copied'); + btn.addEventListener('click', copy); + }, 1500); + }); + }; + btn.addEventListener('click', copy); + wrapper.appendChild(block.cloneNode(true)); + wrapper.appendChild(btn); + block.replaceWith(wrapper); + }); +} + +function addBackToTopBtn() { + const backBtn = document.querySelector('#back-to-top'); + if (!backBtn) return; + const toTop = () => window.scrollTo({ top: 0 }); + const toggle = () => { + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; + if (scrollTop > 200 && !backBtn.classList.contains('shown')) { + backBtn.classList.add('shown'); + backBtn.setAttribute('tabindex', 0); + backBtn.addEventListener('click', toTop); + } else if (scrollTop <= 200 && backBtn.classList.contains('shown')) { + backBtn.classList.remove('shown'); + backBtn.setAttribute('tabindex', -1); + backBtn.removeEventListener('click', toTop); + } + }; + window.addEventListener('scroll', toggle); + toggle(); +} + +function addFootnoteBacklink() { + const footnotes = document.querySelectorAll('.footnote-definition'); + footnotes.forEach(footnote => { + const backlink = document.createElement('button'); + backlink.className = 'backlink'; + backlink.ariaLabel = 'backlink'; + backlink.innerHTML = '↩︎'; + backlink.addEventListener('click', () => window.scrollTo({ + top: document.querySelector(`.footnote-reference a[href="#${footnote.id}"]`).getBoundingClientRect().top + window.scrollY, + })); + const lastEl = footnote.lastElementChild || footnote; + lastEl.appendChild(backlink); + }); +} + +function enableImgLightense() { + window.addEventListener("load", () => Lightense(".prose img:not(.no-lightense)", { background: 'rgba(43, 43, 43, 0.19)' })); +} + +function enableReaction() { + const container = document.querySelector('.reaction'); + if (!container) return; + const endpoint = container.dataset.endpoint; + const slug = location.pathname.split('/').filter(Boolean).pop(); + let state = { error: false, reaction: {} }; + const render = () => { + const btns = Object.entries(state.reaction).map(([emoji, [count, reacted]])=> { + const span = document.createElement('span'); + span.textContent = count; + const btn = document.createElement('button'); + if (reacted) btn.classList.add('reacted'); + btn.append(emoji, span); + btn.onclick = () => toggle(emoji); + return btn; + }); + if (state.error) { + container.classList.add('error'); + } else { + container.classList.remove('error'); + } + container.replaceChildren(...btns); + }; + const toggle = async (target) => { + const [count, reacted] = state.reaction[target]; + state.reaction[target] = reacted ? [count - 1, false] : [count + 1, true]; + render(); + try { + const resp = await fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ slug, target, reacted: !reacted }), + }); + if (resp.status === 200) { + error = false; + } else { + throw new Error(); + } + } catch (err) { + state.error = true; + state.reaction[target] = [count, reacted]; + render(); + } + }; + const init = async () => { + const resp = await fetch(`${endpoint}?slug=${slug}`); + if (resp.status === 200) { + state.reaction = await resp.json(); + render(); + } + }; + init(); +} + +enableThemeToggle(); +enablePrerender(); +enableRssMask(); +if (document.body.classList.contains('post')) { + enableOutdateAlert(); + addBackToTopBtn(); + enableTocTooltip(); +} +if (document.querySelector('.prose')) { + addCopyBtns(); + addFootnoteBacklink(); + enableImgLightense(); + enableReaction(); +} diff --git a/src/themes/serene/templates/404.html b/src/themes/serene/templates/404.html new file mode 100644 index 0000000..1e924de --- /dev/null +++ b/src/themes/serene/templates/404.html @@ -0,0 +1,14 @@ +{% extends "_base.html" %} + +{% block page %}not-found{% endblock page%} +{% block lang %}{{ config.default_language }}{% endblock lang %} +{% block title %}404{% endblock title %} + +{% block content %} +<div class="wrapper"> + <p class="error"> + {{ config.extra.not_found_error_text }} + </p> + <a class="instant" href="{{ get_url(path='/') }}">{{ config.extra.not_found_recover_text }}</a> +</div> +{% endblock %} diff --git a/src/themes/serene/templates/_base.html b/src/themes/serene/templates/_base.html new file mode 100644 index 0000000..0bd7b3b --- /dev/null +++ b/src/themes/serene/templates/_base.html @@ -0,0 +1,38 @@ +<!DOCTYPE html> + +<html lang="{% block lang %}{% endblock lang %}"> + +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta name="color-scheme" content="light dark"> + {% block desc %}{% endblock desc %} + <title>{% block title %}{% endblock title %}</title> + <link rel="icon" type="image/png" sizes="32x32" href="{{ get_url(path='img/favicon-32x32.png') }}"> + <link rel="icon" type="image/png" sizes="16x16" href="{{ get_url(path='img/favicon-16x16.png') }}"> + <link rel="apple-touch-icon" sizes="180x180" href="{{ get_url(path='img/apple-touch-icon.png') }}"> + {% include "_custom_font.html" %} + {% include "_custom_css.html" %} + <link rel="stylesheet" href="{{ get_url(path='main.css') }}"> + {% block head %}{% endblock head %} + {% include "_head_extend.html" %} +</head> + +<body class="{% block page %}{% endblock page%}{% if config.extra.force_theme == "dark" %} dark{% endif %}"> + {% if not config.extra.force_theme %} + <script> + const theme = sessionStorage.getItem('theme'); + const match = window.matchMedia("(prefers-color-scheme: dark)").matches + if ((theme && theme == 'dark') || (!theme && match)) { + document.body.classList.add('dark'); + const hl = document.querySelector('link#hl'); + if (hl) hl.href = '{{ get_url(path="hl-dark.css") }}'; + } + </script> + {% endif %} + {% block content %}{% endblock content %} + {% block script %}{% endblock script %} + <script src="{{ get_url(path='js/main.js') }}"></script> +</body> + +</html> diff --git a/src/themes/serene/templates/_custom_css.html b/src/themes/serene/templates/_custom_css.html new file mode 100644 index 0000000..f767a33 --- /dev/null +++ b/src/themes/serene/templates/_custom_css.html @@ -0,0 +1,59 @@ +<style> + + /* light mode colors */ + body { + --primary-color: #5871a2; + --primary-pale-color: #5871a233; + --primary-decoration-color: #5871a210; + --bg-color: #ffffff; + --text-color: #2f3030; + --text-pale-color: #767676; + --text-decoration-color: #a9a9a9; + --highlight-mark-color: #5f75b020; + + --callout-note-color: #5871a2; + --callout-tip-color: #268556; + --callout-important-color: #885fc9; + --callout-warning-color: #ab6632; + --callout-caution-color: #c64e4e; + } + + /* dark mode colors */ + body.dark { + --primary-color: #6f8fd1; + --primary-pale-color: #6f8fd166; + --primary-decoration-color: #6f8fd112; + --bg-color: #1c1c1c; + --text-color: #c1c1c1; + --text-pale-color: #848484; + --text-decoration-color: #5f5f5f; + --highlight-mark-color: #8296cb3b; + + --callout-note-color: #6f8fd1; + --callout-tip-color: #47976f; + --callout-important-color: #9776cd; + --callout-warning-color: #ad7a52; + --callout-caution-color: #d06161; + } + + /* typography */ + body { + --main-font: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --code-font: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace; + --homepage-max-width: 768px; + --main-max-width: 768px; + --avatar-size: 60px; + --font-size: 16px; + --line-height: 1.75; + --img-border-radius: 0px; + --detail-border-radius: 0px; + --dark-mode-img-brightness: 0.75; + --dark-mode-chart-brightness: 0.75; + --inline-code-border-radius: 2px; + --inline-code-bg-color: var(--primary-decoration-color); + --block-code-border-radius: 0px; + --block-code-border-color: var(--primary-color); + --detail-border-color: var(--primary-color); + } + +</style> diff --git a/src/themes/serene/templates/_custom_font.html b/src/themes/serene/templates/_custom_font.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/themes/serene/templates/_custom_font.html diff --git a/src/themes/serene/templates/_footer.html b/src/themes/serene/templates/_footer.html new file mode 100644 index 0000000..e9c1dd1 --- /dev/null +++ b/src/themes/serene/templates/_footer.html @@ -0,0 +1,52 @@ +{% if config.extra.blog_section_path is defined %} +{% set blog_section_path = config.extra.blog_section_path %} +{% endif %} +<footer> + <div class="left"> + <div class="copyright"> + {{ config.extra.footer_copyright }} + {% if config.extra.footer_credits %} + <span>|</span> + Built with <a href="https://www.getzola.org" rel="noreferrer" target="_blank">zola</a> and <a href="https://github.com/isunjn/serene" rel="noreferrer" target="_blank">serene</a> + {% endif %} + </div> + </div> + + <div class="right"> + {% if blog_section_path is defined and section.path is starting_with(blog_section_path) %} + {% if section.generate_feeds %} + {% set_global rss_path = blog_section_path ~ "/" ~ config.feed_filenames.0 %} + {% elif config.generate_feeds %} + {% set_global rss_path = "/" ~ config.feed_filenames.0 %} + {% endif %} + {% if section.generate_feeds or config.generate_feeds %} + <a id="rss-btn" href="{{ get_url(path=rss_path) }}">RSS</a> + {% endif %} + {% endif %} + + {% if not config.extra.force_theme %} + {% set moon_icon = load_data(path="icon/moon.svg") %} + {% set sun_icon = load_data(path="icon/sun.svg") %} + <button id="theme-toggle" aria-label="theme switch"> + <span class="moon-icon">{{ moon_icon | safe }}</span> + <span class="sun-icon">{{ sun_icon | safe }}</span> + </button> + {% endif %} + </div> +</footer> + +{% if blog_section_path is defined and section.path is starting_with(blog_section_path) %} +{% if section.generate_feeds or config.generate_feeds %} +{% set link = get_url(path=rss_path) %} +<dialog id="rss-mask"> + <div> + <a href="{{ link }}">{{ link }}</a> + {% set copy_icon = load_data(path="icon/copy.svg") %} + {% set check_icon = load_data(path="icon/check.svg") %} + <button autofocus aria-label="copy" data-link="{{ link }}" data-copy-icon="{{ copy_icon }}" data-check-icon="{{ check_icon }}" > + {{ copy_icon | safe }} + </button> + </div> +</dialog> +{% endif %} +{% endif %} diff --git a/src/themes/serene/templates/_giscus_script.html b/src/themes/serene/templates/_giscus_script.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/themes/serene/templates/_giscus_script.html diff --git a/src/themes/serene/templates/_head_extend.html b/src/themes/serene/templates/_head_extend.html new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/themes/serene/templates/_head_extend.html diff --git a/src/themes/serene/templates/_section_title.html b/src/themes/serene/templates/_section_title.html new file mode 100644 index 0000000..191e079 --- /dev/null +++ b/src/themes/serene/templates/_section_title.html @@ -0,0 +1,10 @@ +{% if section.extra.title or section.extra.subtitle %} +<div class="section-title"> + {% if section.extra.title %} + <h1>{{ section.extra.title }}</h1> + {% endif %} + {% if section.extra.subtitle %} + <p>{{ section.extra.subtitle }}</p> + {% endif %} +</div> +{% endif %} diff --git a/src/themes/serene/templates/anchor-link.html b/src/themes/serene/templates/anchor-link.html new file mode 100644 index 0000000..91e875b --- /dev/null +++ b/src/themes/serene/templates/anchor-link.html @@ -0,0 +1 @@ +<a class="zola-anchor" href="#{{ id }}" aria-label="Anchor link for: {{ id }}" style="visibility: hidden;"></a> diff --git a/src/themes/serene/templates/blog.html b/src/themes/serene/templates/blog.html new file mode 100644 index 0000000..2519d28 --- /dev/null +++ b/src/themes/serene/templates/blog.html @@ -0,0 +1,50 @@ +{% import "macros/prose.html" as macros %} +{% extends "_base.html" %} + +{% block page %}blog{% endblock page%} +{% block lang %}{% if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ section.lang }}{% endif %}{% endblock lang %} +{% block title %}{{ section.title }}{% endblock title %} +{% block desc %} + {% if section.description %} + {% set desc = section.description %} + {% else %} + {% set desc = config.description %} + {% endif %} + <meta name="description" content="{{ desc }}"> +{% endblock desc %} + +{% block content %} +<div id="wrapper"> + {{ macros::back_link(path = get_url(path="/")) }} + {% include "_section_title.html" %} + <main class="layout-list"> + {% if section.extra.categorized %} + {% for category,posts in section.pages | sort(attribute="taxonomies.categories.0") | group_by(attribute="taxonomies.categories.0") %} + {% set category_name = category %} + {% if category is matching("^__[0-9]{2}__") %} + {% set category_name = category | split(pat="") | slice(start=7) | join(sep="") %} + {% endif %} + <h2 class="category">{{ category_name }}</h2> + <div class="post-list categorized"> + {% for post in posts %} + <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}"> + <span>{{ post.title }}</span> + <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span> + </a> + {% endfor %} + </div> + {% endfor %} + {% else %} + <div class="post-list"> + {% for post in section.pages %} + <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}"> + <span>{{ post.title }}</span> + <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span> + </a> + {% endfor %} + </div> + {% endif %} + </main> + {% include "_footer.html" %} +</div> +{% endblock content %} diff --git a/src/themes/serene/templates/categories/list.html b/src/themes/serene/templates/categories/list.html new file mode 100644 index 0000000..9069f6b --- /dev/null +++ b/src/themes/serene/templates/categories/list.html @@ -0,0 +1,3 @@ +<script> + window.location.assign('{{ get_url(path="/") }}'); +</script> diff --git a/src/themes/serene/templates/categories/single.html b/src/themes/serene/templates/categories/single.html new file mode 100644 index 0000000..9069f6b --- /dev/null +++ b/src/themes/serene/templates/categories/single.html @@ -0,0 +1,3 @@ +<script> + window.location.assign('{{ get_url(path="/") }}'); +</script> diff --git a/src/themes/serene/templates/feed.xml b/src/themes/serene/templates/feed.xml new file mode 100644 index 0000000..fe1f139 --- /dev/null +++ b/src/themes/serene/templates/feed.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="{{ lang }}"> + <title>{% if section.title %}{{ section.title }}{% else %}{{ config.title }}{% endif %}</title> + {%- if section.description %} + <subtitle>{{ section.description }}</subtitle> + {%- elif config.description %} + <subtitle>{{ config.description }}</subtitle> + {%- endif %} + <link href="{{ feed_url | safe }}" rel="self" type="application/atom+xml"/> + <link href=" + {%- if section -%} + {{ section.permalink | escape_xml | safe }} + {%- else -%} + {{ config.base_url | escape_xml | safe }} + {%- endif -%} + "/> + <updated>{{ last_updated | date(format="%+") }}</updated> + <id>{{ feed_url | safe }}</id> + {%- for page in pages %} + <entry xml:lang="{{ page.lang }}"> + <title>{{ page.title }}</title> + <published>{{ page.date | date(format="%+") }}</published> + <updated>{{ page.updated | default(value=page.date) | date(format="%+") }}</updated> + {%- if page.summary %} + <summary>{{ page.summary }}</summary> + {%- endif %} + <link href="{{ page.permalink | safe }}" type="text/html"/> + <id>{{ page.permalink | safe }}</id> + <content type="html">{{ page.content }}</content> + </entry> + {%- endfor %} +</feed> \ No newline at end of file diff --git a/src/themes/serene/templates/home.html b/src/themes/serene/templates/home.html new file mode 100644 index 0000000..f899884 --- /dev/null +++ b/src/themes/serene/templates/home.html @@ -0,0 +1,119 @@ +{% extends "_base.html" %} + +{% block page %}homepage{% endblock page%} +{% block lang %}{% if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ section.lang }}{% endif %}{% endblock lang %} +{% block title %}{{ config.title }}{% endblock title %} +{% block desc %} + <meta name="description" content="{{ config.description }}"> +{% endblock desc %} + +{% block head %} +{% if config.markdown.highlight_theme == "css" %} + {% if config.extra.force_theme == "dark" %} + <link id="hl" rel="stylesheet" type="text/css" href="{{ get_url(path='hl-dark.css') }}" /> + {% else %} + <link id="hl" rel="stylesheet" type="text/css" href="{{ get_url(path='hl-light.css') }}" /> + {% endif %} +{% endif %} +{% if section.extra.math %} +<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous"> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/copy-tex.min.js" integrity="sha384-HORx6nWi8j5/mYA+y57/9/CZc5z8HnEw4WUZWy5yOn9ToKBv1l58vJaufFAn9Zzi" crossorigin="anonymous"></script> +<script> + document.addEventListener("DOMContentLoaded", function () { + renderMathInElement(document.body, { + delimiters: [ + { left: '$$', right: '$$', display: true }, + { left: '$', right: '$', display: false }, + { left: '\\(', right: '\\)', display: false }, + { left: '\\[', right: '\\]', display: true } + ], + throwOnError: false + }); + }); +</script> +{% endif %} +{% endblock head %} + +{% block content %} +<div id="wrapper"> + <main> + <section id="info"> + {% if section.extra.avatar %} + <img src="{{ get_url(path=section.extra.avatar) }}" alt="avatar"> + {% endif %} + <div id="text"> + <div> + <span id="name">{{ section.extra.name }}</span> + {% if section.extra.id %} + <span id="id">@{{ section.extra.id }}</span> + {% endif %} + </div> + {% if section.extra.bio %} + <div id="bio"> {{ section.extra.bio }} </div> + {% endif %} + </div> + </section> + <section id="links"> + <div id="left"> + {% for section in config.extra.sections %} + <a {% if section.is_external %}href="{{ section.path }}" target="_blank" rel='noreferrer noopener'{% else %}href="{{ get_url(path=section.path) }}" class="instant"{% endif %}>{{ section.name }}</a> + {% endfor %} + </div> + <div id="right"> + {% for link in section.extra.links %} + <a href="{{ link.url }}" aria-label="{{ link.name }}" target="_blank" rel='noreferrer noopener'> + {% set icon_path = "icon/" ~ link.icon ~ ".svg" %} + {% set icon = load_data(path=icon_path) %} + {{ icon | safe }} + </a> + {% endfor %} + {% if not section.extra.footer and not config.extra.force_theme %} + {% set moon_icon = load_data(path="icon/moon.svg") %} + {% set sun_icon = load_data(path="icon/sun.svg") %} + <button id="theme-toggle" aria-label="theme switch"> + <span class="moon-icon">{{ moon_icon | safe }}</span> + <span class="sun-icon">{{ sun_icon | safe }}</span> + </button> + {% endif %} + </div> + </section> + <section id="brief" class="prose"> + {{ section.content | trim | safe }} + </section> + {% if section.extra.recent %} + {% set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") %} + {% set section_md_path = blog_section_path ~ "/_index.md" %} + {% set blog_section = get_section(path=section_md_path) %} + <section class="layout-list"> + <div class="post-list"> + {% for post in blog_section.pages | slice(end=section.extra.recent_max) %} + <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}"> + <span>{{ post.title }}</span> + <span class="line"></span> + <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span> + </a> + {% endfor %} + </div> + <div class="read-more"> + <a class="instant" href="{{ config.extra.blog_section_path }}">{{ section.extra.recent_more_text }}</a> + </div> + </section> + {% endif %} + </main> + {% if section.extra.footer %} + {% include "_footer.html" %} + {% endif %} +</div> +{% endblock content %} + +{% block script %} +<script src="{{ get_url(path='js/lightense.min.js') }}"></script> +{% if section.extra.mermaid %} +<script type="module"> + import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; + mermaid.initialize({ startOnLoad: true }); +</script> +{% endif %} +{% endblock script %} diff --git a/src/themes/serene/templates/macros/collection.html b/src/themes/serene/templates/macros/collection.html new file mode 100644 index 0000000..796eae1 --- /dev/null +++ b/src/themes/serene/templates/macros/collection.html @@ -0,0 +1,153 @@ +{% macro card(item) %} +<blockquote class="collection card{% if item.featured %} featured{% endif %}"> + <div class="meta"> + {% if item.icon %} + <div class="icon-wrapper"> + <img class="icon no-lightense" src="{{ item.icon }}" alt="icon" width="16" height="16"> + </div> + {% endif %} + {% if item.link %} + <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener">{{ item.title }}</a> + {% else %} + <div class="title">{{ item.title }}</div> + {% endif %} + {% if item.date %} + <div class="date">{{ item.date }}</div> + {% endif %} + </div> + {% if item.subtitle %} + <div class="subtitle">{{ item.subtitle }}</div> + {% endif %} + <div class="content">{{ item.content | trim | markdown | safe }}</div> + {% if item.tags %} + <div class="tags"> + {% for tag in item.tags %} + <div><span>#</span>{{ tag }}</div> + {% endfor %} + </div> + {% endif %} +</blockquote> +{% endmacro %} + + +{% macro card_simple(item) %} +<blockquote class="collection card-simple{% if item.featured %} featured{% endif %}"> + <div class="meta"> + {% if item.icon %} + <div class="icon-wrapper"> + <img class="icon no-lightense" src="{{ item.icon }}" alt="icon" width="16" height="16"> + </div> + {% endif %} + {% if item.link %} + <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener">{{ item.title }}</a> + {% else %} + <div class="title">{{ item.title }}</div> + {% endif %} + <div class="content">{{ item.content | trim | markdown | safe }}</div> + {% if item.date %} + <div class="date">{{ item.date }}</div> + {% endif %} + </div> + <div class="content-narrow">{{ item.content | trim | markdown | safe }}</div> +</blockquote> +{% endmacro %} + + +{% macro entry(item) %} +{% if item.link %} +<a href="{{ item.link }}" target="_blank" rel="noreferrer noopener" class="collection entry"> +{% else %} +<blockquote class="collection entry"> +{% endif %} + {% if item.icon %} + <div class="icon-wrapper"> + <img class="icon no-lightense" src="{{ item.icon }}" alt="icon" width="16" height="16" /> + </div> + {% endif %} + {% if item.title or item.subtitle %} + <div class="text"> + {% if item.title %} + <div class="title">{{ item.title }}</div> + {% endif %} + {% if item.subtitle %} + <div class="subtitle">{{ item.subtitle }}</div> + {% endif %} + </div> + {% endif %} +{% if item.link %} +</a> +{% else %} +</blockquote> +{% endif %} +{% endmacro %} + + +{% macro box(item) %} +<div class="collection-box-wrapper"> +{% if item.link %} +<a href="{{ item.link }}" target="_blank" rel="noreferrer noopener" class="collection box"> +{% else %} +<blockquote class="collection box"> +{% endif %} + <div class="text"> + <div class="title">{{ item.title }}</div> + {% if item.subtitle %} + <div class="subtitle">{{ item.subtitle }}</div> + {% endif %} + </div> + {% if item.img %} + <img class="no-lightense{% if item.rotate %} rotate{% endif %}" src="{{ item.img }}" alt="{{ item.title }}" width="48" height="48" /> + {% else %} + <div class="placehold"></div> + {% endif %} +{% if item.link %} +</a> +{% else %} +</blockquote> +{% endif %} +</div> +{% endmacro %} + + +{% macro art(item) %} +<blockquote class="collection art"> + <div class="img-wrapper"> + <img src="{{ item.img }}" alt="{{ item.title }}" height="175"> + </div> + <div class="text"> + {% if item.link %} + <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener" >{{ item.title }}</a> + {% else %} + <div class="title">{{ item.title }}</div> + {% endif %} + {% if item.subtitle %} + <div class="subtitle">{{ item.subtitle }}</div> + {% endif %} + {% if item.content %} + <div class="content">{{ item.content }}</div> + {% endif %} + {% if item.footer %} + <div class="footer">{{ item.footer }}</div> + {% endif %} + </div> +</blockquote> +{% endmacro %} + + +{% macro art_simple(item) %} +<blockquote class="collection art-simple"> + <div class="img-wrapper"> + <img src="{{ item.img }}" alt="{{ item.title }}" height="175"> + </div> + <div class="text"> + {% if item.link %} + <a class="title" href="{{ item.link }}" target="_blank" rel="noreferrer noopener" >{{ item.title }}</a> + {% else %} + <div class="title">{{ item.title }}</div> + {% endif %} + {% if item.subtitle %} + <div class="subtitle">{{ item.subtitle }}</div> + {% endif %} + </div> +</blockquote> +{% endmacro %} diff --git a/src/themes/serene/templates/macros/prose.html b/src/themes/serene/templates/macros/prose.html new file mode 100644 index 0000000..bd9a894 --- /dev/null +++ b/src/themes/serene/templates/macros/prose.html @@ -0,0 +1,32 @@ +{% macro back_link(path) %} +<header> + <nav> + <a href="{{ path }}">← {{ config.extra.back_link_text }}</a> + </nav> +</header> +{% endmacro %} + + +{% macro callout(name) %} +<blockquote class="callout {{ name }} {% if title %}has-title{% else %}no-title{% endif %}"> + {% set icon = load_data(path="icon/" ~ name ~ ".svg") %} + {% if title %} + <p class="title"> + <span class="icon"> + {{ icon | safe }} + </span> + <strong>{{ title }}</strong> + </p> + <div class="content"> + {{ body | markdown | safe }} + </div> + {% else %} + <div class="icon"> + {{ icon | safe }} + </div> + <div class="content"> + {{ body | markdown | safe }} + </div> + {% endif %} +</blockquote> +{% endmacro %} diff --git a/src/themes/serene/templates/post.html b/src/themes/serene/templates/post.html new file mode 100644 index 0000000..24fe0cd --- /dev/null +++ b/src/themes/serene/templates/post.html @@ -0,0 +1,151 @@ +{% import "macros/prose.html" as macros %} +{% extends "_base.html" %} + +{% block page %}post{% endblock page %} +{% block lang -%} +{%- set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") -%} +{%- set section_md_path = blog_section_path ~ "/_index.md" -%} +{%- set section = get_section(path=section_md_path, metadata_only=true) -%} +{%- if page.extra.lang %}{{page.extra.lang}}{% elif section.extra.lang %}{{section.extra.lang}}{% else %}{{page.lang}}{% endif -%} +{%- endblock lang %} +{% block title %}{{ page.title }}{% endblock title %} +{% block desc %} + {% if page.summary %} + {% set desc = page.summary %} + {% elif page.description %} + {% set desc = page.description %} + {% elif section.description %} + {% set desc = section.description %} + {% else %} + {% set desc = config.description %} + {% endif %} + <meta name="description" content="{{ desc }}"> +{% endblock desc %} + +{% block head %} +{% if config.markdown.highlight_theme == "css" %} +<link id="hl" rel="stylesheet" type="text/css" href="/hl-{% if config.extra.force_theme == "dark" %}dark{% else %}light{% endif %}.css" /> +{% endif %} +{% if page.extra.math %} +<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous"> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/copy-tex.min.js" integrity="sha384-HORx6nWi8j5/mYA+y57/9/CZc5z8HnEw4WUZWy5yOn9ToKBv1l58vJaufFAn9Zzi" crossorigin="anonymous"></script> +<script> + document.addEventListener("DOMContentLoaded", function () { + renderMathInElement(document.body, { + delimiters: [ + { left: '$$', right: '$$', display: true }, + { left: '$', right: '$', display: false }, + { left: '\\(', right: '\\)', display: false }, + { left: '\\[', right: '\\]', display: true } + ], + throwOnError: false + }); + }); +</script> +{% endif %} +{% endblock head %} + +{% block content %} +<div id="wrapper"> + <div id="blank"></div> + <aside> + {% if page.extra.toc is defined %}{% set show_toc = page.extra.toc %}{% else %}{% set show_toc = section.extra.toc %}{% endif %} + {% if show_toc and page.toc %} + <nav> + <ul> + {% for h2 in page.toc %} + <li> + <a class="h2" href="#{{ h2.id | safe }}">{{ h2.title }}</a> + {% if h2.children %} + <ul> + {% for h3 in h2.children %} + <li> + <a class="h3" href="#{{ h3.id | safe }}">{{ h3.title }}</a> + </li> + {% endfor %} + </ul> + {% endif %} + </li> + {% endfor %} + </ul> + </nav> + {% endif %} + {% if section.extra.back_to_top %} + <button id="back-to-top" aria-label="back to top"> + {% set icon = load_data(path="icon/arrow-up.svg") %} + {{ icon | safe }} + </button> + {% endif %} + </aside> + <main> + {{ macros::back_link(path = get_url(path=section.path)) }} + + <div> + {% if page.extra.copy is defined %}{% set allow_copy = page.extra.copy %}{% else %}{% set allow_copy = section.extra.copy %}{% endif %} + {% if allow_copy %} + {% set copy_icon = load_data(path="icon/copy.svg") %} + {% set check_icon = load_data(path="icon/check.svg") %} + <div id="copy-cfg" style="display: none;" data-copy-icon="{{ copy_icon }}" data-check-icon="{{ check_icon }}"></div> + {% endif %} + <article class="prose"> + <h1>{{ page.title }}</h1> + <div id="post-info"> + <div id="date"> + <span id="publish">{{ page.date | date(format=section.extra.date_format) }}</span> + {% if page.updated and page.updated != page.date -%} + <span>Updated on <span id="updated">{{ page.updated | date(format=section.extra.date_format) }}</span></span> + {% endif -%} + </div> + + {% if page.taxonomies.tags is defined %} + <div id="tags"> + {% for tag in page.taxonomies.tags -%} + {% set tag_slugify = tag | slugify -%} + <a class="instant" href="{{ config.base_url ~ '/tags/' ~ tag_slugify | safe }}"><span>#</span>{{ tag }}</a> + {%- endfor %} + </div> + {% endif %} + </div> + + {% if page.extra.outdate_alert is defined %}{% set show_outdate_alert = page.extra.outdate_alert %}{% else %}{% set show_outdate_alert = section.extra.outdate_alert %}{% endif %} + {% if page.extra.outdate_alert_days is defined %}{% set outdate_alert_days = page.extra.outdate_alert_days %}{% else %}{% set outdate_alert_days = section.extra.outdate_alert_days %}{% endif %} + + {% if show_outdate_alert -%} + <blockquote id="outdate_alert" class="callout caution hidden" data-days="{{ outdate_alert_days }}" + data-alert-text-before="{{ section.extra.outdate_alert_text_before }}" + data-alert-text-after="{{ section.extra.outdate_alert_text_after }}"> + <div class="content"></div> + </blockquote> + {% endif %} + + {{ page.content | safe }} + </article> + + {% if page.extra.reaction is defined %}{% set show_reaction = page.extra.reaction %}{% else %}{% set show_reaction = config.extra.reaction %}{% endif %} + {% if show_reaction %} + <div class="reaction {{ config.extra.reaction_align }}" data-endpoint="{{ config.extra.reaction_endpoint }}"></div> + {% endif %} + + {% if page.extra.comment is defined %}{% set show_comment = page.extra.comment %}{% else %}{% set show_comment = section.extra.comment %}{% endif %} + {% if show_comment %} + <div class="giscus"></div> + {% include "_giscus_script.html" %} + {% endif %} + </div> + + {% include "_footer.html" %} + </main> +</div> +{% endblock content %} + +{% block script %} +<script src="/js/lightense.min.js"></script> +{% if page.extra.mermaid %} +<script type="module"> + import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; + mermaid.initialize({ startOnLoad: true }); +</script> +{% endif %} +{% endblock script %} diff --git a/src/themes/serene/templates/prose.html b/src/themes/serene/templates/prose.html new file mode 100644 index 0000000..661c408 --- /dev/null +++ b/src/themes/serene/templates/prose.html @@ -0,0 +1,82 @@ +{% import "macros/prose.html" as macros %} +{% extends "_base.html" %} + +{% block page %}prose-page{% endblock page %} +{% block lang -%} +{%- if section.extra.lang %}{{section.extra.lang}}{% else %}{{section.lang}}{% endif -%} +{%- endblock lang %} +{% block title %}{{ section.title }}{% endblock title %} +{% block desc %} + {% if section.description %} + {% set desc = section.description %} + {% else %} + {% set desc = config.description %} + {% endif %} + <meta name="description" content="{{ desc }}"> +{% endblock desc %} + +{% block head %} +{% if config.markdown.highlight_theme == "css" %} +<link id="hl" rel="stylesheet" type="text/css" href="/hl-{% if config.extra.force_theme == "dark" %}dark{% else %}light{% endif %}.css" /> +{% endif %} +{% if section.extra.math %} +<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.css" integrity="sha384-nB0miv6/jRmo5UMMR1wu3Gz6NLsoTkbqJghGIsx//Rlm+ZU03BU6SQNC66uf4l5+" crossorigin="anonymous"> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/katex.min.js" integrity="sha384-7zkQWkzuo3B5mTepMUcHkMB5jZaolc2xDwL6VFqjFALcbeS9Ggm/Yr2r3Dy4lfFg" crossorigin="anonymous"></script> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/auto-render.min.js" integrity="sha384-43gviWU0YVjaDtb/GhzOouOXtZMP/7XUzwPTstBeZFe/+rCMvRwr4yROQP43s0Xk" crossorigin="anonymous"></script> +<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.11/dist/contrib/copy-tex.min.js" integrity="sha384-HORx6nWi8j5/mYA+y57/9/CZc5z8HnEw4WUZWy5yOn9ToKBv1l58vJaufFAn9Zzi" crossorigin="anonymous"></script> +<script> + document.addEventListener("DOMContentLoaded", function () { + renderMathInElement(document.body, { + delimiters: [ + { left: '$$', right: '$$', display: true }, + { left: '$', right: '$', display: false }, + { left: '\\(', right: '\\)', display: false }, + { left: '\\[', right: '\\]', display: true } + ], + throwOnError: false + }); + }); +</script> +{% endif %} +{% endblock head %} + +{% block content %} +<div id="wrapper"> + <main> + {{ macros::back_link(path = get_url(path="/")) }} + {% include "_section_title.html" %} + <div> + {% if section.extra.copy %} + {% set copy_icon = load_data(path="icon/copy.svg") %} + {% set check_icon = load_data(path="icon/check.svg") %} + <div id="copy-cfg" style="display: none;" data-copy-icon="{{ copy_icon }}" data-check-icon="{{ check_icon }}"></div> + {% endif %} + <article class="prose"> + {{ section.content | safe }} + </article> + + {% if section.extra.reaction is defined %}{% set show_reaction = section.extra.reaction %}{% else %}{% set show_reaction = config.extra.reaction %}{% endif %} + {% if show_reaction %} + <div class="reaction {{ config.extra.reaction_align }}" data-endpoint="{{ config.extra.reaction_endpoint }}"></div> + {% endif %} + + {% if section.extra.comment %} + <div class="giscus"></div> + {% include "_giscus_script.html" %} + {% endif %} + </div> + + {% include "_footer.html" %} + </main> +</div> +{% endblock content %} + +{% block script %} +<script src="/js/lightense.min.js"></script> +{% if section.extra.mermaid %} +<script type="module"> + import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; + mermaid.initialize({ startOnLoad: true }); +</script> +{% endif %} +{% endblock script %} diff --git a/src/themes/serene/templates/shortcodes/caution.html b/src/themes/serene/templates/shortcodes/caution.html new file mode 100644 index 0000000..7d1797f --- /dev/null +++ b/src/themes/serene/templates/shortcodes/caution.html @@ -0,0 +1,2 @@ +{% import "macros/prose.html" as macros %} +{{ macros::callout(name="caution") }} diff --git a/src/themes/serene/templates/shortcodes/collection.html b/src/themes/serene/templates/shortcodes/collection.html new file mode 100644 index 0000000..c217a0c --- /dev/null +++ b/src/themes/serene/templates/shortcodes/collection.html @@ -0,0 +1,23 @@ +{% import 'macros/collection.html' as marcos -%} + +{% set data = load_data(path="content" ~ section.path ~ file, format="toml") %} + +<section class="collection-wrapper"> +{% for item in data.collection %} + {% if item.type == "card" %} + {{ marcos::card(item=item) }} + {% elif item.type == "card_simple" %} + {{ marcos::card_simple(item=item) }} + {% elif item.type == "entry" %} + {{ marcos::entry(item=item) }} + {% elif item.type == "box" %} + {{ marcos::box(item=item) }} + {% elif item.type == "art" %} + {{ marcos::art(item=item) }} + {% elif item.type == "art_simple" %} + {{ marcos::art_simple(item=item) }} + {% elif item.type == "br" %} + <br /> + {% endif %} +{% endfor %} +</section> diff --git a/src/themes/serene/templates/shortcodes/detail.html b/src/themes/serene/templates/shortcodes/detail.html new file mode 100644 index 0000000..0d34a74 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/detail.html @@ -0,0 +1,4 @@ +<details {% if default_open %}open{% endif %}> + <summary><span>{{ title }}</span></summary> + {{ body | markdown | safe }} +</details> diff --git a/src/themes/serene/templates/shortcodes/figure.html b/src/themes/serene/templates/shortcodes/figure.html new file mode 100644 index 0000000..8a4123f --- /dev/null +++ b/src/themes/serene/templates/shortcodes/figure.html @@ -0,0 +1,8 @@ +<figure> + <img src="{{ src }}" {% if alt %} alt="{{ alt }}"{% endif %}> + {% if via %} + <figcaption><a href="{{via}}">via</a></figcaption> + {% else %} + <figcaption>{{ caption }}</figcaption> + {% endif %} +</figure> \ No newline at end of file diff --git a/src/themes/serene/templates/shortcodes/important.html b/src/themes/serene/templates/shortcodes/important.html new file mode 100644 index 0000000..451c141 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/important.html @@ -0,0 +1,2 @@ +{% import "macros/prose.html" as macros %} +{{ macros::callout(name="important") }} diff --git a/src/themes/serene/templates/shortcodes/mermaid.html b/src/themes/serene/templates/shortcodes/mermaid.html new file mode 100644 index 0000000..3d3725d --- /dev/null +++ b/src/themes/serene/templates/shortcodes/mermaid.html @@ -0,0 +1,3 @@ +<pre class="mermaid"> + {{ body }} +</pre> \ No newline at end of file diff --git a/src/themes/serene/templates/shortcodes/note.html b/src/themes/serene/templates/shortcodes/note.html new file mode 100644 index 0000000..b16657b --- /dev/null +++ b/src/themes/serene/templates/shortcodes/note.html @@ -0,0 +1,2 @@ +{% import "macros/prose.html" as macros %} +{{ macros::callout(name="note") }} diff --git a/src/themes/serene/templates/shortcodes/quote.html b/src/themes/serene/templates/shortcodes/quote.html new file mode 100644 index 0000000..c7e3c37 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/quote.html @@ -0,0 +1,10 @@ +<blockquote class="quote"> + {% set icon = load_data(path="icon/quote.svg") %} + <div class="icon" style="display: none;">{{ icon | safe }}</div> + <div class="content">{{ body | markdown | safe }}</div> + {% if cite %} + <div class="from"> + {{ "— " ~ cite | markdown | safe }} + </div> + {% endif %} +</blockquote> \ No newline at end of file diff --git a/src/themes/serene/templates/shortcodes/tip.html b/src/themes/serene/templates/shortcodes/tip.html new file mode 100644 index 0000000..8904437 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/tip.html @@ -0,0 +1,2 @@ +{% import "macros/prose.html" as macros %} +{{ macros::callout(name="tip") }} diff --git a/src/themes/serene/templates/shortcodes/warning.html b/src/themes/serene/templates/shortcodes/warning.html new file mode 100644 index 0000000..0964b15 --- /dev/null +++ b/src/themes/serene/templates/shortcodes/warning.html @@ -0,0 +1,2 @@ +{% import "macros/prose.html" as macros %} +{{ macros::callout(name="warning") }} diff --git a/src/themes/serene/templates/tags/list.html b/src/themes/serene/templates/tags/list.html new file mode 100644 index 0000000..bde1123 --- /dev/null +++ b/src/themes/serene/templates/tags/list.html @@ -0,0 +1,29 @@ +{% import "macros/prose.html" as macros %} +{% extends "_base.html" %} + +{% block page %}tag-list{% endblock page%} +{% block lang -%} +{% set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") %} +{% set section_md_path = blog_section_path ~ "/_index.md"%} +{% set section = get_section(path=section_md_path, metadata_only=true) %} +{%- if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ lang }}{% endif -%} +{%- endblock lang %} +{% block title %}Tags{% endblock title %} +{% block desc %} + <meta name="description" content="Tags"> +{% endblock desc %} + +{% block content %} +<div id="wrapper"> + {{ macros::back_link(path = get_url(path="/")) }} + <main> + <h1 class="title">Tags</h1> + <div class="tags"> + {% for tag in terms -%} + <a class="instant" href="{{ tag.permalink | safe }}"># {{ tag.name | lower }}</a> + {% endfor %} + </div> + </main> + {% include "_footer.html" %} +</div> +{% endblock content %} diff --git a/src/themes/serene/templates/tags/single.html b/src/themes/serene/templates/tags/single.html new file mode 100644 index 0000000..1ce059a --- /dev/null +++ b/src/themes/serene/templates/tags/single.html @@ -0,0 +1,34 @@ +{% import "macros/prose.html" as macros %} +{% extends "_base.html" %} + +{% block page %}tag-single{% endblock page %} +{% block lang -%} +{% set blog_section_path = config.extra.blog_section_path | trim_start_matches(pat="/") %} +{% set section_md_path = blog_section_path ~ "/_index.md"%} +{% set section = get_section(path=section_md_path, metadata_only=true) %} +{%- if section.extra.lang %}{{ section.extra.lang }}{% else %}{{ lang }}{% endif -%} +{%- endblock lang %} +{% block title %}{{section.title}}{% endblock title %} +{% block desc %} + <meta name="description" content="{{ term.name }}"> +{% endblock desc %} + +{% block content %} +<div id="wrapper"> + {{ macros::back_link(path = get_url(path="/tags")) }} + <main class="layout-list"> + <div class="title"> + <span># {{ term.name }}</span> + </div> + <div class="post-list"> + {% for post in term.pages %} + <a class="post instant {% if post.extra.featured %}featured{% endif %}" href="{{ post.permalink | safe }}"> + <span>{{ post.title }}</span> + <span class="date">{{ post.date | date(format=section.extra.date_format) }}</span> + </a> + {% endfor %} + </div> + </main> + {% include "_footer.html" %} +</div> +{% endblock content %} diff --git a/src/themes/serene/theme.toml b/src/themes/serene/theme.toml new file mode 100644 index 0000000..30efe2e --- /dev/null +++ b/src/themes/serene/theme.toml @@ -0,0 +1,12 @@ +name = "serene" +description = "A spiffy blog theme for zola" +license = "MIT" +homepage = "https://github.com/isunjn/serene" +min_version = "0.20.0" +demo = "https://serene-demo.pages.dev" + +[author] +name = "isunjn" +homepage = "https://github.com/isunjn" + +[extra] diff --git a/src/writings/index.html b/src/writings/index.html deleted file mode 100644 index 9ada54d..0000000 --- a/src/writings/index.html +++ /dev/null @@ -1,9 +0,0 @@ -<html> - <body> - <h1>My paper like things, provided in the portable document format (pdf)</h1> - <ul> - <li><a href="./philosophy/kant_and_free_software.pdf">My essay on free software and Kantian ethics</a></li> - <li><a href="./chemistry/facharbeit.pdf">My 'Facharbeit' on Raman Spektrometrie</a></li> - </ul> - </body> -</html> diff --git a/treefmt.nix b/treefmt.nix index 78a14d0..e6006d7 100644 --- a/treefmt.nix +++ b/treefmt.nix @@ -18,6 +18,7 @@ treefmt-nix.lib.evalModule pkgs ( }; shellcheck.enable = true; prettier = { + enable = true; settings = { arrowParens = "always"; bracketSameLine = false; @@ -41,10 +42,12 @@ treefmt-nix.lib.evalModule pkgs ( vueIndentScriptAndStyle = false; tabWidth = 4; - overrides = { - files = ["*.js"]; - options.tabwidth = 2; - }; + overrides = [ + { + files = ["*.js"]; + options.tabwidth = 2; + } + ]; }; }; diff --git a/update.sh b/update.sh new file mode 100755 index 0000000..a69eaad --- /dev/null +++ b/update.sh @@ -0,0 +1,3 @@ +#! /usr/bin/env sh + +nix flake update |