Compare commits

...

19 Commits

Author SHA1 Message Date
7fa826d7b3 [style] Fix listing connection levels. 2025-04-01 22:23:47 +00:00
80c5dc793b Fix unindentation function. 2025-04-01 22:23:18 +00:00
Sergio Martínez Portela
e35fafb18e Add parameter to control the amount of highlighted output. 2025-02-23 23:33:38 +01:00
Sergio Martínez Portela
842a5e712b Update highlighting style. 2025-02-23 23:05:04 +01:00
Sergio Martínez Portela
cd30f9b9c9 Add support for retrieval of snippet matches. 2025-02-23 22:50:34 +01:00
Sergio Martínez Portela
41c02acaca Add support for generic drawer outputs. 2025-02-09 14:12:20 +01:00
Sergio Martínez Portela
a00a53612e Add support for skipping private headlines. 2025-02-09 13:25:09 +01:00
Sergio Martínez Portela
06b5d1b50c Add small readme. 2025-02-09 11:53:30 +01:00
Sergio Martínez Portela
fba35555b3 Handle 'uindentation' of empty texts.' 2024-10-28 02:46:48 +01:00
Sergio Martínez Portela
04fe576385 Add default TODO/DONE states. 2024-08-22 20:36:51 +02:00
Sergio Martínez Portela
9a6d0191d7 Handle new headline state type. 2024-08-22 20:31:17 +02:00
Sergio Martínez Portela
2f3c52f5f2 Push notes section to the top. 2024-07-21 19:54:23 +02:00
Sergio Martínez Portela
d630fb0f70 Add Malleable Software slides to homepage. 2024-07-21 19:48:23 +02:00
Sergio Martínez Portela
ce35091852 Add configurable subpath. 2024-05-05 12:29:26 +02:00
Sergio Martínez Portela
d9b85c8475 Fix height of arrows on external links. 2024-03-12 01:40:26 +01:00
Sergio Martínez Portela
9a020285ad Add clean styling for SOMETIME state. 2024-03-10 17:51:58 +01:00
Sergio Martínez Portela
e639df35a7 s/Mastodon/ActivityPub/ 2023-11-12 15:34:44 +01:00
Sergio Martínez Portela
89e50a6310 Fix color scheme on dark mode. 2023-11-01 23:37:57 +01:00
Sergio Martínez Portela
28122c3c31 Use dark-syntax.css only when dark mode is selected. 2023-10-05 08:53:50 +02:00
8 changed files with 321 additions and 120 deletions

29
README.md Normal file
View File

@ -0,0 +1,29 @@
# Codigo para llevar's generator
This is the static site generator used to build [Codigo Para Llevar](https://codigoparallevar.com/) (my personal site). It contains:
- A markdown blog (with content ported from acrylamid). Saved as `/blog/`.
- A set of org-mode based notes. Saved as `/notes/`.
It also copies over some static assets (css, js, fonts).
The scripts are hardcoded with the hostnames and paths for my own site, so you might want to update them.
General documentation is in progress and might be replaced little by little by the more interactive [org-web-editor](https://code.codigoparallevar.com/kenkeiras/org-web-editor) once that one (1) supports all the features here and (2) has support for building static sites.
## Instructions
Generally, what you want to do is to run `make` once to prepare the static files, then run this to generate the notes.
```bash
mkdir -p _gen
WATCH_AND_REBUILD=0 python3 scripts/generate.py <path to your notes> _gen/notes [<DEFAULT SUBPATH (usually 'public', '.' to ignore)>]
```
Use `WATCH_AND_REBUILD=1` (or empty) for automatic rebuilds.
## Filtering
This won't render **all** notes, but try to select the PUBLIC ones and skip the PRIVATE ones.
PUBLIC files are contained on the DEFAULT_SUBPATH, PRIVATE headlines have the `:private:` tag.

View File

@ -46,13 +46,14 @@ IMG_EXTENSIONS = set([
"gif", "gif",
]) ])
SKIPPED_TAGS = set(['attach']) SKIPPED_TAGS = set(['attach'])
DEFAULT_SUBPATH = "public"
WATCH = True WATCH = True
if os.getenv('WATCH_AND_REBUILD', '1') == '0': if os.getenv('WATCH_AND_REBUILD', '1') == '0':
WATCH = False WATCH = False
MIN_HIDDEN_HEADLINE_LEVEL = 2 MIN_HIDDEN_HEADLINE_LEVEL = 2
INDEX_ID = "ea48ec1d-f9d4-4fb7-b39a-faa7b6e2ba95" INDEX_ID = os.getenv("INDEX_ID", "ea48ec1d-f9d4-4fb7-b39a-faa7b6e2ba95")
SITE_NAME = "Código para llevar" SITE_NAME = "Código para llevar"
MONITORED_EVENT_TYPES = ( MONITORED_EVENT_TYPES = (
@ -108,7 +109,9 @@ def load_all(top_dir_relative):
path = os.path.join(root, name) path = os.path.join(root, name)
try: try:
doc = load_org(open(path), extra_cautious=True) doc = load_org(open(path),
environment={"org-todo-keywords": "TODO(t) NEXT(n) MEETING(m/!) Q(q) PAUSED(p!/!) EVENT(e/!) SOMETIME(s) WAITING(w@/!) TRACK(r/!) | DISCARDED(x@/!) VALIDATING(v!/!) DONE(d!/!)"},
extra_cautious=True)
docs.append(doc) docs.append(doc)
except Exception as err: except Exception as err:
import traceback import traceback
@ -120,7 +123,22 @@ def load_all(top_dir_relative):
logging.info("Collected {} files".format(len(docs))) logging.info("Collected {} files".format(len(docs)))
return docs return docs
def regen_all(src_top, dest_top, *, docs=None, db=None): def remove_non_public_headlines(doc: org_rw.OrgDoc | org_rw.Headline):
if isinstance(doc, org_rw.OrgDoc):
doc.headlines = list(filter_private_headlines(doc.headlines))
for hl in doc.headlines:
remove_non_public_headlines(hl)
else:
doc.children = list(filter_private_headlines(doc.children))
for hl in doc.children:
remove_non_public_headlines(hl)
def filter_private_headlines(headlines):
for hl in headlines:
if 'private' not in hl.tags:
yield hl
def regen_all(src_top, dest_top, subpath, *, docs=None, db=None):
files_generated = 0 files_generated = 0
cur = db.cursor() cur = db.cursor()
cleaned_db = False cleaned_db = False
@ -147,10 +165,12 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
main_headline_to_docid = {} main_headline_to_docid = {}
for doc in docs: for doc in docs:
relpath = os.path.relpath(doc.path, src_top) relpath = os.path.relpath(doc.path, src_top)
remove_non_public_headlines(doc)
changed = False changed = False
headlines = list(doc.getAllHeadlines()) headlines = list(doc.getAllHeadlines())
related = None related = None
if not relpath.startswith("public/"): if not relpath.startswith(subpath + "/"):
# print("Skip:", relpath) # print("Skip:", relpath)
continue continue
@ -349,7 +369,7 @@ def regen_all(src_top, dest_top, *, docs=None, db=None):
dirs_exist_ok=True) dirs_exist_ok=True)
def main(src_top, dest_top): def main(src_top, dest_top, subpath):
notifier = inotify.adapters.InotifyTrees([src_top, STATIC_PATH]) notifier = inotify.adapters.InotifyTrees([src_top, STATIC_PATH])
## Initial load ## Initial load
@ -357,7 +377,7 @@ def main(src_top, dest_top):
os.makedirs(dest_top, exist_ok=True) os.makedirs(dest_top, exist_ok=True)
db = create_db(os.path.join(dest_top, 'db.sqlite3')) db = create_db(os.path.join(dest_top, 'db.sqlite3'))
docs = regen_all(src_top, dest_top, db=db) docs = regen_all(src_top, dest_top, subpath=subpath, db=db)
if not WATCH: if not WATCH:
logging.info("Build completed in {:.2f}s".format(time.time() - t0)) logging.info("Build completed in {:.2f}s".format(time.time() - t0))
@ -375,7 +395,7 @@ def main(src_top, dest_top):
print("CHANGED: {}".format(filepath)) print("CHANGED: {}".format(filepath))
t0 = time.time() t0 = time.time()
try: try:
docs = regen_all(src_top, dest_top, docs=docs, db=db) docs = regen_all(src_top, dest_top, subpath=subpath, docs=docs, db=db)
except: except:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
logging.error("Loading new templates failed 😿") logging.error("Loading new templates failed 😿")
@ -477,7 +497,7 @@ def unindent(content):
len(l) - len(l.lstrip(' ')) len(l) - len(l.lstrip(' '))
for l in content.split('\n') for l in content.split('\n')
if len(l.strip()) > 0 if len(l.strip()) > 0
]) ] or [0])
content_lines = [ content_lines = [
l[base_indentation:] l[base_indentation:]
for l in content.split('\n') for l in content.split('\n')
@ -510,6 +530,12 @@ def render_results_block(element, acc, headline, graph):
if len(content.strip()) > 0: if len(content.strip()) > 0:
render_block(content, acc, _class='results lang-text', is_code=False) render_block(content, acc, _class='results lang-text', is_code=False)
def render_generic_drawer_block(element, acc, headline, graph):
items = [e.get_raw() for e in element.children]
content = '\n'.join(items)
if len(content.strip()) > 0:
render_block(content, acc, _class='generic-drawer {}-drawer lang-text'.format(element.drawer_name), is_code=False)
def render_org_text(element, acc, headline, graph): def render_org_text(element, acc, headline, graph):
as_dom = org_rw.text_to_dom(element.contents, element) as_dom = org_rw.text_to_dom(element.contents, element)
render_text_tokens(as_dom, acc, headline, graph) render_text_tokens(as_dom, acc, headline, graph)
@ -642,6 +668,7 @@ def render_tag(element, acc, headline, graph):
dom.CodeBlock: render_code_block, dom.CodeBlock: render_code_block,
dom.Text: render_text, dom.Text: render_text,
dom.ResultsDrawerNode: render_results_block, dom.ResultsDrawerNode: render_results_block,
dom.GenericDrawerNode: render_generic_drawer_block,
org_rw.Text: render_org_text, org_rw.Text: render_org_text,
}[type(element)](element, acc, headline, graph) }[type(element)](element, acc, headline, graph)
@ -714,8 +741,11 @@ def render_connections(headline_id, content, graph, doc_to_headline_remapping):
# return # return
logging.info("Generating centered graph for {}".format(headline_id)) logging.info("Generating centered graph for {}".format(headline_id))
try:
svg = gen_centered_graph.gen(headline_id, graph['nodes'], doc_to_headline_remapping) svg = gen_centered_graph.gen(headline_id, graph['nodes'], doc_to_headline_remapping)
content.append("<div class='connections'>{}</div>".format(svg)) content.append("<div class='connections'>{}</div>".format(svg))
except:
logging.warning("Broken reference on headline ID={}".format(headline_id))
def render(headline, doc, graph, headlineLevel, doc_to_headline_remapping): def render(headline, doc, graph, headlineLevel, doc_to_headline_remapping):
try: try:
@ -735,10 +765,10 @@ def render(headline, doc, graph, headlineLevel, doc_to_headline_remapping):
content.append(render(child, doc, headlineLevel=headlineLevel+1, graph=graph, content.append(render(child, doc, headlineLevel=headlineLevel+1, graph=graph,
doc_to_headline_remapping=doc_to_headline_remapping)) doc_to_headline_remapping=doc_to_headline_remapping))
if headline.state is None: if headline.state is None or headline.state.get('name') is None:
state = "" state = ""
else: else:
state = f'<span class="state todo-{headline.is_todo} state-{headline.state}">{headline.state}</span>' state = f'<span class="state todo-{headline.is_todo} state-{headline.state["name"]}">{headline.state["name"]}</span>'
if headline.is_todo: if headline.is_todo:
todo_state = "todo" todo_state = "todo"
@ -825,9 +855,13 @@ def save_changes(doc):
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) != 3: if len(sys.argv) not in (3, 4):
print("Usage: {} SOURCE_TOP DEST_TOP".format(sys.argv[0])) print("Usage: {} SOURCE_TOP DEST_TOP <SUBPATH>".format(sys.argv[0]))
exit(0) exit(0)
logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s") logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s")
exit(main(sys.argv[1], sys.argv[2])) subpath = DEFAULT_SUBPATH
if len(sys.argv) == 4:
subpath = sys.argv[3]
exit(main(sys.argv[1], sys.argv[2], subpath=subpath))

View File

@ -12,4 +12,4 @@ cd ../../_gen/notes/
set -x set -x
exec docker run -it --rm -p $PORT:80 -e PORT=80 -e DB_PATH=/db.sqlite3 -v `pwd`/db.sqlite3:/db.sqlite3:ro search-server exec docker run -it --rm -p $PORT:80 -e SNIPPET_SIZE=256 -e PORT=80 -e DB_PATH=/db.sqlite3 -v `pwd`/db.sqlite3:/db.sqlite3:ro search-server

View File

@ -33,6 +33,22 @@ func main() {
port = port_num port = port_num
} }
snippet_size := 128
snippet_size_str, ok := os.LookupEnv("SNIPPET_SIZE")
if ok {
snippet_size_num, err := strconv.Atoi(snippet_size_str)
if err != nil {
log.Fatal(err)
os.Exit(1)
}
if (snippet_size_num < 64) {
log.Fatal("Environment variale $SNIPPET_SIZE must be >= 64.")
os.Exit(1)
}
snippet_size = snippet_size_num
}
db, err := sql.Open("sqlite3", database_path) db, err := sql.Open("sqlite3", database_path)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -63,8 +79,23 @@ func main() {
c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS") c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
query := c.Query("q") query := c.Query("q")
body_type := c.Query("body")
if ((body_type != "all") && (body_type != "none") && (body_type != "snippet")) {
body_type = "none"
}
var stm *sql.Stmt
var err error
if (body_type == "snippet") {
stm, err = db.Prepare("SELECT note_id, highlight(note_search, 1, '<span class=\"match\">', '</span>'), top_level_title, is_done, is_todo, snippet(note_search, 2, '<span class=\"match\">', '</span>', '<span class=\"search-result-break\"></span>', ?) FROM note_search(?)")
} else if (body_type == "all") {
stm, err = db.Prepare("SELECT note_id, highlight(note_search, 1, '<span class=\"match\">', '</span>'), top_level_title, is_done, is_todo, highlight(note_search, 2, '<span class=\"match\">', '</span>') FROM note_search(?)")
} else if (body_type == "none") {
stm, err = db.Prepare("SELECT note_id, highlight(note_search, 1, '<span class=\"match\">', '</span>'), top_level_title, is_done, is_todo FROM note_search(?)")
}
stm, err := db.Prepare("SELECT note_id, title, top_level_title, is_done, is_todo FROM note_search(?)")
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -76,8 +107,13 @@ func main() {
} }
results := make([]map[string]string, 0) results := make([]map[string]string, 0)
var rows *sql.Rows
rows, err := stm.Query(query) if (body_type == "snippet") {
rows, err = stm.Query(snippet_size, query)
} else {
rows, err = stm.Query(query)
}
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -94,6 +130,23 @@ func main() {
var note_is_done string var note_is_done string
var note_is_todo string var note_is_todo string
item := make(map[string]string)
if (body_type != "none") {
var note_highlight string
err = rows.Scan(
&note_id,
&note_title,
&note_top_level_title,
&note_is_done,
&note_is_todo,
&note_highlight,
)
if (body_type != "none") {
item["highlight"] = note_highlight
}
} else {
err = rows.Scan( err = rows.Scan(
&note_id, &note_id,
&note_title, &note_title,
@ -101,6 +154,7 @@ func main() {
&note_is_done, &note_is_done,
&note_is_todo, &note_is_todo,
) )
}
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
c.JSON(500, gin.H{ c.JSON(500, gin.H{
@ -110,7 +164,6 @@ func main() {
return return
} }
item := make(map[string]string)
item["id"] = note_id item["id"] = note_id
item["title"] = note_title item["title"] = note_title
item["top_level_title"] = note_top_level_title item["top_level_title"] = note_top_level_title

View File

@ -1,3 +1,5 @@
/* Dark mode. */
@media (prefers-color-scheme: dark) {
pre { line-height: 125%; } pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } 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; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
@ -80,3 +82,4 @@ span.linenos.special { color: #000000; background-color: #ffffc0; padding-left:
.vi { color: #f8f8f2 } /* Name.Variable.Instance */ .vi { color: #f8f8f2 } /* Name.Variable.Instance */
.vm { color: #f8f8f2 } /* Name.Variable.Magic */ .vm { color: #f8f8f2 } /* Name.Variable.Magic */
.il { color: #ae81ff } /* Literal.Number.Integer.Long */ .il { color: #ae81ff } /* Literal.Number.Integer.Long */
}

View File

@ -6,11 +6,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<style> <style>
body { body {
background-color: white;
font-family: sans-serif; font-family: sans-serif;
margin: 0 auto; margin: 0 auto;
width: fit-content; width: fit-content;
max-width: 100ex; max-width: 100ex;
padding: 0 1ex; padding: 0 1ex;
color: black;
} }
.header h1 { .header h1 {
text-align: center; text-align: center;
@ -45,7 +47,7 @@
border-right: 1px solid #000; border-right: 1px solid #000;
} }
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
html { body {
background-color: #1d1f21; background-color: #1d1f21;
color: #fafafe; color: #fafafe;
} }
@ -72,6 +74,12 @@
</div> </div>
<div class="links"> <div class="links">
<a href="/notes">
<section>
<h2>Notes</h2>
<p>Some publicly-visible notes from a sort of knowledge graph that I use as information dump.</p>
</section>
</a>
<section> <section>
<h2><a href="/blog">Blog</a></h2> <h2><a href="/blog">Blog</a></h2>
<p> <p>
@ -100,12 +108,24 @@
</ul> </ul>
</p> </p>
</section> </section>
<a href="/notes">
<section> <section>
<h2>Notes</h2> <h2>Talks / Slides</h2>
<p>Some publicly-visible notes from a sort of knowledge graph that I use as information dump.</p> <p>
<ul>
<li>
Malleable Software
(<a href="/slides/hackliza2024/software-maleable/software-maleable.odp">galician, </a>
for <a href="https://hackliza.gal">Hackliza</a>
<a href="/slides/hackliza2024/software-maleable/software-maleable.pdf">[PDF]</a>
<a href="/slides/hackliza2024/software-maleable/software-maleable.odp">[ODP]</a>)
(<a href="/slides/eslibre2024/software-maleable.odp">spanish,</a>
for <a href="https://eslib.re/2024/">esLibre 2024</a>
<a href="/slides/eslibre2024/software-maleable.pdf">[PDF]</a>
<a href="/slides/eslibre2024/software-maleable.odp">[ODP]</a>).
</li>
</ul>
</p>
</section> </section>
</a>
<!-- section> <!-- section>
<h2>Projects</h2> <h2>Projects</h2>
<p> <p>
@ -116,7 +136,7 @@
<section id="social"> <section id="social">
<h2>Find me</h2> <h2>Find me</h2>
<p> <p>
<a href="https://social.codigoparallevar.com/@kenkeiras">Mastodon</a> <a href="https://social.codigoparallevar.com/@kenkeiras">ActivityPub</a>
<a href="https://github.com/kenkeiras">GitHub</a> <a href="https://github.com/kenkeiras">GitHub</a>
<a href="https://gitlab.com/kenkeiras">GitLab</a> <a href="https://gitlab.com/kenkeiras">GitLab</a>
<a href="https://programaker.com/users/kenkeiras">PrograMaker</a> <a href="https://programaker.com/users/kenkeiras">PrograMaker</a>

View File

@ -1,4 +1,9 @@
function _codigoparallevar_enable_search_box(selector, options) { function _codigoparallevar_enable_search_box(selector, options) {
const unescape = (str, tag) => {
const prev = tag.replaceAll('<', '&lt;').replaceAll('>', '&gt;');
return str.replaceAll(prev, tag);
}
const element = document.querySelector(selector); const element = document.querySelector(selector);
if ('placeholder' in options) { if ('placeholder' in options) {
element.setAttribute('placeholder', options.placeholder); element.setAttribute('placeholder', options.placeholder);
@ -69,7 +74,7 @@ function _codigoparallevar_enable_search_box(selector, options) {
lastVal = val; lastVal = val;
resultsBox.classList.add('loading'); resultsBox.classList.add('loading');
const uri = SEARCH_ENDPOINT + '?q=' + encodeURIComponent(val); const uri = SEARCH_ENDPOINT + '?q=' + encodeURIComponent(val) + '&body=snippet';
let query = fetch(uri); let query = fetch(uri);
currentQuery = query; currentQuery = query;
query query
@ -94,14 +99,32 @@ function _codigoparallevar_enable_search_box(selector, options) {
const resultTitle = document.createElement('h2'); const resultTitle = document.createElement('h2');
resultTitle.innerText = `${note.title} (${note.top_level_title})`; resultTitle.innerText = `${note.title} (${note.top_level_title})`;
resultTitle.innerHTML = unescape(unescape(
resultTitle.innerHTML,
'</span>'), '<span class="match">'
);
if (note.is_todo === "1") { if (note.is_todo === "1") {
resultTitle.setAttribute('class', 'is-todo'); resultCard.setAttribute('class', 'is-todo');
} }
else if (note.is_done === "1") { else if (note.is_done === "1") {
resultTitle.setAttribute('class', 'is-done'); resultCard.setAttribute('class', 'is-done');
} }
resultContents.appendChild(resultTitle); resultContents.appendChild(resultTitle);
if (note.highlight) {
const resultBody = document.createElement('p');
resultBody.innerText = note.highlight;
resultBody.innerHTML = unescape(
unescape(
unescape(
resultBody.innerHTML,
'</span>'),
'<span class="search-result-break">'),
'<span class="match">');
resultContents.appendChild(resultBody);
}
resultCard.appendChild(resultContents); resultCard.appendChild(resultContents);
resultsList.appendChild(resultCard); resultsList.appendChild(resultCard);
} }

View File

@ -10,6 +10,8 @@ body {
max-width: 80ex; max-width: 80ex;
margin: 0 auto; margin: 0 auto;
padding: 0.5ex 1ex; padding: 0.5ex 1ex;
background-color: white;
color: black;
} }
body.blog { body.blog {
@ -124,13 +126,30 @@ body nav input {
box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.25); box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.25);
} }
.results-box ul li a {
text-decoration: none;
}
.results-box ul li h2 { .results-box ul li h2 {
font-size: 110%; font-size: 110%;
padding: 1.25ex; padding: 1.25ex;
display: block; display: block;
margin: 0; margin: 0;
text-decoration: underline;
} }
.results-box ul li p {
padding: 1.25ex;
color: black;
}
.results-box ul li span.match {
background: yellow;
}
.results-box ul li .search-result-break::before {
content: '…';
color: #777;
}
.results-box li h2.is-todo::before { .results-box li h2.is-todo::before {
content: 'TODO'; content: 'TODO';
display: inline-block; display: inline-block;
@ -244,6 +263,11 @@ img {
border-left: 2px solid var(--tree-color); border-left: 2px solid var(--tree-color);
} }
.node .contents ul ul ul li,
.global-table-of-contents ul ul ul li {
margin-left: calc(0px + var(--tree-radius) * 2 + .5ex);
}
.node .contents ul ul li::marker, .node .contents ul ul li::marker,
.global-table-of-contents ul ul li::marker { .global-table-of-contents ul ul li::marker {
content: ''; content: '';
@ -332,6 +356,11 @@ h1.title .state.todo-True {
h1.title .state.todo-False { h1.title .state.todo-False {
background-color: rgba(0,255,0,0.25); background-color: rgba(0,255,0,0.25);
} }
h1.title .state.todo-True.state-SOMETIME {
background-color: #ddd;
color: black;
}
h1.title .tags { h1.title .tags {
float: right; float: right;
@ -368,6 +397,7 @@ a.internal::after {
} }
a.external::after { a.external::after {
content: ' ↗'; content: ' ↗';
vertical-align: top;
} }
/* Markup */ /* Markup */
@ -580,7 +610,7 @@ tr.__table-separator {
/* Dark mode. */ /* Dark mode. */
@media (prefers-color-scheme: dark) { @media (prefers-color-scheme: dark) {
html { html, body {
background-color: #1d1f21; background-color: #1d1f21;
color: #fafafe; color: #fafafe;
} }
@ -651,10 +681,19 @@ tr.__table-separator {
.results-box ul li h2 { .results-box ul li h2 {
color: white; color: white;
} }
.results-box ul li p {
padding: 1.25ex;
color: white;
}
.results-box-container .results-box input:focus { .results-box-container .results-box input:focus {
border-bottom: 1px solid #fff; border-bottom: 1px solid #fff;
} }
.results-box ul li span.match {
background: #886600;
}
/* Code blocks */ /* Code blocks */
.highlight pre { .highlight pre {
padding: 1ex; padding: 1ex;