Add zs init command to initialize new site

This commit is contained in:
James Mills 2024-12-25 12:09:10 +10:00
parent 5e8f404eb5
commit 1e84d9a585
No known key found for this signature in database
GPG Key ID: AC4C014F1440EBD6
29 changed files with 2494 additions and 68 deletions

5
default/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
*~
*.bak
**/.DS_Store
/.pub

14
default/.zs/config.yml Normal file
View File

@ -0,0 +1,14 @@
---
title: Twtxt Docs
description: Twtxt documentation for developers
keywords: docs, twtxt, markdown, text, format, documentation
extensions:
- anchor
- definitionlist
- linkify
- footnote
- strikethrough
- table
- typography
- wikilink

9
default/.zs/editthispage Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
GITEA_BASE_URL="https://git.mills.io"
REPO="yarnsocial/twtxt.dev"
BRANCH="main"
url="${GITEA_BASE_URL}/${REPO}/_edit/${BRANCH}/${ZS_FILE}"
printf "<a href=\"%s\" rel=\"nofollow\" target=\"_blank\">✍️ Edit Page on Gitea</a>" "$url"

14
default/.zs/include Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
set -e
if [ ! $# = 1 ]; then
printf "Usage: %s <file>\n" "$(basename "$0")"
exit 0
fi
if [ -f "$1" ]; then
cat "$1"
else
echo "error: file not found $1"
fi

64
default/.zs/layout.html Normal file
View File

@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Meta Tags -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
<!-- Favicon -->
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<!-- Stylesheets -->
{{ styles }}
</head>
<body>
<!-- Header with Navigation -->
<header>
<div class="container">
<div class="header-top">
<a href="/" class="logo">
<img src="/logo.png" alt="{{ title }}" class="logo-img">
{{ title }}
</a>
<button id="menu-toggle" aria-label="Toggle Navigation">&#9776;</button>
</div>
<nav id="main-nav">
<ul>
{{ nav }}
</ul>
</nav>
</div>
</header>
<!-- Main Content -->
<main class="container">
<article>{{ content }}</article>
</main>
<!-- Footer -->
<footer>
<div class="container">
<span class="edit-page-link">
{{ editthispage }}
</span>
<p>
Last modified <time datetime="{{ date -u +%Y-%m-%dT%H:%M:%SZ }}">{{ date -u }}</time>
Built with <a href="https://git.mills.io/prologic/zs">zs</a>
</p>
<button id="theme-toggle" aria-label="Toggle Theme">🌓</button>
</div>
</footer>
<!-- Scripts -->
{{ scripts }}
<script>
hljs.highlightAll();
</script>
</body>
</html>

90
default/.zs/list Executable file
View File

@ -0,0 +1,90 @@
#!/bin/sh
# Define the directory to iterate over from the first argument
dir="$1"
# Initialize the variable to hold the HTML list and date-file pairs
html=""
date_file_pairs=""
# Check if the directory exists
if [ -d "$dir" ]; then
# Find all Markdown files in the directory and store them in a variable
md_files=$(find "$dir" -maxdepth 1 -type f -name "*.md")
# Process each Markdown file in a regular loop (not in a subshell)
for md_file in $md_files; do
# Extract front matter using sed, ensure the file is quoted
front_matter=$(sed -n '/^---$/,/^---$/p' "$md_file")
# Extract date
date=$(echo "$front_matter" | grep '^date:' | sed 's/^date:[[:space:]]*//')
# If no date is found, use a default date
if [ -z "$date" ]; then
date="1970-01-01" # Default date if none is found
fi
# Add the date and file to the date_file_pairs variable, using printf to correctly append newlines
date_file_pairs=$(printf "%s\n%s|%s" "$date_file_pairs" "$date" "$md_file")
done
# Sort the date and file pairs by date
sorted_date_file_pairs=$(printf "%s" "$date_file_pairs" | sort)
# Create a file descriptor to avoid subshell issues
exec 3<<EOF
$sorted_date_file_pairs
EOF
# Generate the navigation list
html="<ul class=\"list\">"
while IFS= read -r pair <&3; do
# Split the pair into date and filename using | as a delimiter
date=$(printf "%s" "$pair" | cut -d'|' -f1)
md_file=$(printf "%s" "$pair" | cut -d'|' -f2)
# Ensure md_file is not empty (safety check)
[ -z "$md_file" ] && continue
# Extract front matter again using sed, ensure md_file is quoted
front_matter=$(sed -n '/^---$/,/^---$/p' "$md_file")
# Extract title
title=$(echo "$front_matter" | grep '^title:' | sed 's/^title:[[:space:]]*//')
# Use filename as a fallback if title is empty
if [ -z "$title" ]; then
# Get the base filename without extension, quote the argument
base_name=$(basename "$md_file" .md)
# Replace hyphens and underscores with spaces for display
title=$(echo "$base_name" | sed 's/-/ /g' | sed 's/_/ /g')
# Capitalize each word
title=$(echo "$title" | awk '{ for (i=1; i<=NF; i++) $i=toupper(substr($i,1,1)) substr($i,2); print }')
fi
# Format the date using POSIX-compliant date formatting
if formatted_date=$(date -d "$date" '+%Y-%m-%d' 2>/dev/null); then
: # formatted_date is valid
else
formatted_date="$date"
fi
# Construct the link href, quote the argument
href="/$dir/$(basename "${md_file%.md}.html")"
# Append to the HTML list
html="$html<li><a href=\"$href\">$title</a> (published: $formatted_date)</li>"
done
html="$html</ul>"
# Close file descriptor
exec 3<&-
else
# If the directory doesn't exist, set a message
html="<p>No pages found.</p>"
fi
# Output the HTML list
echo "$html"

57
default/.zs/nav Executable file
View File

@ -0,0 +1,57 @@
#!/bin/sh
# Get the directory of the current page
current_dir=$(dirname "$ZS_SOURCE_FILE")
# Remove leading './' if present
case "$current_dir" in
./*) current_dir="${current_dir#./}" ;;
esac
# Initialize the navigation HTML with the 'Home' link
if [ "$ZS_URL" = "index.html" ]; then
nav_html="<li><a href=\"/index.html\" class=\"active\">Home</a></li>"
else
nav_html="<li><a href=\"/\">Home</a></li>"
fi
# Create a temporary file to store filenames
tmpfile=$(mktemp)
# Find all Markdown files in the current directory, excluding 'README.md' and 'index.md'
find "$current_dir" -maxdepth 1 -type f -name "*.md" ! -name "README.md" ! -name "index.md" -print >"$tmpfile"
# Sort the files alphabetically
sort "$tmpfile" >"${tmpfile}.sorted"
# Read each file and process
while IFS= read -r md_file; do
# Get the base filename without extension
base_name=$(basename "$md_file" .md)
# Replace hyphens and underscores with spaces for display
display_name=$(echo "$base_name" | tr '-' ' ' | tr '_' ' ')
# Capitalize the first letter of each word using awk
display_name=$(echo "$display_name" | awk '{ for (i=1; i<=NF; i++) { $i = toupper(substr($i,1,1)) substr($i,2) } print }')
# Construct the link href relative to the site root
if [ "$current_dir" = "." ]; then
href="/${base_name}.html"
else
href="/${current_dir}/${base_name}.html"
fi
# Append the navigation item
if [ "$ZS_URL" = "$base_name.html" ]; then
nav_html="${nav_html}<li><a href=\"${href}\" class=\"active\">${display_name}</a></li>"
else
nav_html="${nav_html}<li><a href=\"${href}\">${display_name}</a></li>"
fi
done <"${tmpfile}.sorted"
# Remove temporary files
rm -f "$tmpfile" "${tmpfile}.sorted"
# Output the navigation HTML
echo "$nav_html"

9
default/.zs/onthispage Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
set -e
{
echo "**On this page**"
echo
toc -d 2 -s 1 "$ZS_FILE"
} | zs gen

19
default/.zs/posthook Executable file
View File

@ -0,0 +1,19 @@
#!/bin/sh
set -e
minify_assets() {
p="$1"
t="$2"
find "$p" -type f -name "*.$t" | while read -r file; do
name="${file#"$p"}"
name="${name#"/"}"
minify -o "${p}/${name}" "$file"
done
}
if command -v minify > /dev/null; then
minify_assets "$ZS_OUTDIR" "css"
minify_assets "$ZS_OUTDIR" "js"
fi

3
default/.zs/prehook Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
exit 0

14
default/.zs/scripts Executable file
View File

@ -0,0 +1,14 @@
#!/bin/sh
set -e
JS="highlight main"
# Load live.js for non-production builds for faster development
if [ -z "$ZS_PRODUCTION" ]; then
JS="$JS live"
fi
for js in $JS; do
printf "<script type=\"application/javascript\" src=\"/assets/js/%s.js\"></script>\n" "$js"
done

9
default/.zs/styles Executable file
View File

@ -0,0 +1,9 @@
#!/bin/sh
set -e
CSS="highlight site"
for css in $CSS; do
printf "<link rel=\"stylesheet\" href=\"/assets/css/%s.css\">\n" "$css"
done

6
default/.zsignore Normal file
View File

@ -0,0 +1,6 @@
*~
*.bak
LICENSE
Makefile
README.md

13
default/LICENSE Normal file
View File

@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

29
default/Makefile Normal file
View File

@ -0,0 +1,29 @@
.PHONY: deps dev build image clean
GOCMD=go
IMAGE := r.mills.io/prologic/twtxt.dev
TAG := latest
all: build
deps:
@$(GOCMD) install go.mills.io/zs@latest
@$(GOCMD) install github.com/tdewolff/minify/v2/cmd/minify@latest
dev : DEBUG=1
dev : build
@zs serve
build:
@zs build
ifeq ($(PUBLISH), 1)
image:
@docker buildx build --platform linux/amd64,linux/arm64 --push -t $(IMAGE):$(TAG) .
else
image:
@docker build -t $(IMAGE):$(TAG) .
endif
clean:
@git clean -f -d

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1,9 @@
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}/*!
Theme: GitHub Dark Dimmed
Description: Dark dimmed theme as seen on github.com
Author: github.com
Maintainer: @Hirse
Updated: 2021-05-15
Colors taken from GitHub's CSS
*/.hljs{color:#adbac7;background:#22272e}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#f47067}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#dcbdfb}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-variable{color:#6cb6ff}.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#96d0ff}.hljs-built_in,.hljs-symbol{color:#f69d50}.hljs-code,.hljs-comment,.hljs-formula{color:#768390}.hljs-name,.hljs-quote,.hljs-selector-pseudo,.hljs-selector-tag{color:#8ddb8c}.hljs-subst{color:#adbac7}.hljs-section{color:#316dca;font-weight:700}.hljs-bullet{color:#eac55f}.hljs-emphasis{color:#adbac7;font-style:italic}.hljs-strong{color:#adbac7;font-weight:700}.hljs-addition{color:#b4f1b4;background-color:#1b4721}.hljs-deletion{color:#ffd8d3;background-color:#78191b}

View File

@ -0,0 +1,48 @@
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

447
default/assets/css/site.css Normal file
View File

@ -0,0 +1,447 @@
/* site.css */
/* Default Light Theme Variables */
:root {
--background-color: #ffffff;
--text-color: #333333;
--link-color: #1d5ed0;
--link-hover-color: #0a47ab;
/* Improved hover contrast for light mode */
--header-background: #f8f9fa;
--footer-background: #f8f9fa;
--button-background: transparent;
--button-color: #333333;
--nav-link-color: #333333;
--nav-link-active: #ccc;
--quote-border-color: #ccc;
--quote-text-color: #555;
--quote-background-color: #f9f9f9;
}
/* Dark Theme Variables */
[data-theme="dark"] {
--background-color: #121212;
--text-color: #e0e0e0;
--link-color: #64B5F6;
/* Light blue with good contrast */
--link-hover-color: #2196F3;
/* Brighter blue for hover effect */
--header-background: #1f1f1f;
--footer-background: #1f1f1f;
--button-background: transparent;
--button-color: #e0e0e0;
--nav-link-color: #e0e0e0;
--nav-link-active: #333333;
--quote-border-color: #555;
--quote-text-color: #ccc;
--quote-background-color: #1e1e1e;
}
/* Global Styles */
body {
margin: 0;
font-family: Arial, sans-serif;
background-color: var(--background-color);
color: var(--text-color);
overflow-x: hidden;
/* Prevent horizontal scrolling */
}
/* Global Link Styles */
a {
color: var(--link-color);
text-decoration: none;
}
a:hover,
a:focus {
color: var(--link-hover-color);
text-decoration: underline;
}
/* Adjusted Container */
.container {
width: 95%;
max-width: 900px;
margin: 0 auto;
padding: 0;
box-sizing: border-box;
/* Include padding and border in width */
}
/* Adjust margins for child elements */
.container h1,
.container h2,
.container h3,
.container h4,
.container h5,
.container h6,
.container p,
.container ul,
.container ol,
.container pre,
.container code,
.container blockquote {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
/* Header */
header {
background-color: var(--header-background);
padding: 0.5em 0;
}
header .container {
display: flex;
flex-direction: column;
}
.header-top {
display: flex;
align-items: center;
justify-content: space-between;
}
header .logo {
font-size: 1.5em;
font-weight: bold;
color: var(--text-color);
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Theme Toggle Button */
#theme-toggle {
background: var(--button-background);
border: none;
cursor: pointer;
color: var(--button-color);
font-size: 1.5em;
margin-left: 1em;
}
/* Menu Toggle Button */
#menu-toggle {
background: var(--button-background);
border: none;
cursor: pointer;
color: var(--button-color);
font-size: 1.5em;
display: none;
/* Hidden by default */
}
/* Navigation Menu */
nav#main-nav {
margin-top: 0.5em;
}
nav ul {
list-style: none;
padding: 0;
margin: 0;
display: flex;
gap: 0.5em;
flex-wrap: wrap;
}
nav ul li {
margin: 0;
}
nav ul li a {
display: block;
color: var(--nav-link-color);
text-decoration: none;
padding: 0.5em;
}
nav ul li a.active {
background-color: var(--nav-link-active);
}
nav ul li a:hover {
background-color: var(--nav-link-active);
text-decoration: none;
color: var(--nav-link-color);
}
/* Navigation Logo Styling */
header .logo {
display: flex;
/* Make the logo a flex container */
align-items: center;
/* Vertically center the content */
font-size: 1.5em;
font-weight: bold;
color: var(--text-color);
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
header .logo img {
height: 40px;
/* Adjust logo size */
width: auto;
margin-right: 0.5em;
/* Space between the logo and the text */
}
@media (max-width: 800px) {
/* Adjust header layout */
.header-top {
flex-wrap: nowrap;
justify-content: space-between;
/* Push the elements to the left and right ends */
}
/* Show the menu toggle button */
#menu-toggle {
display: block;
order: 2;
/* Move the menu toggle to the right */
}
/* Keep the logo on the left */
header .logo {
order: 1;
margin-left: 0.5em;
font-size: 1.2em;
}
/* Adjust theme toggle button */
#theme-toggle {
order: 3;
margin-left: auto;
}
/* Hide the navigation menu by default */
nav#main-nav {
display: none;
order: 4;
width: 100%;
}
/* Show the navigation menu when toggled */
nav#main-nav.open {
display: block;
}
/* Stack the navigation links vertically */
nav ul {
flex-direction: column;
gap: 0;
}
nav ul li a {
padding: 0.75em 0 0.75rem 0.75em;
}
}
@media (min-width: 801px) {
/* Hide the menu toggle button on large screens */
#menu-toggle {
display: none;
}
/* Ensure navigation is visible on large screens */
nav#main-nav {
display: block !important;
}
/* Reset order and margins */
.header-top {
flex-wrap: wrap;
}
header .logo {
order: 0;
margin-left: 0;
}
#theme-toggle {
order: 0;
margin-left: 1em;
}
}
/* Main Content */
main {
padding: 1em 0;
}
article {
line-height: 1.6;
}
article h2 {
margin-top: 1.5em;
}
aside {
width: 33%;
padding-left: .5rem;
margin-left: .5rem;
float: right;
border-left: 2px solid var(--button-color);
font-style: italic;
}
aside>p {
margin: .5rem;
}
/* Inline Code */
code {
background-color: #f5f5f5;
padding: 0.2em 0.4em;
margin: 0;
font-size: 85%;
border-radius: 3px;
}
[data-theme="dark"] code {
background-color: #1e1e1e;
color: #e0e0e0;
}
/* Code Blocks */
pre {
background-color: #f5f5f5;
padding: 0.5em;
overflow-x: auto;
margin-top: 0.5em;
margin-bottom: 0.5em;
max-width: 100%;
box-sizing: border-box;
}
[data-theme="dark"] pre {
background-color: #1e1e1e;
}
pre code {
background-color: inherit;
/* Use the same background as pre */
padding: 0;
margin: 0;
font-size: inherit;
color: inherit;
border-radius: 0;
}
/* Images and Media */
img,
iframe,
video {
max-width: 100%;
height: auto;
}
/* Footer */
footer {
background-color: var(--footer-background);
padding: 1em 0;
}
footer .container {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
gap: 1em;
/* Adds space between footer items */
}
footer p {
margin: 0;
/* Ensure there's no margin collapse and the text stays separated */
}
footer .edit-page-link {
margin-left: auto;
/* Push the edit page link to the far right */
}
footer .edit-page-link a {
color: var(--link-color);
}
footer .edit-page-link a:hover {
text-decoration: underline;
color: var(--link-hover-color);
/* Improved hover color in footer */
}
/* Responsive Footer */
@media (max-width: 600px) {
footer .container {
flex-direction: column;
align-items: flex-start;
gap: 0.5em;
/* Adds vertical space between items */
}
footer .edit-page-link {
margin-left: 0;
margin-top: 0.5em;
}
}
/* Styles for Page Lists */
.list {
list-style-type: none;
padding-left: 0;
}
.list li {
margin-bottom: 0.5em;
}
.list li::before {
content: "• ";
color: var(--link-color);
}
/* Blockquote Styles */
blockquote {
border-left: 4px solid var(--quote-border-color);
padding-left: 1em;
margin-left: 0;
margin-right: 0;
color: var(--quote-text-color);
background-color: var(--quote-background-color);
font-style: italic;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
/* Optional Inline Quote Styles */
q {
quotes: "“""”""""";
font-style: italic;
color: var(--quote-text-color);
}
/* Anchors */
h1:hover .anchor:before,
h2:hover .anchor:before,
h3:hover .anchor:before,
h4:hover .anchor:before,
h5:hover .anchor:before,
h6:hover .anchor:before {
width: 16px;
height: 16px;
content: ' ';
display: inline-block;
background-color: currentColor;
-webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
}

File diff suppressed because one or more lines are too long

233
default/assets/js/live.js Normal file
View File

@ -0,0 +1,233 @@
/*
Live.js - One script closer to Designing in the Browser
Written for Handcraft.com by Martin Kool (@mrtnkl).
Version 4.
Recent change: Made stylesheet and mimetype checks case insensitive.
http://livejs.com
http://livejs.com/license (MIT)
@livejs
Include live.js#css to monitor css changes only.
Include live.js#js to monitor js changes only.
Include live.js#html to monitor html changes only.
Mix and match to monitor a preferred combination such as live.js#html,css
By default, just include live.js to monitor all css, js and html changes.
Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then,
as a page reload due to a change in html or css would not re-include the bookmarklet.
To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify
*/
(function () {
var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 },
resources = {},
pendingRequests = {},
currentLinkElements = {},
oldLinkElements = {},
interval = 1000,
loaded = false,
active = { "html": 1, "css": 1, "js": 1 };
var Live = {
// performs a cycle per interval
heartbeat: function () {
if (document.body) {
// make sure all resources are loaded on first activation
if (!loaded) Live.loadresources();
Live.checkForChanges();
}
setTimeout(Live.heartbeat, interval);
},
// loads all local css and js resources upon first activation
loadresources: function () {
// helper method to assert if a given url is local
function isLocal(url) {
var loc = document.location,
reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host);
return url.match(reg);
}
// gather all resources
var scripts = document.getElementsByTagName("script"),
links = document.getElementsByTagName("link"),
uris = [];
// track local js urls
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i], src = script.getAttribute("src");
if (src && isLocal(src))
uris.push(src);
if (src && src.match(/\blive.js#/)) {
for (var type in active)
active[type] = src.match("[#,|]" + type) != null
if (src.match("notify"))
alert("Live.js is loaded.");
}
}
if (!active.js) uris = [];
if (active.html) uris.push(document.location.href);
// track local css urls
for (var i = 0; i < links.length && active.css; i++) {
var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2);
if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) {
uris.push(href);
currentLinkElements[href] = link;
}
}
// initialize the resources info
for (var i = 0; i < uris.length; i++) {
var url = uris[i];
Live.getHead(url, function (url, info) {
resources[url] = info;
});
}
// add rule for morphing between old and new css files
var head = document.getElementsByTagName("head")[0],
style = document.createElement("style"),
rule = "transition: all .3s ease-out;"
css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join('');
style.setAttribute("type", "text/css");
head.appendChild(style);
style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css));
// yep
loaded = true;
},
// check all tracking resources for changes
checkForChanges: function () {
for (var url in resources) {
if (pendingRequests[url])
continue;
Live.getHead(url, function (url, newInfo) {
var oldInfo = resources[url],
hasChanged = false;
resources[url] = newInfo;
for (var header in oldInfo) {
// do verification based on the header type
var oldValue = oldInfo[header],
newValue = newInfo[header],
contentType = newInfo["Content-Type"];
switch (header.toLowerCase()) {
case "etag":
if (!newValue) break;
// fall through to default
default:
hasChanged = oldValue != newValue;
break;
}
// if changed, act
if (hasChanged) {
Live.refreshResource(url, contentType);
break;
}
}
});
}
},
// act upon a changed url of certain content type
refreshResource: function (url, type) {
switch (type.toLowerCase()) {
// css files can be reloaded dynamically by replacing the link element
case "text/css":
var link = currentLinkElements[url],
html = document.body.parentNode,
head = link.parentNode,
next = link.nextSibling,
newLink = document.createElement("link");
html.className = html.className.replace(/\s*livejs\-loading/gi, '') + ' livejs-loading';
newLink.setAttribute("type", "text/css");
newLink.setAttribute("rel", "stylesheet");
newLink.setAttribute("href", url + "?now=" + new Date() * 1);
next ? head.insertBefore(newLink, next) : head.appendChild(newLink);
currentLinkElements[url] = newLink;
oldLinkElements[url] = link;
// schedule removal of the old link
Live.removeoldLinkElements();
break;
// check if an html resource is our current url, then reload
case "text/html":
if (url != document.location.href)
return;
// local javascript changes cause a reload as well
case "text/javascript":
case "application/javascript":
case "application/x-javascript":
document.location.reload();
}
},
// removes the old stylesheet rules only once the new one has finished loading
removeoldLinkElements: function () {
var pending = 0;
for (var url in oldLinkElements) {
// if this sheet has any cssRules, delete the old link
try {
var link = currentLinkElements[url],
oldLink = oldLinkElements[url],
html = document.body.parentNode,
sheet = link.sheet || link.styleSheet,
rules = sheet.rules || sheet.cssRules;
if (rules.length >= 0) {
oldLink.parentNode.removeChild(oldLink);
delete oldLinkElements[url];
setTimeout(function () {
html.className = html.className.replace(/\s*livejs\-loading/gi, '');
}, 100);
}
} catch (e) {
pending++;
}
if (pending) setTimeout(Live.removeoldLinkElements, 50);
}
},
// performs a HEAD request and passes the header info to the given callback
getHead: function (url, callback) {
pendingRequests[url] = true;
var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp");
xhr.open("HEAD", url, true);
xhr.onreadystatechange = function () {
delete pendingRequests[url];
if (xhr.readyState == 4 && xhr.status != 304) {
xhr.getAllResponseHeaders();
var info = {};
for (var h in headers) {
var value = xhr.getResponseHeader(h);
// adjust the simple Etag variant to match on its significant part
if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, '');
if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1");
info[h] = value;
}
callback(url, info);
}
}
xhr.send();
}
};
// start listening
if (document.location.protocol != "file:") {
if (!window.liveJsLoaded)
Live.heartbeat();
window.liveJsLoaded = true;
}
else if (window.console)
console.log("Live.js doesn't support the file protocol. It needs http.");
})();

41
default/assets/js/main.js Normal file
View File

@ -0,0 +1,41 @@
// Toggle the navigation menu and theme
document.addEventListener('DOMContentLoaded', function() {
var menuToggle = document.getElementById('menu-toggle');
var mainNav = document.getElementById('main-nav');
var themeToggle = document.getElementById('theme-toggle');
var body = document.body;
// Menu toggle functionality
menuToggle.addEventListener('click', function() {
mainNav.classList.toggle('open');
});
// Theme toggle functionality
themeToggle.addEventListener('click', function() {
// Toggle between 'light' and 'dark' themes
var currentTheme = body.getAttribute('data-theme') || 'light';
var newTheme = currentTheme === 'dark' ? 'light' : 'dark';
body.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
});
// On page load, set the theme from localStorage or system preference
var storedTheme = localStorage.getItem('theme');
if (storedTheme) {
body.setAttribute('data-theme', storedTheme);
} else {
// Detect system preference
var prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
var defaultTheme = prefersDarkScheme ? 'dark' : 'light';
body.setAttribute('data-theme', defaultTheme);
}
// Listen for changes in the system color scheme
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function(e) {
var storedTheme = localStorage.getItem('theme');
if (!storedTheme) {
var newColorScheme = e.matches ? 'dark' : 'light';
body.setAttribute('data-theme', newColorScheme);
}
});
});

BIN
default/favicon-16x16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

BIN
default/favicon-32x32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 675 B

5
default/index.md Normal file
View File

@ -0,0 +1,5 @@
---
title: my.zs.site
---
👋 Hello World! 👋j

BIN
default/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

13
go.mod
View File

@ -6,6 +6,7 @@ require (
github.com/13rac1/goldmark-embed v0.0.0-20201220231550-e6806f2de66a
github.com/FurqanSoftware/goldmark-d2 v0.0.0-20230207071629-ec535d32ca47
github.com/alecthomas/chroma/v2 v2.7.0
github.com/gabriel-vasile/mimetype v1.4.7
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
@ -16,7 +17,7 @@ require (
go.abhg.dev/goldmark/anchor v0.1.1
go.abhg.dev/goldmark/wikilink v0.5.0
go.mills.io/static v0.0.0-20230401145044-4ee04f8d2f65
golang.org/x/sync v0.1.0
golang.org/x/sync v0.9.0
gopkg.in/yaml.v2 v2.4.0
)
@ -55,13 +56,13 @@ require (
github.com/subosito/gotenv v1.4.2 // indirect
github.com/unrolled/logger v0.0.0-20201216141554-31a3694fe979 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/crypto v0.29.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/image v0.6.0 // indirect
golang.org/x/net v0.8.0 // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.6.0 // indirect
golang.org/x/text v0.8.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/term v0.26.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
gonum.org/v1/plot v0.12.0 // indirect
google.golang.org/genproto v0.0.0-20230222225845-10f96fb3dbec // indirect

64
go.sum
View File

@ -1,5 +1,3 @@
cdr.dev/slog v1.4.2-0.20221206192828-e4803b10ae17 h1:Jf+VOk2lif79HeTlnLaZ70zYTsuVSUEu/47U9VaG2Rw=
cdr.dev/slog v1.4.2-0.20221206192828-e4803b10ae17/go.mod h1:YPVZsUbRMaLaPgme0RzlPWlC7fI7YmDj/j/kZLuvICs=
cdr.dev/slog v1.4.2 h1:fIfiqASYQFJBZiASwL825atyzeA96NsqSxx2aL61P8I=
cdr.dev/slog v1.4.2/go.mod h1:0EkH+GkFNxizNR+GAXUEdUHanxUH5t9zqPILmPM/Vn8=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
@ -55,18 +53,15 @@ github.com/FurqanSoftware/goldmark-d2 v0.0.0-20230207071629-ec535d32ca47 h1:TTYi
github.com/FurqanSoftware/goldmark-d2 v0.0.0-20230207071629-ec535d32ca47/go.mod h1:re06sclYNSEKe8tBIZ7KweUIZECQewQSCaKKFyc1TpE=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM=
github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ=
github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw=
github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink=
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
github.com/alecthomas/chroma/v2 v2.2.0 h1:Aten8jfQwUqEdadVFFjNyjx7HTexhKP0XuqBG67mRDY=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.7.0 h1:hm1rY6c/Ob4eGclpQ7X/A3yhqBOZNUTk9q+yhyLIViI=
github.com/alecthomas/chroma/v2 v2.7.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae h1:zzGwJfFlFGD94CyyYwCJeSuD32Gj9GTaSi5y9hoVzdY=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
@ -93,13 +88,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.8.0 h1:rJD5HeGIT/2b5CDk63FVCwZA3qgYElfg+oQK7uH5pfE=
github.com/dlclark/regexp2 v1.8.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0=
github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20230203172422-5460598cfa32 h1:audXtK7nV3y4W9ckAxRBE+eQV5Bljf5Non4NTa9kLVE=
github.com/dop251/goja v0.0.0-20230203172422-5460598cfa32/go.mod h1:yRkwfj0CBpOGre+TwBsqPV0IH0Pk73e4PXJOeNDboGs=
github.com/dop251/goja v0.0.0-20230304130813-e2f543bf4b4c h1:/utv6nmTctV6OVgfk5+O6lEMEWL+6KJy4h9NZ5fnkQQ=
github.com/dop251/goja v0.0.0-20230304130813-e2f543bf4b4c/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
@ -111,13 +102,13 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.7 h1:SKFKl7kD0RiPdbht0s7hFtjl489WcQ1VyPW8ZzUMYCA=
github.com/gabriel-vasile/mimetype v1.4.7/go.mod h1:GDlAgAyIRT27BhFl53XNAFtfjzOkLaF35JdEG0P7LtU=
github.com/go-fonts/liberation v0.2.0 h1:jAkAWJP4S+OsrPLZM4/eC9iW7CtHy+HBXrEwZXWo5VM=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -194,6 +185,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
@ -227,16 +219,12 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mazznoer/csscolorparser v0.1.3 h1:vug4zh6loQxAUxfU1DZEu70gTPufDPspamZlHAkKcxE=
github.com/mazznoer/csscolorparser v0.1.3/go.mod h1:Aj22+L/rYN/Y6bj3bYqO3N6g1dtdHtGfQ32xZ5PJQic=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU=
github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us=
github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek=
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
@ -249,16 +237,14 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk=
github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y=
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
@ -304,8 +290,6 @@ go.abhg.dev/goldmark/anchor v0.1.1 h1:NUH3hAzhfeymRqZKOkSoFReZlEAmfXBZlbXEzpD2Qg
go.abhg.dev/goldmark/anchor v0.1.1/go.mod h1:zYKiaHXTdugwVJRZqInVdmNGQRM3ZRJ6AGBC7xP7its=
go.abhg.dev/goldmark/wikilink v0.5.0 h1:/Gndy7+PoXzOc3reVWtXAh7Cni7wSqSxiuXDfmoYlm4=
go.abhg.dev/goldmark/wikilink v0.5.0/go.mod h1:W1NzvDIpo6uoayolBTCsIL6y/QRAHmLTKfUUDfR75DA=
go.mills.io/static v0.0.0-20230316074605-51908d346ffc h1:BMeFNGUZAQCfgo8DxUKxgGNrXQa1KglS5gZ4Kn5fUsQ=
go.mills.io/static v0.0.0-20230316074605-51908d346ffc/go.mod h1:TmaEDwM+IgrCRyMxtVWtmSdoxLP3N6ehBa7AiOZj2Mk=
go.mills.io/static v0.0.0-20230401145044-4ee04f8d2f65 h1:1uqQoVNdJsKdNBLiUmO7nVj9vgfzTvlRnDEzZTt3ub8=
go.mills.io/static v0.0.0-20230401145044-4ee04f8d2f65/go.mod h1:TmaEDwM+IgrCRyMxtVWtmSdoxLP3N6ehBa7AiOZj2Mk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -316,7 +300,9 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -324,12 +310,9 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -340,15 +323,11 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 h1:xP7rWLUr1e1n2xkK5YB4LI0hPEy3LJC6Wk+D4pGlOJg=
golang.org/x/exp v0.0.0-20230206171751-46f607a40771/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.3.0 h1:HTDXbdK9bjfSWkPzDJIw89W8CAtfFGduujWs33NLLsg=
golang.org/x/image v0.3.0/go.mod h1:fXd9211C/0VTlYuAcOhW8dY/RtEJqODXOWBDpmYBf+A=
golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4=
golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -410,10 +389,9 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -434,8 +412,9 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -481,14 +460,14 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -498,11 +477,10 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -675,12 +653,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
oss.terrastruct.com/d2 v0.1.6 h1:TdNET4uzt3Djl66UriSR3sbItWYcCV9ugpHzF9w2eIY=
oss.terrastruct.com/d2 v0.1.6/go.mod h1:7sIRmEj3QeXVB6erSPW6dOfFUX7orsQkO0twmTYILEA=
oss.terrastruct.com/d2 v0.3.0 h1:hhrtD2P8mLbUDlxMzyu7sgJ3O7Y59EYpLozDdi+DT3g=
oss.terrastruct.com/d2 v0.3.0/go.mod h1:7aLT1RP98WkuV6PQFtmCvdO83gOk2unuDiqYHFP2Ww8=
oss.terrastruct.com/util-go v0.0.0-20230124232704-39c2226d2b5e h1:pCKxiUFOLQamCtmyMZsM3Hc8MuKVpDg/4VunhVOVW/4=
oss.terrastruct.com/util-go v0.0.0-20230124232704-39c2226d2b5e/go.mod h1:Fwy72FDIOOM4K8F96ScXkxHHppR1CPfUyo9+x9c1PBU=
oss.terrastruct.com/util-go v0.0.0-20230320053557-dcb5aac7d972 h1:HS7fg2GzGsqRLApsoh7ztaLMvXzxSln/Hfz4wy4tIDA=
oss.terrastruct.com/util-go v0.0.0-20230320053557-dcb5aac7d972/go.mod h1:eMWv0sOtD9T2RUl90DLWfuShZCYp4NrsqNpI8eqO6U4=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

145
main.go
View File

@ -4,9 +4,11 @@ package main
import (
"bytes"
"context"
"embed"
"errors"
"fmt"
"io"
"io/fs"
"io/ioutil"
"os"
"os/exec"
@ -17,16 +19,17 @@ import (
"text/template"
"time"
embed "github.com/13rac1/goldmark-embed"
d2 "github.com/FurqanSoftware/goldmark-d2"
chromahtml "github.com/alecthomas/chroma/v2/formatters/html"
ignore "github.com/sabhiram/go-gitignore"
embedExt "github.com/13rac1/goldmark-embed"
d2Ext "github.com/FurqanSoftware/goldmark-d2"
chromaHTMLExt "github.com/alecthomas/chroma/v2/formatters/html"
"github.com/gabriel-vasile/mimetype"
gitIgnore "github.com/sabhiram/go-gitignore"
log "github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
fences "github.com/stefanfritsch/goldmark-fences"
fencesExt "github.com/stefanfritsch/goldmark-fences"
"github.com/yuin/goldmark"
highlighting "github.com/yuin/goldmark-highlighting/v2"
highlightingExt "github.com/yuin/goldmark-highlighting/v2"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
"github.com/yuin/goldmark/renderer/html"
@ -62,7 +65,7 @@ README.md`
)
// Ignore holds a set of patterns to ignore from parsing a .zsignore file
var Ignore *ignore.GitIgnore
var Ignore *gitIgnore.GitIgnore
// Parser holds a configured global instance of the goldmark markdown parser
var Parser goldmark.Markdown
@ -70,6 +73,9 @@ var Parser goldmark.Markdown
var (
configFile string
enabledExtensions []string
//go:embed default default/.zs
defaultFS embed.FS
)
// Extensions is a mapping of name to extension and the default set of extensions enabled
@ -84,16 +90,16 @@ var Extensions = map[string]goldmark.Extender{
"footnote": extension.Footnote,
"typography": extension.Typographer,
"cjk": extension.CJK,
"highlighting": highlighting.NewHighlighting(
highlighting.WithStyle("github"),
highlighting.WithFormatOptions(
chromahtml.WithLineNumbers(true),
"highlighting": highlightingExt.NewHighlighting(
highlightingExt.WithStyle("github"),
highlightingExt.WithFormatOptions(
chromaHTMLExt.WithLineNumbers(true),
),
),
"anchor": &anchor.Extender{Texter: anchor.Text(" ")},
"d2": &d2.Extender{},
"embed": embed.New(),
"fences": &fences.Extender{},
"d2": &d2Ext.Extender{},
"embed": embedExt.New(),
"fences": &fencesExt.Extender{},
"wikilink": &wikilink.Extender{},
}
@ -137,6 +143,8 @@ var RootCmd = &cobra.Command{
- Use placeholders for variables and plugins in your markdown or html files, e.g. {{ title }} or {{ command arg1 arg2 }}.
- Write extensions in any language you like and put them into the .zs sub-directory.
- Everything the extensions prints to stdout becomes the value of the placeholder.
Quick Start: zs init
`,
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
debug, err := cmd.Flags().GetBool("debug")
@ -214,6 +222,39 @@ var GenerateCmd = &cobra.Command{
},
}
// InitCmd is the init sub-command that creates a new zs site
var InitCmd = &cobra.Command{
Use: "init",
Aliases: []string{"new"},
Short: "Initializes a new Zen Static site",
Long: `The init command creates a new Zen Static site in the current directory`,
Args: cobra.RangeArgs(0, 1),
RunE: func(cmd *cobra.Command, args []string) error {
var (
dir string
err error
)
if len(args) > 0 || (len(args) == 1 && args[0] == ".") {
dir = args[0]
if err := os.MkdirAll(dir, 0755); err != nil {
return fmt.Errorf("error creating directory %q: %w", dir, err)
}
} else {
dir, err = os.Getwd()
if err != nil {
return fmt.Errorf("error getting current directory: %w", err)
}
}
if err := initSite(dir); err != nil {
return fmt.Errorf("error initializing site: %w", err)
}
return nil
},
}
// ServeCmd is the serve sub-command that performs whole builds or single builds
var ServeCmd = &cobra.Command{
Use: "serve [flags]",
@ -683,6 +724,70 @@ func generate(r io.Reader, w io.Writer, v Vars) error {
return nil
}
// copyFile copies a file from the src fs.FS to the dst directory.
//
// This function assumes that the dst directory already exists.
func copyFile(src fs.FS, dst string, path string) error {
r, err := src.Open(path)
if err != nil {
return err
}
f, err := os.Create(filepath.Join(dst, path))
if err != nil {
return err
}
defer f.Close()
if _, err := io.Copy(f, r); err != nil {
return err
}
return nil
}
// initSite initializes the site
func initSite(dir string) error {
defaultFS, err := fs.Sub(defaultFS, "default")
if err != nil {
return fmt.Errorf("failed to get default fs: %w", err)
}
// Copy files and directories from the default structure from defaultFS
if err := fs.WalkDir(defaultFS, ".", func(path string, d fs.DirEntry, err error) error {
if path == "." || path == ".." {
return nil
}
if d.IsDir() {
os.Mkdir(filepath.Join(dir, path), 0755)
return nil
}
if err := copyFile(defaultFS, dir, path); err != nil {
return fmt.Errorf("failed to copy file: %w", err)
}
fileType, err := mimetype.DetectFile(filepath.Join(dir, path))
if err != nil {
return fmt.Errorf("failed to detect file type: %w", err)
}
// If the fileType is an executable application or script, make it executable
if fileType.Is("application/x-executable") {
if err := os.Chmod(filepath.Join(dir, path), 0755); err != nil {
return fmt.Errorf("failed to set executable permissions: %w", err)
}
}
return nil
}); err != nil {
return fmt.Errorf("failed to walk default fs: %w", err)
}
return nil
}
// serve runs a static web server and builds and continuously watches for changes to rebuild
func serve(ctx context.Context, bind, root string) error {
os.Mkdir(root, 0755)
@ -763,6 +868,7 @@ func init() {
RootCmd.AddCommand(BuildCmd)
RootCmd.AddCommand(GenerateCmd)
RootCmd.AddCommand(InitCmd)
RootCmd.AddCommand(ServeCmd)
RootCmd.AddCommand(VarCmd)
RootCmd.AddCommand(WatchCmd)
@ -770,6 +876,11 @@ func init() {
// prepend .zs to $PATH, so plugins will be found before OS commands
w, _ := os.Getwd()
ensureFirstPath(filepath.Join(w, ZSDIR))
// Extend mimetype to support application/x-shellscript and text/x-shellscript
mimetype.Extend(func(raw []byte, limit uint32) bool {
return len(raw) >= 2 && raw[0] == '#' && raw[1] == '!'
}, "application/x-executable", "")
}
// initConfig reads in config file and ENV variables if set.
@ -797,13 +908,13 @@ func initConfig() {
}
// ParseIgnoreFile parsers a .zsignore file or uses the default if an error occurred
func ParseIgnoreFile(fn string) *ignore.GitIgnore {
obj, err := ignore.CompileIgnoreFile(ZSIGNORE)
func ParseIgnoreFile(fn string) *gitIgnore.GitIgnore {
obj, err := gitIgnore.CompileIgnoreFile(ZSIGNORE)
if err != nil {
if !errors.Is(err, os.ErrNotExist) {
log.WithError(err).Warnf("error parsing .zsignore: %s (using defaults)s", fn)
}
return ignore.CompileIgnoreLines(DefaultIgnore)
return gitIgnore.CompileIgnoreLines(DefaultIgnore)
}
return obj