init command now adds default stylesheet and asks for confirmation to overwrite config
This commit is contained in:
parent
c875adb18c
commit
245919cb73
4 changed files with 272 additions and 13 deletions
|
@ -8,7 +8,7 @@ def find_config(start: Path | None = None) -> Path | None:
|
||||||
current = (start or Path.cwd()).resolve()
|
current = (start or Path.cwd()).resolve()
|
||||||
|
|
||||||
for parent in [current, *current.parents]:
|
for parent in [current, *current.parents]:
|
||||||
candidate = parent / "zona.yml"
|
candidate = parent / "config.yml"
|
||||||
if candidate.is_file():
|
if candidate.is_file():
|
||||||
return candidate
|
return candidate
|
||||||
return None
|
return None
|
||||||
|
|
236
src/zona/data/content/static/style.css
Normal file
236
src/zona/data/content/static/style.css
Normal file
|
@ -0,0 +1,236 @@
|
||||||
|
:root {
|
||||||
|
--main-text-color: #b4b4b4;
|
||||||
|
--main-bg-color: #121212;
|
||||||
|
--main-link-color: #df6464;
|
||||||
|
--main-heading-color: #df6464;
|
||||||
|
--main-bullet-color: #d87c4a;
|
||||||
|
--main-transparent: rgba(255, 255, 255, 0.15);
|
||||||
|
--main-small-text-color: rgba(255, 255, 255, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
line-height: 1.6;
|
||||||
|
font-size: 18px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
background: var(--main-bg-color);
|
||||||
|
color: var(--main-text-color);
|
||||||
|
padding-left: calc(100vw - 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* h1, */
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6 {
|
||||||
|
color: var(--main-heading-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin-block-start: 0.67rem;
|
||||||
|
margin-block-end: 0.67rem;
|
||||||
|
font-size: 2rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
article h1:first-of-type {
|
||||||
|
margin-block-start: 1.67rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-block-start: 0.83rem;
|
||||||
|
margin-block-end: 0.83rem;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-block-start: 1rem;
|
||||||
|
margin-block-end: 1rem;
|
||||||
|
font-size: 1.17em;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-block-start: 1.33rem;
|
||||||
|
margin-block-end: 1.33rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
article h1+h4:first-of-type {
|
||||||
|
margin-block-start: 0rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin-block-start: 1.67rem;
|
||||||
|
margin-block-end: 1.67rem;
|
||||||
|
font-size: 0.83rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
h6 {
|
||||||
|
margin-block-start: 2.33rem;
|
||||||
|
margin-block-end: 2.33rem;
|
||||||
|
font-size: 0.67rem;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
list-style-type: disc;
|
||||||
|
/* or any other list style */
|
||||||
|
}
|
||||||
|
|
||||||
|
li::marker {
|
||||||
|
color: var(--main-bullet-color);
|
||||||
|
/* Change this to your desired color */
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--main-link-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
background: var(--main-transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
color: var(--main-small-text-color);
|
||||||
|
border-left: 3px solid var(--main-transparent);
|
||||||
|
padding: 0 1rem;
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--main-small-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: var(--main-transparent);
|
||||||
|
border-radius: 0.1875rem;
|
||||||
|
/* padding: .0625rem .1875rem; */
|
||||||
|
/* margin: 0 .1875rem; */
|
||||||
|
}
|
||||||
|
|
||||||
|
code,
|
||||||
|
pre {
|
||||||
|
white-space: pre;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
font-family: 'Fira Code', 'Consolas', 'Courier New', monospace;
|
||||||
|
font-size: 0.95em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background-color: #1d1d1d;
|
||||||
|
color: #d5d5d5;
|
||||||
|
padding: 1em;
|
||||||
|
border-radius: 5px;
|
||||||
|
line-height: 1.5;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: #1d1d1d;
|
||||||
|
color: #d5d5d5;
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--main-small-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
small a {
|
||||||
|
color: inherit;
|
||||||
|
/* Inherit the color of the surrounding <small> text */
|
||||||
|
text-decoration: underline;
|
||||||
|
/* Optional: Keep the underline to indicate a link */
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title-container h1 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container {
|
||||||
|
text-align: center;
|
||||||
|
margin: 20px 0;
|
||||||
|
/* Optional: add some spacing around the image container */
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container img {
|
||||||
|
/* max-width: 308px; */
|
||||||
|
max-height: 308px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container small {
|
||||||
|
display: block;
|
||||||
|
/* Ensure the caption is on a new line */
|
||||||
|
margin-top: 5px;
|
||||||
|
/* Optional: adjust spacing between image and caption */
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-container small a {
|
||||||
|
color: inherit;
|
||||||
|
/* Ensure the link color matches the small text */
|
||||||
|
text-decoration: underline;
|
||||||
|
/* Optional: underline to indicate a link */
|
||||||
|
}
|
||||||
|
|
||||||
|
#header ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#header li {
|
||||||
|
display: inline;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
margin-right: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
margin: 2.5rem auto;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 60ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
#postlistdiv ul {
|
||||||
|
list-style-type: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.moreposts {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
padding-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#nextprev {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.4rem;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#footer {
|
||||||
|
color: var(--main-small-text-color);
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from rich import print
|
||||||
|
import typer
|
||||||
from dataclasses import dataclass, asdict
|
from dataclasses import dataclass, asdict
|
||||||
from zona.config import ZonaConfig, find_config
|
from zona.config import ZonaConfig, find_config
|
||||||
from zona import util
|
from zona import util
|
||||||
|
@ -24,7 +26,9 @@ class Layout:
|
||||||
)
|
)
|
||||||
if validate:
|
if validate:
|
||||||
if not layout.content.is_dir():
|
if not layout.content.is_dir():
|
||||||
raise FileNotFoundError("Missing required content directory!")
|
raise FileNotFoundError(
|
||||||
|
"Missing required content directory!"
|
||||||
|
)
|
||||||
if not layout.templates.is_dir():
|
if not layout.templates.is_dir():
|
||||||
# use the included defaults
|
# use the included defaults
|
||||||
layout.templates = util.get_resource_dir("templates")
|
layout.templates = util.get_resource_dir("templates")
|
||||||
|
@ -53,21 +57,29 @@ def initialize_site(root: Path | None = None):
|
||||||
root = root.absolute().resolve()
|
root = root.absolute().resolve()
|
||||||
config = find_config(root)
|
config = find_config(root)
|
||||||
if config is not None:
|
if config is not None:
|
||||||
raise FileExistsError(f"Config file already exists at {config}")
|
ans = typer.confirm(
|
||||||
|
text=(
|
||||||
|
f"A config file already exists at {config}.\n"
|
||||||
|
f"Delete it and restore defaults?"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if ans:
|
||||||
|
config.unlink()
|
||||||
# create requires layout
|
# create requires layout
|
||||||
layout = Layout.from_input(root=root, validate=False)
|
layout = Layout.from_input(root=root, validate=False)
|
||||||
# load template resources
|
# load template resources
|
||||||
templates = util.get_resources("templates")
|
templates = util.get_resources("templates")
|
||||||
|
static = util.get_resources("content")
|
||||||
for dir, resources in [
|
for dir, resources in [
|
||||||
(layout.root, None),
|
(layout.root, None),
|
||||||
(layout.content, None),
|
(layout.content, static),
|
||||||
(layout.templates, templates),
|
(layout.templates, templates),
|
||||||
]:
|
]:
|
||||||
if not dir.is_dir():
|
if not dir.is_dir():
|
||||||
dir.mkdir()
|
dir.mkdir()
|
||||||
if resources is not None:
|
if resources is not None:
|
||||||
for r in resources:
|
for r in resources:
|
||||||
Path(r.name).write_text(r.contents)
|
(root / Path(r.name)).write_text(r.contents)
|
||||||
|
|
||||||
config_path = layout.root / "config.yml"
|
config_path = layout.root / "config.yml"
|
||||||
config = ZonaConfig()
|
config = ZonaConfig()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from importlib.resources.abc import Traversable
|
||||||
from typing import NamedTuple
|
from typing import NamedTuple
|
||||||
from rich import print
|
from rich import print
|
||||||
from importlib import resources
|
from importlib import resources
|
||||||
|
@ -15,13 +16,21 @@ class ZonaResource(NamedTuple):
|
||||||
def get_resources(subdir: str) -> list[ZonaResource]:
|
def get_resources(subdir: str) -> list[ZonaResource]:
|
||||||
"""Load the packaged resources in data/subdir"""
|
"""Load the packaged resources in data/subdir"""
|
||||||
out: list[ZonaResource] = []
|
out: list[ZonaResource] = []
|
||||||
for resource in (
|
base = resources.files("zona").joinpath("data", subdir)
|
||||||
resources.files("zona").joinpath(f"data/{subdir}").iterdir()
|
|
||||||
):
|
def walk(trav: Traversable, prefix: str = ""):
|
||||||
out.append(
|
for item in trav.iterdir():
|
||||||
ZonaResource(f"{subdir}/{resource.name}", resource.read_text())
|
path = f"{prefix}{item.name}"
|
||||||
)
|
if item.is_dir():
|
||||||
print(out)
|
walk(item, prefix=f"{path}/")
|
||||||
|
else:
|
||||||
|
out.append(
|
||||||
|
ZonaResource(
|
||||||
|
name=f"{subdir}/{path}", contents=item.read_text()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
walk(base)
|
||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
@ -58,4 +67,6 @@ def normalize_url(url: str) -> str:
|
||||||
|
|
||||||
def should_ignore(path: Path, patterns: list[str], base: Path) -> bool:
|
def should_ignore(path: Path, patterns: list[str], base: Path) -> bool:
|
||||||
rel_path = path.relative_to(base)
|
rel_path = path.relative_to(base)
|
||||||
return any(fnmatch.fnmatch(str(rel_path), pattern) for pattern in patterns)
|
return any(
|
||||||
|
fnmatch.fnmatch(str(rel_path), pattern) for pattern in patterns
|
||||||
|
)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue