diff --git a/markata.toml b/markata.toml index 22d581e1..40badb72 100644 --- a/markata.toml +++ b/markata.toml @@ -33,7 +33,7 @@ hooks = [ # "markata.plugins.subroute", "markata.plugins.docs", # "markata.plugins.prevnext", - "markata.plugins.service_worker", + # "markata.plugins.service_worker", "default", ] disabled_hooks = [ @@ -282,9 +282,9 @@ plugin = "markata.plugins.mdit_details:details_plugin" [[markata.render_markdown.extensions]] plugin = "mdit_py_plugins.anchors:anchors_plugin" -[markata.render_markdown.extensions.config] -permalink = true -permalinkSymbol = '' +# [markata.render_markdown.extensions.config] +# permalink = true +# permalinkSymbol = '' [[markata.render_markdown.extensions]] plugin = "markata.plugins.md_it_wikilinks:wikilinks_plugin" diff --git a/markata/__init__.py b/markata/__init__.py index cd01c8f3..ef2055b3 100644 --- a/markata/__init__.py +++ b/markata/__init__.py @@ -3,6 +3,7 @@ # annotations needed to return self from __future__ import annotations +import copy import atexit import datetime import hashlib @@ -30,6 +31,7 @@ logger = logging.getLogger("markata") + DEFAULT_MD_EXTENSIONS = [ "codehilite", "markdown.extensions.admonition", @@ -55,7 +57,7 @@ DEFAULT_HOOKS = [ "markata.plugins.copy_assets", - "markata.plugins.heading_link", + # "markata.plugins.heading_link", "markata.plugins.pyinstrument", "markata.plugins.glob", "markata.plugins.load", @@ -65,7 +67,7 @@ # "markata.plugins.generator", "markata.plugins.feeds", "markata.plugins.auto_description", - "markata.plugins.seo", + # "markata.plugins.seo", "markata.plugins.post_template", "markata.plugins.covers", "markata.plugins.publish_html", @@ -85,7 +87,7 @@ "markata.plugins.post_model", "markata.plugins.config_model", "markata.plugins.create_models", - "markata.plugins.jinja_md", + # "markata.plugins.jinja_md", ] DEFUALT_CONFIG = { @@ -106,6 +108,7 @@ class HooksConfig(pydantic.BaseModel): class Markata: def __init__(self: "Markata", console: Console = None, config=None) -> None: + self.__version__ = __version__ self.stages_ran = set() self.threded = False self._cache = None @@ -306,7 +309,7 @@ def get_config( def make_hash(self, *keys: str) -> str: str_keys = [str(key) for key in keys] - return hashlib.md5("".join(str_keys).encode("utf-8")).hexdigest() + return hashlib.sha256("".join(str_keys).encode("utf-8")).hexdigest() @property def content_dir_hash(self: "Markata") -> str: diff --git a/markata/plugins/feeds.py b/markata/plugins/feeds.py index 67f7cd57..886ecadd 100644 --- a/markata/plugins/feeds.py +++ b/markata/plugins/feeds.py @@ -193,6 +193,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Any, List, Optional +from markata import background import pydantic import typer from jinja2 import Template, Undefined @@ -233,7 +234,7 @@ class FeedConfig(pydantic.BaseModel): """ - template: str = Path(__file__).parent / "default_post_template.html.jinja" + template: str = "feed.html" rss_template: str = Path(__file__).parent / "default_rss_template.xml" sitemap_template: str = Path(__file__).parent / "default_sitemap_template.xml" xsl_template: str = Path(__file__).parent / "default_xsl_template.xsl" @@ -486,14 +487,17 @@ def create_page( posts = feed.posts - cards = [ - create_card(markata, post, feed.config.card_template, cache) for post in posts - ] - cards.insert(0, "") - cards = "".join(cards) + # card_futures = [ + # create_card(markata, post, feed.config.card_template, cache) for post in posts + # ] + # cards = [card.result() for card in card_futures] - template = get_template(feed.config.template) + # cards.insert(0, "") + # cards = "".join(cards) + + # template = get_template(feed.config.template) + template = markata.config.jinja_env.get_template(feed.config.template) rss_template = get_template(feed.config.rss_template) sitemap_template = get_template(feed.config.sitemap_template) output_file = Path(markata.config.output_dir) / feed.config.slug / "index.html" @@ -512,7 +516,7 @@ def create_page( "feeds", template, __version__, - cards, + # cards, markata.config.url, markata.config.description, feed.config.title, @@ -526,7 +530,8 @@ def create_page( feed_html = template.render( markata=markata, __version__=__version__, - body=cards, + # body=cards, + posts=posts, url=markata.config.url, description=markata.config.description, title=feed.config.title, @@ -534,8 +539,7 @@ def create_page( today=datetime.datetime.today(), config=markata.config, ) - with markata.cache as cache: - markata.cache.set(key, feed_html) + cache.set(key, feed_html) feed_rss = rss_template.render(markata=markata, feed=feed) feed_sitemap = sitemap_template.render(markata=markata, feed=feed) @@ -545,6 +549,7 @@ def create_page( sitemap_output_file.write_text(feed_sitemap) +@background.task def create_card( markata: "Markata", post: "Post", diff --git a/markata/plugins/glob.py b/markata/plugins/glob.py index c98196ab..7ad7ae9d 100644 --- a/markata/plugins/glob.py +++ b/markata/plugins/glob.py @@ -1,6 +1,7 @@ """Default glob plugin""" from pathlib import Path from typing import List, TYPE_CHECKING, Union +from markata import background from more_itertools import flatten import pydantic @@ -68,6 +69,7 @@ def glob(markata: "Markata") -> None: with markata.cache as cache: cache.set(key, spec) + @background.task def check_spec(file: str) -> bool: key = markata.make_hash("glob", "check_spec", file) check = markata.precache.get(key) @@ -79,4 +81,6 @@ def check_spec(file: str) -> bool: cache.set(key, check) return check - markata.files = [file for file in markata.files if not check_spec(str(file))] + file_checks = [(file, check_spec(str(file))) for file in markata.files] + [check.result() for _, check in file_checks] + markata.files = [file for file, check in file_checks if check] diff --git a/markata/plugins/jinja_md.py b/markata/plugins/jinja_md.py index 4fe66b02..77b0a40a 100644 --- a/markata/plugins/jinja_md.py +++ b/markata/plugins/jinja_md.py @@ -133,14 +133,14 @@ def run(self, arg, caller): """ from pathlib import Path -from typing import List, TYPE_CHECKING +from typing import TYPE_CHECKING, List import jinja2 -from jinja2 import TemplateSyntaxError, Undefined, UndefinedError, nodes -from jinja2.ext import Extension import pathspec import pkg_resources import pydantic +from jinja2 import TemplateSyntaxError, Undefined, UndefinedError, nodes +from jinja2.ext import Extension from markata import __version__ from markata.hookspec import hook_impl, register_attr @@ -186,7 +186,7 @@ class _SilentUndefined(Undefined): # Example ```python template = '{{ variable }}' - article.content = Template( template, undefined=_SilentUndefined).render() + post.content = Template( template, undefined=_SilentUndefined).render() ``` """ @@ -222,39 +222,39 @@ def pre_render(markata: "Markata") -> None: config = markata.config.jinja_md ignore_spec = pathspec.PathSpec.from_lines("gitwildmatch", config.ignore) - # for article in markata.iter_articles(description="jinja_md"): + # for post in markata.iter_articles(description="jinja_md"): jinja_env = jinja2.Environment( extensions=[IncludeRawExtension, *register_jinja_extensions(config)], ) - for article in markata.articles: - if article.get("jinja", True) and not ignore_spec.match_file(article["path"]): + for post in markata.posts: + if post.get("jinja", True) and not ignore_spec.match_file(post["path"]): try: - key = markata.make_hash(article.content) + key = markata.make_hash(post.content) content_from_cache = markata.precache.get(key) if content_from_cache is None: - article.content = jinja_env.from_string(article.content).render( + post.content = jinja_env.from_string(post.content).render( __version__=__version__, - **article, - post=article, + **post, + post=post, ) with markata.cache: - markata.cache.set(key, article.content) + markata.cache.set(key, post.content) else: - article.content = content_from_cache + post.content = content_from_cache # prevent double rendering - article.jinja = False + post.jinja = False except TemplateSyntaxError as e: - errorline = article.content.split("\n")[e.lineno - 1] + errorline = post.content.split("\n")[e.lineno - 1] msg = f""" - Error while processing post {article['path']} + Error while processing post {post['path']} {errorline} """ raise PostTemplateSyntaxError(msg, lineno=e.lineno) except UndefinedError as e: - raise UndefinedError(f'{e} in {article["path"]}') + raise UndefinedError(f'{e} in {post["path"]}') class JinjaMdConfig(pydantic.BaseModel): diff --git a/markata/plugins/load.py b/markata/plugins/load.py index 80a256ac..31736112 100644 --- a/markata/plugins/load.py +++ b/markata/plugins/load.py @@ -3,6 +3,7 @@ from pathlib import Path from typing import TYPE_CHECKING, Callable, List, Optional +from markata import background import frontmatter import pydantic from rich.progress import BarColumn, Progress @@ -30,13 +31,17 @@ def load(markata: "MarkataMarkdown") -> None: console=markata.console, ) markata.console.log(f"found {len(markata.files)} posts") + post_futures = [get_post(article, markata) for article in markata.files] + posts = [post.result() for post in post_futures if post is not None] + markata.posts_obj = markata.Posts.parse_obj( - {"posts": [get_post(article, markata) for article in markata.files]}, + {"posts": posts}, ) markata.posts = markata.posts_obj.posts markata.articles = markata.posts +@background.task def get_post(path: Path, markata: "Markata") -> Optional[Callable]: if markata.Post: post = pydantic_get_post(path=path, markata=markata) diff --git a/markata/plugins/post_model.py b/markata/plugins/post_model.py index 07867139..c4cafb65 100644 --- a/markata/plugins/post_model.py +++ b/markata/plugins/post_model.py @@ -44,6 +44,7 @@ class Post(pydantic.BaseModel): arbitrary_types_allowed=True, extra="allow", ) + template: Optional[str] = "post.html" def __repr_args__(self: "Post") -> "ReprArgs": return [ @@ -52,6 +53,20 @@ def __repr_args__(self: "Post") -> "ReprArgs": if key in self.markata.config.post_model.repr_include ] + @property + def key(self: "Post") -> List[str]: + return self.markata.make_hash( + self.slug, + self.slug, + self.href, + self.published, + self.description, + self.content, + self.date, + self.title, + self.template, + ) + @property def metadata(self: "Post") -> Dict: "for backwards compatability" @@ -177,7 +192,7 @@ def parse_markdown(cls, markata, path: Union[Path, str], **kwargs) -> "Post": **fm, } - return markata.Post(**post_args) + return markata.Post.parse_obj(post_args) def dumps(self): """ @@ -195,6 +210,12 @@ def index_slug_is_empty(cls, v, *, values): return "" return v + @pydantic.validator("slug", pre=True, always=True) + def no_double_slash_in_slug(cls, v, *, values): + if v is None: + return v + return v.replace("//", "/") + @pydantic.validator("href", pre=True, always=True) def default_href(cls, v, *, values): if v: @@ -346,3 +367,4 @@ def config_model(markata: "Markata") -> None: class PostFactory(ModelFactory): __model__ = Post + __model__ = Post diff --git a/markata/plugins/post_template.py b/markata/plugins/post_template.py index 9161a905..e9d835eb 100644 --- a/markata/plugins/post_template.py +++ b/markata/plugins/post_template.py @@ -69,19 +69,20 @@ ``` """ +from functools import lru_cache import inspect from pathlib import Path -from typing import TYPE_CHECKING, List, Union +from typing import List, Optional, TYPE_CHECKING, Union import jinja2 -import pydantic from jinja2 import Template, Undefined + from more_itertools import flatten +import pydantic -from markata import __version__ +from markata import __version__, background from markata.hookspec import hook_impl -env = jinja2.Environment() if TYPE_CHECKING: from markata import Markata @@ -169,20 +170,39 @@ def html(self): class Config(pydantic.BaseModel): head: HeadConfig = HeadConfig() style: Style = Style() - post_template: str = None - - @pydantic.validator("post_template", pre=True, always=True) - def default_post_template(cls, v): - if v is None: - return ( - Path(__file__).parent / "default_post_template.html.jinja" - ).read_text() - if isinstance(v, Path): - return v.read_text() - if isinstance(v, str) and Path(v).exists(): - return Path(v).read_text() + post_template: str = "post.html" + dynamic_templates_dir: Path = Path(".markata.cache/templates") + templates_dir: List[Path] = pydantic.Field( + [Path("templates"), Path(__file__).parents[1] / "templates"], + ) + env_options: dict = {} + + @pydantic.validator("templates_dir", pre=True, always=True) + def dynamic_templates_in_templates_dir(cls, v, *, values): + if values["dynamic_templates_dir"] not in v: + v.append(values["dynamic_templates_dir"]) return v + @property + def jinja_loader(self): + return jinja2.FileSystemLoader(self.templates_dir) + + @property + def jinja_env( + self, + ): + if hasattr(self, "_jinja_env"): + return self._jinja_env + self.env_options.setdefault("loader", self.jinja_loader) + self.env_options.setdefault("undefined", SilentUndefined) + self.env_options.setdefault("lstrip_blocks", True) + self.env_options.setdefault("trim_blocks", True) + + env = jinja2.Environment(**self.env_options) + + self._jinja_env = env + return env + class PostOverrides(pydantic.BaseModel): head: HeadConfig = HeadConfig() @@ -191,6 +211,13 @@ class PostOverrides(pydantic.BaseModel): class Post(pydantic.BaseModel): config_overrides: PostOverrides = PostOverrides() + template: Optional[str] = None + + @pydantic.validator("template", pre=True, always=True) + def default_template(cls, v, *, values): + if v is None: + return values["markata"].config.post_template + return v @hook_impl(tryfirst=True) @@ -228,6 +255,15 @@ def pre_render(markata: "Markata") -> None: allowing an simpler jinja template. This enablees the use of the `markata.head.text` list in configuration. """ + + markata.config.dynamic_templates_dir.mkdir(parents=True, exist_ok=True) + head_template = markata.config.dynamic_templates_dir / "head.html" + head_template.write_text( + markata.config.jinja_env.get_template("base_head.html").render( + {"markata": markata} + ), + ) + for article in [a for a in markata.articles if "config_overrides" in a]: raw_text = article.get("config_overrides", {}).get("head", {}).get("text", "") @@ -239,48 +275,95 @@ def pre_render(markata: "Markata") -> None: @hook_impl def render(markata: "Markata") -> None: - template = Template(markata.config.post_template, undefined=SilentUndefined) + # with markata.cache as cache: + # for article in markata.articles: + # merged_config = markata.config + # key = markata.make_hash( + # "post_template", + # __version__, + # merged_config, + # article.key, + # ) - if "{{" in str(markata.config.get("head", {})): - Template( - str(markata.config.get("head", {})), - undefined=SilentUndefined, - ) - else: - pass + # article._html = markata.precache.get(key) + + # futures = [ + # (article, render_article(markata, article, cache)) + # for article in markata.articles + # ] + futures = [] merged_config = markata.config - for article in [a for a in markata.articles if hasattr(a, "html")]: - # TODO do we need to handle merge?? - # if head_template: - # head = eval( - # head_template.render( - # __version__=__version__, - # config=_full_config, - # **article, - # ) - # ) - - # merged_config = { - # **_full_config, - # **{"head": head}, - # } - - # merged_config = always_merger.merge( - # merged_config, - # copy.deepcopy( - # article.get( - # "config_overrides", - # {}, - # ) - # ), - # ) - - article.html = template.render( - __version__=__version__, - body=article.html, - toc=markata.md.toc, # type: ignore - config=merged_config, - post=article, - **article.metadata, + for article in markata.articles: + key = markata.make_hash( + "post_template", + __version__, + merged_config, + article.key, ) + html = markata.precache.get(key) + + if html is not None: + article.html = html + else: + futures.append((article, render_article(markata, article))) + + for article, future in futures: + article.html = future.result() + # cache.set(key, article.html) + + +@lru_cache() +def get_template(markata, template): + try: + return markata.config.jinja_env.get_template(template) + except jinja2.TemplateNotFound: + # try to load it as a file + ... + + try: + return Template(Path(template).read_text(), undefined=SilentUndefined) + except FileNotFoundError: + # default to load it as a string + ... + return Template(template, undefined=SilentUndefined) + + +@background.task +def render_article(markata, article): + merged_config = markata.config + template = get_template(markata, article.template) + # TODO do we need to handle merge?? + # if head_template: + # head = eval( + # head_template.render( + # __version__=__version__, + # config=_full_config, + # **article, + # ) + # ) + + # merged_config = { + # **_full_config, + # **{"head": head}, + # } + + # merged_config = always_merger.merge( + # merged_config, + # copy.deepcopy( + # article.get( + # "config_overrides", + # {}, + # ) + # ), + # ) + + html = template.render( + __version__=__version__, + body=article.article_html, + toc=markata.md.toc, # type: ignore + config=merged_config, + post=article, + **article.metadata, + ) + return html diff --git a/markata/templates/base.html b/markata/templates/base.html new file mode 100644 index 00000000..23643590 --- /dev/null +++ b/markata/templates/base.html @@ -0,0 +1,50 @@ + + + + + {% block head %} + {% if post.title or config.title %} + {{ post.title or config.title }} + {% endif %} + + + {% if post.description or config.description %} + + {% endif %} {% if config.icon %} + + {% endif %} + + + + + + {% if 'markata.plugins.service_worker' in config.hooks %} + + {% endif %} + + {% include "head.html" %} + {% endblock %} + + + + {% include "nav.html" %} + {% block content %} {% endblock %} + {% block footer %} {% endblock %} + + + diff --git a/markata/templates/base_head.html b/markata/templates/base_head.html new file mode 100644 index 00000000..7d5e322e --- /dev/null +++ b/markata/templates/base_head.html @@ -0,0 +1,7 @@ +{% for meta in markata.config.head.meta %} + +{% endfor %} + +{% for link in markata.config.head.link %} + +{% endfor %} diff --git a/markata/templates/feed.html b/markata/templates/feed.html new file mode 100644 index 00000000..70811023 --- /dev/null +++ b/markata/templates/feed.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% block content %} +
+
+

{{ title }}

+
+
+ +
+
+{% endblock %} diff --git a/markata/templates/nav.html b/markata/templates/nav.html new file mode 100644 index 00000000..80720a7b --- /dev/null +++ b/markata/templates/nav.html @@ -0,0 +1,12 @@ + +
+ +
diff --git a/markata/templates/post.css b/markata/templates/post.css new file mode 100644 index 00000000..1af7ddbb --- /dev/null +++ b/markata/templates/post.css @@ -0,0 +1,957 @@ +:root { + --color-bg:{{markata.config.style.color_bg}}; + --color-bg-code:{{markata.config.style.color_bg_code}}; + --color-text:{{markata.config.style.color_text}}; + --color-link:{{markata.config.style.color_link}}; + --color-accent:{{markata.config.style.color_accent}}; + --overlay-brightness:{{markata.config.style.overlay_brightness}}; + --body-width:{{markata.config.style.body_width}}; +} + +[data-theme="dark"] { + --color-bg:{{markata.config.style.color_bg}}; + --color-bg-code:{{markata.config.style.color_bg_code}}; + --color-text:{{markata.config.style.color_text}}; + --color-link:{{markata.config.style.color_link}}; + --color-accent:{{markata.config.style.color_accent}}; + --overlay-brightness:{{markata.config.style.overlay_brightness}}; + --body-width:{{markata.config.style.body_width}}; + --audio-filter: invert(100%); +} + +[data-theme="light"] { + --color-bg:{{markata.config.style.color_bg_light}}; + --color-bg-2:{{markata.config.style.color_bg_light_2}}; + --color-bg-code:{{markata.config.style.color_bg_code_light}}; + --color-text:{{markata.config.style.color_text_light}}; + --color-link:{{markata.config.style.color_link_light}}; + --color-accent:{{markata.config.style.color_accent_light}}; + --overlay-brightness:{{markata.config.style.overlay_brightness_light}}; + --body-width:{{markata.config.style.body_width}}; + --audio-filter: invert(0%); +} + +audio { + filter: var(--audio-filter); + width: -webkit-fill-available; + margin: 1rem 5rem +} + +html { + font-family: "Space Mono", monospace; + background: var(--color-bg); + color: var(--color-text); +} + +a { + color: var(--color-link); +} + +main a { + max-width: 100%; +} + +.heading-permalink { + font-size: .7em; +} + +body { + max-width: var(--body-width); + margin: 5rem auto; + padding: 0 .5rem; + font-size: 1rem; + line-height: 1.56; +} + +blockquote { + background: var(--color-bg); + filter: brightness(var(--overlay-brightness)); + border-left: 4px solid var(--color-accent); + border-radius: 4px; + box-shadow: + -0.8rem 0rem 1rem -1rem #f1fa8c, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); + padding-left: 1rem; + margin: 1rem; +} + +li.post { + list-style-type: None; + padding: .2rem 0; +} + +pre.wrapper { + padding: 0; + box-shadow: 0.2rem 0rem 1rem rgb(0, 0, 0, .4); + display: flex; + flex-direction: column; + position: relative; + margin: 2rem; +} + +pre { + margin: 0; + padding: 1rem; + min-width: -webkit-fill-available; + max-width: fit-content; + overflow-x: auto; +} + +pre .filepath { + margin: 0; + padding-left: 1rem; + border-radius: 4px 4px 0 0; + background: black; + display: flex; + justify-content: space-between; + align-items: center; +} + +pre .filepath p { + margin: 0 +} + +pre .filepath .right { + display: flex; + gap: .2rem; + align-items: center; +} + +pre::-webkit-scrollbar { + height: 4px; + background-color: transparent; +} + +pre::-webkit-scrollbar-thumb { + background-color: #d3d3d32e; + border-radius: 2px; +} + +pre::-webkit-scrollbar-track { + background-color: transparent; +} + +.copy-wrapper { + background: none; + position: absolute; + width: 100%; + z-index: 100; + display: flex; + justify-content: flex-end; +} + +button.copy { + z-index: 100; + background: none; + fill: #ffffff45; + border: none; + width: 32px; + align-self: flex-end; + top: 0; + right: 0; + margin: 0.5rem 0.2rem; + +} + +button.copy:hover { + fill: white +} + +a.help { + fill: #ffffff45; +} + +a.help:hover { + fill: white; +} + +a.help svg { + height: 24px; + width: 24px; +} + +.highlight { + background: var(--color-bg-code); + color: var(--color-text); + filter: brightness(var(--overlay-brightness)); + border-radius: 0 0 4px 4px; +} + +.highlight .c { + color: #8b8b8b +} + +/* Comment */ +.highlight .err { + color: #960050; + background-color: #1e0010 +} + +/* Error */ +.highlight .k { + color: #c678dd +} + +/* Keyword */ +.highlight .l { + color: #ae81ff +} + +/* Literal */ +.highlight .n { + color: #abb2bf +} + +/* Name */ +.highlight .o { + color: #c678dd +} + +/* Operator */ +.highlight .p { + color: #abb2bf +} + +/* Punctuation */ +.highlight .ch { + color: #8b8b8b +} + +/* Comment.Hashbang */ +.highlight .cm { + color: #8b8b8b +} + +/* Comment.Multiline */ +.highlight .cp { + color: #8b8b8b +} + +/* Comment.Preproc */ +.highlight .cpf { + color: #8b8b8b +} + +/* Comment.PreprocFile */ +.highlight .c1 { + color: #8b8b8b +} + +/* Comment.Single */ +.highlight .cs { + color: #8b8b8b +} + +/* Comment.Special */ +.highlight .gd { + color: #c678dd +} + +/* Generic.Deleted */ +.highlight .ge { + font-style: italic +} + +/* Generic.Emph */ +.highlight .gi { + color: #a6e22e +} + +/* Generic.Inserted */ +.highlight .gs { + font-weight: bold +} + +/* Generic.Strong */ +.highlight .gu { + color: #8b8b8b +} + +/* Generic.Subheading */ +.highlight .kc { + color: #c678dd +} + +/* Keyword.Constant */ +.highlight .kd { + color: #c678dd +} + +/* Keyword.Declaration */ +.highlight .kn { + color: #c678dd +} + +/* Keyword.Namespace */ +.highlight .kp { + color: #c678dd +} + +/* Keyword.Pseudo */ +.highlight .kr { + color: #c678dd +} + +/* Keyword.Reserved */ +.highlight .kt { + color: #c678dd +} + +/* Keyword.Type */ +.highlight .ld { + color: #e6db74 +} + +/* Literal.Date */ +.highlight .m { + color: #ae81ff +} + +/* Literal.Number */ +.highlight .s { + color: #e6db74 +} + +/* Literal.String */ +.highlight .na { + color: #a6e22e +} + +/* Name.Attribute */ +.highlight .nb { + color: #98c379 +} + +/* Name.Builtin */ +.highlight .nc { + color: #abb2bf +} + +/* Name.Class */ +.highlight .no { + color: #c678dd +} + +/* Name.Constant */ +.highlight .nd { + color: #abb2bf +} + +/* Name.Decorator */ +.highlight .ni { + color: #abb2bf +} + +/* Name.Entity */ +.highlight .ne { + color: #a6e22e +} + +/* Name.Exception */ +.highlight .nf { + color: #61afef +} + +/* Name.Function */ +.highlight .nl { + color: #abb2bf +} + +/* Name.Label */ +.highlight .nn { + color: #abb2bf +} + +/* Name.Namespace */ +.highlight .nx { + color: #a6e22e +} + +/* Name.Other */ +.highlight .py { + color: #abb2bf +} + +/* Name.Property */ +.highlight .nt { + color: #c678dd +} + +/* Name.Tag */ +.highlight .nv { + color: #abb2bf +} + +/* Name.Variable */ +.highlight .ow { + color: #c678dd +} + +/* Operator.Word */ +.highlight .w { + color: #abb2bf +} + +/* Text.Whitespace */ +.highlight .mb { + color: #ae81ff +} + +/* Literal.Number.Bin */ +.highlight .mf { + color: #ae81ff +} + +/* Literal.Number.Float */ +.highlight .mh { + color: #ae81ff +} + +/* Literal.Number.Hex */ +.highlight .mi { + color: #ae81ff +} + +/* Literal.Number.Integer */ +.highlight .mo { + color: #ae81ff +} + +/* Literal.Number.Oct */ +.highlight .sa { + color: #e6db74 +} + +/* Literal.String.Affix */ +.highlight .sb { + color: #e6db74 +} + +/* Literal.String.Backtick */ +.highlight .sc { + color: #e6db74 +} + +/* Literal.String.Char */ +.highlight .dl { + color: #e6db74 +} + +/* Literal.String.Delimiter */ +.highlight .sd { + color: #98c379 +} + +/* Literal.String.Doc */ +.highlight .s2 { + color: #98c379 +} + +/* Literal.String.Double */ +.highlight .se { + color: #ae81ff +} + +/* Literal.String.Escape */ +.highlight .sh { + color: #e6db74 +} + +/* Literal.String.Heredoc */ +.highlight .si { + color: #e6db74 +} + +/* Literal.String.Interpol */ +.highlight .sx { + color: #e6db74 +} + +/* Literal.String.Other */ +.highlight .sr { + color: #e6db74 +} + +/* Literal.String.Regex */ +.highlight .s1 { + color: #e6db74 +} + +/* Literal.String.Single */ +.highlight .ss { + color: #e6db74 +} + +/* Literal.String.Symbol */ +.highlight .bp { + color: #abb2bf +} + +/* Name.Builtin.Pseudo */ +.highlight .fm { + color: #61afef +} + +/* Name.Function.Magic */ +.highlight .vc { + color: #abb2bf +} + +/* Name.Variable.Class */ +.highlight .vg { + color: #abb2bf +} + +/* Name.Variable.Global */ +.highlight .vi { + color: #abb2bf +} + +/* Name.Variable.Instance */ +.highlight .vm { + color: #abb2bf +} + +/* Name.Variable.Magic */ +.highlight .il { + color: #ae81ff +} + +/* Literal.Number.Integer.Long */ + +/* Tab style starts here */ +.tabbed-set { + position: relative; + display: flex; + flex-wrap: wrap; + margin: 1em 0; + border-radius: 0.1rem; +} + +.tabbed-set>input { + display: none; +} + +.tabbed-set label { + width: auto; + padding: 0.9375em 1.25em 0.78125em; + font-weight: 700; + font-size: 0.84em; + white-space: nowrap; + border-bottom: 0.15rem solid transparent; + border-top-left-radius: 0.1rem; + border-top-right-radius: 0.1rem; + cursor: pointer; + transition: background-color 250ms, color 250ms; +} + +.tabbed-set .tabbed-content { + width: 100%; + display: none; + box-shadow: 0 -.05rem #ddd; +} + +.tabbed-set input { + position: absolute; + opacity: 0; +} + +/* fonts */ +h1 { + font-weight: 700; +} + +h1#title a { + font-size: 16px; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + margin-top: 3rem; +} + +h1 { + font-size: 2.5em; + margin-top: 5rem; +} + +h2 { + font-size: 1.63rem; + margin-top: 5rem; +} + + + +p { + font-size: 21px; + font-style: normal; + font-variant: normal; + font-weight: 400; + line-height: 1.5; +} + +@media only screen and (max-width: 700px) { + p { + font-size: 18px; + } +} + +@media only screen and (max-width: 600px) { + p { + font-size: 16px; + } +} + +@media only screen and (max-width: 500px) { + p { + font-size: 14px; + } +} + +@media only screen and (max-width: 400px) { + p { + font-size: 12px; + } +} + + +pre { + font-style: normal; + font-variant: normal; + font-weight: 400; + line-height: 18.5714px; + */ +} + +a { + font-weight: 600; + text-decoration-color: var(--color-accent); + color: var(--color-link); + padding: .3rem .5rem; + display: inline-block; +} + +.admonition, +details { + box-shadow: 0.2rem 0rem 1rem rgb(0, 0, 0, .4); + margin: 5rem 0; + border: 1px solid transparent; + border-radius: 4px; + text-align: left; + padding: 0; + border: 0; + +} + +.admonition { + padding-bottom: 1rem; +} + +details[open] { + padding-bottom: .5rem; +} + +.admonition p { + padding: .2rem .6rem; +} + +.admonition-title, +.details-title, +summary { + background: var(--color-bg-2); + padding: 0; + margin: 0; + position: sticky; + top: 0; + z-index: 10; +} + +summary:hover { + cursor: pointer; +} + +summary.admonition-title, +summary.details-title { + padding: .5rem; + padding-left: 1rem; +} + +.note { + border-left: 4px solid #f1fa8c; + box-shadow: + -0.8rem 0rem 1rem -1rem #f1fa8c, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.note>.admonition-title { + border-bottom: 1px solid #3c3d2d; +} + +.abstract { + border-left: 4px solid #8be9fd; + box-shadow: + -0.8rem 0rem 1rem -1rem #8be9fd, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.abstract>.admonition-title { + border-bottom: 1px solid #2c3a3f; +} + +.info { + border-left: 4px solid; + box-shadow: + -0.8rem 0rem 1rem -1rem #8bb0fd, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.info>.admonition-title { + border-bottom: 1px solid #2c313f; +} + +.tip { + border-left: 4px solid #008080; + box-shadow: + -0.8rem 0rem 1rem -1rem #008080, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.tip>.admonition-title { + border-bottom: 1px solid #1b2a2b; +} + +.success { + border-left: 4px solid #50fa7b; + box-shadow: + -0.8rem 0rem 1rem -1rem #50fa7b, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.success>.admonition-title { + border-bottom: 1px solid #263e2b; +} + +.question { + border-left: 4px solid #a7fcbd; + box-shadow: + -0.8rem 0rem 1rem -1rem #a7fcbd, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.question>.admonition-title { + border-bottom: 1px solid #303e35; +} + +.warning { + border-left: 4px solid #ffb86c; + box-shadow: + -0.8rem 0rem 1rem -1rem #ffb86c, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.warning>.admonition-title { + border-bottom: 1px solid #3f3328; +} + +.failure { + border-left: 4px solid #b23b3b; + box-shadow: + -0.8rem 0rem 1rem -1rem #b23b3b, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.failure>.admonition-title { + border-bottom: 1px solid #34201f; +} + +.danger { + border-left: 4px solid #ff5555; + box-shadow: + -0.8rem 0rem 1rem -1rem #ff5555, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.danger>.admonition-title { + border-bottom: 1px solid #402523; +} + +.bug { + border-left: 4px solid #b2548a; + box-shadow: + -0.8rem 0rem 1rem -1rem #b2548a, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.bug>.admonition-title { + border-bottom: 1px solid #32232c; +} + +.example { + border-left: 4px solid #bd93f9; + box-shadow: + -0.8rem 0rem 1rem -1rem #bd93f9, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.example>.admonition-title { + border-bottom: 1px solid #332d3e; +} + +.source { + border-left: 4px solid #bd93f9; + box-shadow: + -0.8rem 0rem 1rem -1rem #bd93f9, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.source>.admonition-title { + border-bottom: 1px solid #332d3e; +} + +.quote { + border-left: 4px solid #999; + box-shadow: + -0.8rem 0rem 1rem -1rem #999, + 0.2rem 0rem 1rem rgb(0, 0, 0, .4); +} + +.quote>.admonition-title { + border-bottom: 1px solid #2d2e2f; +} + +table { + margin: 1rem 0; + border-collapse: collapse; + border-spacing: 0; + display: block; + max-width: -moz-fit-content; + max-width: fit-content; + overflow-x: auto; + white-space: nowrap; +} + +table thead th { + border: solid 1px var(--color-text); + padding: 10px; + text-align: left; +} + +table tbody td { + border: solid 1px var(--color-text); + padding: 10px; +} + +.theme-switch { + z-index: 10; + display: inline-block; + height: 34px; + position: relative; + width: 60px; + + display: flex; + justify-content: flex-end; + margin-right: 1rem; + margin-left: auto; + position: fixed; + right: 1rem; + top: 1rem; +} + +.theme-switch input { + display: none; + +} + +.slider { + background-color: #ccc; + bottom: 0; + cursor: pointer; + left: 0; + position: absolute; + right: 0; + top: 0; + transition: .4s; +} + +.slider:before { + background-color: #fff; + bottom: 4px; + content: ""; + height: 26px; + left: 4px; + position: absolute; + transition: .4s; + width: 26px; +} + +input:checked+.slider { + background-color: #343434; +} + +input:checked+.slider:before { + background-color: #848484; +} + +input:checked+.slider:before { + transform: translateX(26px); +} + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} + +img { + width: 100%; + width: -moz-available; + width: -webkit-fill-available; +} + +a { + max-width: -moz-available; + max-width: -webkit-fill-available; +} + +details>* { + margin: 1rem; +} + +.admonition>* { + margin: 1rem; +} + +p.admonition-title, +summary { + margin: 0; + padding-left: 1.2rem; +} + +.small { + font-size: .9rem; + color: #888; +} + +admonition+admonition { + margin-top: 20rem; +} + +::-webkit-scrollbar { + height: 12px; + background-color: transparent; +} + +::-webkit-scrollbar-thumb { + background-color: #d3d3d32e; + border-radius: 6px; +} + +::-webkit-scrollbar-track { + background-color: transparent; +} diff --git a/markata/templates/post.html b/markata/templates/post.html new file mode 100644 index 00000000..b188cc88 --- /dev/null +++ b/markata/templates/post.html @@ -0,0 +1,9 @@ +{% extends "base.html" %} +{% block content %} +
+ {% include "title.html" %} +
+ {{ body }} +
+
+{% endblock %} diff --git a/markata/templates/theme.js b/markata/templates/theme.js new file mode 100644 index 00000000..9ccd87e6 --- /dev/null +++ b/markata/templates/theme.js @@ -0,0 +1,56 @@ + + function setTheme(theme) { + document.documentElement.setAttribute("data-theme", theme); + } + + function detectColorSchemeOnLoad() { + //local storage is used to override OS theme settings + if (localStorage.getItem("theme")) { + if (localStorage.getItem("theme") == "dark") { + setTheme("dark"); + } else if (localStorage.getItem("theme") == "light") { + setTheme("light"); + } + } else if (!window.matchMedia) { + //matchMedia method not supported + setTheme("light"); + return false; + } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) { + //OS theme setting detected as dark + setTheme("dark"); + } else { + setTheme("light"); + } + } + detectColorSchemeOnLoad(); + document.addEventListener( + "DOMContentLoaded", + function () { + //identify the toggle switch HTML element + const toggleSwitch = document.querySelector( + '#theme-switch input[type="checkbox"]', + ); + + //function that changes the theme, and sets a localStorage variable to track the theme between page loads + function switchTheme(e) { + if (e.target.checked) { + localStorage.setItem("theme", "dark"); + document.documentElement.setAttribute("data-theme", "dark"); + toggleSwitch.checked = true; + } else { + localStorage.setItem("theme", "light"); + document.documentElement.setAttribute("data-theme", "light"); + toggleSwitch.checked = false; + } + } + + //listener for changing themes + toggleSwitch.addEventListener("change", switchTheme, false); + + //pre-check the dark-theme checkbox if dark-theme is set + if (document.documentElement.getAttribute("data-theme") == "dark") { + toggleSwitch.checked = true; + } + }, + false, + ); diff --git a/markata/templates/title.html b/markata/templates/title.html new file mode 100644 index 00000000..cfe56855 --- /dev/null +++ b/markata/templates/title.html @@ -0,0 +1,31 @@ +
+

+ {{ title }} {% if config.get %} + + + + + + + + + + + + + {% endif %} +

+