63 Commits
v2.7.3 ... main

Author SHA1 Message Date
895ac03590 fallback 2026-02-04 08:55:30 +01:00
cad6d88b22 import update 2026-02-04 08:54:19 +01:00
e06df9444d update theme color 2026-02-04 08:53:49 +01:00
9d5ce13e14 version bump 2026-02-04 08:47:29 +01:00
6593e63ea9 updated themes version 2026-02-04 08:41:54 +01:00
027a5688cb updated dependencies 2026-02-04 07:53:28 +01:00
1aa9dd716b recursive darktheme applied 2026-02-04 07:17:29 +01:00
d1f7f62229 added automatic dark theme detection and dark mode switch 2026-02-03 15:35:45 +01:00
2f37f78039 updated subproject 2026-01-26 15:49:28 +01:00
b109627bf9 fixed so symlink gets resolved 2026-01-26 15:47:10 +01:00
4ef1b429f2 version bump 2026-01-07 14:36:21 +01:00
b8314c0e3d don't leave progressbar 2026-01-07 08:15:56 +01:00
60f3ca1f20 reduced thumbnail quality 2025-12-11 08:59:40 +01:00
bc519ac5d0 Remove duplicate 'Star History' section
Removed duplicate 'Star History' section from README.
2025-12-10 13:30:26 +01:00
b0c1b2ce48 Add Star History section to README 2025-12-10 13:29:56 +01:00
faa283efa3 tags optional 2025-11-25 07:07:47 +01:00
ce70aa7bd9 changed python version 2025-11-07 09:22:47 +01:00
59c7cfd406 breaking change: new requirement ConfigArgParse 2025-11-07 09:04:17 +01:00
cfe1585c39 added exception to log 2025-10-27 15:05:10 +01:00
57b6b56388 fixed TypeError 2025-10-27 12:06:14 +01:00
2c6f8fbcb7 move thumbdir declare 2025-10-27 11:12:03 +01:00
30eb1ed8df added metadata dataclass 2025-10-27 09:51:00 +01:00
0ef6e1ccf6 default [] 2025-10-27 06:49:55 +01:00
f24a24bcd2 fixed extension exclude 2025-10-22 14:53:07 +02:00
d1bf0fbc9a added --ignore-extension argument 2025-10-17 15:11:54 +02:00
285227dea7 updated themes 2025-10-10 09:32:31 +02:00
3867d56896 added submodule 2025-10-10 09:26:38 +02:00
d86da24963 moved themes to submodule 2025-10-10 09:25:29 +02:00
922b56c27e update default theme 2025-10-01 10:07:32 +02:00
a67968d222 fixed tag render in title 2025-09-29 17:33:56 +02:00
d094cd6683 test show tags in title 2025-09-29 15:14:17 +02:00
83a29bf62b fixed centering 2025-09-28 17:45:15 +02:00
0f955993ca added title 2025-09-28 16:40:06 +02:00
559ff6a7b3 version bump 2025-09-28 15:45:59 +02:00
3e3bb67884 fixed wrong preload on filtering 2025-09-28 15:34:37 +02:00
d0935a474f forgot the debug timeout 2025-09-25 14:32:11 +02:00
a57bc8e3b5 added loader to imagelist 2025-09-25 14:27:12 +02:00
08622bd092 added non breaking space 2025-09-15 13:19:45 +02:00
a8b2df43bd moved item name replace 2025-08-20 08:42:35 +02:00
b249ae9d46 patched fotoswipe to use searchParams instead of the wrong and stupid hash 2025-08-18 23:04:58 +02:00
b4badfbd15 replaced with URL 2025-08-18 14:21:21 +02:00
cb0fe96654 added pathname 2025-08-18 14:18:18 +02:00
6b04b75314 test window.location object use 2025-08-18 13:28:14 +02:00
6076b5f6f2 updated requirements.txt 2025-07-09 11:45:22 +02:00
5036ff79f7 hotfix 2025-07-06 17:34:26 +02:00
50ca8ab5bf fixed reset not unchecking recursive 2025-07-05 22:50:11 +02:00
3f427dfa32 moved javascript to global file, added js minifier and html formatter 2025-07-05 00:54:17 +02:00
9b7c3dc697 added --reread-sidecar option 2025-07-02 13:35:42 +02:00
c732b49339 ui improvements 2025-06-30 00:18:51 +02:00
9829a74f95 hotfix symbols in URL issue 2025-06-29 19:34:34 +02:00
372b960261 fixed selection of partial matching tags 2025-06-29 12:54:40 +02:00
c671c2a4bc fixed double showing of recursive filtered items 2025-06-27 23:48:21 +02:00
8088b2e69c sort reintroduced 2025-06-27 13:29:56 +02:00
e4d63de680 recursive sharable link 2025-06-27 11:32:03 +02:00
0e8287e4a8 more percentencode 2025-06-27 09:09:05 +02:00
48950d1d82 fixed url flicker 2025-06-27 00:18:46 +02:00
e1d74c3091 hm... 2025-06-27 00:03:35 +02:00
f57d81f508 hotfix 2025-06-26 23:21:01 +02:00
67a9058b7d well then... recursive filtering works :) 2025-06-26 22:00:03 +02:00
79e34d7e43 hierarchical tagging selection 2025-06-26 13:36:02 +02:00
0cda1706fa added selected tags after hash in url 2025-06-25 22:37:42 +02:00
85467f4f1e remove st tag 2025-06-24 19:45:41 +02:00
00ccb96581 moved functions around 2025-06-24 16:22:33 +02:00
90 changed files with 5447 additions and 3050 deletions

View File

@@ -8,7 +8,9 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
python-version: '3.12'
- name: Install PyInstaller
run: pip install pyinstaller
- name: Install Dependencies
run: pip install -r requirements.txt
- name: Build Package

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "themes"]
path = themes
url = https://github.com/greflm13/SGBthemes

10
.hintrc
View File

@@ -3,6 +3,14 @@
"development"
],
"hints": {
"apple-touch-icons": "off"
"apple-touch-icons": "off",
"compat-api/css": [
"default",
{
"ignore": [
"-webkit-app-region"
]
}
]
}
}

View File

@@ -1 +1 @@
2.7.3
2.9.1

View File

@@ -55,6 +55,7 @@ The script supports several command-line options to customize its behavior. Belo
- `--ignore-other-files`: Ignore files that do not match the specified extensions.
- `--regenerate-thumbnails`: Regenerate thumbnails even if they already exist.
- `--reread-metadata`: Reread image metadata if it already exists.
- `--reread-sidecar`: Reread sidecar file data.
- `--reverse-sort`: Sort images by reverse name order.
- `--theme-path PATH`: Specify the path to the CSS theme file. Default is the provided default theme.
- `--use-fancy-folders`: Enable fancy folder view instead of the default Apache directory listing.
@@ -102,3 +103,7 @@ To generate a web manifest file:
## License
This project is licensed under the AGPL-3.0 License. See the [LICENSE](LICENSE) file for details.
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=greflm13/StaticGalleryBuilder&type=date&legend=top-left)](https://www.star-history.com/#greflm13/StaticGalleryBuilder&type=date&legend=top-left)

View File

@@ -90,8 +90,9 @@
"Scans",
"--exclude-folder",
"*/Galleries/*",
"--folderthumbnails",
"--reread-metadata"
"--exclude-folder",
"*/Wallpaper/*",
"--folderthumbnails"
],
"console": "integratedTerminal",
"name": "production",
@@ -161,10 +162,12 @@
}
],
"prettier.htmlWhitespaceSensitivity": "css",
"pylint.args": [
"--disable=C0111",
"--disable=C0301",
"--good-names-rgxs=^[_a-z][_a-z0-9]?$"
"python-envs.pythonProjects": [
{
"path": "",
"envManager": "ms-python.python:pyenv",
"packageManager": "ms-python.python:pip"
}
],
"python.analysis.inlayHints.callArgumentNames": "off",
"python.analysis.inlayHints.functionReturnTypes": false,
@@ -257,6 +260,21 @@
"showReuseMessage": false,
"clear": true
}
},
{
"command": "COLUMNS=120 ./builder.py --generate-help-preview help.svg",
"isBackground": false,
"label": "Create help svg",
"problemMatcher": [],
"type": "shell",
"presentation": {
"echo": false,
"reveal": "always",
"focus": true,
"panel": "dedicated",
"showReuseMessage": false,
"clear": true
}
}
]
}

View File

@@ -11,17 +11,14 @@ from pathlib import Path
from tqdm.auto import tqdm
from PIL import Image, ImageOps
from jsmin import jsmin
from modules.argumentparser import parse_arguments, Args
# fmt: off
# Constants
if __package__ is None:
PACKAGE = ""
else:
PACKAGE = __package__
SCRIPTDIR = os.path.abspath(os.path.dirname(__file__).removesuffix(PACKAGE))
SCRIPTDIR = os.path.dirname(os.path.realpath(__file__)).removesuffix(__package__ if __package__ else "")
STATIC_FILES_DIR = os.path.join(os.path.abspath(SCRIPTDIR), "files")
VERSION = open(os.path.join(SCRIPTDIR, ".version"), "r", encoding="utf-8").read()
RAW_EXTENSIONS = [
@@ -36,8 +33,8 @@ NOT_LIST = ["*/Galleries/*", "Archives"]
args = parse_arguments(VERSION)
lock_file = os.path.join(args.root_directory, ".lock")
if os.path.exists(lock_file):
LOCKFILE = os.path.join(args.root_directory, ".lock")
if os.path.exists(LOCKFILE):
print("Another instance of this program is running.")
sys.exit()
else:
@@ -74,7 +71,48 @@ def init_globals(_args: Args, raw: list[str]) -> tuple[Args, list[str]]:
return _args, raw
def copy_static_files(_args: Args) -> None:
def handle_theme_icon(themepath: str, dest: str) -> None:
"""
Handle the icon specified in the theme file.
"""
logger.info("reading theme file", extra={"theme": themepath})
with open(themepath, "r", encoding="utf-8") as f:
theme = f.read()
split = theme.split(".foldericon {")
split2 = split[1].split("}", maxsplit=1)
themehead = split[0]
themetail = split2[1]
foldericon = split2[0].strip()
foldericon = re.sub(r"/\*.*\*/", "", foldericon)
for match in re.finditer(r"content: (.*);", foldericon):
foldericon = match[1]
foldericon = foldericon.replace('"', "")
logger.info("found foldericon", extra={"foldericon": foldericon})
break
if "url" in foldericon:
logger.info("foldericon in theme file, using it")
shutil.copyfile(themepath, dest)
else:
with open(os.path.join(SCRIPTDIR, foldericon), "r", encoding="utf-8") as f:
logger.info("Reading foldericon svg")
svg = f.read()
if "svg.j2" in foldericon:
logger.info("foldericon in theme file is a jinja2 template")
colorscheme = extract_colorscheme(themepath)
for color_key, color_value in colorscheme.items():
svg = svg.replace(f"{{{{ {color_key} }}}}", color_value)
logger.info("replaced colors in svg")
svg = urllib.parse.quote(svg)
with open(dest, "x", encoding="utf-8") as f:
logger.info("writing theme file")
f.write(themehead + '\n.foldericon {\n content: url("data:image/svg+xml,' + svg + '");\n}\n' + themetail)
def copy_static_files(_args: Args) -> bool:
"""
Copy static files to the root directory.
@@ -84,6 +122,7 @@ def copy_static_files(_args: Args) -> None:
Parsed command-line arguments.
"""
static_dir = os.path.join(_args.root_directory, ".static")
darktheme = False
if os.path.exists(static_dir):
print("Removing existing .static folder...")
logger.info("removing existing .static folder")
@@ -92,37 +131,20 @@ def copy_static_files(_args: Args) -> None:
print("Copying static files...")
logger.info("copying static files")
shutil.copytree(STATIC_FILES_DIR, static_dir, dirs_exist_ok=True)
logger.info("reading theme file", extra={"theme": _args.theme_path})
with open(_args.theme_path, "r", encoding="utf-8") as f:
theme = f.read()
split = theme.split(".foldericon {")
split2 = split[1].split("}", maxsplit=1)
themehead = split[0]
themetail = split2[1]
foldericon = split2[0].strip()
foldericon = re.sub(r"/\*.*\*/", "", foldericon)
for match in re.finditer(r"content: (.*);", foldericon):
foldericon = match[1]
foldericon = foldericon.replace('"', "")
logger.info("found foldericon", extra={"foldericon": foldericon})
break
if "url" in foldericon:
logger.info("foldericon in theme file, using it")
shutil.copyfile(_args.theme_path, os.path.join(static_dir, "theme.css"))
return
with open(os.path.join(SCRIPTDIR, foldericon), "r", encoding="utf-8") as f:
logger.info("Reading foldericon svg")
svg = f.read()
if "svg.j2" in foldericon:
logger.info("foldericon in theme file is a jinja2 template")
colorscheme = extract_colorscheme(_args.theme_path)
for color_key, color_value in colorscheme.items():
svg = svg.replace(f"{{{{ {color_key} }}}}", color_value)
logger.info("replaced colors in svg")
svg = urllib.parse.quote(svg)
with open(os.path.join(static_dir, "theme.css"), "x", encoding="utf-8") as f:
logger.info("writing theme file")
f.write(themehead + '\n.foldericon {\n content: url("data:image/svg+xml,' + svg + '");\n}\n' + themetail)
theme = os.path.splitext(os.path.abspath(_args.theme_path))[0]
darktheme_path = f"{theme}-dark.css"
if os.path.exists(darktheme_path):
handle_theme_icon(darktheme_path, os.path.join(static_dir, "theme-dark.css"))
darktheme = True
handle_theme_icon(_args.theme_path, os.path.join(static_dir, "theme.css"))
logger.info("minifying javascript")
with open(os.path.join(SCRIPTDIR, "templates", "functionality.js"), "r", encoding="utf-8") as js_file:
with open(os.path.join(static_dir, "functionality.min.js"), "w+", encoding="utf-8") as min_file:
min_file.write(jsmin(js_file.read()))
return darktheme
def generate_thumbnail(arguments: tuple[str, str, str]) -> None:
@@ -150,7 +172,7 @@ def generate_thumbnail(arguments: tuple[str, str, str]) -> None:
imgrgb = imgfile.convert("RGB")
img = ImageOps.exif_transpose(imgrgb)
img.thumbnail((512, 512))
img.save(path, "JPEG", quality=75, optimize=True, mode="RGB")
img.save(path, "JPEG", quality=50, optimize=True, mode="RGB", subsampling=2)
except OSError:
logger.error("Failed to generate thumbnail for %s", item, extra={"path": image})
print(f"Failed to generate thumbnail for {image}")
@@ -163,13 +185,14 @@ def main(args) -> None:
"""
Main function to process images and generate a static image hosting website.
"""
thumbnails: list[tuple[str, str, str, bool]] = []
thumbnails: list[tuple[str, str, str]] = []
args, raw = init_globals(args, RAW_EXTENSIONS)
thumbdir = os.path.join(args.root_directory, ".thumbnails")
try:
Path(lock_file).touch()
logger.info("starting builder", extra={"version": VERSION})
Path(LOCKFILE).touch()
logger.info("starting builder", extra={"version": VERSION, "arguments": args})
logger.info("getting logo from sorogon.eu")
req = urllib.request.Request("https://files.sorogon.eu/logo.svg")
@@ -190,12 +213,12 @@ def main(args) -> None:
logger.warning("reread metadata flag is set to true, all image metadata will be reread")
if args.regenerate_thumbnails:
logger.warning("regenerate thumbnails flag is set to true, all thumbnails will be regenerated")
if os.path.exists(os.path.join(args.root_directory, ".thumbnails")):
if os.path.exists(thumbdir):
logger.info("removing old thumbnails folder")
shutil.rmtree(os.path.join(args.root_directory, ".thumbnails"))
os.makedirs(os.path.join(args.root_directory, ".thumbnails"), exist_ok=True)
shutil.rmtree(thumbdir)
os.makedirs(thumbdir, exist_ok=True)
copy_static_files(args)
args.darktheme = copy_static_files(args)
icons(args)
if args.generate_webmanifest:
@@ -224,8 +247,11 @@ def main(args) -> None:
dynamic_ncols=True,
):
pass
except Exception as e:
logger.critical("an unhandled exception occurred: %s", str(e), exc_info=True)
print(f"An unhandled exception occurred: {str(e)}")
finally:
os.remove(lock_file)
os.remove(LOCKFILE)
logger.info("finished builder", extra={"version": VERSION})

3
example.config Normal file
View File

@@ -0,0 +1,3 @@
root-directory = /path/to/webroot
site-title = Website Title
web-root-url = https://www.example.com

View File

@@ -66,12 +66,15 @@ body {
padding: 0 2px;
}
figure {
margin: 0;
.centerload {
margin-top: 100px;
display: flex;
justify-content: center;
align-items: center;
}
.licensefile {
padding: 30px;
figure {
margin: 0;
}
.caption {
@@ -86,7 +89,6 @@ figure {
position: absolute;
bottom: 0;
right: 0;
font-size: xx-small;
padding: 6px;
}
@@ -95,7 +97,8 @@ figure {
bottom: 0;
width: 100%;
padding: 6px;
min-height: calc(6.75pt + 12px);
height: calc(9.75pt + 12px);
font-size: small;
}
.footer a {
@@ -103,7 +106,7 @@ figure {
}
.footer a img {
height: 22px !important;
height: 9.75pt !important;
margin-left: 3px;
vertical-align: text-bottom;
}
@@ -133,16 +136,19 @@ figure {
.navbar .navleft {
float: left;
height: 100%;
}
.navbar .navcenter {
position: absolute;
left: 50%;
transform: translateX(-50%);
height: 100%;
}
.navbar .navright {
float: right
float: right;
height: 100%;
}
.navbar li .header {
@@ -157,10 +163,31 @@ figure {
cursor: pointer;
}
.tooltip .tooltiptext {
display: none;
.tooltip a {
cursor: pointer;
}
.tagtoggle {
cursor: pointer;
float: right;
padding: 12px;
}
.tagflex {
display: flex;
}
.tag {
flex: 1;
}
input {
pointer-events: none;
}
.tooltiptext {
cursor: default;
width: max-content;
position: absolute;
z-index: 100;
opacity: 0;
@@ -172,33 +199,66 @@ figure {
.tooltip .infotext {
padding: 12px;
width: max-content;
}
.tooltip .tagdropdown {
.tooltiptext.tagdropdown {
width: max-content;
right: 0;
padding: 0;
margin: 0;
overflow: hidden;
transition: max-height 0.3s ease, opacity 0.3s ease;
max-height: 0;
opacity: 0;
}
.tooltip:hover .tooltiptext {
.tooltiptext.tagdropdown.show {
max-height: 80vh;
overflow-y: scroll;
opacity: 1;
}
.tooltip:hover .infotext {
display: block;
opacity: 1;
}
.tooltip:active .tooltiptext {
.tooltip:active .infotext {
display: block;
opacity: 1;
}
.tooltip .tooltiptext .tagentry {
.tagentryparent {
width: 100%;
overflow: hidden;
transition: max-height 0.3s ease, opacity 0.3s ease;
max-height: 0;
opacity: 0;
}
.tagentryparent.show {
max-height: 80vh;
opacity: 1;
overflow-y: scroll;
}
.tagentry {
list-style: none;
width: 100%;
cursor: pointer;
margin: 0;
padding: 0;
display: block;
}
.tooltip .tooltiptext ol {
margin-left: 0;
padding-left: 0.5em;
}
.tooltip .tooltiptext .tagentry label {
cursor: pointer;
width: 100%;
height: 100%;
padding: 12px;
display: block;
@@ -242,6 +302,59 @@ figure {
border-style: none;
}
.darkmodeswitch {
font-size: smaller;
height: 100%;
}
#dark-mode-switch {
display: flex;
align-items: center;
justify-content: center;
border: none;
cursor: pointer;
position: relative;
}
#dark-mode-switch .checkbox {
position: absolute;
width: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
z-index: 2;
}
#dark-mode-switch .knobs {
display: flex;
align-items: center;
justify-content: center;
column-gap: 1em;
position: relative;
width: 100%;
}
#dark-mode-switch .light,
#dark-mode-switch .dark {
text-align: center;
}
#dark-mode-switch .slider {
position: absolute;
width: calc(2em - 2px);
height: calc(2em - 2px);
border: 1px solid currentColor;
border-radius: 3px;
top: 50%;
transform: translate(-50%, -50%);
transition: left 0.25s ease, transform 0.25s ease;
left: calc(50% - 1em + 1px);
}
#dark-mode-switch .checkbox:checked+.knobs .slider {
left: calc(50% + 1em - 1px);
}
@media screen and (max-width: 1000px) {
.column {
-ms-flex: 25%;
@@ -249,6 +362,14 @@ figure {
max-width: 25%;
}
.footer {
font-size: small;
}
.footer a img {
height: 9.75pt !important;
}
.folders figure {
width: 160px;
}
@@ -282,6 +403,14 @@ figure {
max-width: 50%;
}
.footer {
font-size: x-small;
}
.footer a img {
height: 7.5pt !important;
}
.folders figure {
width: 140px;
}
@@ -323,6 +452,14 @@ figure {
max-width: 100%;
}
.footer {
font-size: xx-small;
}
.footer a img {
height: 6.75pt !important;
}
.folders figure {
width: 120px;
}

View File

@@ -35,8 +35,7 @@
transition: opacity 0.2s;
-webkit-box-shadow: none;
box-shadow: none; }
.pswp__button:focus,
.pswp__button:hover {
.pswp__button:focus, .pswp__button:hover {
opacity: 1; }
.pswp__button:active {
outline: none;
@@ -272,14 +271,14 @@ a.pswp__share--download:hover {
color: #BBB; }
.pswp__caption__center {
text-align: center;
text-align: left;
max-width: 420px;
margin: 0 auto;
font-size: 13px;
padding: 10px;
line-height: 20px;
color: #CCC;
font-weight: bold; }
text-align: center; }
.pswp__caption--empty {
display: none; }

View File

@@ -1,131 +1 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="264"
height="88"
viewBox="0 0 264 88"
version="1.1"
id="svg12"
sodipodi:docname="default-skin.svg"
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/">
<defs
id="defs12" />
<sodipodi:namedview
id="namedview12"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="true"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="true"
inkscape:deskcolor="#d1d1d1"
showborder="true"
borderlayer="true"
inkscape:zoom="3.0855569"
inkscape:cx="95.606729"
inkscape:cy="65.142212"
inkscape:window-width="1896"
inkscape:window-height="1016"
inkscape:window-x="1932"
inkscape:window-y="52"
inkscape:window-maximized="1"
inkscape:current-layer="g11" />
<title
id="title1">default-skin 2</title>
<g
fill="none"
fill-rule="evenodd"
id="g12">
<g
id="g11">
<g
fill="#fff"
id="g1">
<path
d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z"
id="path1" />
</g>
<g
fill="#fff"
id="g2">
<path
d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"
id="path2" />
</g>
<path
d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z"
fill="#fff"
id="path3" />
<path
d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z"
fill="#fff"
id="path4" />
<path
d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z"
fill="#fff"
id="path5" />
<g
id="g6">
<path
id="Rectangle-11"
fill="#fff"
d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z" />
<path
d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z"
id="Oval-1"
stroke="#fff"
stroke-width="1.5" />
<path
fill="#fff"
d="M150 21h5v1h-5z"
id="path6" />
</g>
<g
id="g10">
<path
d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z"
fill="#fff"
id="path7" />
<path
d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z"
stroke="#fff"
stroke-width="1.5"
id="path8" />
<path
fill="#fff"
d="M106 21h5v1h-5z"
id="path9" />
<path
fill="#fff"
d="M109.043 19.008l-.085 5-1-.017.085-5z"
id="path10" />
</g>
<g
id="g14">
<path
id="Shape"
style="fill:#ffffff"
d="M 63.708984 57.572266 L 63.708984 66.917969 L 62.773438 66.917969 L 59.173828 66.917969 L 65.089844 72.916016 L 71.173828 66.917969 L 67.40625 66.917969 L 66.640625 66.917969 L 66.640625 57.572266 L 63.708984 57.572266 z " />
<path
id="rect13"
style="fill:#ffffff;stroke:#ffffff;stroke-width:0.649;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 56.24391,72.078606 v 3.51729 h 17.859837 v -3.51729 h -1.210961 v 2.324677 H 57.456705 v -2.324677 z" />
</g>
</g>
</g>
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:title>default-skin 2</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
</svg>
<svg width="264" height="88" viewBox="0 0 264 88" xmlns="http://www.w3.org/2000/svg"><title>default-skin 2</title><g fill="none" fill-rule="evenodd"><g><path d="M67.002 59.5v3.768c-6.307.84-9.184 5.75-10.002 9.732 2.22-2.83 5.564-5.098 10.002-5.098V71.5L73 65.585 67.002 59.5z" id="Shape" fill="#fff"/><g fill="#fff"><path d="M13 29v-5h2v3h3v2h-5zM13 15h5v2h-3v3h-2v-5zM31 15v5h-2v-3h-3v-2h5zM31 29h-5v-2h3v-3h2v5z" id="Shape"/></g><g fill="#fff"><path d="M62 24v5h-2v-3h-3v-2h5zM62 20h-5v-2h3v-3h2v5zM70 20v-5h2v3h3v2h-5zM70 24h5v2h-3v3h-2v-5z"/></g><path d="M20.586 66l-5.656-5.656 1.414-1.414L22 64.586l5.656-5.656 1.414 1.414L23.414 66l5.656 5.656-1.414 1.414L22 67.414l-5.656 5.656-1.414-1.414L20.586 66z" fill="#fff"/><path d="M111.785 65.03L110 63.5l3-3.5h-10v-2h10l-3-3.5 1.785-1.468L117 59l-5.215 6.03z" fill="#fff"/><path d="M152.215 65.03L154 63.5l-3-3.5h10v-2h-10l3-3.5-1.785-1.468L147 59l5.215 6.03z" fill="#fff"/><g><path id="Rectangle-11" fill="#fff" d="M160.957 28.543l-3.25-3.25-1.413 1.414 3.25 3.25z"/><path d="M152.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" id="Oval-1" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M150 21h5v1h-5z"/></g><g><path d="M116.957 28.543l-1.414 1.414-3.25-3.25 1.414-1.414 3.25 3.25z" fill="#fff"/><path d="M108.5 27c3.038 0 5.5-2.462 5.5-5.5s-2.462-5.5-5.5-5.5-5.5 2.462-5.5 5.5 2.462 5.5 5.5 5.5z" stroke="#fff" stroke-width="1.5"/><path fill="#fff" d="M106 21h5v1h-5z"/><path fill="#fff" d="M109.043 19.008l-.085 5-1-.017.085-5z"/></g></g></g></svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

File diff suppressed because one or more lines are too long

View File

@@ -15,21 +15,14 @@
touch-action: none;
z-index: 1500;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
/* create separate layer, to avoid paint on window.onscroll in webkit/blink */
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
outline: none;
}
.pswp * {
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.pswp img {
max-width: none;
}
outline: none; }
.pswp * {
-webkit-box-sizing: border-box;
box-sizing: border-box; }
.pswp img {
max-width: none; }
/* style is added when JS option showHideOpacity is set to true */
.pswp--animate_opacity {
@@ -38,33 +31,28 @@
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
}
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp--open {
display: block;
}
display: block; }
.pswp--zoom-allowed .pswp__img {
/* autoprefixer: off */
cursor: -webkit-zoom-in;
cursor: -moz-zoom-in;
cursor: zoom-in;
}
cursor: zoom-in; }
.pswp--zoomed-in .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grab;
cursor: -moz-grab;
cursor: grab;
}
cursor: grab; }
.pswp--dragging .pswp__img {
/* autoprefixer: off */
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
cursor: grabbing;
}
cursor: grabbing; }
/*
Background is added as a separate element.
@@ -78,10 +66,10 @@
height: 100%;
background: #000;
opacity: 0;
-webkit-transform: translateZ(0);
transform: translateZ(0);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
will-change: opacity;
}
will-change: opacity; }
.pswp__scroll-wrap {
position: absolute;
@@ -89,8 +77,7 @@
top: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
overflow: hidden; }
.pswp__container,
.pswp__zoom-wrap {
@@ -100,8 +87,7 @@
left: 0;
right: 0;
top: 0;
bottom: 0;
}
bottom: 0; }
/* Prevent selection and tap highlights */
.pswp__container,
@@ -109,10 +95,9 @@
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
user-select: none;
-webkit-tap-highlight-color: transparent;
-webkit-touch-callout: none;
}
-webkit-touch-callout: none; }
.pswp__zoom-wrap {
position: absolute;
@@ -122,27 +107,22 @@
transform-origin: left top;
/* for open/close transition */
-webkit-transition: -webkit-transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1);
}
transition: transform 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp__bg {
will-change: opacity;
/* for open/close transition */
-webkit-transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1);
}
transition: opacity 333ms cubic-bezier(0.4, 0, 0.22, 1); }
.pswp--animated-in .pswp__bg,
.pswp--animated-in .pswp__zoom-wrap {
-webkit-transition: none;
transition: none;
}
transition: none; }
.pswp__container,
.pswp__zoom-wrap {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
-webkit-backface-visibility: hidden; }
.pswp__item {
position: absolute;
@@ -150,40 +130,34 @@
right: 0;
top: 0;
bottom: 0;
overflow: hidden;
}
overflow: hidden; }
.pswp__img {
position: absolute;
width: auto;
height: auto;
top: 0;
left: 0;
}
left: 0; }
/*
stretched thumbnail or div placeholder element (see below)
style is added to avoid flickering in webkit/blink when layers overlap
*/
.pswp__img--placeholder {
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
}
-webkit-backface-visibility: hidden; }
/*
div element that matches size of large image
large image loads on top of it
*/
.pswp__img--placeholder--blank {
background: #222;
}
background: #222; }
.pswp--ie .pswp__img {
width: 100% !important;
height: auto !important;
left: 0;
top: 0;
}
top: 0; }
/*
Error message appears when image is not loaded
@@ -198,10 +172,8 @@
font-size: 14px;
line-height: 16px;
margin-top: -8px;
color: #CCC;
}
color: #CCC; }
.pswp__error-msg a {
color: #CCC;
text-decoration: underline;
}
text-decoration: underline; }

3788
files/pswp/photoswipe.js Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -1,268 +0,0 @@
import os
import re
import sys
import shutil
import base64
import fileinput
import urllib.parse
import urllib.request
from typing import List
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from modules.logger import consolelogger as logger
from modules.css_color import extract_colorscheme
def replace_all(file, search_exp, replace_exp):
for line in fileinput.input(file, inplace=1):
line = re.sub(search_exp, replace_exp, line)
sys.stdout.write(line)
def take_screenshot(html_file_path: str, css_file: str, output_file: str, driver: webdriver.Chrome) -> None:
"""
Takes a screenshot of the given HTML file with the specified CSS applied.
Args:
html_file_path (str): Path to the HTML file or URL.
css_file (str): Path to the CSS file to be applied.
output_file (str): Path where the screenshot will be saved.
driver (webdriver.Chrome): The Chrome WebDriver instance.
"""
logger.info("taking screenshot for %s", css_file)
try:
# Open the HTML file or URL
if html_file_path.startswith(("http://", "https://")):
logger.info("opening URL: %s", html_file_path)
driver.get(html_file_path)
else:
logger.info("opening file: %s", html_file_path)
driver.get(f"file://{os.path.abspath(html_file_path)}")
# Remove current theme.css
remove_css_script = """
var links = document.querySelectorAll("link[rel='stylesheet']");
links.forEach(link => {
if (link.href.includes('theme.css')) {
link.parentNode.removeChild(link);
}
});
"""
logger.info("removing current theme.css")
driver.execute_script(remove_css_script)
with open(css_file, "r", encoding="utf-8") as f:
logger.info("reading CSS file: %s", css_file)
css_content = f.read()
# Extract folder icon content
css_parts = css_content.split(".foldericon {")
css_head = css_parts[0]
css_tail = css_parts[1].split("}", maxsplit=1)[1]
folder_icon_content = css_parts[1].split("}", maxsplit=1)[0].strip()
folder_icon_content = re.sub(r"/\*.*\*/", "", folder_icon_content)
for match in re.finditer(r"content: (.*);", folder_icon_content):
logger.info("found foldericon", extra={"foldericon": folder_icon_content})
folder_icon_content = match.group(1).replace('"', "")
break
if "url" not in folder_icon_content:
logger.info("Reading foldericon svg")
with open(folder_icon_content, "r", encoding="utf-8") as f:
svg = f.read()
if "svg.j2" in folder_icon_content:
logger.info("foldericon in theme file is a jinja2 template")
colorscheme = extract_colorscheme(css_file)
for color_key, color_value in colorscheme.items():
svg = svg.replace(f"{{{{ {color_key} }}}}", color_value)
logger.info("replaced colors in svg")
svg = urllib.parse.quote(svg)
css_content = f'{css_head}\n.foldericon {{\n content: url("data:image/svg+xml,{svg}");\n}}\n{css_tail}'
# Encode CSS content as Base64
logger.info("encoding css content as base64")
encoded_css = base64.b64encode(css_content.encode("utf-8")).decode("utf-8")
# Inject CSS into HTML using JavaScript
apply_css_script = f"""
var style = document.createElement('style');
style.innerHTML = atob('{encoded_css}');
document.head.appendChild(style);
"""
logger.info("injecting CSS into HTML")
driver.execute_script(apply_css_script)
# Wait for a while to ensure CSS is applied
# time.sleep(1)
# Move mouse to info
logger.info("moving mouse to info")
hoverable = driver.find_element(By.CLASS_NAME, "tooltip")
webdriver.ActionChains(driver).move_to_element(hoverable).perform()
# Capture screenshot
logger.info("taking screenshot")
driver.save_screenshot(output_file)
logger.info("screenshot saved to %s", output_file)
except Exception as e:
logger.error("failed to take screenshot for %s: %s", css_file, e)
def create_preview(html_file_path: str, css_file: str, previews_folder: str):
logger.info("creating preview for %s", css_file)
out_file = os.path.basename(css_file).removesuffix(".css") + ".html"
urllib.request.urlretrieve(html_file_path, os.path.join(previews_folder, out_file))
basename = os.path.basename(css_file)
path = css_file.removesuffix(basename)
replace_all(
os.path.join(previews_folder, out_file),
r'^\s*?<link rel="stylesheet" href=".*theme.css">\s*?$',
f' <link rel="stylesheet" href="file://{path}previews/{basename}">',
)
with open(css_file, "r", encoding="utf-8") as f:
theme = f.read()
split = theme.split(".foldericon {")
split2 = split[1].split("}", maxsplit=1)
themehead = split[0]
themetail = split2[1]
foldericon = split2[0].strip()
foldericon = re.sub(r"/\*.*\*/", "", foldericon)
for match in re.finditer(r"content: (.*);", foldericon):
foldericon = match[1]
foldericon = foldericon.replace('"', "")
break
if "url" in foldericon:
logger.info("foldericon in theme file, using it")
shutil.copyfile(css_file, os.path.join(path, "previews", basename))
return
with open(os.path.join(path, foldericon.removeprefix("themes/")), "r", encoding="utf-8") as f:
logger.info("Reading foldericon svg")
svg = f.read()
if "svg.j2" in foldericon:
logger.info("foldericon in theme file is a jinja2 template")
colorscheme = extract_colorscheme(css_file)
for color_key, color_value in colorscheme.items():
svg = svg.replace(f"{{{{ {color_key} }}}}", color_value)
logger.info("replaced colors in svg")
svg = urllib.parse.quote(svg)
if os.path.exists(os.path.join(path, "previews", basename)):
os.remove(os.path.join(path, "previews", basename))
with open(os.path.join(path, "previews", basename), "x", encoding="utf-8") as f:
logger.info("writing theme file")
f.write(themehead + '\n.foldericon {\n content: url("data:image/svg+xml,' + svg + '");\n}\n' + themetail)
logger.info("preview created for %s", css_file)
def write_readme(directory_path: str, themes: List[str]) -> None:
"""
Writes the README file with previews of included themes.
Args:
directory_path (str): Path to the folder containing the themes and README.md.
themes (List[str]): List of theme names.
"""
readme_path = os.path.join(directory_path, "README.md")
try:
with open(readme_path, "r", encoding="utf-8") as f:
logger.info("reading README.md", extra={"file": readme_path})
readme = f.read()
readme_head = readme.split("## Previews of included themes")[0]
readme_head += "## Previews of included themes\n"
readme_head += "".join([f"\n### {theme}\n\n![{theme}](screenshots/{theme}.png)\n" for theme in themes])
with open(readme_path, "w", encoding="utf-8") as f:
logger.info("writing README.md", extra={"file": readme_path})
f.write(readme_head)
logger.info("README.md updated with previews of included themes.")
except FileNotFoundError:
logger.error("README.md not found in %s", directory_path)
except Exception as e:
logger.error("failed to write README.md: %s", e)
def write_index(directory_path: str, themes: List[str]) -> None:
with open(os.path.join(directory_path, "index.html"), "w", encoding="utf-8") as f:
f.write(
"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Themes</title>
</head>
<body>"""
)
for theme in themes:
f.write(f'<a href="previews/{theme}.html">{theme}</a><br>\n')
f.write("</body></html>")
def main(directory_path: str, html_file_path: str) -> None:
"""
Main function to take screenshots for each CSS file in the folder and update the README.md.
Args:
directory_path (str): Path to the folder containing CSS files.
html_file_path (str): Path to the HTML file or URL for rendering.
"""
if not os.path.exists(directory_path):
logger.error('Error: Folder path "%s" does not exist.', directory_path)
return
# Setup Chrome options
chrome_options = Options()
chrome_options.add_argument("--headless") # Run in headless mode, no GUI
chrome_options.add_argument("--window-size=1920,1080") # Set window size to at least 1920x1080
# Initialize Chrome WebDriver
chromedriver_path = "/usr/bin/chromedriver"
service = Service(chromedriver_path)
logger.info("Using chromedriver at %s", chromedriver_path, extra={"chrome_options": chrome_options})
driver = webdriver.Chrome(service=service, options=chrome_options)
try:
themes = []
# Iterate over all files in the folder
for filename in sorted(os.listdir(directory_path)):
if filename.endswith(".css"):
theme_name = os.path.splitext(filename)[0]
themes.append(theme_name)
css_file = os.path.join(directory_path, filename)
output_file = os.path.join(directory_path, "screenshots", f"{theme_name}.png")
previews_folder = os.path.join(directory_path, "previews")
# Create screenshots folder if it doesn't exist
os.makedirs(os.path.dirname(output_file), exist_ok=True)
os.makedirs(previews_folder, exist_ok=True)
# Take screenshot for this CSS file
take_screenshot(html_file_path, css_file, output_file, driver)
create_preview(html_file_path, css_file, previews_folder)
# Write the README file with the new previews
write_readme(directory_path, themes)
write_index(directory_path, themes)
finally:
logger.info("closing chrome webdriver")
driver.quit()
if __name__ == "__main__":
if len(sys.argv) != 3:
logger.error("Usage: python script_name.py directory_path html_file_path")
else:
dir_path = sys.argv[1]
html_path = sys.argv[2]
logger.info("Starting script", extra={"directory_path": dir_path, "html_file_path": html_path})
main(dir_path, html_path)
logger.info("Done!", extra={"directory_path": dir_path})

192
help.svg
View File

@@ -1,4 +1,4 @@
<svg class="rich-terminal" viewBox="0 0 1482 855.1999999999999" xmlns="http://www.w3.org/2000/svg">
<svg class="rich-terminal" viewBox="0 0 1482 1074.8" xmlns="http://www.w3.org/2000/svg">
<!-- Generated with Rich https://www.textualize.io -->
<style>
@@ -19,171 +19,207 @@
font-weight: 700;
}
.terminal-1308707172-matrix {
.terminal-1915255058-matrix {
font-family: Fira Code, monospace;
font-size: 20px;
line-height: 24.4px;
font-variant-east-asian: full-width;
}
.terminal-1308707172-title {
.terminal-1915255058-title {
font-size: 18px;
font-weight: bold;
font-family: arial;
}
.terminal-1308707172-r1 { fill: #ff8700 }
.terminal-1308707172-r2 { fill: #c5c8c6 }
.terminal-1308707172-r3 { fill: #808080 }
.terminal-1308707172-r4 { fill: #68a0b3 }
.terminal-1308707172-r5 { fill: #00af87 }
.terminal-1915255058-r1 { fill: #ff8700 }
.terminal-1915255058-r2 { fill: #c5c8c6 }
.terminal-1915255058-r3 { fill: #808080 }
.terminal-1915255058-r4 { fill: #68a0b3 }
.terminal-1915255058-r5 { fill: #00af87 }
</style>
<defs>
<clipPath id="terminal-1308707172-clip-terminal">
<rect x="0" y="0" width="1463.0" height="804.1999999999999" />
<clipPath id="terminal-1915255058-clip-terminal">
<rect x="0" y="0" width="1463.0" height="1023.8" />
</clipPath>
<clipPath id="terminal-1308707172-line-0">
<clipPath id="terminal-1915255058-line-0">
<rect x="0" y="1.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-1">
<clipPath id="terminal-1915255058-line-1">
<rect x="0" y="25.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-2">
<clipPath id="terminal-1915255058-line-2">
<rect x="0" y="50.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-3">
<clipPath id="terminal-1915255058-line-3">
<rect x="0" y="74.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-4">
<clipPath id="terminal-1915255058-line-4">
<rect x="0" y="99.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-5">
<clipPath id="terminal-1915255058-line-5">
<rect x="0" y="123.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-6">
<clipPath id="terminal-1915255058-line-6">
<rect x="0" y="147.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-7">
<clipPath id="terminal-1915255058-line-7">
<rect x="0" y="172.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-8">
<clipPath id="terminal-1915255058-line-8">
<rect x="0" y="196.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-9">
<clipPath id="terminal-1915255058-line-9">
<rect x="0" y="221.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-10">
<clipPath id="terminal-1915255058-line-10">
<rect x="0" y="245.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-11">
<clipPath id="terminal-1915255058-line-11">
<rect x="0" y="269.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-12">
<clipPath id="terminal-1915255058-line-12">
<rect x="0" y="294.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-13">
<clipPath id="terminal-1915255058-line-13">
<rect x="0" y="318.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-14">
<clipPath id="terminal-1915255058-line-14">
<rect x="0" y="343.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-15">
<clipPath id="terminal-1915255058-line-15">
<rect x="0" y="367.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-16">
<clipPath id="terminal-1915255058-line-16">
<rect x="0" y="391.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-17">
<clipPath id="terminal-1915255058-line-17">
<rect x="0" y="416.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-18">
<clipPath id="terminal-1915255058-line-18">
<rect x="0" y="440.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-19">
<clipPath id="terminal-1915255058-line-19">
<rect x="0" y="465.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-20">
<clipPath id="terminal-1915255058-line-20">
<rect x="0" y="489.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-21">
<clipPath id="terminal-1915255058-line-21">
<rect x="0" y="513.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-22">
<clipPath id="terminal-1915255058-line-22">
<rect x="0" y="538.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-23">
<clipPath id="terminal-1915255058-line-23">
<rect x="0" y="562.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-24">
<clipPath id="terminal-1915255058-line-24">
<rect x="0" y="587.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-25">
<clipPath id="terminal-1915255058-line-25">
<rect x="0" y="611.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-26">
<clipPath id="terminal-1915255058-line-26">
<rect x="0" y="635.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-27">
<clipPath id="terminal-1915255058-line-27">
<rect x="0" y="660.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-28">
<clipPath id="terminal-1915255058-line-28">
<rect x="0" y="684.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-29">
<clipPath id="terminal-1915255058-line-29">
<rect x="0" y="709.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-30">
<clipPath id="terminal-1915255058-line-30">
<rect x="0" y="733.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1308707172-line-31">
<clipPath id="terminal-1915255058-line-31">
<rect x="0" y="757.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-32">
<rect x="0" y="782.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-33">
<rect x="0" y="806.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-34">
<rect x="0" y="831.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-35">
<rect x="0" y="855.5" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-36">
<rect x="0" y="879.9" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-37">
<rect x="0" y="904.3" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-38">
<rect x="0" y="928.7" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-39">
<rect x="0" y="953.1" width="1464" height="24.65"/>
</clipPath>
<clipPath id="terminal-1915255058-line-40">
<rect x="0" y="977.5" width="1464" height="24.65"/>
</clipPath>
</defs>
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="853.2" rx="8"/>
<rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" x="1" y="1" width="1480" height="1072.8" rx="8"/>
<g transform="translate(26,22)">
<circle cx="0" cy="0" r="7" fill="#ff5f57"/>
<circle cx="22" cy="0" r="7" fill="#febc2e"/>
<circle cx="44" cy="0" r="7" fill="#28c840"/>
</g>
<g transform="translate(9, 41)" clip-path="url(#terminal-1308707172-clip-terminal)">
<g transform="translate(9, 41)" clip-path="url(#terminal-1915255058-clip-terminal)">
<g class="terminal-1308707172-matrix">
<text class="terminal-1308707172-r1" x="0" y="20" textLength="73.2" clip-path="url(#terminal-1308707172-line-0)">Usage:</text><text class="terminal-1308707172-r3" x="85.4" y="20" textLength="122" clip-path="url(#terminal-1308707172-line-0)">builder.py</text><text class="terminal-1308707172-r2" x="207.4" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">&#160;[</text><text class="terminal-1308707172-r4" x="231.8" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-h</text><text class="terminal-1308707172-r2" x="256.2" y="20" textLength="36.6" clip-path="url(#terminal-1308707172-line-0)">]&#160;[</text><text class="terminal-1308707172-r4" x="292.8" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-a</text><text class="terminal-1308707172-r5" x="329.4" y="20" textLength="73.2" clip-path="url(#terminal-1308707172-line-0)">AUTHOR</text><text class="terminal-1308707172-r2" x="402.6" y="20" textLength="36.6" clip-path="url(#terminal-1308707172-line-0)">]&#160;[</text><text class="terminal-1308707172-r4" x="439.2" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-e</text><text class="terminal-1308707172-r5" x="475.8" y="20" textLength="109.8" clip-path="url(#terminal-1308707172-line-0)">EXTENSION</text><text class="terminal-1308707172-r2" x="585.6" y="20" textLength="36.6" clip-path="url(#terminal-1308707172-line-0)">]&#160;[</text><text class="terminal-1308707172-r4" x="622.2" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-l</text><text class="terminal-1308707172-r5" x="658.8" y="20" textLength="85.4" clip-path="url(#terminal-1308707172-line-0)">LICENSE</text><text class="terminal-1308707172-r2" x="744.2" y="20" textLength="36.6" clip-path="url(#terminal-1308707172-line-0)">]&#160;[</text><text class="terminal-1308707172-r4" x="780.8" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-m</text><text class="terminal-1308707172-r2" x="805.2" y="20" textLength="36.6" clip-path="url(#terminal-1308707172-line-0)">]&#160;[</text><text class="terminal-1308707172-r4" x="841.8" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-n</text><text class="terminal-1308707172-r2" x="866.2" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">]&#160;</text><text class="terminal-1308707172-r4" x="890.6" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-p</text><text class="terminal-1308707172-r5" x="927.2" y="20" textLength="48.8" clip-path="url(#terminal-1308707172-line-0)">ROOT</text><text class="terminal-1308707172-r4" x="988.2" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-t</text><text class="terminal-1308707172-r5" x="1024.8" y="20" textLength="61" clip-path="url(#terminal-1308707172-line-0)">TITLE</text><text class="terminal-1308707172-r4" x="1098" y="20" textLength="24.4" clip-path="url(#terminal-1308707172-line-0)">-w</text><text class="terminal-1308707172-r5" x="1134.6" y="20" textLength="36.6" clip-path="url(#terminal-1308707172-line-0)">URL</text><text class="terminal-1308707172-r2" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-1308707172-line-0)">
</text><text class="terminal-1308707172-r2" x="0" y="44.4" textLength="231.8" clip-path="url(#terminal-1308707172-line-1)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[</text><text class="terminal-1308707172-r4" x="231.8" y="44.4" textLength="195.2" clip-path="url(#terminal-1308707172-line-1)">--exclude-folder</text><text class="terminal-1308707172-r5" x="439.2" y="44.4" textLength="73.2" clip-path="url(#terminal-1308707172-line-1)">FOLDER</text><text class="terminal-1308707172-r2" x="512.4" y="44.4" textLength="36.6" clip-path="url(#terminal-1308707172-line-1)">]&#160;[</text><text class="terminal-1308707172-r4" x="549" y="44.4" textLength="244" clip-path="url(#terminal-1308707172-line-1)">--ignore-other-files</text><text class="terminal-1308707172-r2" x="793" y="44.4" textLength="36.6" clip-path="url(#terminal-1308707172-line-1)">]&#160;[</text><text class="terminal-1308707172-r4" x="829.6" y="44.4" textLength="280.6" clip-path="url(#terminal-1308707172-line-1)">--regenerate-thumbnails</text><text class="terminal-1308707172-r2" x="1110.2" y="44.4" textLength="36.6" clip-path="url(#terminal-1308707172-line-1)">]&#160;[</text><text class="terminal-1308707172-r4" x="1146.8" y="44.4" textLength="207.4" clip-path="url(#terminal-1308707172-line-1)">--reread-metadata</text><text class="terminal-1308707172-r2" x="1354.2" y="44.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-1)">]</text><text class="terminal-1308707172-r2" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-1)">
</text><text class="terminal-1308707172-r2" x="0" y="68.8" textLength="231.8" clip-path="url(#terminal-1308707172-line-2)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[</text><text class="terminal-1308707172-r4" x="231.8" y="68.8" textLength="170.8" clip-path="url(#terminal-1308707172-line-2)">--reverse-sort</text><text class="terminal-1308707172-r2" x="402.6" y="68.8" textLength="36.6" clip-path="url(#terminal-1308707172-line-2)">]&#160;[</text><text class="terminal-1308707172-r4" x="439.2" y="68.8" textLength="146.4" clip-path="url(#terminal-1308707172-line-2)">--theme-path</text><text class="terminal-1308707172-r5" x="597.8" y="68.8" textLength="48.8" clip-path="url(#terminal-1308707172-line-2)">PATH</text><text class="terminal-1308707172-r2" x="646.6" y="68.8" textLength="36.6" clip-path="url(#terminal-1308707172-line-2)">]&#160;[</text><text class="terminal-1308707172-r4" x="683.2" y="68.8" textLength="231.8" clip-path="url(#terminal-1308707172-line-2)">--use-fancy-folders</text><text class="terminal-1308707172-r2" x="915" y="68.8" textLength="36.6" clip-path="url(#terminal-1308707172-line-2)">]&#160;[</text><text class="terminal-1308707172-r4" x="951.6" y="68.8" textLength="109.8" clip-path="url(#terminal-1308707172-line-2)">--version</text><text class="terminal-1308707172-r2" x="1061.4" y="68.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-2)">]</text><text class="terminal-1308707172-r2" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-2)">
</text><text class="terminal-1308707172-r2" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-1308707172-line-3)">
</text><text class="terminal-1308707172-r2" x="0" y="117.6" textLength="671" clip-path="url(#terminal-1308707172-line-4)">Generate&#160;HTML&#160;files&#160;for&#160;a&#160;static&#160;image&#160;hosting&#160;website.</text><text class="terminal-1308707172-r2" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-1308707172-line-4)">
</text><text class="terminal-1308707172-r2" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-1308707172-line-5)">
</text><text class="terminal-1308707172-r1" x="0" y="166.4" textLength="97.6" clip-path="url(#terminal-1308707172-line-6)">Options:</text><text class="terminal-1308707172-r2" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-6)">
</text><text class="terminal-1308707172-r4" x="24.4" y="190.8" textLength="24.4" clip-path="url(#terminal-1308707172-line-7)">-h</text><text class="terminal-1308707172-r2" x="48.8" y="190.8" textLength="24.4" clip-path="url(#terminal-1308707172-line-7)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="190.8" textLength="73.2" clip-path="url(#terminal-1308707172-line-7)">--help</text><text class="terminal-1308707172-r2" x="292.8" y="190.8" textLength="378.2" clip-path="url(#terminal-1308707172-line-7)">show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1308707172-r2" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-7)">
</text><text class="terminal-1308707172-r4" x="24.4" y="215.2" textLength="24.4" clip-path="url(#terminal-1308707172-line-8)">-a</text><text class="terminal-1308707172-r2" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-1308707172-line-8)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="215.2" textLength="158.6" clip-path="url(#terminal-1308707172-line-8)">--author-name</text><text class="terminal-1308707172-r5" x="244" y="215.2" textLength="73.2" clip-path="url(#terminal-1308707172-line-8)">AUTHOR</text><text class="terminal-1308707172-r2" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-1308707172-line-8)">
</text><text class="terminal-1308707172-r2" x="292.8" y="239.6" textLength="402.6" clip-path="url(#terminal-1308707172-line-9)">Name&#160;of&#160;the&#160;author&#160;of&#160;the&#160;images.</text><text class="terminal-1308707172-r2" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-1308707172-line-9)">
</text><text class="terminal-1308707172-r4" x="24.4" y="264" textLength="24.4" clip-path="url(#terminal-1308707172-line-10)">-e</text><text class="terminal-1308707172-r2" x="48.8" y="264" textLength="24.4" clip-path="url(#terminal-1308707172-line-10)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="264" textLength="207.4" clip-path="url(#terminal-1308707172-line-10)">--file-extensions</text><text class="terminal-1308707172-r5" x="292.8" y="264" textLength="109.8" clip-path="url(#terminal-1308707172-line-10)">EXTENSION</text><text class="terminal-1308707172-r2" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-1308707172-line-10)">
</text><text class="terminal-1308707172-r2" x="292.8" y="288.4" textLength="744.2" clip-path="url(#terminal-1308707172-line-11)">File&#160;extensions&#160;to&#160;include&#160;(can&#160;be&#160;specified&#160;multiple&#160;times).</text><text class="terminal-1308707172-r2" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-11)">
</text><text class="terminal-1308707172-r4" x="24.4" y="312.8" textLength="24.4" clip-path="url(#terminal-1308707172-line-12)">-l</text><text class="terminal-1308707172-r2" x="48.8" y="312.8" textLength="24.4" clip-path="url(#terminal-1308707172-line-12)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="312.8" textLength="170.8" clip-path="url(#terminal-1308707172-line-12)">--license-type</text><text class="terminal-1308707172-r5" x="256.2" y="312.8" textLength="85.4" clip-path="url(#terminal-1308707172-line-12)">LICENSE</text><text class="terminal-1308707172-r2" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-12)">
</text><text class="terminal-1308707172-r2" x="292.8" y="337.2" textLength="488" clip-path="url(#terminal-1308707172-line-13)">Specify&#160;the&#160;license&#160;type&#160;for&#160;the&#160;images.</text><text class="terminal-1308707172-r2" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-1308707172-line-13)">
</text><text class="terminal-1308707172-r4" x="24.4" y="361.6" textLength="24.4" clip-path="url(#terminal-1308707172-line-14)">-m</text><text class="terminal-1308707172-r2" x="48.8" y="361.6" textLength="24.4" clip-path="url(#terminal-1308707172-line-14)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="361.6" textLength="170.8" clip-path="url(#terminal-1308707172-line-14)">--web-manifest</text><text class="terminal-1308707172-r2" x="292.8" y="361.6" textLength="353.8" clip-path="url(#terminal-1308707172-line-14)">Generate&#160;a&#160;web&#160;manifest&#160;file.</text><text class="terminal-1308707172-r2" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-1308707172-line-14)">
</text><text class="terminal-1308707172-r4" x="24.4" y="386" textLength="24.4" clip-path="url(#terminal-1308707172-line-15)">-n</text><text class="terminal-1308707172-r2" x="48.8" y="386" textLength="24.4" clip-path="url(#terminal-1308707172-line-15)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="386" textLength="268.4" clip-path="url(#terminal-1308707172-line-15)">--non-interactive-mode</text><text class="terminal-1308707172-r2" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-1308707172-line-15)">
</text><text class="terminal-1308707172-r2" x="292.8" y="410.4" textLength="646.6" clip-path="url(#terminal-1308707172-line-16)">Run&#160;in&#160;non-interactive&#160;mode,&#160;disabling&#160;progress&#160;bars.</text><text class="terminal-1308707172-r2" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-16)">
</text><text class="terminal-1308707172-r4" x="24.4" y="434.8" textLength="24.4" clip-path="url(#terminal-1308707172-line-17)">-p</text><text class="terminal-1308707172-r2" x="48.8" y="434.8" textLength="24.4" clip-path="url(#terminal-1308707172-line-17)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="434.8" textLength="195.2" clip-path="url(#terminal-1308707172-line-17)">--root-directory</text><text class="terminal-1308707172-r5" x="280.6" y="434.8" textLength="48.8" clip-path="url(#terminal-1308707172-line-17)">ROOT</text><text class="terminal-1308707172-r2" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-17)">
</text><text class="terminal-1308707172-r2" x="292.8" y="459.2" textLength="451.4" clip-path="url(#terminal-1308707172-line-18)">Root&#160;directory&#160;containing&#160;the&#160;images.</text><text class="terminal-1308707172-r2" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-1308707172-line-18)">
</text><text class="terminal-1308707172-r4" x="24.4" y="483.6" textLength="24.4" clip-path="url(#terminal-1308707172-line-19)">-t</text><text class="terminal-1308707172-r2" x="48.8" y="483.6" textLength="24.4" clip-path="url(#terminal-1308707172-line-19)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="483.6" textLength="146.4" clip-path="url(#terminal-1308707172-line-19)">--site-title</text><text class="terminal-1308707172-r5" x="231.8" y="483.6" textLength="61" clip-path="url(#terminal-1308707172-line-19)">TITLE</text><text class="terminal-1308707172-r2" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-1308707172-line-19)">
</text><text class="terminal-1308707172-r2" x="292.8" y="508" textLength="390.4" clip-path="url(#terminal-1308707172-line-20)">Title&#160;of&#160;the&#160;image&#160;hosting&#160;site.</text><text class="terminal-1308707172-r2" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-1308707172-line-20)">
</text><text class="terminal-1308707172-r4" x="24.4" y="532.4" textLength="24.4" clip-path="url(#terminal-1308707172-line-21)">-w</text><text class="terminal-1308707172-r2" x="48.8" y="532.4" textLength="24.4" clip-path="url(#terminal-1308707172-line-21)">,&#160;</text><text class="terminal-1308707172-r4" x="73.2" y="532.4" textLength="170.8" clip-path="url(#terminal-1308707172-line-21)">--web-root-url</text><text class="terminal-1308707172-r5" x="256.2" y="532.4" textLength="36.6" clip-path="url(#terminal-1308707172-line-21)">URL</text><text class="terminal-1308707172-r2" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-21)">
</text><text class="terminal-1308707172-r2" x="292.8" y="556.8" textLength="634.4" clip-path="url(#terminal-1308707172-line-22)">Base&#160;URL&#160;of&#160;the&#160;web&#160;root&#160;for&#160;the&#160;image&#160;hosting&#160;site.</text><text class="terminal-1308707172-r2" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-22)">
</text><text class="terminal-1308707172-r4" x="24.4" y="581.2" textLength="195.2" clip-path="url(#terminal-1308707172-line-23)">--exclude-folder</text><text class="terminal-1308707172-r5" x="231.8" y="581.2" textLength="73.2" clip-path="url(#terminal-1308707172-line-23)">FOLDER</text><text class="terminal-1308707172-r2" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-1308707172-line-23)">
</text><text class="terminal-1308707172-r2" x="292.8" y="605.6" textLength="1049.2" clip-path="url(#terminal-1308707172-line-24)">Folders&#160;to&#160;exclude&#160;from&#160;processing,&#160;globs&#160;supported&#160;(can&#160;be&#160;specified&#160;multiple&#160;times).</text><text class="terminal-1308707172-r2" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-1308707172-line-24)">
</text><text class="terminal-1308707172-r4" x="24.4" y="630" textLength="244" clip-path="url(#terminal-1308707172-line-25)">--ignore-other-files</text><text class="terminal-1308707172-r2" x="292.8" y="630" textLength="683.2" clip-path="url(#terminal-1308707172-line-25)">Ignore&#160;files&#160;that&#160;do&#160;not&#160;match&#160;the&#160;specified&#160;extensions.</text><text class="terminal-1308707172-r2" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-1308707172-line-25)">
</text><text class="terminal-1308707172-r4" x="24.4" y="654.4" textLength="280.6" clip-path="url(#terminal-1308707172-line-26)">--regenerate-thumbnails</text><text class="terminal-1308707172-r2" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-26)">
</text><text class="terminal-1308707172-r2" x="292.8" y="678.8" textLength="597.8" clip-path="url(#terminal-1308707172-line-27)">Regenerate&#160;thumbnails&#160;even&#160;if&#160;they&#160;already&#160;exist.</text><text class="terminal-1308707172-r2" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-27)">
</text><text class="terminal-1308707172-r4" x="24.4" y="703.2" textLength="207.4" clip-path="url(#terminal-1308707172-line-28)">--reread-metadata</text><text class="terminal-1308707172-r2" x="292.8" y="703.2" textLength="256.2" clip-path="url(#terminal-1308707172-line-28)">Reread&#160;image&#160;metadata</text><text class="terminal-1308707172-r2" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-1308707172-line-28)">
</text><text class="terminal-1308707172-r4" x="24.4" y="727.6" textLength="170.8" clip-path="url(#terminal-1308707172-line-29)">--reverse-sort</text><text class="terminal-1308707172-r2" x="292.8" y="727.6" textLength="353.8" clip-path="url(#terminal-1308707172-line-29)">Sort&#160;images&#160;in&#160;reverse&#160;order.</text><text class="terminal-1308707172-r2" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-1308707172-line-29)">
</text><text class="terminal-1308707172-r4" x="24.4" y="752" textLength="146.4" clip-path="url(#terminal-1308707172-line-30)">--theme-path</text><text class="terminal-1308707172-r5" x="183" y="752" textLength="48.8" clip-path="url(#terminal-1308707172-line-30)">PATH</text><text class="terminal-1308707172-r2" x="292.8" y="752" textLength="329.4" clip-path="url(#terminal-1308707172-line-30)">Path&#160;to&#160;the&#160;CSS&#160;theme&#160;file.</text><text class="terminal-1308707172-r2" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-1308707172-line-30)">
</text><text class="terminal-1308707172-r4" x="24.4" y="776.4" textLength="231.8" clip-path="url(#terminal-1308707172-line-31)">--use-fancy-folders</text><text class="terminal-1308707172-r2" x="292.8" y="776.4" textLength="890.6" clip-path="url(#terminal-1308707172-line-31)">Enable&#160;fancy&#160;folder&#160;view&#160;instead&#160;of&#160;the&#160;default&#160;Apache&#160;directory&#160;listing.</text><text class="terminal-1308707172-r2" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-1308707172-line-31)">
</text><text class="terminal-1308707172-r4" x="24.4" y="800.8" textLength="109.8" clip-path="url(#terminal-1308707172-line-32)">--version</text><text class="terminal-1308707172-r2" x="292.8" y="800.8" textLength="463.6" clip-path="url(#terminal-1308707172-line-32)">show&#160;program&#x27;s&#160;version&#160;number&#160;and&#160;exit</text><text class="terminal-1308707172-r2" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-1308707172-line-32)">
<g class="terminal-1915255058-matrix">
<text class="terminal-1915255058-r1" x="0" y="20" textLength="73.2" clip-path="url(#terminal-1915255058-line-0)">Usage:</text><text class="terminal-1915255058-r3" x="85.4" y="20" textLength="122" clip-path="url(#terminal-1915255058-line-0)">builder.py</text><text class="terminal-1915255058-r2" x="207.4" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">&#160;[</text><text class="terminal-1915255058-r4" x="231.8" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-h</text><text class="terminal-1915255058-r2" x="256.2" y="20" textLength="36.6" clip-path="url(#terminal-1915255058-line-0)">]&#160;[</text><text class="terminal-1915255058-r4" x="292.8" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-a</text><text class="terminal-1915255058-r5" x="329.4" y="20" textLength="73.2" clip-path="url(#terminal-1915255058-line-0)">AUTHOR</text><text class="terminal-1915255058-r2" x="402.6" y="20" textLength="36.6" clip-path="url(#terminal-1915255058-line-0)">]&#160;[</text><text class="terminal-1915255058-r4" x="439.2" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-e</text><text class="terminal-1915255058-r5" x="475.8" y="20" textLength="109.8" clip-path="url(#terminal-1915255058-line-0)">EXTENSION</text><text class="terminal-1915255058-r2" x="585.6" y="20" textLength="36.6" clip-path="url(#terminal-1915255058-line-0)">]&#160;[</text><text class="terminal-1915255058-r4" x="622.2" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-l</text><text class="terminal-1915255058-r5" x="658.8" y="20" textLength="85.4" clip-path="url(#terminal-1915255058-line-0)">LICENSE</text><text class="terminal-1915255058-r2" x="744.2" y="20" textLength="36.6" clip-path="url(#terminal-1915255058-line-0)">]&#160;[</text><text class="terminal-1915255058-r4" x="780.8" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-m</text><text class="terminal-1915255058-r2" x="805.2" y="20" textLength="36.6" clip-path="url(#terminal-1915255058-line-0)">]&#160;[</text><text class="terminal-1915255058-r4" x="841.8" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-n</text><text class="terminal-1915255058-r2" x="866.2" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">]&#160;</text><text class="terminal-1915255058-r4" x="890.6" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-p</text><text class="terminal-1915255058-r5" x="927.2" y="20" textLength="48.8" clip-path="url(#terminal-1915255058-line-0)">ROOT</text><text class="terminal-1915255058-r4" x="988.2" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-t</text><text class="terminal-1915255058-r5" x="1024.8" y="20" textLength="61" clip-path="url(#terminal-1915255058-line-0)">TITLE</text><text class="terminal-1915255058-r4" x="1098" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-w</text><text class="terminal-1915255058-r5" x="1134.6" y="20" textLength="36.6" clip-path="url(#terminal-1915255058-line-0)">URL</text><text class="terminal-1915255058-r2" x="1171.2" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">&#160;[</text><text class="terminal-1915255058-r4" x="1195.6" y="20" textLength="24.4" clip-path="url(#terminal-1915255058-line-0)">-c</text><text class="terminal-1915255058-r5" x="1232.2" y="20" textLength="134.2" clip-path="url(#terminal-1915255058-line-0)">CONFIG_FILE</text><text class="terminal-1915255058-r2" x="1366.4" y="20" textLength="12.2" clip-path="url(#terminal-1915255058-line-0)">]</text><text class="terminal-1915255058-r2" x="1464" y="20" textLength="12.2" clip-path="url(#terminal-1915255058-line-0)">
</text><text class="terminal-1915255058-r2" x="0" y="44.4" textLength="231.8" clip-path="url(#terminal-1915255058-line-1)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[</text><text class="terminal-1915255058-r4" x="231.8" y="44.4" textLength="195.2" clip-path="url(#terminal-1915255058-line-1)">--exclude-folder</text><text class="terminal-1915255058-r5" x="439.2" y="44.4" textLength="73.2" clip-path="url(#terminal-1915255058-line-1)">FOLDER</text><text class="terminal-1915255058-r2" x="512.4" y="44.4" textLength="36.6" clip-path="url(#terminal-1915255058-line-1)">]&#160;[</text><text class="terminal-1915255058-r4" x="549" y="44.4" textLength="219.6" clip-path="url(#terminal-1915255058-line-1)">--folderthumbnails</text><text class="terminal-1915255058-r2" x="768.6" y="44.4" textLength="36.6" clip-path="url(#terminal-1915255058-line-1)">]&#160;[</text><text class="terminal-1915255058-r4" x="805.2" y="44.4" textLength="244" clip-path="url(#terminal-1915255058-line-1)">--ignore-other-files</text><text class="terminal-1915255058-r2" x="1049.2" y="44.4" textLength="36.6" clip-path="url(#terminal-1915255058-line-1)">]&#160;[</text><text class="terminal-1915255058-r4" x="1085.8" y="44.4" textLength="219.6" clip-path="url(#terminal-1915255058-line-1)">--ignore-extension</text><text class="terminal-1915255058-r5" x="1317.6" y="44.4" textLength="109.8" clip-path="url(#terminal-1915255058-line-1)">EXTENSION</text><text class="terminal-1915255058-r2" x="1427.4" y="44.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-1)">]</text><text class="terminal-1915255058-r2" x="1464" y="44.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-1)">
</text><text class="terminal-1915255058-r2" x="0" y="68.8" textLength="231.8" clip-path="url(#terminal-1915255058-line-2)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[</text><text class="terminal-1915255058-r4" x="231.8" y="68.8" textLength="280.6" clip-path="url(#terminal-1915255058-line-2)">--regenerate-thumbnails</text><text class="terminal-1915255058-r2" x="512.4" y="68.8" textLength="36.6" clip-path="url(#terminal-1915255058-line-2)">]&#160;[</text><text class="terminal-1915255058-r4" x="549" y="68.8" textLength="207.4" clip-path="url(#terminal-1915255058-line-2)">--reread-metadata</text><text class="terminal-1915255058-r2" x="756.4" y="68.8" textLength="36.6" clip-path="url(#terminal-1915255058-line-2)">]&#160;[</text><text class="terminal-1915255058-r4" x="793" y="68.8" textLength="195.2" clip-path="url(#terminal-1915255058-line-2)">--reread-sidecar</text><text class="terminal-1915255058-r2" x="988.2" y="68.8" textLength="36.6" clip-path="url(#terminal-1915255058-line-2)">]&#160;[</text><text class="terminal-1915255058-r4" x="1024.8" y="68.8" textLength="170.8" clip-path="url(#terminal-1915255058-line-2)">--reverse-sort</text><text class="terminal-1915255058-r2" x="1195.6" y="68.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-2)">]</text><text class="terminal-1915255058-r2" x="1464" y="68.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-2)">
</text><text class="terminal-1915255058-r2" x="0" y="93.2" textLength="231.8" clip-path="url(#terminal-1915255058-line-3)">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;[</text><text class="terminal-1915255058-r4" x="231.8" y="93.2" textLength="146.4" clip-path="url(#terminal-1915255058-line-3)">--theme-path</text><text class="terminal-1915255058-r5" x="390.4" y="93.2" textLength="48.8" clip-path="url(#terminal-1915255058-line-3)">PATH</text><text class="terminal-1915255058-r2" x="439.2" y="93.2" textLength="36.6" clip-path="url(#terminal-1915255058-line-3)">]&#160;[</text><text class="terminal-1915255058-r4" x="475.8" y="93.2" textLength="231.8" clip-path="url(#terminal-1915255058-line-3)">--use-fancy-folders</text><text class="terminal-1915255058-r2" x="707.6" y="93.2" textLength="36.6" clip-path="url(#terminal-1915255058-line-3)">]&#160;[</text><text class="terminal-1915255058-r4" x="744.2" y="93.2" textLength="109.8" clip-path="url(#terminal-1915255058-line-3)">--version</text><text class="terminal-1915255058-r2" x="854" y="93.2" textLength="36.6" clip-path="url(#terminal-1915255058-line-3)">]&#160;[</text><text class="terminal-1915255058-r4" x="890.6" y="93.2" textLength="170.8" clip-path="url(#terminal-1915255058-line-3)">--write-config</text><text class="terminal-1915255058-r5" x="1073.6" y="93.2" textLength="134.2" clip-path="url(#terminal-1915255058-line-3)">CONFIG_FILE</text><text class="terminal-1915255058-r2" x="1207.8" y="93.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-3)">]</text><text class="terminal-1915255058-r2" x="1464" y="93.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-3)">
</text><text class="terminal-1915255058-r2" x="1464" y="117.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-4)">
</text><text class="terminal-1915255058-r2" x="0" y="142" textLength="658.8" clip-path="url(#terminal-1915255058-line-5)">generate&#160;HTML&#160;files&#160;for&#160;a&#160;static&#160;image&#160;hosting&#160;website</text><text class="terminal-1915255058-r2" x="1464" y="142" textLength="12.2" clip-path="url(#terminal-1915255058-line-5)">
</text><text class="terminal-1915255058-r2" x="1464" y="166.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-6)">
</text><text class="terminal-1915255058-r1" x="0" y="190.8" textLength="97.6" clip-path="url(#terminal-1915255058-line-7)">Options:</text><text class="terminal-1915255058-r2" x="1464" y="190.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-7)">
</text><text class="terminal-1915255058-r4" x="24.4" y="215.2" textLength="24.4" clip-path="url(#terminal-1915255058-line-8)">-h</text><text class="terminal-1915255058-r2" x="48.8" y="215.2" textLength="24.4" clip-path="url(#terminal-1915255058-line-8)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="215.2" textLength="73.2" clip-path="url(#terminal-1915255058-line-8)">--help</text><text class="terminal-1915255058-r2" x="292.8" y="215.2" textLength="378.2" clip-path="url(#terminal-1915255058-line-8)">show&#160;this&#160;help&#160;message&#160;and&#160;exit</text><text class="terminal-1915255058-r2" x="1464" y="215.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-8)">
</text><text class="terminal-1915255058-r4" x="24.4" y="239.6" textLength="24.4" clip-path="url(#terminal-1915255058-line-9)">-a</text><text class="terminal-1915255058-r2" x="48.8" y="239.6" textLength="24.4" clip-path="url(#terminal-1915255058-line-9)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="239.6" textLength="158.6" clip-path="url(#terminal-1915255058-line-9)">--author-name</text><text class="terminal-1915255058-r5" x="244" y="239.6" textLength="73.2" clip-path="url(#terminal-1915255058-line-9)">AUTHOR</text><text class="terminal-1915255058-r2" x="1464" y="239.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-9)">
</text><text class="terminal-1915255058-r2" x="292.8" y="264" textLength="390.4" clip-path="url(#terminal-1915255058-line-10)">name&#160;of&#160;the&#160;author&#160;of&#160;the&#160;images</text><text class="terminal-1915255058-r2" x="1464" y="264" textLength="12.2" clip-path="url(#terminal-1915255058-line-10)">
</text><text class="terminal-1915255058-r4" x="24.4" y="288.4" textLength="24.4" clip-path="url(#terminal-1915255058-line-11)">-e</text><text class="terminal-1915255058-r2" x="48.8" y="288.4" textLength="24.4" clip-path="url(#terminal-1915255058-line-11)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="288.4" textLength="207.4" clip-path="url(#terminal-1915255058-line-11)">--file-extensions</text><text class="terminal-1915255058-r5" x="292.8" y="288.4" textLength="109.8" clip-path="url(#terminal-1915255058-line-11)">EXTENSION</text><text class="terminal-1915255058-r2" x="1464" y="288.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-11)">
</text><text class="terminal-1915255058-r2" x="292.8" y="312.8" textLength="732" clip-path="url(#terminal-1915255058-line-12)">file&#160;extensions&#160;to&#160;include&#160;(can&#160;be&#160;specified&#160;multiple&#160;times)</text><text class="terminal-1915255058-r2" x="1464" y="312.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-12)">
</text><text class="terminal-1915255058-r4" x="24.4" y="337.2" textLength="24.4" clip-path="url(#terminal-1915255058-line-13)">-l</text><text class="terminal-1915255058-r2" x="48.8" y="337.2" textLength="24.4" clip-path="url(#terminal-1915255058-line-13)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="337.2" textLength="170.8" clip-path="url(#terminal-1915255058-line-13)">--license-type</text><text class="terminal-1915255058-r5" x="256.2" y="337.2" textLength="85.4" clip-path="url(#terminal-1915255058-line-13)">LICENSE</text><text class="terminal-1915255058-r2" x="1464" y="337.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-13)">
</text><text class="terminal-1915255058-r2" x="292.8" y="361.6" textLength="475.8" clip-path="url(#terminal-1915255058-line-14)">specify&#160;the&#160;license&#160;type&#160;for&#160;the&#160;images</text><text class="terminal-1915255058-r2" x="1464" y="361.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-14)">
</text><text class="terminal-1915255058-r4" x="24.4" y="386" textLength="24.4" clip-path="url(#terminal-1915255058-line-15)">-m</text><text class="terminal-1915255058-r2" x="48.8" y="386" textLength="24.4" clip-path="url(#terminal-1915255058-line-15)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="386" textLength="170.8" clip-path="url(#terminal-1915255058-line-15)">--web-manifest</text><text class="terminal-1915255058-r2" x="292.8" y="386" textLength="341.6" clip-path="url(#terminal-1915255058-line-15)">generate&#160;a&#160;web&#160;manifest&#160;file</text><text class="terminal-1915255058-r2" x="1464" y="386" textLength="12.2" clip-path="url(#terminal-1915255058-line-15)">
</text><text class="terminal-1915255058-r4" x="24.4" y="410.4" textLength="24.4" clip-path="url(#terminal-1915255058-line-16)">-n</text><text class="terminal-1915255058-r2" x="48.8" y="410.4" textLength="24.4" clip-path="url(#terminal-1915255058-line-16)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="410.4" textLength="268.4" clip-path="url(#terminal-1915255058-line-16)">--non-interactive-mode</text><text class="terminal-1915255058-r2" x="1464" y="410.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-16)">
</text><text class="terminal-1915255058-r2" x="292.8" y="434.8" textLength="634.4" clip-path="url(#terminal-1915255058-line-17)">run&#160;in&#160;non-interactive&#160;mode,&#160;disabling&#160;progress&#160;bars</text><text class="terminal-1915255058-r2" x="1464" y="434.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-17)">
</text><text class="terminal-1915255058-r4" x="24.4" y="459.2" textLength="24.4" clip-path="url(#terminal-1915255058-line-18)">-p</text><text class="terminal-1915255058-r2" x="48.8" y="459.2" textLength="24.4" clip-path="url(#terminal-1915255058-line-18)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="459.2" textLength="195.2" clip-path="url(#terminal-1915255058-line-18)">--root-directory</text><text class="terminal-1915255058-r5" x="280.6" y="459.2" textLength="48.8" clip-path="url(#terminal-1915255058-line-18)">ROOT</text><text class="terminal-1915255058-r2" x="1464" y="459.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-18)">
</text><text class="terminal-1915255058-r2" x="292.8" y="483.6" textLength="439.2" clip-path="url(#terminal-1915255058-line-19)">root&#160;directory&#160;containing&#160;the&#160;images</text><text class="terminal-1915255058-r2" x="1464" y="483.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-19)">
</text><text class="terminal-1915255058-r4" x="24.4" y="508" textLength="24.4" clip-path="url(#terminal-1915255058-line-20)">-t</text><text class="terminal-1915255058-r2" x="48.8" y="508" textLength="24.4" clip-path="url(#terminal-1915255058-line-20)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="508" textLength="146.4" clip-path="url(#terminal-1915255058-line-20)">--site-title</text><text class="terminal-1915255058-r5" x="231.8" y="508" textLength="61" clip-path="url(#terminal-1915255058-line-20)">TITLE</text><text class="terminal-1915255058-r2" x="1464" y="508" textLength="12.2" clip-path="url(#terminal-1915255058-line-20)">
</text><text class="terminal-1915255058-r2" x="292.8" y="532.4" textLength="378.2" clip-path="url(#terminal-1915255058-line-21)">title&#160;of&#160;the&#160;image&#160;hosting&#160;site</text><text class="terminal-1915255058-r2" x="1464" y="532.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-21)">
</text><text class="terminal-1915255058-r4" x="24.4" y="556.8" textLength="24.4" clip-path="url(#terminal-1915255058-line-22)">-w</text><text class="terminal-1915255058-r2" x="48.8" y="556.8" textLength="24.4" clip-path="url(#terminal-1915255058-line-22)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="556.8" textLength="170.8" clip-path="url(#terminal-1915255058-line-22)">--web-root-url</text><text class="terminal-1915255058-r5" x="256.2" y="556.8" textLength="36.6" clip-path="url(#terminal-1915255058-line-22)">URL</text><text class="terminal-1915255058-r2" x="1464" y="556.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-22)">
</text><text class="terminal-1915255058-r2" x="292.8" y="581.2" textLength="622.2" clip-path="url(#terminal-1915255058-line-23)">base&#160;URL&#160;of&#160;the&#160;web&#160;root&#160;for&#160;the&#160;image&#160;hosting&#160;site</text><text class="terminal-1915255058-r2" x="1464" y="581.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-23)">
</text><text class="terminal-1915255058-r4" x="24.4" y="605.6" textLength="24.4" clip-path="url(#terminal-1915255058-line-24)">-c</text><text class="terminal-1915255058-r2" x="48.8" y="605.6" textLength="24.4" clip-path="url(#terminal-1915255058-line-24)">,&#160;</text><text class="terminal-1915255058-r4" x="73.2" y="605.6" textLength="158.6" clip-path="url(#terminal-1915255058-line-24)">--config-file</text><text class="terminal-1915255058-r5" x="244" y="605.6" textLength="134.2" clip-path="url(#terminal-1915255058-line-24)">CONFIG_FILE</text><text class="terminal-1915255058-r2" x="1464" y="605.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-24)">
</text><text class="terminal-1915255058-r2" x="292.8" y="630" textLength="195.2" clip-path="url(#terminal-1915255058-line-25)">config&#160;file&#160;path</text><text class="terminal-1915255058-r2" x="1464" y="630" textLength="12.2" clip-path="url(#terminal-1915255058-line-25)">
</text><text class="terminal-1915255058-r4" x="24.4" y="654.4" textLength="195.2" clip-path="url(#terminal-1915255058-line-26)">--exclude-folder</text><text class="terminal-1915255058-r5" x="231.8" y="654.4" textLength="73.2" clip-path="url(#terminal-1915255058-line-26)">FOLDER</text><text class="terminal-1915255058-r2" x="1464" y="654.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-26)">
</text><text class="terminal-1915255058-r2" x="292.8" y="678.8" textLength="1037" clip-path="url(#terminal-1915255058-line-27)">folders&#160;to&#160;exclude&#160;from&#160;processing,&#160;globs&#160;supported&#160;(can&#160;be&#160;specified&#160;multiple&#160;times)</text><text class="terminal-1915255058-r2" x="1464" y="678.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-27)">
</text><text class="terminal-1915255058-r4" x="24.4" y="703.2" textLength="219.6" clip-path="url(#terminal-1915255058-line-28)">--folderthumbnails</text><text class="terminal-1915255058-r2" x="292.8" y="703.2" textLength="817.4" clip-path="url(#terminal-1915255058-line-28)">generate&#160;subfolder&#160;thumbnails&#160;(first&#160;image&#160;in&#160;folder&#160;will&#160;be&#160;shown)</text><text class="terminal-1915255058-r2" x="1464" y="703.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-28)">
</text><text class="terminal-1915255058-r4" x="24.4" y="727.6" textLength="244" clip-path="url(#terminal-1915255058-line-29)">--ignore-other-files</text><text class="terminal-1915255058-r2" x="292.8" y="727.6" textLength="671" clip-path="url(#terminal-1915255058-line-29)">ignore&#160;files&#160;that&#160;do&#160;not&#160;match&#160;the&#160;specified&#160;extensions</text><text class="terminal-1915255058-r2" x="1464" y="727.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-29)">
</text><text class="terminal-1915255058-r4" x="24.4" y="752" textLength="219.6" clip-path="url(#terminal-1915255058-line-30)">--ignore-extension</text><text class="terminal-1915255058-r5" x="256.2" y="752" textLength="109.8" clip-path="url(#terminal-1915255058-line-30)">EXTENSION</text><text class="terminal-1915255058-r2" x="1464" y="752" textLength="12.2" clip-path="url(#terminal-1915255058-line-30)">
</text><text class="terminal-1915255058-r2" x="292.8" y="776.4" textLength="719.8" clip-path="url(#terminal-1915255058-line-31)">file&#160;extensions&#160;to&#160;ignore&#160;(can&#160;be&#160;specified&#160;multiple&#160;times)</text><text class="terminal-1915255058-r2" x="1464" y="776.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-31)">
</text><text class="terminal-1915255058-r4" x="24.4" y="800.8" textLength="280.6" clip-path="url(#terminal-1915255058-line-32)">--regenerate-thumbnails</text><text class="terminal-1915255058-r2" x="1464" y="800.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-32)">
</text><text class="terminal-1915255058-r2" x="292.8" y="825.2" textLength="585.6" clip-path="url(#terminal-1915255058-line-33)">regenerate&#160;thumbnails&#160;even&#160;if&#160;they&#160;already&#160;exist</text><text class="terminal-1915255058-r2" x="1464" y="825.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-33)">
</text><text class="terminal-1915255058-r4" x="24.4" y="849.6" textLength="207.4" clip-path="url(#terminal-1915255058-line-34)">--reread-metadata</text><text class="terminal-1915255058-r2" x="292.8" y="849.6" textLength="256.2" clip-path="url(#terminal-1915255058-line-34)">reread&#160;image&#160;metadata</text><text class="terminal-1915255058-r2" x="1464" y="849.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-34)">
</text><text class="terminal-1915255058-r4" x="24.4" y="874" textLength="195.2" clip-path="url(#terminal-1915255058-line-35)">--reread-sidecar</text><text class="terminal-1915255058-r2" x="292.8" y="874" textLength="244" clip-path="url(#terminal-1915255058-line-35)">reread&#160;sidecar&#160;files</text><text class="terminal-1915255058-r2" x="1464" y="874" textLength="12.2" clip-path="url(#terminal-1915255058-line-35)">
</text><text class="terminal-1915255058-r4" x="24.4" y="898.4" textLength="170.8" clip-path="url(#terminal-1915255058-line-36)">--reverse-sort</text><text class="terminal-1915255058-r2" x="292.8" y="898.4" textLength="341.6" clip-path="url(#terminal-1915255058-line-36)">sort&#160;images&#160;in&#160;reverse&#160;order</text><text class="terminal-1915255058-r2" x="1464" y="898.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-36)">
</text><text class="terminal-1915255058-r4" x="24.4" y="922.8" textLength="146.4" clip-path="url(#terminal-1915255058-line-37)">--theme-path</text><text class="terminal-1915255058-r5" x="183" y="922.8" textLength="48.8" clip-path="url(#terminal-1915255058-line-37)">PATH</text><text class="terminal-1915255058-r2" x="292.8" y="922.8" textLength="317.2" clip-path="url(#terminal-1915255058-line-37)">path&#160;to&#160;the&#160;CSS&#160;theme&#160;file</text><text class="terminal-1915255058-r2" x="1464" y="922.8" textLength="12.2" clip-path="url(#terminal-1915255058-line-37)">
</text><text class="terminal-1915255058-r4" x="24.4" y="947.2" textLength="231.8" clip-path="url(#terminal-1915255058-line-38)">--use-fancy-folders</text><text class="terminal-1915255058-r2" x="292.8" y="947.2" textLength="878.4" clip-path="url(#terminal-1915255058-line-38)">enable&#160;fancy&#160;folder&#160;view&#160;instead&#160;of&#160;the&#160;default&#160;Apache&#160;directory&#160;listing</text><text class="terminal-1915255058-r2" x="1464" y="947.2" textLength="12.2" clip-path="url(#terminal-1915255058-line-38)">
</text><text class="terminal-1915255058-r4" x="24.4" y="971.6" textLength="109.8" clip-path="url(#terminal-1915255058-line-39)">--version</text><text class="terminal-1915255058-r2" x="292.8" y="971.6" textLength="463.6" clip-path="url(#terminal-1915255058-line-39)">show&#160;program&#x27;s&#160;version&#160;number&#160;and&#160;exit</text><text class="terminal-1915255058-r2" x="1464" y="971.6" textLength="12.2" clip-path="url(#terminal-1915255058-line-39)">
</text><text class="terminal-1915255058-r4" x="24.4" y="996" textLength="170.8" clip-path="url(#terminal-1915255058-line-40)">--write-config</text><text class="terminal-1915255058-r5" x="207.4" y="996" textLength="134.2" clip-path="url(#terminal-1915255058-line-40)">CONFIG_FILE</text><text class="terminal-1915255058-r2" x="1464" y="996" textLength="12.2" clip-path="url(#terminal-1915255058-line-40)">
</text><text class="terminal-1915255058-r2" x="292.8" y="1020.4" textLength="561.2" clip-path="url(#terminal-1915255058-line-41)">write&#160;current&#160;command&#160;line&#160;args&#160;to&#160;config&#160;file</text><text class="terminal-1915255058-r2" x="1464" y="1020.4" textLength="12.2" clip-path="url(#terminal-1915255058-line-41)">
</text>
</g>
</g>

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -1,7 +1,8 @@
from dataclasses import dataclass
from typing import Optional
import os
import argparse
import configargparse
try:
from rich_argparse import RichHelpFormatter, HelpPreviewAction
@@ -11,14 +12,18 @@ except ModuleNotFoundError:
RICH = False
if __package__ is None:
PACKAGE = ""
else:
PACKAGE = __package__
SCRIPTDIR = os.path.abspath(os.path.dirname(__file__).removesuffix(PACKAGE))
SCRIPTDIR = os.path.dirname(os.path.realpath(__file__)).removesuffix(__package__ if __package__ else "")
DEFAULT_THEME_PATH = os.path.join(SCRIPTDIR, "templates", "default.css")
DEFAULT_AUTHOR = "Author"
if "APPDATA" in os.environ:
CONFIGHOME = os.environ["APPDATA"]
elif "XDG_CONFIG_HOME" in os.environ:
CONFIGHOME = os.environ["XDG_CONFIG_HOME"]
else:
CONFIGHOME = os.path.join(os.environ["HOME"], ".config")
CONFIGPATH = os.path.join(CONFIGHOME, "StaticGalleryBuilder")
@dataclass(init=True)
class Args:
@@ -55,6 +60,8 @@ class Args:
Whether to enable fancy folder view.
web_root_url : str
The base URL of the web root for the image hosting site.
darktheme : bool
Whether a dark theme is present.
"""
author_name: str
@@ -62,17 +69,20 @@ class Args:
file_extensions: list[str]
folder_thumbs: bool
generate_webmanifest: bool
ignore_extensions: list[str]
ignore_other_files: bool
license_type: Optional[str]
non_interactive_mode: bool
regenerate_thumbnails: bool
reread_metadata: bool
reread_sidecar: bool
reverse_sort: bool
root_directory: str
site_title: str
theme_path: str
use_fancy_folders: bool
web_root_url: str
darktheme: bool = False
def to_dict(self) -> dict:
result: dict = {}
@@ -81,18 +91,21 @@ class Args:
result["file_extensions"] = self.file_extensions
result["folder_thumbs"] = self.folder_thumbs
result["generate_webmanifest"] = self.generate_webmanifest
result["ignore_extensions"] = self.ignore_extensions
result["ignore_other_files"] = self.ignore_other_files
if self.license_type is not None:
result["license_type"] = self.license_type
result["non_interactive_mode"] = self.non_interactive_mode
result["regenerate_thumbnails"] = self.regenerate_thumbnails
result["reread_metadata"] = self.reread_metadata
result["reread_sidecar"] = self.reread_sidecar
result["reverse_sort"] = self.reverse_sort
result["root_directory"] = self.root_directory
result["site_title"] = self.site_title
result["theme_path"] = self.theme_path
result["use_fancy_folders"] = self.use_fancy_folders
result["web_root_url"] = self.web_root_url
result["darktheme"] = self.darktheme
return result
@@ -112,29 +125,37 @@ def parse_arguments(version: str) -> Args:
"""
# fmt: off
if RICH:
parser = argparse.ArgumentParser(description="Generate HTML files for a static image hosting website.", formatter_class=RichHelpFormatter)
parser = configargparse.ArgumentParser(default_config_files=[CONFIGPATH], add_config_file_help=False, description="generate HTML files for a static image hosting website", formatter_class=RichHelpFormatter)
else:
parser = argparse.ArgumentParser(description="Generate HTML files for a static image hosting website.")
parser.add_argument("-a", "--author-name", help="Name of the author of the images.", default=DEFAULT_AUTHOR, type=str, dest="author_name", metavar="AUTHOR")
parser.add_argument("-e", "--file-extensions", help="File extensions to include (can be specified multiple times).", action="append", dest="file_extensions", metavar="EXTENSION")
parser.add_argument("-l", "--license-type", help="Specify the license type for the images.", choices=["cc-zero", "cc-by", "cc-by-sa", "cc-by-nd", "cc-by-nc", "cc-by-nc-sa", "cc-by-nc-nd"], default=None, dest="license_type", metavar="LICENSE")
parser.add_argument("-m", "--web-manifest", help="Generate a web manifest file.", action="store_true", default=False, dest="generate_webmanifest")
parser.add_argument("-n", "--non-interactive-mode", help="Run in non-interactive mode, disabling progress bars.", action="store_true", default=False, dest="non_interactive_mode")
parser.add_argument("-p", "--root-directory", help="Root directory containing the images.", required=True, type=str, dest="root_directory", metavar="ROOT")
parser.add_argument("-t", "--site-title", help="Title of the image hosting site.", required=True, type=str, dest="site_title", metavar="TITLE")
parser.add_argument("-w", "--web-root-url", help="Base URL of the web root for the image hosting site.", required=True, type=str, dest="web_root_url", metavar="URL")
parser.add_argument("--exclude-folder", help="Folders to exclude from processing, globs supported (can be specified multiple times).", action="append", dest="exclude_folders", metavar="FOLDER")
parser.add_argument("--folderthumbnails", help="Generate subfolder thumbnails (first image in folder will be shown)", action="store_true", default=False, dest="folder_thumbs")
parser = configargparse.ArgumentParser(default_config_files=[CONFIGPATH], add_config_file_help=False, description="generate HTML files for a static image hosting website")
parser.add_argument("-a", "--author-name", help="name of the author of the images", default=DEFAULT_AUTHOR, type=str, dest="author_name", metavar="AUTHOR")
parser.add_argument("-e", "--file-extensions", help="file extensions to include (can be specified multiple times)", action="append", dest="file_extensions", metavar="EXTENSION")
parser.add_argument("-l", "--license-type", help="specify the license type for the images", choices=["cc-zero", "cc-by", "cc-by-sa", "cc-by-nd", "cc-by-nc", "cc-by-nc-sa", "cc-by-nc-nd"], default=None, dest="license_type", metavar="LICENSE")
parser.add_argument("-m", "--web-manifest", help="generate a web manifest file", action="store_true", default=False, dest="generate_webmanifest")
parser.add_argument("-n", "--non-interactive-mode", help="run in non-interactive mode, disabling progress bars", action="store_true", default=False, dest="non_interactive_mode")
parser.add_argument("-p", "--root-directory", help="root directory containing the images", required=True, type=str, dest="root_directory", metavar="ROOT")
parser.add_argument("-t", "--site-title", help="title of the image hosting site", required=True, type=str, dest="site_title", metavar="TITLE")
parser.add_argument("-w", "--web-root-url", help="base URL of the web root for the image hosting site", required=True, type=str, dest="web_root_url", metavar="URL")
parser.add_argument('-c', '--config-file', is_config_file=True, help='config file path', metavar="CONFIG_FILE")
parser.add_argument("--exclude-folder", help="folders to exclude from processing, globs supported (can be specified multiple times)", action="append", dest="exclude_folders", metavar="FOLDER")
parser.add_argument("--folderthumbnails", help="generate subfolder thumbnails (first image in folder will be shown)", action="store_true", default=False, dest="folder_thumbs")
if RICH:
parser.add_argument("--generate-help-preview", action=HelpPreviewAction, path="help.svg", )
parser.add_argument("--ignore-other-files", help="Ignore files that do not match the specified extensions.", action="store_true", default=False, dest="ignore_other_files")
parser.add_argument("--regenerate-thumbnails", help="Regenerate thumbnails even if they already exist.", action="store_true", default=False, dest="regenerate_thumbnails")
parser.add_argument("--reread-metadata", help="Reread image metadata", action="store_true", default=False, dest="reread_metadata")
parser.add_argument("--reverse-sort", help="Sort images in reverse order.", action="store_true", default=False, dest="reverse_sort")
parser.add_argument("--theme-path", help="Path to the CSS theme file.", default=DEFAULT_THEME_PATH, type=str, dest="theme_path", metavar="PATH")
parser.add_argument("--use-fancy-folders", help="Enable fancy folder view instead of the default Apache directory listing.", action="store_true", default=False, dest="use_fancy_folders")
parser.add_argument("--generate-help-preview", action=HelpPreviewAction, path="help.svg")
parser.add_argument("--ignore-other-files", help="ignore files that do not match the specified extensions", action="store_true", default=False, dest="ignore_other_files")
parser.add_argument("--ignore-extension", help="file extensions to ignore (can be specified multiple times)", action="append", default=[], dest="ignore_extensions", metavar="EXTENSION")
parser.add_argument("--regenerate-thumbnails", help="regenerate thumbnails even if they already exist", action="store_true", default=False, dest="regenerate_thumbnails")
parser.add_argument("--reread-metadata", help="reread image metadata", action="store_true", default=False, dest="reread_metadata")
parser.add_argument("--reread-sidecar", help="reread sidecar files", action="store_true", default=False, dest="reread_sidecar")
parser.add_argument("--reverse-sort", help="sort images in reverse order", action="store_true", default=False, dest="reverse_sort")
parser.add_argument("--theme-path", help="path to the CSS theme file", default=DEFAULT_THEME_PATH, type=str, dest="theme_path", metavar="PATH")
parser.add_argument("--use-fancy-folders", help="enable fancy folder view instead of the default Apache directory listing", action="store_true", default=False, dest="use_fancy_folders")
parser.add_argument("--version", action="version", version=f"%(prog)s {version}")
parser.add_argument("--write-config", type=str, required=False, help="write current command line args to config file", metavar="CONFIG_FILE")
parsed_args = parser.parse_args()
if parsed_args.write_config:
config_path = parsed_args.write_config
del parsed_args.write_config
parser.write_config_file(parsed_args, [config_path], exit_after=False)
# fmt: on
_args = Args(
author_name=parsed_args.author_name,
@@ -143,15 +164,18 @@ def parse_arguments(version: str) -> Args:
folder_thumbs=parsed_args.folder_thumbs,
generate_webmanifest=parsed_args.generate_webmanifest,
ignore_other_files=parsed_args.ignore_other_files,
ignore_extensions=parsed_args.ignore_extensions,
license_type=parsed_args.license_type,
non_interactive_mode=parsed_args.non_interactive_mode,
regenerate_thumbnails=parsed_args.regenerate_thumbnails,
reread_metadata=parsed_args.reread_metadata,
reread_sidecar=parsed_args.reread_sidecar,
reverse_sort=parsed_args.reverse_sort,
root_directory=parsed_args.root_directory,
site_title=parsed_args.site_title,
theme_path=parsed_args.theme_path,
use_fancy_folders=parsed_args.use_fancy_folders,
web_root_url=parsed_args.web_root_url,
darktheme=False,
)
return _args

View File

@@ -19,7 +19,7 @@ def extract_colorscheme(theme_path: str) -> dict[str, str]:
dictionary containing color scheme variables and their hexadecimal values.
"""
logger.info("extracting color scheme from theme file", extra={"theme_path": theme_path})
pattern = r"--(color[1-4]|bcolor1):\s*(#[0-9a-fA-F]+|rgba?\([^)]*\)|hsla?\([^)]*\)|[a-zA-Z]+);"
pattern = r"--(color\d+|bcolor\d+):\s*(#[0-9a-fA-F]+|rgba?\([^)]*\)|hsla?\([^)]*\)|[a-zA-Z]+);"
colorscheme = {}
with open(theme_path, "r", encoding="utf-8") as f:
@@ -95,7 +95,8 @@ def css_color_to_hex(css_color: str) -> str:
# Helper function to convert HSL tuple to RGB tuple
def hsl_to_rgb(hsl: tuple[int, float, float]) -> tuple[int, int, int]:
logger.debug("converting hsl tuple to rgb tuple", extra={"hsl": hsl})
return tuple(round(c * 255) for c in colorsys.hls_to_rgb(hsl[0] / 360, hsl[1] / 100, hsl[2] / 100))
r, g, b = colorsys.hls_to_rgb(hsl[0] / 360, hsl[1] / 100, hsl[2] / 100)
return (round(r * 255), round(g * 255), round(b * 255))
# Regular expression pattern to match CSS colors
color_pattern = re.compile(

View File

@@ -0,0 +1,158 @@
from dataclasses import dataclass
from typing import List, Optional, Any, Dict, TypeVar, Callable, Type, cast
T = TypeVar("T")
def from_int(x: Any) -> int:
assert isinstance(x, int) and not isinstance(x, bool)
return x
def from_str(x: Any) -> str:
assert isinstance(x, str)
return x
def from_list(f: Callable[[Any], T], x: Any) -> List[T]:
assert isinstance(x, list)
return [f(y) for y in x]
def from_none(x: Any) -> Any:
assert x is None
return x
def from_union(fs, x):
for f in fs:
try:
return f(x)
except AssertionError:
pass
assert False
def to_class(c: Type[T], x: Any) -> dict:
assert isinstance(x, c)
return cast(Any, x).to_dict()
def from_dict(f: Callable[[Any], T], x: Any) -> Dict[str, T]:
assert isinstance(x, dict)
return {k: f(v) for (k, v) in x.items()}
def from_native_dict(f: Callable[[Any], T], x: Any) -> Dict[Any, T]:
assert isinstance(x, dict)
return x
@dataclass
class ImageMetadata:
w: Optional[int]
h: Optional[int]
tags: Optional[List[str]]
exifdata: Optional[Dict[str, Any]]
xmp: Optional[Dict[str, Any]]
src: str
msrc: str
name: str
title: str
tiff: Optional[str] = None
raw: Optional[str] = None
@staticmethod
def from_dict(obj: Any) -> "ImageMetadata":
assert isinstance(obj, dict)
w = from_union([from_int, from_none], obj.get("w"))
h = from_union([from_int, from_none], obj.get("h"))
tags = from_union([lambda x: from_list(from_str, x), from_none], obj.get("tags"))
exifdata = from_union([lambda x: from_native_dict(dict, x), from_none], obj.get("exifdata"))
xmp = from_union([lambda x: from_native_dict(dict, x), from_none], obj.get("xmp"))
src = from_str(obj.get("src"))
msrc = from_str(obj.get("msrc"))
name = from_str(obj.get("name"))
title = from_str(obj.get("title"))
tiff = from_union([from_str, from_none], obj.get("tiff"))
raw = from_union([from_str, from_none], obj.get("raw"))
return ImageMetadata(w, h, tags, exifdata, xmp, src, msrc, name, title, tiff, raw)
def to_dict(self) -> dict:
result: dict = {}
if self.w is not None:
result["w"] = from_union([from_int, from_none], self.w)
if self.h is not None:
result["h"] = from_union([from_int, from_none], self.h)
if self.tags is not None:
result["tags"] = from_union([lambda x: from_list(from_str, x), from_none], self.tags)
result["src"] = from_str(self.src)
result["msrc"] = from_str(self.msrc)
result["name"] = from_str(self.name)
result["title"] = from_str(self.title)
if self.tiff is not None:
result["tiff"] = from_union([from_str, from_none], self.tiff)
if self.raw is not None:
result["raw"] = from_union([from_str, from_none], self.raw)
if self.exifdata is not None:
result["exifdata"] = from_union([lambda x: from_native_dict(dict, x), from_none], self.exifdata)
if self.xmp is not None:
result["xmp"] = from_union([lambda x: from_native_dict(dict, x), from_none], self.xmp)
return result
@dataclass
class SubfolderMetadata:
url: str
name: str
metadata: Optional[str] = None
thumb: Optional[str] = None
@staticmethod
def from_dict(obj: Any) -> "SubfolderMetadata":
assert isinstance(obj, dict)
url = from_str(obj.get("url"))
name = from_str(obj.get("name"))
metadata = from_union([from_none, from_str], obj.get("metadata"))
thumb = from_union([from_none, from_str], obj.get("thumb"))
return SubfolderMetadata(url, name, metadata, thumb)
def to_dict(self) -> dict:
result: dict = {}
result["url"] = from_str(self.url)
result["name"] = from_str(self.name)
result["metadata"] = from_union([from_none, from_str], self.metadata)
result["thumb"] = from_union([from_none, from_str], self.thumb)
return result
@dataclass
class Metadata:
images: Dict[str, ImageMetadata]
subfolders: Optional[List[SubfolderMetadata]] = None
@staticmethod
def from_dict(obj: Any) -> "Metadata":
assert isinstance(obj, dict)
images = from_dict(ImageMetadata.from_dict, obj.get("images"))
subfolders = from_union([lambda x: from_list(SubfolderMetadata.from_dict, x), from_none], obj.get("subfolders"))
return Metadata(images, subfolders)
def to_dict(self) -> dict:
result: dict = {}
result["images"] = from_dict(lambda x: to_class(ImageMetadata, x), self.images)
if self.subfolders is not None:
result["subfolders"] = from_union([lambda x: from_list(lambda x: to_class(SubfolderMetadata, x), x), from_none], self.subfolders)
return result
def sort(self, reverse=False) -> "Metadata":
self.images = {key: self.images[key] for key in sorted(self.images, reverse=reverse)}
return self
def top_level_from_dict(s: Any) -> Metadata:
return Metadata.from_dict(s)
def top_level_to_dict(x: Metadata) -> Any:
return to_class(Metadata, x)

View File

@@ -3,24 +3,24 @@ import re
import urllib.parse
import fnmatch
import json
import html
from typing import Any
from datetime import datetime
from collections import defaultdict
from tqdm.auto import tqdm
from PIL import Image, ExifTags, TiffImagePlugin, UnidentifiedImageError
from jinja2 import Environment, FileSystemLoader
from defusedxml import ElementTree
from bs4 import BeautifulSoup
from modules.logger import logger
from modules import cclicense
from modules.argumentparser import Args
from modules.datatypes.metadata import Metadata, ImageMetadata, SubfolderMetadata
# Constants for file paths and exclusions
if __package__ is None:
PACKAGE = ""
else:
PACKAGE = __package__
SCRIPTDIR = os.path.abspath(os.path.dirname(__file__).removesuffix(PACKAGE))
SCRIPTDIR = os.path.dirname(os.path.realpath(__file__)).removesuffix(__package__ if __package__ else "")
FAVICON_PATH = ".static/favicon.ico"
GLOBAL_CSS_PATH = ".static/global.css"
EXCLUDES = ["index.html", "manifest.json", "robots.txt"]
@@ -70,7 +70,7 @@ def getxmp(strbuffer: str) -> dict[str, Any]:
return {get_name(root.tag): get_value(root)}
def initialize_metadata(folder: str) -> dict[str, dict[str, int]]:
def initialize_metadata(folder: str) -> Metadata:
"""
Initializes the metadata JSON file if it doesn't exist.
@@ -97,12 +97,38 @@ def initialize_metadata(folder: str) -> dict[str, dict[str, int]]:
# remove old sizelist if it exists
sizelist_path = os.path.join(folder, ".sizelist.json")
if os.path.exists(sizelist_path):
with open(sizelist_path, "r") as sizelist:
metadata = json.loads(sizelist.read())
logger.warning("found old .sizelist.json, removing it...", extra={"path": sizelist_path})
os.remove(sizelist_path)
return metadata
# convert from old metadata format
if "images" not in metadata and "subfolders" not in metadata:
images = metadata.copy()
metadata = {}
metadata["images"] = images
elif "images" not in metadata:
metadata["images"] = {}
for k, v in metadata["images"].items():
if "width" in v:
metadata["images"][k]["w"] = v["width"]
del metadata["images"][k]["width"]
if "height" in v:
metadata["images"][k]["h"] = v["height"]
del metadata["images"][k]["height"]
if "tags" not in v:
metadata["images"][k]["tags"] = []
if "exifdata" not in v:
metadata["images"][k]["exifdata"] = None
if "xmp" not in v:
metadata["images"][k]["xmp"] = None
if "title" not in v:
metadata["images"][k]["title"] = v["name"]
return Metadata.from_dict(metadata)
def update_metadata(metadata: dict[str, dict[str, Any]], folder: str) -> None:
def update_metadata(metadata: Metadata, folder: str) -> None:
"""
Updates the metadata JSON file.
@@ -114,14 +140,14 @@ def update_metadata(metadata: dict[str, dict[str, Any]], folder: str) -> None:
if metadata:
with open(metadata_path, "w", encoding="utf-8") as metadatafile:
logger.info("writing metadata file", extra={"file": metadata_path})
metadatafile.write(json.dumps(metadata, indent=4))
metadatafile.write(json.dumps(metadata.to_dict(), indent=4))
else:
if os.path.exists(metadata_path):
logger.info("deleting empty metadata file", extra={"file": metadata_path})
os.remove(metadata_path)
def get_image_info(item: str, folder: str) -> dict[str, Any]:
def get_image_info(item: str, folder: str) -> ImageMetadata:
"""
Extracts image information and EXIF data.
@@ -143,7 +169,7 @@ def get_image_info(item: str, folder: str) -> dict[str, Any]:
except UnidentifiedImageError:
logger.error("cannot identify image file", extra={"file": file})
print(f"cannot identify image file: {file}")
return {"width": None, "height": None, "tags": None, "exifdata": None, "xmp": None}
return ImageMetadata(w=None, h=None, tags=[], exifdata=None, xmp=None, src="", msrc="", name="", title="")
if exif:
logger.info("extracting EXIF data", extra={"file": file})
ifd = exif.get_ifd(ExifTags.IFD.Exif)
@@ -189,23 +215,72 @@ def get_image_info(item: str, folder: str) -> dict[str, Any]:
tags = xmpdata["xmpmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
xmp = xmpdata
except TypeError:
...
pass
except KeyError:
...
pass
try:
tags = xmpdata["xapmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
xmp = xmpdata
except TypeError:
...
pass
except KeyError:
...
if None in tags:
tags.remove(None)
return {"width": width, "height": height, "tags": tags, "exifdata": exifdata, "xmp": xmp}
pass
try:
tags = xmpdata["xmpmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
except TypeError:
pass
except KeyError:
pass
try:
tags = xmpdata["xapmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
except TypeError:
pass
except KeyError:
pass
sidecarfile = os.path.join(folder, item + ".xmp")
if os.path.exists(sidecarfile):
logger.info("xmp sidecar file found", extra={"file": sidecarfile})
try:
tags = get_tags(sidecarfile)
except Exception as e:
logger.error(e)
if None in tags: # type: ignore
tags.remove(None) # type: ignore
if not isinstance(tags, list):
tags = []
return ImageMetadata(w=width, h=height, tags=tags, exifdata=exifdata, xmp=xmp, src="", msrc="", name="", title="")
def nested_dict():
return defaultdict(nested_dict)
def insert_path(d, path):
for part in path[:-1]:
d = d[part]
last = path[-1]
if not isinstance(d[last], dict):
d[last] = {}
def finalize(d):
if isinstance(d, defaultdict):
return {k: finalize(d[k]) for k in sorted(d)}
return d or []
def parse_hierarchical_tags(tags, delimiter="|"):
tree = nested_dict()
for tag in tags:
parts = tag.split(delimiter)
insert_path(tree, parts)
return finalize(tree)
def get_tags(sidecarfile: str) -> list[str]:
@@ -228,23 +303,39 @@ def get_tags(sidecarfile: str) -> list[str]:
if isinstance(tags, str):
tags = [tags]
except TypeError:
...
pass
except KeyError:
...
pass
try:
tags = xmpdata["xapmeta"]["RDF"]["Description"]["subject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
except TypeError:
...
pass
except KeyError:
...
if None in tags:
tags.remove(None)
return tags
pass
try:
tags = xmpdata["xmpmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
except TypeError:
pass
except KeyError:
pass
try:
tags = xmpdata["xapmeta"]["RDF"]["Description"]["hierarchicalSubject"]["Bag"]["li"]
if isinstance(tags, str):
tags = [tags]
except TypeError:
pass
except KeyError:
pass
if None in tags: # type: ignore
tags.remove(None) # type: ignore
return tags # type: ignore
def process_image(item: str, folder: str, _args: Args, baseurl: str, metadata: dict[str, dict[str, int]], raw: list[str]) -> dict[str, Any]:
def process_image(item: str, folder: str, _args: Args, baseurl: str, metadata: Metadata, raw: list[str]) -> tuple[ImageMetadata, Metadata]:
"""
Processes an image and prepares its data for the HTML template.
@@ -260,26 +351,22 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, metadata: d
dict[str, Any]: dictionary containing image details for HTML rendering.
"""
extsplit = os.path.splitext(item)
if item not in metadata or _args.reread_metadata:
metadata[item] = get_image_info(item, folder)
sidecarfile = os.path.join(folder, item + ".xmp")
if os.path.exists(sidecarfile):
if item not in metadata.images or _args.reread_metadata:
metadata.images[item] = get_image_info(item, folder)
if _args.reread_sidecar and os.path.exists(sidecarfile):
logger.info("xmp sidecar file found", extra={"file": sidecarfile})
try:
metadata[item]["tags"] = get_tags(sidecarfile)
metadata.images[item].tags = get_tags(sidecarfile)
except Exception as e:
logger.error(e)
image = {
"url": f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}",
"thumbnail": f"{_args.web_root_url}.thumbnails/{baseurl}{urllib.parse.quote(item)}.jpg",
"name": item,
"width": metadata[item]["width"],
"height": metadata[item]["height"],
"tags": metadata[item]["tags"],
"exifdata": metadata[item].get("exifdata", ""),
"xmp": metadata[item].get("xmp", ""),
}
image = metadata.images[item]
image.src = f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}"
image.msrc = f"{_args.web_root_url}.thumbnails/{baseurl}{urllib.parse.quote(item)}.jpg"
image.name = item
image.title = item
path = os.path.join(_args.root_directory, ".thumbnails", baseurl, item + ".jpg")
if not os.path.exists(path) or _args.regenerate_thumbnails:
if os.path.exists(path):
@@ -292,14 +379,17 @@ def process_image(item: str, folder: str, _args: Args, baseurl: str, metadata: d
url = f"{_args.web_root_url}{baseurl}{urllib.parse.quote(extsplit[0])}{_raw}"
if _raw in (".tif", ".tiff"):
logger.info("tiff file found", extra={"file": file})
image["tiff"] = url
image.tiff = url
else:
logger.info("raw file found", extra={"file": file, "extension": _raw})
image["raw"] = url
return image
image.raw = url
metadata.images[item] = image
return image, metadata
def generate_html(folder: str, title: str, _args: Args, raw: list[str], version: str, logo) -> None:
def generate_html(folder: str, title: str, _args: Args, raw: list[str], version: str, logo: str) -> set[str]:
"""
Generates HTML content for a folder of images.
@@ -318,50 +408,63 @@ def generate_html(folder: str, title: str, _args: Args, raw: list[str], version:
items = sorted(os.listdir(folder))
contains_files = False
images = []
subfolders = []
images: list[ImageMetadata] = []
subfolders: list[SubfolderMetadata] = []
subfoldertags: set[str] = set()
foldername = folder.removeprefix(_args.root_directory)
foldername = f"{foldername}/" if foldername else ""
baseurl = urllib.parse.quote(foldername)
gone = [item for item in metadata.images if item not in items]
for gon in gone:
del metadata.images[gon]
create_thumbnail_folder(foldername, _args.root_directory)
logger.info("processing contents", extra={"folder": folder})
if not _args.non_interactive_mode:
for item in tqdm(items, total=len(items), desc=f"Getting image infos - {folder}", unit="files", ascii=True, dynamic_ncols=True):
if item not in EXCLUDES and not item.startswith("."):
for item in tqdm(items, total=len(items), desc=f"Getting image infos - {folder}", unit="files", ascii=True, dynamic_ncols=True, leave=False):
if item not in EXCLUDES and not item.startswith(".") and os.path.splitext(item)[1][1:].lower() not in _args.ignore_extensions:
if os.path.isdir(os.path.join(folder, item)):
process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo)
subfoldertags.update(process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo))
else:
contains_files = True
if os.path.splitext(item)[1].lower() in _args.file_extensions:
images.append(process_image(item, folder, _args, baseurl, metadata, raw))
img, metadata = process_image(item, folder, _args, baseurl, metadata, raw)
images.append(img)
if item == "info":
process_info_file(folder, item)
if item == "LICENSE":
process_license(folder, item)
else:
for item in items:
if item not in EXCLUDES and not item.startswith("."):
if item not in EXCLUDES and not item.startswith(".") and os.path.splitext(item)[1][1:].lower() not in _args.ignore_extensions:
if os.path.isdir(os.path.join(folder, item)):
process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo)
subfoldertags.update(process_subfolder(item, folder, baseurl, subfolders, _args, raw, version, logo))
else:
contains_files = True
if os.path.splitext(item)[1].lower() in _args.file_extensions:
images.append(process_image(item, folder, _args, baseurl, metadata, raw))
img, metadata = process_image(item, folder, _args, baseurl, metadata, raw)
images.append(img)
if item == "info":
process_info_file(folder, item)
if item == "LICENSE":
process_license(folder, item)
metadata.subfolders = subfolders
if _args.reverse_sort:
metadata.sort(reverse=True)
else:
metadata.sort()
update_metadata(metadata, folder)
if should_generate_html(images, contains_files, _args):
create_html_file(folder, title, foldername, images, subfolders, _args, version, logo)
subfoldertags = create_html_file(folder, title, foldername, images, subfolders, _args, version, logo, subfoldertags)
else:
if os.path.exists(os.path.join(folder, "index.html")):
logger.info("removing existing index.html", extra={"folder": folder})
os.remove(os.path.join(folder, "index.html"))
return subfoldertags
def create_thumbnail_folder(foldername: str, root_directory: str) -> None:
@@ -378,7 +481,7 @@ def create_thumbnail_folder(foldername: str, root_directory: str) -> None:
os.mkdir(thumbnails_path)
def process_subfolder(item: str, folder: str, baseurl: str, subfolders: list[dict[str, str | None]], _args: Args, raw: list[str], version: str, logo: str) -> None:
def process_subfolder(item: str, folder: str, baseurl: str, subfolders: list[SubfolderMetadata], _args: Args, raw: list[str], version: str, logo: str) -> set[str]:
"""
Processes a subfolder.
@@ -404,25 +507,28 @@ def process_subfolder(item: str, folder: str, baseurl: str, subfolders: list[dic
else:
thumb = f"{_args.web_root_url}.thumbnails/{baseurl}{urllib.parse.quote(item)}/{urllib.parse.quote(thumbitems[0])}.jpg"
subfolders.append({"url": subfolder_url, "name": item, "thumb": thumb})
if item not in _args.exclude_folders:
if not any(fnmatch.fnmatchcase(os.path.join(folder, item), exclude) for exclude in _args.exclude_folders):
generate_html(os.path.join(folder, item), os.path.join(folder, item).removeprefix(_args.root_directory), _args, raw, version, logo)
subfolders.append(SubfolderMetadata(url=subfolder_url, name=item, thumb=thumb, metadata=f"{_args.web_root_url}{baseurl}{urllib.parse.quote(item)}/.metadata.json"))
return generate_html(os.path.join(folder, item), os.path.join(folder, item).removeprefix(_args.root_directory), _args, raw, version, logo)
subfolders.append(SubfolderMetadata(url=subfolder_url, name=item, thumb=thumb))
return set()
def process_license(folder: str, item: str) -> None:
"""
Processes a LICENSE file.
Processes a LICENSE file, preserving formatting in HTML.
Args:
folder (str): The folder containing the info file.
item (str): The licenses file name.
folder (str): The folder containing the LICENSE file.
item (str): The LICENSE file name.
"""
with open(os.path.join(folder, item), encoding="utf-8") as f:
logger.info("processing LICENSE", extra={"path": os.path.join(folder, item)})
licens[urllib.parse.quote(folder)] = (
f.read().replace("\n", "</br>\n").replace(" ", "&emsp;").replace(" ", "&ensp;").replace("sp; ", "sp;&ensp;").replace("&ensp;&ensp;", "&emsp;")
)
path = os.path.join(folder, item)
with open(path, encoding="utf-8") as f:
logger.info("processing LICENSE", extra={"path": path})
raw_text = f.read()
escaped_text = html.escape(raw_text)
licens[urllib.parse.quote(folder)] = f"<pre>{escaped_text}</pre>"
def process_info_file(folder: str, item: str) -> None:
@@ -438,7 +544,7 @@ def process_info_file(folder: str, item: str) -> None:
info[urllib.parse.quote(folder)] = f.read()
def should_generate_html(images: list[dict[str, Any]], contains_files, _args: Args) -> bool:
def should_generate_html(images: list[ImageMetadata], contains_files, _args: Args) -> bool:
"""
Determines if HTML should be generated.
@@ -449,10 +555,17 @@ def should_generate_html(images: list[dict[str, Any]], contains_files, _args: Ar
Returns:
bool: True if HTML should be generated, False otherwise.
"""
return images or (_args.use_fancy_folders and not contains_files) or (_args.use_fancy_folders and _args.ignore_other_files)
return bool(images) or (_args.use_fancy_folders and not contains_files) or (_args.use_fancy_folders and _args.ignore_other_files)
def create_html_file(folder: str, title: str, foldername: str, images: list[dict[str, Any]], subfolders: list[dict[str, str]], _args: Args, version: str, logo: str) -> None:
def format_html(html: str) -> str:
soup = BeautifulSoup(html, "html5lib")
return soup.prettify()
def create_html_file(
folder: str, title: str, foldername: str, images: list[ImageMetadata], subfolders: list[SubfolderMetadata], _args: Args, version: str, logo: str, subfoldertags: set[str]
) -> set[str]:
"""
Creates the HTML file using the template.
@@ -485,14 +598,13 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
alltags = set()
for img in images:
for tag in img["tags"]:
alltags.add(tag)
alltags = sorted(alltags)
if img.tags:
alltags.update(img.tags)
alltags.update(subfoldertags)
folder_info = info.get(urllib.parse.quote(folder), "").split("\n")
_info = [i for i in folder_info if len(i) > 1] if folder_info else None
if _args.reverse_sort:
images.sort(key=lambda i: i["name"], reverse=True)
folder_license = licens.get(urllib.parse.quote(folder), False)
@@ -509,6 +621,7 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
favicon=f"{_args.web_root_url}{FAVICON_PATH}",
stylesheet=f"{_args.web_root_url}{GLOBAL_CSS_PATH}",
theme=f"{_args.web_root_url}.static/theme.css",
darktheme=f"{_args.web_root_url}.static/theme-dark.css" if _args.darktheme else None,
root=_args.web_root_url,
parent=f"{_args.web_root_url}{urllib.parse.quote(foldername)}",
header=f"{header} - LICENSE",
@@ -518,7 +631,7 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
logo=logo,
licensefile=folder_license,
)
f.write(content)
f.write(format_html(content))
html = env.get_template("index.html.j2")
content = html.render(
@@ -526,23 +639,25 @@ def create_html_file(folder: str, title: str, foldername: str, images: list[dict
favicon=f"{_args.web_root_url}{FAVICON_PATH}",
stylesheet=f"{_args.web_root_url}{GLOBAL_CSS_PATH}",
theme=f"{_args.web_root_url}.static/theme.css",
darktheme=f"{_args.web_root_url}.static/theme-dark.css" if _args.darktheme else None,
root=_args.web_root_url,
parent=parent,
header=header,
license=license_info,
subdirectories=subfolders,
images=images,
info=_info,
webmanifest=_args.generate_webmanifest,
version=version,
logo=logo,
licensefile=license_url,
tags=alltags,
tags=parse_hierarchical_tags(alltags),
)
with open(html_file, "w", encoding="utf-8") as f:
logger.info("writing html file", extra={"path": html_file})
f.write(content)
logger.info("writing formatted html file", extra={"path": html_file})
f.write(format_html(content))
return set(sorted(alltags))
def list_folder(folder: str, title: str, _args: Args, raw: list[str], version: str, logo: str) -> list[tuple[str, str, str]]:

View File

@@ -18,14 +18,10 @@ import gzip
import shutil
import logging
from datetime import datetime
from pythonjsonlogger import jsonlogger
from pythonjsonlogger import json as jsonlogger
# Constants for file paths and exclusions
if __package__ is None:
PACKAGE = ""
else:
PACKAGE = __package__
SCRIPTDIR = os.path.abspath(os.path.dirname(__file__).removesuffix(PACKAGE))
SCRIPTDIR = os.path.dirname(os.path.realpath(__file__)).removesuffix(__package__ if __package__ else "")
LOG_DIR = os.path.join(SCRIPTDIR, "logs")
LATEST_LOG_FILE = os.path.join(LOG_DIR, "latest.jsonl")
@@ -95,7 +91,7 @@ def setup_logger(level=logging.INFO):
"""
_logger = logging.getLogger(name="defaultlogger")
supported_keys = ["asctime", "created", "filename", "funcName", "levelname", "levelno", "lineno", "module", "msecs", "message", "name", "pathname", "process", "processName", "relativeCreated", "thread", "threadName", "taskName"]
supported_keys = ["asctime", "created", "filename", "funcName", "levelname", "levelno", "lineno", "module", "msecs", "message", "process", "processName", "relativeCreated", "thread", "threadName"]
custom_format = " ".join(log_format(supported_keys))
formatter = jsonlogger.JsonFormatter(custom_format)
@@ -121,7 +117,7 @@ def setup_consolelogger(level=logging.INFO):
"""
_logger = logging.getLogger(name="consolelogger")
supported_keys = ["asctime", "created", "filename", "funcName", "levelname", "levelno", "lineno", "module", "msecs", "message", "name", "pathname", "process", "processName", "relativeCreated", "thread", "threadName", "taskName"]
supported_keys = ["asctime", "created", "filename", "funcName", "levelname", "levelno", "lineno", "module", "msecs", "message", "process", "processName", "relativeCreated", "thread", "threadName"]
custom_format = " ".join(log_format(supported_keys))
formatter = jsonlogger.JsonFormatter(custom_format)

View File

@@ -15,14 +15,10 @@ except ImportError:
from modules.logger import logger
from modules.argumentparser import Args
from modules.css_color import extract_theme_color, extract_colorscheme
from modules.css_color import extract_colorscheme
# Define constants for static files directory and icon sizes
if __package__ is None:
PACKAGE = ""
else:
PACKAGE = __package__
SCRIPTDIR = os.path.abspath(os.path.dirname(__file__).removesuffix(PACKAGE))
SCRIPTDIR = os.path.dirname(os.path.realpath(__file__)).removesuffix(__package__ if __package__ else "")
STATIC_FILES_DIR = os.path.join(SCRIPTDIR, "files")
ICON_SIZES = ["36x36", "48x48", "72x72", "96x96", "144x144", "192x192", "512x512"]
@@ -146,16 +142,16 @@ def render_manifest_json(_args: Args, icon_list: list[Icon], colors: dict[str, s
colors : dict[str, str]
dictionary containing color scheme and theme color.
"""
manifest = env.get_template("manifest.json.j2")
manifest = env.get_template("manifest.webmanifest.j2")
content = manifest.render(
name=_args.web_root_url.replace("https://", "").replace("http://", "").replace("/", ""),
short_name=_args.site_title,
icons=icon_list,
background_color=colors["bcolor1"],
theme_color=colors["theme_color"],
theme_color=colors["color1"],
)
with open(os.path.join(_args.root_directory, ".static", "manifest.json"), "w", encoding="utf-8") as f:
logger.info("rendering manifest.json", extra={"path": os.path.join(_args.root_directory, ".static", "manifest.json")})
with open(os.path.join(_args.root_directory, ".static", "manifest.webmanifest"), "w", encoding="utf-8") as f:
logger.info("rendering manifest.webmanifest", extra={"path": os.path.join(_args.root_directory, ".static", "manifest.webmanifest")})
f.write(content)
@@ -266,5 +262,4 @@ def webmanifest(_args: Args) -> None:
return
colorscheme = extract_colorscheme(os.path.join(_args.root_directory, ".static", "theme.css"))
colorscheme["theme_color"] = extract_theme_color(os.path.join(_args.root_directory, ".static", "theme.css"))
render_manifest_json(_args, icon_list, colorscheme)

View File

@@ -1,9 +1,12 @@
CairoSVG==2.7.1
defusedxml==0.7.1
Jinja2==3.1.5
Pillow==11.1.0
pyinstaller==6.11.1
python_json_logger==2.0.7
rich_argparse==1.7.0
selenium==4.28.1
tqdm==4.66.4
beautifulsoup4~=4.14.3
CairoSVG~=2.7.1
ConfigArgParse~=1.7.1
defusedxml~=0.7.1
html5lib~=1.1
Jinja2~=3.1.6
jsmin~=3.0.1
Pillow~=12.1.0
python_json_logger~=2.0.7
rich_argparse~=1.7.2
selenium~=4.40.0
tqdm~=4.66.6

View File

@@ -52,7 +52,11 @@
background-color: var(--color3);
}
.tagentry:hover {
.tagentry label:hover {
background-color: var(--color4);
}
.tagentry .tagtoggle:hover {
background-color: var(--color4);
}
@@ -70,6 +74,27 @@
font-weight: 800;
}
.loader {
width: 48px;
height: 48px;
border-radius: 50%;
display: inline-block;
border-top: 3px solid var(--bcolor1);
border-right: 3px solid transparent;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
body {
color: var(--bcolor1);
background-color: var(--color4);

View File

@@ -52,7 +52,11 @@
background-color: var(--color2);
}
.tagentry:hover {
.tagentry label:hover {
background-color: var(--color4);
}
.tagentry .tagtoggle:hover {
background-color: var(--color4);
}
@@ -70,6 +74,27 @@
font-weight: 800;
}
.loader {
width: 48px;
height: 48px;
border-radius: 50%;
display: inline-block;
border-top: 3px solid var(--bcolor2);
border-right: 3px solid transparent;
box-sizing: border-box;
animation: rotation 1s linear infinite;
}
@keyframes rotation {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
body {
color: var(--bcolor2);
background-color: var(--bcolor1);

498
templates/functionality.js Normal file
View File

@@ -0,0 +1,498 @@
class PhotoGallery {
constructor() {
this.pswpElement = document.querySelector(".pswp");
this.items = [];
this.shown = [];
this.subfolders = [];
this.controllers = {};
this.tagDropdownShown = false;
this.debounce = this.debounce.bind(this);
this.openSwipe = this.openSwipe.bind(this);
this.prefetch = this.prefetch.bind(this);
this.cancel = this.cancel.bind(this);
this.reset = this.reset.bind(this);
this.recursive = this.recursive.bind(this);
this.requestMetadata = this.requestMetadata.bind(this);
this.filter = this.filter.bind(this);
this.updateImageList = this.updateImageList.bind(this);
this.setFilter = this.setFilter.bind(this);
this.toggleTag = this.toggleTag.bind(this);
this.setupDropdownToggle = this.setupDropdownToggle.bind(this);
this.setupTagHandlers = this.setupTagHandlers.bind(this);
this.setupClickHandlers = this.setupClickHandlers.bind(this);
this.scrollFunction = this.scrollFunction.bind(this);
this.topFunction = this.topFunction.bind(this);
this.onLoad = this.onLoad.bind(this);
this.darkMode = this.darkMode.bind(this);
this.lightMode = this.lightMode.bind(this);
this.darkModeToggle = this.darkModeToggle.bind(this);
this.detectDarkMode = this.detectDarkMode.bind(this);
this.init();
}
debounce(fn, delay) {
let timeoutId;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => fn.apply(this, args), delay);
};
}
openSwipe(imgIndex) {
const options = { index: imgIndex };
const gallery = new PhotoSwipe(this.pswpElement, PhotoSwipeUI_Default, this.shown, options);
gallery.init();
}
prefetch(imgIndex) {
if (this.controllers[imgIndex]) {
this.cancel(imgIndex);
}
const controller = new AbortController();
const signal = controller.signal;
this.controllers[imgIndex] = controller;
const urlToFetch = this.shown[imgIndex]?.src;
if (urlToFetch) {
fetch(urlToFetch, { method: "GET", signal }).catch(() => {});
}
}
cancel(imgIndex) {
if (this.controllers[imgIndex]) {
this.controllers[imgIndex].abort();
delete this.controllers[imgIndex];
}
}
reset() {
const content = document.documentElement.innerHTML;
const title = document.title;
const folders = document.querySelector(".folders");
let path = window.location.origin + window.location.pathname;
if (path.startsWith("null")) {
path = window.location.protocol + "//" + path.substring(4);
}
if (folders) folders.style.display = "";
document.getElementById("recursive").checked = false;
document.querySelectorAll("#tagdropdown input.tagcheckbox:checked").forEach((checkbox) => (checkbox.checked = false));
window.history.replaceState({ html: content, pageTitle: title }, "", path);
this.requestMetadata();
}
showLoader() {
const imagelist = document.getElementById("imagelist");
imagelist.innerHTML = '<span class="loader"></span>';
imagelist.classList.add("centerload");
imagelist.classList.remove("row");
}
async recursive() {
this.showLoader();
const loc = new URL(window.location.href);
const content = document.documentElement.innerHTML;
const title = document.title;
const isChecked = document.getElementById("recursive")?.checked;
const folders = document.querySelector(".folders");
if (!isChecked) {
if (folders) folders.style.display = "";
loc.searchParams.delete("recursive");
window.history.replaceState({ html: content, pageTitle: title }, "", loc);
this.requestMetadata();
return;
}
if (folders) folders.style.display = "none";
loc.searchParams.delete("recursive");
loc.searchParams.append("recursive", true);
window.history.replaceState({ html: content, pageTitle: title }, "", loc);
const visited = new Set();
const existingItems = new Set();
const newItems = [];
try {
const response = await fetch(".metadata.json");
if (!response.ok) throw new Error("Failed to fetch metadata");
const data = await response.json();
this.items = [];
this.subfolders = data.subfolders || [];
for (const image of Object.values(data.images || {})) {
newItems.push(image);
existingItems.add(image.src);
}
} catch {
return;
}
const fetchFoldersRecursively = async (folderList) => {
if (!Array.isArray(folderList)) return;
const nextLevel = [];
await Promise.all(
folderList.map(async (folder) => {
if (!folder || !folder.metadata || visited.has(folder.url)) return;
visited.add(folder.url);
try {
const response = await fetch(folder.metadata);
if (!response.ok) throw new Error();
const data = await response.json();
for (const image of Object.values(data.images || {})) {
if (!existingItems.has(image.src)) {
newItems.push(image);
existingItems.add(image.src);
}
}
if (Array.isArray(data.subfolders)) nextLevel.push(...data.subfolders);
} catch {}
}),
);
if (nextLevel.length > 0) await fetchFoldersRecursively(nextLevel);
};
await fetchFoldersRecursively(this.subfolders);
this.items = [...newItems];
this.filter();
}
requestMetadata() {
this.showLoader();
const hash = window.location.hash;
const searchParams = new URLSearchParams(window.location.search);
fetch(".metadata.json")
.then((response) => {
if (!response.ok) throw new Error("Failed to fetch metadata");
return response.json();
})
.then((data) => {
this.items = Object.values(data.images || {});
this.subfolders = data.subfolders || [];
if (hash != "") {
const selected = hash.replace("#", "").split(",");
this.setFilter(selected);
}
if (searchParams.get("recursive") != null) {
const recChk = document.getElementById("recursive");
if (recChk) recChk.checked = true;
this.recursive();
} else {
this.filter();
}
})
.catch(() => {});
}
filter() {
const searchParams = new URLSearchParams(window.location.search);
this.shown = [];
let path = decodeURIComponent(window.location.origin + window.location.pathname.replace("index.html", ""));
if (path.startsWith("null")) {
path = window.location.protocol + "//" + path.substring(4);
}
const selectedTags = [];
document.querySelectorAll("#tagdropdown input.tagcheckbox:checked").forEach((checkbox) => {
let tag = checkbox.parentElement.id.trim().substring(1);
if (checkbox.parentElement.parentElement.children.length > 1) tag += "|";
selectedTags.push(tag);
});
const urltags = selectedTags.join(",");
let isRecursiveChecked = false;
try {
isRecursiveChecked = document.getElementById("recursive")?.checked || false;
} catch {}
for (const item of this.items) {
const tags = item.tags || [];
const include = selectedTags.every((selected) => {
const isParent = selected.endsWith("|");
return isParent ? tags.some((t) => t.startsWith(selected)) : tags.includes(selected);
});
if (include || selectedTags.length === 0) {
if (!isRecursiveChecked) {
if (decodeURIComponent(item.src).replace(item.name, "") === path) {
this.shown.push(item);
}
} else {
this.shown.push(item);
}
}
}
this.updateImageList();
window.location.hash = urltags;
const pid = searchParams.get("pid") - 1;
if (pid != -1) {
this.openSwipe(pid);
}
}
insertPath(obj, path) {
let current = obj;
for (let i = 0; i < path.length; i++) {
const part = path[i];
if (i === path.length - 1) {
if (!current[part]) {
current[part] = null;
}
} else {
if (!current[part] || typeof current[part] !== "object") {
current[part] = {};
}
current = current[part];
}
}
}
finalize(obj) {
if (typeof obj === "object" && obj !== null && !Array.isArray(obj)) {
const result = {};
Object.keys(obj)
.sort()
.forEach((key) => {
if (obj[key] === null) {
result[key] = [];
} else {
result[key] = this.finalize(obj[key]);
}
});
return result;
}
return obj || [];
}
parseHierarchicalTags(tags, delimiter = "|") {
const tree = {};
for (const tag of tags) {
const parts = tag.split(delimiter);
this.insertPath(tree, parts);
}
return this.finalize(tree);
}
renderTree = (obj, depth = 0) => {
let lines = [];
const indent = "&nbsp;&nbsp;".repeat(depth);
for (const key of Object.keys(obj)) {
lines.push(indent + key);
if (Array.isArray(obj[key])) {
for (const val of obj[key]) {
lines.push("&nbsp;&nbsp;".repeat(depth + 1) + val);
}
} else if (typeof obj[key] === "object" && obj[key] !== null) {
lines = lines.concat(this.renderTree(obj[key], depth + 1));
}
}
return lines.join("\n");
};
updateImageList() {
const imagelist = document.getElementById("imagelist");
if (!imagelist) return;
let str = "";
this.shown.forEach((item, index) => {
let tags = this.parseHierarchicalTags(item.tags || []);
str += `<div class="column"><figure title="${this.renderTree(tags)}"><img src="${
item.msrc
}" data-index="${index}" /><figcaption class="caption">${item.name}`;
if (item.tiff) str += `&nbsp;<a href="${item.tiff}">TIFF</a>`;
if (item.raw) str += `&nbsp;<a href="${item.raw}">RAW</a>`;
str += "</figcaption></figure></div>";
});
imagelist.classList.add("row");
imagelist.classList.remove("centerload");
imagelist.innerHTML = str;
}
setFilter(selected) {
document.querySelectorAll("#tagdropdown input.tagcheckbox").forEach((checkbox) => {
selected.forEach((tag) => {
if (checkbox.parentElement.id.trim().substring(1).replace(" ", "%20") === tag) {
checkbox.checked = true;
}
});
});
}
toggleTag(tagid) {
const tag = document.getElementById(tagid);
const ol = tag?.closest(".tagentry")?.querySelector(".tagentryparent");
const svg = tag?.parentElement.querySelector(".tagtoggle svg");
if (!ol || !svg) return;
ol.classList.toggle("show");
svg.style.transform = ol.classList.contains("show") ? "rotate(180deg)" : "rotate(0deg)";
}
setupDropdownToggle() {
const toggleLink = document.getElementById("tagtogglelink");
const dropdown = document.getElementById("tagdropdown");
if (!toggleLink) return;
toggleLink.addEventListener("click", (event) => {
event.stopPropagation();
const svg = toggleLink.querySelector("svg");
dropdown.classList.toggle("show");
if (svg) svg.style.transform = dropdown.classList.contains("show") ? "rotate(180deg)" : "rotate(0deg)";
this.tagDropdownShown = dropdown.classList.contains("show");
});
document.addEventListener("click", (event) => {
if (!dropdown.contains(event.target) && !toggleLink.contains(event.target)) {
dropdown.classList.remove("show");
this.tagDropdownShown = false;
const svg = toggleLink.querySelector("svg");
if (svg) svg.style.transform = "rotate(0deg)";
}
});
}
setupTagHandlers() {
const tagContainer = document.getElementById("tagdropdown");
if (!tagContainer) return;
const debouncedFilter = this.debounce(this.filter, 150);
tagContainer.addEventListener("change", debouncedFilter);
tagContainer.addEventListener("click", (event) => {
const toggle = event.target.closest(".tagtoggle");
if (toggle) {
event.stopPropagation();
const tagid = toggle.dataset.toggleid;
this.toggleTag(tagid);
}
});
}
setupClickHandlers() {
const resetEl = document.getElementById("reset-filter")?.querySelector("label");
if (resetEl) resetEl.addEventListener("click", this.reset);
const recurseEl = document.getElementById("recursive");
if (recurseEl) recurseEl.addEventListener("change", this.debounce(this.recursive, 150));
const totop = document.getElementById("totop");
if (totop) totop.addEventListener("click", this.topFunction);
const darkModeSwitch = document.getElementById("dark-mode-switch");
if (darkModeSwitch) darkModeSwitch.addEventListener("click", this.darkModeToggle);
const imagelist = document.getElementById("imagelist");
if (imagelist) {
imagelist.addEventListener("click", (event) => {
const img = event.target.closest("img");
if (!img || !img.dataset.index) return;
const index = parseInt(img.dataset.index);
if (!isNaN(index)) this.openSwipe(index);
});
imagelist.addEventListener("mouseover", (event) => {
const img = event.target.closest("img");
if (!img || !img.dataset.index) return;
const index = parseInt(img.dataset.index);
if (!isNaN(index)) this.prefetch(index);
});
imagelist.addEventListener("mouseleave", (event) => {
const img = event.target.closest("img");
if (!img || !img.dataset.index) return;
const index = parseInt(img.dataset.index);
if (!isNaN(index)) this.cancel(index);
});
}
}
scrollFunction() {
const totopbutton = document.getElementById("totop");
if (!totopbutton) return;
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
totopbutton.style.display = "block";
} else {
totopbutton.style.display = "none";
}
}
topFunction() {
window.scrollTo({ top: 0, behavior: "smooth" });
}
darkMode() {
const themeLink = document.getElementById("theme");
const darkThemeLink = document.getElementById("darktheme");
if (themeLink) themeLink.disabled = true;
if (darkThemeLink) darkThemeLink.disabled = false;
}
lightMode() {
const themeLink = document.getElementById("theme");
const darkThemeLink = document.getElementById("darktheme");
if (themeLink) themeLink.disabled = false;
if (darkThemeLink) darkThemeLink.disabled = true;
}
darkModeToggle(mode) {
const switchState = document.getElementById("dark-mode-switch-check");
if (mode == "dark") {
this.darkMode();
if (switchState) {
switchState.checked = true;
}
} else if (mode == "light") {
this.lightMode();
if (switchState) {
switchState.checked = false;
}
} else {
if (switchState.checked) {
switchState.checked = false;
this.lightMode();
} else {
switchState.checked = true;
this.darkMode();
}
}
}
detectDarkMode() {
if (document.getElementById("darktheme")) {
if (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) {
this.darkModeToggle("dark");
} else {
this.darkModeToggle("light");
}
}
}
onLoad() {
document.querySelectorAll(".tagtoggle").forEach((toggle) => {
toggle.addEventListener("mouseup", (event) => {
event.stopPropagation();
const tagid = toggle.getAttribute("data-tagid");
this.toggleTag(tagid);
});
});
this.requestMetadata();
this.setupDropdownToggle();
this.setupTagHandlers();
this.setupClickHandlers();
this.detectDarkMode();
window.addEventListener("scroll", this.scrollFunction);
}
init() {
if (window.addEventListener) {
window.addEventListener("load", this.onLoad, false);
} else if (window.attachEvent) {
window.attachEvent("onload", this.onLoad);
}
}
}

View File

@@ -1,3 +1,25 @@
{%- macro render_tags(tag_tree, parent) -%}
{%- for key, value in tag_tree.items() %}
<li class="tagentry">
<div class="tagflex">
<label class="tag" title="{{ key }}" id="{{ parent }}|{{ key }}">
<input type="checkbox" class="tagcheckbox" />{{ key }}
</label>{% if value %} <span class="tagtoggle" data-tagid="{{ parent }}|{{ key }}">
<svg width="1em" height="1em" viewBox="0 0 129.87601 129.87624">
<g id="layer1" transform="translate(-33.816833,-52.685642)">
<path stroke="currentColor" style="fill:none;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 54.476483,95.484647 98.754836,139.76308 143.03319,95.484647" id="path1" />
</g>
</svg></span>{% endif %}
</div>
{%- if value %}
<ol class="tagentryparent">
{{ render_tags(value, parent + '|' + key) }}
</ol>
{%- endif %}
</li><br>
{%- endfor %}
{%- endmacro -%}
<!DOCTYPE html>
<html lang="en">
@@ -6,27 +28,33 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
{%- if webmanifest %}
<link rel="manifest" href="/.static/manifest.json">
<link rel="manifest" href="/.static/manifest.webmanifest">
{%- endif %}
<link rel="preload" href="{{ stylesheet }}" as="style">
{%- if theme %}
<link rel="preload" href="{{ theme }}" as="style">
{%- endif %}
{%- if darktheme %}
<link rel="preload" href="{{ darktheme }}" as="style">
{%- endif %}
<link rel="icon" type="image/x-icon" href="{{ favicon }}">
<link rel="stylesheet" href="{{ stylesheet }}">
{%- if theme %}
<link rel="stylesheet" href="{{ theme }}">
<link rel="stylesheet" href="{{ theme }}" id="theme">
{%- endif %}
{%- if darktheme %}
<link rel="stylesheet" href="{{ darktheme }}" id="darktheme" disabled>
{%- endif %}
<link rel="preload" href="{{ root }}.static/pswp/photoswipe.css" as="style">
<link rel="preload" href="{{ root }}.static/pswp/default-skin/default-skin.css" as="style">
<link rel="modulepreload" href="{{ root }}.static/pswp/photoswipe.min.js">
<link rel="modulepreload" href="{{ root }}.static/pswp/photoswipe-ui-default.min.js">
{%- if images %}
<link rel="modulepreload" href="{{ root }}.static/functionality.min.js">
<link rel="stylesheet" href="{{ root }}.static/pswp/photoswipe.css">
<link rel="stylesheet" href="{{ root }}.static/pswp/default-skin/default-skin.css">
<script src="{{ root }}.static/pswp/photoswipe.min.js"></script>
<script src="{{ root }}.static/pswp/photoswipe-ui-default.min.js"></script>
{%- endif %}
<script src="{{ root }}.static/functionality.min.js"></script>
</head>
<body>
@@ -49,24 +77,43 @@
<li class="title"><span class="header">{{ header }}</span></li>
</div>
<div class="navright">
{%- if tags|length > 0 %}
<li class="tooltip"><a>Filter by Tags</a>
{% if tags %}
<li class="tooltip">
<a id="tagtogglelink">Filter by Tags <svg width="0.8em" height="0.8em" viewBox="0 0 129.87601 129.87624">
<g id="layer1" transform="translate(-33.816833,-52.685642)">
<path stroke="currentColor" style="fill:none;stroke-width:20;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 54.476483,95.484647 98.754836,139.76308 143.03319,95.484647" id="path1" />
</g>
</svg></a>
<ol class="tooltiptext tagdropdown" id="tagdropdown">
{%- for tag in tags -%}
<li class="tagentry"><label onclick="filter()"><input type="checkbox" />{{ tag }}</label></li><br />
{%- endfor -%}
<span class="tagentry" id="reset-filter"><label>reset filter</label></span>
<span class="tagentry">
<label>
<input type="checkbox" id="recursive" />recursive filter
</label>
</span>
{{ render_tags(tags, '') }}
</ol>
</li>
{%- endif %}
{%- if licensefile %}
<li class="license"><a href="{{ licensefile }}">License</a></li>
{%- endif %}
{%- if darktheme %}
<li class="darkmodeswitch">
<a class="button" id="dark-mode-switch">
<input type="checkbox" class="checkbox" id="dark-mode-switch-check" />
<div class="knobs">
<span class="light">☀︎</span>
<span class="slider"></span>
<span class="dark">☽</span>
</div>
</a>
</li>
{%- endif %}
</div>
</ol>
{% if subdirectories %}
{%- for subdirectory in subdirectories %}
<link rel="preload" href="{{ subdirectory.url }}/index.html" type="text/html">
{%- endfor %}
<div class="folders">
{%- for subdirectory in subdirectories %}
<a href="{{ subdirectory.url }}">
@@ -82,11 +129,8 @@
</div>
{%- endif %}
</div>
{% if images %}
{%- set ns = namespace(count = 0) -%}
<div class="row" id="imagelist">
</div>
{%- endif %}
{% if license %}
{%- if 'CC' in license.type %}
<div class="footer" xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/">
@@ -94,7 +138,7 @@
<a property="dct:title" rel="cc:attributionURL" href="{{ root }}">{{ license.project }}</a> by <span property="cc:attributionName">{{ license.author }}</span> is marked with
<a href="{{ license.url }}" target="_blank" rel="license noopener noreferrer" style="display: inline-block">CC0 1.0
{%- for pic in license.pics %}
<img style="height: 22px !important; margin-left: 3px; vertical-align: text-bottom" src="{{ pic }}" alt="" />
<img src="{{ pic }}" alt="" />
{%- endfor %}
</a>
{%- else %}
@@ -107,17 +151,16 @@
{%- endif %}
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
<button type="button" id="totop" title="Back to Top">Back to Top</button>
</div>
{%- endif %}
{%- else %}
<div class="footer">
<span class="attribution">Made with <a href="https://github.com/greflm13/StaticGalleryBuilder" target="_blank" rel="noopener noreferrer">StaticGalleryBuilder {{ version }}</a> by <a
href="https://github.com/greflm13" target="_blank" rel="noopener noreferrer">{{ logo }}</a>.</span>
<button type="button" onclick="topFunction()" id="totop" title="Back to Top">Back to Top</button>
<button type="button" id="totop" title="Back to Top">Back to Top</button>
</div>
{%- endif %}
{% if images %}
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">
<div class="pswp__bg"></div>
<div class="pswp__scroll-wrap">
@@ -154,114 +197,9 @@
</div>
</div>
</div>
<script>
var pswpElement = document.querySelectorAll('.pswp')[0];
var items = [
{%- for image in images %}
{%- if image.exifdata.DateTime %}
{ name: "{{ image.name }}", tiff: "{{ image.tiff }}", raw: "{{ image.raw }}", src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}", tags: "{{ image.tags }}", title: "Captured: {{ image.exifdata.DateTime }}" },
{%- else %}
{ name: "{{ image.name }}", tiff: "{{ image.tiff }}", raw: "{{ image.raw }}", src: "{{ image.url }}", w: {{ image.width }}, h: {{ image.height }}, msrc: "{{ image.thumbnail }}", tags: "{{ image.tags }}" },
{%- endif %}
{%- endfor %}
];
var shown = [];
var re = /pid=(\d+)/;
var controllers = {}
function openSwipe(img) {
var options = {
index: img
};
var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, shown, options);
gallery.init();
}
let totopbutton = document.getElementById("totop");
window.onscroll = function () { scrollFunction() };
function scrollFunction() {
if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 20) {
totopbutton.style.display = "block";
} else {
totopbutton.style.display = "none";
}
}
function topFunction() {
window.scrollTo({ top: 0, behavior: 'smooth' })
}
window.onload = function () { filter() };
function updateImageList() {
var str = ""
var imagelist = document.getElementById("imagelist");
shown.forEach((item, index) => {
str += '<div class="column"><figure><img src="' + item.msrc + '" onclick="openSwipe(' + index + ')" onmouseover="prefetch(' + index + ')" onmouseleave="cancel(' + index + ')" /><figcaption class="caption">' + item.name;
if (item.tiff != "") {
str += ' <a href="' + item.tiff + '">TIFF</a>';
}
if (item.raw != "") {
str += ' <a href="' + item.raw + '">RAW</a>';
}
str += '</figcaption></figure></div>';
});
imagelist.innerHTML = str;
}
function prefetch(img) {
const controller = new AbortController()
const signal = controller.signal
controllers[img] = controller;
let urlToFetch = items[img].src;
fetch(urlToFetch, {
method: 'get',
signal: signal,
}).catch(function (err) { });
}
function cancel(img) {
controllers[img].abort();
delete controllers[img];
}
function filter() {
var selected_tags = [];
var tagdropdown, tags, incl;
shown = [];
tagdropdown = document.getElementById("tagdropdown").getElementsByTagName("li");
for (var i = 0; i < tagdropdown.length; i++) {
if (tagdropdown[i].firstChild.firstChild.checked) {
selected_tags.push([tagdropdown[i].innerText])
}
}
items.forEach((item, index) => {
tags = item.tags;
incl = true;
selected_tags.forEach((tag) => {
if (tags.indexOf(tag) == -1) {
incl = false;
}
});
if (incl | selected_tags == []) {
shown.push(item)
}
});
updateImageList();
if (re.test(window.location.href)) {
var pid = window.location.href.match(re)[1];
console.log(pid);
console.log(shown);
openSwipe(parseInt(pid));
}
}
</script>
{%- endif %}
</body>
<script>
new PhotoGallery();
</script>
</html>

View File

@@ -21,13 +21,16 @@
<body>
<div class="header">
<ul class="navbar">
<li><a href="{{ root }}">Home</a></li>
{%- if parent %}
<li><a href="{{ parent }}">Parent Directory</a></li>
{%- endif %}
<li class="title"><span class="header">{{ header }}</span></li>
</ul>
<ol class="navbar">
<div class="navleft">
<li><a href="{{ root }}">Home</a></li>
{%- if parent %}
<li><a href="{{ parent }}">Parent Directory</a></li>
{%- endif %}
</div>
<div class="navcenter">
<li class="title"><span class="header">{{ header }}</span></li>
</div>
</div>
{%- if licensefile %}
<div class="licensefile">

View File

@@ -2,6 +2,6 @@
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC00009.jpg">
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>aqueduct</rdf:li><rdf:li>arch</rdf:li><rdf:li>arch bridge</rdf:li><rdf:li>bridge</rdf:li><rdf:li>hillside</rdf:li><rdf:li>passenger train</rdf:li><rdf:li>railroad</rdf:li><rdf:li>railroad bridge</rdf:li><rdf:li>span</rdf:li><rdf:li>train track</rdf:li><rdf:li>tree</rdf:li><rdf:li>viaduct</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|aqueduct</rdf:li><rdf:li>|arch</rdf:li><rdf:li>|arch bridge</rdf:li><rdf:li>|bridge</rdf:li><rdf:li>|hillside</rdf:li><rdf:li>|passenger train</rdf:li><rdf:li>|railroad</rdf:li><rdf:li>|railroad bridge</rdf:li><rdf:li>|span</rdf:li><rdf:li>|train track</rdf:li><rdf:li>|tree</rdf:li><rdf:li>|viaduct</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>aqueduct</rdf:li><rdf:li>arch</rdf:li><rdf:li>arch bridge</rdf:li><rdf:li>bridge</rdf:li><rdf:li>hillside</rdf:li><rdf:li>passenger train</rdf:li><rdf:li>railroad</rdf:li><rdf:li>railroad bridge</rdf:li><rdf:li>span</rdf:li><rdf:li>train track</rdf:li><rdf:li>tree</rdf:li><rdf:li>viaduct</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|aqueduct</rdf:li><rdf:li>st|arch</rdf:li><rdf:li>st|arch bridge</rdf:li><rdf:li>st|bridge</rdf:li><rdf:li>st|hillside</rdf:li><rdf:li>st|passenger train</rdf:li><rdf:li>st|railroad</rdf:li><rdf:li>st|railroad bridge</rdf:li><rdf:li>st|span</rdf:li><rdf:li>st|train track</rdf:li><rdf:li>st|tree</rdf:li><rdf:li>st|viaduct</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@@ -1,7 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC03470.JPG">
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>bus</rdf:li><rdf:li>illuminate</rdf:li><rdf:li>neon</rdf:li><rdf:li>passenger train</rdf:li><rdf:li>neon light</rdf:li><rdf:li>night</rdf:li><rdf:li>sign</rdf:li><rdf:li>train car</rdf:li><rdf:li>trolley</rdf:li><rdf:li>window</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|bus</rdf:li><rdf:li>|illuminate</rdf:li><rdf:li>|neon</rdf:li><rdf:li>|neon light</rdf:li><rdf:li>|night</rdf:li><rdf:li>|sign</rdf:li><rdf:li>|train car</rdf:li><rdf:li>|trolley</rdf:li><rdf:li>|window</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
</rdf:RDF>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmpMM:DerivedFrom="DSC03470.JPG">
<dc:subject>
<rdf:Bag>
<rdf:li>st</rdf:li>
<rdf:li>bus</rdf:li>
<rdf:li>illuminate</rdf:li>
<rdf:li>neon</rdf:li>
<rdf:li>neon light</rdf:li>
<rdf:li>night</rdf:li>
<rdf:li>sign</rdf:li>
<rdf:li>train car</rdf:li>
<rdf:li>trolley</rdf:li>
<rdf:li>window</rdf:li>
<rdf:li>train</rdf:li>
</rdf:Bag>
</dc:subject>
<lr:hierarchicalSubject>
<rdf:Bag>
<rdf:li>st|bus</rdf:li>
<rdf:li>st|illuminate</rdf:li>
<rdf:li>st|neon</rdf:li>
<rdf:li>st|neon light</rdf:li>
<rdf:li>st|night</rdf:li>
<rdf:li>st|sign</rdf:li>
<rdf:li>st|train car</rdf:li>
<rdf:li>st|trolley</rdf:li>
<rdf:li>st|window</rdf:li>
<rdf:li>st|train</rdf:li>
</rdf:Bag>
</lr:hierarchicalSubject>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@@ -2,6 +2,6 @@
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC03508.ARW">
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>building</rdf:li><rdf:li>ceiling</rdf:li><rdf:li>pillar</rdf:li><rdf:li>display</rdf:li><rdf:li>rail</rdf:li><rdf:li>steam engine</rdf:li><rdf:li>steam locomotive</rdf:li><rdf:li>train</rdf:li><rdf:li>train car</rdf:li><rdf:li>train track</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|building</rdf:li><rdf:li>|ceiling</rdf:li><rdf:li>|pillar</rdf:li><rdf:li>|display</rdf:li><rdf:li>|rail</rdf:li><rdf:li>|steam engine</rdf:li><rdf:li>|steam locomotive</rdf:li><rdf:li>|train</rdf:li><rdf:li>|train car</rdf:li><rdf:li>|train track</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>building</rdf:li><rdf:li>ceiling</rdf:li><rdf:li>pillar</rdf:li><rdf:li>display</rdf:li><rdf:li>rail</rdf:li><rdf:li>steam engine</rdf:li><rdf:li>steam locomotive</rdf:li><rdf:li>train</rdf:li><rdf:li>train car</rdf:li><rdf:li>train track</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|building</rdf:li><rdf:li>st|ceiling</rdf:li><rdf:li>st|pillar</rdf:li><rdf:li>st|display</rdf:li><rdf:li>st|rail</rdf:li><rdf:li>st|steam engine</rdf:li><rdf:li>st|steam locomotive</rdf:li><rdf:li>st|train</rdf:li><rdf:li>st|train car</rdf:li><rdf:li>st|train track</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@@ -1,7 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC03508.JPG">
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>attach</rdf:li><rdf:li>basement</rdf:li><rdf:li>beam</rdf:li><rdf:li>building</rdf:li><rdf:li>ceiling</rdf:li><rdf:li>equipment</rdf:li><rdf:li>floor</rdf:li><rdf:li>pipe</rdf:li><rdf:li>red</rdf:li><rdf:li>room</rdf:li><rdf:li>scaffold</rdf:li><rdf:li>tube</rdf:li><rdf:li>warehouse</rdf:li><rdf:li>water pipe</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|attach</rdf:li><rdf:li>|basement</rdf:li><rdf:li>|beam</rdf:li><rdf:li>|building</rdf:li><rdf:li>|ceiling</rdf:li><rdf:li>|equipment</rdf:li><rdf:li>|floor</rdf:li><rdf:li>|pipe</rdf:li><rdf:li>|red</rdf:li><rdf:li>|room</rdf:li><rdf:li>|scaffold</rdf:li><rdf:li>|tube</rdf:li><rdf:li>|warehouse</rdf:li><rdf:li>|water pipe</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
</rdf:RDF>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmpMM:DerivedFrom="DSC03508.JPG">
<dc:subject>
<rdf:Bag>
<rdf:li>st</rdf:li>
<rdf:li>attach</rdf:li>
<rdf:li>basement</rdf:li>
<rdf:li>beam</rdf:li>
<rdf:li>building</rdf:li>
<rdf:li>ceiling</rdf:li>
<rdf:li>equipment</rdf:li>
<rdf:li>floor</rdf:li>
<rdf:li>pipe</rdf:li>
<rdf:li>red</rdf:li>
<rdf:li>room</rdf:li>
<rdf:li>scaffold</rdf:li>
<rdf:li>tube</rdf:li>
<rdf:li>warehouse</rdf:li>
<rdf:li>water pipe</rdf:li>
<rdf:li>train</rdf:li>
</rdf:Bag>
</dc:subject>
<lr:hierarchicalSubject>
<rdf:Bag>
<rdf:li>st|attach</rdf:li>
<rdf:li>st|basement</rdf:li>
<rdf:li>st|beam</rdf:li>
<rdf:li>st|building</rdf:li>
<rdf:li>st|ceiling</rdf:li>
<rdf:li>st|equipment</rdf:li>
<rdf:li>st|floor</rdf:li>
<rdf:li>st|pipe</rdf:li>
<rdf:li>st|red</rdf:li>
<rdf:li>st|room</rdf:li>
<rdf:li>st|scaffold</rdf:li>
<rdf:li>st|tube</rdf:li>
<rdf:li>st|warehouse</rdf:li>
<rdf:li>st|water pipe</rdf:li>
<rdf:li>st|train</rdf:li>
</rdf:Bag>
</lr:hierarchicalSubject>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@@ -1,7 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="example.jpg">
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>evening sky</rdf:li><rdf:li>sea</rdf:li><rdf:li>sky</rdf:li><rdf:li>storm cloud</rdf:li><rdf:li>stormy</rdf:li><rdf:li>sun</rdf:li><rdf:li>sunset</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|cloud</rdf:li><rdf:li>|cloudy</rdf:li><rdf:li>|evening sky</rdf:li><rdf:li>|sea</rdf:li><rdf:li>|sky</rdf:li><rdf:li>|storm cloud</rdf:li><rdf:li>|stormy</rdf:li><rdf:li>|sun</rdf:li><rdf:li>|sunset</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
</rdf:RDF>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmpMM:DerivedFrom="example.jpg">
<dc:subject>
<rdf:Bag>
<rdf:li>st</rdf:li>
<rdf:li>cloudy</rdf:li>
<rdf:li>evening sky</rdf:li>
<rdf:li>sea</rdf:li>
<rdf:li>sky</rdf:li>
<rdf:li>storm cloud</rdf:li>
<rdf:li>stormy</rdf:li>
<rdf:li>sun</rdf:li>
<rdf:li>sunset</rdf:li>
</rdf:Bag>
</dc:subject>
<lr:hierarchicalSubject>
<rdf:Bag>
<rdf:li>st|cloudy</rdf:li>
<rdf:li>st|evening sky</rdf:li>
<rdf:li>st|sea</rdf:li>
<rdf:li>st|sky</rdf:li>
<rdf:li>st|storm cloud</rdf:li>
<rdf:li>st|stormy</rdf:li>
<rdf:li>st|sun</rdf:li>
<rdf:li>st|sunset</rdf:li>
</rdf:Bag>
</lr:hierarchicalSubject>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@@ -2,6 +2,6 @@
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="example.tif">
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>evening sky</rdf:li><rdf:li>sea</rdf:li><rdf:li>sky</rdf:li><rdf:li>storm cloud</rdf:li><rdf:li>stormy</rdf:li><rdf:li>sun</rdf:li><rdf:li>sunset</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|cloud</rdf:li><rdf:li>|cloudy</rdf:li><rdf:li>|evening sky</rdf:li><rdf:li>|sea</rdf:li><rdf:li>|sky</rdf:li><rdf:li>|storm cloud</rdf:li><rdf:li>|stormy</rdf:li><rdf:li>|sun</rdf:li><rdf:li>|sunset</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>evening sky</rdf:li><rdf:li>sea</rdf:li><rdf:li>sky</rdf:li><rdf:li>storm cloud</rdf:li><rdf:li>stormy</rdf:li><rdf:li>sun</rdf:li><rdf:li>sunset</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|cloud</rdf:li><rdf:li>st|cloudy</rdf:li><rdf:li>st|evening sky</rdf:li><rdf:li>st|sea</rdf:li><rdf:li>st|sky</rdf:li><rdf:li>st|storm cloud</rdf:li><rdf:li>st|stormy</rdf:li><rdf:li>st|sun</rdf:li><rdf:li>st|sunset</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

Before

Width:  |  Height:  |  Size: 3.5 MiB

After

Width:  |  Height:  |  Size: 3.5 MiB

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmpMM:DerivedFrom="DSC01106.jpg">
<dc:subject>
<rdf:Bag>
<rdf:li>st</rdf:li>
<rdf:li>clear</rdf:li>
<rdf:li>dark</rdf:li>
<rdf:li>moon</rdf:li>
<rdf:li>night</rdf:li>
<rdf:li>night sky</rdf:li>
<rdf:li>sky</rdf:li>
</rdf:Bag>
</dc:subject>
<lr:hierarchicalSubject>
<rdf:Bag>
<rdf:li>st|clear</rdf:li>
<rdf:li>st|dark</rdf:li>
<rdf:li>st|moon</rdf:li>
<rdf:li>st|night</rdf:li>
<rdf:li>st|sky|night sky</rdf:li>
</rdf:Bag>
</lr:hierarchicalSubject>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 MiB

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/"
xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/"
xmpMM:DerivedFrom="000041900001.jpg">
<dc:subject>
<rdf:Bag>
<rdf:li>st</rdf:li>
<rdf:li>cloud</rdf:li>
<rdf:li>fly</rdf:li>
<rdf:li>sky</rdf:li>
</rdf:Bag>
</dc:subject>
<lr:hierarchicalSubject>
<rdf:Bag>
<rdf:li>st|cloud</rdf:li>
<rdf:li>st|fly</rdf:li>
<rdf:li>st|sky</rdf:li>
</rdf:Bag>
</lr:hierarchicalSubject>
</rdf:Description>
</rdf:RDF>
</x:xmpmeta>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<x:xmpmeta x:xmptk="XMP Core 4.4.0-Exiv2" xmlns:x="adobe:ns:meta/">
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="DSC01106.jpg">
<dc:subject><rdf:Bag><rdf:li></rdf:li><rdf:li>clear</rdf:li><rdf:li>dark</rdf:li><rdf:li>moon</rdf:li><rdf:li>night</rdf:li><rdf:li>night sky</rdf:li><rdf:li>sky</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>|clear</rdf:li><rdf:li>|dark</rdf:li><rdf:li>|moon</rdf:li><rdf:li>|night</rdf:li><rdf:li>|night sky</rdf:li><rdf:li>|sky</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:exif="http://ns.adobe.com/exif/1.0/" xmlns:lr="http://ns.adobe.com/lightroom/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmpMM:DerivedFrom="000041900001.tif">
<dc:subject><rdf:Bag><rdf:li>st</rdf:li><rdf:li>cloud</rdf:li><rdf:li>cloudy</rdf:li><rdf:li>fly</rdf:li><rdf:li>sky</rdf:li></rdf:Bag></dc:subject><lr:hierarchicalSubject><rdf:Bag><rdf:li>st|cloud</rdf:li><rdf:li>st|cloudy</rdf:li><rdf:li>st|fly</rdf:li><rdf:li>st|sky</rdf:li></rdf:Bag></lr:hierarchicalSubject></rdf:Description>
</rdf:RDF>
</x:xmpmeta>

1
themes Submodule

Submodule themes added at 3bb36480e7

View File

@@ -1,106 +0,0 @@
# Custom CSS Themes
You can create custom Themes. They must at least include the following:
## Requirements for Themes
### Required Variables
Define the following variables in your theme:
- `--color1`: Primary color
- `--color2`: Secondary color
- `--color3`: Additional color
- `--color4`: Another color
These variables are essential for automatic icon generation.
### Folder Icon Specification
Include the following CSS rule to specify the folder icon used in your theme:
```css
.foldericon {
content: url("data:image/svg+xml,%3Csvg width='800px' height='800px' viewBox='0 0 1024 1024' class='icon' version='1.1' xmlns='http://www.w3.org/2000/svg' fill='%23000000'%3E%3Cg id='SVGRepo_bgCarrier' stroke-width='0' /%3E%3Cg id='SVGRepo_tracerCarrier' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cg id='SVGRepo_iconCarrier'%3E%3Cpath d='M853.333333 256H469.333333l-85.333333-85.333333H170.666667c-46.933333 0-85.333333 38.4-85.333334 85.333333v170.666667h853.333334v-85.333334c0-46.933333-38.4-85.333333-85.333334-85.333333z' fill='%233674e7' /%3E%3Cpath d='M853.333333 256H170.666667c-46.933333 0-85.333333 38.4-85.333334 85.333333v426.666667c0 46.933333 38.4 85.333333 85.333334 85.333333h682.666666c46.933333 0 85.333333-38.4 85.333334-85.333333V341.333333c0-46.933333-38.4-85.333333-85.333334-85.333333z' fill='%236495ed' /%3E%3C/g%3E%3C/svg%3E");
}
```
Replace the SVG data URI (`url("data:image/svg+xml,...")`) with your desired SVG icon content.
## Previews of included themes
### alpenglow-dark
![alpenglow-dark](screenshots/alpenglow-dark.png)
### alpenglow
![alpenglow](screenshots/alpenglow.png)
### aritim-dark
![aritim-dark](screenshots/aritim-dark.png)
### aritim
![aritim](screenshots/aritim.png)
### autumn
![autumn](screenshots/autumn.png)
### carnation
![carnation](screenshots/carnation.png)
### catpuccin
![catpuccin](screenshots/catpuccin.png)
### cornflower
![cornflower](screenshots/cornflower.png)
### default-dark
![default-dark](screenshots/default-dark.png)
### default
![default](screenshots/default.png)
### ivy
![ivy](screenshots/ivy.png)
### kjoe
![kjoe](screenshots/kjoe.png)
### monokai-vibrant
![monokai-vibrant](screenshots/monokai-vibrant.png)
### rainbow
![rainbow](screenshots/rainbow.png)
### spring
![spring](screenshots/spring.png)
### steam
![steam](screenshots/steam.png)
### summer
![summer](screenshots/summer.png)
### sunflower
![sunflower](screenshots/sunflower.png)
### winter
![winter](screenshots/winter.png)

View File

@@ -1,116 +0,0 @@
@import url("https://fonts.cdnfonts.com/css/metropolis-2");
* {
--color1: #ffa769;
--color2: #fc4ca0;
--color3: #7542e5;
--color4: #ff4ad9;
--color5: #5f2eca;
--color6: #7033ca;
--color7: #ff778e;
--bcolor1: #e2d9f8;
--bcolor2: #20123a;
--bcolor3: #2b1753;
--bcolor4: #321c64;
--gradient: linear-gradient(
80deg,
var(--bcolor2) 0%,
var(--color5) 1%,
var(--color3) 2%,
var(--color2) 3%,
var(--color1) 4%,
var(--color1) 96%,
var(--color2) 97%,
var(--color3) 98%,
var(--color5) 99%,
var(--bcolor2) 100%
);
}
body {
color: var(--bcolor1);
background-color: var(--bcolor3);
font-family: "Metropolis", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
}
.navbar {
font-weight: 800;
color: var(--bcolor1);
background-color: var(--color3);
background-image: var(--gradient);
}
.navbar li a {
font-weight: 800;
color: var(--bcolor1);
}
.navbar li a:hover {
text-decoration: none;
background-color: var(--color4);
}
.footer {
color: var(--bcolor2);
background-color: var(--color1);
font-weight: 600;
}
.footer a {
color: var(--color2);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/catpuccin.svg.j2";
}
.folders a {
font-weight: 500;
color: var(--bcolor1);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color7);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 500;
background-color: var(--color6);
}
.tagentry:hover {
background-color: var(--color3);
}
.column img {
background-color: var(--bcolor2);
}
#totop:hover {
background-color: var(--color6);
}
#totop {
background-color: var(--bcolor4);
color: var(--bcolor1);
font-weight: 600;
}

View File

@@ -1,115 +0,0 @@
@import url("https://fonts.cdnfonts.com/css/metropolis-2");
* {
--color1: #ffa769;
--color2: #fc4ca0;
--color3: #7542e5;
--color4: #ff4ad9;
--color5: #ff778e;
--color6: #fff5f6;
--color7: #5f2eca;
--bcolor1: #ffe7ea;
--bcolor2: #20123b;
--bcolor3: #2b1753;
--bcolor4: #321c64;
--gradient: linear-gradient(80deg,
var(--bcolor2) 0%,
var(--color3) 1%,
var(--color4) 2%,
var(--color5) 3%,
var(--color1) 4%,
var(--color1) 96%,
var(--color5) 97%,
var(--color4) 98%,
var(--color3) 99%,
var(--bcolor2) 100%);
}
body {
color: var(--bcolor2);
background-color: var(--color6);
font-family: "Metropolis", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
}
.navbar {
font-weight: 800;
color: var(--bcolor2);
background-color: var(--color5);
background-image: var(--gradient);
}
.navbar li a {
font-weight: 800;
color: var(--bcolor2);
}
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor1);
background-color: var(--bcolor4);
font-weight: 600;
}
.footer a {
color: var(--color5);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/catpuccin.svg.j2";
}
.folders a {
font-weight: 500;
color: var(--bcolor2);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color7);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 500;
color: var(--bcolor1);
background-color: var(--color3);
}
.tagentry:hover {
background-color: var(--color7);
}
.column img {
background-color: var(--bcolor1);
}
#totop:hover {
background-color: var(--color3);
}
#totop {
background-color: var(--color2);
color: var(--bcolor2);
font-weight: 600;
}

View File

@@ -1,102 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap");
* {
--color1: #5d88ca;
--color2: #27ae60;
--color3: #f67400;
--color4: #da4453;
--color5: #212b36;
--bcolor1: #10151a;
--bcolor2: #d3dae3;
--bcolor3: #141a21;
}
.navbar {
font-weight: 600;
color: var(--bcolor2);
background-color: var(--color5);
}
.navbar li a {
font-weight: 500;
color: var(--bcolor2);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--bcolor3);
}
.footer {
color: var(--bcolor1);
background-color: var(--color2);
font-weight: 500;
}
.footer a {
color: var(--color4);
text-decoration: none;
font-weight: 400;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-papirus.svg.j2";
}
.folders a {
font-weight: 600;
color: var(--bcolor2);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color1);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 400;
background-color: var(--bcolor1);
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor1);
}
#totop:hover {
background-color: var(--color3);
}
#totop {
background-color: var(--color5);
color: var(--bcolor2);
font-weight: 600;
}
body {
color: var(--bcolor2);
background-color: var(--bcolor3);
font-family: "Poppins", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}

View File

@@ -1,102 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap");
* {
--color1: #1d99f3;
--color2: #27ae60;
--color3: #f67400;
--color4: #da4453;
--color5: #eef2f5;
--bcolor1: #a3a5ac;
--bcolor2: #303030;
--bcolor3: #eff0f1;
}
.navbar {
font-weight: 600;
color: var(--bcolor2);
background-color: var(--bcolor1);
}
.navbar li a {
font-weight: 500;
color: var(--bcolor2);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
color: var(--bcolor1);
background-color: var(--bcolor2);
}
.footer {
color: var(--bcolor2);
background-color: var(--color3);
font-weight: 500;
}
.footer a {
color: var(--color4);
text-decoration: none;
font-weight: 400;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-papirus.svg.j2";
}
.folders a {
font-weight: 600;
color: var(--bcolor2);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color1);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 400;
background-color: var(--bcolor1);
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor1);
}
#totop:hover {
background-color: var(--color4);
}
#totop {
background-color: var(--color5);
color: var(--bcolor2);
font-weight: 600;
}
body {
color: var(--bcolor2);
background-color: var(--bcolor3);
font-family: "Poppins", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}

View File

@@ -1,108 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Playfair+Display:wght@400;500;600;700;800;900&display=swap");
* {
--color1: #FF7F50; /* Coral */
--color2: #D2691E; /* Chocolate */
--color3: #8B4513; /* SaddleBrown */
--color4: #FFA07A; /* LightSalmon */
--bcolor1: #FFF8DC; /* Cornsilk */
--bcolor2: #2F4F4F; /* DarkSlateGray */
--bcolor3: #3E2723; /* Darker brown */
--bcolor4: #4E342E; /* Slightly lighter brown */
}
.navbar {
font-weight: 600;
color: var(--bcolor1);
background-color: var(--color1);
font-family: "Playfair Display", serif;
}
.navbar li a {
font-weight: 500;
color: var(--bcolor1);
font-family: "Playfair Display", serif;
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor1);
background-color: var(--color3);
font-weight: 500;
font-family: "Playfair Display", serif;
}
.footer a {
color: var(--color4);
text-decoration: none;
font-weight: 400;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-3.svg.j2";
}
.folders a {
font-weight: 600;
color: var(--bcolor1);
text-decoration: none;
font-family: "Playfair Display", serif;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color2);
text-decoration: none;
font-family: "Playfair Display", serif;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 400;
background-color: var(--bcolor2);
font-family: "Playfair Display", serif;
}
.tagentry:hover {
background-color: var(--color3);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 600;
font-family: "Playfair Display", serif;
}
body {
color: var(--bcolor1);
background-color: var(--bcolor3);
font-family: "Playfair Display", serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}

View File

@@ -1,101 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
* {
--color1: #8b0000;
--color2: #b22222;
--color3: #ff4500;
--color4: #6e0000;
--bcolor1: #171717;
--bcolor2: #191919;
--bcolor3: #ebebeb;
--bcolor4: #0a0a0a;
}
.navbar {
font-weight: bold;
color: var(--bcolor3);
background-color: var(--color1);
font-weight: 900;
}
.navbar li a {
font-weight: 800;
color: var(--bcolor3);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor3);
background-color: var(--color3);
font-weight: 700;
}
.footer a {
color: var(--color4);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-1.svg.j2";
}
.folders a {
font-weight: 800;
color: var(--bcolor1);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 800;
color: var(--color2);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 600;
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 800;
}
body {
color: var(--bcolor1);
background-color: var(--bcolor3);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: normal;
}

View File

@@ -1,109 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Nunito:wght@300;400;500;600;700&display=swap");
* {
--color1: #F28FAD;
--color2: #ABE9B3;
--color3: #FAE3B0;
--color4: #96CDFB;
--bcolor1: #F5E0DC;
--bcolor2: #575268;
--bcolor3: #D9E0EE;
--bcolor4: #C9CBFF;
}
.navbar {
font-weight: 600;
color: var(--bcolor2);
background-color: var(--color1);
font-family: "Nunito", sans-serif;
}
.navbar li a {
font-weight: 500;
color: var(--bcolor2);
font-family: "Nunito", sans-serif;
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor2);
background-color: var(--color3);
font-weight: 500;
font-family: "Nunito", sans-serif;
}
.footer a {
color: var(--color1);
text-decoration: none;
font-weight: 400;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/catpuccin.svg.j2";
}
.folders a {
font-weight: 600;
color: var(--bcolor2);
text-decoration: none;
font-family: "Nunito", sans-serif;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color1);
text-decoration: none;
font-family: "Nunito", sans-serif;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 400;
background-color: var(--color3);
font-family: "Nunito", sans-serif;
}
.tagentry:hover {
background-color: var(--color4);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
color: var(--bcolor2);
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 600;
font-family: "Nunito", sans-serif;
}
body {
color: var(--bcolor2);
background-color: var(--bcolor3);
font-family: "Nunito", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}

View File

@@ -1,101 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
* {
--color1: cornflowerblue;
--color2: #1346a4;
--color3: #0e3377;
--color4: #3674e7;
--bcolor1: #171717;
--bcolor2: #191919;
--bcolor3: #ebebeb;
--bcolor4: #0a0a0a;
}
.navbar {
font-weight: bold;
color: var(--bcolor3);
background-color: var(--color1);
font-weight: 900;
}
.navbar li a {
font-weight: 800;
color: var(--bcolor3);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor3);
background-color: var(--color3);
font-weight: 700;
}
.footer a {
color: var(--color4);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-1.svg.j2";
}
.folders a {
font-weight: 800;
color: var(--bcolor1);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 800;
color: var(--color2);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 600;
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 800;
}
body {
color: var(--bcolor1);
background-color: var(--bcolor3);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: normal;
}

View File

@@ -1,86 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap");
* {
--color1: #333333;
--color2: #888888;
--color3: #dddddd;
--color4: #111111;
--color5: #0055ff;
--bcolor1: #ffffff;
--bcolor2: #000000;
}
.navbar {
font-weight: bold;
color: var(--bcolor1);
background-color: var(--color1);
}
.navbar li a {
font-weight: 700;
color: var(--bcolor1);
}
/* Change the link color on hover */
.navbar li a:hover {
background-color: var(--color4);
}
.footer {
color: var(--bcolor2);
background-color: var(--color3);
font-weight: 500;
}
.footer a {
color: var(--color5);
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-2.svg.j2";
}
.folders a {
font-weight: 700;
color: var(--color5);
text-decoration: none;
}
.tooltiptext {
font-weight: 400;
background-color: var(--color2);
}
.tagentry:hover {
background-color: var(--color4);
}
.column img {
background-color: var(--color2);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 800;
}
body {
color: var(--bcolor2);
background-color: var(--bcolor1);
font-family: "Ubuntu", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}
body a {
font-weight: 400;
color: var(--color5);
text-decoration: none;
}

View File

@@ -1,6 +0,0 @@
<svg width='64' height='64' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path d='M20 6H10L8 4H4C2.89543 4 2 4.89543 2 6V18C2 19.1046 2.89543 20 4 20H20C21.1046 20 22 19.1046 22 18V8C22 6.89543 21.1046 6 20 6Z' fill='{{ color1 }}' />
<path d='M4 4H8L10 6H20C21.1046 6 22 6.89543 22 8H2C2 6.89543 2.89543 6 4 6V4Z' fill='{{ color4 }}' />
<path d='M10 6H14L12 4H8L10 6Z' fill='{{ color3 }}' />
<path d='M14 6H18L16 4H12L14 6Z' fill='{{ color2 }}' />
</svg>

Before

Width:  |  Height:  |  Size: 493 B

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
<path style="fill:{{ color2 }};" d="M44.5,41h-41C2.119,41,1,39.881,1,38.5v-31C1,6.119,2.119,5,3.5,5h11.597
c1.519,0,2.955,0.69,3.904,1.877L21.5,10h23c1.381,0,2.5,1.119,2.5,2.5v26C47,39.881,45.881,41,44.5,41z" />
<path style="fill:{{ color2 }};" d="M2,25h20V11H4c-1.105,0-2,0.895-2,2V25z" />
<path style="fill:#FFFFFF;" d="M2,26h20V12H4c-1.105,0-2,0.895-2,2V26z" />
<path style="fill:{{ color1 }};" d="M1,37.875V38.5C1,39.881,2.119,41,3.5,41h41c1.381,0,2.5-1.119,2.5-2.5v-0.625H1z" />
<path style="fill:{{ color2 }};"
d="M44.5,11h-23l-1.237,0.824C19.114,12.591,17.763,13,16.381,13H3.5C2.119,13,1,14.119,1,15.5v22C1,38.881,2.119,40,3.5,40h41c1.381,0,2.5-1.119,2.5-2.5v-24C47,12.119,45.881,11,44.5,11z" />
<path style="fill:{{ color1 }};" d="M44.5,40h-41C2.119,40,1,38.881,1,37.5v-21C1,15.119,2.119,14,3.5,14h13.256
c1.382,0,2.733-0.409,3.883-1.176L21.875,12H44.5c1.381,0,2.5,1.119,2.5,2.5v23C47,38.881,45.881,40,44.5,40z" />
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<svg width="800px" height="800px" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img"
preserveAspectRatio="xMidYMid meet">
<path fill="{{ color2 }}"
d="M491 326V92.143C491 79.021 480.259 68 467.137 68H341.13c-9.287 0-17.723 5.603-21.596 14.044l-12.709 27.903C302.952 118.388 294.516 124 285.229 124H67.538C54.416 124 44 134.426 44 147.549v216.363C44 377.034 54.416 388 67.538 388h399.599c.628 0 1.248-.36 1.863-.408V433h.342c0 6 4.877 10.636 10.829 10.636c5.952 0 10.829-4.967 10.829-10.919V326z">
</path>
<path fill="{{ color1 }}"
d="M480.171 443.636c-5.952 0-10.829-4.636-10.829-10.636H469V210.181C469 197.058 458.661 186 445.539 186H45.94C32.818 186 22 197.058 22 210.181V449.37C22 462.492 32.818 473 45.94 473h399.599c1.385 0 2.741-.06 4.061-.288c1.639.227 3.31.385 5.012.385c20.04 0 36.136-16.229 36.136-36.269c0-.534-.036-1.058-.058-1.586c-1.147 4.766-5.435 8.394-10.519 8.394z">
</path>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,34 +0,0 @@
<svg height="200px" width="200px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-51.2 -51.2 614.40 614.40" xml:space="preserve"
fill="#000000" stroke="#000000" stroke-width="0.00512" transform="matrix(1, 0, 0, 1, 0, 0)rotate(0)">
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="10.24">
</g>
<g id="SVGRepo_iconCarrier">
<g transform="translate(0 1)">
<path style="fill: {{ color2 }};"
d="M422.4,144.067c0-18.773-13.653-34.133-29.867-34.133h-107.52l-37.547-51.2H81.92 c-11.947,0-22.187,11.947-22.187,25.6v59.733H422.4z"></path>
<path style="fill: {{ color3 }};" d="M426.667,109.933h-34.133c16.213,0,29.867,15.36,29.867,34.133h38.4 C460.8,125.293,445.44,109.933,426.667,109.933"></path>
<path style="fill: rgb(255, 255, 255);"
d="M81.92,58.733H59.733c-14.507,0-25.6,11.947-25.6,25.6v59.733h25.6V84.333 C59.733,70.68,69.973,58.733,81.92,58.733"></path>
<path style="fill: {{ color1 }};"
d="M34.133,169.667v256c0,14.507,10.24,25.6,23.04,25.6h397.653c12.8,0,23.04-11.093,23.04-25.6v-256 c0-14.507-10.24-25.6-23.04-25.6H57.173C44.373,144.067,34.133,156.013,34.133,169.667">
</path>
<path style="fill: rgb(255, 255, 255);"
d="M34.133,425.667v-256c0-14.507,10.24-25.6,23.04-25.6h-23.04c-14.507,0-25.6,11.093-25.6,25.6v256 c0,14.507,11.093,25.6,25.6,25.6h23.04C44.373,451.267,34.133,440.173,34.133,425.667">
</path>
<path style="fill: {{ color2 }};"
d="M477.867,144.067h-23.04c12.8,0,23.04,11.093,23.04,25.6v256c0,14.507-10.24,25.6-23.04,25.6h23.04 c14.507,0,25.6-11.093,25.6-25.6v-256C503.467,156.013,492.373,144.067,477.867,144.067">
</path>
<path
d="M477.867,459.8H34.133C15.36,459.8,0,444.44,0,425.667v-51.2c0-5.12,3.413-8.533,8.533-8.533s8.533,3.413,8.533,8.533v51.2 c0,9.387,7.68,17.067,17.067,17.067h443.733c9.387,0,17.067-7.68,17.067-17.067v-256c0-9.387-7.68-17.067-17.067-17.067H34.133 c-9.387,0-17.067,7.68-17.067,17.067V306.2c0,5.12-3.413,8.533-8.533,8.533S0,311.32,0,306.2V169.667 c0-18.773,15.36-34.133,34.133-34.133h443.733c18.773,0,34.133,15.36,34.133,34.133v256C512,444.44,496.64,459.8,477.867,459.8z">
</path>
<path d="M17.067,340.333c0-5.12-3.413-8.533-8.533-8.533S0,335.213,0,340.333c0,5.12,3.413,8.533,8.533,8.533 S17.067,345.453,17.067,340.333"></path>
<path
d="M460.8,152.6H34.133c-5.12,0-8.533-3.413-8.533-8.533V84.333C25.6,65.56,40.96,50.2,59.733,50.2h187.733 c2.56,0,5.12,0.853,6.827,3.413L294.4,101.4h132.267c23.893,0,42.667,18.773,42.667,42.667 C469.333,149.187,465.92,152.6,460.8,152.6z M42.667,135.533H450.56c-3.413-10.24-12.8-17.067-23.893-17.067H290.133 c-2.56,0-5.12-0.853-6.827-3.413L243.2,67.267H59.733c-9.387,0-17.067,7.68-17.067,17.067V135.533z">
</path>
<path d="M162.133,203.8h-102.4c-5.12,0-8.533-3.413-8.533-8.533c0-5.12,3.413-8.533,8.533-8.533h102.4 c5.12,0,8.533,3.413,8.533,8.533C170.667,200.387,167.253,203.8,162.133,203.8z"></path>
<path d="M162.133,237.933h-102.4c-5.12,0-8.533-3.413-8.533-8.533c0-5.12,3.413-8.533,8.533-8.533h102.4 c5.12,0,8.533,3.413,8.533,8.533C170.667,234.52,167.253,237.933,162.133,237.933z">
</path>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" width="48px" height="48px"><linearGradient id="WQEfvoQAcpQgQgyjQQ4Hqa" x1="24" x2="24" y1="6.708" y2="14.977" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#eba600"/><stop offset="1" stop-color="#c28200"/></linearGradient><path fill="url(#WQEfvoQAcpQgQgyjQQ4Hqa)" d="M24.414,10.414l-2.536-2.536C21.316,7.316,20.553,7,19.757,7L5,7C3.895,7,3,7.895,3,9l0,30 c0,1.105,0.895,2,2,2l38,0c1.105,0,2-0.895,2-2V13c0-1.105-0.895-2-2-2l-17.172,0C25.298,11,24.789,10.789,24.414,10.414z"/><linearGradient id="WQEfvoQAcpQgQgyjQQ4Hqb" x1="24" x2="24" y1="10.854" y2="40.983" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#ffd869"/><stop offset="1" stop-color="#fec52b"/></linearGradient><path fill="url(#WQEfvoQAcpQgQgyjQQ4Hqb)" d="M21.586,14.414l3.268-3.268C24.947,11.053,25.074,11,25.207,11H43c1.105,0,2,0.895,2,2v26 c0,1.105-0.895,2-2,2H5c-1.105,0-2-0.895-2-2V15.5C3,15.224,3.224,15,3.5,15h16.672C20.702,15,21.211,14.789,21.586,14.414z"/></svg>

Before

Width:  |  Height:  |  Size: 1020 B

View File

@@ -1,10 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" version="1">
<rect style="opacity:0.2" width="56" height="36" x="4" y="22" rx="2.8" ry="2.8" />
<path style="fill:{{ color2 }}"
d="M 4,46.2 C 4,47.751 5.2488,49 6.8,49 H 57.2 C 58.751,49 60,47.751 60,46.2 V 15.8 C 60,14.249 58.751,13 57.2,13 H 32 C 27.8,13 26.4,7 22.2,7 H 6.8 C 5.2488,7 4,8.2488 4,9.8" />
<rect style="opacity:0.2" width="56" height="36" x="4" y="20" rx="2.8" ry="2.8" />
<rect style="fill:#e4e4e4" width="48" height="22" x="8" y="16" rx="2.8" ry="2.8" />
<rect style="fill:{{ color1 }}" width="56" height="36" x="4" y="21" rx="2.8" ry="2.8" />
<path style="opacity:0.1;fill:#ffffff"
d="M 6.8008,7 C 5.2496,7 4,8.2496 4,9.8008 V 10.801 C 4,9.2496 5.2496,8 6.8008,8 H 22.199 C 26.399,8 27.8,14 32,14 H 57.199 C 58.75,14 60,15.25 60,16.801 V 15.801 C 60,14.25 58.75,13 57.199,13 H 32 C 27.8,13 26.399,7 22.199,7 Z" />
</svg>

Before

Width:  |  Height:  |  Size: 943 B

View File

@@ -1,11 +0,0 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<svg height='512' width='512' version='1.1' id='Layer_1' viewBox='0 0 327.68 327.68' xml:space='preserve' xmlns='http://www.w3.org/2000/svg'
xmlns:svg='http://www.w3.org/2000/svg'>
<defs id='defs1' />
<g id='icon' transform='translate(-128.08785,-19.123746)'>
<path id='fg' style='fill:#dcdedf;fill-opacity:1;stroke:none;stroke-width:6.4;stroke-linecap:round'
d='m 428.2793,160.97461 c -78.34525,0.002 -156.69075,-0.007 -235.03585,0.0159 -6.54719,0.16827 -12.82625,4.35683 -15.37415,10.41233 -1.13922,2.74038 -1.79474,5.64883 -2.74311,8.45618 -10.27476,33.32809 -20.56379,66.65251 -30.78952,99.99526 -1.21885,4.66702 0.98496,9.97729 5.17832,12.3776 2.40372,1.49255 5.31559,1.67974 8.07056,1.58986 78.16762,-0.0305 156.33557,0.008 234.503,-0.0259 6.43644,-0.17102 12.61041,-4.22124 15.23762,-10.1162 1.2077,-2.75858 1.84495,-5.72012 2.81312,-8.56426 10.08283,-32.66775 20.13843,-65.34481 30.20361,-98.01744 0.84274,-2.37238 1.35887,-4.96865 0.67937,-7.45078 -0.98831,-4.2533 -4.6015,-7.82876 -8.96172,-8.48315 -1.24947,-0.20685 -2.51789,-0.23068 -3.78125,-0.18945 z' />
<path id='bg' style='fill:#dcdedf;fill-opacity:1;stroke:none;stroke-width:6.4;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal'
d='m 214.44922,72.089844 c -20.32611,0.01324 -40.65359,-0.03032 -60.97883,0.03325 -5.06767,0.228794 -9.72081,4.176673 -10.64873,9.186171 -0.54368,2.865585 -0.16202,5.794266 -0.28069,8.688777 -4.6e-4,27.527908 -0.008,55.056498 0.0176,82.583978 0.17186,3.22428 3.44977,5.89016 6.64757,5.28488 2.4037,-0.30447 4.32209,-2.31086 4.87898,-4.61617 3.53052,-10.60549 6.98896,-21.23707 10.60492,-31.81263 1.14174,-2.71356 4.14972,-4.41014 7.06111,-4.12697 16.80704,-0.0631 33.61433,-0.01 50.42149,-0.0279 55.72175,-0.002 111.44406,0.006 167.16547,-0.0191 5.71319,-0.19462 10.82844,-5.25566 10.91838,-11.00339 0.12602,-2.9908 -0.0352,-5.98579 0.0574,-8.97822 0.0534,-2.61272 -0.3941,-5.33067 -1.94259,-7.50013 -2.21535,-3.37352 -6.26106,-5.45808 -10.30253,-5.1988 -44.42303,-0.0315 -88.8461,0.006 -133.26914,-0.0339 -1.35699,0.0355 -2.86726,0.10531 -3.94821,-0.87625 -1.14131,-0.86056 -1.57137,-2.31551 -1.50179,-3.694889 -0.0148,-5.79966 0.0803,-11.603104 -0.0604,-17.400318 -0.41681,-5.210159 -4.80455,-9.807583 -10.02134,-10.361315 -2.37314,-0.240789 -4.76368,-0.0457 -7.14443,-0.12428 -5.89141,-0.0074 -11.78281,-0.0066 -17.67422,-0.0028 z' />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="100"
height="100"
viewBox="0 0 100 100"
version="1.1"
id="svg3"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<rect
x="10"
y="30"
width="80"
height="50"
fill="{{ color1 }}"
rx="5"
ry="5"
id="rect1" />
<rect
x="10"
y="20"
width="40"
height="20"
fill="{{ color1 }}"
rx="5"
ry="5"
id="rect2" />
<rect
x="20"
y="40"
width="60"
height="40"
fill="{{ color2 }}"
rx="3"
ry="3"
id="rect3" />
<rect
x="30"
y="50"
width="40"
height="30"
fill="{{ color1 }}"
rx="2"
ry="2"
id="rect4" />
</svg>

Before

Width:  |  Height:  |  Size: 876 B

View File

@@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Themes</title>
</head>
<body><a href="previews/alpenglow-dark.html">alpenglow-dark</a><br>
<a href="previews/alpenglow.html">alpenglow</a><br>
<a href="previews/aritim-dark.html">aritim-dark</a><br>
<a href="previews/aritim.html">aritim</a><br>
<a href="previews/autumn.html">autumn</a><br>
<a href="previews/carnation.html">carnation</a><br>
<a href="previews/catpuccin.html">catpuccin</a><br>
<a href="previews/cornflower.html">cornflower</a><br>
<a href="previews/default-dark.html">default-dark</a><br>
<a href="previews/default.html">default</a><br>
<a href="previews/ivy.html">ivy</a><br>
<a href="previews/kjoe.html">kjoe</a><br>
<a href="previews/monokai-vibrant.html">monokai-vibrant</a><br>
<a href="previews/rainbow.html">rainbow</a><br>
<a href="previews/spring.html">spring</a><br>
<a href="previews/steam.html">steam</a><br>
<a href="previews/summer.html">summer</a><br>
<a href="previews/sunflower.html">sunflower</a><br>
<a href="previews/winter.html">winter</a><br>
</body></html>

View File

@@ -1,99 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
* {
--color1: #006400;
--color2: #008000;
--color3: #32cd32;
--color4: #004300;
--bcolor1: #171717;
--bcolor2: #191919;
--bcolor3: #ebebeb;
--bcolor4: #0a0a0a;
}
.navbar {
font-weight: bold;
color: var(--bcolor3);
background-color: var(--color1);
font-weight: 900;
}
.navbar li a {
font-weight: 800;
color: var(--bcolor3);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor3);
background-color: var(--color3);
font-weight: 700;
}
.footer a {
color: var(--color4);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-1.svg.j2";
}
.folders a {
font-weight: 800;
color: var(--bcolor1);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 800;
color: var(--color2);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 600;
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
}
body {
color: var(--bcolor1);
background-color: var(--bcolor3);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: normal;
}

View File

@@ -1,100 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
* {
--color1: #260000;
--color2: #470000;
--color3: #191313;
--color4: #ff2727;
--bcolor1: #ebebeb;
--bcolor2: #171717;
--bcolor3: #0a0a0a;
}
.navbar {
font-weight: bold;
color: var(--bcolor1);
background-color: var(--color1);
font-weight: 900;
}
.navbar li a {
font-weight: 800;
color: var(--bcolor1);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor1);
background-color: var(--color3);
font-weight: 700;
}
.footer a {
color: var(--color4);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: url("data:image/svg+xml,%3Csvg width='800px' height='800px' viewBox='0 0 1024 1024' class='icon' version='1.1' xmlns='http://www.w3.org/2000/svg' fill='%23000000'%3E%3Cg id='SVGRepo_bgCarrier' stroke-width='0' /%3E%3Cg id='SVGRepo_tracerCarrier' stroke-linecap='round' stroke-linejoin='round' /%3E%3Cg id='SVGRepo_iconCarrier'%3E%3Cpath d='M853.333333 256H469.333333l-85.333333-85.333333H170.666667c-46.933333 0-85.333333 38.4-85.333334 85.333333v170.666667h853.333334v-85.333334c0-46.933333-38.4-85.333333-85.333334-85.333333z' fill='%238e0000' /%3E%3Cpath d='M853.333333 256H170.666667c-46.933333 0-85.333333 38.4-85.333334 85.333333v426.666667c0 46.933333 38.4 85.333333 85.333334 85.333333h682.666666c46.933333 0 85.333333-38.4 85.333334-85.333333V341.333333c0-46.933333-38.4-85.333333-85.333334-85.333333z' fill='%23FF2727' /%3E%3C/g%3E%3C/svg%3E");
}
.folders a {
font-weight: 800;
color: var(--bcolor1);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 800;
color: var(--color4);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 600;
background-color: var(--color3);
}
.tagentry:hover {
background-color: var(--color2);
}
.column img {
background-color: var(--bcolor3);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 800;
}
body {
color: var(--bcolor1);
background-color: var(--bcolor2);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: normal;
}

View File

@@ -1,89 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
* {
--color1: #81f900;
--color2: #e542ff;
--color3: #ffd945;
--color4: #528bff;
--bcolor1: #f6f6f6;
--bcolor2: #2c313a;
--bcolor3: #191b20;
--bcolor4: #16171d;
--bcolor5: #0b0c0f;
}
.navbar {
font-weight: bold;
color: var(--bcolor1);
background-color: var(--bcolor3);
font-weight: 900;
}
.navbar li a {
font-weight: 900;
color: var(--bcolor1);
}
/* Change the link color on hover */
.navbar li a:hover {
background-color: var(--bcolor2);
}
.footer {
color: var(--bcolor1);
background-color: var(--bcolor2);
font-weight: 700;
}
.footer a {
color: var(--color4);
text-decoration: none;
}
.foldericon {
content: "themes/icons/subfolder.svg.j2";
}
.folders a {
font-weight: 900;
color: var(--color3);
text-decoration: none;
}
.tooltiptext {
font-weight: 600;
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor5);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 800;
}
body {
color: var(--bcolor1);
background-color: var(--bcolor4);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 800;
font-style: normal;
}
body a {
font-weight: 900;
color: var(--color2);
text-decoration: none;
}

View File

@@ -1,104 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
* {
--color5: #e50000;
--color6: #ff8d00;
--color3: #ffee00;
--color4: #028121;
--color1: #004cff;
--color2: #770088;
--bcolor1: #ebebeb;
--bcolor2: #191919;
--bcolor3: #171717;
--bcolor4: #0a0a0a;
--bcolor5: #929292;
}
.navbar {
font-weight: bold;
color: var(--bcolor1);
background-color: var(--color5);
font-weight: 900;
}
.navbar li a {
font-weight: 800;
color: var(--bcolor1);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color6);
}
.footer {
color: var(--bcolor2);
background-color: var(--color3);
font-weight: 700;
}
.footer a {
color: var(--color4);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/subfolder.svg.j2";
}
.folders a {
font-weight: 800;
color: var(--bcolor3);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 800;
color: var(--color4);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 600;
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor5);
}
#totop:hover {
background-color: var(--color3);
}
#totop {
background-color: var(--color6);
color: var(--bcolor4);
font-weight: 800;
}
body {
color: var(--bcolor3);
background-color: var(--bcolor1);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: normal;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

View File

@@ -1,108 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Lora:wght@300;400;500;600;700&display=swap");
* {
--color1: #FFB6C1; /* LightPink */
--color2: #98FB98; /* PaleGreen */
--color3: #FFD700; /* Gold */
--color4: #87CEFA; /* LightSkyBlue */
--bcolor1: #FFFFFF; /* White */
--bcolor2: #2F4F4F; /* DarkSlateGray */
--bcolor3: #FAF0E6; /* Linen */
--bcolor4: #E6E6FA; /* Lavender */
}
.navbar {
font-weight: 600;
color: var(--bcolor2);
background-color: var(--color1);
font-family: "Lora", serif;
}
.navbar li a {
font-weight: 500;
color: var(--bcolor2);
font-family: "Lora", serif;
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor2);
background-color: var(--color3);
font-weight: 500;
font-family: "Lora", serif;
}
.footer a {
color: var(--color4);
text-decoration: none;
font-weight: 400;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-3.svg.j2";
}
.folders a {
font-weight: 600;
color: var(--bcolor2);
text-decoration: none;
font-family: "Lora", serif;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color2);
text-decoration: none;
font-family: "Lora", serif;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 400;
background-color: var(--bcolor4);
font-family: "Lora", serif;
}
.tagentry:hover {
background-color: var(--bcolor3);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 600;
font-family: "Lora", serif;
}
body {
color: var(--bcolor2);
background-color: var(--bcolor3);
font-family: "Lora", serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}

View File

@@ -1,108 +0,0 @@
@import url('https://fonts.cdnfonts.com/css/arial');
* {
--color1: #171d25;
--color2: #1a9fff;
--color3: #17191b;
--color4: #3d4450;
--bcolor1: #dcdedf;
--bcolor2: #262a32;
}
.navbar {
font-weight: 900;
color: var(--bcolor1);
background-color: var(--color1);
}
.navbar li a {
font-weight: 900;
color: var(--bcolor1);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: underline;
text-decoration-thickness: 0.3ex;
text-underline-offset: 0.5ex;
color: var(--color2)
}
.footer {
color: var(--bcolor1);
background-color: var(--color3);
font-weight: 500;
}
.footer a {
color: var(--color2);
text-decoration: none;
font-weight: 700;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8' standalone='no'%3F%3E%3Csvg height='512' width='512' version='1.1' id='Layer_1' viewBox='0 0 327.68 327.68' xml:space='preserve' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs id='defs1' /%3E%3Cg id='icon' transform='translate(-128.08785,-19.123746)'%3E%3Cpath id='fg' style='fill:%23dcdedf;fill-opacity:1;stroke:none;stroke-width:6.4;stroke-linecap:round' d='m 428.2793,160.97461 c -78.34525,0.002 -156.69075,-0.007 -235.03585,0.0159 -6.54719,0.16827 -12.82625,4.35683 -15.37415,10.41233 -1.13922,2.74038 -1.79474,5.64883 -2.74311,8.45618 -10.27476,33.32809 -20.56379,66.65251 -30.78952,99.99526 -1.21885,4.66702 0.98496,9.97729 5.17832,12.3776 2.40372,1.49255 5.31559,1.67974 8.07056,1.58986 78.16762,-0.0305 156.33557,0.008 234.503,-0.0259 6.43644,-0.17102 12.61041,-4.22124 15.23762,-10.1162 1.2077,-2.75858 1.84495,-5.72012 2.81312,-8.56426 10.08283,-32.66775 20.13843,-65.34481 30.20361,-98.01744 0.84274,-2.37238 1.35887,-4.96865 0.67937,-7.45078 -0.98831,-4.2533 -4.6015,-7.82876 -8.96172,-8.48315 -1.24947,-0.20685 -2.51789,-0.23068 -3.78125,-0.18945 z' /%3E%3Cpath id='bg' style='fill:%23dcdedf;fill-opacity:1;stroke:none;stroke-width:6.4;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal' d='m 214.44922,72.089844 c -20.32611,0.01324 -40.65359,-0.03032 -60.97883,0.03325 -5.06767,0.228794 -9.72081,4.176673 -10.64873,9.186171 -0.54368,2.865585 -0.16202,5.794266 -0.28069,8.688777 -4.6e-4,27.527908 -0.008,55.056498 0.0176,82.583978 0.17186,3.22428 3.44977,5.89016 6.64757,5.28488 2.4037,-0.30447 4.32209,-2.31086 4.87898,-4.61617 3.53052,-10.60549 6.98896,-21.23707 10.60492,-31.81263 1.14174,-2.71356 4.14972,-4.41014 7.06111,-4.12697 16.80704,-0.0631 33.61433,-0.01 50.42149,-0.0279 55.72175,-0.002 111.44406,0.006 167.16547,-0.0191 5.71319,-0.19462 10.82844,-5.25566 10.91838,-11.00339 0.12602,-2.9908 -0.0352,-5.98579 0.0574,-8.97822 0.0534,-2.61272 -0.3941,-5.33067 -1.94259,-7.50013 -2.21535,-3.37352 -6.26106,-5.45808 -10.30253,-5.1988 -44.42303,-0.0315 -88.8461,0.006 -133.26914,-0.0339 -1.35699,0.0355 -2.86726,0.10531 -3.94821,-0.87625 -1.14131,-0.86056 -1.57137,-2.31551 -1.50179,-3.694889 -0.0148,-5.79966 0.0803,-11.603104 -0.0604,-17.400318 -0.41681,-5.210159 -4.80455,-9.807583 -10.02134,-10.361315 -2.37314,-0.240789 -4.76368,-0.0457 -7.14443,-0.12428 -5.89141,-0.0074 -11.78281,-0.0066 -17.67422,-0.0028 z' /%3E%3C/g%3E%3C/svg%3E%0A");
}
.folders a {
font-weight: 700;
color: var(--bcolor1);
text-decoration: none;
}
.folders figure:hover img {
content: url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8' standalone='no'%3F%3E%3Csvg height='512' width='512' version='1.1' id='Layer_1' viewBox='0 0 327.68 327.68' xml:space='preserve' xmlns='http://www.w3.org/2000/svg' xmlns:svg='http://www.w3.org/2000/svg'%3E%3Cdefs id='defs1' /%3E%3Cg id='icon' transform='translate(-128.08785,-19.123746)'%3E%3Cpath id='fg' style='fill:%23dcdedf;fill-opacity:1;stroke:none;stroke-width:6.4;stroke-linecap:round' d='m 428.2793,160.97461 c -78.34525,0.002 -156.69075,-0.007 -235.03585,0.0159 -6.54719,0.16827 -12.82625,4.35683 -15.37415,10.41233 -1.13922,2.74038 -1.79474,5.64883 -2.74311,8.45618 -10.27476,33.32809 -20.56379,66.65251 -30.78952,99.99526 -1.21885,4.66702 0.98496,9.97729 5.17832,12.3776 2.40372,1.49255 5.31559,1.67974 8.07056,1.58986 78.16762,-0.0305 156.33557,0.008 234.503,-0.0259 6.43644,-0.17102 12.61041,-4.22124 15.23762,-10.1162 1.2077,-2.75858 1.84495,-5.72012 2.81312,-8.56426 10.08283,-32.66775 20.13843,-65.34481 30.20361,-98.01744 0.84274,-2.37238 1.35887,-4.96865 0.67937,-7.45078 -0.98831,-4.2533 -4.6015,-7.82876 -8.96172,-8.48315 -1.24947,-0.20685 -2.51789,-0.23068 -3.78125,-0.18945 z' /%3E%3Cpath id='bg' style='fill:%231a9fff;fill-opacity:1;stroke:none;stroke-width:6.4;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1;paint-order:normal' d='m 214.44922,72.089844 c -20.32611,0.01324 -40.65359,-0.03032 -60.97883,0.03325 -5.06767,0.228794 -9.72081,4.176673 -10.64873,9.186171 -0.54368,2.865585 -0.16202,5.794266 -0.28069,8.688777 -4.6e-4,27.527908 -0.008,55.056498 0.0176,82.583978 0.17186,3.22428 3.44977,5.89016 6.64757,5.28488 2.4037,-0.30447 4.32209,-2.31086 4.87898,-4.61617 3.53052,-10.60549 6.98896,-21.23707 10.60492,-31.81263 1.14174,-2.71356 4.14972,-4.41014 7.06111,-4.12697 16.80704,-0.0631 33.61433,-0.01 50.42149,-0.0279 55.72175,-0.002 111.44406,0.006 167.16547,-0.0191 5.71319,-0.19462 10.82844,-5.25566 10.91838,-11.00339 0.12602,-2.9908 -0.0352,-5.98579 0.0574,-8.97822 0.0534,-2.61272 -0.3941,-5.33067 -1.94259,-7.50013 -2.21535,-3.37352 -6.26106,-5.45808 -10.30253,-5.1988 -44.42303,-0.0315 -88.8461,0.006 -133.26914,-0.0339 -1.35699,0.0355 -2.86726,0.10531 -3.94821,-0.87625 -1.14131,-0.86056 -1.57137,-2.31551 -1.50179,-3.694889 -0.0148,-5.79966 0.0803,-11.603104 -0.0604,-17.400318 -0.41681,-5.210159 -4.80455,-9.807583 -10.02134,-10.361315 -2.37314,-0.240789 -4.76368,-0.0457 -7.14443,-0.12428 -5.89141,-0.0074 -11.78281,-0.0066 -17.67422,-0.0028 z' /%3E%3C/g%3E%3C/svg%3E%0A");
}
.folders a:hover {
text-decoration: underline;
text-decoration-thickness: 0.3ex;
text-underline-offset: 0.5ex;
color: var(--color2)
}
.row a {
font-weight: 500;
color: var(--color2);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 500;
background-color: var(--color4);
}
.tagentry:hover {
background-color: var(--color3);
}
.column img {
background-color: var(--color4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 700;
}
body {
color: var(--bcolor1);
background-color: var(--bcolor2);
font-family: "Arial", sans-serif;
font-optical-sizing: auto;
font-weight: 500;
font-style: normal;
}

View File

@@ -1,110 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap");
* {
--color1: #FFD700; /* Gold */
--color2: #00BFFF; /* DeepSkyBlue */
--color3: #32CD32; /* LimeGreen */
--color4: #FFA500; /* Orange */
--bcolor1: #FFFFFF; /* White */
--bcolor2: #1E1E1E; /* DarkGrey */
--bcolor3: #F0F8FF; /* AliceBlue */
--bcolor4: #E0FFFF; /* LightCyan */
}
.navbar {
font-weight: 700;
color: var(--bcolor2);
background-color: var(--color1);
font-family: "Roboto", sans-serif;
}
.navbar li a {
font-weight: 500;
color: var(--bcolor2);
font-family: "Roboto", sans-serif;
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor1);
background-color: var(--color3);
font-weight: 500;
font-family: "Roboto", sans-serif;
}
.footer a {
color: var(--color4);
text-decoration: none;
font-weight: 400;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-3.svg.j2";
}
.folders a {
font-weight: 600;
color: var(--bcolor2);
text-decoration: none;
font-family: "Roboto", sans-serif;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color2);
text-decoration: none;
font-family: "Roboto", sans-serif;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 400;
color: var(--bcolor1);
background-color: var(--bcolor2);
font-family: "Roboto", sans-serif;
}
.tagentry:hover {
background-color: var(--bcolor3);
color: var(--bcolor2);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 600;
font-family: "Roboto", sans-serif;
}
body {
color: var(--bcolor2);
background-color: var(--bcolor3);
font-family: "Roboto", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}

View File

@@ -1,102 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,100..900;1,100..900&display=swap");
* {
--color1: #ffb000;
--color2: #ffd700;
--color3: #ffe135;
--color4: #ce8c00;
--bcolor1: #171717;
--bcolor2: #191919;
--bcolor3: #ebebeb;
--bcolor4: #0a0a0a;
}
.navbar {
font-weight: bold;
color: var(--bcolor2);
background-color: var(--color1);
font-weight: 900;
}
.navbar li a {
font-weight: 800;
color: var(--bcolor2);
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor2);
background-color: var(--color3);
font-weight: 700;
}
.footer a {
color: var(--color4);
text-decoration: none;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-1.svg.j2";
}
.folders a {
font-weight: 800;
color: var(--bcolor1);
text-decoration: none;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 800;
color: var(--color1);
text-decoration: none;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 600;
color: var(--bcolor3);
background-color: var(--bcolor2);
}
.tagentry:hover {
background-color: var(--bcolor4);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 800;
}
body {
color: var(--bcolor1);
background-color: var(--bcolor3);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 700;
font-style: normal;
}

View File

@@ -1,117 +0,0 @@
@import url("https://fonts.googleapis.com/css2?family=Montserrat:wght@300;400;500;600;700&display=swap");
* {
--color1: #00CED1;
/* DarkTurquoise */
--color2: #4682B4;
/* SteelBlue */
--color3: #B0C4DE;
/* LightSteelBlue */
--color4: #5F9EA0;
/* CadetBlue */
--bcolor1: #FFFFFF;
/* White */
--bcolor2: #2F4F4F;
/* DarkSlateGray */
--bcolor3: #E0E0E0;
/* LightGray */
--bcolor4: #D3D3D3;
/* LightGray */
}
.navbar {
font-weight: 600;
color: var(--bcolor2);
background-color: var(--color1);
font-family: "Montserrat", sans-serif;
}
.navbar li a {
font-weight: 500;
color: var(--bcolor2);
font-family: "Montserrat", sans-serif;
}
/* Change the link color on hover */
.navbar li a:hover {
text-decoration: none;
background-color: var(--color2);
}
.footer {
color: var(--bcolor2);
background-color: var(--color3);
font-weight: 500;
font-family: "Montserrat", sans-serif;
}
.footer a {
color: var(--color2);
text-decoration: none;
font-weight: 400;
}
.footer a:hover {
text-decoration: none;
}
.foldericon {
content: "themes/icons/folder-3.svg.j2";
}
.folders a {
font-weight: 600;
color: var(--bcolor2);
text-decoration: none;
font-family: "Montserrat", sans-serif;
}
.folders a:hover {
text-decoration: none;
}
.row a {
font-weight: 500;
color: var(--color2);
text-decoration: none;
font-family: "Montserrat", sans-serif;
}
.row a:hover {
text-decoration: underline;
}
.tooltiptext {
font-weight: 400;
color: var(--bcolor3);
background-color: var(--bcolor2);
font-family: "Montserrat", sans-serif;
}
.tagentry:hover {
background-color: var(--color2);
}
.column img {
background-color: var(--bcolor4);
}
#totop:hover {
background-color: var(--color2);
}
#totop {
background-color: var(--color1);
color: var(--bcolor1);
font-weight: 600;
font-family: "Montserrat", sans-serif;
}
body {
color: var(--bcolor2);
background-color: var(--bcolor3);
font-family: "Montserrat", sans-serif;
font-optical-sizing: auto;
font-weight: 400;
font-style: normal;
}