Compare commits
2 Commits
8b4af3f35b
...
ae4d30ade9
| Author | SHA1 | Date | |
|---|---|---|---|
| ae4d30ade9 | |||
| cabcda5b00 |
1
.gitattributes
vendored
1
.gitattributes
vendored
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
# Ignore Jupyter Notebooks in language stats.
|
# Ignore Jupyter Notebooks in language stats.
|
||||||
*.ipynb linguist-vendored
|
*.ipynb linguist-vendored
|
||||||
|
site/assets/css/codehilite.css linguist-vendored
|
||||||
27
config.yaml
27
config.yaml
@ -8,19 +8,12 @@ sites:
|
|||||||
build_cache: ./site
|
build_cache: ./site
|
||||||
assets:
|
assets:
|
||||||
- /assets
|
- /assets
|
||||||
|
- robots.txt
|
||||||
articles:
|
articles:
|
||||||
- ./pages/*.md
|
- '*.md'
|
||||||
template_selections:
|
template_selections:
|
||||||
article: templates.components.simple_article
|
article: templates.components.simple_article
|
||||||
|
|
||||||
robots_txt:
|
|
||||||
title: robots.txt
|
|
||||||
base_url: http://localhost:8000/robots.txt
|
|
||||||
web_root: ./dist
|
|
||||||
build_cache: ./site
|
|
||||||
assets:
|
|
||||||
- robots.txt
|
|
||||||
|
|
||||||
resume:
|
resume:
|
||||||
title: Resume
|
title: Resume
|
||||||
base_url: http://localhost:8000
|
base_url: http://localhost:8000
|
||||||
@ -50,6 +43,20 @@ sites:
|
|||||||
addons:
|
addons:
|
||||||
- rss
|
- rss
|
||||||
|
|
||||||
# TODO: add Jimoire Laboratoire
|
jimoire_laboratoire:
|
||||||
|
title: Jimoire Laboratoire
|
||||||
|
description: How I built the Jimlab, and you can too!
|
||||||
|
base_url: http://localhost:8000/jimoire-laboratoire
|
||||||
|
git_repo:
|
||||||
|
url: ssh://gitea/jimlab/jimoire-laboratoire.git
|
||||||
|
branch: jimsite
|
||||||
|
lfs: false
|
||||||
|
build_cache: ./build/jimoire-laboratoire
|
||||||
|
web_root: ./dist/jimoire-laboratoire
|
||||||
|
assets: []
|
||||||
|
articles:
|
||||||
|
- '*.md'
|
||||||
|
- 'essentials/*.md'
|
||||||
|
- '**/*.md'
|
||||||
# TODO: add newsletter
|
# TODO: add newsletter
|
||||||
|
|
||||||
@ -5,6 +5,7 @@ import logging
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
# TODO: add a way to change log level
|
||||||
|
|
||||||
from .common import filepath_or_string, GlobalVars, SiteConfig, dotmap_access
|
from .common import filepath_or_string, GlobalVars, SiteConfig, dotmap_access
|
||||||
from .templating import format_html_template, map_templates, TemplateSelections
|
from .templating import format_html_template, map_templates, TemplateSelections
|
||||||
|
|||||||
@ -2,7 +2,10 @@ import os
|
|||||||
import glob
|
import glob
|
||||||
import yaml
|
import yaml
|
||||||
import markdown
|
import markdown
|
||||||
|
from pygments.formatters import HtmlFormatter
|
||||||
|
from markdown.extensions.codehilite import CodeHiliteExtension
|
||||||
import pydantic
|
import pydantic
|
||||||
|
import logging
|
||||||
from typing import Optional, Union
|
from typing import Optional, Union
|
||||||
from dotmap import DotMap
|
from dotmap import DotMap
|
||||||
from datetime import date
|
from datetime import date
|
||||||
@ -10,12 +13,37 @@ from datetime import date
|
|||||||
from .common import filepath_or_string, SiteConfig
|
from .common import filepath_or_string, SiteConfig
|
||||||
from .templating import format_html_template, TemplateSelections
|
from .templating import format_html_template, TemplateSelections
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class CustomHiliteFormatter(HtmlFormatter):
|
||||||
|
'''
|
||||||
|
Override the default Pygments formatter (used by markdown.extensions.codehilite
|
||||||
|
to add syntax highlighting to fenced code) to include the `language-*` class.
|
||||||
|
|
||||||
|
From https://python-markdown.github.io/extensions/code_hilite/
|
||||||
|
'''
|
||||||
|
def __init__(self, lang_str='', **options):
|
||||||
|
super().__init__(**options)
|
||||||
|
self.lang_str = lang_str
|
||||||
|
|
||||||
|
def _wrap_code(self, source):
|
||||||
|
yield 0, f'<code class="{self.lang_str}">'
|
||||||
|
yield from source
|
||||||
|
yield 0, '</code>'
|
||||||
|
|
||||||
|
# Pick extensions to use for Markdown parsing.
|
||||||
|
MARKDOWN_EXTENSIONS = [
|
||||||
|
'footnotes',
|
||||||
|
'fenced_code',
|
||||||
|
CodeHiliteExtension(linenums = True, pygments_formatter = CustomHiliteFormatter)
|
||||||
|
]
|
||||||
|
|
||||||
class ArticleMetadata(pydantic.BaseModel):
|
class ArticleMetadata(pydantic.BaseModel):
|
||||||
'''A model for the YAML frontmatter included with Markdown articles.'''
|
'''A model for the YAML frontmatter included with Markdown articles.'''
|
||||||
title: str
|
title: str
|
||||||
date: date
|
date: date
|
||||||
published: bool
|
tags: Optional[list] = []
|
||||||
tags: list
|
published: Optional[bool] = False
|
||||||
author: Optional[str] = None
|
author: Optional[str] = None
|
||||||
lastmod: Optional[date] = None
|
lastmod: Optional[date] = None
|
||||||
thumbnail: Optional[str] = None
|
thumbnail: Optional[str] = None
|
||||||
@ -42,11 +70,13 @@ def load_markdown(md: str) -> tuple[Optional[ArticleMetadata], str]:
|
|||||||
# Split the metadata from the contents.
|
# Split the metadata from the contents.
|
||||||
[raw_metadata, raw_article] = md.split('---')
|
[raw_metadata, raw_article] = md.split('---')
|
||||||
|
|
||||||
|
# TODO: Consider adding an optional postscript document, for things like unescaped CSS.
|
||||||
|
|
||||||
# Use YAML to parse the metadata.
|
# Use YAML to parse the metadata.
|
||||||
metadata = yaml.safe_load(raw_metadata)
|
metadata = yaml.safe_load(raw_metadata)
|
||||||
|
|
||||||
# Convert the contents to a HTML string.
|
# Convert the contents to a HTML string.
|
||||||
content = markdown.markdown(raw_article)
|
content = markdown.markdown(raw_article, extensions = MARKDOWN_EXTENSIONS)
|
||||||
|
|
||||||
return ArticleMetadata(**metadata), content
|
return ArticleMetadata(**metadata), content
|
||||||
|
|
||||||
@ -66,21 +96,23 @@ def build_index(site: SiteConfig) -> dict[str, Article]:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
for article_full_path in expanded_article_list:
|
for article_full_path in set(expanded_article_list):
|
||||||
|
|
||||||
|
logger.debug(f'Loading article from {article_full_path}')
|
||||||
metadata, content = load_markdown(article_full_path)
|
metadata, content = load_markdown(article_full_path)
|
||||||
|
|
||||||
|
# TODO: consider adding support for articles with no metadata
|
||||||
|
if metadata is None:
|
||||||
|
logger.debug(f'Article loaded from {article_full_path} has no metadata. Skipping.')
|
||||||
|
continue
|
||||||
|
|
||||||
# Skip unpublished articles.
|
# Skip unpublished articles.
|
||||||
if not metadata.published:
|
if not metadata.published:
|
||||||
|
logger.debug(f'Article loaded from {article_full_path} is not published. Skipping.')
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Construct the article's path for the index by discarding the build cache
|
# Construct the article's path for the index by discarding the build cache.
|
||||||
# and replacing .md with .html.
|
article_path = article_full_path.removeprefix(site.build_cache).lstrip("/").replace('.md','.html')
|
||||||
# article_path = article_full_path\
|
|
||||||
# .removeprefix(site.build_cache)\
|
|
||||||
# .lstrip('/')\
|
|
||||||
# .replace('.md','.html')
|
|
||||||
# TODO: add tolerance for a hierarchical article directory structure.
|
|
||||||
article_path = os.path.basename(article_full_path).replace('.md', '.html')
|
|
||||||
|
|
||||||
index[article_path] = Article(
|
index[article_path] = Article(
|
||||||
path = article_path,
|
path = article_path,
|
||||||
@ -147,7 +179,14 @@ def build_articles(
|
|||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
with open(f"{site.web_root.rstrip('/')}/{article.path}", 'w') as f:
|
# Construct the path to which the article page is to be saved.
|
||||||
|
destination = f"{site.web_root.rstrip('/')}/{article.path}"
|
||||||
|
|
||||||
|
# Ensure the directory structure exists.
|
||||||
|
os.makedirs(os.path.dirname(destination), exist_ok=True)
|
||||||
|
|
||||||
|
# Save the file.
|
||||||
|
with open(destination, 'w') as f:
|
||||||
f.write(page_html)
|
f.write(page_html)
|
||||||
|
|
||||||
|
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import os
|
import os
|
||||||
import glob
|
import glob
|
||||||
import shutil
|
import shutil
|
||||||
|
import logging
|
||||||
from .common import run, GitRepo, SiteConfig
|
from .common import run, GitRepo, SiteConfig
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
def pull_git_repo(repo: GitRepo, build_cache: str) -> None:
|
def pull_git_repo(repo: GitRepo, build_cache: str) -> None:
|
||||||
'''Pulls/clones a repo into the build cache directory.'''
|
'''Pulls/clones a repo into the build cache directory.'''
|
||||||
@ -49,7 +51,7 @@ def copy_assets(site: SiteConfig) -> None:
|
|||||||
glob.glob(f'{site.build_cache}/{a.lstrip("/")}')
|
glob.glob(f'{site.build_cache}/{a.lstrip("/")}')
|
||||||
)
|
)
|
||||||
|
|
||||||
for asset in expanded_asset_list:
|
for asset in set(expanded_asset_list):
|
||||||
|
|
||||||
# Skip ignored assets.
|
# Skip ignored assets.
|
||||||
if asset in ignored_asset_list:
|
if asset in ignored_asset_list:
|
||||||
|
|||||||
@ -58,9 +58,9 @@ def build_blog_archive(
|
|||||||
|
|
||||||
tag_selector_options = []
|
tag_selector_options = []
|
||||||
tag_selector_css_rules = [f'''
|
tag_selector_css_rules = [f'''
|
||||||
body:has(input[name="tag-selector"][value="*"]:checked) li:has(.blog-tag){{{{
|
body:has(input[name="tag-selector"][value="*"]:checked) li:has(.blog-tag){{
|
||||||
display: list-item!important;
|
display: list-item!important;
|
||||||
}}}}
|
}}
|
||||||
''']
|
''']
|
||||||
|
|
||||||
# Add tag selector options in descending order of article count.
|
# Add tag selector options in descending order of article count.
|
||||||
@ -75,12 +75,12 @@ def build_blog_archive(
|
|||||||
if tag == '*':
|
if tag == '*':
|
||||||
continue
|
continue
|
||||||
tag_selector_css_rules.append(f'''
|
tag_selector_css_rules.append(f'''
|
||||||
body:has(input[name="tag-selector"]:not([value="{tag}"]):checked) li:has(.blog-tag[data="{tag}"]){{{{
|
body:has(input[name="tag-selector"]:not([value="{tag}"]):checked) li:has(.blog-tag[data="{tag}"]){{
|
||||||
display: none;
|
display: none;
|
||||||
}}}}
|
}}
|
||||||
body:has(input[name="tag-selector"][value="{tag}"]:checked) li:has(.blog-tag[data="{tag}"]){{{{
|
body:has(input[name="tag-selector"][value="{tag}"]:checked) li:has(.blog-tag[data="{tag}"]){{
|
||||||
display: list-item!important;
|
display: list-item!important;
|
||||||
}}}}
|
}}
|
||||||
''')
|
''')
|
||||||
|
|
||||||
# For Python 3.9-friendliness.
|
# For Python 3.9-friendliness.
|
||||||
|
|||||||
@ -1,18 +1,52 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
|
||||||
|
# Use the community regex package instead of re for QoL
|
||||||
|
# features like variable-length lookbacks.
|
||||||
|
import regex as re
|
||||||
|
import logging
|
||||||
from dotmap import DotMap
|
from dotmap import DotMap
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
from .common import filepath_or_string, GlobalVars, SiteConfig, dotmap_access
|
from .common import filepath_or_string, GlobalVars, SiteConfig, dotmap_access
|
||||||
|
|
||||||
|
def escape_braces(template) -> str:
|
||||||
|
'''Escapes any curly braces that are unclosed or enclose
|
||||||
|
anything other than a pattern for a valid variable
|
||||||
|
(alphanumerics, underscores, and periods ONLY;
|
||||||
|
must start with alpha or underscore).
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Match any left braces that are not followed by a valid variable
|
||||||
|
# name (with no whitespace) and a closing right brace.
|
||||||
|
left_brace_pattern = r'{(?![A-Za-z_][\w.]+})'
|
||||||
|
|
||||||
|
# Match any right braces that are not preceded by an opening
|
||||||
|
# left brace and a valid variable name.
|
||||||
|
right_brace_pattern = r'(?<!{[A-Za-z_][\w.]+)}'
|
||||||
|
|
||||||
|
# Escape non-variable-enclosing braces with double braces.
|
||||||
|
return re.sub(
|
||||||
|
right_brace_pattern, '}}',
|
||||||
|
re.sub(
|
||||||
|
left_brace_pattern, '{{', template
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def extract_placeholders(s: str) -> set:
|
def extract_placeholders(s: str) -> set:
|
||||||
'''Extracts placeholder variables in the format `{variable}` from
|
'''Extracts placeholder variables in the format `{variable}` from
|
||||||
an unformatted template string.'''
|
an unformatted template string.'''
|
||||||
|
|
||||||
# Regex pattern to match placeholders with alphanumerics, dots, and underscores.
|
# Regex pattern to match placeholders with alphanumerics, dots, and underscores.
|
||||||
placeholder_pattern = r'\{([\w\.]+)\}'
|
placeholder_pattern = r'\{([A-Za-z_][\w.]+)\}'
|
||||||
|
|
||||||
|
# Escape braces that are not used to enclose Python variables
|
||||||
|
# (e.g. those used in inline CSS or JavaScript)
|
||||||
|
escaped = escape_braces(s)
|
||||||
|
|
||||||
# Find all matches in the string.
|
# Find all matches in the string.
|
||||||
matches = re.findall(placeholder_pattern, s)
|
matches = re.findall(placeholder_pattern, escaped)
|
||||||
|
|
||||||
# Return the set of distinct placeholders.
|
# Return the set of distinct placeholders.
|
||||||
return set(matches)
|
return set(matches)
|
||||||
@ -85,7 +119,19 @@ def format_html_template(template: str, **kwargs) -> str:
|
|||||||
# there are no more placeholders. The loop is used to account for nested template references.
|
# there are no more placeholders. The loop is used to account for nested template references.
|
||||||
formatted_html = template
|
formatted_html = template
|
||||||
while len(extract_placeholders(formatted_html)) > 0:
|
while len(extract_placeholders(formatted_html)) > 0:
|
||||||
formatted_html = formatted_html.format(globalvars = GlobalVars(), **kwargs)
|
|
||||||
|
# Escape non-variable-enclosing braces each iteration of the
|
||||||
|
# loop, because each time they'll be replaced with single braces.
|
||||||
|
try:
|
||||||
|
escaped = escape_braces(formatted_html)
|
||||||
|
formatted_html = escaped.format(
|
||||||
|
globalvars = GlobalVars(),
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error('Encountered an exception while formatting the following:')
|
||||||
|
logger.error(escaped)
|
||||||
|
raise e
|
||||||
|
|
||||||
# Return the formatted HTML.
|
# Return the formatted HTML.
|
||||||
return formatted_html
|
return formatted_html
|
||||||
|
|||||||
@ -4,3 +4,5 @@ pyyaml
|
|||||||
rfeed
|
rfeed
|
||||||
dotmap
|
dotmap
|
||||||
pydantic
|
pydantic
|
||||||
|
pygments
|
||||||
|
pymdownextensions
|
||||||
75
site/assets/css/codehilite.css
vendored
Normal file
75
site/assets/css/codehilite.css
vendored
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
pre { line-height: 125%; }
|
||||||
|
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||||
|
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
|
||||||
|
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||||
|
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
|
||||||
|
.codehilite .hll { background-color: #ffffcc }
|
||||||
|
.codehilite { background: #f8f8f8; }
|
||||||
|
.codehilite .c { color: #3D7B7B; font-style: italic } /* Comment */
|
||||||
|
.codehilite .err { border: 1px solid #F00 } /* Error */
|
||||||
|
.codehilite .k { color: #008000; font-weight: bold } /* Keyword */
|
||||||
|
.codehilite .o { color: #666 } /* Operator */
|
||||||
|
.codehilite .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
|
||||||
|
.codehilite .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
|
||||||
|
.codehilite .cp { color: #9C6500 } /* Comment.Preproc */
|
||||||
|
.codehilite .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
|
||||||
|
.codehilite .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
|
||||||
|
.codehilite .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
|
||||||
|
.codehilite .gd { color: #A00000 } /* Generic.Deleted */
|
||||||
|
.codehilite .ge { font-style: italic } /* Generic.Emph */
|
||||||
|
.codehilite .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
|
||||||
|
.codehilite .gr { color: #E40000 } /* Generic.Error */
|
||||||
|
.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||||
|
.codehilite .gi { color: #008400 } /* Generic.Inserted */
|
||||||
|
.codehilite .go { color: #717171 } /* Generic.Output */
|
||||||
|
.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
|
||||||
|
.codehilite .gs { font-weight: bold } /* Generic.Strong */
|
||||||
|
.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||||
|
.codehilite .gt { color: #04D } /* Generic.Traceback */
|
||||||
|
.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
|
||||||
|
.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
|
||||||
|
.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
|
||||||
|
.codehilite .kp { color: #008000 } /* Keyword.Pseudo */
|
||||||
|
.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
|
||||||
|
.codehilite .kt { color: #B00040 } /* Keyword.Type */
|
||||||
|
.codehilite .m { color: #666 } /* Literal.Number */
|
||||||
|
.codehilite .s { color: #BA2121 } /* Literal.String */
|
||||||
|
.codehilite .na { color: #687822 } /* Name.Attribute */
|
||||||
|
.codehilite .nb { color: #008000 } /* Name.Builtin */
|
||||||
|
.codehilite .nc { color: #00F; font-weight: bold } /* Name.Class */
|
||||||
|
.codehilite .no { color: #800 } /* Name.Constant */
|
||||||
|
.codehilite .nd { color: #A2F } /* Name.Decorator */
|
||||||
|
.codehilite .ni { color: #717171; font-weight: bold } /* Name.Entity */
|
||||||
|
.codehilite .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
|
||||||
|
.codehilite .nf { color: #00F } /* Name.Function */
|
||||||
|
.codehilite .nl { color: #767600 } /* Name.Label */
|
||||||
|
.codehilite .nn { color: #00F; font-weight: bold } /* Name.Namespace */
|
||||||
|
.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */
|
||||||
|
.codehilite .nv { color: #19177C } /* Name.Variable */
|
||||||
|
.codehilite .ow { color: #A2F; font-weight: bold } /* Operator.Word */
|
||||||
|
.codehilite .w { color: #BBB } /* Text.Whitespace */
|
||||||
|
.codehilite .mb { color: #666 } /* Literal.Number.Bin */
|
||||||
|
.codehilite .mf { color: #666 } /* Literal.Number.Float */
|
||||||
|
.codehilite .mh { color: #666 } /* Literal.Number.Hex */
|
||||||
|
.codehilite .mi { color: #666 } /* Literal.Number.Integer */
|
||||||
|
.codehilite .mo { color: #666 } /* Literal.Number.Oct */
|
||||||
|
.codehilite .sa { color: #BA2121 } /* Literal.String.Affix */
|
||||||
|
.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */
|
||||||
|
.codehilite .sc { color: #BA2121 } /* Literal.String.Char */
|
||||||
|
.codehilite .dl { color: #BA2121 } /* Literal.String.Delimiter */
|
||||||
|
.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
|
||||||
|
.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */
|
||||||
|
.codehilite .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
|
||||||
|
.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */
|
||||||
|
.codehilite .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
|
||||||
|
.codehilite .sx { color: #008000 } /* Literal.String.Other */
|
||||||
|
.codehilite .sr { color: #A45A77 } /* Literal.String.Regex */
|
||||||
|
.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */
|
||||||
|
.codehilite .ss { color: #19177C } /* Literal.String.Symbol */
|
||||||
|
.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */
|
||||||
|
.codehilite .fm { color: #00F } /* Name.Function.Magic */
|
||||||
|
.codehilite .vc { color: #19177C } /* Name.Variable.Class */
|
||||||
|
.codehilite .vg { color: #19177C } /* Name.Variable.Global */
|
||||||
|
.codehilite .vi { color: #19177C } /* Name.Variable.Instance */
|
||||||
|
.codehilite .vm { color: #19177C } /* Name.Variable.Magic */
|
||||||
|
.codehilite .il { color: #666 } /* Literal.Number.Integer.Long */
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
/* TODO: Reorganize CSS */
|
||||||
|
|
||||||
body{
|
body{
|
||||||
background: inherit;
|
background: inherit;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
@ -293,3 +295,13 @@ p.socials{
|
|||||||
margin-left: 0.25em;
|
margin-left: 0.25em;
|
||||||
margin-right:0.25em;
|
margin-right:0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article ul, article ol{
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre:has(> code), mark{
|
||||||
|
font-family: 'Oxygen Mono', sans-serif;
|
||||||
|
font-display: swap;
|
||||||
|
background-color: #d8d8d8;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
<article>
|
|
||||||
<h1>404 Error</h1>
|
|
||||||
<hr />
|
|
||||||
<span id="404-message"></span>
|
|
||||||
<script>
|
|
||||||
var query_page = $("[name='query_page']")[0].innerHTML;
|
|
||||||
document.getElementById("404-message").innerHTML="Page '"+query_page+"' not found.";
|
|
||||||
</script>
|
|
||||||
</article>
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
<article>
|
|
||||||
<h1>About</h1>
|
|
||||||
<hr />
|
|
||||||
Coming soon!
|
|
||||||
|
|
||||||
</article>
|
|
||||||
@ -12,9 +12,9 @@
|
|||||||
{article.content}
|
{article.content}
|
||||||
<br /><hr /><br />
|
<br /><hr /><br />
|
||||||
<p>
|
<p>
|
||||||
<a href="{previous_article.path}" title="{previous_article.metadata.title}" data="{previous_article.path}" class="previous-article-link">< Previous</a>
|
<a href="{site.base_url}/{previous_article.path}" title="{previous_article.metadata.title}" data="{previous_article.path}" class="previous-article-link">< Previous</a>
|
||||||
<!-- This non-breaking space prevents the RSS icon from getting weird when previous-article-link is hidden. -->
|
<!-- This non-breaking space prevents the RSS icon from getting weird when previous-article-link is hidden. -->
|
||||||
<a href="{next_article.path}" title="{next_article.metadata.title}" data="{next_article.path}" class="next-article-link">Next ></a>
|
<a href="{site.base_url}/{next_article.path}" title="{next_article.metadata.title}" data="{next_article.path}" class="next-article-link">Next ></a>
|
||||||
</p>
|
</p>
|
||||||
<p>Tags: {blog_tags}{templates.components.rss_icon}</p>
|
<p>Tags: {blog_tags}{templates.components.rss_icon}</p>
|
||||||
</article>
|
</article>
|
||||||
20
site/templates/components/jimoire-article.html
Normal file
20
site/templates/components/jimoire-article.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<article>
|
||||||
|
<span class="first-published-label">First published: </span>
|
||||||
|
<time class="first-published-date" pubdate data="{article.metadata.date}" datetime="{article.metadata.date}">{article.metadata.date}</time>
|
||||||
|
<span class="last-modified-label"> · Last modified: </span>
|
||||||
|
<time class="last-modified-date" data="{article.metadata.lastmod}" datetime="{article.metadata.lastmod}">{article.metadata.lastmod}</time>
|
||||||
|
<hr />
|
||||||
|
<h1 class="headline">{article.metadata.title}</h1>
|
||||||
|
<p class="byline">
|
||||||
|
<address class="author">By <a rel="author" href="mailto:admin@jimlab.io">Jim Shepich III</a></address>
|
||||||
|
</p>
|
||||||
|
{article.content}
|
||||||
|
<br /><hr /><br />
|
||||||
|
<p>
|
||||||
|
<!-- TODO: highlightjs.org -->
|
||||||
|
<a href="{site.base_url}/{previous_article.path}" title="{previous_article.metadata.title}" data="{previous_article.path}" class="previous-article-link">< Previous</a>
|
||||||
|
<!-- This non-breaking space prevents the RSS icon from getting weird when previous-article-link is hidden. -->
|
||||||
|
<a href="{site.base_url}/{next_article.path}" title="{next_article.metadata.title}" data="{next_article.path}" class="next-article-link">Next ></a>
|
||||||
|
</p>
|
||||||
|
<p>Tags: {blog_tags}{templates.components.rss_icon}</p>
|
||||||
|
</article>
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<link rel="stylesheet" type="text/css" href="/assets/css/reset.css">
|
<link rel="stylesheet" type="text/css" href="/assets/css/reset.css">
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Jacquard+12|Fira+Sans|UnifrakturMaguntia">
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Jacquard+12|Fira+Sans|UnifrakturMaguntia|Oxygen+Mono">
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/css/common.css">
|
<link rel="stylesheet" type="text/css" href="/assets/css/common.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/css/layout.css">
|
<link rel="stylesheet" type="text/css" href="/assets/css/layout.css">
|
||||||
<link rel="stylesheet" type="text/css" href="/assets/css/theme.css">
|
<link rel="stylesheet" type="text/css" href="/assets/css/theme.css">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/assets/css/codehilite.css">
|
||||||
|
|||||||
@ -1,5 +1,4 @@
|
|||||||
<footer>
|
<footer>
|
||||||
<!-- Add socials (email, Matrix, Friendica, Gitea)-->
|
|
||||||
<p class="socials">
|
<p class="socials">
|
||||||
<a class="social" href="https://matrix.to/#/@jim:jimlab.io" title="Matrix"><img src="/assets/img/matrix.svg" alt="Matrix logo"></a>
|
<a class="social" href="https://matrix.to/#/@jim:jimlab.io" title="Matrix"><img src="/assets/img/matrix.svg" alt="Matrix logo"></a>
|
||||||
<a class="social" href="https://friendica.jimlab.io/profile/jim/profile" title="Friendica"><img src="/assets/img/friendica.svg" alt="Friendica logo"></a>
|
<a class="social" href="https://friendica.jimlab.io/profile/jim/profile" title="Friendica"><img src="/assets/img/friendica.svg" alt="Friendica logo"></a>
|
||||||
@ -7,4 +6,5 @@
|
|||||||
<a class="social" href="mailto:admin@jimlab.io" title="eMail"><img src="/assets/img/email.svg" alt="eMail logo"></a>
|
<a class="social" href="mailto:admin@jimlab.io" title="eMail"><img src="/assets/img/email.svg" alt="eMail logo"></a>
|
||||||
</span>
|
</span>
|
||||||
<br /><p class='copyright'>Copyright © 2021-{globalvars.today.year} Jim Shepich</span>
|
<br /><p class='copyright'>Copyright © 2021-{globalvars.today.year} Jim Shepich</span>
|
||||||
|
<!-- TODO: Add licenses for 3rd party content (e.g. logos) -->
|
||||||
</footer>
|
</footer>
|
||||||
@ -2,4 +2,5 @@
|
|||||||
<a href='/index.html' target='_self'><div class='nav-tab'><span class='nav-text'>Home</span></div></a>
|
<a href='/index.html' target='_self'><div class='nav-tab'><span class='nav-text'>Home</span></div></a>
|
||||||
<a href='/shepich_resume.pdf' target='_blank'><div class='nav-tab'><span class='nav-text'>Resume</span></div></a>
|
<a href='/shepich_resume.pdf' target='_blank'><div class='nav-tab'><span class='nav-text'>Resume</span></div></a>
|
||||||
<a href='/dogma-jimfinium/archive.html' target='_self'><div class='nav-tab'><span class='nav-text'>Dogma Jimfinium</span></div></a>
|
<a href='/dogma-jimfinium/archive.html' target='_self'><div class='nav-tab'><span class='nav-text'>Dogma Jimfinium</span></div></a>
|
||||||
|
<a href='/jimoire-laboratoire/index.html' target='_self'><div class='nav-tab'><span class='nav-text'>Jimoire Laboratoire</span></div></a>
|
||||||
</nav>
|
</nav>
|
||||||
Loading…
x
Reference in New Issue
Block a user