Compare commits
19 Commits
dev/global
...
develop
Author | SHA1 | Date | |
---|---|---|---|
7fa826d7b3 | |||
80c5dc793b | |||
![]() |
e35fafb18e | ||
![]() |
842a5e712b | ||
![]() |
cd30f9b9c9 | ||
![]() |
41c02acaca | ||
![]() |
a00a53612e | ||
![]() |
06b5d1b50c | ||
![]() |
fba35555b3 | ||
![]() |
04fe576385 | ||
![]() |
9a6d0191d7 | ||
![]() |
2f3c52f5f2 | ||
![]() |
d630fb0f70 | ||
![]() |
ce35091852 | ||
![]() |
d9b85c8475 | ||
![]() |
9a020285ad | ||
![]() |
e639df35a7 | ||
![]() |
89e50a6310 | ||
![]() |
28122c3c31 |
29
README.md
Normal file
29
README.md
Normal 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.
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
@ -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(
|
||||||
|
¬e_id,
|
||||||
|
¬e_title,
|
||||||
|
¬e_top_level_title,
|
||||||
|
¬e_is_done,
|
||||||
|
¬e_is_todo,
|
||||||
|
¬e_highlight,
|
||||||
|
)
|
||||||
|
if (body_type != "none") {
|
||||||
|
item["highlight"] = note_highlight
|
||||||
|
}
|
||||||
|
} else {
|
||||||
err = rows.Scan(
|
err = rows.Scan(
|
||||||
¬e_id,
|
¬e_id,
|
||||||
¬e_title,
|
¬e_title,
|
||||||
@ -101,6 +154,7 @@ func main() {
|
|||||||
¬e_is_done,
|
¬e_is_done,
|
||||||
¬e_is_todo,
|
¬e_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
|
||||||
|
@ -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 */
|
||||||
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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('<', '<').replaceAll('>', '>');
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user