ctucx.git: nimstagit

[nimlang] incomplete reimplementation of stagit

commit 05f736febb0e9775b82f2cfaeae8a2dbf7b9f0fa
parent ddd878af68fb33b21fd3a9fd26f366a2012f3ee5
Author: Leah (ctucx) <leah@ctu.cx>
Date: Thu, 25 Mar 2021 15:58:25 +0100

use nim-SCF for templates
19 files changed, 503 insertions(+), 531 deletions(-)
M
src/assets.nim
|
10
++--------
D
src/assets/overview.html
|
50
--------------------------------------------------
D
src/assets/repoBlob.html
|
42
------------------------------------------
D
src/assets/repoCommit.html
|
58
----------------------------------------------------------
D
src/assets/repoLog.html
|
56
--------------------------------------------------------
D
src/assets/repoRefs.html
|
29
-----------------------------
D
src/assets/repoSummary.html
|
77
-----------------------------------------------------------------------------
D
src/assets/repoTree.html
|
60
------------------------------------------------------------
M
src/index.nim
|
19
+++----------------
M
src/nimstagit.nim
|
21
++++++++++++---------
M
src/repoGenerator.nim
|
233
+++++++++++++++++++++++++++++++++++++------------------------------------------
A
src/templates/index.nimf
|
52
++++++++++++++++++++++++++++++++++++++++++++++++++++
A
src/templates/repoBlob.nimf
|
46
++++++++++++++++++++++++++++++++++++++++++++++
A
src/templates/repoCommit.nimf
|
63
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
src/templates/repoLog.nimf
|
41
+++++++++++++++++++++++++++++++++++++++++
A
src/templates/repoRefs.nimf
|
31
+++++++++++++++++++++++++++++++
A
src/templates/repoSummary.nimf
|
59
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
A
src/templates/repoTree.nimf
|
65
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M
src/types.nim
|
22
++++++++++++++++++++--
diff --git a/src/assets.nim b/src/assets.nim
@@ -1,9 +1,2 @@
 const
-  assetStyleCss*       = staticRead "assets/style.css" 
-  templateOverview*    = staticRead "assets/overview.html"
-  templateRepoSummary* = staticRead "assets/repoSummary.html"
-  templateRepoLog*     = staticRead "assets/repoLog.html"
-  templateRepoCommit*  = staticRead "assets/repoCommit.html"
-  templateRepoTree*    = staticRead "assets/repoTree.html"
-  templateRepoBlob*    = staticRead "assets/repoBlob.html"
-  templateRepoRefs*    = staticRead "assets/repoRefs.html"
+  assetStyleCss*       = staticRead "assets/style.css"+
\ No newline at end of file
diff --git a/src/assets/overview.html b/src/assets/overview.html
@@ -1,50 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		<title>{{repoName}} - {{siteTitle}}</title>
-
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, initial-scale=1.0">
-		<meta name="description" content="{{description}}">
-        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
-
-		<link href="/style.css" rel="stylesheet" />
-	</head>
-	<body>
-		<header>
-			<h1>{{siteTitle}}</h1>
-			<p>{{description}}</p>
-		</header>
-		<nav>
-            <a class="active" href="index.html">index</a>
-		</nav>
-		<main>
-			<h3>Overview</h3>
-            <table>
-                <thead>
-                    <tr>
-                        <td>Name</td>
-                        <td>Description</td>
-                        <td>Idle</td>
-                    </tr>
-                </thead>
-                {{#categories}}
-                {{#name}}
-				<tr>
-					<td colspan="4" class="reposection">{{name}}</td>
-				</tr>
-				{{/name}}
-				{{#repos}}
-                <tr>
-                    <td class="sublevel-repo"><a href="{{repoUrl}}/">{{repoName}}</a></td>
-                    <td><a href="{{repoUrl}}/">{{description}}</a></td>
-                    <td><span class="age-hours"><a href="/{{repoUrl}}/log/{{objId}}.html">{{lastActivity}}</a></span></td>
-                </tr>
-                {{/repos}}
-				{{/categories}}
-            </table>
-		</main>
-		<footer>Generated on {{generated}}</footer>
-	</body>
-</html>
-
diff --git a/src/assets/repoBlob.html b/src/assets/repoBlob.html
@@ -1,42 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		<title>{{repoName}} - {{siteTitle}}</title>
-
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, initial-scale=1.0">
-		<meta name="description" content="{{description}}">
-        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
-
-		<link href="/style.css" rel="stylesheet" />
-	</head>
-	<body>
-		<header>
-			<h1><a href="/">{{siteTitle}}</a>: {{repoName}}</h1>
-			<p>{{description}}</p>
-		</header>
-		<nav>
-            <a href="/{{repoUrl}}">summary</a>
-			<a href="/{{repoUrl}}/log">log</a>
-			<a href="/{{repoUrl}}/tree" class="active">tree</a>
-			<a href="/{{repoUrl}}/refs">refs</a>
-		</nav>
-		<main>
-			{{#path}}<a href="{{url}}">{{name}}</a> / {{/path}} {{filename}} (<a href="{{filenameUrl}}">plain</a>)<br>
-            blob: {{id}} {{filesize}}
-            {{#isBinary}}
-            <pre>Binary file.</pre>
-            {{/isBinary}}
-            {{^isBinary}}
-            <div class="code">
-	            <pre class="lines">{{#lines}}<a id="L{{.}}" href="#L{{.}}">{{.}}</a>
-{{/lines}}</pre>
-	            <pre class="highlight">{{content}}</pre>
-	        </div>
-            {{/isBinary}}
-		</main>
-		<footer>Generated on {{generated}}</footer>
-		<script src="https://git.sr.ht/static/linelight.js"></script>
-	</body>
-</html>
-
diff --git a/src/assets/repoCommit.html b/src/assets/repoCommit.html
@@ -1,58 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		<title>{{repoName}} - {{siteTitle}}</title>
-
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, initial-scale=1.0">
-		<meta name="description" content="{{description}}">
-        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
-
-		<link href="/style.css" rel="stylesheet" />
-	</head>
-	<body>
-		<header>
-			<h1><a href="/">{{siteTitle}}</a>: {{repoName}}</h1>
-			<p>{{description}}</p>
-		</header>
-		<nav>
-            <a href="/{{repoUrl}}">summary</a>
-			<a href="/{{repoUrl}}/log" class="active">log</a>
-			<a href="/{{repoUrl}}/tree">tree</a>
-			<a href="/{{repoUrl}}/refs">refs</a>
-		</nav>
-		<main>
-		<div class="event-list">
-			<div class="event">
-				<a class="right" href="/{{repoUrl}}/log/{{id}}.html">{{when}} ago</a>
-				commit: {{id}}<br>
-				{{#parents}}
-				parent: <a href="/{{repoUrl}}/log/{{.}}.html">{{.}}</a><br>
-				{{/parents}}
-				author: {{authorName}}<br>
-				committer: {{committerName}}<br><br>
-				<pre>{{message}}</pre>
-			</div>
-			<div class="event">
-				<pre>{{filesChanged}} files changed, {{insertions}} insertions, {{deletions}} deletions
-
-{{#files}}
-{{status}} <a href="/{{repoUrl}}/tree/{{oldFileUrl}}.html">{{oldFile}}</a>
-{{/files}}</pre>
-			</div>
-			{{#files}}
-			<div class="event">
-<pre>{{status}} {{oldFile}} => {{newFile}}
-{{#hunks}}
-{{header}}
-{{#lines}}<span class="{{type}}">{{prefix}}{{content}}</span>{{/lines}}
-{{/hunks}}
-</pre>
-			</div>
-			{{/files}}
-		</div>
-		</main>
-		<footer>Generated on {{generated}}</footer>
-	</body>
-</html>
-
diff --git a/src/assets/repoLog.html b/src/assets/repoLog.html
@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		<title>{{repoName}} - {{siteTitle}}</title>
-
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, initial-scale=1.0">
-		<meta name="description" content="{{description}}">
-        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
-
-		<link href="/style.css" rel="stylesheet" />
-	</head>
-	<body>
-		<header>
-			<h1><a href="/">{{siteTitle}}</a>: {{repoName}}</h1>
-			<p>{{description}}</p>
-		</header>
-		<nav>
-            <a href="/{{repoUrl}}">summary</a>
-			<a href="/{{repoUrl}}/log" class="active">log</a>
-			<a href="/{{repoUrl}}/tree">tree</a>
-			<a href="/{{repoUrl}}/refs">refs</a>
-		</nav>
-		<main>
-			<div class="events">
-				{{#commits}}
-				<div class="event">
-					<a href="{{id}}.html">{{shortId}}</a> — {{authorName}}
-					<small class="right">{{when}} ago</small>
-					<pre>{{message}}</pre>
-				</div>
-				{{/commits}}
-			</div>
-			<!-- <table>
-                <thead>
-                    <tr>
-                        <td>id</td>
-                        <td>Commit message</td>
-                        <td>Author</td>
-                        <td>Date</td>
-                    </tr>
-                </thead>
-                {{#commits}}
-                <tr>
-                    <td><a href="{{id}}.html">{{shortId}}</a></td>
-                    <td style="text-overflow: ellipsis;"><a href="{{id}}.html">{{summary}}</a></td>
-                    <td>{{authorName}}</td>
-                    <td>{{when}} ago</td>
-                </tr>
-                {{/commits}}
-            </table> -->
-		</main>
-		<footer>Generated on {{generated}}</footer>
-	</body>
-</html>
-
diff --git a/src/assets/repoRefs.html b/src/assets/repoRefs.html
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-        <title>{{repoName}} - {{siteTitle}}</title>
-
-        <meta charset="utf-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1.0">
-        <meta name="description" content="{{description}}">
-        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
-
-        <link href="/style.css" rel="stylesheet" />
-	</head>
-	<body>
-		<header>
-			<h1><a href="/">{{siteTitle}}</a>: {{repoName}}</h1>
-			<p>{{description}}</p>
-		</header>
-		<nav>
-            <a href="/{{repoUrl}}">summary</a>
-			<a href="/{{repoUrl}}/log">log</a>
-			<a href="/{{repoUrl}}/tree">tree</a>
-			<a href="/{{repoUrl}}/refs" class="active">refs</a>
-		</nav>
-		<main>
-		</main>
-		<footer>Generated on {{generated}}</footer>
-	</body>
-</html>
-
diff --git a/src/assets/repoSummary.html b/src/assets/repoSummary.html
@@ -1,76 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-		<title>{{repoName}} - {{siteTitle}}</title>
-
-		<meta charset="utf-8">
-		<meta name="viewport" content="width=device-width, initial-scale=1.0">
-		<meta name="description" content="{{description}}">
-        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
-
-		<link href="/style.css" rel="stylesheet" />
-	</head>
-	<body>
-		<header>
-			<h1><a href="/">{{siteTitle}}</a>: {{repoName}}</h1>
-			<p>{{description}}</p>
-		</header>
-		<nav>
-            <a href="/{{repoUrl}}" class="active">summary</a>
-			<a href="/{{repoUrl}}/log">log</a>
-			<a href="/{{repoUrl}}/tree">tree</a>
-			<a href="/{{repoUrl}}/refs">refs</a>
-		</nav>
-		<main>
-			<div class="container">
-				<div class="row">
-					<div class="col-8">
-						<h3>last commits</h3>
-						<div class="events">
-							{{#lastCommits}}
-							<div class="event">
-								<a href="/{{repoUrl}}/log/{{id}}.html">{{shortId}}</a> — {{authorName}}
-								<small class="right">{{when}} ago</small>
-								<pre>{{summary}}</pre>
-							</div>
-							{{/lastCommits}}
-						</div>
-						<!-- <table>
-							<thead>
-								<tr>
-									<td>id</td>
-									<td>Commit message</td>
-									<td>author</td>
-									<td>date</td>
-								</tr>
-							</thead>
-							{{#lastCommits}}
-							<tr>
-								<td>{{shortId}}</td>
-								<td>{{summary}}</td>
-								<td>{{committerName}}</td>
-								<td>{{when}} ago</td>
-							</tr>
-							{{/lastCommits}}
-						</table> -->
-					</div>
-					<div class="col-4">
-						<h3>clone</h3>
-						<dl>
-							<dt>read-only</dt>
-							<dd><a href="https://git.ctu.cx/{{repoUrl}}">https://git.ctu.cx/{{repoName}}</a></dd>
-							<dt>read/write</dt>
-							<dd>git@wanderduene.ctu.cx:{{repoName}}</dd>
-						</dl>
-					</div>
-				</div>
-			</div>
-		<div class="responsive">
-		{{{readmeContent}}}
-		</div>
-		</main>
-		<footer><p>Generated on {{generated}}</p></footer>
-	</body>
-</html>
-
-   -
\ No newline at end of file
diff --git a/src/assets/repoTree.html b/src/assets/repoTree.html
@@ -1,60 +0,0 @@
-<!DOCTYPE html>
-<html>
-	<head>
-        <title>{{repoName}} - {{siteTitle}}</title>
-
-        <meta charset="utf-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1.0">
-        <meta name="description" content="{{description}}">
-        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
-
-        <link href="/style.css" rel="stylesheet" />
-	</head>
-	<body>
-		<header>
-			<h1><a href="/">{{siteTitle}}</a>: {{repoName}}</h1>
-			<p>{{description}}</p>
-		</header>
-		<nav>
-            <a href="/{{repoUrl}}">summary</a>
-			<a href="/{{repoUrl}}/log">log</a>
-			<a href="/{{repoUrl}}/tree" class="active">tree</a>
-            <a href="/{{repoUrl}}/refs">refs</a>
-		</nav>
-		<main>
-            {{#isSubdir}}
-			{{#path}}<a href="{{url}}">{{name}}</a> / {{/path}} {{dirName}}
-            {{/isSubdir}}
-            <table>
-                <thead>
-                    <tr>
-                        <td style="width: 10%">Mode</td>
-                        <td style="width: 80%">Name</td>
-                        <td style="width: 10%">Size</td>
-                    </tr>
-                </thead>
-                {{#isSubdir}}
-                <tr>
-                    <td></td>
-                    <td><a href="..">..</a></td>
-                    <td></td>
-                </tr>
-                {{/isSubdir}}
-                {{#entries}}
-                <tr>
-                    <td>{{mode}}</td>
-                    {{#isDir}}
-                    <td><a href="{{name}}/">{{name}}</a></td>
-                    {{/isDir}}
-                    {{^isDir}}
-                    <td><a href="{{name}}.html">{{name}}</a></td>
-                    {{/isDir}}
-                    <td>{{size}}</td>
-                </tr>
-                {{/entries}}
-            </table>
-		</main>
-		<footer>Generated on {{generated}}</footer>
-	</body>
-</html>
-
diff --git a/src/index.nim b/src/index.nim
@@ -3,19 +3,6 @@ import nimgit
 
 import types, utils
 
-
-type
-    Repository = object
-        repoName:     string
-        repoUrl:      string
-        description:  string
-        lastActivity: string
-        objId:        string
-
-    Category = object
-        name:  string
-        repos: seq[Repository]
-
 proc reposOverview* (config: Config): seq[Category] =
     template getRepoMetadata = 
         try:

@@ -31,8 +18,8 @@ proc reposOverview* (config: Config): seq[Category] =
                 categoriesTable[category] = newSeq[Repository]()
 
             categoriesTable[category].add(Repository(
-                    repoName:     repoName,
-                    repoUrl:      encodeUrl(repoName),
+                    name:         repoName,
+                    url:          encodeUrl(repoName),
                     description:  config.get("gitweb.description"),
                     lastActivity: relativeTimeFromNow(commit.time.time),
                     objId:        $objId

@@ -57,7 +44,7 @@ proc reposOverview* (config: Config): seq[Category] =
     for name, repos in categoriesTable:
         var repos = repos
 
-        repos.sort(proc (x, y: Repository): int = cmp(x.repoName, y.repoName))
+        repos.sort(proc (x, y: Repository): int = cmp(x.name, y.name))
         
         result.add(Category(
                 name: name,
diff --git a/src/nimstagit.nim b/src/nimstagit.nim
@@ -1,10 +1,11 @@
-import os, times, json
-import nimgit, moustachu
+import os, times
+import nimgit
 
 import types, utils, assets
 
 import index, repoGenerator
 
+include "templates/index.nimf"
 
 var config {.threadvar.}: Config
 

@@ -27,15 +28,17 @@ proc main =
 
     case paramStr(1):
         of "index":
-            let templateContext = %* {
-                    "siteTitle":   config.title,
-                    "description": config.description,
-                    "generated":   $now(),
-                    "categories":  reposOverview(config)
-                }
+            let templateContext = TemplateContext(
+                    siteTitle:   config.title,
+                    description: config.description,
+                    generated:   $now()
+                )
 
             echo "Generate page: index.html"
-            writeFile(joinPath(config.outputDirectory, "index.html"), render(templateOverview, templateContext))
+            writeFile(
+                joinPath(config.outputDirectory, "index.html"),
+                templateIndexPage(templateContext, reposOverview(config))
+            )
             echo "Create asset: style.css"
             writeFile(joinPath(config.outputDirectory, "style.css"), assetStyleCss)
 
diff --git a/src/repoGenerator.nim b/src/repoGenerator.nim
@@ -1,7 +1,7 @@
-import os, json, times, uri, strutils, sequtils
-import moustachu, nimgit, markdown
+import os, times, uri, strutils, sequtils
+import nimgit, markdown
 
-import types, utils, assets
+import types, utils
 
 type
     CommitSummary = object

@@ -25,19 +25,48 @@ type
     RepoTreeEntry = object
         isDir:             bool
         name:              string
+        nameUrl:           string
         mode:              string
         size:              string
 
-    RepoTree = object
-        description:       string
-        category:          string
-        entries:           seq[RepoTreeEntry]
-
     PathObj = object
         name:              string
         url:               string
 
+    Line = object
+        `type`:            string
+        prefix:            string
+        content:           string
+
+    Hunk = object
+        header:            string
+        lines:             seq[Line]
+
+    Patch = object
+        status:            string
+        newFile:           string
+        newFileUrl:        string
+        oldFile:           string
+        oldFileUrl:        string
+        hunks:             seq[Hunk]
+
+    Blob = object
+        path:              seq[PathObj]
+        id:                string
+        filename:          string
+        filenameUrl:       string
+        filesize:          string
+        isBinary:          bool
+        lines:             seq[int]
+        content:           string
+
 
+include "templates/repoSummary.nimf"
+include "templates/repoTree.nimf"
+include "templates/repoBlob.nimf"
+include "templates/repoLog.nimf"
+include "templates/repoCommit.nimf"
+include "templates/repoRefs.nimf"
 
 proc createCommitSummary (commit: GitCommit): CommitSummary =
     let author    = commit.author

@@ -56,33 +85,21 @@ proc createCommitSummary (commit: GitCommit): CommitSummary =
     result.message           = commit.message
 
 
-proc generateCommitPage (path: string, templateContext: JsonNode, commit: GitCommit) =
-#    if fileExists(joinPath(joinPath(path, $commit.id & ".html"))): return
+proc generateCommitPage (path: string, templateContext: TemplateContext, commit: GitCommit) =
+    if fileExists(joinPath(joinPath(path, $commit.id & ".html"))): return
 
     let
       id        = $commit.id
       author    = commit.author
       committer = commit.committer
 
-    var parents: seq[string]
+    var parents       : seq[string]
+    let commitSummary = createCommitSummary(commit) 
 
     if commit.parentCount != 0:
         for id in commit.parentIds:
             parents.add($id)
 
-    var commitTemplateContext = %* {
-            "id":                id,
-            "shortId":           commit.shortId,
-            "when":              relativeTimeFromNow(commit.time.time),
-            "message":           commit.message,
-            "committerIsAuthor": (committer.email == author.email),
-            "committerName":     committer.name,
-            "committerMail":     committer.email,
-            "authorName":        author.name,
-            "authorMail":        author.email,
-            "parents":           parents
-        }
-
     let repo       = commit.owner
     let tree       = commit.tree
 

@@ -116,89 +133,79 @@ proc generateCommitPage (path: string, templateContext: JsonNode, commit: GitCom
 
     let diffStats = diff.stats
 
-    commitTemplateContext["filesChanged"] = %diffStats.filesChanged
-    commitTemplateContext["insertions"]   = %diffStats.insertions
-    commitTemplateContext["deletions"]    = %diffStats.deletions
-
-    commitTemplateContext["files"] = newJArray()
+    var patches: seq[Patch]
     for deltaIndex, delta in diff.deltas:
         let patch = diff.patch(deltaIndex)
-        var hunks = newJArray()
+        var hunks : seq[Hunk]
 
         for hunkIndex, hunk in patch.hunks():
-            var lines = newJArray()
+            var lines: seq[Line]
             for lineIndex, line in patch.lines(hunkIndex):
-                var lineJson = newJObject()
+                var content = newString(line.content_len)
+                copyMem(content.cstring, line.content, line.content_len)
+
+                var tLine = Line(content: content)
 
                 if line.old_lineno == -1:
-                    lineJson["type"]   = %"insertion"
-                    lineJson["prefix"] = %"+"
+                    tLine.type   = "insertion"
+                    tLine.prefix = "+"
                 elif line.new_lineno == -1:
-                    lineJson["type"]   = %"deletion"
-                    lineJson["prefix"] = %"-"
+                    tLine.type   = "deletion"
+                    tLine.prefix = "-"
                 else:
-                    lineJson["prefix"] = %" "
+                    tLine.prefix = " "
 
-                var content = newString(line.content_len)
-                copyMem(content.cstring, line.content, line.content_len)
-                lineJson["content"] = %content
-                lines.add(lineJson)
+                lines.add(tLine)
 
             var header = newString(hunk.header_len)
             copyMem(header.cstring, hunk.header[0].unsafeAddr, hunk.header_len)
-            hunks.add(%* {
-                    "header": header,
-                    "lines":  lines
-                })
-
-        commitTemplateContext["files"].add(%* {
-                "status":     delta.statusChar,
-                "newFile":    $delta.new_file.path,
-                "newFileUrl": encodeUrl($delta.new_file.path),
-                "oldFile":    $delta.old_file.path,
-                "oldFileUrl": encodeUrl($delta.old_file.path),
-                "hunks":      hunks
-            })
+            hunks.add(Hunk(
+                    header: header,
+                    lines:  lines
+                ))
+
+        patches.add(Patch(
+                status:     delta.statusChar,
+                newFile:    $delta.new_file.path,
+                newFileUrl: encodeUrl($delta.new_file.path),
+                oldFile:    $delta.old_file.path,
+                oldFileUrl: encodeUrl($delta.old_file.path),
+                hunks:      hunks
+            ))
 
         free(patch)
     free(diff)
 
     writeFile(
         joinPath(joinPath(path, id & ".html")),
-        render(
-            templateRepoCommit,
-            mergeJson(templateContext, commitTemplateContext)
-        )
+        templateCommitPage(templateContext, commitSummary, parents, diffStats, patches) 
     )
 
 
-proc generateRepoBlobPage (path: string, pathSeq: seq[PathObj], templateContext: JsonNode, entry: GitTreeEntry, blob: GitBlob) =
+proc generateRepoBlobPage (path: string, pathSeq: seq[PathObj], templateContext: TemplateContext, entry: GitTreeEntry, blob: GitBlob) =
     let content = blob.content
 
-    var blobData = %*{
-            "path":        pathSeq,
-            "id":          $entry.id,
-            "filename":    entry.name,
-            "filenameUrl": encodeUrl(entry.name),
-            "filesize":    formatSize(blob.size),
-            "isBinary":    blob.isBinary
-        }
+    var blobData = Blob(
+            path:        pathSeq,
+            id:          $entry.id,
+            filename:    entry.name,
+            filenameUrl: encodeUrl(entry.name),
+            filesize:    formatSize(blob.size),
+            isBinary:    blob.isBinary
+        )
 
     if not blob.isBinary:
-        blobData["content"] = %content
-        blobData["lines"]   = %toSeq(1..content.countLines)
+        blobData.content = content
+        blobData.lines   = toSeq(1..content.countLines)
 
     writeFile(joinPath(path, entry.name), content)
 
     writeFile(
         joinPath(joinPath(path, entry.name & ".html")),
-        render(
-            templateRepoBlob,
-            mergeJson(templateContext, blobData)
-        )
+        templateBlobPage(templateContext, pathSeq, blobData)
     )
 
-proc generateRepoTreePage (isSubdir: bool, path: string, pathSeq: seq[PathObj], dirName: string, templateContext: JsonNode, tree: GitTree) =
+proc generateRepoTreePage (isSubdir: bool, path: string, pathSeq: seq[PathObj], dirName: string, templateContext: TemplateContext, tree: GitTree) =
     discard existsOrCreateDir(joinPath(path))
 
     var

@@ -233,6 +240,7 @@ proc generateRepoTreePage (isSubdir: bool, path: string, pathSeq: seq[PathObj], 
         entries.add(RepoTreeEntry(
                 isDir:    (entry.type != goBlob),
                 name:     entry.name,
+                nameUrl:  encodeUrl(entry.name),
                 mode:     entry.modeStr,
                 size:     formatSize(size)
             ))

@@ -248,15 +256,12 @@ proc generateRepoTreePage (isSubdir: bool, path: string, pathSeq: seq[PathObj], 
 
     writeFile(
         joinPath(path, "index.html"),
-        render(
-            templateRepoTree,
-            mergeJson(templateContext, %*{
-                    "isSubdir": isSubdir,
-                    "path":     pathSeq,
-                    "dirName":  dirName,
-                    "entries":  entries
-                }
-            )
+        templateTreePage(
+            templateContext,
+            dirName,
+            isSubdir,
+            pathSeq,
+            entries
         )
     )
 

@@ -274,14 +279,14 @@ proc repoGenerator* (config: Config, name: string) =
           repoName        = nameFromPath(name)
           description     = repoConfig.get("gitweb.description")
           category        = repoConfig.get("gitweb.category")
-          templateContext = %* {
-                    "siteTitle":     config.title,
-                    "repoName":      repoName,
-                    "repoUrl":       encodeUrl(repoName),
-                    "generated":     $now(),
-                    "description":   description,
-                    "category":      category,
-                }
+          templateContext = TemplateContext(
+                    siteTitle:     config.title,
+                    repoName:      repoName,
+                    repoUrl:       encodeUrl(repoName),
+                    generated:     $now(),
+                    description:   description,
+                    category:      category
+                )
 
 
         echo "Generate pages for repo: " & repoName

@@ -330,16 +335,7 @@ proc repoGenerator* (config: Config, name: string) =
         echo "Generate repo-summary page"
         writeFile(
             joinPath(config.outputDirectory, repoName, "index.html"), 
-            render(
-                templateRepoSummary,
-                mergeJson(
-                    templateContext,
-                    %* {
-                        "lastCommits":   lastCommits,
-                        "readmeContent": readmeContent
-                    }
-                )
-            )
+            templateSummaryPage(templateContext, lastCommits, readmeContent)
         )
 
         #

@@ -350,14 +346,18 @@ proc repoGenerator* (config: Config, name: string) =
         # Tree pages
         #
 
-        var pathSeq: seq[PathObj]
+        let headCacheFile = joinPath(config.outputDirectory, repoName, "tree/.head")
+        if not fileExists(headCacheFile) or readFile(headCacheFile) != $headObjId:
+            writeFile(headCacheFile, $headObjId)
+            var pathSeq: seq[PathObj]
 
-        pathSeq.add(PathObj(
-                name: "root",
-                url: "/" & encodeUrl(repoName) & "/tree"
-            ))
+            pathSeq.add(PathObj(
+                    name: "root",
+                    url: "/" & encodeUrl(repoName) & "/tree"
+                ))
 
-        generateRepoTreePage(false, joinPath(config.outputDirectory, repoName, "tree"), pathSeq, "", templateContext, tree)
+            echo "Generate repo-tree and blob pagesp"
+            generateRepoTreePage(false, joinPath(config.outputDirectory, repoName, "tree"), pathSeq, "", templateContext, tree)
 
         #
         # END Tree pages

@@ -385,15 +385,7 @@ proc repoGenerator* (config: Config, name: string) =
         echo "Generate repo-commit and log page(s)"
         writeFile(
             joinPath(config.outputDirectory, repoName, "log/index.html"), 
-            render(
-                templateRepoLog,
-                mergeJson(
-                    templateContext,
-                    %* {
-                        "commits": commits
-                    }
-                )
-            )
+            templateLogPage(templateContext, commits) 
         )
 
         #

@@ -408,14 +400,7 @@ proc repoGenerator* (config: Config, name: string) =
         echo "Generate repo-refs page!"
         writeFile(
             joinPath(config.outputDirectory, repoName, "refs/index.html"), 
-            render(
-                templateRepoRefs,
-                mergeJson(
-                    templateContext,
-                    %* {
-                    }
-                )
-            )
+            templateRefsPage(templateContext)
         )
 
         #
diff --git a/src/templates/index.nimf b/src/templates/index.nimf
@@ -0,0 +1,52 @@
+#? stdtmpl | standard
+#proc templateIndexPage(templateContext: TemplateContext, categories: seq[Category]): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>$templateContext.siteTitle</title>
+
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="$templateContext.description">
+        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
+
+        <link href="/style.css" rel="stylesheet" />
+    </head>
+    <body>
+        <header>
+            <h1>$templateContext.siteTitle</h1>
+            <p>$templateContext.description</p>
+        </header>
+        <nav>
+            <a class="active" href="index.html">index</a>
+        </nav>
+        <main>
+            <h3>Overview</h3>
+            <table>
+                <thead>
+                    <tr>
+                        <td>Name</td>
+                        <td>Description</td>
+                        <td>Idle</td>
+                    </tr>
+                </thead>
+                #for category in items(categories):
+                #if category.name != "":
+                <tr>
+                    <td colspan="4" class="reposection">$category.name</td>
+                </tr>
+                # end if
+                # for repo in items(category.repos):
+                <tr>
+                    <td class="sublevel-repo"><a href="$repo.url/">$repo.name</a></td>
+                    <td><a href="$repo.url/">$repo.description</a></td>
+                    <td><span class="age-hours"><a href="/$repo.url/log/{$repo.objId}.html">$repo.lastActivity</a></span></td>
+                </tr>
+                #end for
+                #end for
+            </table>
+        </main>
+        <footer>Generated on $templateContext.generated</footer>
+    </body>
+</html>
diff --git a/src/templates/repoBlob.nimf b/src/templates/repoBlob.nimf
@@ -0,0 +1,46 @@
+#? stdtmpl | standard
+#proc templateBlobPage(templateContext: TemplateContext, paths: seq[PathObj], blob: Blob): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>$templateContext.repoName - $templateContext.siteTitle</title>
+
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="$templateContext.description">
+        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
+
+        <link href="/style.css" rel="stylesheet" />
+    </head>
+    <body>
+        <header>
+            <h1><a href="/">$templateContext.siteTitle</a>: $templateContext.repoName</h1>
+            <p>$templateContext.description</p>
+        </header>
+        <nav>
+            <a href="/$templateContext.repoUrl">summary</a>
+            <a href="/$templateContext.repoUrl/log">log</a>
+            <a href="/$templateContext.repoUrl/tree" class="active">tree</a>
+            <a href="/$templateContext.repoUrl/refs">refs</a>
+        </nav>
+        <main>
+            #for path in paths:
+            <a href="$path.url">$path.name</a> /
+            #end for
+            $blob.filename (<a href="$blob.filenameUrl">plain</a>)<br>
+            blob: $blob.id $blob.filesize
+            #if not blob.isBinary:
+            <div class="code">
+                <pre class="lines">
+                #for l in blob.lines:
+<a id="L$l" href="#L$l">$l</a>
+#end for
+</pre>
+                <pre class="highlight">$blob.content</pre>
+            </div>
+            #end if
+        </main>
+        <footer><p>Generated on $templateContext.generated</p></footer>
+    </body>
+</html>
diff --git a/src/templates/repoCommit.nimf b/src/templates/repoCommit.nimf
@@ -0,0 +1,62 @@
+#? stdtmpl | standard
+#proc templateCommitPage(templateContext: TemplateContext, commit: CommitSummary, parents: seq[string], diffStats: GitDiffStats, patches: seq[Patch]): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>$templateContext.repoName - $templateContext.siteTitle</title>
+
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="$templateContext.description">
+        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
+
+        <link href="/style.css" rel="stylesheet" />
+    </head>
+    <body>
+        <header>
+            <h1><a href="/">$templateContext.siteTitle</a>: $templateContext.repoName</h1>
+            <p>$templateContext.description</p>
+        </header>
+        <nav>
+            <a href="/$templateContext.repoUrl">summary</a>
+            <a href="/$templateContext.repoUrl/log" class="active">log</a>
+            <a href="/$templateContext.repoUrl/tree">tree</a>
+            <a href="/$templateContext.repoUrl/refs">refs</a>
+        </nav>
+        <main>
+        <div class="event-list">
+            <div class="event">
+                <a class="right" href="/$templateContext.repoUrl/log/{$commit.id}.html">$commit.when ago</a>
+                commit: $commit.id<br>
+                #for parent in parents:
+                parent: <a href="/$templateContext.repoUrl/log/{$parent}.html">$parent</a><br>
+                #end for
+                author: $commit.authorName<br>
+                committer: $commit.committerName<br><br>
+                <pre>$commit.message</pre>
+            </div>
+            <div class="event">
+                <pre>$diffStats.filesChanged files changed, $diffStats.insertions insertions, $diffStats.deletions deletions
+
+#for patch in patches:
+$patch.status <a href="/$templateContext.repoUrl/tree/${patch.oldFileUrl}.html">$patch.oldFile</a>
+#end for
+</pre>
+            </div>
+            #for patch in patches:
+            <div class="event">
+<pre>$patch.status $patch.oldFile => $patch.newFile
+#for hunk in patch.hunks:
+$hunk.header
+#for line in hunk.lines: result.add("<span class=\"" & line.type & "\">" & line.prefix & line.content & "</span>")
+#end for
+#end for
+</pre>
+            </div>
+            #end for
+        </div>
+        </main>
+        <footer><p>Generated on $templateContext.generated</p></footer>
+    </body>
+</html>+
\ No newline at end of file
diff --git a/src/templates/repoLog.nimf b/src/templates/repoLog.nimf
@@ -0,0 +1,40 @@
+#? stdtmpl | standard
+#proc templateLogPage(templateContext: TemplateContext, commits: seq[CommitSummary]): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>$templateContext.repoName - $templateContext.siteTitle</title>
+
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="$templateContext.description">
+        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
+
+        <link href="/style.css" rel="stylesheet" />
+    </head>
+    <body>
+        <header>
+            <h1><a href="/">$templateContext.siteTitle</a>: $templateContext.repoName</h1>
+            <p>$templateContext.description</p>
+        </header>
+        <nav>
+            <a href="/$templateContext.repoUrl">summary</a>
+            <a href="/$templateContext.repoUrl/log" class="active">log</a>
+            <a href="/$templateContext.repoUrl/tree">tree</a>
+            <a href="/$templateContext.repoUrl/refs">refs</a>
+        </nav>
+        <main>
+            <div class="events">
+                #for commit in items(commits):
+                <div class="event">
+                    <a href="${commit.id}.html">$commit.shortId</a> — $commit.authorName
+                    <small class="right">$commit.when ago</small>
+                    <pre>$commit.message</pre>
+                </div>
+                #end for
+            </div>
+        </main>
+        <footer><p>Generated on $templateContext.generated</p></footer>
+    </body>
+</html>+
\ No newline at end of file
diff --git a/src/templates/repoRefs.nimf b/src/templates/repoRefs.nimf
@@ -0,0 +1,31 @@
+#? stdtmpl | standard
+#proc templateRefsPage(templateContext: TemplateContext): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>$templateContext.repoName - $templateContext.siteTitle</title>
+
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="$templateContext.description">
+        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
+
+        <link href="/style.css" rel="stylesheet" />
+    </head>
+    <body>
+        <header>
+            <h1><a href="/">$templateContext.siteTitle</a>: $templateContext.repoName</h1>
+            <p>$templateContext.description</p>
+        </header>
+        <nav>
+            <a href="/$templateContext.repoUrl">summary</a>
+            <a href="/$templateContext.repoUrl/log">log</a>
+            <a href="/$templateContext.repoUrl/tree">tree</a>
+            <a href="/$templateContext.repoUrl/refs" class="active">refs</a>
+        </nav>
+        <main>
+        </main>
+        <footer><p>Generated on $templateContext.generated</p></footer>
+    </body>
+</html>
diff --git a/src/templates/repoSummary.nimf b/src/templates/repoSummary.nimf
@@ -0,0 +1,59 @@
+#? stdtmpl | standard
+#proc templateSummaryPage(templateContext: TemplateContext, lastCommits: seq[CommitSummary], readmeContent: string): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>$templateContext.repoName - $templateContext.siteTitle</title>
+
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="$templateContext.description">
+        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
+
+        <link href="/style.css" rel="stylesheet" />
+    </head>
+    <body>
+        <header>
+            <h1><a href="/">$templateContext.siteTitle</a>: $templateContext.repoName</h1>
+            <p>$templateContext.description</p>
+        </header>
+        <nav>
+            <a href="/$templateContext.repoUrl" class="active">summary</a>
+            <a href="/$templateContext.repoUrl/log">log</a>
+            <a href="/$templateContext.repoUrl/tree">tree</a>
+            <a href="/$templateContext.repoUrl/refs">refs</a>
+        </nav>
+        <main>
+            <div class="container">
+                <div class="row">
+                    <div class="col-8">
+                        <h3>last commits</h3>
+                        <div class="events">
+                            #for commit in items(lastCommits):
+                            <div class="event">
+                                <a href="/$templateContext.repoUrl/log/${commit.id}.html">$commit.shortId</a> — $commit.authorName
+                                <small class="right">$commit.when ago</small>
+                                <pre>$commit.summary</pre>
+                            </div>
+                            #end for
+                        </div>
+                    </div>
+                    <div class="col-4">
+                        <h3>clone</h3>
+                        <dl>
+                            <dt>read-only</dt>
+                            <dd><a href="https://git.ctu.cx/$templateContext.repoUrl">https://git.ctu.cx/$templateContext.repoName</a></dd>
+                            <dt>read/write</dt>
+                            <dd>git@wanderduene.ctu.cx:$templateContext.repoName</dd>
+                        </dl>
+                    </div>
+                </div>
+            </div>
+        <div class="responsive">
+        $readmeContent
+        </div>
+        </main>
+        <footer><p>Generated on $templateContext.generated</p></footer>
+    </body>
+</html>
diff --git a/src/templates/repoTree.nimf b/src/templates/repoTree.nimf
@@ -0,0 +1,65 @@
+#? stdtmpl | standard
+#proc templateTreePage(templateContext: TemplateContext, dirName: string, isSubdir: bool, paths: seq[PathObj], entries: seq[RepoTreeEntry]): string =
+#  result = ""
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>$templateContext.repoName - $templateContext.siteTitle</title>
+
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta name="description" content="$templateContext.description">
+        <meta name="generator" content="https://cgit.ctu.cx/nimstagit">
+
+        <link href="/style.css" rel="stylesheet" />
+    </head>
+    <body>
+        <header>
+            <h1><a href="/">$templateContext.siteTitle</a>: $templateContext.repoName</h1>
+            <p>$templateContext.description</p>
+        </header>
+        <nav>
+            <a href="/$templateContext.repoUrl">summary</a>
+            <a href="/$templateContext.repoUrl/log">log</a>
+            <a href="/$templateContext.repoUrl/tree" class="active">tree</a>
+            <a href="/$templateContext.repoUrl/refs">refs</a>
+        </nav>
+        <main>
+            #if isSubdir:
+            #for path in paths:
+            <a href="$path.url">$path.name</a>
+            #end for
+             / $dirName
+            #end if
+            <table>
+                <thead>
+                    <tr>
+                        <td style="width: 10%">Mode</td>
+                        <td style="width: 80%">Name</td>
+                        <td style="width: 10%">Size</td>
+                    </tr>
+                </thead>
+                #if isSubdir:
+                <tr>
+                    <td></td>
+                    <td><a href="..">..</a></td>
+                    <td></td>
+                </tr>
+                #end if
+                #for entry in entries:
+                <tr>
+                    <td>$entry.mode</td>
+                    #if entry.isDir:
+                    <td><a href="$entry.nameUrl/">$entry.name</a></td>
+                    #end if
+                    #if not entry.isDir:
+                    <td><a href="${entry.nameUrl}.html">$entry.name</a></td>
+                    #end if
+                    <td>$entry.size</td>
+                </tr>
+                #end for
+            </table>
+        </main>
+        <footer><p>Generated on $templateContext.generated</p></footer>
+    </body>
+</html>
diff --git a/src/types.nim b/src/types.nim
@@ -6,4 +6,23 @@ type
         scanPath*:        string
         projectsList*:    seq[string]
         readmeFiles*:     seq[string]
-        renderMarkdown*:  bool-
\ No newline at end of file
+        renderMarkdown*:  bool
+
+    TemplateContext* = object
+        siteTitle*:       string
+        description*:     string
+        repoName*:        string
+        repoUrl*:         string
+        category*:        string
+        generated*:       string
+
+    Repository* = object
+        name*:            string
+        url*:             string
+        description*:     string
+        lastActivity*:    string
+        objId*:           string
+
+    Category* = object
+        name*:  string
+        repos*: seq[Repository]