diff --git a/readme.md b/readme.md
index 1e6c661..f7a0454 100644
--- a/readme.md
+++ b/readme.md
@@ -1,27 +1,34 @@
# Basant
+I collect a lot of information. Basant is a tool to help me refine, reflect and understand the vast troves of data I generate.
+Think of it like a unified front end to all the data sources I have. From these vairous outputs can be created, with the end goal eventually being a matrix bot to publish to The Feed, a front page showing latest changes, and of course, my website.
+I'd like to be able to draw 'links' or annotations between the tags I use across various different pieces of software,
+These are the tools I use construct the space.
+
The map is not the teritory.
-I have carved out a small sancturary. It is available through my website: anish.lakhwara.com
-These are the tools I use construct the space.
-
## Architecture
-2 input sources, kitaab & streams
-These sources are then built into HTML files
-Those HTML files are published to my website
+Basant is made from various different data sources, each with their own folder.
+Each data source will have a `grab`, `build`, raw & output. `grab` gets the raw data, and `build` makes it into HTML
+Depending on the data source, there may be additional partial/ asset/ media/ folders in the directory. I'm not yet sure how to cascade these and build upwards.
+Some of those HTML files get published
+
+For better ideas look into [HPI](https://github.com/karlicoss/hpi) and how it's structured
### Inputs
-Inputs should be seperated into a sub-module (for each?) so that they can be built heremetically through nixOS
-
-kitaab requires a local copy, stored in /home/anish/vimwiki/
-there needs to be a .vimwiki_tags file there (which can be built in vimwiki using :VimwikiRebuildTags)
-It'll grab the relevant files, censor them, and then add them to the raw/ directory under public-kitaab
+`kitaab-public` requires a local copy, stored in /home/anish/vimwiki/
+there needs to be a .vimwiki_tags file there (which can be built in vimwiki using `:VimwikiRebuildTags`; how to automate this?)
+It'll grab the relevant files, censor them, and then add them to raw/
+`kitaab` should run the same thing, but without the filtering and censoring
streams is a big TODO
- wallabag
- shaarli
- rss
+- kobo
+- tasks
+- habits
### Build
TODO
@@ -34,21 +41,3 @@ and it saves it to output
TODO
some nix concotion that grabs the HTML and publishes it to anish.lakhwara.com
-## Old overview (src/inputs/kitaab/)
-Helps make my Kitaab public by grabbing Kitaab, filtering & censoring it, and then passing it through to an SSG to make HTML out of a markdown page.
-makePublic.py
-- the whole filter pipeline
-
-TagHelper.py
-- Reads the .vimwiki_tags file that is generated by vimwiki
-
-tree.py
-- builds a tree out of all the links using dfs
-
-network-builder.py
-- uses the network from tree.py to visualize links
-
-tag-fixer.py
-- old script used to fix problem tags (1 character tags)
-- (TODO) tags that are case sensitive
-- (TODO) entries that have no tags
diff --git a/src/builder/kitaab-builder b/src/builder/kitaab-builder
deleted file mode 160000
index db06fd7..0000000
--- a/src/builder/kitaab-builder
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit db06fd7890f3348cd56112434b9368545ad2afa6
diff --git a/src/inputs/kitaab/makePublic.py b/src/inputs/kitaab/makePublic.py
deleted file mode 100644
index 8b0944d..0000000
--- a/src/inputs/kitaab/makePublic.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#! /usr/bin/env python3
-# Various methods of grabbing files from Kitaab based on their ctags as generated by vimwiki
-# censor - burn all mentions of this word wherever it appears in Kitaab
-# filter - leave names in tact but do not link to relevant documents
-# create - list in an index file for website
-from os import listdir, mkdir
-from os.path import isfile, join
-import shutil
-import random
-import TagHelper
-import censor as cen
-from icecream import ic
-
-## TODO: generalize based on headings(?) in index.wiki
-
-censor = cen.listBad
-filter = ["Dream", "Gratitude", "Review", "jrnl", "piracy", "work", "job", "debt", "people"]
-OUTPUTPATH = '../../../raw/public-kitaab/'
-INPUTPATH = '/home/anish/vimwiki/'
-#TODO create = ...
-
-def loadNetwork(path='network.json'):
- # Loads the network.json file
- with open(path, 'r') as f:
- everything = json.load(f)
- return everything
-
-def setNetwork(everything):
- # Everything is loaded from the network.json file
- keys = set(everything.network())
- values = set(everything.values())
- everything = network + values
- return everything
-
-def filterFiles(AllFileList):
- toFilter = filter + censor
- tagList = TagHelper.getAllTags()
- doNotTakeFiles = set()
- for mask in toFilter:
- try:
- doNotTakeFiles.update(tagList[mask])
- except KeyError:
- print("Key not found: {}".format(mask))
- return AllFileList - doNotTakeFiles
-
-def censorNetwork(network):
- # Takes network as a set
- for c in censor:
- if c in network:
- network.add(generateBlocks(len(c) + random.randint(0, 5)))
- network.remove(c)
- return network
-
-def generateBlocks(size=1):
- # Generates a length size unicode string that is formed from blocks:
- charList = ['▐', '░', '▒', '▓', '▔', '▕', '▖▗', '▘▙', '▚', '▛', '▜', '▝', '▞', '▟']
- ret = ""
- while size > 0:
- size = size - 1
- rand = random.randint(0,13)
- ret = ret + charList[rand]
- return ret
-
-def createIndexNode(network):
- pass
-
-def copyFiles(fileList):
- try:
- mkdir(OUTPUTPATH)
- except FileExistsError:
- pass
- for f in fileList:
- try:
- shutil.copy(INPUTPATH + f, OUTPUTPATH)
- except FileNotFoundError:
- print("Could not copy file {}:".format(INPUTPATH + f))
-
-def censorFiles():
- outputBasePath = './output/'
- for fileName in listdir(OUTPUTPATH):
- fileName = OUTPUTPATH + fileName
- newFile = ""
- print("Censoring: {}".format(fileName))
- with open(fileName, 'r') as f:
- for line in f.readlines():
- for c in censor:
- line = line.replace(c, generateBlocks(len(c)))
- newFile = newFile + line
- with open(fileName, 'w') as f:
- for line in newFile:
- f.write(line)
-
-if __name__=='__main__':
- #network = loadNetwork()
- #network = censorNetwork(network)
- AllFiles = TagHelper.getAllFiles()
- FilteredFiles = filterFiles(AllFiles)
- copyFiles(FilteredFiles)
- print("Censoring files...")
- censorFiles()
diff --git a/src/inputs/kitaab/network-builder.py b/src/inputs/kitaab/network-builder.py
deleted file mode 100644
index 4d2cde1..0000000
--- a/src/inputs/kitaab/network-builder.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import json
-import networkx as nx
-import pygraphviz
-
-def loadJson(file):
- with open(file, 'r') as f:
- return json.load(f)
-
-def makeNetwork(json):
- G = nx.DiGraph()
- for (key, value) in json.items():
- G.add_nodes_from([key] + value)
- G.add_edges_from([(key, v) for v in value])
- return G
-
-def makeAGraph(graph):
- G = nx.nx_agraph.to_agraph(graph)
- G.draw("nop.png", prog="nop")
-
-if __name__ == "__main__":
- OUTFILE = 'network.json'
- json = loadJson('network.json')
- graph = makeNetwork(json)
- with open(OUTFILE, 'w') as f:
- json.dump(f, makeAGraph(graph))
diff --git a/src/inputs/kitaab/tag-fixer.py b/src/inputs/kitaab/tag-fixer.py
deleted file mode 100644
index 5d7d1ad..0000000
--- a/src/inputs/kitaab/tag-fixer.py
+++ /dev/null
@@ -1,178 +0,0 @@
-#! /usr/bin/env python3
-
-from ctags import CTags, TagEntry
-import sys
-from icecream import ic
-import os
-import shutil
-import fileinput
-
-def loadTagFile(path='/home/anish/vimwiki/.vimwiki_tags'):
- # Available file information keys:
- # opened - was the tag file successfully opened?
- # error_number - errno value when 'opened' is false
- # format - format of tag file (1 = original, 2 = extended)
- # sort - how is the tag file sorted?
- # author - name of author of generating program (may be empy string)
- # name - name of program (may be empy string)
- # url - URL of distribution (may be empy string)
- # version - program version (may be empty string)
- try:
- tagFile = CTags(path)
- return tagFile
- except:
- print("No tagfile at {}".format(path))
- sys.exit(1)
-
-bad_tags = [('Public', 'public'), ('Travel', 'travel'), ('jrl', 'jrnl'), ('Kitaab', 'kitaab'), ('Dream', 'dream'), ('Story', 'story'), ('Review', 'review'), ('private', 'priv'), ('ideas', 'idea'), ('quotes', 'quote'), ('rocks', 'rock'), ('sec', 'cybsec'), ('sealight.services', 'sealight')]
-bad_tags_only = [x[0] for x in bad_tags]
-
-def replaceBadTags(tagFile):
- # no higher order function sadness
- # this does the same thing as loopThrough but runs a different function
- # (loop through looks for single character tags)
- # We look for any tag in the list and replace it
- entry = TagEntry()
- tag = tagFile.next(entry)
- tagErrors = set()
- print(bad_tags_only)
- while tag:
- tag = entry['name'].decode('utf-8')
- if tag in bad_tags_only:
- tagErrors.add(entry['file'])
- tag = tagFile.next(entry)
- return tagErrors
-
-def loopThrough(tagFile):
- # Available TagEntry keys:
- # name - name of tag
- # file - path of source file containing definition of tag
- # pattern - pattern for locating source line (None if no pattern)
- # lineNumber - line number in source file of tag definition (may be zero if not known)
- # kind - kind of tag (none if not known)
- # fileScope - is tag of file-limited scope?
-
- # Note: other keys will be assumed as an extension key and will
- # return None if no such key is found
- ''' Entries that are less than 1 character in length are errors
- Returns a set containing of all the files with errors in their tags'''
- entry = TagEntry()
- tag = tagFile.next(entry)
- tagErrors = set()
- errorTagNames = set()
- while tag:
- if len(entry['name']) <= 1:
- tagErrors.add(entry['file'])
- errorTagNames.add(entry['name'])
- tag = tagFile.next(entry)
- ic(errorTagNames)
- return tagErrors
-
-def fixBadTags(errors):
- ''' More lack of higher order function sadness
- same as fixErrors, but not really
- '''
- fileBase = '/home/anish/vimwiki/'
- for v in errors:
- print("Fixing {}...".format(v))
- fileName = fileBase + getFileName(v)
- newFile = ""
- with open(fileName, 'r') as f:
- newFile = newFile + f.readline()
- line = f.readline()
- # bad and ungly
- for bad_tag in bad_tags:
- line = line.replace(bad_tag[0], bad_tag[1])
- newFile = newFile + line
- for line in f.readlines():
- newFile = newFile + line
- with open(fileName, 'w') as f:
- for line in newFile:
- f.write(line)
- print("Written new file...")
- print()
- print(newFile)
-
-
-def fixErrors(errors):
- ''' Replaces :: with :, which seems to be the error for many of the tags '''
- fileBase = '/home/anish/vimwiki/'
- for v in errors:
- print("Fixing {}...".format(v))
- fileName = fileBase + getFileName(v)
- newFile = ""
- with open(fileName, 'r') as f:
- for line in f.readlines():
- line = line.replace(":]:]:", "")
- line = line.replace(":[:[:", "")
- line = line.replace("G:r:a:t:i:t:u:d:e:", ":grat:")
- line = line.replace(":J:o:u:r:n:a:l:", ":jrnl:")
- line = line.replace(":R:e:v:i:e:w:", ":review:")
- line = line.replace(":T:r:a:v:e:l:", ":travel:")
- line = line.replace(":D:r:e:a:m:", ":dream:")
- line = line.replace("%date", "\n%date")
- line = line.replace("{date}", createDateStringFromFile(v)+"\n\n")
- line = line.replace("{title}", "{title}\n")
- line = line.replace(" :", ":")
- line = line.replace("::", ":")
- line = line.replace(" :", ":")
- line = line.replace("::", ":")
- newFile = newFile + line
- with open(fileName, 'w') as f:
- for line in newFile:
- f.write(line)
- print("Written new file...")
- print()
- print(newFile)
-
-def createDateStringFromFile(error):
- file = getFileName(error).replace("jrnl/", "").replace(".wiki", "")
- dateConverted = file.split('-') # create date and time
- dateConvertedSplit = list(dateConverted[0]) # splite date
- dateConvertedSplit.insert(2, '-') # add - between year and month
- dateConvertedSplit.insert(5, '-') # add - between month and day
- dateConvertedSplit.insert(0, '20') # add 20 to year
- dateConverted[0] = "".join(dateConvertedSplit) # make date a string
- dateConverted[1] = dateConverted[1][:2] + ":" + dateConverted[1][2:] # add colon in between hour and minute
- dateConverted = " ".join(dateConverted) # make date and time a string
- return dateConverted
-
-def printErrorFiles(errors):
- fileBase = '/home/anish/vimwiki/'
- for v in errors:
- fileName = fileBase + getFileName(v)
- print("Reading {}...".format(v))
- with open(fileName, 'r') as f:
- #f.readline()
- ic(f.readline())
-
-def listTags(tagFile):
- entry = TagEntry()
- tag = tagFile.next(entry)
- allTags = set()
- while tag:
- tag = entry['name'].decode('utf-8')
- allTags.add(tag)
- tag = tagFile.next(entry)
- return allTags
-
-def getFileName(f):
- return str(f, encoding="utf-8")
-
-def getBackupFiles(errors):
- fileBase = '/home/anish/vimwiki/'
- backupBase = '/home/anish/vimwiki/jrnl/'
- for files in errors.values():
- for f in files:
- f = getFileName(f)
- print("Moving {} from backup".format(f))
- shutil.copy(backupBase + f, fileBase + f)
-
-if __name__=='__main__':
- tagFile = loadTagFile()
- #ic(tagFile)
- #errors = replaceBadTags(tagFile)
- #fixBadTags(errors)
- print(sorted(listTags(tagFile)))
- #ic(errors)
- #getBackupFiles(errors)
diff --git a/src/inputs/kitaab/tree.py b/src/inputs/kitaab/tree.py
deleted file mode 100644
index 605ab45..0000000
--- a/src/inputs/kitaab/tree.py
+++ /dev/null
@@ -1,120 +0,0 @@
-#! /usr/bin/env python
-# -*- coding: utf-8 -*-
-from icecream import ic
-import json
-
-"""network-vimwiki.py
-Create site map for vimwiki.
-Must run script under vimwiki directory.
-
-Example:
- output network map to network.json
- dfsvw.py
-
-Python version: 3"""
-import sys
-import re
-
-linkRE = re.compile(r'\[\[([^\\\]|#]+)(?:#[^\\\]|]+)?(?:\|([^\\\]]+))?\]\]', re.UNICODE)
-
-def probeLink(s):
- """
- >>> probeLink(' abc')
- []
- >>> probeLink(' [[]]')
- []
- >>> probeLink(' [[abc]] [[abc|123]] ')
- [('abc', ''), ('abc', '123')]
- >>> a, b = probeLink(' [[中文1|中文2]]')[0]; print a, b
- 中文1 中文2
- >>> probeLink(' [[abc|abc 123]]')
- [('abc', 'abc 123')]
- """
- ret = linkRE.findall(s)
- return [(a, b) for a, b in ret]
-
-
-def probeAllLink(fn):
- """extract all link in a wiki file"""
- try:
- lst = []
- print(fn)
- fp = open(fn)
- for line in fp.readlines():
- l = probeLink(line)
- lst.extend(l)
- except IOError:
- print("cannot open",sys.stderr)
- except UnicodeDecodeError:
- lst = []
- fp = open(fn, encoding="utf-16BE")
- for line in fp.readlines():
- l = probeLink(line)
- lst.extend(l)
- return lst
-
-
-def outputNode(link, name, level):
- """output one wiki node, level for vimwiki tree hierarchy"""
- """ Unused """
- prefix = " " * 4 * level
- if not name:
- name = link
- dct = {"prefix":prefix, "link":link, "name":name}
- if sys.stdout.isatty():
- fmt = "{prefix}{name}"
- else:
- if link == name:
- fmt = "{prefix}[[{name}]]"
- else:
- fmt = "{prefix}[[{link}|{name}]]"
- print(fmt.format(**dct))
-
-def addNode(link, node, name, graph):
- """ adds a node to the dictionary that tracks edges"""
- if name:
- node = name
- if link in graph.keys():
- graph[link].add(node)
- else:
- graph[link] = {node}
-
-def bfs(index):
- """breath first search on vimwiki
- keep seen and unseen to track which files to search
- ugly loops instead of recursive setup """
- seen = set()
- unseen = [index]
- graph = {}
- filecount = 0
- while 1:
- try:
- link = unseen.pop(0)
- except IndexError:
- print("Search complete...")
- ic(filecount)
- return graph
- seen.add(link)
- ret = probeAllLink(link + '.wiki')
- for (node, name) in ret:
- addNode(link, node, name, graph)
- if not node in seen:
- unseen.append(node)
- filecount = filecount + 1
- ic(filecount)
- ic(seen)
-
-def transformGraph(graph):
- for key, value in graph.items():
- graph[key] = list(value)
- return graph
-
-if __name__ == "__main__":
- graph = bfs('index')
- print("Transforming Graph...")
- graph = transformGraph(graph)
- print("Transform Complete...")
- ic(graph)
- print("Outputting file")
- with open('network.json', 'w+') as out:
- json.dump(graph, out)
diff --git a/src/kitaab-public/asset/android-icon-144x144.png b/src/kitaab-public/asset/android-icon-144x144.png
new file mode 100644
index 0000000..d6f1a82
Binary files /dev/null and b/src/kitaab-public/asset/android-icon-144x144.png differ
diff --git a/src/kitaab-public/asset/android-icon-192x192.png b/src/kitaab-public/asset/android-icon-192x192.png
new file mode 100644
index 0000000..10230ea
Binary files /dev/null and b/src/kitaab-public/asset/android-icon-192x192.png differ
diff --git a/src/kitaab-public/asset/android-icon-36x36.png b/src/kitaab-public/asset/android-icon-36x36.png
new file mode 100644
index 0000000..a969a10
Binary files /dev/null and b/src/kitaab-public/asset/android-icon-36x36.png differ
diff --git a/src/kitaab-public/asset/android-icon-48x48.png b/src/kitaab-public/asset/android-icon-48x48.png
new file mode 100644
index 0000000..02d652d
Binary files /dev/null and b/src/kitaab-public/asset/android-icon-48x48.png differ
diff --git a/src/kitaab-public/asset/android-icon-72x72.png b/src/kitaab-public/asset/android-icon-72x72.png
new file mode 100644
index 0000000..9fad473
Binary files /dev/null and b/src/kitaab-public/asset/android-icon-72x72.png differ
diff --git a/src/kitaab-public/asset/android-icon-96x96.png b/src/kitaab-public/asset/android-icon-96x96.png
new file mode 100644
index 0000000..4523876
Binary files /dev/null and b/src/kitaab-public/asset/android-icon-96x96.png differ
diff --git a/src/kitaab-public/asset/apple-icon-114x114.png b/src/kitaab-public/asset/apple-icon-114x114.png
new file mode 100644
index 0000000..89d54f3
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-114x114.png differ
diff --git a/src/kitaab-public/asset/apple-icon-120x120.png b/src/kitaab-public/asset/apple-icon-120x120.png
new file mode 100644
index 0000000..c8c5460
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-120x120.png differ
diff --git a/src/kitaab-public/asset/apple-icon-144x144.png b/src/kitaab-public/asset/apple-icon-144x144.png
new file mode 100644
index 0000000..d6f1a82
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-144x144.png differ
diff --git a/src/kitaab-public/asset/apple-icon-152x152.png b/src/kitaab-public/asset/apple-icon-152x152.png
new file mode 100644
index 0000000..7a41592
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-152x152.png differ
diff --git a/src/kitaab-public/asset/apple-icon-180x180.png b/src/kitaab-public/asset/apple-icon-180x180.png
new file mode 100644
index 0000000..f36e8e0
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-180x180.png differ
diff --git a/src/kitaab-public/asset/apple-icon-57x57.png b/src/kitaab-public/asset/apple-icon-57x57.png
new file mode 100644
index 0000000..73de85b
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-57x57.png differ
diff --git a/src/kitaab-public/asset/apple-icon-60x60.png b/src/kitaab-public/asset/apple-icon-60x60.png
new file mode 100644
index 0000000..c7330af
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-60x60.png differ
diff --git a/src/kitaab-public/asset/apple-icon-72x72.png b/src/kitaab-public/asset/apple-icon-72x72.png
new file mode 100644
index 0000000..9fad473
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-72x72.png differ
diff --git a/src/kitaab-public/asset/apple-icon-76x76.png b/src/kitaab-public/asset/apple-icon-76x76.png
new file mode 100644
index 0000000..cb7d29c
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-76x76.png differ
diff --git a/src/kitaab-public/asset/apple-icon-precomposed.png b/src/kitaab-public/asset/apple-icon-precomposed.png
new file mode 100644
index 0000000..e08c6c5
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon-precomposed.png differ
diff --git a/src/kitaab-public/asset/apple-icon.png b/src/kitaab-public/asset/apple-icon.png
new file mode 100644
index 0000000..e08c6c5
Binary files /dev/null and b/src/kitaab-public/asset/apple-icon.png differ
diff --git a/src/kitaab-public/asset/favicon-16x16.png b/src/kitaab-public/asset/favicon-16x16.png
new file mode 100644
index 0000000..8b65f33
Binary files /dev/null and b/src/kitaab-public/asset/favicon-16x16.png differ
diff --git a/src/kitaab-public/asset/favicon-32x32.png b/src/kitaab-public/asset/favicon-32x32.png
new file mode 100644
index 0000000..0a38bad
Binary files /dev/null and b/src/kitaab-public/asset/favicon-32x32.png differ
diff --git a/src/kitaab-public/asset/favicon-96x96.png b/src/kitaab-public/asset/favicon-96x96.png
new file mode 100644
index 0000000..4523876
Binary files /dev/null and b/src/kitaab-public/asset/favicon-96x96.png differ
diff --git a/src/kitaab-public/asset/favicon.ico b/src/kitaab-public/asset/favicon.ico
new file mode 100644
index 0000000..2846df3
Binary files /dev/null and b/src/kitaab-public/asset/favicon.ico differ
diff --git a/src/kitaab-public/asset/main.css b/src/kitaab-public/asset/main.css
new file mode 100644
index 0000000..21d7112
--- /dev/null
+++ b/src/kitaab-public/asset/main.css
@@ -0,0 +1,294 @@
+/* RESET CSS BY ANDY BELL */
+
+/* Box sizing rules */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+/* Remove default margin */
+body,
+h1,
+h2,
+h3,
+h4,
+p,
+figure,
+blockquote,
+dl,
+dd {
+ margin: 0;
+}
+
+/* Remove list styles on ul, ol elements with a list role, which suggests default styling will be removed */
+ul[role='list'],
+ol[role='list'] {
+ list-style: none;
+}
+
+/* Set core root defaults */
+html:focus-within {
+ scroll-behavior: smooth;
+}
+
+/* Set core body defaults */
+body {
+ min-height: 100vh;
+ text-rendering: optimizeSpeed;
+ line-height: 1.5;
+}
+
+/* A elements that don't have a class get default styles */
+a:not([class]) {
+ text-decoration-skip-ink: auto;
+}
+
+/* Make images easier to work with */
+img,
+picture {
+ max-width: 100%;
+ display: block;
+}
+
+/* Inherit fonts for inputs and buttons */
+input,
+button,
+textarea,
+select {
+ font: inherit;
+}
+
+
+/* Remove all animations, transitions and smooth scroll for people that prefer not to see them */
+@media (prefers-reduced-motion: reduce) {
+ html:focus-within {
+ scroll-behavior: auto;
+ }
+
+ *,
+ *::before,
+ *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ scroll-behavior: auto !important;
+ }
+}
+
+
+/* CHISAI CSS BY THOMASORUS */
+
+:root {
+ --text: black;
+ --background: white;
+ --dots: radial-gradient(currentColor 1px, transparent 1px), radial-gradient(currentColor 1px, transparent 1px);
+ --ratio: 1.5;
+ --s-5: calc(var(--s-4) / var(--ratio));
+ --s-4: calc(var(--s-3) / var(--ratio));
+ --s-3: calc(var(--s-2) / var(--ratio));
+ --s-2: calc(var(--s-1) / var(--ratio));
+ --s-1: calc(var(--s0) / var(--ratio));
+ --s0: 1rem;
+ --s1: calc(var(--s0) * var(--ratio));
+ --s2: calc(var(--s1) * var(--ratio));
+ --s3: calc(var(--s2) * var(--ratio));
+ --s4: calc(var(--s3) * var(--ratio));
+ --s5: calc(var(--s4) * var(--ratio));
+ font-size: calc(1rem + 0.5vw);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --text: white;
+ --background: black;
+ --dots: radial-gradient(white 1px, transparent 1px), radial-gradient(white 1px, transparent 1px);
+ }
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ background-color: var(--background);
+ color: var(--text);
+}
+
+header {
+ width: 100%;
+ text-align: center;
+ font-size: var(--s1);
+ padding-top: var(--s3);
+}
+
+header nav {
+ display: flex;
+ justify-content: space-around;
+
+}
+
+header a {
+ color: var(--text);
+ text-transform: uppercase;
+ font-weight: 700;
+}
+
+a {
+ color: var(--text);
+ text-decoration: none;
+}
+
+nav em {
+ font-size: var(--s0);
+}
+
+h1 {
+ font-size: var(--s2);
+ color: var(--text);
+ line-height: 1;
+}
+
+h2 {
+ font-size: var(--s1);
+ color: var(--text);
+}
+
+h3 {
+ font-size: var(--s0);
+ color: var(--text);
+}
+
+main {
+ padding: var(--s0);
+ padding: var(--s-1);
+ background-image: var(--dots);
+ background-size: calc(4 * 1px) calc(4 * 1px);
+ background-position: 0 0, calc(2 * 1px) calc(2 * 1px);
+}
+
+article {
+ background-color: var(--background);
+ padding: var(--s0);
+}
+
+main a {
+ color: var(--text);
+ border-bottom: 2px solid var(--text);
+}
+
+main date {
+ display: block;
+}
+
+li {
+ line-height: 1.3;
+}
+
+ul>*+* {
+ margin-top: var(--s0);
+}
+
+.container {
+ --space: var(--s1);
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-start;
+ max-width: 60ch;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 0 var(--s0);
+}
+
+.flow>*+* {
+ margin-top: var(--s0);
+}
+
+.flow-small>*+* {
+ margin-top: var(--s-3);
+}
+
+
+.flow-large>*+* {
+ margin-top: var(--s3);
+}
+
+.logo {
+ border-radius: 100%;
+ margin: 0 auto;
+ max-width: 180px;
+ padding: 15px;
+}
+
+.youtube {
+ position: relative;
+ padding-bottom: 56.25%;
+ height: 0;
+ overflow: hidden;
+}
+
+.youtube>iframe {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ left: 0;
+ right: 0;
+}
+
+.listing {
+ padding: 0;
+ list-style: none;
+ line-height: 1;
+}
+
+.listing h3 {
+ font-weight: 300;
+}
+
+.listing small {
+ display: block;
+ font-size: var(--s-1);
+}
+
+.listing>*+* {
+ margin-top: var(--s-2);
+}
+
+
+@media (max-width: 520px) {
+
+ :root {
+ --s1: var(--s0);
+ }
+
+ .logo {
+ max-width: 120px;
+ padding: 5px;
+ }
+}
+
+/*
+@media (prefers-color-scheme: dark) {
+ :root {
+ --purple-light: rgb(172, 16, 239);
+ --purple-light-transparent: rgba(172, 16, 239, 0.7);
+ --purple-medium: #5927e0;
+ --purple-strong: #3f1ca0;
+ --background: #111;
+ --text: hsl(221, 19%, 66%);
+ --title: white;
+ --article-shadow: black;
+ --link: var(--purple-medium);
+ }
+}
+
+@media (prefers-color-scheme: light) {
+ :root {
+ --purple-light: rgb(172, 16, 239);
+ --purple-light-transparent: rgba(172, 16, 239, 0.7);
+ --purple-medium: #5927e0;
+ --purple-strong: #3f1ca0;
+ --background: white;
+ --text: #6b7280;
+ --title: black;
+ --article-shadow: #333;
+ --link: var(--purple-light-transparent);
+ }
+} */
\ No newline at end of file
diff --git a/src/kitaab-public/asset/ms-icon-144x144.png b/src/kitaab-public/asset/ms-icon-144x144.png
new file mode 100644
index 0000000..d6f1a82
Binary files /dev/null and b/src/kitaab-public/asset/ms-icon-144x144.png differ
diff --git a/src/kitaab-public/asset/ms-icon-150x150.png b/src/kitaab-public/asset/ms-icon-150x150.png
new file mode 100644
index 0000000..1d14f1a
Binary files /dev/null and b/src/kitaab-public/asset/ms-icon-150x150.png differ
diff --git a/src/kitaab-public/asset/ms-icon-310x310.png b/src/kitaab-public/asset/ms-icon-310x310.png
new file mode 100644
index 0000000..7ebf4b5
Binary files /dev/null and b/src/kitaab-public/asset/ms-icon-310x310.png differ
diff --git a/src/kitaab-public/asset/ms-icon-70x70.png b/src/kitaab-public/asset/ms-icon-70x70.png
new file mode 100644
index 0000000..35d1650
Binary files /dev/null and b/src/kitaab-public/asset/ms-icon-70x70.png differ
diff --git a/src/kitaab-public/asset/rss.png b/src/kitaab-public/asset/rss.png
new file mode 100644
index 0000000..ce1d6ff
Binary files /dev/null and b/src/kitaab-public/asset/rss.png differ
diff --git a/src/kitaab-public/asset/social.png b/src/kitaab-public/asset/social.png
new file mode 100644
index 0000000..ce1d6ff
Binary files /dev/null and b/src/kitaab-public/asset/social.png differ
diff --git a/src/kitaab-public/build/README.md b/src/kitaab-public/build/README.md
new file mode 100644
index 0000000..6c125b6
--- /dev/null
+++ b/src/kitaab-public/build/README.md
@@ -0,0 +1,39 @@
+![Chisai logo](medias/chisai_square.png)
+
+# Chīsai!
+
+> A small website generator, editable and hosted on github, with automatic deployment!
+
+## Purpose and mission
+
+Chīsai is aimed at empowering people who need a free solution to have their own small space on the internet without falling into the CMS install and maintenance crazyness. All content is in markdown files, in folders, and the user can edit, upload images and host on github.
+
+Chīsai answers three very important things for me when it comes to websites:
+
+1. It should be easy to modify by someone else
+2. It should be durable and long lasting
+3. Each person should own the code of its website
+
+## Who can use it?
+
+Chīsai is a free to use by individuals and organizations that do not operate by capitalist principles. For more information see the [license](LICENSE) file.
+
+## How does it work?
+
+You can see a demo [here](https://thomasorus.github.io/Chisai/). It's quite simple:
+
+- Each section is a folder with files named as the dates of their publication.
+- Each section has it's parent section that regroups all the files inside.
+- All sections 5 latest entries are regrouped on the home page with a link to see the full content.
+- Content is edited using the Markdown syntax (the same you can find on Discord or Slack).
+- Edit the content (create folders, files, edit files) directly on github, here!
+
+That's all!
+
+## How to use it?
+
+See the [wiki](https://github.com/Thomasorus/Chisai/wiki) for extensive guides for everything!
+
+## Can I self-host it?
+
+Yes of course! Chisai only produces HTML. You can add the generated website on your own server. Even better: if your server has Python, you can rebuild the website direcly on your server. Be careful that you might have to reconfigure some things to make it work.
diff --git a/src/inputs/kitaab/TagHelper.py b/src/kitaab-public/build/TagHelper.py
similarity index 95%
rename from src/inputs/kitaab/TagHelper.py
rename to src/kitaab-public/build/TagHelper.py
index d45df46..d278f17 100644
--- a/src/inputs/kitaab/TagHelper.py
+++ b/src/kitaab-public/build/TagHelper.py
@@ -59,13 +59,10 @@ def makeDictFromTags(tagFile, key='name', value='file'):
def getAllFiles():
# Returns a set containing all the files we wish to make public
- return makeSetFromTags(loadTagFile())
+ return makeSetFromTags(loadTagFile(tagFile))
def getAllTags():
# Returns a dictionary with:
# - key as tag titles
# - values as files that have the key in their tags
return makeDictFromTags(loadTagFile())
-
-if __name__ == '__main__':
- print(getAllTags().keys())
diff --git a/src/kitaab-public/build/build.py b/src/kitaab-public/build/build.py
new file mode 100644
index 0000000..7576ab4
--- /dev/null
+++ b/src/kitaab-public/build/build.py
@@ -0,0 +1,476 @@
+from jinja2 import Environment, FileSystemLoader
+import TagHelper
+import argparse
+import glob
+import os
+import platform
+import re
+import shutil
+import subprocess
+import sys
+from datetime import datetime
+import pathlib
+
+# Default encoding
+encoding = 'utf-8'
+
+# Import local files
+mistune = __import__('mistune')
+config = __import__('config')
+
+
+# Activating the renderer
+renderer = mistune.Renderer()
+markdown = mistune.Markdown(renderer=renderer)
+
+
+# Checking for dev command
+parser = argparse.ArgumentParser()
+parser.add_argument(
+ "-p", "--prod", help="Prints the supplied argument.", action="store_true")
+args = parser.parse_args()
+
+# Declaring build url that will be used in several parts of the app
+build_url = config.relative_build_url
+if args.prod:
+ build_url = config.absolute_build_url
+
+def generate_navigation(template_nav):
+ file_loader = FileSystemLoader('')
+ env = Environment(loader=file_loader)
+ template = env.get_template(template_nav)
+
+ index_tags = TagHelper.getAllTags()['dev']
+ output = template.render(index_urls=[f.split(".")[0] for f in index_tags])
+ return output
+
+# Generates html files in the site folder, using the entries and the template.
+def generate_html_pages(site_folder, entries, template, sub_pages_list, template_nav):
+ for entry in entries:
+ page_template = template.replace('page_title', entry['title'])
+ page_template = template.replace('page_tags', str(entry['tags']))
+
+ # Checking if the page is root of a folder
+ if entry["file"] == "index":
+
+ # If root page
+ # Concatenate the content of the index page with the listing of sub-pages
+ # Remove "page_date" from template
+ # Replaces "page_body" with the page content in the template
+ entry["pageContent"] += sub_pages_list
+ page_template = page_template.replace('page_date', "")
+ page_template = page_template.replace(
+ 'page_body', entry['pageContent'])
+ else:
+ # If content page
+ # Replaces "page_body" with the page content in the template
+ # Replaces "page_date" with the page date in the template
+ page_template = page_template.replace(
+ 'page_body', entry['pageContent'])
+ page_template = page_template.replace(
+ 'page_date', "%s" % (entry['date']))
+
+ # Creating navigation
+ url_link = build_url
+ url_text = entry['parent_text']
+
+ nav_html = generate_navigation(template_nav)
+ #if not entry["parent_url"]:
+ # # If index page, return to the home
+ # url_link = build_url
+ #else:
+ # # If content page, return to parent page
+ # url_link = build_url + entry['parent_url']
+
+ #nav_template = open(template_nav, 'r').read()
+ #nav_html = nav_template.replace("link_url", url_link)
+ #nav_html = nav_html.replace("text_url", url_text)
+ #nav_html = nav_html.replace("go_back", config.go_back)
+ page_template = page_template.replace('page_navigation', nav_html)
+
+ # Replaces all occurrences of build_url in the template files (assets, urls, etc)
+ page_template = page_template.replace('build_url', build_url)
+
+ page_template = page_template.replace(
+ 'name_of_site', config.name_of_site)
+ page_template = page_template.replace(
+ 'site_meta_description', config.site_meta_description)
+ page_template = page_template.replace(
+ 'twitter_name', config.twitter_name)
+
+ # Checking if content folder exists
+ folderExists = os.path.exists(site_folder+entry['folder'])
+ # If not, create it
+ if config.flat_build == False:
+ if not folderExists:
+ os.mkdir(site_folder+entry['folder'])
+
+ # Write the HTML file
+ slug_file = site_folder + entry['slug']
+ with open(slug_file, 'w', encoding=encoding) as fobj:
+ fobj.write(page_template)
+
+ print("All pages created!")
+
+
+# Get title by parsing and cleaning the first line of the markdown file
+def get_entry_title(page):
+ pageContent = open(page, 'r')
+ textContent = pageContent.read()
+ textContent = textContent.splitlines()
+ textContent = textContent[0]
+ textContent = textContent.replace('# ', '')
+ if '%title' in textContent:
+ textContent = textContent.replace('%title','')
+ else:
+ if 'index' not in page:
+ textContent = page.split('/')[1].rstrip('.wiki')
+
+ return textContent
+
+
+# Get the slug from the markdown file name
+def get_entry_slug(page):
+ slug = page.split("/")[-1]
+ slug = re.sub('\.wiki$', '', slug)
+ if slug:
+ return slug
+ else:
+ return ''
+
+
+def style_iframes(page):
+ regex = r"<\/iframe>"
+ matches = re.finditer(regex, page, re.MULTILINE)
+
+ for matchNum, match in enumerate(matches, start=1):
+ match = match.group()
+ iframe = "" % match
+ page = page.replace(match, iframe)
+ return page
+
+# Get a list of the tags in the page
+def get_tags(page):
+ regex = r":.*:"
+ matches = re.finditer(regex, page, re.MULTILINE)
+ ret = []
+
+ for match in matches:
+ match = match.group()
+ match = match.rstrip(":")
+ match = match.lstrip(":")
+ match = match.split(":")
+ ret = ret + match
+ ret = " ".join(ret)
+ return ret
+
+
+# Checks for local images links in markdown and add the build_url and medias_folder url
+def fix_images_urls(page):
+ regex = r"\!\[.*\]\((.*)\)"
+ matches = re.finditer(regex, page, re.MULTILINE)
+
+ for matchNum, match in enumerate(matches, start=1):
+ for groupNum in range(0, len(match.groups())):
+ captured_group = match.group(groupNum + 1)
+ if captured_group[:4] != "http":
+ full_url = build_url + config.medias_folder + captured_group
+ page = page.replace(captured_group, full_url)
+ return page
+
+
+def fix_amp(page):
+ page = page.replace(" & ", " & ")
+ return page
+
+def fix_wiki_links(page, path):
+ regex = r"\[\[([\s\S]+?\|?[\s\S]+?)\]\]"
+ matches = re.finditer(regex, page, re.MULTILINE)
+ for matchNum, match in enumerate(matches, start=1):
+ for groupNum in range(0, len(match.groups())):
+ captured_group = match.group(groupNum + 1)
+ link_elem = captured_group.split("|")
+ if len(link_elem) > 1:
+ full_url = "" + link_elem[0].strip() + ""
+ else:
+ if config.flat_build:
+ full_url = "" + link_elem[0].strip() + ""
+ else:
+ full_url = "" + link_elem[0].strip() + ""
+ page = page.replace(match[0], full_url)
+ return page
+
+# From the list of files, creates the main array of entries that will be processed later
+
+
+def create_entries(pages):
+ fullContent = []
+ for page in pages:
+ tempPage = {}
+
+ # Process the page with dedicated functions
+ path = clean_path(page)
+ title = get_entry_title(page)
+
+ markdown_text = open(page, 'r').read()
+ markdown_text = style_iframes(markdown_text)
+ markdown_text = fix_images_urls(markdown_text)
+ markdown_text = fix_wiki_links(markdown_text, path["folder"])
+ tags = get_tags(markdown_text)
+ pageContent = markdown(markdown_text)
+
+ # Create the page object with all the informations we need
+ tempPage['slug'] = path["slug"]
+ tempPage['file'] = path['file']
+ tempPage['folder'] = path["folder"]
+ tempPage['parent_url'] = path['parent_url']
+ tempPage['parent_text'] = path['parent_text']
+ tempPage['date'] = path['date']
+ tempPage['iso_date'] = path['iso_date']
+ tempPage['title'] = fix_amp(title)
+ tempPage['pageContent'] = pageContent
+ tempPage['tags'] = tags
+
+ fullContent.append(tempPage)
+
+ return fullContent
+
+
+# Copy assets to production folder
+def move_files(site_folder, path):
+ assets = os.listdir(path)
+ if assets:
+ for asset in assets:
+ asset = os.path.join(path, asset)
+ if os.path.isfile(asset):
+ shutil.copy(asset, site_folder+path)
+ else:
+ print("No assets found!")
+
+
+# Transforms the file locations to an array of strings
+def clean_path(path):
+ path_clean = re.sub('\.wiki$', '', path)
+ items = []
+ if(platform.system() == 'Windows'):
+ items = path_clean.split('\\')
+ else:
+ items = path_clean.split('/')
+
+ path_items = {
+ "slug" : None,
+ "date" : None,
+ "folder": items[0],
+ "file": items[1]
+ }
+
+ # xx-xx-xxxx = xx-xx-xxxx.html
+ # xx-xx-xxxx-string or string = string.html or /folder/string.html
+
+ # Checking if the file has a date
+ regex = r"[0-9]{6}-[0-9]{4}"
+ match = re.match(regex, path_items["file"])
+ has_date = False
+
+ if match:
+ has_date = True
+
+ if has_date:
+ if match[0] != path_items["file"]:
+ path_items["date"] = match[0]
+ path_items["slug"] = path_items["file"].replace(match[0] + "-", "") + ".html"
+ else:
+ path_items["date"] = match[0]
+ path_items["slug"] = path_items["file"] + ".html"
+
+ # Converts the EU date to US date to allow page sorting
+ if config.date_format == "EU":
+ path_items["iso_date"] = str(datetime.strptime(path_items["date"], '%y%m%d-%H%M'))
+ if config.date_format == "ISO":
+ path_items["iso_date"] = str(datetime.strptime(path_items["date"], '%Y-%m-%d'))
+ else:
+ path_items["slug"] = path_items["file"] + ".html"
+ #last_edit = str(subprocess.check_output('git log -1 --format="%ci" ' + path, shell=True)).replace("b'", "").replace("\\n'", '')
+ # get last edited time from system
+ try:
+ last_edit = os.path.getmtime('/home/anish/'+path)
+ last_edit_iso = datetime.fromtimestamp(last_edit)
+ except ValueError:
+ last_edit_iso = datetime.now()
+ except FileNotFoundError:
+ last_edit_iso = datetime.now()
+
+ if config.date_format == "EU":
+ path_items["date"] = str(last_edit_iso.strftime("%d %b, %y"))
+ else:
+ path_items["date"] = str(last_edit_iso)
+
+ path_items["iso_date"] = str(last_edit_iso)
+
+ if config.flat_build == False:
+ path_items["slug"] = path_items["folder"] + "/" + path_items["slug"]
+
+ if path_items["file"] == "index":
+ path_items["parent_url"] = ""
+ path_items["parent_text"] = config.home_name
+ if config.flat_build:
+ path_items["slug"] = path_items["folder"] + ".html"
+ else:
+ if config.flat_build:
+ path_items["parent_url"] = path_items["folder"] + ".html"
+ path_items["parent_text"] = path_items["folder"].replace("-", " ").capitalize()
+ else:
+ path_items["parent_url"] = path_items["folder"]
+ path_items["parent_text"] = path_items["folder"].replace("-", " ").capitalize()
+
+
+ return path_items
+
+
+# Generate the list of sub pages for each section
+def generate_sub_pages(entries, num, folder, title):
+
+ # Sort entries by date using the iso_date format
+ entries.sort(key=lambda x: x["iso_date"], reverse=True)
+
+ # Take n number of entries (5 for the home, all for the sub-section pages)
+ selected_entries = entries[:num]
+
+ # Create the list
+ sub_page_list = "
"
+ for entry in selected_entries:
+ if title:
+ link_url = entry["slug"]
+ else:
+ link_url = entry["file"] + ".html"
+
+ if entry["file"] != "index":
+ entry_string = "
with the given text.
+ :param text: text content of the blockquote.
+ """
+ return '
%s\n
\n' % text.rstrip('\n')
+
+ def block_html(self, html):
+ """Rendering block level pure html content.
+ :param html: text content of the html snippet.
+ """
+ if self.options.get('skip_style') and \
+ html.lower().startswith('