Fresh Zola + Linkita install
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
[submodule "themes/linkita"]
|
||||||
|
path = themes/linkita
|
||||||
|
url = https://codeberg.org/salif/linkita.git
|
||||||
19
config.toml
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# The URL the site will be built for
|
||||||
|
base_url = "https://lvl0.xyz"
|
||||||
|
title = "lvl0"
|
||||||
|
theme = "linkita"
|
||||||
|
|
||||||
|
# Whether to automatically compile all Sass files in the sass directory
|
||||||
|
compile_sass = true
|
||||||
|
|
||||||
|
# Whether to build a search index to be used later on by a JavaScript library
|
||||||
|
build_search_index = false
|
||||||
|
|
||||||
|
[markdown]
|
||||||
|
# Whether to do syntax highlighting
|
||||||
|
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
|
||||||
|
highlight_code = false
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
# Put all your custom variables here
|
||||||
|
show_title_as_logo = true
|
||||||
11
content/_index.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
+++
|
||||||
|
title = "lvl0"
|
||||||
|
template = "index.html"
|
||||||
|
+++
|
||||||
|
|
||||||
|
**lvl0 is a starting point.**
|
||||||
|
We build tiny, focused tools that support calm productivity, financial independence, and long-term thinking.
|
||||||
|
|
||||||
|
Our projects embrace minimalism, self-hosting, and the pursuit of autonomy — in tech, finance, and life.
|
||||||
|
|
||||||
|
- [incr](https://incr.lvl0.xyz): track your FIRE journey in shares, not slogans.
|
||||||
29
themes/linkita/.editorconfig
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.{html,js,css,json,yaml,xml}]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.toml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[justfile]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
trim_trailing_whitespace = false
|
||||||
1
themes/linkita/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/node_modules
|
||||||
2
themes/linkita/.prettierignore
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
pnpm-lock.yaml
|
||||||
|
templates/macros.html
|
||||||
4
themes/linkita/.prettierrc.js
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
module.exports = {
|
||||||
|
printWidth: 100,
|
||||||
|
plugins: ["prettier-plugin-tailwindcss"],
|
||||||
|
};
|
||||||
62
themes/linkita/CHANGELOG.md
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this theme will be documented in this file.
|
||||||
|
|
||||||
|
## [Unreleased](https://codeberg.org/salif/linkita/compare/v3.2025_04_22..linkita)
|
||||||
|
|
||||||
|
- Add `extra.taxonomy_sorting` config variable and sort terms in taxonomy pages by the number of posts by default.
|
||||||
|
- Add `newtab` bool to `project.links` in the projects shortcode.
|
||||||
|
- Add localization for Ukrainian, Kazakh, and Russian.
|
||||||
|
|
||||||
|
## [v3.2025_04_22](https://codeberg.org/salif/linkita/compare/v2.2025_04_02.0..v3.2025_04_22)
|
||||||
|
|
||||||
|
- Add frontmatter variables to override the `extra.style` config variable:
|
||||||
|
`extra.style.bg_color`, `extra.style.bg_dark_color`, `extra.style.header_color`, `extra.style.header_dark_color`.
|
||||||
|
- Allow `pages.html` and `archive.html` to be used in a section.
|
||||||
|
- Add `extra.relative_urls` config variable to use relative urls that do not contain the base url.
|
||||||
|
- Add `extra.urls_to_index_html` config variable to browse the site without a webserver.
|
||||||
|
- Remove the `date_format_archive` config variable and add `extra.date_format`
|
||||||
|
frontmatter variable for the `archive.html` template.
|
||||||
|
- Change the `extra.page_info` variable to an array of objects.
|
||||||
|
- Add `extra.page_info_on_paginator` config variable. The frontmatter variable is `extra.page_info`,
|
||||||
|
but its default value is not the `extra.page_info` config variable.
|
||||||
|
- Remove `extra.open_graph.video` and `extra.open_graph.audio` frontmatter variables.
|
||||||
|
- Use page slug in `archive.html` and `taxonomy_list.html` when page title is empty.
|
||||||
|
|
||||||
|
## [v2.2025_04_02.0](https://codeberg.org/salif/linkita/compare/v1.2025_01_04.0..v2.2025_04_02.0)
|
||||||
|
|
||||||
|
- Rename the local storage key for the color scheme.
|
||||||
|
- Always show translation button on bilingual pages.
|
||||||
|
- Add support for `extra.profile` frontmatter variable in the `pages.html` and `archive.html` templates.
|
||||||
|
- Remove `extra.open_graph.cover_type` frontmatter variable.
|
||||||
|
- Add localization for Simplified Chinese, Turkish, Arabic, Korean, Japanese, French, and Spanish.
|
||||||
|
- Add localization for Czech.
|
||||||
|
- Add `extra.post_navigation` config variable and reverse post navigation direction.
|
||||||
|
It can be reverted by setting `extra.post_navigation = "reversed"` config variable.
|
||||||
|
- Deprecate `extra.goatcounter.src` config variable.
|
||||||
|
- Add social icons for `linkedin`, `mastodon`, `matrix`, `youtube`.
|
||||||
|
- Self-host KaTeX, instantpage, and gc. It can be reverted by setting `extra.use_cdn = true` config variable.
|
||||||
|
- Rename `extra.open_graph.cover_width` frontmatter variable to `extra.cover.width` and
|
||||||
|
`extra.open_graph.cover_height` to `extra.cover.height`.
|
||||||
|
- Rename `extra.languages[lang].art_x_lang` config variable to `extra.languages[lang].language_code`.
|
||||||
|
- Convert the project template to shortcode. Use the `pages.html` template instead and
|
||||||
|
use `projects(path="data.toml", format="toml")` shortcode.
|
||||||
|
- Add `extra.toc` config and frontmatter variable. Table of contents can be disabled by setting to `false`.
|
||||||
|
It can be expanded by default by setting `toc = { open = true }`.
|
||||||
|
- Add `head_end` inject point and put `injects/head` before js and css files.
|
||||||
|
You may need to rename your `injects/head.html` file to `injects/head_end.html`.
|
||||||
|
- Remove the `extra.post_navigation` config variable and add `extra.invert_page_navigation` config variable.
|
||||||
|
- Remove the `email` and `url` config variables of `extra.profiles.your_username` as they are not used anywhere.
|
||||||
|
- Replace `taxonomy_list_description` with `taxonomy_descriptions[taxonomy.name]` and
|
||||||
|
`taxonomy_single_description` with `term_descriptions[taxonomy.name]`.
|
||||||
|
- Rename the `width-scroll` class to `horizontal-scroll`.
|
||||||
|
- Apply linebreaksbr and truncate to title and description in head.
|
||||||
|
- Add `extra.page_summary_on_paginator` config and frontmatter variable to prioritize summary over description.
|
||||||
|
|
||||||
|
## [v1.2025_01_04.0](https://codeberg.org/salif/linkita/compare/v0.2024_11_01.0..v1.2025_01_04.0)
|
||||||
|
|
||||||
|
See git commits.
|
||||||
|
|
||||||
|
## [v0.2024_11_01.0](https://codeberg.org/salif/linkita/compare/e8746d1a74..v0.2024_11_01.0)
|
||||||
|
|
||||||
|
See git commits.
|
||||||
21
themes/linkita/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2023 st1020
|
||||||
|
Copyright (c) 2024-2025 Salif Mehmed
|
||||||
|
|
||||||
|
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.
|
||||||
763
themes/linkita/README.md
Normal file
|
|
@ -0,0 +1,763 @@
|
||||||
|
# Linkita
|
||||||
|
|
||||||
|
A clean and elegant blog theme for [Zola](https://www.getzola.org/).
|
||||||
|
Linkita is based on [Kita](https://github.com/st1020/kita) and [Hugo-Paper](https://github.com/nanxiaobei/hugo-paper) and is multilingual and SEO friendly.
|
||||||
|
|
||||||
|
- The source code is available on [Codeberg](https://codeberg.org/salif/linkita) and mirrored on [GitHub](https://github.com/salif/linkita).
|
||||||
|
- For discussion, join the [Matrix chat room](https://matrix.to/#/#linkita:mozilla.org).
|
||||||
|
- Open bug reports and feature requests on [Codeberg](https://codeberg.org/salif/linkita/issues).
|
||||||
|
- See [demo source code](https://codeberg.org/salif/linkita-demo).
|
||||||
|
- Live preview in:
|
||||||
|
- [English](https://salif.github.io/linkita/en/), [Bulgarian](https://salif.github.io/linkita/), [Esperanto](https://salif.github.io/linkita/eo/).
|
||||||
|
- [Chinese](https://salif.github.io/linkita/zh/), [Arabic](https://salif.github.io/linkita/ar/), [Turkish](https://salif.github.io/linkita/tr/), [Globasa](https://salif.github.io/linkita/gb/).
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- Easy to use and modify
|
||||||
|
- No preset limits
|
||||||
|
- Inject support
|
||||||
|
- Dark mode
|
||||||
|
- Responsive design
|
||||||
|
- Social icons
|
||||||
|
- Taxonomies support
|
||||||
|
- Projects page
|
||||||
|
- Archive page
|
||||||
|
- Table of Content
|
||||||
|
- Admonition shortcode
|
||||||
|
- SEO friendly
|
||||||
|
- Comments using [Giscus](https://giscus.app/)
|
||||||
|
- Mathematical notations using [KaTeX](https://katex.org/)
|
||||||
|
- Diagrams and charts using [Mermaid](https://mermaid.js.org/)
|
||||||
|
- Multilingual support
|
||||||
|
- Search support (elasticlunr_javascript)
|
||||||
|
- Improved search engine optimization
|
||||||
|
- Improved configurability
|
||||||
|
- Author profiles
|
||||||
|
- Projects shortcode
|
||||||
|
- Keyboard shortcuts
|
||||||
|
- Relative URLs support
|
||||||
|
|
||||||
|
## Installing
|
||||||
|
|
||||||
|
0. Create a new Zola site if you haven't already:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
zola init myblog
|
||||||
|
cd myblog
|
||||||
|
git init
|
||||||
|
```
|
||||||
|
|
||||||
|
1. Add the theme as a git submodule:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git submodule add https://codeberg.org/salif/linkita.git themes/linkita
|
||||||
|
```
|
||||||
|
|
||||||
|
If you don't want to use git submodules, you can clone the repository instead: `git clone https://codeberg.org/salif/linkita.git themes/linkita`
|
||||||
|
|
||||||
|
2. Enable the theme in your `config.toml` file:
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
theme = "linkita"
|
||||||
|
```
|
||||||
|
|
||||||
|
Place it near the `base_url` variable, not under `[extra]`.
|
||||||
|
|
||||||
|
## Managing versions
|
||||||
|
|
||||||
|
To update the theme, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git submodule update --remote themes/linkita
|
||||||
|
```
|
||||||
|
|
||||||
|
Check the [changelog](https://codeberg.org/salif/linkita/src/branch/linkita/CHANGELOG.md)
|
||||||
|
for all versions after the one you are using. There may be breaking changes that require manual involvement.
|
||||||
|
|
||||||
|
If and only if you use Tailwind classes in your templates directory, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd themes/linkita
|
||||||
|
pnpm tailwindcss -i ./src/app.css -o ../../static/main.min.css --minify
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Linkita uses the following front matter variables.
|
||||||
|
All variables are optional.
|
||||||
|
Set the ones you need.
|
||||||
|
|
||||||
|
### YAML frontmatter
|
||||||
|
|
||||||
|
```yaml ,name=frontmatter
|
||||||
|
---
|
||||||
|
title: ""
|
||||||
|
description: ""
|
||||||
|
date:
|
||||||
|
updated:
|
||||||
|
taxonomies:
|
||||||
|
categories:
|
||||||
|
tags:
|
||||||
|
extra:
|
||||||
|
comment: false
|
||||||
|
math: false
|
||||||
|
mermaid: false
|
||||||
|
cover:
|
||||||
|
image: ""
|
||||||
|
alt: ""
|
||||||
|
---
|
||||||
|
```
|
||||||
|
|
||||||
|
### TOML frontmatter
|
||||||
|
|
||||||
|
```toml ,name=frontmatter
|
||||||
|
+++
|
||||||
|
title = ""
|
||||||
|
description = ""
|
||||||
|
# The date of the post
|
||||||
|
date = 2025-12-30
|
||||||
|
# The last updated date of the post
|
||||||
|
updated = 2025-12-31
|
||||||
|
|
||||||
|
[taxonomies]
|
||||||
|
categories = []
|
||||||
|
tags = []
|
||||||
|
|
||||||
|
[extra]
|
||||||
|
# Enable comments
|
||||||
|
comment = false
|
||||||
|
# Enable KaTeX support
|
||||||
|
math = false
|
||||||
|
# Enable Mermaid support
|
||||||
|
mermaid = false
|
||||||
|
[extra.cover]
|
||||||
|
# Path to the cover image
|
||||||
|
image = ""
|
||||||
|
# A description of the cover image
|
||||||
|
alt = ""
|
||||||
|
+++
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other extra front matter variables
|
||||||
|
|
||||||
|
Linkita supports [more extra variables, listed here](https://salif.github.io/linkita/en/extra-frontmatter/).
|
||||||
|
|
||||||
|
### Pages and Posts
|
||||||
|
|
||||||
|
#### Home page
|
||||||
|
|
||||||
|
Create a `content/_index.md` file and set `extra.profile` to your username:
|
||||||
|
|
||||||
|
```toml ,name=content/_index.md
|
||||||
|
+++
|
||||||
|
title = ""
|
||||||
|
description = ""
|
||||||
|
sort_by = "date"
|
||||||
|
paginate_by = 4
|
||||||
|
[extra]
|
||||||
|
profile = "your_username"
|
||||||
|
+++
|
||||||
|
```
|
||||||
|
|
||||||
|
Do it for each language in your blog.
|
||||||
|
For French, the file name is `content/_index.fr.md`.
|
||||||
|
|
||||||
|
See [Profiles](#profiles) for more.
|
||||||
|
|
||||||
|
#### Posts
|
||||||
|
|
||||||
|
In the `content` directory, create a subdirectory named `blog` or another name of your choice.
|
||||||
|
|
||||||
|
Create a `content/blog/_index.md` file:
|
||||||
|
|
||||||
|
```toml ,name=content/blog/_index.md
|
||||||
|
+++
|
||||||
|
title = "Archive"
|
||||||
|
description = ""
|
||||||
|
template = "archive.html"
|
||||||
|
transparent = true
|
||||||
|
[extra]
|
||||||
|
date_format = "%m-%d"
|
||||||
|
+++
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a `content/blog/hello.md` file:
|
||||||
|
|
||||||
|
```md ,name=content/blog/hello.md
|
||||||
|
+++
|
||||||
|
title = "Title"
|
||||||
|
date = 2025-12-30
|
||||||
|
+++
|
||||||
|
|
||||||
|
Summary <!-- more -->
|
||||||
|
|
||||||
|
## Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Pages
|
||||||
|
|
||||||
|
The default page template `page.html` is for blog posts.
|
||||||
|
For pages that are not blog posts you can use the `pages.html` template.
|
||||||
|
|
||||||
|
In the `content` directory, create a subdirectory named `pages` and
|
||||||
|
create a `content/pages/_index.md` file:
|
||||||
|
|
||||||
|
```toml ,name=content/pages/_index.md
|
||||||
|
+++
|
||||||
|
render = false
|
||||||
|
page_template = "pages.html"
|
||||||
|
+++
|
||||||
|
```
|
||||||
|
|
||||||
|
Create a `content/pages/about.md` file:
|
||||||
|
|
||||||
|
```md ,name=content/pages/about.md
|
||||||
|
+++
|
||||||
|
title = "About me"
|
||||||
|
description = ""
|
||||||
|
path = "about"
|
||||||
|
+++
|
||||||
|
|
||||||
|
## Hello, world!
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want, you can also create [a page for your projects](https://salif.github.io/linkita/en/shortcodes/#projects).
|
||||||
|
|
||||||
|
### Setting page authors
|
||||||
|
|
||||||
|
Choose one of the following options or skip if you don't know what you're doing:
|
||||||
|
|
||||||
|
#### Option A: Using `page.authors` and `config.author`
|
||||||
|
|
||||||
|
The default author for posts is set using the `author` variable in the `config.toml` file.
|
||||||
|
|
||||||
|
You don't need to set `authors` in the front matter if the default author is the only author of the post. Otherwise, set `authors`:
|
||||||
|
|
||||||
|
```toml ,name=frontmatter
|
||||||
|
+++
|
||||||
|
authors = ["author_username"]
|
||||||
|
+++
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Option B: Using Taxonomies
|
||||||
|
|
||||||
|
Useful if the blog has a team of several authors.
|
||||||
|
If you choose this option you should set taxonomies in each post.
|
||||||
|
|
||||||
|
```toml ,name=frontmatter
|
||||||
|
+++
|
||||||
|
[taxonomies]
|
||||||
|
authors = ["author_username", "author2_username"]
|
||||||
|
+++
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inject support
|
||||||
|
|
||||||
|
You can easily use inject to add new features to your side without modifying the theme itself.
|
||||||
|
|
||||||
|
To use inject, you need to add some HTML files to the `templates/injects` directory.
|
||||||
|
|
||||||
|
The available inject points are: `head.html`, `head_end.html`, `header_nav.html`,
|
||||||
|
`body_start.html`, `body_end.html`, `page_start.html`, `page_end.html`, `footer.html`.
|
||||||
|
|
||||||
|
For example, you can add JavaScript files and CSS stylesheets in the `templates/injects/head_end.html` file.
|
||||||
|
|
||||||
|
### Keyboard shortcuts
|
||||||
|
|
||||||
|
| Action | Shortcut |
|
||||||
|
| ----------------- | ---------------------------- |
|
||||||
|
| Home | <kbd>Alt</kbd>+<kbd>\!</kbd> |
|
||||||
|
| Search | <kbd>Alt</kbd>+<kbd>\/</kbd> |
|
||||||
|
| Toggle menu | <kbd>Alt</kbd>+<kbd>\+</kbd> |
|
||||||
|
| Toggle dark mode | <kbd>Alt</kbd>+<kbd>\$</kbd> |
|
||||||
|
| Go to prev page | <kbd>Alt</kbd>+<kbd>\,</kbd> |
|
||||||
|
| Go to next page | <kbd>Alt</kbd>+<kbd>\.</kbd> |
|
||||||
|
| Table of contents | <kbd>Alt</kbd>+<kbd>\=</kbd> |
|
||||||
|
| Skip to footer | <kbd>Alt</kbd>+<kbd>\_</kbd> |
|
||||||
|
|
||||||
|
## Configuring
|
||||||
|
|
||||||
|
Copy and paste the examples into your `config.toml` file
|
||||||
|
and comment out the variables you don't use instead of setting empty values.
|
||||||
|
All variables are optional.
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
# The URL the site will be built for.
|
||||||
|
base_url = "https://example.com"
|
||||||
|
|
||||||
|
# The site theme to use.
|
||||||
|
theme = "linkita"
|
||||||
|
|
||||||
|
# The default language.
|
||||||
|
# "en" is for English.
|
||||||
|
default_language = "en"
|
||||||
|
|
||||||
|
# The default author for pages.
|
||||||
|
# See "extra.profiles".
|
||||||
|
author = "your_username"
|
||||||
|
|
||||||
|
# The site title.
|
||||||
|
# Will be in all page titles.
|
||||||
|
title = ""
|
||||||
|
|
||||||
|
# The site description.
|
||||||
|
# Used in feeds by default.
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
# Automatically generate a feed.
|
||||||
|
# Default value: false
|
||||||
|
generate_feeds = true
|
||||||
|
|
||||||
|
# The filenames to use for the feeds.
|
||||||
|
# e.g. ["rss.xml"]
|
||||||
|
feed_filenames = ["atom.xml"]
|
||||||
|
|
||||||
|
# Build a search index from the pages and section content
|
||||||
|
# for "default_language".
|
||||||
|
# build_search_index = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Zola has built-in support for taxonomies.
|
||||||
|
Linkita has special support for taxonomies named `tags`, `categories`, and `authors`.
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[[taxonomies]]
|
||||||
|
name = "categories"
|
||||||
|
feed = true
|
||||||
|
paginate_by = 4
|
||||||
|
|
||||||
|
[[taxonomies]]
|
||||||
|
name = "tags"
|
||||||
|
feed = true
|
||||||
|
paginate_by = 4
|
||||||
|
|
||||||
|
[[taxonomies]]
|
||||||
|
name = "authors"
|
||||||
|
feed = true
|
||||||
|
paginate_by = 4
|
||||||
|
```
|
||||||
|
|
||||||
|
You can add more languages by replacing `fr` from the following example with the language code:
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[languages.fr]
|
||||||
|
title = "Site title in French"
|
||||||
|
description = "Site description in French"
|
||||||
|
generate_feeds = true
|
||||||
|
feed_filenames = ["atom.xml"] # or ["rss.xml"]
|
||||||
|
build_search_index = true
|
||||||
|
taxonomies = [
|
||||||
|
{ name = "tags", feed = true, paginate_by = 4 }
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
By default, taxonomy pages are sorted in descending order by the number of posts.
|
||||||
|
You can customize this behavior using the `extra.taxonomy_sorting` variable:
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra.taxonomy_sorting]
|
||||||
|
# "ascending_frequency", "descending_frequency" or "name"
|
||||||
|
# Default value: "descending_frequency"
|
||||||
|
categories = "name"
|
||||||
|
tags = "descending_frequency"
|
||||||
|
authors = "name"
|
||||||
|
```
|
||||||
|
|
||||||
|
### General config
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra]
|
||||||
|
# Enable KaTeX math formula support globally.
|
||||||
|
# Default value: false
|
||||||
|
math = false
|
||||||
|
|
||||||
|
# Enable Mermaid support globally.
|
||||||
|
# Default value: false
|
||||||
|
mermaid = false
|
||||||
|
|
||||||
|
# Enable comments globally.
|
||||||
|
# Default value: false
|
||||||
|
comment = false
|
||||||
|
|
||||||
|
# Title separator.
|
||||||
|
title_separator = " | "
|
||||||
|
|
||||||
|
# The top menu.
|
||||||
|
# See "extra.menus".
|
||||||
|
header_menu_name = "menu_name"
|
||||||
|
|
||||||
|
# If you disable default favicons, you can use
|
||||||
|
# the inject support to set your own favicons.
|
||||||
|
# Default value: false
|
||||||
|
disable_default_favicon = false
|
||||||
|
|
||||||
|
# If you want to implement the JS code
|
||||||
|
# yourself, set to true and use the inject support.
|
||||||
|
# Default value: false
|
||||||
|
disable_javascript = false
|
||||||
|
|
||||||
|
# Default value: false
|
||||||
|
use_cdn = false
|
||||||
|
|
||||||
|
# Use relative urls.
|
||||||
|
# It doesn't apply for content yet.
|
||||||
|
# Default value: false
|
||||||
|
relative_urls = false
|
||||||
|
|
||||||
|
# If you want to view the site without a webserver
|
||||||
|
# set this and "relative_urls" to true.
|
||||||
|
# Default value: false
|
||||||
|
urls_to_index_html = false
|
||||||
|
|
||||||
|
# Prioritize summary over description.
|
||||||
|
# Default value: false
|
||||||
|
page_summary_on_paginator = false
|
||||||
|
|
||||||
|
# Reverse the order of prev and next post links.
|
||||||
|
# Default value: false
|
||||||
|
invert_page_navigation = false
|
||||||
|
|
||||||
|
# You can reorder the strings, remove them, replace them.
|
||||||
|
# For example, you can replace "site_title" with "home_button".
|
||||||
|
# Default value: ["site_title", "theme_button", "search_button", "translations_button"]
|
||||||
|
# header_buttons = []
|
||||||
|
|
||||||
|
# Valid "when" values:
|
||||||
|
# "date", "date_updated", "reading_time", "word_count", "authors", "tags", "".
|
||||||
|
# The "prepend" and "append" are used when the value of "when" is defined for the page.
|
||||||
|
# e.g. [{when="", prepend="Page Info: "},{when="date",prepend="Published on "},{when="authors",prepend="By "}]
|
||||||
|
# page_info = [{ when="date" }, { when="date_updated", prepend="(", append=")" }, { when="reading_time" }]
|
||||||
|
# page_info_on_paginator = [{ when="date" }, { when="reading_time" }]
|
||||||
|
|
||||||
|
# Enable table of contents on all pages.
|
||||||
|
# If not set, toc is enabled only on posts.
|
||||||
|
# If set to false, toc is disabled on all pages.
|
||||||
|
# Type: boolean or object
|
||||||
|
# toc = true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Style config
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra.style]
|
||||||
|
# The custom background color.
|
||||||
|
bg_color = "#f4f4f5"
|
||||||
|
# The custom background color in dark mode.
|
||||||
|
bg_dark_color = "#18181b"
|
||||||
|
|
||||||
|
# Enable header blur.
|
||||||
|
header_blur = false
|
||||||
|
|
||||||
|
# The custom header color, only available
|
||||||
|
# when "header_blur" is false.
|
||||||
|
header_color = "#e4e4e7"
|
||||||
|
# The custom header color in dark mode, only available
|
||||||
|
# when "header_blur" is false.
|
||||||
|
header_dark_color = "#27272a"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Menus
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra.menus]
|
||||||
|
menu_name = [
|
||||||
|
{ url = "$BASE_URL/blog/", name = "Archive" },
|
||||||
|
{ url = "$BASE_URL/categories/", name = "Categories" },
|
||||||
|
{ url = "$BASE_URL/tags/", name = "Tags" },
|
||||||
|
{ url = "$BASE_URL/about/", name = "About" },
|
||||||
|
]
|
||||||
|
|
||||||
|
# Example multilingual menu.
|
||||||
|
multilingual_menu_name = [
|
||||||
|
{ url = "$BASE_URL/about/", names = { en = "About", fr = "About in French" } },
|
||||||
|
{ url = "$BASE_URL/projects/", names = { en = "Projects", fr = "Projects in French" } },
|
||||||
|
{ url = "$BASE_URL/blog/", names = { en = "Archive", fr = "Archive in French" } },
|
||||||
|
{ url = "$BASE_URL/categories/", names = { en = "Categories", fr = "Categories in French" } },
|
||||||
|
{ url = "$BASE_URL/tags/", names = { en = "Tags", fr = "Tags in French" } },
|
||||||
|
{ url = "$BASE_URL/authors/", names = { en = "Authors", fr = "Authors in French" } },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
To use a menu, set `extra.header_menu_name`.
|
||||||
|
|
||||||
|
`$BASE_URL` in `url` will be automatically replaced with the language specific base url.
|
||||||
|
You can use `names_i18n` instead of `names`, see the `static/i18n.json` file,
|
||||||
|
set `names_i18n` to a `common_` key.
|
||||||
|
|
||||||
|
You can use [Internal links](https://www.getzola.org/documentation/content/linking/#internal-links) instead of `$BASE_URL`.
|
||||||
|
|
||||||
|
### Profiles
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
# Replace "your_username" with your username.
|
||||||
|
[extra.profiles.your_username]
|
||||||
|
|
||||||
|
# The URL of avatar.
|
||||||
|
# e.g. "icons/github.svg"
|
||||||
|
avatar_url = ""
|
||||||
|
|
||||||
|
# A description of what is in the avatar.
|
||||||
|
avatar_alt = ""
|
||||||
|
|
||||||
|
# Invert avatar color in dark mode.
|
||||||
|
# Default value: false
|
||||||
|
avatar_invert = false
|
||||||
|
|
||||||
|
# Profile name.
|
||||||
|
# Default value: the username
|
||||||
|
name = ""
|
||||||
|
|
||||||
|
# Profile bio.
|
||||||
|
# Supports Markdown.
|
||||||
|
bio = ""
|
||||||
|
|
||||||
|
# Social icons.
|
||||||
|
# "name" should be the file name of "static/icons/*.svg" or
|
||||||
|
# the icon name of https://simpleicons.org/
|
||||||
|
# "url" supports "$BASE_URL".
|
||||||
|
# Other variables: "urls", "title", "titles".
|
||||||
|
social = [
|
||||||
|
{ name = "bluesky", url = "https://bsky.app/profile/username" },
|
||||||
|
{ name = "github", url = "https://github.com/username" },
|
||||||
|
{ name = "email", url = "mailto:example@example.com" },
|
||||||
|
{ name = "rss", url = "$BASE_URL/atom.xml" },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Profile translations
|
||||||
|
|
||||||
|
Skip if your site is not multilingual.
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
# For French. Replace "your_username" with your username.
|
||||||
|
[extra.profiles.your_username.languages.fr]
|
||||||
|
|
||||||
|
# A description of what is in the avatar.
|
||||||
|
avatar_alt = ""
|
||||||
|
|
||||||
|
# Profile name.
|
||||||
|
name = ""
|
||||||
|
|
||||||
|
# Profile bio.
|
||||||
|
# Supports Markdown.
|
||||||
|
bio = ""
|
||||||
|
|
||||||
|
# Social icons.
|
||||||
|
social = []
|
||||||
|
```
|
||||||
|
|
||||||
|
### Open Graph for profiles
|
||||||
|
|
||||||
|
See [the Open Graph protocol](https://ogp.me/).
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
# Replace "your_username" with your username.
|
||||||
|
[extra.profiles.your_username.open_graph]
|
||||||
|
|
||||||
|
# The URL of social image.
|
||||||
|
image = ""
|
||||||
|
|
||||||
|
# A description of what is in the social image.
|
||||||
|
# Default value: ""
|
||||||
|
image_alt = ""
|
||||||
|
|
||||||
|
# Your first name. No default value.
|
||||||
|
first_name = ""
|
||||||
|
# Your last name. No default value.
|
||||||
|
last_name = ""
|
||||||
|
# Your username. No default value.
|
||||||
|
username = ""
|
||||||
|
# e.g. "female" or "male". No default value.
|
||||||
|
gender = ""
|
||||||
|
|
||||||
|
# fb_app_id = "Your fb app ID"
|
||||||
|
# fb_admins = ["YOUR_USER_ID"]
|
||||||
|
|
||||||
|
# Set if you have a Fediverse account.
|
||||||
|
# handle - Your Fediverse handle.
|
||||||
|
# domain - Your Fediverse instance.
|
||||||
|
# url - Your Fediverse account URL. Optional.
|
||||||
|
# Example for @me@mastodon.social:
|
||||||
|
# { handle = "me", domain = "mastodon.social" }
|
||||||
|
fediverse_creator = { handle = "", domain = "" }
|
||||||
|
```
|
||||||
|
|
||||||
|
`image` and `image_alt` of the default author's profile will be used
|
||||||
|
as a fallback open graph image for all pages.
|
||||||
|
|
||||||
|
#### Open Graph translations
|
||||||
|
|
||||||
|
Skip if your site is not multilingual.
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
# For French. Replace "your_username" with your username.
|
||||||
|
[extra.profiles.your_username.open_graph.languages.fr]
|
||||||
|
# A description of what is in the social image.
|
||||||
|
image_alt = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
### The page footer
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra.footer]
|
||||||
|
# Replace with the correct year.
|
||||||
|
# Default value: the current year
|
||||||
|
since = 2025
|
||||||
|
|
||||||
|
# Replace with the URL of the license you want.
|
||||||
|
# No default value. Supports "$BASE_URL".
|
||||||
|
license_url = "https://creativecommons.org/licenses/by-sa/4.0/deed"
|
||||||
|
|
||||||
|
# Replace "Your Name" with your name and "CC BY-SA 4.0" with the name of the license you want.
|
||||||
|
copyright = "© $YEAR Your Name | [CC BY-SA 4.0]($LICENSE_URL)"
|
||||||
|
|
||||||
|
# Not used yet.
|
||||||
|
# Supports "$BASE_URL".
|
||||||
|
# privacy_policy_url = "$BASE_URL/privacy-policy/"
|
||||||
|
|
||||||
|
# Not used yet.
|
||||||
|
# Supports "$BASE_URL".
|
||||||
|
# terms_of_service_url = "$BASE_URL/terms-of-service/"
|
||||||
|
|
||||||
|
# Not used yet.
|
||||||
|
# Supports "$BASE_URL".
|
||||||
|
# search_page_url = "$BASE_URL/search/"
|
||||||
|
```
|
||||||
|
|
||||||
|
The `copyright` variable supports Markdown, `$BASE_URL`, `$YEAR` (uses `since`), and `$LICENSE_URL` (uses `license_url`).
|
||||||
|
|
||||||
|
### Language specific options
|
||||||
|
|
||||||
|
For date format, see [chrono docs](https://docs.rs/chrono/0.4/chrono/format/strftime/index.html).
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
# For English
|
||||||
|
[extra.languages.en]
|
||||||
|
|
||||||
|
# No default value.
|
||||||
|
locale = "en_US"
|
||||||
|
|
||||||
|
# Default value: "%F"
|
||||||
|
date_format = "%x"
|
||||||
|
|
||||||
|
# Default value: extra.page_info
|
||||||
|
# page_info = []
|
||||||
|
|
||||||
|
# Default value: extra.page_info_on_paginator
|
||||||
|
# page_info_on_paginator = []
|
||||||
|
|
||||||
|
# Default value: extra.header_menu_name
|
||||||
|
# header_menu_name = "menu_name"
|
||||||
|
|
||||||
|
# Default value: extra.header_buttons
|
||||||
|
# header_buttons = []
|
||||||
|
|
||||||
|
# To set a different "lang" attribute of the document.
|
||||||
|
# You can set IETF tag for artificial languages, e.g. "art-x-code".
|
||||||
|
# language_code = ""
|
||||||
|
|
||||||
|
# To use a different interface language, e.g. English.
|
||||||
|
# i18n_code = "en"
|
||||||
|
|
||||||
|
# Set a description for taxonomy pages.
|
||||||
|
[extra.languages.en.taxonomy_descriptions]
|
||||||
|
categories = "A map of all categories on this site. Start exploring!"
|
||||||
|
tags = "A map of all tags on this site. Start exploring!"
|
||||||
|
authors = "A map of all authors on this site. Start exploring!"
|
||||||
|
|
||||||
|
# Set a description for term pages.
|
||||||
|
# "$NAME" will be automatically replaced.
|
||||||
|
[extra.languages.en.term_descriptions]
|
||||||
|
categories = "Browse articles related to $NAME. Start exploring!"
|
||||||
|
tags = "Browse articles related to $NAME. Start exploring!"
|
||||||
|
authors = "Browse articles written by $NAME. Start exploring!"
|
||||||
|
```
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
# For French
|
||||||
|
[extra.languages.fr]
|
||||||
|
locale = "fr_FR"
|
||||||
|
date_format = "%x"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web analytics
|
||||||
|
|
||||||
|
#### GoatCounter
|
||||||
|
|
||||||
|
Set only if you use [GoatCounter](https://www.goatcounter.com/).
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra.goatcounter]
|
||||||
|
# No default value.
|
||||||
|
endpoint = "https://MYCODE.goatcounter.com/count"
|
||||||
|
|
||||||
|
# To enable tracking pixel, set to an empty string.
|
||||||
|
# If your "base_url" includes a subpath, set to
|
||||||
|
# the subpath without a trailing slash.
|
||||||
|
# noscript_prefix = ""
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Vercel Analytics
|
||||||
|
|
||||||
|
Set only if you use [Vercel Web Analytics](https://vercel.com/docs/analytics).
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra.vercel_analytics]
|
||||||
|
# No default value.
|
||||||
|
src = "/_vercel/insights/script.js"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Prevent tracking own pageviews
|
||||||
|
|
||||||
|
Open a page of your site, adding `#disable-analytics` to the page address.
|
||||||
|
Do this once for each browser and device.
|
||||||
|
For example, open <http://127.0.0.1:1111/#disable-analytics>.
|
||||||
|
|
||||||
|
### Comments
|
||||||
|
|
||||||
|
See [giscus.app](https://giscus.app/).
|
||||||
|
Only available when `extra.comment` in the front matter or `extra.comment` in the config is set to true.
|
||||||
|
|
||||||
|
```toml ,name=config.toml
|
||||||
|
[extra.giscus]
|
||||||
|
# No default value.
|
||||||
|
repo = ""
|
||||||
|
# No default value.
|
||||||
|
repo_id = ""
|
||||||
|
# No default value.
|
||||||
|
category = ""
|
||||||
|
# No default value.
|
||||||
|
category_id = ""
|
||||||
|
# Default value: "pathname"
|
||||||
|
mapping = "pathname"
|
||||||
|
# Default value: 1
|
||||||
|
strict = 1
|
||||||
|
# Default value: 0
|
||||||
|
reactions_enabled = 0
|
||||||
|
# Default value: 0
|
||||||
|
emit_metadata = 0
|
||||||
|
# Default value: "top"
|
||||||
|
input_position = "top"
|
||||||
|
# Default value: "light"
|
||||||
|
theme = "light"
|
||||||
|
# Default value: "en"
|
||||||
|
lang = "en"
|
||||||
|
# Default value: "lazy"
|
||||||
|
loading = "lazy"
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
See the [MIT License](https://codeberg.org/salif/linkita/src/branch/linkita/LICENSE) file.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Pull requests are welcome on [Codeberg](https://codeberg.org/salif/linkita) and [Github](https://github.com/salif/linkita).
|
||||||
|
|
||||||
|
## Sites using Linkita
|
||||||
|
|
||||||
|
- [Zola Themes Collection](https://github.com/salif/zola-themes-collection)
|
||||||
|
- [salif.eu](https://github.com/salif/personal-web-page): Personal website
|
||||||
|
- [Rratic's blog](https://github.com/Rratic/rratic.github.io): Personal website
|
||||||
|
|
||||||
|
If your blog uses Linkita and is open source, feel free to create a pull request to add it to this list.
|
||||||
28
themes/linkita/package.json
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"name": "linkita",
|
||||||
|
"version": "3.2025_04_22",
|
||||||
|
"description": "A clean and elegant blog theme for Zola. Linkita is based on Kita and Hugo-Paper and is multilingual and SEO friendly.",
|
||||||
|
"homepage": "https://codeberg.org/salif/linkita",
|
||||||
|
"license": "MIT",
|
||||||
|
"author": {
|
||||||
|
"name": "Salif Mehmed",
|
||||||
|
"email": "mail@salif.eu",
|
||||||
|
"url": "https://salif.eu"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://codeberg.org/salif/linkita.git"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@iarna/toml": "^2.2.5",
|
||||||
|
"@tailwindcss/typography": "^0.5.16",
|
||||||
|
"instant.page": "^5.2.0",
|
||||||
|
"katex": "^0.16.22",
|
||||||
|
"prettier": "^3.5.3",
|
||||||
|
"prettier-plugin-tailwindcss": "^0.6.12",
|
||||||
|
"simple-icons": "^14.15.0",
|
||||||
|
"tailwindcss": "^3.4.17",
|
||||||
|
"terser": "^5.43.1",
|
||||||
|
"zx": "^8.5.5"
|
||||||
|
}
|
||||||
|
}
|
||||||
1055
themes/linkita/pnpm-lock.yaml
Normal file
BIN
themes/linkita/screenshot.png
Normal file
|
After Width: | Height: | Size: 80 KiB |
149
themes/linkita/src/app.css
Normal file
|
|
@ -0,0 +1,149 @@
|
||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer base {
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
@apply h-full;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
--bg: transparent;
|
||||||
|
--header: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
*,
|
||||||
|
::before,
|
||||||
|
::after {
|
||||||
|
/* Auto direction for text */
|
||||||
|
unicode-bidi: plaintext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code block */
|
||||||
|
pre mark {
|
||||||
|
@apply block text-inherit;
|
||||||
|
margin: 0 -18px;
|
||||||
|
padding: 0 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre table {
|
||||||
|
@apply !m-0 !w-full border-collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre table tr {
|
||||||
|
@apply !border-0 !border-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre table td {
|
||||||
|
@apply !p-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre table td:nth-of-type(1) {
|
||||||
|
@apply min-w-4 select-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose .footnote-definition {
|
||||||
|
@apply flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose .footnote-definition p {
|
||||||
|
@apply m-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose .footnote-definition .footnote-definition-label {
|
||||||
|
@apply static mr-2 text-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prose .footnote-definition .footnote-definition-label::after {
|
||||||
|
content: ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Linkita */
|
||||||
|
|
||||||
|
a {
|
||||||
|
@apply underline-offset-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
sup a {
|
||||||
|
@apply underline-offset-1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.not-ready * {
|
||||||
|
@apply !transition-none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-menu::before,
|
||||||
|
.btn-menu::after {
|
||||||
|
@apply block h-[2px] w-5 bg-black duration-100 content-[''] dark:invert;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open {
|
||||||
|
@apply overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open .btn-menu::before {
|
||||||
|
@apply w-6 translate-y-[5.5px] rotate-45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open .btn-menu::after {
|
||||||
|
@apply w-6 -translate-y-[5.5px] -rotate-45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-wrapper {
|
||||||
|
@apply hidden lg:flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.open .nav-wrapper {
|
||||||
|
@apply flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
article {
|
||||||
|
@apply text-lg;
|
||||||
|
}
|
||||||
|
|
||||||
|
.blur-header {
|
||||||
|
@apply bg-black/10 backdrop-blur dark:bg-white/10;
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-bg {
|
||||||
|
@apply bg-black/[3%] dark:bg-white/[8%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-hover {
|
||||||
|
@apply hover:bg-black/[5%] dark:hover:bg-white/[11%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.block-hover-mask {
|
||||||
|
@apply hover:bg-black/[2%] dark:hover:bg-white/[3%];
|
||||||
|
}
|
||||||
|
|
||||||
|
.primary-link {
|
||||||
|
@apply text-black no-underline hover:opacity-80 dark:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-link {
|
||||||
|
@apply text-[var(--tw-prose-body)] no-underline hover:text-black dark:hover:text-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Linkita */
|
||||||
|
|
||||||
|
.horizontal-scroll {
|
||||||
|
@apply overflow-x-auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-icons {
|
||||||
|
--icon-home: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>');
|
||||||
|
--icon-search: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>');
|
||||||
|
--icon-translations: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>');
|
||||||
|
--icon-theme-light: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-sun"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23" ></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>');
|
||||||
|
--icon-theme-dark: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-moon"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>');
|
||||||
|
}
|
||||||
|
}
|
||||||
275
themes/linkita/src/gc.js
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
/**
|
||||||
|
* @file GoatCounter v4: https://www.goatcounter.com
|
||||||
|
* @license
|
||||||
|
* This file is released under the ISC license: https://opensource.org/licenses/ISC
|
||||||
|
*/
|
||||||
|
|
||||||
|
;(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.goatcounter && window.goatcounter.vars) // Compatibility with very old version; do not use.
|
||||||
|
window.goatcounter = window.goatcounter.vars
|
||||||
|
else
|
||||||
|
window.goatcounter = window.goatcounter || {}
|
||||||
|
|
||||||
|
// Load settings from data-goatcounter-settings.
|
||||||
|
var s = document.querySelector('script[data-goatcounter]')
|
||||||
|
if (s && s.dataset.goatcounterSettings) {
|
||||||
|
try { var set = JSON.parse(s.dataset.goatcounterSettings) }
|
||||||
|
catch (err) { console.error('invalid JSON in data-goatcounter-settings: ' + err) }
|
||||||
|
for (var k in set)
|
||||||
|
if (['no_onload', 'no_events', 'allow_local', 'allow_frame', 'path', 'title', 'referrer', 'event'].indexOf(k) > -1)
|
||||||
|
window.goatcounter[k] = set[k]
|
||||||
|
}
|
||||||
|
|
||||||
|
var enc = encodeURIComponent
|
||||||
|
|
||||||
|
// Get all data we're going to send off to the counter endpoint.
|
||||||
|
var get_data = function(vars) {
|
||||||
|
var data = {
|
||||||
|
p: (vars.path === undefined ? goatcounter.path : vars.path),
|
||||||
|
r: (vars.referrer === undefined ? goatcounter.referrer : vars.referrer),
|
||||||
|
t: (vars.title === undefined ? goatcounter.title : vars.title),
|
||||||
|
e: !!(vars.event || goatcounter.event),
|
||||||
|
s: [window.screen.width, window.screen.height, (window.devicePixelRatio || 1)],
|
||||||
|
b: is_bot(),
|
||||||
|
q: location.search,
|
||||||
|
}
|
||||||
|
|
||||||
|
var rcb, pcb, tcb // Save callbacks to apply later.
|
||||||
|
if (typeof(data.r) === 'function') rcb = data.r
|
||||||
|
if (typeof(data.t) === 'function') tcb = data.t
|
||||||
|
if (typeof(data.p) === 'function') pcb = data.p
|
||||||
|
|
||||||
|
if (is_empty(data.r)) data.r = document.referrer
|
||||||
|
if (is_empty(data.t)) data.t = document.title
|
||||||
|
if (is_empty(data.p)) data.p = get_path()
|
||||||
|
|
||||||
|
if (rcb) data.r = rcb(data.r)
|
||||||
|
if (tcb) data.t = tcb(data.t)
|
||||||
|
if (pcb) data.p = pcb(data.p)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if a value is "empty" for the purpose of get_data().
|
||||||
|
var is_empty = function(v) { return v === null || v === undefined || typeof(v) === 'function' }
|
||||||
|
|
||||||
|
// See if this looks like a bot; there is some additional filtering on the
|
||||||
|
// backend, but these properties can't be fetched from there.
|
||||||
|
var is_bot = function() {
|
||||||
|
// Headless browsers are probably a bot.
|
||||||
|
var w = window, d = document
|
||||||
|
if (w.callPhantom || w._phantom || w.phantom)
|
||||||
|
return 150
|
||||||
|
if (w.__nightmare)
|
||||||
|
return 151
|
||||||
|
if (d.__selenium_unwrapped || d.__webdriver_evaluate || d.__driver_evaluate)
|
||||||
|
return 152
|
||||||
|
if (navigator.webdriver)
|
||||||
|
return 153
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Object to urlencoded string, starting with a ?.
|
||||||
|
var urlencode = function(obj) {
|
||||||
|
var p = []
|
||||||
|
for (var k in obj)
|
||||||
|
if (obj[k] !== '' && obj[k] !== null && obj[k] !== undefined && obj[k] !== false)
|
||||||
|
p.push(enc(k) + '=' + enc(obj[k]))
|
||||||
|
return '?' + p.join('&')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show a warning in the console.
|
||||||
|
var warn = function(msg) {
|
||||||
|
if (console && 'warn' in console)
|
||||||
|
console.warn('goatcounter: ' + msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the endpoint to send requests to.
|
||||||
|
var get_endpoint = function() {
|
||||||
|
var s = document.querySelector('script[data-goatcounter]')
|
||||||
|
if (s && s.dataset.goatcounter)
|
||||||
|
return s.dataset.goatcounter
|
||||||
|
return (goatcounter.endpoint || window.counter) // counter is for compat; don't use.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current path.
|
||||||
|
var get_path = function() {
|
||||||
|
var loc = location,
|
||||||
|
c = document.querySelector('link[rel="canonical"][href]')
|
||||||
|
if (c) { // May be relative or point to different domain.
|
||||||
|
var a = document.createElement('a')
|
||||||
|
a.href = c.href
|
||||||
|
if (a.hostname.replace(/^www\./, '') === location.hostname.replace(/^www\./, ''))
|
||||||
|
loc = a
|
||||||
|
}
|
||||||
|
return (loc.pathname + loc.search) || '/'
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run function after DOM is loaded.
|
||||||
|
var on_load = function(f) {
|
||||||
|
if (document.body === null)
|
||||||
|
document.addEventListener('DOMContentLoaded', function() { f() }, false)
|
||||||
|
else
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter some requests that we (probably) don't want to count.
|
||||||
|
goatcounter.filter = function() {
|
||||||
|
if ('visibilityState' in document && document.visibilityState === 'prerender')
|
||||||
|
return 'visibilityState'
|
||||||
|
if (!goatcounter.allow_frame && location !== parent.location)
|
||||||
|
return 'frame'
|
||||||
|
if (!goatcounter.allow_local && location.hostname.match(/(localhost$|^127\.|^10\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^192\.168\.|^0\.0\.0\.0$)/))
|
||||||
|
return 'localhost'
|
||||||
|
if (!goatcounter.allow_local && location.protocol === 'file:')
|
||||||
|
return 'localfile'
|
||||||
|
if (localStorage && localStorage.getItem('skipgc') === 't')
|
||||||
|
return 'disabled with #toggle-goatcounter'
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get URL to send to GoatCounter.
|
||||||
|
window.goatcounter.url = function(vars) {
|
||||||
|
var data = get_data(vars || {})
|
||||||
|
if (data.p === null) // null from user callback.
|
||||||
|
return
|
||||||
|
data.rnd = Math.random().toString(36).substr(2, 5) // Browsers don't always listen to Cache-Control.
|
||||||
|
|
||||||
|
var endpoint = get_endpoint()
|
||||||
|
if (!endpoint)
|
||||||
|
return warn('no endpoint found')
|
||||||
|
|
||||||
|
return endpoint + urlencode(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count a hit.
|
||||||
|
window.goatcounter.count = function(vars) {
|
||||||
|
var f = goatcounter.filter()
|
||||||
|
if (f)
|
||||||
|
return warn('not counting because of: ' + f)
|
||||||
|
var url = goatcounter.url(vars)
|
||||||
|
if (!url)
|
||||||
|
return warn('not counting because path callback returned null')
|
||||||
|
|
||||||
|
if (!navigator.sendBeacon(url)) {
|
||||||
|
// This mostly fails due to being blocked by CSP; try again with an
|
||||||
|
// image-based fallback.
|
||||||
|
var img = document.createElement('img')
|
||||||
|
img.src = url
|
||||||
|
img.style.position = 'absolute' // Affect layout less.
|
||||||
|
img.style.bottom = '0px'
|
||||||
|
img.style.width = '1px'
|
||||||
|
img.style.height = '1px'
|
||||||
|
img.loading = 'eager'
|
||||||
|
img.setAttribute('alt', '')
|
||||||
|
img.setAttribute('aria-hidden', 'true')
|
||||||
|
|
||||||
|
var rm = function() { if (img && img.parentNode) img.parentNode.removeChild(img) }
|
||||||
|
img.addEventListener('load', rm, false)
|
||||||
|
document.body.appendChild(img)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a query parameter.
|
||||||
|
window.goatcounter.get_query = function(name) {
|
||||||
|
var s = location.search.substr(1).split('&')
|
||||||
|
for (var i = 0; i < s.length; i++)
|
||||||
|
if (s[i].toLowerCase().indexOf(name.toLowerCase() + '=') === 0)
|
||||||
|
return s[i].substr(name.length + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track click events.
|
||||||
|
window.goatcounter.bind_events = function() {
|
||||||
|
if (!document.querySelectorAll) // Just in case someone uses an ancient browser.
|
||||||
|
return
|
||||||
|
|
||||||
|
var send = function(elem) {
|
||||||
|
return function() {
|
||||||
|
goatcounter.count({
|
||||||
|
event: true,
|
||||||
|
path: (elem.dataset.goatcounterClick || elem.name || elem.id || ''),
|
||||||
|
title: (elem.dataset.goatcounterTitle || elem.title || (elem.innerHTML || '').substr(0, 200) || ''),
|
||||||
|
referrer: (elem.dataset.goatcounterReferrer || elem.dataset.goatcounterReferral || ''),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array.prototype.slice.call(document.querySelectorAll("*[data-goatcounter-click]")).forEach(function(elem) {
|
||||||
|
if (elem.dataset.goatcounterBound)
|
||||||
|
return
|
||||||
|
var f = send(elem)
|
||||||
|
elem.addEventListener('click', f, false)
|
||||||
|
elem.addEventListener('auxclick', f, false) // Middle click.
|
||||||
|
elem.dataset.goatcounterBound = 'true'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a "visitor counter" frame or image.
|
||||||
|
window.goatcounter.visit_count = function(opt) {
|
||||||
|
on_load(function() {
|
||||||
|
opt = opt || {}
|
||||||
|
opt.type = opt.type || 'html'
|
||||||
|
opt.append = opt.append || 'body'
|
||||||
|
opt.path = opt.path || get_path()
|
||||||
|
opt.attr = opt.attr || {width: '200', height: (opt.no_branding ? '60' : '80')}
|
||||||
|
|
||||||
|
opt.attr['src'] = get_endpoint() + 'er/' + enc(opt.path) + '.' + enc(opt.type) + '?'
|
||||||
|
if (opt.no_branding) opt.attr['src'] += '&no_branding=1'
|
||||||
|
if (opt.style) opt.attr['src'] += '&style=' + enc(opt.style)
|
||||||
|
if (opt.start) opt.attr['src'] += '&start=' + enc(opt.start)
|
||||||
|
if (opt.end) opt.attr['src'] += '&end=' + enc(opt.end)
|
||||||
|
|
||||||
|
var tag = {png: 'img', svg: 'img', html: 'iframe'}[opt.type]
|
||||||
|
if (!tag)
|
||||||
|
return warn('visit_count: unknown type: ' + opt.type)
|
||||||
|
|
||||||
|
if (opt.type === 'html') {
|
||||||
|
opt.attr['frameborder'] = '0'
|
||||||
|
opt.attr['scrolling'] = 'no'
|
||||||
|
}
|
||||||
|
|
||||||
|
var d = document.createElement(tag)
|
||||||
|
for (var k in opt.attr)
|
||||||
|
d.setAttribute(k, opt.attr[k])
|
||||||
|
|
||||||
|
var p = document.querySelector(opt.append)
|
||||||
|
if (!p)
|
||||||
|
return warn('visit_count: append not found: ' + opt.append)
|
||||||
|
p.appendChild(d)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make it easy to skip your own views.
|
||||||
|
if (location.hash === '#toggle-goatcounter') {
|
||||||
|
if (localStorage.getItem('skipgc') === 't') {
|
||||||
|
localStorage.removeItem('skipgc', 't')
|
||||||
|
alert('GoatCounter tracking is now ENABLED in this browser.')
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
localStorage.setItem('skipgc', 't')
|
||||||
|
alert('GoatCounter tracking is now DISABLED in this browser until ' + location + ' is loaded again.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goatcounter.no_onload)
|
||||||
|
on_load(function() {
|
||||||
|
// 1. Page is visible, count request.
|
||||||
|
// 2. Page is not yet visible; wait until it switches to 'visible' and count.
|
||||||
|
// See #487
|
||||||
|
if (!('visibilityState' in document) || document.visibilityState === 'visible')
|
||||||
|
goatcounter.count()
|
||||||
|
else {
|
||||||
|
var f = function(e) {
|
||||||
|
if (document.visibilityState !== 'visible')
|
||||||
|
return
|
||||||
|
document.removeEventListener('visibilitychange', f)
|
||||||
|
goatcounter.count()
|
||||||
|
}
|
||||||
|
document.addEventListener('visibilitychange', f)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!goatcounter.no_events)
|
||||||
|
goatcounter.bind_events()
|
||||||
|
})
|
||||||
|
})();
|
||||||
83
themes/linkita/src/linkita-search.js
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
"use strict";
|
||||||
|
(function () {
|
||||||
|
let searchFiles;
|
||||||
|
let mySearchIndex;
|
||||||
|
|
||||||
|
function toggleSearch() {
|
||||||
|
const searchWrapperEl = document.getElementById("linkita-search-wrapper");
|
||||||
|
const searchResultsEl = document.getElementById("linkita-search-results");
|
||||||
|
if (null == searchWrapperEl || null == searchResultsEl) {
|
||||||
|
console.error("searchWrapper is null");
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
searchWrapperEl.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
const q = prompt("Enter your search term");
|
||||||
|
if (null == q) {
|
||||||
|
searchWrapperEl.classList.add("hidden");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("undefined" === typeof (searchIndex) && "undefined" === typeof (elasticlunr)) {
|
||||||
|
searchResultsEl.innerHTML = "<li>Search: Please wait...</li>";
|
||||||
|
Promise.all(searchFiles.map(loadScript))
|
||||||
|
.catch(error => {
|
||||||
|
showError(searchResultsEl, "<li>Search file not found: <code>" + error + "</code></li>");
|
||||||
|
})
|
||||||
|
.then((t) => {
|
||||||
|
mySearchIndex = elasticlunr.Index.load(window.searchIndex);
|
||||||
|
doSearch(q, searchResultsEl);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
doSearch(q, searchResultsEl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSearch(q, searchResultsEl) {
|
||||||
|
const searchResults = mySearchIndex.search(q);
|
||||||
|
const searchResultsCount = searchResults.length;
|
||||||
|
if (searchResultsCount > 0) {
|
||||||
|
const searchResultsRows = ["<li><strong>" + searchResultsCount + "</strong> search " +
|
||||||
|
(searchResultsCount === 1 ? "result" : "results") + " for <code>" + mySafe(q) + "</code>:</li>"];
|
||||||
|
for (let i = 0; i < searchResultsCount; i++) {
|
||||||
|
const searchResult = searchResults[i];
|
||||||
|
searchResultsRows.push("<li><a href=\"" + mySafe(searchResult.ref) + "\">" +
|
||||||
|
mySafe(searchResult.doc.title) + "</a></li>");
|
||||||
|
}
|
||||||
|
searchResultsEl.innerHTML = searchResultsRows.join("");
|
||||||
|
searchResultsEl.scrollIntoViewIfNeeded();
|
||||||
|
} else {
|
||||||
|
showError(searchResultsEl, "<li>No search results for <code>" + mySafe(q) + "</code>.</li>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showError(searchResultsEl, err) {
|
||||||
|
searchResultsEl.innerHTML = err;
|
||||||
|
searchResultsEl.scrollIntoViewIfNeeded();
|
||||||
|
}
|
||||||
|
|
||||||
|
function mySafe(code) {
|
||||||
|
return code.replace(/&/g, "&").replace(/</g, "<").
|
||||||
|
replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadScript(fileName) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const scriptEl = document.createElement("script");
|
||||||
|
scriptEl.onload = () => resolve(fileName);
|
||||||
|
scriptEl.onerror = () => reject(fileName);
|
||||||
|
scriptEl.async = true;
|
||||||
|
scriptEl.src = fileName;
|
||||||
|
document.head.appendChild(scriptEl);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSearchButton({scripts}) {
|
||||||
|
searchFiles = scripts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null == window.linkita) window.linkita = {};
|
||||||
|
window.linkita.toggleSearch = toggleSearch;
|
||||||
|
window.linkita.initSearchButton = initSearchButton;
|
||||||
|
})();
|
||||||
187
themes/linkita/src/linkita.js
Normal file
|
|
@ -0,0 +1,187 @@
|
||||||
|
"use strict";
|
||||||
|
(function () {
|
||||||
|
const htmlClass = document.documentElement.classList;
|
||||||
|
const themeColorTag = document.head.querySelector('meta[name="theme-color"]');
|
||||||
|
const darkScheme = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
const colorSchemeKey = "linkita-color-scheme";
|
||||||
|
|
||||||
|
function applyDarkMode(isDark, doDispatchEvent) {
|
||||||
|
if (isDark) {
|
||||||
|
htmlClass.add("dark");
|
||||||
|
} else {
|
||||||
|
htmlClass.remove("dark");
|
||||||
|
}
|
||||||
|
if (undefined != themeColorTag) {
|
||||||
|
themeColorTag.setAttribute("content", isDark ?
|
||||||
|
themeColorTag.dataset.dark : themeColorTag.dataset.light);
|
||||||
|
}
|
||||||
|
if (doDispatchEvent && undefined != document.body) {
|
||||||
|
document.body.dispatchEvent(new CustomEvent("set-theme",
|
||||||
|
{ detail: isDark ? "dark" : "light" }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initDarkMode() {
|
||||||
|
const darkVal = localStorage.getItem(colorSchemeKey);
|
||||||
|
if (darkVal) {
|
||||||
|
applyDarkMode(darkVal === "dark", false);
|
||||||
|
} else if (htmlClass.contains("dark")) {
|
||||||
|
applyDarkMode(true, false);
|
||||||
|
} else {
|
||||||
|
applyDarkMode(darkScheme.matches, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
darkScheme.addEventListener("change", function (event) {
|
||||||
|
applyDarkMode(event.matches, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
htmlClass.remove("not-ready");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleDarkMode() {
|
||||||
|
const isDark = !htmlClass.contains("dark");
|
||||||
|
applyDarkMode(isDark, true);
|
||||||
|
localStorage.setItem(colorSchemeKey, isDark ? "dark" : "light");
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetDarkMode() {
|
||||||
|
localStorage.removeItem(colorSchemeKey);
|
||||||
|
applyDarkMode(darkScheme.matches, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTranslationsButton({ btn, rel }) {
|
||||||
|
const pageLanguage = document.documentElement.getAttribute("lang");
|
||||||
|
const pageTranslations = document.head.querySelectorAll('link[rel="alternate"][hreflang]');
|
||||||
|
|
||||||
|
let userLanguages = [];
|
||||||
|
if (pageTranslations.length < 2) return;
|
||||||
|
else if (pageTranslations.length === 2) userLanguages = [
|
||||||
|
pageTranslations[0].getAttribute("hreflang"), pageTranslations[1].getAttribute("hreflang")];
|
||||||
|
else if (navigator.languages) userLanguages = navigator.languages;
|
||||||
|
else if (navigator.language != undefined) userLanguages = [navigator.language];
|
||||||
|
else if (navigator.userLanguage != undefined) userLanguages = [navigator.userLanguage];
|
||||||
|
|
||||||
|
const pageTranslationsLinks = new Map();
|
||||||
|
pageTranslations.forEach(function (el) {
|
||||||
|
const hreflang = el.getAttribute("hreflang");
|
||||||
|
const href = rel === "true" ? el.dataset.href : el.getAttribute("href");
|
||||||
|
if (hreflang !== pageLanguage) {
|
||||||
|
pageTranslationsLinks.set(hreflang, href);
|
||||||
|
const hreflangcode = hreflang.split("-")[0];
|
||||||
|
if (!pageTranslationsLinks.has(hreflangcode)) {
|
||||||
|
pageTranslationsLinks.set(hreflangcode, href);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const pageTranslationLink = getPageTranslationLink(userLanguages, pageTranslationsLinks);
|
||||||
|
if (undefined != pageTranslationLink) {
|
||||||
|
btn.classList.remove("hidden");
|
||||||
|
btn.addEventListener("click", function () {
|
||||||
|
window.location.href = pageTranslationLink;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPageTranslationLink(userLanguages, pageTranslationsLinks) {
|
||||||
|
for (let i = 0; i < userLanguages.length; i++) {
|
||||||
|
const userLanguage = userLanguages[i];
|
||||||
|
const pageTranslationLink = pageTranslationsLinks.get(userLanguage) ||
|
||||||
|
pageTranslationsLinks.get(userLanguage.split("-")[0]);
|
||||||
|
if (undefined != pageTranslationLink) {
|
||||||
|
return pageTranslationLink;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleHeaderMenu() {
|
||||||
|
htmlClass.toggle("open");
|
||||||
|
}
|
||||||
|
|
||||||
|
function initKatex() {
|
||||||
|
window.renderMathInElement(document.body, {
|
||||||
|
delimiters: [
|
||||||
|
{ left: "$$", right: "$$", display: true },
|
||||||
|
{ left: "$", right: "$", display: false },
|
||||||
|
],
|
||||||
|
throwOnError: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableAnalytics({ key }) {
|
||||||
|
return localStorage.removeItem(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableAnalytics({ key }) {
|
||||||
|
return localStorage.setItem(key, "t");
|
||||||
|
}
|
||||||
|
|
||||||
|
function isAnalyticsEnabled({ key, init }) {
|
||||||
|
if (init) {
|
||||||
|
if (window.location.hash === "#enable-analytics") {
|
||||||
|
if (localStorage.getItem(key) === "t") {
|
||||||
|
enableAnalytics({ key });
|
||||||
|
alert("Analytics is now ENABLED in this browser. To disable analytics, load #disable-analytics.");
|
||||||
|
}
|
||||||
|
} else if (window.location.hash === "#disable-analytics") {
|
||||||
|
if (localStorage.getItem(key) !== "t") {
|
||||||
|
disableAnalytics({ key });
|
||||||
|
alert("Analytics is now DISABLED in this browser. To enable analytics, load #enable-analytics.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return localStorage.getItem(key) !== "t";
|
||||||
|
}
|
||||||
|
|
||||||
|
function initGoatCounterAnalytics({ src, endpoint }) {
|
||||||
|
if (isAnalyticsEnabled({ key: "skipgc", init: true })) {
|
||||||
|
const newScript = document.createElement("script");
|
||||||
|
newScript.async = true;
|
||||||
|
newScript.src = src;
|
||||||
|
newScript.dataset.goatcounter = endpoint;
|
||||||
|
if (undefined != document.body) {
|
||||||
|
document.body.appendChild(newScript);
|
||||||
|
} else if (undefined != document.head) {
|
||||||
|
document.head.appendChild(newScript);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initVercelAnalytics({ src }) {
|
||||||
|
if (isAnalyticsEnabled({ key: "va-disable", init: true })) {
|
||||||
|
if (undefined == window.va) {
|
||||||
|
window.va = function () {
|
||||||
|
(window.vaq = window.vaq || []).push(arguments);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const newScript = document.createElement("script");
|
||||||
|
newScript.async = true;
|
||||||
|
newScript.src = src;
|
||||||
|
if (undefined != document.body) {
|
||||||
|
document.body.appendChild(newScript);
|
||||||
|
} else if (undefined != document.head) {
|
||||||
|
document.head.appendChild(newScript);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function main() {
|
||||||
|
initDarkMode();
|
||||||
|
|
||||||
|
window.linkita = {
|
||||||
|
applyDarkMode: applyDarkMode,
|
||||||
|
toggleDarkMode: toggleDarkMode,
|
||||||
|
resetDarkMode: resetDarkMode,
|
||||||
|
initTranslationsButton: initTranslationsButton,
|
||||||
|
toggleHeaderMenu: toggleHeaderMenu,
|
||||||
|
initKatex: initKatex,
|
||||||
|
isAnalyticsEnabled: isAnalyticsEnabled,
|
||||||
|
enableAnalytics: enableAnalytics,
|
||||||
|
disableAnalytics: disableAnalytics,
|
||||||
|
initGoatCounterAnalytics: initGoatCounterAnalytics,
|
||||||
|
initVercelAnalytics: initVercelAnalytics,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
|
})();
|
||||||
2145
themes/linkita/src/main.css
Normal file
BIN
themes/linkita/static/android-icon.png
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
themes/linkita/static/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
themes/linkita/static/favicon.ico
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
1022
themes/linkita/static/i18n.json
Normal file
1
themes/linkita/static/icons/abstract.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M64 144a48 48 0 1 0 0-96 48 48 0 1 0 0 96zM192 64c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zm0 160c-17.7 0-32 14.3-32 32s14.3 32 32 32H480c17.7 0 32-14.3 32-32s-14.3-32-32-32H192zM64 464a48 48 0 1 0 0-96 48 48 0 1 0 0 96zm48-208a48 48 0 1 0 -96 0 48 48 0 1 0 96 0z"/></svg>
|
||||||
|
After Width: | Height: | Size: 637 B |
1
themes/linkita/static/icons/bluesky.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Bluesky</title><path d="M12 10.8c-1.087-2.114-4.046-6.053-6.798-7.995C2.566.944 1.561 1.266.902 1.565.139 1.908 0 3.08 0 3.768c0 .69.378 5.65.624 6.479.815 2.736 3.713 3.66 6.383 3.364.136-.02.275-.039.415-.056-.138.022-.276.04-.415.056-3.912.58-7.387 2.005-2.83 7.078 5.013 5.19 6.87-1.113 7.823-4.308.953 3.195 2.05 9.271 7.733 4.308 4.267-4.308 1.172-6.498-2.74-7.078a8.741 8.741 0 0 1-.415-.056c.14.017.279.036.415.056 2.67.297 5.568-.628 6.383-3.364.246-.828.624-5.79.624-6.478 0-.69-.139-1.861-.902-2.206-.659-.298-1.664-.62-4.3 1.24C16.046 4.748 13.087 8.687 12 10.8Z"/></svg>
|
||||||
|
After Width: | Height: | Size: 661 B |
1
themes/linkita/static/icons/bug.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 0c53 0 96 43 96 96v3.6c0 15.7-12.7 28.4-28.4 28.4H188.4c-15.7 0-28.4-12.7-28.4-28.4V96c0-53 43-96 96-96zM41.4 105.4c12.5-12.5 32.8-12.5 45.3 0l64 64c.7 .7 1.3 1.4 1.9 2.1c14.2-7.3 30.4-11.4 47.5-11.4H312c17.1 0 33.2 4.1 47.5 11.4c.6-.7 1.2-1.4 1.9-2.1l64-64c12.5-12.5 32.8-12.5 45.3 0s12.5 32.8 0 45.3l-64 64c-.7 .7-1.4 1.3-2.1 1.9c6.2 12 10.1 25.3 11.1 39.5H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H416c0 24.6-5.5 47.8-15.4 68.6c2.2 1.3 4.2 2.9 6 4.8l64 64c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0l-63.1-63.1c-24.5 21.8-55.8 36.2-90.3 39.6V240c0-8.8-7.2-16-16-16s-16 7.2-16 16V479.2c-34.5-3.4-65.8-17.8-90.3-39.6L86.6 502.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l64-64c1.9-1.9 3.9-3.4 6-4.8C101.5 367.8 96 344.6 96 320H32c-17.7 0-32-14.3-32-32s14.3-32 32-32H96.3c1.1-14.1 5-27.5 11.1-39.5c-.7-.6-1.4-1.2-2.1-1.9l-64-64c-12.5-12.5-12.5-32.8 0-45.3z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
1
themes/linkita/static/icons/codeberg.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Codeberg</title><path d="M11.955.49A12 12 0 0 0 0 12.49a12 12 0 0 0 1.832 6.373L11.838 5.928a.187.14 0 0 1 .324 0l10.006 12.935A12 12 0 0 0 24 12.49a12 12 0 0 0-12-12 12 12 0 0 0-.045 0zm.375 6.467l4.416 16.553a12 12 0 0 0 5.137-4.213z"/></svg>
|
||||||
|
After Width: | Height: | Size: 322 B |
1
themes/linkita/static/icons/danger.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M368 128c0 44.4-25.4 83.5-64 106.4V256c0 17.7-14.3 32-32 32H176c-17.7 0-32-14.3-32-32V234.4c-38.6-23-64-62.1-64-106.4C80 57.3 144.5 0 224 0s144 57.3 144 128zM168 176a32 32 0 1 0 0-64 32 32 0 1 0 0 64zm144-32a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zM3.4 273.7c7.9-15.8 27.1-22.2 42.9-14.3L224 348.2l177.7-88.8c15.8-7.9 35-1.5 42.9 14.3s1.5 35-14.3 42.9L295.6 384l134.8 67.4c15.8 7.9 22.2 27.1 14.3 42.9s-27.1 22.2-42.9 14.3L224 419.8 46.3 508.6c-15.8 7.9-35 1.5-42.9-14.3s-1.5-35 14.3-42.9L152.4 384 17.7 316.6C1.9 308.7-4.5 289.5 3.4 273.7z"/></svg>
|
||||||
|
After Width: | Height: | Size: 783 B |
1
themes/linkita/static/icons/email.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M64 112c-8.8 0-16 7.2-16 16v22.1L220.5 291.7c20.7 17 50.4 17 71.1 0L464 150.1V128c0-8.8-7.2-16-16-16H64zM48 212.2V384c0 8.8 7.2 16 16 16H448c8.8 0 16-7.2 16-16V212.2L322 328.8c-38.4 31.5-93.7 31.5-132 0L48 212.2zM0 128C0 92.7 28.7 64 64 64H448c35.3 0 64 28.7 64 64V384c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128z"/></svg>
|
||||||
|
After Width: | Height: | Size: 567 B |
1
themes/linkita/static/icons/example.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M24 56c0-13.3 10.7-24 24-24H80c13.3 0 24 10.7 24 24V176h16c13.3 0 24 10.7 24 24s-10.7 24-24 24H40c-13.3 0-24-10.7-24-24s10.7-24 24-24H56V80H48C34.7 80 24 69.3 24 56zM86.7 341.2c-6.5-7.4-18.3-6.9-24 1.2L51.5 357.9c-7.7 10.8-22.7 13.3-33.5 5.6s-13.3-22.7-5.6-33.5l11.1-15.6c23.7-33.2 72.3-35.6 99.2-4.9c21.3 24.4 20.8 60.9-1.1 84.7L86.8 432H120c13.3 0 24 10.7 24 24s-10.7 24-24 24H32c-9.5 0-18.2-5.6-22-14.4s-2.1-18.9 4.3-25.9l72-78c5.3-5.8 5.4-14.6 .3-20.5zM224 64H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H224c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 160H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H224c-17.7 0-32-14.3-32-32s14.3-32 32-32zm0 160H480c17.7 0 32 14.3 32 32s-14.3 32-32 32H224c-17.7 0-32-14.3-32-32s14.3-32 32-32z"/></svg>
|
||||||
|
After Width: | Height: | Size: 966 B |
1
themes/linkita/static/icons/failure.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z"/></svg>
|
||||||
|
After Width: | Height: | Size: 528 B |
1
themes/linkita/static/icons/git.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Git</title><path d="M23.546 10.93L13.067.452c-.604-.603-1.582-.603-2.188 0L8.708 2.627l2.76 2.76c.645-.215 1.379-.07 1.889.441.516.515.658 1.258.438 1.9l2.658 2.66c.645-.223 1.387-.078 1.9.435.721.72.721 1.884 0 2.604-.719.719-1.881.719-2.6 0-.539-.541-.674-1.337-.404-1.996L12.86 8.955v6.525c.176.086.342.203.488.348.713.721.713 1.883 0 2.6-.719.721-1.889.721-2.609 0-.719-.719-.719-1.879 0-2.598.182-.18.387-.316.605-.406V8.835c-.217-.091-.424-.222-.6-.401-.545-.545-.676-1.342-.396-2.009L7.636 3.7.45 10.881c-.6.605-.6 1.584 0 2.189l10.48 10.477c.604.604 1.582.604 2.186 0l10.43-10.43c.605-.603.605-1.582 0-2.187"/></svg>
|
||||||
|
After Width: | Height: | Size: 702 B |
1
themes/linkita/static/icons/github.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>GitHub</title><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></svg>
|
||||||
|
After Width: | Height: | Size: 822 B |
1
themes/linkita/static/icons/home.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-home"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
|
||||||
|
After Width: | Height: | Size: 334 B |
1
themes/linkita/static/icons/info.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
|
||||||
|
After Width: | Height: | Size: 500 B |
1
themes/linkita/static/icons/linkedin.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>LinkedIn</title><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"/></svg>
|
||||||
|
After Width: | Height: | Size: 610 B |
1
themes/linkita/static/icons/mastodon.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Mastodon</title><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
1
themes/linkita/static/icons/matrix.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>Matrix</title><path d="M.632.55v22.9H2.28V24H0V0h2.28v.55zm7.043 7.26v1.157h.033c.309-.443.683-.784 1.117-1.024.433-.245.936-.365 1.5-.365.54 0 1.033.107 1.481.314.448.208.785.582 1.02 1.108.254-.374.6-.706 1.034-.992.434-.287.95-.43 1.546-.43.453 0 .872.056 1.26.167.388.11.716.286.993.53.276.245.489.559.646.951.152.392.23.863.23 1.417v5.728h-2.349V11.52c0-.286-.01-.559-.032-.812a1.755 1.755 0 0 0-.18-.66 1.106 1.106 0 0 0-.438-.448c-.194-.11-.457-.166-.785-.166-.332 0-.6.064-.803.189a1.38 1.38 0 0 0-.48.499 1.946 1.946 0 0 0-.231.696 5.56 5.56 0 0 0-.06.785v4.768h-2.35v-4.8c0-.254-.004-.503-.018-.752a2.074 2.074 0 0 0-.143-.688 1.052 1.052 0 0 0-.415-.503c-.194-.125-.476-.19-.854-.19-.111 0-.259.024-.439.074-.18.051-.36.143-.53.282-.171.138-.319.337-.439.595-.12.259-.18.6-.18 1.02v4.966H5.46V7.81zm15.693 15.64V.55H21.72V0H24v24h-2.28v-.55z"/></svg>
|
||||||
|
After Width: | Height: | Size: 939 B |
1
themes/linkita/static/icons/note.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M410.3 231l11.3-11.3-33.9-33.9-62.1-62.1L291.7 89.8l-11.3 11.3-22.6 22.6L58.6 322.9c-10.4 10.4-18 23.3-22.2 37.4L1 480.7c-2.5 8.4-.2 17.5 6.1 23.7s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L387.7 253.7 410.3 231zM160 399.4l-9.1 22.7c-4 3.1-8.5 5.4-13.3 6.9L59.4 452l23-78.1c1.4-4.9 3.8-9.4 6.9-13.3l22.7-9.1v32c0 8.8 7.2 16 16 16h32zM362.7 18.7L348.3 33.2 325.7 55.8 314.3 67.1l33.9 33.9 62.1 62.1 33.9 33.9 11.3-11.3 22.6-22.6 14.5-14.5c25-25 25-65.5 0-90.5L453.3 18.7c-25-25-65.5-25-90.5 0zm-47.4 168l-144 144c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l144-144c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6z"/></svg>
|
||||||
|
After Width: | Height: | Size: 863 B |
1
themes/linkita/static/icons/question.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM169.8 165.3c7.9-22.3 29.1-37.3 52.8-37.3h58.3c34.9 0 63.1 28.3 63.1 63.1c0 22.6-12.1 43.5-31.7 54.8L280 264.4c-.2 13-10.9 23.6-24 23.6c-13.3 0-24-10.7-24-24V250.5c0-8.6 4.6-16.5 12.1-20.8l44.3-25.4c4.7-2.7 7.6-7.7 7.6-13.1c0-8.4-6.8-15.1-15.1-15.1H222.6c-3.4 0-6.4 2.1-7.5 5.3l-.4 1.2c-4.4 12.5-18.2 19-30.6 14.6s-19-18.2-14.6-30.6l.4-1.2zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"/></svg>
|
||||||
|
After Width: | Height: | Size: 681 B |
1
themes/linkita/static/icons/quote.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M448 296c0 66.3-53.7 120-120 120h-8c-17.7 0-32-14.3-32-32s14.3-32 32-32h8c30.9 0 56-25.1 56-56v-8H320c-35.3 0-64-28.7-64-64V160c0-35.3 28.7-64 64-64h64c35.3 0 64 28.7 64 64v32 32 72zm-256 0c0 66.3-53.7 120-120 120H64c-17.7 0-32-14.3-32-32s14.3-32 32-32h8c30.9 0 56-25.1 56-56v-8H64c-35.3 0-64-28.7-64-64V160c0-35.3 28.7-64 64-64h64c35.3 0 64 28.7 64 64v32 32 72z"/></svg>
|
||||||
|
After Width: | Height: | Size: 610 B |
1
themes/linkita/static/icons/rss.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="7.5 7.5 81 81"><path d="M14.5 14.5L12 16.9v62.2l2.5 2.4 2.4 2.5h62.2l2.4-2.5 2.5-2.4V16.9l-2.5-2.4-2.4-2.5H16.9l-2.4 2.5zm22.7 10.6C56.7 29.3 72 48 72 67.7V72h-7.7l-.5-6.4C62.3 47.1 46.4 32 28.3 32H24v-4c0-4 0-4 4-4 2.1 0 6.3.5 9.2 1.1zM37 41.6c10.4 3.1 19 15.1 19 26.5 0 3.9 0 3.9-3.9 3.9h-3.8l-.6-5.5c-1.1-10.4-7.8-17.1-18.1-18.2l-5.6-.6v-3.8c0-3.8.1-3.9 3.8-3.9 2.1 0 6.2.7 9.2 1.6zM34 62c1.1 1.1 2 2.9 2 4 0 2.6-3.4 6-6 6s-6-3.4-6-6c0-1.1.9-2.9 2-4s2.9-2 4-2 2.9.9 4 2z"/></svg>
|
||||||
|
After Width: | Height: | Size: 532 B |
1
themes/linkita/static/icons/search.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
|
||||||
|
After Width: | Height: | Size: 310 B |
1
themes/linkita/static/icons/success.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>
|
||||||
|
After Width: | Height: | Size: 441 B |
1
themes/linkita/static/icons/theme.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-0.5 -0.5 25 25" width="64" height="32" fill="none"><g transform="translate(-12,0)"><path d="M9.3812 2.04327C7.76937 2.50154 6.2485 3.36519 4.97948 4.63421C1.00684 8.60687 1.00684 15.0478 4.97948 19.0205C8.95213 22.9932 15.3931 22.9932 19.3657 19.0205C20.6429 17.7433 21.5095 16.211 21.9654 14.5876M9.5384 2C8.6321 5.39377 9.51018 9.16492 12.1726 11.8274C14.8351 14.4899 18.6063 15.368 22 14.4617" stroke="#000" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /></g><g transform="translate(12,0)"><path d="M17.5 17.5L19 19M20 12H22M6.5 6.5L5 5M17.5 6.5L19 5M6.5 17.5L5 19M2 12H4M12 2V4M12 20V22M16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 9.79086 16 12Z" stroke="#000" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /></g></svg>
|
||||||
|
After Width: | Height: | Size: 869 B |
1
themes/linkita/static/icons/tip.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M272 384c9.6-31.9 29.5-59.1 49.2-86.2l0 0c5.2-7.1 10.4-14.2 15.4-21.4c19.8-28.5 31.4-63 31.4-100.3C368 78.8 289.2 0 192 0S16 78.8 16 176c0 37.3 11.6 71.9 31.4 100.3c5 7.2 10.2 14.3 15.4 21.4l0 0c19.8 27.1 39.7 54.4 49.2 86.2H272zM192 512c44.2 0 80-35.8 80-80V416H112v16c0 44.2 35.8 80 80 80zM112 176c0 8.8-7.2 16-16 16s-16-7.2-16-16c0-61.9 50.1-112 112-112c8.8 0 16 7.2 16 16s-7.2 16-16 16c-44.2 0-80 35.8-80 80z"/></svg>
|
||||||
|
After Width: | Height: | Size: 660 B |
1
themes/linkita/static/icons/translations.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-globe"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 411 B |
1
themes/linkita/static/icons/warning.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480H40c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24V296c0 13.3 10.7 24 24 24s24-10.7 24-24V184c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"/></svg>
|
||||||
|
After Width: | Height: | Size: 551 B |
1
themes/linkita/static/icons/youtube.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><title>YouTube</title><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></svg>
|
||||||
|
After Width: | Height: | Size: 459 B |
6
themes/linkita/static/js/gc.min.js
vendored
Normal file
2
themes/linkita/static/js/instantpage.min.js
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
/*! instant.page v5.2.0 - (C) 2019-2023 Alexandre Dieulot - https://instant.page/license */
|
||||||
|
let t,e,n,o,i,a=null,s=65,c=new Set;const r=1111;function d(t){o=performance.now();const e=t.target.closest("a");m(e)&&p(e.href,"high")}function u(t){if(performance.now()-o<r)return;if(!("closest"in t.target))return;const e=t.target.closest("a");m(e)&&(e.addEventListener("mouseout",f,{passive:!0}),i=setTimeout(()=>{p(e.href,"high"),i=void 0},s))}function l(t){const e=t.target.closest("a");m(e)&&p(e.href,"high")}function f(t){t.relatedTarget&&t.target.closest("a")==t.relatedTarget.closest("a")||i&&(clearTimeout(i),i=void 0)}function h(t){if(performance.now()-o<r)return;const e=t.target.closest("a");if(t.which>1||t.metaKey||t.ctrlKey)return;if(!e)return;e.addEventListener("click",function(t){1337!=t.detail&&t.preventDefault()},{capture:!0,passive:!1,once:!0});const n=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!1,detail:1337});e.dispatchEvent(n)}function m(o){if(o&&o.href&&(!n||"instant"in o.dataset)){if(o.origin!=location.origin){if(!(e||"instant"in o.dataset)||!a)return}if(["http:","https:"].includes(o.protocol)&&("http:"!=o.protocol||"https:"!=location.protocol)&&(t||!o.search||"instant"in o.dataset)&&!(o.hash&&o.pathname+o.search==location.pathname+location.search||"noInstant"in o.dataset))return!0}}function p(t,e="auto"){if(c.has(t))return;const n=document.createElement("link");n.rel="prefetch",n.href=t,n.fetchPriority=e,n.as="document",document.head.appendChild(n),c.add(t)}!function(){if(!document.createElement("link").relList.supports("prefetch"))return;const o="instantVaryAccept"in document.body.dataset||"Shopify"in window,i=navigator.userAgent.indexOf("Chrome/");i>-1&&(a=parseInt(navigator.userAgent.substring(i+"Chrome/".length)));if(o&&a&&a<110)return;const c="instantMousedownShortcut"in document.body.dataset;t="instantAllowQueryString"in document.body.dataset,e="instantAllowExternalLinks"in document.body.dataset,n="instantWhitelist"in document.body.dataset;const r={capture:!0,passive:!0};let f=!1,v=!1,g=!1;if("instantIntensity"in document.body.dataset){const t=document.body.dataset.instantIntensity;if(t.startsWith("mousedown"))f=!0,"mousedown-only"==t&&(v=!0);else if(t.startsWith("viewport")){const e=navigator.connection&&navigator.connection.saveData,n=navigator.connection&&navigator.connection.effectiveType&&navigator.connection.effectiveType.includes("2g");e||n||("viewport"==t?document.documentElement.clientWidth*document.documentElement.clientHeight<45e4&&(g=!0):"viewport-all"==t&&(g=!0))}else{const e=parseInt(t);isNaN(e)||(s=e)}}v||document.addEventListener("touchstart",d,r);f?c||document.addEventListener("mousedown",l,r):document.addEventListener("mouseover",u,r);c&&document.addEventListener("mousedown",h,r);if(g){let t=window.requestIdleCallback;t||(t=(t=>{t()})),t(function(){const t=new IntersectionObserver(e=>{e.forEach(e=>{if(e.isIntersecting){const n=e.target;t.unobserve(n),p(n.href)}})});document.querySelectorAll("a").forEach(e=>{m(e)&&t.observe(e)})},{timeout:1500})}}();
|
||||||
1
themes/linkita/static/js/linkita-search.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
"use strict";(function(){let searchFiles;let mySearchIndex;function toggleSearch(){const searchWrapperEl=document.getElementById("linkita-search-wrapper");const searchResultsEl=document.getElementById("linkita-search-results");if(null==searchWrapperEl||null==searchResultsEl){console.error("searchWrapper is null");return}else{searchWrapperEl.classList.remove("hidden")}const q=prompt("Enter your search term");if(null==q){searchWrapperEl.classList.add("hidden");return}if("undefined"===typeof searchIndex&&"undefined"===typeof elasticlunr){searchResultsEl.innerHTML="<li>Search: Please wait...</li>";Promise.all(searchFiles.map(loadScript)).catch((error=>{showError(searchResultsEl,"<li>Search file not found: <code>"+error+"</code></li>")})).then((t=>{mySearchIndex=elasticlunr.Index.load(window.searchIndex);doSearch(q,searchResultsEl)}))}else{doSearch(q,searchResultsEl)}}function doSearch(q,searchResultsEl){const searchResults=mySearchIndex.search(q);const searchResultsCount=searchResults.length;if(searchResultsCount>0){const searchResultsRows=["<li><strong>"+searchResultsCount+"</strong> search "+(searchResultsCount===1?"result":"results")+" for <code>"+mySafe(q)+"</code>:</li>"];for(let i=0;i<searchResultsCount;i++){const searchResult=searchResults[i];searchResultsRows.push('<li><a href="'+mySafe(searchResult.ref)+'">'+mySafe(searchResult.doc.title)+"</a></li>")}searchResultsEl.innerHTML=searchResultsRows.join("");searchResultsEl.scrollIntoViewIfNeeded()}else{showError(searchResultsEl,"<li>No search results for <code>"+mySafe(q)+"</code>.</li>")}}function showError(searchResultsEl,err){searchResultsEl.innerHTML=err;searchResultsEl.scrollIntoViewIfNeeded()}function mySafe(code){return code.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function loadScript(fileName){return new Promise(((resolve,reject)=>{const scriptEl=document.createElement("script");scriptEl.onload=()=>resolve(fileName);scriptEl.onerror=()=>reject(fileName);scriptEl.async=true;scriptEl.src=fileName;document.head.appendChild(scriptEl)}))}function initSearchButton({scripts:scripts}){searchFiles=scripts}if(null==window.linkita)window.linkita={};window.linkita.toggleSearch=toggleSearch;window.linkita.initSearchButton=initSearchButton})();
|
||||||
1
themes/linkita/static/js/linkita.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
"use strict";(function(){const htmlClass=document.documentElement.classList;const themeColorTag=document.head.querySelector('meta[name="theme-color"]');const darkScheme=window.matchMedia("(prefers-color-scheme: dark)");const colorSchemeKey="linkita-color-scheme";function applyDarkMode(isDark,doDispatchEvent){if(isDark){htmlClass.add("dark")}else{htmlClass.remove("dark")}if(undefined!=themeColorTag){themeColorTag.setAttribute("content",isDark?themeColorTag.dataset.dark:themeColorTag.dataset.light)}if(doDispatchEvent&&undefined!=document.body){document.body.dispatchEvent(new CustomEvent("set-theme",{detail:isDark?"dark":"light"}))}}function initDarkMode(){const darkVal=localStorage.getItem(colorSchemeKey);if(darkVal){applyDarkMode(darkVal==="dark",false)}else if(htmlClass.contains("dark")){applyDarkMode(true,false)}else{applyDarkMode(darkScheme.matches,false)}darkScheme.addEventListener("change",(function(event){applyDarkMode(event.matches,true)}));htmlClass.remove("not-ready")}function toggleDarkMode(){const isDark=!htmlClass.contains("dark");applyDarkMode(isDark,true);localStorage.setItem(colorSchemeKey,isDark?"dark":"light")}function resetDarkMode(){localStorage.removeItem(colorSchemeKey);applyDarkMode(darkScheme.matches,true)}function initTranslationsButton({btn:btn,rel:rel}){const pageLanguage=document.documentElement.getAttribute("lang");const pageTranslations=document.head.querySelectorAll('link[rel="alternate"][hreflang]');let userLanguages=[];if(pageTranslations.length<2)return;else if(pageTranslations.length===2)userLanguages=[pageTranslations[0].getAttribute("hreflang"),pageTranslations[1].getAttribute("hreflang")];else if(navigator.languages)userLanguages=navigator.languages;else if(navigator.language!=undefined)userLanguages=[navigator.language];else if(navigator.userLanguage!=undefined)userLanguages=[navigator.userLanguage];const pageTranslationsLinks=new Map;pageTranslations.forEach((function(el){const hreflang=el.getAttribute("hreflang");const href=rel==="true"?el.dataset.href:el.getAttribute("href");if(hreflang!==pageLanguage){pageTranslationsLinks.set(hreflang,href);const hreflangcode=hreflang.split("-")[0];if(!pageTranslationsLinks.has(hreflangcode)){pageTranslationsLinks.set(hreflangcode,href)}}}));const pageTranslationLink=getPageTranslationLink(userLanguages,pageTranslationsLinks);if(undefined!=pageTranslationLink){btn.classList.remove("hidden");btn.addEventListener("click",(function(){window.location.href=pageTranslationLink}))}}function getPageTranslationLink(userLanguages,pageTranslationsLinks){for(let i=0;i<userLanguages.length;i++){const userLanguage=userLanguages[i];const pageTranslationLink=pageTranslationsLinks.get(userLanguage)||pageTranslationsLinks.get(userLanguage.split("-")[0]);if(undefined!=pageTranslationLink){return pageTranslationLink}}}function toggleHeaderMenu(){htmlClass.toggle("open")}function initKatex(){window.renderMathInElement(document.body,{delimiters:[{left:"$$",right:"$$",display:true},{left:"$",right:"$",display:false}],throwOnError:false})}function enableAnalytics({key:key}){return localStorage.removeItem(key)}function disableAnalytics({key:key}){return localStorage.setItem(key,"t")}function isAnalyticsEnabled({key:key,init:init}){if(init){if(window.location.hash==="#enable-analytics"){if(localStorage.getItem(key)==="t"){enableAnalytics({key:key});alert("Analytics is now ENABLED in this browser. To disable analytics, load #disable-analytics.")}}else if(window.location.hash==="#disable-analytics"){if(localStorage.getItem(key)!=="t"){disableAnalytics({key:key});alert("Analytics is now DISABLED in this browser. To enable analytics, load #enable-analytics.")}}}return localStorage.getItem(key)!=="t"}function initGoatCounterAnalytics({src:src,endpoint:endpoint}){if(isAnalyticsEnabled({key:"skipgc",init:true})){const newScript=document.createElement("script");newScript.async=true;newScript.src=src;newScript.dataset.goatcounter=endpoint;if(undefined!=document.body){document.body.appendChild(newScript)}else if(undefined!=document.head){document.head.appendChild(newScript)}}}function initVercelAnalytics({src:src}){if(isAnalyticsEnabled({key:"va-disable",init:true})){if(undefined==window.va){window.va=function(){(window.vaq=window.vaq||[]).push(arguments)}}const newScript=document.createElement("script");newScript.async=true;newScript.src=src;if(undefined!=document.body){document.body.appendChild(newScript)}else if(undefined!=document.head){document.head.appendChild(newScript)}}}function main(){initDarkMode();window.linkita={applyDarkMode:applyDarkMode,toggleDarkMode:toggleDarkMode,resetDarkMode:resetDarkMode,initTranslationsButton:initTranslationsButton,toggleHeaderMenu:toggleHeaderMenu,initKatex:initKatex,isAnalyticsEnabled:isAnalyticsEnabled,enableAnalytics:enableAnalytics,disableAnalytics:disableAnalytics,initGoatCounterAnalytics:initGoatCounterAnalytics,initVercelAnalytics:initVercelAnalytics}}main()})();
|
||||||
125
themes/linkita/static/katex/KaTeX.md
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
<h1><a href="https://katex.org/">
|
||||||
|
<picture>
|
||||||
|
<source media="(prefers-color-scheme: dark)" srcset="https://katex.org/img/katex-logo.svg">
|
||||||
|
<img alt="KaTeX" width=130 src="https://katex.org/img/katex-logo-black.svg">
|
||||||
|
</picture>
|
||||||
|
</a></h1>
|
||||||
|
|
||||||
|
[](https://www.npmjs.com/package/katex)
|
||||||
|
[](https://github.com/semantic-release/semantic-release)
|
||||||
|
[](https://github.com/KaTeX/KaTeX/actions?query=workflow%3ACI)
|
||||||
|
[](https://codecov.io/gh/KaTeX/KaTeX)
|
||||||
|
[](https://github.com/KaTeX/KaTeX/discussions)
|
||||||
|
[](https://www.jsdelivr.com/package/npm/katex)
|
||||||
|

|
||||||
|
[](https://gitpod.io/#https://github.com/KaTeX/KaTeX)
|
||||||
|
[](https://opencollective.com/katex)
|
||||||
|
|
||||||
|
KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.
|
||||||
|
|
||||||
|
* **Fast:** KaTeX renders its math synchronously and doesn't need to reflow the page. See how it compares to a competitor in [this speed test](https://www.intmath.com/cg5/katex-mathjax-comparison.php).
|
||||||
|
* **Print quality:** KaTeX's layout is based on Donald Knuth's TeX, the gold standard for math typesetting.
|
||||||
|
* **Self contained:** KaTeX has no dependencies and can easily be bundled with your website resources.
|
||||||
|
* **Server side rendering:** KaTeX produces the same output regardless of browser or environment, so you can pre-render expressions using Node.js and send them as plain HTML.
|
||||||
|
|
||||||
|
KaTeX is compatible with all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 11.
|
||||||
|
|
||||||
|
KaTeX supports much (but not all) of LaTeX and many LaTeX packages. See the [list of supported functions](https://katex.org/docs/supported.html).
|
||||||
|
|
||||||
|
Try out KaTeX [on the demo page](https://katex.org/#demo)!
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
### Starter template
|
||||||
|
|
||||||
|
```html
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<!-- KaTeX requires the use of the HTML5 doctype. Without it, KaTeX may not render properly -->
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css" integrity="sha384-5TcZemv2l/9On385z///+d7MSYlvIEw9FuZTIdZ14vJLqWphw7e7ZPuOiCHJcFCP" crossorigin="anonymous">
|
||||||
|
|
||||||
|
<!-- The loading of KaTeX is deferred to speed up page rendering -->
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js" integrity="sha384-cMkvdD8LoxVzGF/RPUKAcvmm49FQ0oxwDF3BGKtDXcEc+T1b2N+teh/OJfpU0jr6" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<!-- To automatically render math in text elements, include the auto-render extension: -->
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/contrib/auto-render.min.js" integrity="sha384-hCXGrW6PitJEwbkoStFjeJxv+fSOOQKOPbJxSfM6G5sWZjAyWhXiTIIAmQqnlLlh" crossorigin="anonymous"
|
||||||
|
onload="renderMathInElement(document.body);"></script>
|
||||||
|
</head>
|
||||||
|
...
|
||||||
|
</html>
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also [download KaTeX](https://github.com/KaTeX/KaTeX/releases) and host it yourself.
|
||||||
|
|
||||||
|
For details on how to configure auto-render extension, refer to [the documentation](https://katex.org/docs/autorender.html).
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
Call `katex.render` to render a TeX expression directly into a DOM element.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
katex.render("c = \\pm\\sqrt{a^2 + b^2}", element, {
|
||||||
|
throwOnError: false
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Call `katex.renderToString` to generate an HTML string of the rendered math,
|
||||||
|
e.g., for server-side rendering. For example:
|
||||||
|
|
||||||
|
```js
|
||||||
|
var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}", {
|
||||||
|
throwOnError: false
|
||||||
|
});
|
||||||
|
// '<span class="katex">...</span>'
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure to include the CSS and font files in both cases.
|
||||||
|
If you are doing all rendering on the server, there is no need to include the
|
||||||
|
JavaScript on the client.
|
||||||
|
|
||||||
|
The examples above use the `throwOnError: false` option, which renders invalid
|
||||||
|
inputs as the TeX source code in red (by default), with the error message as
|
||||||
|
hover text. For other available options, see the
|
||||||
|
[API documentation](https://katex.org/docs/api.html),
|
||||||
|
[options documentation](https://katex.org/docs/options.html), and
|
||||||
|
[handling errors documentation](https://katex.org/docs/error.html).
|
||||||
|
|
||||||
|
## Demo and Documentation
|
||||||
|
|
||||||
|
Learn more about using KaTeX [on the website](https://katex.org)!
|
||||||
|
|
||||||
|
## Contributors
|
||||||
|
|
||||||
|
### Code Contributors
|
||||||
|
|
||||||
|
This project exists thanks to all the people who contribute code. If you'd like to help, see [our guide to contributing code](CONTRIBUTING.md).
|
||||||
|
<a href="https://github.com/KaTeX/KaTeX/graphs/contributors"><img src="https://contributors-svg.opencollective.com/katex/contributors.svg?width=890&button=false" alt="Code contributors" /></a>
|
||||||
|
|
||||||
|
### Financial Contributors
|
||||||
|
|
||||||
|
Become a financial contributor and help us sustain our community.
|
||||||
|
|
||||||
|
#### Individuals
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/katex"><img src="https://opencollective.com/katex/individuals.svg?width=890" alt="Contribute on Open Collective"></a>
|
||||||
|
|
||||||
|
#### Organizations
|
||||||
|
|
||||||
|
Support this project with your organization. Your logo will show up here with a link to your website.
|
||||||
|
|
||||||
|
<a href="https://opencollective.com/katex/organization/0/website"><img src="https://opencollective.com/katex/organization/0/avatar.svg" alt="Organization 1"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/1/website"><img src="https://opencollective.com/katex/organization/1/avatar.svg" alt="Organization 2"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/2/website"><img src="https://opencollective.com/katex/organization/2/avatar.svg" alt="Organization 3"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/3/website"><img src="https://opencollective.com/katex/organization/3/avatar.svg" alt="Organization 4"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/4/website"><img src="https://opencollective.com/katex/organization/4/avatar.svg" alt="Organization 5"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/5/website"><img src="https://opencollective.com/katex/organization/5/avatar.svg" alt="Organization 6"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/6/website"><img src="https://opencollective.com/katex/organization/6/avatar.svg" alt="Organization 7"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/7/website"><img src="https://opencollective.com/katex/organization/7/avatar.svg" alt="Organization 8"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/8/website"><img src="https://opencollective.com/katex/organization/8/avatar.svg" alt="Organization 9"></a>
|
||||||
|
<a href="https://opencollective.com/katex/organization/9/website"><img src="https://opencollective.com/katex/organization/9/avatar.svg" alt="Organization 10"></a>
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
KaTeX is licensed under the [MIT License](https://opensource.org/licenses/MIT).
|
||||||
1
themes/linkita/static/katex/contrib/auto-render.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={757:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={};r.d(o,{default:function(){return p}});var i=r(757),a=r.n(i);const l=function(e,t,n){let r=n,o=0;const i=e.length;for(;r<t.length;){const n=t[r];if(o<=0&&t.slice(r,r+i)===e)return r;"\\"===n?r++:"{"===n?o++:"}"===n&&o--,r++}return-1},s=/^\\begin{/;var d=function(e,t){let n;const r=[],o=new RegExp("("+t.map((e=>e.left.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"))).join("|")+")");for(;n=e.search(o),-1!==n;){n>0&&(r.push({type:"text",data:e.slice(0,n)}),e=e.slice(n));const o=t.findIndex((t=>e.startsWith(t.left)));if(n=l(t[o].right,e,t[o].left.length),-1===n)break;const i=e.slice(0,n+t[o].right.length),a=s.test(i)?i:e.slice(t[o].left.length,n);r.push({type:"math",data:a,rawData:i,display:t[o].display}),e=e.slice(n+t[o].right.length)}return""!==e&&r.push({type:"text",data:e}),r};const c=function(e,t){const n=d(e,t.delimiters);if(1===n.length&&"text"===n[0].type)return null;const r=document.createDocumentFragment();for(let e=0;e<n.length;e++)if("text"===n[e].type)r.appendChild(document.createTextNode(n[e].data));else{const o=document.createElement("span");let i=n[e].data;t.displayMode=n[e].display;try{t.preProcess&&(i=t.preProcess(i)),a().render(i,o,t)}catch(o){if(!(o instanceof a().ParseError))throw o;t.errorCallback("KaTeX auto-render: Failed to parse `"+n[e].data+"` with ",o),r.appendChild(document.createTextNode(n[e].rawData));continue}r.appendChild(o)}return r},f=function(e,t){for(let n=0;n<e.childNodes.length;n++){const r=e.childNodes[n];if(3===r.nodeType){let o=r.textContent,i=r.nextSibling,a=0;for(;i&&i.nodeType===Node.TEXT_NODE;)o+=i.textContent,i=i.nextSibling,a++;const l=c(o,t);if(l){for(let e=0;e<a;e++)r.nextSibling.remove();n+=l.childNodes.length-1,e.replaceChild(l,r)}else n+=a}else if(1===r.nodeType){const e=" "+r.className+" ";-1===t.ignoredTags.indexOf(r.nodeName.toLowerCase())&&t.ignoredClasses.every((t=>-1===e.indexOf(" "+t+" ")))&&f(r,t)}}};var p=function(e,t){if(!e)throw new Error("No element provided to render");const n={};for(const e in t)t.hasOwnProperty(e)&&(n[e]=t[e]);n.delimiters=n.delimiters||[{left:"$$",right:"$$",display:!0},{left:"\\(",right:"\\)",display:!1},{left:"\\begin{equation}",right:"\\end{equation}",display:!0},{left:"\\begin{align}",right:"\\end{align}",display:!0},{left:"\\begin{alignat}",right:"\\end{alignat}",display:!0},{left:"\\begin{gather}",right:"\\end{gather}",display:!0},{left:"\\begin{CD}",right:"\\end{CD}",display:!0},{left:"\\[",right:"\\]",display:!0}],n.ignoredTags=n.ignoredTags||["script","noscript","style","textarea","pre","code","option"],n.ignoredClasses=n.ignoredClasses||[],n.errorCallback=n.errorCallback||console.error,n.macros=n.macros||{},f(e,n)};return o=o.default}()}));
|
||||||
1
themes/linkita/static/katex/contrib/copy-tex.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var n=t();for(var o in n)("object"==typeof exports?exports:e)[o]=n[o]}}("undefined"!=typeof self?self:this,(function(){return function(){"use strict";var e={};const t={inline:["$","$"],display:["$$","$$"]};var n=function(e,n){void 0===n&&(n=t);const o=e.querySelectorAll(".katex-mathml + .katex-html");for(let e=0;e<o.length;e++){const t=o[e];t.remove?t.remove():t.parentNode&&t.parentNode.removeChild(t)}const r=e.querySelectorAll(".katex-mathml");for(let e=0;e<r.length;e++){const t=r[e],o=t.querySelector("annotation");o&&(t.replaceWith?t.replaceWith(o):t.parentNode&&t.parentNode.replaceChild(o,t),o.innerHTML=n.inline[0]+o.innerHTML+n.inline[1])}const l=e.querySelectorAll(".katex-display annotation");for(let e=0;e<l.length;e++){const t=l[e];t.innerHTML=n.display[0]+t.innerHTML.substr(n.inline[0].length,t.innerHTML.length-n.inline[0].length-n.inline[1].length)+n.display[1]}return e};function o(e){const t=e instanceof Element?e:e.parentElement;return t&&t.closest(".katex")}return document.addEventListener("copy",(function(e){const t=window.getSelection();if(t.isCollapsed||!e.clipboardData)return;const r=e.clipboardData,l=t.getRangeAt(0),i=o(l.startContainer);i&&l.setStartBefore(i);const a=o(l.endContainer);a&&l.setEndAfter(a);const s=l.cloneContents();if(!s.querySelector(".katex-mathml"))return;const c=Array.prototype.map.call(s.childNodes,(e=>e instanceof Text?e.textContent:e.outerHTML)).join("");r.setData("text/html",c),r.setData("text/plain",n(s).textContent),e.preventDefault()})),e=e.default}()}));
|
||||||
1
themes/linkita/static/katex/contrib/mathtex-script-type.min.js
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
!function(e,t){if("object"==typeof exports&&"object"==typeof module)module.exports=t(require("katex"));else if("function"==typeof define&&define.amd)define(["katex"],t);else{var n="object"==typeof exports?t(require("katex")):t(e.katex);for(var r in n)("object"==typeof exports?exports:e)[r]=n[r]}}("undefined"!=typeof self?self:this,(function(e){return function(){"use strict";var t={757:function(t){t.exports=e}},n={};function r(e){var o=n[e];if(void 0!==o)return o.exports;var i=n[e]={exports:{}};return t[e](i,i.exports,r),i.exports}r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,{a:t}),t},r.d=function(e,t){for(var n in t)r.o(t,n)&&!r.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:t[n]})},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)};var o={},i=r(757),a=r.n(i);let u=document.body.getElementsByTagName("script");return u=Array.prototype.slice.call(u),u.forEach((function(e){if(!e.type||!e.type.match(/math\/tex/i))return-1;const t=null!=e.type.match(/mode\s*=\s*display(;|\s|\n|$)/),n=document.createElement(t?"div":"span");n.setAttribute("class",t?"equation":"inline-equation");try{a().render(e.text,n,{displayMode:t})}catch(t){n.textContent=e.text}e.parentNode.replaceChild(n,e)})),o=o.default}()}));
|
||||||
1
themes/linkita/static/katex/contrib/mhchem.min.js
vendored
Normal file
1
themes/linkita/static/katex/contrib/render-a11y-string.min.js
vendored
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_AMS-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Caligraphic-Bold.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Fraktur-Bold.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Fraktur-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Main-Bold.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Main-BoldItalic.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Main-Italic.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Main-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Math-BoldItalic.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Math-Italic.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_SansSerif-Bold.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_SansSerif-Italic.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_SansSerif-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Script-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Size1-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Size2-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Size3-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Size4-Regular.woff2
Normal file
BIN
themes/linkita/static/katex/fonts/KaTeX_Typewriter-Regular.woff2
Normal file
1
themes/linkita/static/katex/katex.min.css
vendored
Normal file
1
themes/linkita/static/katex/katex.min.js
vendored
Normal file
1
themes/linkita/static/main.min.css
vendored
Normal file
10
themes/linkita/tailwind.config.js
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: ["./templates/**/*.html", "../../templates/**/*.html"],
|
||||||
|
darkMode: "class",
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
safelist: ["horizontal-scroll"],
|
||||||
|
plugins: [require("@tailwindcss/typography")],
|
||||||
|
};
|
||||||
5
themes/linkita/templates/404.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
{% extends "index.html" %}
|
||||||
|
{% block main %}
|
||||||
|
<h1 class="absolute inset-x-8 bottom-20 top-0 flex items-center justify-center text-6xl">{{
|
||||||
|
m_i18n::tr(key=`error_not_found`, lk=g_lang_k, d=g_trans_d) }}</h1>
|
||||||
|
{% endblock main %}
|
||||||
52
themes/linkita/templates/archive.html
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
{% extends "index.html" %}
|
||||||
|
{% block main %}
|
||||||
|
{%- if page.extra.date_format %}
|
||||||
|
{%- set date_format_archive = page.extra.date_format %}
|
||||||
|
{%- elif section.extra.date_format %}
|
||||||
|
{%- set date_format_archive = section.extra.date_format %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set date_format_archive = `%m-%d` %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_profile is defined %}
|
||||||
|
{% include "partials/profile.html" %}
|
||||||
|
{%- elif page.title %}
|
||||||
|
<h1 class="mb-16">{{ page.title }}</h1>
|
||||||
|
{%- elif section.title %}
|
||||||
|
<h1 class="mb-16">{{ section.title }}</h1>
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if page.content %}
|
||||||
|
<section>{{ page.content | safe }}</section>
|
||||||
|
{%- elif section.content %}
|
||||||
|
<section>{{ section.content | safe }}</section>
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if page.extra.section is defined %}
|
||||||
|
{%- set section = get_section(path=page.extra.section) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if section.pages is undefined and paginator.pages is undefined %}
|
||||||
|
{{- throw(message="The archive page is missing the extra.section frontmatter variable!") }}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- for year, posts in section.pages | default(value=paginator.pages) | sort(attribute=`date`) | reverse | group_by(attribute=`year`) %}
|
||||||
|
<h2>{{ year }} <sup class="opacity-60">{{ posts | length }}</sup></h2>
|
||||||
|
|
||||||
|
<div class="not-prose pl-6">
|
||||||
|
<ul>
|
||||||
|
{%- for post in posts %}
|
||||||
|
<li>
|
||||||
|
<div class="my-2 flex items-center justify-between">
|
||||||
|
<a class="secondary-link" href="{{ m_url::rel(there=post.permalink, here=g_here, base=g_base) }}">{%
|
||||||
|
if post.title %}{{ post.title }}{% else %}{{ post.slug }}{% endif %}</a>
|
||||||
|
<time class="font-mono text-sm max-md:flex-none opacity-80" datetime="{{ post.date | date(format=`%+`) }}">{%
|
||||||
|
if config.extra.languages[lang].locale %}{{ post.date | date(format=date_format_archive,
|
||||||
|
locale=config.extra.languages[lang].locale) }}{%
|
||||||
|
else %}{{ post.date | date(format=date_format_archive) }}{% endif %}</time>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
{%- endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{%- endfor %}
|
||||||
|
{% endblock main %}
|
||||||
52
themes/linkita/templates/index.html
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
{% extends "variables.html" %}
|
||||||
|
{% block html -%}
|
||||||
|
<!doctype html>
|
||||||
|
<html class="not-ready lg:text-base overflow-y-scroll scroll-pt-14" lang="{{ g_lang_html | escape_xml | safe }}">
|
||||||
|
|
||||||
|
<head prefix="og: https://ogp.me/ns#{%
|
||||||
|
if g_profile is defined %} profile: https://ogp.me/ns/profile#{%
|
||||||
|
elif g_is_article %} article: https://ogp.me/ns/article#{%
|
||||||
|
endif %}">
|
||||||
|
{%- include "partials/head.html" %}
|
||||||
|
<!-- Begin Head inject -->
|
||||||
|
{% include "injects/head.html" ignore missing %}
|
||||||
|
<!-- End Head inject -->
|
||||||
|
{% include "partials/head_res.html" %}
|
||||||
|
<!-- Begin Head End inject -->
|
||||||
|
{% include "injects/head_end.html" ignore missing %}
|
||||||
|
<!-- End Head End inject -->
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="text-black duration-100 ease-out bg-[var(--bg)] dark:text-white">
|
||||||
|
{% include "partials/header.html" %}
|
||||||
|
<!-- Begin Body Start inject -->
|
||||||
|
{% include "injects/body_start.html" ignore missing %}
|
||||||
|
<!-- End Body Start inject -->
|
||||||
|
<main class="prose prose-neutral relative mx-auto min-h-[calc(100%-4rem)]
|
||||||
|
max-w-3xl break-words px-4 pb-12 pt-28 lg:pt-32 dark:prose-invert prose-pre:rounded-lg prose-img:rounded-lg">
|
||||||
|
{%- if config.build_search_index %}
|
||||||
|
{% include "partials/search.html" %}
|
||||||
|
{%- endif %}
|
||||||
|
{% block main %}
|
||||||
|
{%- if g_profile is defined %}
|
||||||
|
{%- if paginator is undefined or paginator.current_index == 1 %}
|
||||||
|
{% include "partials/profile.html" %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif section.title %}
|
||||||
|
<h1 class="mb-16">{{ section.title }}</h1>
|
||||||
|
{%- endif %}
|
||||||
|
{%- if section.content %}
|
||||||
|
<section>{{ section.content | safe }}</section>
|
||||||
|
{%- endif %}
|
||||||
|
{% include "partials/page_list.html" %}
|
||||||
|
{% endblock main %}
|
||||||
|
</main>
|
||||||
|
{% include "partials/footer.html" %}
|
||||||
|
{% include "partials/body_res.html" %}
|
||||||
|
<!-- Begin Body End inject -->
|
||||||
|
{% include "injects/body_end.html" ignore missing %}
|
||||||
|
<!-- End Body End inject -->
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
{%- endblock html %}
|
||||||
11
themes/linkita/templates/internal/alias.html
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link rel="canonical" href="{{ url | safe }}">
|
||||||
|
<title>Redirect</title>
|
||||||
|
<script>
|
||||||
|
window.location.replace("{{ url | safe }}" + (window.location.search || "") + (window.location.hash || ""));
|
||||||
|
</script>
|
||||||
|
<noscript>
|
||||||
|
<meta http-equiv="refresh" content="0; url={{ url | safe }}">
|
||||||
|
</noscript>
|
||||||
|
<p><a href="{{ url | safe }}">Click here</a> to be redirected.</p>
|
||||||
21
themes/linkita/templates/macros/i18n.html
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
{%- macro tr(key, lk, d) %}{%
|
||||||
|
if d[key][lk] %}{{
|
||||||
|
d[key][lk] }}{%
|
||||||
|
elif d[key]["en"] %}{{
|
||||||
|
d[key]["en"] }}{%
|
||||||
|
else %}{{
|
||||||
|
throw(message="No translation for key: " ~ key ~ ", language: " ~ lk) }}{%
|
||||||
|
endif %}
|
||||||
|
{%- endmacro tr -%}
|
||||||
|
|
||||||
|
{%- macro get_taxonomy_title(key, lk, d) %}{%
|
||||||
|
if key == "tags" %}{{
|
||||||
|
self::tr(key=`taxonomy_tags`, lk=lk, d=d) }}{%
|
||||||
|
elif key == "categories" %}{{
|
||||||
|
self::tr(key=`taxonomy_categories`, lk=lk, d=d) }}{%
|
||||||
|
elif key == "authors" %}{{
|
||||||
|
self::tr(key=`taxonomy_authors`, lk=lk, d=d) }}{%
|
||||||
|
else %}{{
|
||||||
|
key }}{%
|
||||||
|
endif %}
|
||||||
|
{%- endmacro get_taxonomy_title -%}
|
||||||
14
themes/linkita/templates/macros/profiles.html
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
{%- macro get_name(username) %}{%
|
||||||
|
if config.extra.profiles[username] %}{%
|
||||||
|
set profile_data = config.extra.profiles[username] %}{%
|
||||||
|
if profile_data.languages[lang].name %}{{
|
||||||
|
profile_data.languages[lang].name }}{%
|
||||||
|
elif profile_data.name %}{{
|
||||||
|
profile_data.name }}{%
|
||||||
|
else %}{{
|
||||||
|
username }}{%
|
||||||
|
endif %}{%
|
||||||
|
else %}{{
|
||||||
|
username }}{%
|
||||||
|
endif %}
|
||||||
|
{%- endmacro get_name -%}
|
||||||
49
themes/linkita/templates/macros/url.html
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
{%- macro rel(there, here, base) -%}
|
||||||
|
{%- if here and there is starting_with(base) %}
|
||||||
|
{%- for _ in here %}
|
||||||
|
{%- if not loop.last %}
|
||||||
|
{{- "../" | safe }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
{{- there | trim_start_matches(pat=base) | safe }}
|
||||||
|
{%- if config.extra.urls_to_index_html and there is ending_with("/") %}
|
||||||
|
{{- "index.html" }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- else %}
|
||||||
|
{{- there | safe }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endmacro rel -%}
|
||||||
|
|
||||||
|
{%- macro get(url, bu="") -%}
|
||||||
|
{%- if url is starting_with("@") %}
|
||||||
|
{{- get_url(path=url) }}
|
||||||
|
{%- elif url is starting_with("$BASE_URL") %}
|
||||||
|
{%- if bu == "" %}{% set bu = get_url(path=``, lang=lang) %}{% endif %}
|
||||||
|
{{- bu | safe }}{{ url | trim_start_matches(pat="$BASE_URL") | safe }}
|
||||||
|
{%- else %}
|
||||||
|
{{- url | safe }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endmacro get -%}
|
||||||
|
|
||||||
|
{%- macro social_icon(icon, prl=false) -%}
|
||||||
|
{%- set icon_path = "icons/" ~ icon ~ ".svg" %}
|
||||||
|
{%- set meta = get_image_metadata(path=icon_path, allow_missing=true) %}
|
||||||
|
{%- if meta %}
|
||||||
|
{%- if prl %}
|
||||||
|
{{- get_url(path=icon_path) }}
|
||||||
|
{%- else %}
|
||||||
|
{{- icon_path | safe }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- else %}
|
||||||
|
{{- "https://cdn.jsdelivr.net/npm/simple-icons/icons/" ~ icon ~ ".svg" | safe }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endmacro social_icon -%}
|
||||||
|
|
||||||
|
{%- macro cover_image(path, assets) -%}
|
||||||
|
{%- for asset in assets %}
|
||||||
|
{%- if asset is ending_with(path) %}
|
||||||
|
{{- asset | safe }}
|
||||||
|
{%- break %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endmacro cover_image -%}
|
||||||
45
themes/linkita/templates/page.html
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
{% extends "index.html" %}
|
||||||
|
{%- block meta %}
|
||||||
|
{%- set g_is_article = true %}
|
||||||
|
{%- endblock meta %}
|
||||||
|
{% block main %}
|
||||||
|
<article>
|
||||||
|
<!-- Begin Page Start inject -->
|
||||||
|
{% include "injects/page_start.html" ignore missing %}
|
||||||
|
<!-- End Page Start inject -->
|
||||||
|
|
||||||
|
<header class="mb-16">
|
||||||
|
<h1 class="!my-0 pb-2.5">{{ page.title }}</h1>
|
||||||
|
{% include "partials/page_info.html" %}
|
||||||
|
</header>
|
||||||
|
{%- if g_image_url %}
|
||||||
|
<figure class="mb-12 mt-0">
|
||||||
|
<img
|
||||||
|
class="h-auto w-full rounded-lg"
|
||||||
|
src="{{ m_url::rel(there=g_image_url, here=g_here, base=g_base) }}"
|
||||||
|
{%- if page.extra.cover.alt is defined %}
|
||||||
|
alt="{{ page.extra.cover.alt | escape_xml | safe }}"{% endif %}
|
||||||
|
{%- if g_image_data.width is defined %}
|
||||||
|
width="{{ g_image_data.width | int }}"{% endif %}
|
||||||
|
{%- if g_image_data.height is defined %}
|
||||||
|
height="{{ g_image_data.height | int }}"{% endif %}
|
||||||
|
/>
|
||||||
|
</figure>
|
||||||
|
{%- endif %}
|
||||||
|
{%- set page_toc = true %}
|
||||||
|
{% include "partials/toc.html" %}
|
||||||
|
<!-- Content -->
|
||||||
|
<section>{{ page.content | safe }}</section>
|
||||||
|
<hr />
|
||||||
|
{% include "partials/post_taxonomies.html" %}
|
||||||
|
{%- if page.lower or page.higher %}
|
||||||
|
{% include "partials/post_navigation.html" %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.extra.comment | default(value=config.extra.comment) %}
|
||||||
|
{% include "partials/comment.html" %}
|
||||||
|
{%- endif %}
|
||||||
|
<!-- Begin Page End inject -->
|
||||||
|
{% include "injects/page_end.html" ignore missing %}
|
||||||
|
<!-- End Page End inject -->
|
||||||
|
</article>
|
||||||
|
{% endblock main %}
|
||||||
32
themes/linkita/templates/pages.html
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{% extends "index.html" %}
|
||||||
|
{%- block meta %}
|
||||||
|
{%- set g_is_article = true %}
|
||||||
|
{%- endblock meta %}
|
||||||
|
{% block main %}
|
||||||
|
{%- if g_profile is defined %}
|
||||||
|
{% include "partials/profile.html" %}
|
||||||
|
{%- endif %}
|
||||||
|
<article>
|
||||||
|
{%- if g_profile is undefined and (page.title or section.title) %}
|
||||||
|
<header class="mb-16">
|
||||||
|
{%- if page.title %}
|
||||||
|
<h1 class="!my-0">{{ page.title }}</h1>
|
||||||
|
{%- elif section.title %}
|
||||||
|
<h1 class="!my-0">{{ section.title }}</h1>
|
||||||
|
{%- endif %}
|
||||||
|
</header>
|
||||||
|
{%- endif %}
|
||||||
|
{%- set page_toc = false %}
|
||||||
|
{% include "partials/toc.html" %}
|
||||||
|
<!-- Content -->
|
||||||
|
{%- if page.content is defined %}
|
||||||
|
<section>{{ page.content | safe }}</section>
|
||||||
|
{%- elif section.content is defined %}
|
||||||
|
<section>{{ section.content | safe }}</section>
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.lower or page.higher %}
|
||||||
|
<hr />
|
||||||
|
{% include "partials/post_navigation.html" %}
|
||||||
|
{%- endif %}
|
||||||
|
</article>
|
||||||
|
{% endblock main %}
|
||||||
29
themes/linkita/templates/partials/body_res.html
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
{%- if not config.extra.disable_javascript %}
|
||||||
|
{%- if page.extra.mermaid | default(value=(section.extra.mermaid | default(value=config.extra.mermaid))) %}
|
||||||
|
{% include "partials/mermaid.html" %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.goatcounter.endpoint %}
|
||||||
|
{%- if config.extra.goatcounter.src %}
|
||||||
|
{%- set goatcounter_src = config.extra.goatcounter.src %}
|
||||||
|
{%- elif config.extra.use_cdn %}
|
||||||
|
{%- set goatcounter_src = "//gc.zgo.at/count.js" %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set goatcounter_src = m_url::rel(there=get_url(path=`js/gc.min.js`, cachebust=true), here=g_here, base=g_base) %}
|
||||||
|
{%- endif %}
|
||||||
|
<!-- GoatCounter -->
|
||||||
|
<script>window.linkita.initGoatCounterAnalytics({
|
||||||
|
src: "{{ goatcounter_src | escape_xml | safe }}",
|
||||||
|
endpoint: "{{ config.extra.goatcounter.endpoint | escape_xml | safe }}"
|
||||||
|
});</script>
|
||||||
|
{%- if config.extra.goatcounter.noscript_prefix is defined and current_path is defined %}
|
||||||
|
<noscript><img src="{{ config.extra.goatcounter.endpoint | escape_xml | safe }}?p={{
|
||||||
|
config.extra.goatcounter.noscript_prefix ~ current_path | urlencode | safe }}" alt=""></noscript>
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.vercel_analytics.src %}
|
||||||
|
<!-- Vercel Web Analytics -->
|
||||||
|
<script>window.linkita.initVercelAnalytics({
|
||||||
|
src: "{{ config.extra.vercel_analytics.src | escape_xml | safe }}"
|
||||||
|
});</script>
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
21
themes/linkita/templates/partials/comment.html
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
<!-- Comment -->
|
||||||
|
{%- if config.extra.giscus.repo and not config.extra.disable_javascript %}
|
||||||
|
<div class="giscus mt-12"></div>
|
||||||
|
<script
|
||||||
|
src="https://giscus.app/client.js"
|
||||||
|
data-repo="{{ config.extra.giscus.repo | escape_xml | safe }}"
|
||||||
|
data-repo-id="{{ config.extra.giscus.repo_id | escape_xml | safe }}"
|
||||||
|
data-category="{{ config.extra.giscus.category | escape_xml | safe }}"
|
||||||
|
data-category-id="{{ config.extra.giscus.category_id | escape_xml | safe }}"
|
||||||
|
data-mapping="{{ config.extra.giscus.mapping | default(value=`pathname`) | escape_xml | safe }}"
|
||||||
|
data-strict="{{ config.extra.giscus.strict | default(value=`1`) | int }}"
|
||||||
|
data-reactions-enabled="{{ config.extra.giscus.reactions_enabled | default(value=`0`) | int }}"
|
||||||
|
data-emit-metadata="{{ config.extra.giscus.emit_metadata | default(value=`0`) | int }}"
|
||||||
|
data-input-position="{{ config.extra.giscus.input_position | default(value=`top`) | escape_xml | safe }}"
|
||||||
|
data-theme="{{ config.extra.giscus.theme | default(value=`light`) | escape_xml | safe }}"
|
||||||
|
data-lang="{{ config.extra.giscus.lang | default(value=`en`) | escape_xml | safe }}"
|
||||||
|
data-loading="{{ config.extra.giscus.loading | default(value=`lazy`) | escape_xml | safe }}"
|
||||||
|
crossorigin="anonymous"
|
||||||
|
async
|
||||||
|
></script>
|
||||||
|
{%- endif %}
|
||||||
45
themes/linkita/templates/partials/footer.html
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="mx-auto flex lg:mt-5 max-w-3xl flex-wrap items-center px-4 py-3 text-sm opacity-60">
|
||||||
|
<div class="mr-auto basis-full lg:basis-1/2">
|
||||||
|
{%- set lang_base_url = get_url(path=``, lang=lang) %}
|
||||||
|
{%- if config.extra.footer.copyright %}
|
||||||
|
{%- set copyright = config.extra.footer.copyright | replace(from=`$BASE_URL`, to=lang_base_url) %}
|
||||||
|
{%- if config.extra.footer.license_url %}
|
||||||
|
{%- set license_url = m_url::rel(there=m_url::get(url=config.extra.footer.license_url), here=g_here, base=g_base) %}
|
||||||
|
{%- set copyright = copyright | replace(from=`$LICENSE_URL`, to=license_url) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- else %}
|
||||||
|
{%- if config.author %}
|
||||||
|
{%- set author_name = m_profiles::get_name(username=config.author) %}
|
||||||
|
{%- set copyright = "© $YEAR " ~ author_name %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set copyright = "© $YEAR" %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.footer.license_url %}
|
||||||
|
{%- set license_url = m_url::rel(there=m_url::get(url=config.extra.footer.license_url), here=g_here, base=g_base) %}
|
||||||
|
{%- set copyright = '[' ~ copyright ~ '](' ~ license_url ~ ')' %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- set current_year = now() | date(format=`%Y`) | int %}
|
||||||
|
{%- if config.extra.footer.since and config.extra.footer.since != current_year %}
|
||||||
|
{%- set copyright_years = '<time datetime="' ~ config.extra.footer.since ~ '">' ~
|
||||||
|
config.extra.footer.since ~ '</time> - <time datetime="' ~ current_year ~ '">' ~
|
||||||
|
current_year ~ '</time>' %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set copyright_years = '<time datetime="' ~ current_year ~ '">' ~ current_year ~ '</time>' %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- set copyright = copyright | replace(from=`$YEAR`, to=copyright_years) %}
|
||||||
|
{{ copyright | markdown(inline=true) | safe }}
|
||||||
|
</div>
|
||||||
|
<div class="flex basis-full lg:basis-1/2 lg:justify-end">
|
||||||
|
<span class="mr-6 lg:ml-6">
|
||||||
|
<a class="link" href="https://www.getzola.org/" target="_blank">{{
|
||||||
|
m_i18n::tr(key=`footer_powered_by`, lk=g_lang_k, d=g_trans_d) }}</a>
|
||||||
|
</span>
|
||||||
|
<a class="link" href="https://www.getzola.org/themes/linkita/" target="_blank">✎ {{
|
||||||
|
m_i18n::tr(key=`footer_theme_name`, lk=g_lang_k, d=g_trans_d) }}</a>
|
||||||
|
</div>
|
||||||
|
<!-- Begin Footer inject -->
|
||||||
|
{% include "injects/footer.html" ignore missing %}
|
||||||
|
<!-- End Footer inject -->
|
||||||
|
</footer>
|
||||||
251
themes/linkita/templates/partials/head.html
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
{%- set config_title = config.title | default(value="") | truncate(length=100) %}
|
||||||
|
{%- set title_separator = config.extra.title_separator | default(value= " | ") %}
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
|
||||||
|
<meta name="generator" content="Zola" />
|
||||||
|
{%- if page.title %}
|
||||||
|
{%- set page_title = page.title | truncate(length=200) %}
|
||||||
|
{%- if page.description %}
|
||||||
|
{%- set page_description = page.description %}
|
||||||
|
{%- elif page.summary %}
|
||||||
|
{%- set page_description = page.summary | linebreaksbr | striptags | trim_end_matches(pat=`.`) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif taxonomy.name %}
|
||||||
|
{%- set page_title = m_i18n::get_taxonomy_title(key=taxonomy.name, lk=g_lang_k, d=g_trans_d) %}
|
||||||
|
{%- if term.name %}
|
||||||
|
{%- if taxonomy.name == "authors" %}
|
||||||
|
{%- set term_name = m_profiles::get_name(username=term.name) %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set term_name = term.name %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- set page_title = term_name ~ title_separator ~ page_title %}
|
||||||
|
{%- if config.extra.languages[lang].term_descriptions[taxonomy.name] %}
|
||||||
|
{%- set page_description = config.extra.languages[lang].term_descriptions[taxonomy.name] |
|
||||||
|
replace(from=`$NAME`, to=term_name) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- else %}
|
||||||
|
{%- if config.extra.languages[lang].taxonomy_descriptions[taxonomy.name] %}
|
||||||
|
{%- set page_description = config.extra.languages[lang].taxonomy_descriptions[taxonomy.name] %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif section.title %}
|
||||||
|
{%- set page_title = section.title | truncate(length=200) %}
|
||||||
|
{%- if section.description %}
|
||||||
|
{%- set page_description = section.description %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif current_url %}
|
||||||
|
{%- set page_title = config_title %}
|
||||||
|
{%- set page_description = config.description | default(value="") %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set page_title = m_i18n::tr(key=`error_not_found`, lk=g_lang_k, d=g_trans_d) %}
|
||||||
|
{%- set is_404 = true %}
|
||||||
|
<meta name="robots" content="noindex" />
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if page_title %}
|
||||||
|
{%- if config_title %}
|
||||||
|
<title>{%
|
||||||
|
if page_title == config_title %}{{ page_title }}{%
|
||||||
|
else %}{{ page_title ~ title_separator ~ config_title }}{%
|
||||||
|
endif %}</title>
|
||||||
|
<meta property="og:site_name" content="{{ config_title | linebreaksbr | truncate(length=100) | escape_xml | safe }}" />
|
||||||
|
{%- else %}
|
||||||
|
<title>{{ page_title }}</title>
|
||||||
|
{%- endif %}
|
||||||
|
<meta property="og:title" content="{{ page_title | linebreaksbr | truncate(length=200) | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page_description %}
|
||||||
|
<meta name="description" content="{{ page_description | linebreaksbr | truncate(length=200, end=`…`) | escape_xml | safe }}" />
|
||||||
|
<meta property="og:description" content="{{ page_description | linebreaksbr | truncate(length=300, end=`…`) | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if current_url %}
|
||||||
|
{%- if page.extra.open_graph.url %}
|
||||||
|
<meta property="og:url" content="{{ page.extra.open_graph.url | escape_xml | safe }}" />
|
||||||
|
{%- elif section.extra.open_graph.url %}
|
||||||
|
<meta property="og:url" content="{{ section.extra.open_graph.url | escape_xml | safe }}" />
|
||||||
|
{%- else %}
|
||||||
|
<meta property="og:url" content="{{ current_url | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
<link rel="canonical" href="{{ current_url | escape_xml | safe }}" />
|
||||||
|
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if g_is_article %}
|
||||||
|
<meta property="og:type" content="article" />
|
||||||
|
{%- if page.date %}
|
||||||
|
<meta property="article:published_time" content="{{ page.date | date(format=`%+`) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.updated %}
|
||||||
|
<meta property="article:modified_time" content="{{ page.updated | date(format=`%+`) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.extra.open_graph.expiration_time %}
|
||||||
|
<meta property="article:expiration_time" content="{{ page.extra.open_graph.expiration_time | date(format=`%+`) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.taxonomies.authors %}
|
||||||
|
{%- for page_author in page.taxonomies.authors %}
|
||||||
|
<meta property="article:author" content="{{ get_taxonomy_url(kind=`authors`, name=page_author, lang=lang) }}" />
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.extra.open_graph.content_tier %}
|
||||||
|
<meta property="article:content_tier" content="{{ page.extra.open_graph.content_tier | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.extra.open_graph.section %}
|
||||||
|
<meta property="article:section" content="{{ page.extra.open_graph.section | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.extra.open_graph.opinion is defined %}
|
||||||
|
<meta property="article:opinion" content="{{ page.extra.open_graph.opinion == true }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.extra.open_graph.locations %}
|
||||||
|
{%- for og_location in page.extra.open_graph.locations %}
|
||||||
|
<meta property="article:location" content="{{ og_location | escape_xml | safe }}" />
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if page.taxonomies.tags %}
|
||||||
|
{%- for og_tag in page.taxonomies.tags %}
|
||||||
|
<meta property="article:tag" content="{{ og_tag | escape_xml | safe }}" />
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif g_profile is defined %}
|
||||||
|
<meta property="og:type" content="profile" />
|
||||||
|
{%- if g_profile.open_graph.first_name %}
|
||||||
|
<meta property="profile:first_name" content="{{ g_profile.open_graph.first_name | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_profile.open_graph.last_name %}
|
||||||
|
<meta property="profile:last_name" content="{{ g_profile.open_graph.last_name | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_profile.open_graph.username %}
|
||||||
|
<meta property="profile:username" content="{{ g_profile.open_graph.username | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_profile.open_graph.gender %}
|
||||||
|
<meta property="profile:gender" content="{{ g_profile.open_graph.gender | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- else %}
|
||||||
|
<meta property="og:type" content="website" />
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if g_profile is defined or (config.author and config.extra.profiles[config.author]) %}
|
||||||
|
{#- order is important #}
|
||||||
|
{%- if config.extra.profiles[config.author].open_graph %}
|
||||||
|
{%- set profile_og_data = config.extra.profiles[config.author].open_graph %}
|
||||||
|
{%- if profile_og_data.fb_app_id %}
|
||||||
|
<meta property="fb:app_id" content="{{ profile_og_data.fb_app_id | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if profile_og_data.fb_admins %}
|
||||||
|
{%- for fb_admin in profile_og_data.fb_admins %}
|
||||||
|
<meta property="fb:admins" content="{{ fb_admin | escape_xml | safe }}" />
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_profile.open_graph %}
|
||||||
|
{%- set profile_og_data = g_profile.open_graph %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if profile_og_data.fediverse_creator.handle and profile_og_data.fediverse_creator.domain %}
|
||||||
|
<meta name="fediverse:creator" content="@{{ profile_og_data.fediverse_creator.handle | escape_xml | safe
|
||||||
|
}}@{{ profile_og_data.fediverse_creator.domain | replace(from=`https://`, to=``) | escape_xml | safe }}" />
|
||||||
|
{%- if profile_og_data.fediverse_creator.url %}
|
||||||
|
<link rel="me" href="{{ profile_og_data.fediverse_creator.url | escape_xml | safe }}" />
|
||||||
|
{%- else %}
|
||||||
|
<link rel="me" href="https://{{ profile_og_data.fediverse_creator.domain | replace(from=`https://`, to=``) |
|
||||||
|
escape_xml | safe }}/@{{ profile_og_data.fediverse_creator.handle | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if g_image_url or g_profile.open_graph.image or (
|
||||||
|
config.author and config.extra.profiles[config.author].open_graph.image) %}
|
||||||
|
{%- if g_image_url %}
|
||||||
|
{%- set og_image_alt = page.extra.cover.alt | default(value=(section.extra.cover.alt | default(value=""))) %}
|
||||||
|
{%- elif g_profile.open_graph.image %}
|
||||||
|
{%- set og_image_path = g_profile.open_graph.image %}
|
||||||
|
{%- if g_profile.open_graph.languages[lang].image_alt is defined %}
|
||||||
|
{%- set og_image_alt = g_profile.open_graph.languages[lang].image_alt %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set og_image_alt = g_profile.open_graph.image_alt | default(value="") %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set og_image_path = config.extra.profiles[config.author].open_graph.image %}
|
||||||
|
{%- if config.extra.profiles[config.author].open_graph.languages[lang].image_alt is defined %}
|
||||||
|
{%- set og_image_alt = config.extra.profiles[config.author].open_graph.languages[lang].image_alt %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set og_image_alt = config.extra.profiles[config.author].open_graph.image_alt | default(value=config.author) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_image_url %}
|
||||||
|
<meta property="og:image" content="{{ g_image_url | escape_xml | safe }}" />
|
||||||
|
{%- else %}
|
||||||
|
{%- set g_image_data = get_image_metadata(path=og_image_path, allow_missing=false) %}
|
||||||
|
<meta property="og:image" content="{{ get_url(path=og_image_path) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
<meta property="og:image:alt" content="{{ og_image_alt | linebreaksbr | truncate(length=500) | escape_xml | safe }}" />
|
||||||
|
{%- if g_image_data.width is defined and g_image_data.height is defined %}
|
||||||
|
<meta property="og:image:width" content="{{ g_image_data.width | int }}" />
|
||||||
|
<meta property="og:image:height" content="{{ g_image_data.height | int }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_image_data.mime is defined %}
|
||||||
|
<meta property="og:image:type" content="{{ g_image_data.mime | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if page.translations or section.translations %}
|
||||||
|
{%- set page_translations = page.translations | default(value=section.translations) | sort(attribute=`lang`) %}
|
||||||
|
{%- for page_translation in page_translations %}
|
||||||
|
{%- if page_translation.lang == lang and config.extra.languages[lang].locale %}
|
||||||
|
<meta property="og:locale" content="{{ config.extra.languages[lang].locale | escape_xml | safe }}" />
|
||||||
|
{%- elif config.extra.languages[page_translation.lang].locale %}
|
||||||
|
<meta property="og:locale:alternate" content="{{
|
||||||
|
config.extra.languages[page_translation.lang].locale | escape_xml | safe }}" />
|
||||||
|
{%- else %}
|
||||||
|
<!-- No locale for {{ page_translation.lang }} -->
|
||||||
|
{%- endif %}
|
||||||
|
<link rel="alternate" href="{{ page_translation.permalink | escape_xml | safe }}" hreflang="{%
|
||||||
|
if config.extra.languages[page_translation.lang].language_code %}{{
|
||||||
|
config.extra.languages[page_translation.lang].language_code | escape_xml | safe }}{%
|
||||||
|
else %}{{ page_translation.lang | escape_xml | safe }}{% endif %}"{%
|
||||||
|
if config.extra.relative_urls %} data-href="{{
|
||||||
|
m_url::rel(there=page_translation.permalink, here=g_here, base=g_base) }}"{% endif %} />
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if config.generate_feeds or section.generate_feeds or (taxonomy.feed and term.path) %}
|
||||||
|
{%- for feed_filename in config.feed_filenames %}
|
||||||
|
{%- if taxonomy.feed and term.path %}
|
||||||
|
{%- set feed_title = page_title ~ title_separator %}
|
||||||
|
{%- set feed_filename = term.path ~ feed_filename %}
|
||||||
|
{%- elif config_title %}
|
||||||
|
{%- set feed_title = config_title ~ title_separator %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set feed_title = "" %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if feed_filename is ending_with("atom.xml") %}
|
||||||
|
<link rel="alternate" type="application/atom+xml" href="{{
|
||||||
|
m_url::rel(there=get_url(path=feed_filename, lang=lang), here=g_here, base=g_base)
|
||||||
|
}}" title="{{ feed_title ~ `Atom` | escape_xml | safe }}" />
|
||||||
|
{%- elif feed_filename is ending_with("rss.xml") %}
|
||||||
|
<link rel="alternate" type="application/rss+xml" href="{{
|
||||||
|
m_url::rel(there=get_url(path=feed_filename, lang=lang), here=g_here, base=g_base)
|
||||||
|
}}" title="{{ feed_title ~ `RSS` | escape_xml | safe }}" />
|
||||||
|
{%- else %}
|
||||||
|
<link rel="alternate" href="{{
|
||||||
|
m_url::rel(there=get_url(path=feed_filename, lang=lang), here=g_here, base=g_base)
|
||||||
|
}}" title="{{ feed_title ~ feed_filename | escape_xml | safe }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if config.extra.webmanifest %}
|
||||||
|
<link rel="manifest" href="{{ m_url::rel(there=m_url::get(url=config.extra.webmanifest), here=g_here, base=g_base) }}">
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.footer.license_url %}
|
||||||
|
<link rel="license" href="{{ m_url::rel(there=m_url::get(url=config.extra.footer.license_url), here=g_here, base=g_base) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.footer.privacy_policy_url %}
|
||||||
|
<link rel="privacy-policy" href="{{ m_url::rel(there=m_url::get(url=config.extra.footer.privacy_policy_url), here=g_here, base=g_base) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.footer.terms_of_service_url %}
|
||||||
|
<link rel="terms-of-service" href="{{ m_url::rel(there=m_url::get(url=config.extra.footer.terms_of_service_url), here=g_here, base=g_base) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.footer.search_page_url %}
|
||||||
|
<link rel="search" href="{{ m_url::rel(there=m_url::get(url=config.extra.footer.search_page_url), here=g_here, base=g_base) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{#- -#}
|
||||||
42
themes/linkita/templates/partials/head_res.html
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
<link rel="stylesheet" href="{{ m_url::rel(there=get_url(path=`main.min.css`, cachebust=true), here=g_here, base=g_base) }}" />
|
||||||
|
{%- include "partials/head_style.html" %}
|
||||||
|
{%- if g_profile is defined and not config.extra.urls_to_index_html %}
|
||||||
|
{%- if g_profile.avatar_url %}
|
||||||
|
<link rel="preload" as="image" href="{{ m_url::rel(there=get_url(path=g_profile.avatar_url, cachebust=true), here=g_here, base=g_base) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
{%- if g_profile.social %}
|
||||||
|
{%- for image_social in g_profile.social %}
|
||||||
|
<link rel="preload" as="image" href="{{ m_url::rel(there=m_url::social_icon(icon=image_social.name, prl=true), here=g_here, base=g_base) }}" />
|
||||||
|
{%- endfor %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if not config.extra.disable_default_favicon %}
|
||||||
|
<link rel="icon" type="image/x-icon" sizes="16x16" href="{{ m_url::rel(there=get_url(path=`favicon.ico`), here=g_here, base=g_base) }}" />
|
||||||
|
<link rel="apple-touch-icon" type="image/png" href="{{ m_url::rel(there=get_url(path=`apple-touch-icon.png`, cachebust=true), here=g_here, base=g_base) }}" />
|
||||||
|
<link rel="icon" type="image/png" href="{{ m_url::rel(there=get_url(path=`android-icon.png`, cachebust=true), here=g_here, base=g_base) }}" />
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if not config.extra.disable_javascript %}
|
||||||
|
<script src="{{ m_url::rel(there=get_url(path=`js/linkita.min.js`, cachebust=true), here=g_here, base=g_base) }}"></script>
|
||||||
|
{%- if config.build_search_index %}
|
||||||
|
<script src="{{ m_url::rel(there=get_url(path=`js/linkita-search.min.js`, cachebust=true), here=g_here, base=g_base) }}"></script>
|
||||||
|
{%- endif %}
|
||||||
|
|
||||||
|
{%- if page.extra.math | default(value=(section.extra.math | default(value=config.extra.math))) %}
|
||||||
|
{%- if config.extra.use_cdn %}
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.css"
|
||||||
|
integrity="sha384-5TcZemv2l/9On385z///+d7MSYlvIEw9FuZTIdZ14vJLqWphw7e7ZPuOiCHJcFCP" crossorigin="anonymous" />
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/katex.min.js"
|
||||||
|
integrity="sha384-cMkvdD8LoxVzGF/RPUKAcvmm49FQ0oxwDF3BGKtDXcEc+T1b2N+teh/OJfpU0jr6" crossorigin="anonymous"></script>
|
||||||
|
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.22/dist/contrib/auto-render.min.js"
|
||||||
|
integrity="sha384-hCXGrW6PitJEwbkoStFjeJxv+fSOOQKOPbJxSfM6G5sWZjAyWhXiTIIAmQqnlLlh" crossorigin="anonymous"></script>
|
||||||
|
{%- else %}
|
||||||
|
<link rel="stylesheet" href="{{ m_url::rel(there=get_url(path=`katex/katex.min.css`, cachebust=true), here=g_here, base=g_base) }}" />
|
||||||
|
<script defer src="{{ m_url::rel(there=get_url(path=`katex/katex.min.js`, cachebust=true), here=g_here, base=g_base) }}"></script>
|
||||||
|
<script defer src="{{ m_url::rel(there=get_url(path=`katex/contrib/auto-render.min.js`, cachebust=true), here=g_here, base=g_base) }}"></script>
|
||||||
|
{%- endif %}
|
||||||
|
<script>document.addEventListener("DOMContentLoaded", window.linkita.initKatex);</script>
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{#- -#}
|
||||||
28
themes/linkita/templates/partials/head_style.html
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{%- set style_bg_color = page.extra.style.bg_color |
|
||||||
|
default(value=(section.extra.style.bg_color |
|
||||||
|
default(value=(config.extra.style.bg_color |
|
||||||
|
default(value="#f4f4f5"))))) %}
|
||||||
|
{%- set style_bg_dark_color = page.extra.style.bg_dark_color |
|
||||||
|
default(value=(section.extra.style.bg_dark_color |
|
||||||
|
default(value=(config.extra.style.bg_dark_color |
|
||||||
|
default(value="#18181b"))))) %}
|
||||||
|
{%- set style_header_color = page.extra.style.header_color |
|
||||||
|
default(value=(section.extra.style.header_color |
|
||||||
|
default(value=(config.extra.style.header_color |
|
||||||
|
default(value="#e4e4e7"))))) %}
|
||||||
|
{%- set style_header_dark_color = page.extra.style.header_dark_color |
|
||||||
|
default(value=(section.extra.style.header_dark_color |
|
||||||
|
default(value=(config.extra.style.header_dark_color |
|
||||||
|
default(value="#27272a"))))) %}
|
||||||
|
<style>
|
||||||
|
:root{--bg: {{ style_bg_color | escape_xml | safe
|
||||||
|
}}; --header: {{ style_header_color | escape_xml | safe
|
||||||
|
}}; color-scheme: light;}
|
||||||
|
:root.dark{--bg: {{ style_bg_dark_color | escape_xml | safe
|
||||||
|
}}; --header: {{ style_header_dark_color | escape_xml | safe
|
||||||
|
}}; color-scheme: dark;}
|
||||||
|
</style>
|
||||||
|
{%- if not config.extra.style.header_blur %}
|
||||||
|
<meta name="theme-color" data-light="{{ style_header_color | escape_xml | safe }}" data-dark="{{
|
||||||
|
style_header_dark_color | escape_xml | safe }}" content="{{ style_header_color | escape_xml | safe }}" />
|
||||||
|
{%- endif -%}
|
||||||
93
themes/linkita/templates/partials/header.html
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
<!-- Header -->
|
||||||
|
{%- if config.extra.languages[lang].header_buttons is defined %}
|
||||||
|
{%- set header_buttons = config.extra.languages[lang].header_buttons %}
|
||||||
|
{%- elif config.extra.header_buttons is defined %}
|
||||||
|
{%- set header_buttons = config.extra.header_buttons %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set header_buttons = ["site_title", "theme_button", "search_button", "translations_button"] %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- if config.extra.languages[lang].header_menu_name %}
|
||||||
|
{%- if config.extra.menus[config.extra.languages[lang].header_menu_name] is defined %}
|
||||||
|
{%- set header_menu = config.extra.menus[config.extra.languages[lang].header_menu_name] %}
|
||||||
|
{%- else %}
|
||||||
|
{{- throw(message="The '" ~ config.extra.languages[lang].header_menu_name ~ "' menu is undefined!") }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif config.extra.header_menu_name %}
|
||||||
|
{%- if config.extra.menus[config.extra.header_menu_name] is defined %}
|
||||||
|
{%- set header_menu = config.extra.menus[config.extra.header_menu_name] %}
|
||||||
|
{%- else %}
|
||||||
|
{{- throw(message="The '" ~ config.extra.header_menu_name ~ "' menu is undefined!") }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif config.extra.menu %}
|
||||||
|
{%- set header_menu = config.extra.menu %}
|
||||||
|
{%- endif %}
|
||||||
|
<header class="{%
|
||||||
|
if config.extra.style.header_blur %}blur-header{% else %}bg-[var(--header)]{% endif %} fixed top-0 z-40 mx-auto min-h-[3.25rem] w-full header-icons">
|
||||||
|
<div class="mx-auto w-full max-w-4xl p-2.5 lg:flex lg:justify-between">
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<div class="flex items-center min-h-8">
|
||||||
|
{%- for header_button in header_buttons %}
|
||||||
|
{%- if header_button == "site_title" and config.title %}
|
||||||
|
<a title="{{ m_i18n::tr(key=`act_go_to_home`, lk=g_lang_k, d=g_trans_d) }}" accesskey="!"
|
||||||
|
href="{{ m_url::rel(there=m_url::get(url=`$BASE_URL/`), here=g_here, base=g_base) }}" class="text-2xl font-semibold">{{ config.title }}</a>
|
||||||
|
{%- elif header_button == "home_button" or header_button == "site_title" %}
|
||||||
|
<button type="button" title="{{ m_i18n::tr(key=`act_go_to_home`, lk=g_lang_k, d=g_trans_d) }}" accesskey="!"
|
||||||
|
onclick="window.location.href='{{ m_url::rel(there=m_url::get(url=`$BASE_URL/`), here=g_here, base=g_base) }}';"
|
||||||
|
class="btn-home h-6 w-6 shrink-0 cursor-pointer text-[0] bg-center bg-no-repeat bg-cover [background-image:var(--icon-home)] dark:invert"
|
||||||
|
></button>
|
||||||
|
{%- elif header_button == "theme_button" %}
|
||||||
|
<button type="button" title="{{ m_i18n::tr(key=`act_switch_color_scheme`, lk=g_lang_k, d=g_trans_d) }}" accesskey="$"
|
||||||
|
onclick="window.linkita.toggleDarkMode();" ondblclick="window.linkita.resetDarkMode();"
|
||||||
|
class="btn-dark ml-4 h-6 w-6 shrink-0 cursor-pointer text-[0] bg-center bg-no-repeat bg-cover dark:invert [background-image:var(--icon-theme-dark)] dark:[background-image:var(--icon-theme-light)]"
|
||||||
|
></button>
|
||||||
|
{%- elif header_button == "search_button" and config.build_search_index %}
|
||||||
|
<button type="button" title="{{ m_i18n::tr(key=`act_search`, lk=g_lang_k, d=g_trans_d) }}" accesskey="/"
|
||||||
|
onclick="window.linkita.toggleSearch();"
|
||||||
|
class="btn-search ml-4 h-6 w-6 shrink-0 cursor-pointer text-[0] bg-center bg-no-repeat bg-cover [background-image:var(--icon-search)] dark:invert"
|
||||||
|
></button>
|
||||||
|
{%- if not config.extra.disable_javascript %}
|
||||||
|
<script>window.linkita.initSearchButton({ scripts: ["{{ m_url::rel(there=get_url(path=`elasticlunr.min.js`), here=g_here, base=g_base) }}", "{{ m_url::rel(there=get_url(path=`search_index.`~lang~`.js`), here=g_here, base=g_base) }}"] });</script>
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif header_button == "translations_button" and (page.translations or section.translations) %}
|
||||||
|
<button type="button" title="{{ m_i18n::tr(key=`act_go_to_translation`, lk=g_lang_k, d=g_trans_d) }}" accesskey=";"
|
||||||
|
class="btn-translations hidden ml-4 h-6 w-6 shrink-0 cursor-pointer text-[0] bg-center bg-no-repeat bg-cover [background-image:var(--icon-translations)] dark:invert"
|
||||||
|
></button>
|
||||||
|
{%- if not config.extra.disable_javascript %}
|
||||||
|
<script>window.linkita.initTranslationsButton({btn: document.querySelector(".btn-translations"), rel: "{{ config.extra.relative_urls | default(value=false) }}"});</script>
|
||||||
|
{%- endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
</div>
|
||||||
|
{%- if header_menu %}
|
||||||
|
<div title="{{ m_i18n::tr(key=`act_menu`, lk=g_lang_k, d=g_trans_d) }}" role="button" accesskey="+" tabindex="0"
|
||||||
|
class="btn-menu relative z-50 flex h-8 w-8 shrink-0 cursor-pointer flex-col items-center justify-center gap-2.5 lg:hidden"
|
||||||
|
onclick="window.linkita.toggleHeaderMenu();"
|
||||||
|
onkeydown="(event.keyCode == 13 || event.keyCode == 32) ? event.preventDefault() || window.linkita.toggleHeaderMenu() : true;"
|
||||||
|
></div>
|
||||||
|
{%- endif %}
|
||||||
|
</div>
|
||||||
|
{%- if header_menu %}
|
||||||
|
<nav class="flex w-full items-center lg:w-auto">
|
||||||
|
<menu
|
||||||
|
class="nav-wrapper flex w-full flex-col py-2 lg:w-auto lg:flex-row lg:self-center lg:py-0">
|
||||||
|
{%- for menu in header_menu %}
|
||||||
|
<li>
|
||||||
|
<a
|
||||||
|
class="primary-link block py-2 text-center text-lg font-medium lg:px-3 lg:py-0"
|
||||||
|
href="{{ m_url::rel(there=m_url::get(url=menu.url), here=g_here, base=g_base) }}"
|
||||||
|
>{%
|
||||||
|
if menu.names[lang] %}{{ menu.names[lang] }}{%
|
||||||
|
elif menu.names_i18n and g_trans_d[menu.names_i18n][g_lang_k] %}{{
|
||||||
|
g_trans_d[menu.names_i18n][g_lang_k] }}{%
|
||||||
|
elif menu.name %}{{ menu.name }}{%
|
||||||
|
else %}{{ menu.url | replace(from=`$BASE_URL`, to=``) | escape_xml | safe }}{% endif %}</a>
|
||||||
|
</li>
|
||||||
|
{%- endfor %}
|
||||||
|
</menu>
|
||||||
|
<!-- Begin Header Nav inject -->
|
||||||
|
{% include "injects/header_nav.html" ignore missing %}
|
||||||
|
<!-- End Header Nav inject -->
|
||||||
|
</nav>
|
||||||
|
{%- endif %}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
33
themes/linkita/templates/partials/mermaid.html
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
<!-- Mermaid -->
|
||||||
|
<script type="module">
|
||||||
|
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
||||||
|
function initMermaid(isDark) {
|
||||||
|
mermaid.initialize({
|
||||||
|
theme: isDark ? "dark" : "default",
|
||||||
|
themeVariables: {
|
||||||
|
darkMode: isDark
|
||||||
|
},
|
||||||
|
startOnLoad: false,
|
||||||
|
});
|
||||||
|
mermaid.run();
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add data-mermaid-code attribute on all mermaid block.
|
||||||
|
document.querySelectorAll(".mermaid").forEach((element) => {
|
||||||
|
element.setAttribute("data-mermaid-code", element.innerHTML);
|
||||||
|
});
|
||||||
|
|
||||||
|
initMermaid(document.documentElement.classList.contains("dark"));
|
||||||
|
|
||||||
|
// Re-render mermaid when theme changed.
|
||||||
|
document.body.addEventListener("set-theme", (e) => {
|
||||||
|
document.querySelectorAll(".mermaid").forEach((element) => {
|
||||||
|
const mermaidCode = element.getAttribute("data-mermaid-code");
|
||||||
|
if (mermaidCode != null) {
|
||||||
|
element.removeAttribute("data-processed");
|
||||||
|
element.innerHTML = mermaidCode;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
initMermaid(e.detail === "dark");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
99
themes/linkita/templates/partials/page_info.html
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
<!-- Page Info -->
|
||||||
|
<div class="text-sm antialiased opacity-80">
|
||||||
|
{%- set is_paginator = paginator is defined %}
|
||||||
|
{%- if page.extra.page_info is defined %}
|
||||||
|
{%- set page_info = page.extra.page_info %}
|
||||||
|
{%- elif section.extra.page_info is defined %}
|
||||||
|
{%- set page_info = section.extra.page_info %}
|
||||||
|
{%- elif is_paginator %}
|
||||||
|
{%- if config.extra.languages[lang].page_info_on_paginator is defined %}
|
||||||
|
{%- set page_info = config.extra.languages[lang].page_info_on_paginator %}
|
||||||
|
{%- elif config.extra.page_info_on_paginator is defined %}
|
||||||
|
{%- set page_info = config.extra.page_info_on_paginator %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set page_info = load_data(literal=`[{"when": "date"}, {"when": "reading_time"}]`, format=`json`) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif config.extra.languages[lang].page_info is defined %}
|
||||||
|
{%- set page_info = config.extra.languages[lang].page_info %}
|
||||||
|
{%- elif config.extra.page_info is defined %}
|
||||||
|
{%- set page_info = config.extra.page_info %}
|
||||||
|
{%- else %}
|
||||||
|
{%- set page_info = load_data(literal=`[{"when": "date"}, {"when": "date_updated", "prepend": "(", "append": ")"}, {"when": "reading_time"}]`, format=`json`) %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- set date_format = config.extra.languages[lang].date_format | default(value="%F") %}
|
||||||
|
{%- set info_sep = m_i18n::tr(key=`middot`, lk=g_lang_k, d=g_trans_d) %}
|
||||||
|
|
||||||
|
{%- for info in page_info %}
|
||||||
|
{%- if info.when == "date" %}{%
|
||||||
|
if page.date %}{%
|
||||||
|
if info.prepend %}{{ info.prepend }}{% endif %}<time
|
||||||
|
datetime="{{ page.date | date(format=`%+`) }}">{%
|
||||||
|
if config.extra.languages[lang].locale %}{{
|
||||||
|
page.date | date(format=date_format, locale=config.extra.languages[lang].locale) }}{%
|
||||||
|
else %}{{ page.date | date(format=date_format) }}{% endif %}</time>
|
||||||
|
{%- if info.append %}{{ info.append }}{% endif %}
|
||||||
|
{%- if not loop.last %}<span
|
||||||
|
class="mx-1">{{ info_sep | safe }}</span>{% endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif info.when == "date_updated" %}{%
|
||||||
|
if page.updated and page.updated != page.date %}{%
|
||||||
|
if info.prepend %}{{ info.prepend }}{% endif %}<time
|
||||||
|
datetime="{{ page.updated | date(format=`%+`) }}">{%
|
||||||
|
if config.extra.languages[lang].locale %}{{
|
||||||
|
page.updated | date(format=date_format, locale=config.extra.languages[lang].locale) }}{%
|
||||||
|
else %}{{ page.updated | date(format=date_format) }}{% endif %}</time>
|
||||||
|
{%- if info.append %}{{ info.append }}{% endif %}
|
||||||
|
{%- if not loop.last %}<span
|
||||||
|
class="mx-1">{{ info_sep | safe }}</span>{% endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif info.when == "reading_time" %}{%
|
||||||
|
if info.prepend %}{{ info.prepend }}{% endif %}{%
|
||||||
|
set reading_time_minutes = page.reading_time % 60 %}<time
|
||||||
|
datetime="PT{{ (page.reading_time - reading_time_minutes) / 60 }}H{{ reading_time_minutes }}M0S">{{
|
||||||
|
page.reading_time }} {{ m_i18n::tr(key=`word_minutes`, lk=g_lang_k, d=g_trans_d) }}</time>
|
||||||
|
{%- if info.append %}{{ info.append }}{% endif %}
|
||||||
|
{%- if not loop.last %}<span
|
||||||
|
class="mx-1">{{ info_sep | safe }}</span>{% endif %}
|
||||||
|
{%- elif info.when == "word_count" %}{%
|
||||||
|
if info.prepend %}{{ info.prepend }}{% endif %}<span>{{
|
||||||
|
page.word_count }} {{ m_i18n::tr(key=`word_words`, lk=g_lang_k, d=g_trans_d) }}</span>
|
||||||
|
{%- if info.append %}{{ info.append }}{% endif %}
|
||||||
|
{%- if not loop.last %}<span
|
||||||
|
class="mx-1">{{ info_sep | safe }}</span>{% endif %}
|
||||||
|
{%- elif info.when == "authors" %}{%
|
||||||
|
if page.taxonomies.authors %}{% set page_authors = page.taxonomies.authors %}{%
|
||||||
|
elif page.authors %}{% set page_authors = page.authors %}{%
|
||||||
|
elif config.author %}{% set page_authors = [config.author] %}{%
|
||||||
|
else %}{% endif %}{%
|
||||||
|
if page_authors %}{%
|
||||||
|
if info.prepend %}{{ info.prepend }}{% endif %}{%
|
||||||
|
for page_author in page_authors %}{%
|
||||||
|
if not loop.first %}, {% endif %}<span>{{
|
||||||
|
m_profiles::get_name(username=page_author) }}</span>
|
||||||
|
{%- endfor %}
|
||||||
|
{%- if info.append %}{{ info.append }}{% endif %}
|
||||||
|
{%- if not loop.last %}<span
|
||||||
|
class="mx-1">{{ info_sep | safe }}</span>{% endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif info.when == "tags" %}{%
|
||||||
|
if page.taxonomies.tags %}{%
|
||||||
|
if info.prepend %}{{ info.prepend }}{% endif %}{%
|
||||||
|
for page_tag in page.taxonomies.tags %}{%
|
||||||
|
if not loop.first %} {% endif %}{%
|
||||||
|
if is_paginator %}<span>#{{ page_tag }}</span>{%
|
||||||
|
else %}<a
|
||||||
|
class="no-underline hover:underline"
|
||||||
|
href="{{ m_url::rel(there=get_taxonomy_url(kind=`tags`, name=page_tag, lang=lang),
|
||||||
|
here=g_here, base=g_base) }}">#{{ page_tag }}</a>{% endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
{%- if info.append %}{{ info.append }}{% endif %}
|
||||||
|
{%- if not loop.last %}<span
|
||||||
|
class="mx-1">{{ info_sep | safe }}</span>{% endif %}
|
||||||
|
{%- endif %}
|
||||||
|
{%- elif info.when == "" %}{{
|
||||||
|
info.prepend | safe }}
|
||||||
|
{%- else %}{{
|
||||||
|
throw(message="page info is not valid, unknown 'when': '" ~ info.when ~ "'") }}
|
||||||
|
{%- endif %}
|
||||||
|
{%- endfor %}
|
||||||
|
</div>
|
||||||